ImageEditor.st
author Claus Gittinger <cg@exept.de>
Sat, 25 Apr 1998 13:11:50 +0200
changeset 809 6f127dd5e578
parent 767 ed96f735dab1
child 815 b2b9356a648c
permissions -rw-r--r--
added copyright & documentation

"
 COPYRIGHT (c) 1997-1998 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice. This software may not
 be provided or otherwise made available to, or used by, any
 other person. No title to or ownership of the software is
 hereby transferred.
"

ToolApplicationModel subclass:#ImageEditor
	instanceVariableNames:'colorMapMode selectedColorIndex postOpenAction'
	classVariableNames:''
	poolDictionaries:''
	category:'Interface-UIPainter'
!

!ImageEditor class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 1997-1998 by eXept Software AG
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice. This software may not
 be provided or otherwise made available to, or used by, any
 other person. No title to or ownership of the software is
 hereby transferred.
"
!

documentation
"
    Image Editor allows you to create, design, modify or just inspect images.

    [start with:]
        ImageEditor open
        ImageEditor openOnClass:ImageEditor andSelector:#newImageIcon

    [see also:]
        ImageEditView Image

    [author:]
        Thomas Zwick, eXept Software AG
"
! !

!ImageEditor class methodsFor:'instance creation'!

openModalOnClass: aClass andSelector: aSelector
    "opens modal the Image Editor on aClass and aSelector"
    "
     self openModalOnClass: self andSelector: #leftMouseKeyIcon
    "

    |imageEditor imageEditView className resourceClassName resourceSelector| 

    imageEditor := self new.

    aClass isClass ifTrue: [className := aClass name].
    aClass isString ifTrue:[className := aClass].      

    imageEditor postOpenAction: [imageEditView := imageEditor imageEditView. imageEditor loadFromOrPrepareForMessage: className, ' ', aSelector].
    imageEditor openModal.

    resourceClassName := imageEditView resourceClass.
    resourceSelector  := imageEditView resourceSelector.

    (className asString ~= resourceClassName) | (aSelector asString ~= resourceSelector)
        ifTrue: [^resourceClassName, ' ', resourceSelector]
        ifFalse:[^nil]
!

openOnClass: aClass andSelector: aSelector
    "opens the Image Editor on aClass and aSelector"
    "
     self openOnClass: self andSelector: #leftMouseKeyIcon
    "

    ^self open loadFromMessage: aClass name, ' ', aSelector
!

openOnFile: aFileName
    "opens the Image Editor on aFileName"
    "
     self openOnFile: 'bitmaps/SmalltalkX.xbm'
    "

    ^self open loadFromFile: aFileName
!

openOnImage: anImage
    "opens the Image Editor on anImage"
    "
     self openOnImage: self stxIcon
    "

    ^self open loadFromImage: anImage
! !

!ImageEditor class methodsFor:'accessing'!

listOfColorMaps
    "returns the list of default color maps for a new image"

    |colorMap|
    (colorMap := OrderedCollection new)
        add: Color black;
        add: Color white;
        add: Color red;
        add: Color green;
        add: Color blue;
        add: Color cyan;
        add: Color yellow;
        add: Color magenta;
        add: (Color red: 50 green: 0 blue: 0);
        add: (Color red: 0 green: 50 blue: 0);
        add: (Color red: 0 green: 0 blue: 50);
        add: (Color red: 0 green: 50 blue: 50);
        add: (Color red: 50 green: 50 blue: 0);
        add: (Color red: 50 green: 0 blue: 50);
        add: Color gray;
        add: Color lightGray.                                
    0 to: 5 do:
    [:r|                                                    
        0 to: 5 do:         
        [:g|
            0 to: 5 do:                             
            [:b|
                colorMap add: (Color red: (r*100//5) ceiling green: (g*100//5) ceiling blue: (b*100//5) ceiling)
            ]
        ]
    ].

    1 to: 25 do:
    [:g|                             
        colorMap add: (Color gray: (g*100//26) ceiling)
    ].

    ^Dictionary new
        at: '8-plane' put: colorMap;
        at: '8-plane + mask' put: colorMap;
        at: '4-plane' put: (colorMap copyFrom: 1 to: 16);
        at: '4-plane + mask' put: (colorMap copyFrom: 1 to: 16);
        at: '2-plane' put: (colorMap copyFrom: 1 to: 4);
        at: '2-plane + mask' put: (colorMap copyFrom: 1 to: 4);
        at: '1-plane' put: (colorMap copyFrom: 1 to: 2);
        at: '1-plane + mask' put: (colorMap copyFrom: 1 to: 2);
        yourself
!

listOfDefaultSizes
    "returns the list of default sizes for a new image"

    ^#('22x22' '16x16' '32x32')
! !

!ImageEditor class methodsFor:'help specs'!

helpSpec
    "This resource specification was automatically generated
     by the UIHelpTool of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the UIHelpTool may not be able to read the specification."

    "
     UIHelpTool openOnClass:ImageEditor    
    "

    <resource: #help>

    ^super helpSpec addPairsFrom:#(

#colorMap
'Increases or reduces size of color map of the image.'

#colorMapTable
'Shows a list of used colors of the image.'

#drawModeBox
'Switches to mode drawing boxes.'

#drawModeCopy
'Switches to mode copying areas.'

#drawModeFill
'Switches to mode filling areas around selected point.'

#drawModeFilledBox
'Switches to mode drawing filled boxes.'

#drawModePaste
'Switches to mode pasting areas at selected point.'

#drawModePoint
'Switches to mode drawing points.'

#editFlipHorizontal
'Flips horizontally the image.'

#editFlipVertical
'Flips vertically the image.'

#editMagnifyImage
'Opens a dialog to magnify the image.'

#editNegate
'Convertes colors of image by negating them.'

#editResize
'Opens a dialog to resize the image with preserving the old image.'

#editRotate
'Opens a dialog to rotate the image in degrees.'

#fileLoadFromClass
'Opens a dialog for selecting and loading an image from resource method of a class.'

#fileLoadFromFile
'Opens a dialog for selecting and loading an image from a file.'

#fileNewImage
'Opens a dialog with choices of size and color depth to create an new image.'

#filePrint
'Print the image on a postscript printer.'

#fileSaveAs
'Opens a dialog to save the image to a file.'

#fileSaveMaskAs
'Opens a dialog to save the mask of the image to a file.'

#fileSaveMethod
'Saves the image on current class and selector.'

#fileSaveMethodAs
'Opens a dialog for selecting and saving the image in a resource method of a class.'

#magnificationNumber
'Shows current number of magnification.'

#magnifyImageDown
'Magnifies down the image.'

#magnifyImageUp
'Magnifies up the image.'

#mouseKeyColorMode
'Switches between left and right mouse button color.'

#previewView
'Shows preview of the image.'

)
! !

!ImageEditor class methodsFor:'image specs'!

leftMouseKeyIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the ImageEditor may not be able to read the specification."

    "
     ImageEditor openOnClass:self andSelector:#leftMouseKeyIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor leftMouseKeyIcon'
        ifAbsentPut:[(Depth2Image new) width: 16; height: 16; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@@@JEE@@B!!QP@@(TT@@@@@@@AUUP@@UUT@@EUU@@AUUP@@UUT@@EUU@@@UU@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 255 255 255 0 0 0 255 0]; mask:((Depth1Image new) width: 16; height: 16; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@?0G? _>A?8G? _>A?8G? _>A?8G? O<@_ @@@b') ; yourself); yourself]!

rightMouseKeyIcon
    "This resource specification was automatically generated
     by the ImageEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the ImageEditor may not be able to read the specification."

    "
     ImageEditor openOnClass:self andSelector:#rightMouseKeyIcon
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor rightMouseKeyIcon'
        ifAbsentPut:[(Depth2Image new) width: 16; height: 16; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@@@EEJ@@AQR @@TT(@@@@@@@AUUP@@UUT@@EUU@@AUUP@@UUT@@EUU@@@UU@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 255 255 255 0 0 0 255 0]; mask:((Depth1Image new) width: 16; height: 16; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@?0G? _>A?8G? _>A?8G? _>A?8G? O<@_ @@@b') ; yourself); yourself]! !

!ImageEditor class methodsFor:'interface specs'!

dialogSpecForNewImage
    "This resource specification was automatically generated
     by the UIPainter of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the UIPainter may not be able to read the specification."

    "
     UIPainter new openOnClass:ImageEditor andSelector:#dialogSpecForNewImage
     ImageEditor new openInterface:#dialogSpecForNewImage
    "

    <resource: #canvas>

    ^
     
       #(#FullSpec
          #window: 
           #(#WindowSpec
              #name: 'New Image'
              #layout: #(#LayoutFrame 240 0 278 0 540 0 382 0)
              #label: 'New Image'
              #min: #(#Point 10 10)
              #max: #(#Point 1152 900)
              #bounds: #(#Rectangle 240 278 541 383)
              #usePreferredExtent: false
          )
          #component: 
           #(#SpecCollection
              #collection: 
               #(
                 #(#ViewSpec
                    #name: 'View'
                    #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 -35 1.0)
                    #component: 
                     #(#SpecCollection
                        #collection: 
                         #(
                           #(#FramedBoxSpec
                              #name: 'framedBox1'
                              #layout: #(#LayoutFrame 1 0.0 7 0.0 0 0.4 66 0)
                              #component: 
                               #(#SpecCollection
                                  #collection: 
                                   #(
                                     #(#ComboBoxSpec
                                        #name: 'defaultSizesComboBox'
                                        #layout: #(#LayoutFrame 15 0.0 20 0.0 17 1.0 16 1.0)
                                        #model: #selectionOfSize
                                        #type: #string
                                        #comboList: #listOfDefaultSizes
                                    )
                                  )
                              )
                              #label: 'Size'
                              #labelPosition: #topLeft
                              #style: #(#FontDescription #helvetica #medium #roman 12)
                          )
                           #(#FramedBoxSpec
                              #name: 'framedBox2'
                              #layout: #(#LayoutFrame 0 0.4 7 0.0 -1 1.0 66 0)
                              #component: 
                               #(#SpecCollection
                                  #collection: 
                                   #(
                                     #(#ComboListSpec
                                        #name: 'colorMapComboBox'
                                        #layout: #(#LayoutFrame 15 0.0 20 0.0 18 1.0 16 1.0)
                                        #model: #selectionOfColorMap
                                        #comboList: #listOfColorMaps
                                        #useIndex: false
                                    )
                                  )
                              )
                              #label: 'Color Map'
                              #labelPosition: #topLeft
                              #style: #(#FontDescription #helvetica #medium #roman 12)
                          )
                        )
                    )
                    #level: 1
                )
                 #(#UISubSpecification
                    #name: 'UISubSpecification1'
                    #layout: #(#LayoutFrame 2 0.0 -26 1 -2 1.0 -2 1.0)
                    #majorKey: #ToolApplicationModel
                    #minorKey: #windowSpecForCommitWithoutChannels
                )
              )
          )
      )
!

windowSpec
    "This resource specification was automatically generated
     by the UIPainter of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the UIPainter may not be able to read the specification."

    "
     UIPainter new openOnClass:ImageEditor andSelector:#windowSpec
     ImageEditor new openInterface:#windowSpec
     ImageEditor open
    "

    <resource: #canvas>

    ^
     
       #(#FullSpec
          #window: 
           #(#WindowSpec
              #name: 'Image Editor'
              #layout: #(#LayoutFrame 167 0 330 0 666 0 675 0)
              #label: 'Image Editor'
              #min: #(#Point 400 320)
              #max: #(#Point 1152 900)
              #bounds: #(#Rectangle 167 330 667 676)
              #menu: #menu
              #usePreferredExtent: false
          )
          #component: 
           #(#SpecCollection
              #collection: 
               #(
                 #(#MenuPanelSpec
                    #name: 'menuToolbarView'
                    #layout: #(#LayoutFrame 0 0.0 0 0 0 1.0 32 0)
                    #menu: #menuToolbar
                    #style: #(#FontDescription #helvetica #medium #roman 10)
                    #showSeparatingLines: true
                )
                 #(#VariableHorizontalPanelSpec
                    #name: 'variableHorizontalPanel1'
                    #layout: #(#LayoutFrame 0 0.0 34 0.0 0 1.0 -26 1.0)
                    #component: 
                     #(#SpecCollection
                        #collection: 
                         #(
                           #(#ViewSpec
                              #name: 'view1'
                              #component: 
                               #(#SpecCollection
                                  #collection: 
                                   #(
                                     #(#VariableVerticalPanelSpec
                                        #name: 'VariableVerticalPanel1'
                                        #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
                                        #component: 
                                         #(#SpecCollection
                                            #collection: 
                                             #(
                                               #(#ViewSpec
                                                  #name: 'View1'
                                                  #component: 
                                                   #(#SpecCollection
                                                      #collection: 
                                                       #(
                                                         #(#MenuPanelSpec
                                                            #name: 'MouseButtonColorToolBar'
                                                            #layout: #(#LayoutFrame 0 0.0 0 0 0 1.0 24 0)
                                                            #menu: #menuMouseButtonColors
                                                        )
                                                         #(#DataSetSpec
                                                            #name: 'colorDataSetView'
                                                            #layout: #(#LayoutFrame 0 0.0 26 0.0 0 1.0 0 1.0)
                                                            #activeHelpKey: #colorMapTable
                                                            #model: #selectionOfColor
                                                            #style: #(#FontDescription #helvetica #medium #roman 10)
                                                            #hasHorizontalScrollBar: true
                                                            #hasVerticalScrollBar: true
                                                            #miniScrollerHorizontal: true
                                                            #miniScrollerVertical: true
                                                            #level: -1
                                                            #dataList: #listOfColors
                                                            #has3Dsepartors: true
                                                            #verticalSpacing: 1
                                                            #columns: 
                                                             #(
                                                               #(#DataSetColumnSpec
                                                                  #rendererType: #rowSelector
                                                                  #backgroundSelector: #yourself
                                                              )
                                                               #(#DataSetColumnSpec
                                                                  #label: 'R'
                                                                  #labelAlignment: #left
                                                                  #model: #rowRedByte
                                                                  #canSelect: false
                                                              )
                                                               #(#DataSetColumnSpec
                                                                  #label: 'G'
                                                                  #labelAlignment: #left
                                                                  #model: #rowGreenByte
                                                                  #canSelect: false
                                                              )
                                                               #(#DataSetColumnSpec
                                                                  #label: 'B'
                                                                  #labelAlignment: #left
                                                                  #model: #rowBlueByte
                                                                  #canSelect: false
                                                              )
                                                            )
                                                        )
                                                      )
                                                  )
                                              )
                                               #(#ArbitraryComponentSpec
                                                  #name: 'imagePreView'
                                                  #activeHelpKey: #previewView
                                                  #hasHorizontalScrollBar: true
                                                  #hasVerticalScrollBar: true
                                                  #miniScrollerHorizontal: true
                                                  #miniScrollerVertical: true
                                                  #component: #ImageView
                                                  #hasBorder: false
                                              )
                                            )
                                        )
                                        #handles: #(#Any 0.723776 1.0)
                                    )
                                  )
                              )
                          )
                           #(#ViewSpec
                              #name: 'view2'
                              #component: 
                               #(#SpecCollection
                                  #collection: 
                                   #(
                                     #(#ArbitraryComponentSpec
                                        #name: 'imageEditView'
                                        #layout: #(#LayoutFrame 2 0.0 2 0.0 -2 1.0 -24 1.0)
                                        #hasHorizontalScrollBar: true
                                        #hasVerticalScrollBar: true
                                        #component: #ImageEditView
                                        #hasBorder: false
                                    )
                                     #(#LabelSpec
                                        #name: 'coordLabel'
                                        #layout: #(#LayoutFrame 2 0.0 -22 1 -83 1.0 0 1.0)
                                        #level: -1
                                        #adjust: #left
                                        #resizeForLabel: false
                                    )
                                     #(#ArrowButtonSpec
                                        #name: 'magnifyDownButton'
                                        #layout: #(#LayoutFrame -80 1 -22 1 -58 1 0 1)
                                        #activeHelpKey: #magnifyImageDown
                                        #model: #doMagnifyDown
                                        #enableChannel: #imageIsLoaded
                                        #isTriggerOnDown: true
                                        #direction: #left
                                    )
                                     #(#ArrowButtonSpec
                                        #name: 'magnifyUpButton'
                                        #layout: #(#LayoutFrame -24 1 -22 1 -2 1 0 1)
                                        #activeHelpKey: #magnifyImageUp
                                        #model: #doMagnifyUp
                                        #enableChannel: #imageIsLoaded
                                        #isTriggerOnDown: true
                                        #direction: #right
                                    )
                                     #(#InputFieldSpec
                                        #name: 'magnificationInputField'
                                        #layout: #(#LayoutFrame -57 1 -22 1 -26 1 0 1)
                                        #activeHelpKey: #magnificationNumber
                                        #enableChannel: #imageIsLoaded
                                        #model: #valueOfMagnification
                                        #type: #number
                                        #acceptOnReturn: false
                                        #acceptOnTab: false
                                        #numChars: 2
                                    )
                                  )
                              )
                              #level: -1
                          )
                        )
                    )
                    #handles: #(#Any 0.276 1.0)
                )
                 #(#UISubSpecification
                    #name: 'infoBarSubSpec'
                    #layout: #(#LayoutFrame 0 0.0 -24 1 0 1.0 0 1.0)
                    #majorKey: #ToolApplicationModel
                    #minorKey: #windowSpecForInfoBar
                )
              )
          )
      )
! !

!ImageEditor class methodsFor:'menu specs'!

menu
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:ImageEditor andSelector:#menu
     (Menu new fromLiteralArrayEncoding:(ImageEditor menu)) startUp
    "

    <resource: #menu>

    ^
     
       #(#Menu
          
           #(
             #(#MenuItem
                #label: 'About'
                #activeHelpKey: #about
                #labelImage: #(#ResourceRetriever nil #menuIcon)
                #submenuChannel: #menuAbout
            )
             #(#MenuItem
                #label: 'File'
                #translateLabel: true
                #activeHelpKey: #file
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'New...'
                          #value: #doNewImage
                          #activeHelpKey: #fileNewImage
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Load...'
                          #value: #doLoadFromClass
                          #activeHelpKey: #fileLoadFromClass
                      )
                       #(#MenuItem
                          #label: 'Load From File...'
                          #value: #doLoadFromFile
                          #activeHelpKey: #fileLoadFromFile
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Save'
                          #value: #doSaveMethod
                          #activeHelpKey: #fileSaveMethod
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: 'Save As...'
                          #value: #doSaveMethodAs
                          #activeHelpKey: #fileSaveMethodAs
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Save To File...'
                          #value: #doSaveImageFileAs
                          #activeHelpKey: #fileSaveAs
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: 'Save Mask To File...'
                          #value: #doSaveImageMaskFileAs
                          #activeHelpKey: #fileSaveMaskAs
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Print'
                          #value: #doPrint
                          #activeHelpKey: #filePrint
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Browse Class'
                          #value: #doBrowseClass
                          #activeHelpKey: #fileBrowseClass
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Exit'
                          #translateLabel: true
                          #value: #closeRequest
                          #activeHelpKey: #fileExit
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Edit'
                #activeHelpKey: #edit
                #enabled: #imageIsLoaded
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Undo'
                          #value: #doUndo
                          #activeHelpKey: #editUndo
                          #enabled: #valueOfCanUndo
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Flip - Vertical'
                          #value: #doFlipVertical
                          #activeHelpKey: #editFlipVertical
                      )
                       #(#MenuItem
                          #label: 'Flip - Horizontal'
                          #value: #doFlipHorizontal
                          #activeHelpKey: #editFlipHorizontal
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Resize...'
                          #value: #doResizeImage
                          #activeHelpKey: #editResize
                      )
                       #(#MenuItem
                          #label: 'Magnify...'
                          #value: #doMagnifyImage
                          #activeHelpKey: #editMagnifyImage
                      )
                       #(#MenuItem
                          #label: 'Rotate...'
                          #value: #doRotateImage
                          #activeHelpKey: #editRotate
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Negative'
                          #value: #doNegativeImage
                          #activeHelpKey: #editNegate
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Color Map'
                #activeHelpKey: #colorMap
                #enabled: #imageIsLoaded
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: '8-Plane'
                          #activeHelpKey: #colorMap
                          #argument: '8-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '4-Plane'
                          #activeHelpKey: #colorMap
                          #argument: '4-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '2-Plane'
                          #activeHelpKey: #colorMap
                          #argument: '2-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '1-Plane'
                          #activeHelpKey: #colorMap
                          #argument: '1-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: '8-Plane + Mask'
                          #activeHelpKey: #colorMap
                          #argument: '8-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '4-Plane + Mask'
                          #activeHelpKey: #colorMap
                          #argument: '4-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '2-Plane + Mask'
                          #activeHelpKey: #colorMap
                          #argument: '2-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '1-Plane + Mask'
                          #activeHelpKey: #colorMap
                          #argument: '1-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Settings'
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Fonts'
                          #submenuChannel: #menuFont
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'History'
                #activeHelpKey: #history
                #submenuChannel: #menuHistory
            )
             #(#MenuItem
                #label: 'Help'
                #startGroup: #right
                #activeHelpKey: #help
                #submenuChannel: #menuHelp
            )
          ) nil
          nil
      )
!

menuMouseButtonColors
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:ImageEditor andSelector:#menuMouseButtonColors
     (Menu new fromLiteralArrayEncoding:(ImageEditor menuMouseButtonColors)) startUp
    "

    <resource: #menu>

    ^
     
       #(#Menu
          
           #(
             #(#MenuItem
                #label: 'Left Mouse Button'
                #nameKey: #leftMouseKeyButton
                #activeHelpKey: #mouseKeyColorMode
                #argument: '1'
                #labelImage: #(#ResourceRetriever nil #leftMouseKeyIcon)
                #indication: #mouseKeyColorMode:value:
            )
             #(#MenuItem
                #label: 'Right Mouse Button'
                #nameKey: #rightMouseKeyButton
                #activeHelpKey: #mouseKeyColorMode
                #argument: '2'
                #labelImage: #(#ResourceRetriever nil #rightMouseKeyIcon)
                #indication: #mouseKeyColorMode:value:
            )
          ) nil
          nil
      )
!

menuToolbar
    "This resource specification was automatically generated
     by the MenuEditor of ST/X."

    "Do not manually edit this!! If it is corrupted,
     the MenuEditor may not be able to read the specification."

    "
     MenuEditor new openOnClass:ImageEditor andSelector:#menuToolbar
     (Menu new fromLiteralArrayEncoding:(ImageEditor menuToolbar)) startUp
    "

    <resource: #menu>

    ^
     
       #(#Menu
          
           #(
             #(#MenuItem
                #label: 'newImage'
                #isButton: true
                #value: #doNewImage
                #activeHelpKey: #fileNewImage
                #labelImage: #(#ResourceRetriever nil #newIcon)
            )
             #(#MenuItem
                #label: 'loadFromClass'
                #isButton: true
                #value: #doLoadFromClass
                #activeHelpKey: #fileLoadFromClass
                #labelImage: #(#ResourceRetriever nil #loadIcon)
            )
             #(#MenuItem
                #label: 'saveMethod'
                #isButton: true
                #value: #doSaveMethod
                #activeHelpKey: #fileSaveMethod
                #enabled: #imageIsLoaded
                #labelImage: #(#ResourceRetriever nil #saveIcon)
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: ''
            )
             #(#MenuItem
                #label: 'Point'
                #activeHelpKey: #drawModePoint
                #enabled: #imageIsLoaded
                #argument: 'point'
                #indication: #editMode:value:
            )
             #(#MenuItem
                #label: 'Box'
                #activeHelpKey: #drawModeBox
                #enabled: #imageIsLoaded
                #argument: 'box'
                #indication: #editMode:value:
            )
             #(#MenuItem
                #label: 'Filled Box'
                #activeHelpKey: #drawModeFilledBox
                #enabled: #imageIsLoaded
                #argument: 'filledBox'
                #indication: #editMode:value:
            )
             #(#MenuItem
                #label: 'Fill'
                #activeHelpKey: #drawModeFill
                #enabled: #imageIsLoaded
                #argument: 'fill'
                #indication: #editMode:value:
            )
             #(#MenuItem
                #label: 'Copy'
                #activeHelpKey: #drawModeCopy
                #enabled: #imageIsLoaded
                #argument: 'copy'
                #indication: #editMode:value:
            )
             #(#MenuItem
                #label: 'Paste'
                #activeHelpKey: #drawModePaste
                #enabled: #imageIsLoaded
                #argument: 'paste'
                #indication: #editMode:value:
            )
          ) nil
          nil
      )
! !

!ImageEditor methodsFor:'accessing'!

image
    "returns the current editing image"

    ^self imageEditView image
!

postOpenAction: anAction
   "sets an action which is evaluated after opening"

    postOpenAction := anAction
! !

!ImageEditor methodsFor:'accessing - views'!

coordLabel
    "returns the view the coord label"

    ^builder componentAt: #coordLabel
!

imageEditView
    "returns the image editing view"

    ^(builder componentAt: #imageEditView) subViews first 
!

imagePreView
    "returns the preview of the image"

    ^(builder componentAt: #imagePreView) subViews first 
! !

!ImageEditor methodsFor:'aspects'!

imageIsLoaded
    "returns whether an image is loaded as value holder"

    |holder|
    (holder := builder bindingAt:#imageIsLoaded) isNil ifTrue:[
        builder aspectAt:#imageIsLoaded put:(holder :=  false asValue).
    ].
    ^ holder
!

listOfColors
    "returns the list of colors in a List"

    |holder|
    (holder := builder bindingAt:#listOfColors) isNil ifTrue:[
        builder aspectAt:#listOfColors put:(holder :=  List new).
    ].
    ^ holder
!

selectionOfColor
    "returns current selection of the edit color as an AspectAdaptor"

    |holder|
    (holder := builder bindingAt:#selectionOfColor) isNil ifTrue:[
        builder aspectAt:#selectionOfColor put:(
        holder := AspectAdaptor new subject:self; forAspect:#selectedColorIndex).
    ].
    ^ holder
!

valueOfMagnification
    "returns current magnification of the image as an AspectAdaptor"

    |holder|
    (holder := builder bindingAt:#valueOfMagnification) isNil ifTrue:[
        builder aspectAt:#valueOfMagnification put:(
        holder := AspectAdaptor new subject:self; forAspect:#magnification)
    ].
    ^ holder
! !

!ImageEditor methodsFor:'change & update'!

findColorMapMode
    "finds the colorMapMode for an new image"

    self image depth > 8 ifTrue: [colorMapMode := ''. self listOfColors removeAll. ^nil].
    colorMapMode := self image depth printString, '-plane'.
    self listOfColors isEmpty
    ifTrue:
    [   
         self colorMapMode: colorMapMode value: nil
    ].                               
    self imageEditView selectColors: (Array with: (self listOfColors at: 1) with: (self listOfColors at: 2 ifAbsent: [self listOfColors at: 1])).
    (self image mask notNil and: [(self listOfColors includes: (Color basicNew setColorId:0)) not])
    ifTrue:
    [         
        colorMapMode := colorMapMode, ' + mask'.
        self listOfColors addFirst: (Color basicNew setColorId:0).
        self imageEditView selectColors: (Array with: (self listOfColors at: 2 ifAbsent: [self listOfColors at: 1]) with: (self listOfColors at: 1)).
    ].
    self selectionOfColor value: 0.
    self selectionOfColor value: (self listOfColors indexOf: self imageEditView selectedColor).
!

updateForNoneImage
    "updates channels and view, if image is loaded"

    self imageIsLoaded value: false.
    self listOfColors removeAll.
    self imagePreView image: nil




!

updateLabelsAndHistory
    "updates labels and history, if something has changed"

    self imageIsLoaded value: self image notNil.

    self image isNil ifTrue: [^nil].

    self updateInfoLabel.

    self imageEditView resourceMessage asCollectionOfWords size = 2
        ifTrue: [self addToHistory: self imageEditView resourceMessage -> #loadFromMessage:].

    self image fileName notNil
        ifTrue: [self addToHistory: self image fileName -> #loadFromFile:].



! !

!ImageEditor methodsFor:'help'!

defaultInfoLabel
    "returns the default info label"

    (self imageEditView resourceClass isSymbol and: [(Smalltalk at: self imageEditView resourceClass) isClass])
    ifTrue:
    [
        ^self imageEditView resourceClass, ' >> ', self imageEditView resourceSelector
    ].
    ^'No class and selector defined.'
!

openDocumentation
    "opens the documentation file of the Image Editor"

    self openHTMLDocument: 'tools/uipainter/ImageEditor.html'

! !

!ImageEditor methodsFor:'loading'!

loadFromFile: aFileName
    "loads an image from aFileName and sets up color map list and other info labels"

    (self imageEditView loadFromFile: aFileName) notNil
    ifTrue:
    [
        self image notNil
        ifTrue:
        [          
            self image colorMap notNil
            ifTrue:
            [
                self listOfColors contents: self image usedColors asSet.
            ]
            ifFalse:
            [
                self listOfColors removeAll.
            ].
            self findColorMapMode.     
            self updateLabelsAndHistory.
        ]
        ifFalse:
        [
            self updateForNoneImage
        ]
    ]
!

loadFromImage: anImage
    "loads an image from anImage and sets up color map list and other info labels"

    self imageEditView image: anImage.
    self image notNil
    ifTrue:
    [    
        self image colorMap notNil
        ifTrue:
        [
            self listOfColors contents: self image usedColors asSet asOrderedCollection.
        ]
        ifFalse:
        [
            self listOfColors removeAll.
        ].
        self findColorMapMode.
        self updateLabelsAndHistory.
    ]
    ifFalse:
    [
        self updateForNoneImage
    ]
!

loadFromMessage: aMessage
    "loads an image by evaluating aMessage and sets up color map list and other info labels"

    (self imageEditView loadFromMessage: aMessage) notNil
    ifTrue:
    [
        self image notNil
        ifTrue:
        [
            self listOfColors contents: self image usedColors asSet asOrderedCollection.
            self findColorMapMode.
            self updateLabelsAndHistory.
        ]
        ifFalse:
        [
            self updateForNoneImage
        ]
    ]
!

loadFromOrPrepareForMessage: aMessage
    "loads an image by evaluating aMessage;
     if no image could extract from aMessage; do set the class and the selector from 
     the aMessage for a saving at the end of editing"

    (self imageEditView loadFromMessage: aMessage) notNil
    ifTrue:
    [
        self listOfColors contents: self image usedColors asSet asOrderedCollection.
        self findColorMapMode.
    ]
    ifFalse:
    [
        self imageEditView resourceMessage: aMessage.
    ].
    self updateLabelsAndHistory.
! !

!ImageEditor methodsFor:'menu modes'!

colorMapMode: aMode
    "returns whether colorMapMode is equal to aMode"

    ^colorMapMode = aMode
!

colorMapMode: aMode value: aValue
    "calculates a new color map for the image from aMode"

    |depth newColorMap newImage image newColors realColorMap oldFileName| 

    newColorMap := self class listOfColorMaps at: aMode.
    depth := (newColorMap size log: 2) asInteger. 
    newImage := (Image implementorForDepth: depth) new.  
    oldFileName := self image fileName.
    Object errorSignal handle:
    [:ex|
        Object errorSignal handle:
        [:ex|
            ^self warn: 'Convertation failed!!'
        ]
        do:
        [
            self image colorsFromX:0 y:0 toX:(self image width-1) y:(self image height-1) do:
            [:x :y :clr |
                (newColorMap includes: clr)
                    ifTrue: [self image colorAtX:x y:y put:clr]
                    ifFalse: [self image colorAtX:x y:y put: self image colorMap first]
            ].
            image := newImage fromImage: self image.
        ].
    ] 
    do:
    [ 
        image := newImage fromImage: self image
    ].
    (String fromString: aMode) reverse readStream nextWord reverse = 'mask'
    ifTrue:
    [
        image mask isNil
        ifTrue:
        [
            image mask: (Depth1Image fromImage: (image asThresholdMonochromeImage: 0.1)). 
        ].
    ]
    ifFalse:
    [ 
        image mask: nil.
    ]. 
    realColorMap := OrderedCollection new.
    image realColorMap do:
    [:clr|
        (realColorMap includes: clr) ifFalse: [realColorMap add: clr]
    ].
    newColors := realColorMap copyFrom: 1 to: (newColorMap size min: realColorMap size).
    newColorMap do:
    [:clr|
        ((newColors size < newColorMap size) and: [(newColors includes: clr) not]) 
        ifTrue:
        [      
            newColors add: clr
        ]
    ].       
    image colorMap: newColors.
    colorMapMode := aMode.
    (self imageEditView image: image) notNil
    ifTrue:
    [
        self image fileName: oldFileName.
        self listOfColors contents: image colorMap.
        self findColorMapMode.
        self updateLabelsAndHistory.
    ]
!

editMode: aMode
    "returns whether editMode is equal to aMode"

    ^self imageEditView editMode = aMode
!

editMode: aMode value: aValue
    "sets the editMode to aMode"

    self imageEditView editMode: aMode
!

mouseKeyColorMode: aMode
    "sets the mouseKeyColorMode to aMode"

    ^self imageEditView mouseKeyColorMode = aMode
!

mouseKeyColorMode: aMode value: aValue
    "sets the mouseKeyColorMode to aMode"

    self imageEditView mouseKeyColorMode: aMode.

    self selectionOfColor value: (self listOfColors indexOf: self imageEditView selectedColor).
! !

!ImageEditor methodsFor:'queries'!

preferredExtent
    "returns the preferred extent"

    ^super preferredExtent max: (Screen current width//3)@(Screen current height//3.5)


! !

!ImageEditor methodsFor:'selection'!

magnification
    "returns the magnification of the image"

    (builder componentAt: #imageEditView) isNil ifTrue: [^1].
    ^self imageEditView magnification x
!

magnification: aValue
    "sets the magnification of the image"

    |magnification|
    magnification := aValue asInteger asPoint.
    (magnification = self imageEditView magnification or: [magnification = (0@0)]) ifTrue: [^nil].
    self imageEditView magnification: magnification
!

selectedColorIndex
    "returns the index of the selected color"

    ^selectedColorIndex
!

selectedColorIndex: anIndex
    "sets the index of the selected color"

    selectedColorIndex := anIndex.    
    self imageEditView selectedColor: (self listOfColors at: anIndex ifAbsent: [^nil])

! !

!ImageEditor methodsFor:'startup / release'!

closeRequest
    "close request"

    self imageEditView checkModified ifTrue:[super closeRequest]

!

open
    "after opening, sets the masterApplication of the imageEditView to self"

    super open.

    self imageEditView masterApplication: self
!

postOpenWith:aBuilder
    "after opening, sets the masterApplication of the imageEditView to self;
     evaluate the postOpenAction"

    self imageEditView masterApplication: self.
    postOpenAction notNil ifTrue: [postOpenAction value].

    super postOpenWith:aBuilder
! !

!ImageEditor methodsFor:'user actions - editing'!

doBrowseClass
    "opens a System Browser on the resourceClass and the resourceSelector"

    SystemBrowser openInClass: (Smalltalk at: self imageEditView resourceClass ifAbsent: [^nil]) class selector: self imageEditView resourceSelector
!

doFlipHorizontal
    "flips horizontally current image"

    self imageEditView flipHorizontal
!

doFlipVertical
    "flips vertically current image"

    self imageEditView flipVertical
!

doMagnifyDown
    "magnifies current image one step down"

    self valueOfMagnification value > 1
    ifTrue:
    [
        self valueOfMagnification value: self valueOfMagnification value - 1
    ]
!

doMagnifyImage
    "magnifies current image"

    self imageEditView magnifyImage.   
    self updateInfoLabel
!

doMagnifyUp
    "magnifies current image one step up"

    self valueOfMagnification value < 99
    ifTrue:
    [
        self valueOfMagnification value: self valueOfMagnification value + 1
    ]
!

doNegativeImage
    "negates current image by negating the color map"

    self imageEditView negativeImage.
    self listOfColors removeAll.
    self findColorMapMode     
!

doResizeImage
    "resizes current image"

    self imageEditView resizeImage.   
    self updateInfoLabel
!

doRotateImage
    "rotates current image"

    self imageEditView rotateImage.
    self updateInfoLabel
!

doUndo
    "reverses last edit action"

    self imageEditView undo
! !

!ImageEditor methodsFor:'user actions - loading'!

doLoadFromClass
    "opens a dialog for loading an image from class and a (resource-) selector"

    (self imageEditView loadFromClass) notNil
    ifTrue:
    [
        self image notNil
        ifTrue:
        [
            self listOfColors contents: self image usedColors asSet asOrderedCollection.
            self findColorMapMode.
            self updateLabelsAndHistory
        ]
        ifFalse:
        [
            self updateForNoneImage
        ]
    ]
!

doLoadFromFile
    "opens a dialog for loading an image from a file"

    self loadFromFile:
        (FileSelectionBrowser
            request: 'Load Image From'
            fileName: (self image notNil ifTrue: [self image fileName] ifFalse: [nil])
            withFileFilters: FileSelectionBrowser loadImageFileNameFilters)
!

doNewImage
    "opens a dialog with choices of size and color map for creating a new image"

    |aspects|

    aspects  := IdentityDictionary new
        at:#listOfSizes         put: self class listOfDefaultSizes asValue;
        at:#listOfColorMaps     put: self class listOfColorMaps keys asSortedCollection asValue;
        at:#selectionOfSize     put: self class listOfDefaultSizes first copy asValue;
        at:#selectionOfColorMap put: self class listOfColorMaps keys asSortedCollection first asValue;
        yourself.

    (self openDialogInterface:#dialogSpecForNewImage withBindings:aspects)
    ifTrue:
    [
        |width height cMap imageClass image|

        width  := 128 min: (Integer readFromString: ((aspects at:#selectionOfSize) value upTo: $x) onError:[24]).
        height := 128 min: (Integer readFromString: ((aspects at:#selectionOfSize) value copy reverse upTo: $x) reverse onError:[24]).

        cMap       := (self class listOfColorMaps at: (colorMapMode := (aspects at:#selectionOfColorMap) value)).
        imageClass := Image implementorForDepth: ((cMap size log: 2) asInteger).
        image      := imageClass width: width height: height fromArray: (ByteArray new: width*height).

        (colorMapMode copy reverse readStream nextWord reverse = 'mask')
        ifTrue:
        [
            image mask: (Depth1Image width: width height: height depth: 1 fromArray: (ByteArray new: width*height)) clearMaskedPixels
        ].
        image colorMap: cMap.
        image fillRectangleX:0 y:0 width:width height:height with:Color white.
        (self imageEditView image: image) notNil
        ifTrue:
        [
            self listOfColors contents: cMap.
            self findColorMapMode.
            self updateLabelsAndHistory
        ]
    ]
! !

!ImageEditor methodsFor:'user actions - saving'!

doPrint
    "prints current image on the current printer"

    self imageEditView print
!

doSaveImageFile
    "saves current image to current file"

    self imageEditView save
!

doSaveImageFileAs
    "opens a dialog for saving current image to a file"

    self imageEditView saveImageFileAs.
    self updateLabelsAndHistory
!

doSaveImageMaskFileAs
    "opens a dialog for saving mask of current image to a file"

    self imageEditView saveImageMaskFileAs
!

doSaveMethod
    "saves current image on current class and selector"

    self imageEditView saveMethod notNil
    ifTrue:
    [
        self updateLabelsAndHistory
    ]
!

doSaveMethodAs
    "opens a dialog for saving current image on a class and a selector"

    self imageEditView saveMethodAs notNil
    ifTrue:
    [
        self updateLabelsAndHistory
    ]
! !

!ImageEditor class methodsFor:'documentation'!

version
    ^ '$Header$'
! !