ImageEditor.st
author Claus Gittinger <cg@exept.de>
Sat, 08 Aug 1998 13:35:53 +0200
changeset 943 b9ba76358fbd
parent 942 4b47d3d652ad
child 945 cb689f3971ab
permissions -rw-r--r--
help texts

"
 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:'imageEditView 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:Icon andSelector:#startIcon

    [see also:]
        ImageEditView Image

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

!ImageEditor class methodsFor:'instance creation'!

openModalOnClass: aClass andSelector: aSelector
    "opens modal a 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].      
    aClass isNil    ifTrue: [className := ''].      

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

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

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

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

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

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

    ^self open loadFromFile: aFileName
!

openOnImage: anImage
    "opens a Image Editor on anImage"
    "
     self openOnImage: Icon startIcon
    "

    ^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 redByte: 127 greenByte:   0 blueByte:   0);
        add: (Color redByte:   0 greenByte: 127 blueByte:   0);
        add: (Color redByte:   0 greenByte:   0 blueByte: 127);
        add: (Color redByte:   0 greenByte: 127 blueByte: 127);
        add: (Color redByte: 127 greenByte: 127 blueByte:   0);
        add: (Color redByte: 127 greenByte:   0 blueByte: 127);
        add: (Color redByte: 127 greenByte: 127 blueByte: 127);
        add: (Color redByte: 170 greenByte: 170 blueByte: 170).

    0 to: 5 do:
    [:r|                                                    
        0 to: 5 do:         
        [:g|
            0 to: 5 do:                             
            [:b|
                colorMap add: (Color redByte: (r*255//5) ceiling greenByte: (g*255//5) ceiling blueByte: (b*255//5) ceiling)
            ]
        ]
    ].

    1 to: 25 do:
    [:g|                             
        colorMap add: (Color redByte: (g*255//26) ceiling greenByte: (g*255//26) ceiling blueByte: (g*255//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"

    ^#('8x8' '16x16' '22x22' '32x32' '48x48' '64x64')

    "Modified: / 31.7.1998 / 01:57:34 / cg"
! !

!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
'ColorMap functions.'

#colorMap1
'Convert to depth-1 image.'

#colorMap2
'Convert to depth-2 image.'

#colorMap4
'Convert to depth-4 image.'

#colorMap8
'Convert to depth-8 image.'

#colorMap1M
'Convert to depth-1 image plus mask.'

#colorMap2M
'Convert to depth-2 image plus mask.'

#colorMap4M
'Convert to depth-4 image plus mask.'

#colorMap8M
'Convert to depth-8 image plus mask.'

#compressColormap
'Remove unneeded entries from the colorMap.'

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

#drawModeBox
'Switches to box-drawing mode.'

#drawModeCopy
'Switches to area-copy mode.'

#drawModeFill
'Switches to flood-fill mode.'

#drawModeFilledBox
'Switches to filled-box drawing mode.'

#drawModePaste
'Switches to paste mode.'

#drawModePasteUnder
'Switches to paste under mode.'

#drawModePoint
'Switches to point-drawing mode.'

#editFlipHorizontal
'Flips the image horizontally.'

#editFlipVertical
'Flips the image vertically.'

#editMagnifyImage
'Magnify the image.'

#editNegate
'Invert the images colors.'

#editResize
'Resize the image (preserving the old image).'

#editRotate
'Rotate the image.'

#fileGrabImage
'Pick an image from the screen.'

#fileLoadFromClass
'Select and load an image from a resource method.'

#fileLoadFromFile
'Select and load an image from a file.'

#fileNewImage
'Create a new image'

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

#fileSaveAs
'Save the image to a file.'

#fileSaveMaskAs
'Save the mask of the image to a file.'

#fileSaveMethod
'Save the image as resource method in the current class and selector.'

#fileSaveMethodAs
'Save the image as resource method in a class.'

#historyMenuItem
'Edit this image.'

#magnificationNumber
'Shows the current magnification.'

#magnifyImageDown
'Decrease magnification.'

#magnifyImageUp
'Increase magnification.'

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

#previewView
'Shows a preview of the image.'

#settingsGridMagnification
'Change the grid magnification of the edit view.'

)

    "Modified: / 29.7.1998 / 22:27:36 / cg"
! !

!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 102 0 64 0 402 0 182 0)
              #label: 'New Image'
              #min: #(#Point 10 10)
              #max: #(#Point 1152 900)
              #bounds: #(#Rectangle 102 64 403 183)
              #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 76 0)
                              #component: 
                               #(#SpecCollection
                                  #collection: 
                                   #(
                                     #(#ComboBoxSpec
                                        #name: 'defaultSizesComboBox'
                                        #layout: #(#LayoutFrame 11 0.0 25 0.0 108 0.0 48 0.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 76 0)
                              #component: 
                               #(#SpecCollection
                                  #collection: 
                                   #(
                                     #(#ComboListSpec
                                        #name: 'colorMapComboBox'
                                        #layout: #(#LayoutFrame 15 0.0 24 0.0 163 0.0 47 0.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
                )
              )
          )
      )

    "Modified: / 31.7.1998 / 01:56:31 / cg"
!

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 362 0 221 0 861 0 566 0)
              #label: 'Image Editor'
              #min: #(#Point 400 320)
              #max: #(#Point 1152 900)
              #bounds: #(#Rectangle 362 221 862 567)
              #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
                                                            #dataList: #listOfColors
                                                            #has3Dsepartors: true
                                                            #has3Dseparators: true
                                                            #verticalSpacing: 1
                                                            #columns: 
                                                             #(
                                                               #(#DataSetColumnSpec
                                                                  #rendererType: #rowSelector
                                                                  #backgroundSelector: #yourself
                                                              )
                                                               #(#DataSetColumnSpec
                                                                  #label: 'R'
                                                                  #labelAlignment: #left
                                                                  #columnAlignment: #right
                                                                  #editorType: #InputField
                                                                  #type: #number
                                                                  #model: #redFromColor:
                                                                  #writeSelector: #redAtColor:put:
                                                                  #canSelect: false
                                                              )
                                                               #(#DataSetColumnSpec
                                                                  #label: 'G'
                                                                  #labelAlignment: #left
                                                                  #columnAlignment: #right
                                                                  #editorType: #InputField
                                                                  #type: #number
                                                                  #model: #greenFromColor:
                                                                  #writeSelector: #greenAtColor:put:
                                                                  #canSelect: false
                                                              )
                                                               #(#DataSetColumnSpec
                                                                  #label: 'B'
                                                                  #labelAlignment: #left
                                                                  #columnAlignment: #right
                                                                  #editorType: #InputField
                                                                  #type: #number
                                                                  #model: #blueFromColor:
                                                                  #writeSelector: #blueAtColor:put:
                                                                  #canSelect: false
                                                              )
                                                            )
                                                            #columnAdaptor: #colorColumnAdaptor
                                                        )
                                                      )
                                                  )
                                              )
                                               #(#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)
                                        #labelChannel: #imageInfoHolder
                                        #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: #numberInRange
                                        #acceptOnReturn: false
                                        #acceptOnTab: false
                                        #numChars: 2
                                        #minValue: 1
                                        #maxValue: 99
                                    )
                                  )
                              )
                              #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'
                #translateLabel: true
                #activeHelpKey: #about
                #labelImage: #(#ResourceRetriever nil #menuIcon)
                #submenuChannel: #menuAbout
            )
             #(#MenuItem
                #label: 'File'
                #translateLabel: true
                #activeHelpKey: #file
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'New...'
                          #translateLabel: true
                          #value: #doNewImage
                          #activeHelpKey: #fileNewImage
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Load...'
                          #translateLabel: true
                          #value: #doLoadFromClass
                          #activeHelpKey: #fileLoadFromClass
                      )
                       #(#MenuItem
                          #label: 'Load From File...'
                          #translateLabel: true
                          #value: #doLoadFromFile
                          #activeHelpKey: #fileLoadFromFile
                      )
                       #(#MenuItem
                          #label: 'Grab From Screen'
                          #translateLabel: true
                          #value: #grabScreenImage
                          #activeHelpKey: #fileGrabImage
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Save'
                          #translateLabel: true
                          #value: #doSaveMethod
                          #activeHelpKey: #fileSaveMethod
                          #enabled: #imageIsLoadedAndClassDefined
                      )
                       #(#MenuItem
                          #label: 'Save As...'
                          #translateLabel: true
                          #value: #doSaveMethodAs
                          #activeHelpKey: #fileSaveMethodAs
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Save To File...'
                          #translateLabel: true
                          #value: #doSaveImageFileAs
                          #activeHelpKey: #fileSaveAs
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: 'Save Mask To File...'
                          #translateLabel: true
                          #value: #doSaveImageMaskFileAs
                          #activeHelpKey: #fileSaveMaskAs
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Print'
                          #translateLabel: true
                          #value: #doPrint
                          #activeHelpKey: #filePrint
                          #enabled: #imageIsLoaded
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Browse Class'
                          #translateLabel: true
                          #value: #doBrowseClass
                          #activeHelpKey: #fileBrowseClass
                          #enabled: #hasClassDefined
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Exit'
                          #translateLabel: true
                          #value: #closeRequest
                          #activeHelpKey: #fileExit
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Edit'
                #translateLabel: true
                #activeHelpKey: #edit
                #enabled: #imageIsLoaded
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Undo'
                          #translateLabel: true
                          #value: #doUndo
                          #activeHelpKey: #editUndo
                          #enabled: #valueOfCanUndo
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Flip - Vertical'
                          #translateLabel: true
                          #value: #doFlipVertical
                          #activeHelpKey: #editFlipVertical
                      )
                       #(#MenuItem
                          #label: 'Flip - Horizontal'
                          #translateLabel: true
                          #value: #doFlipHorizontal
                          #activeHelpKey: #editFlipHorizontal
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Resize...'
                          #translateLabel: true
                          #value: #doResizeImage
                          #activeHelpKey: #editResize
                      )
                       #(#MenuItem
                          #label: 'Magnify...'
                          #translateLabel: true
                          #value: #doMagnifyImage
                          #activeHelpKey: #editMagnifyImage
                      )
                       #(#MenuItem
                          #label: 'Rotate...'
                          #translateLabel: true
                          #value: #doRotateImage
                          #activeHelpKey: #editRotate
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'ColorMap'
                #translateLabel: true
                #activeHelpKey: #colorMap
                #enabled: #imageIsLoaded
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: '8-Plane'
                          #translateLabel: true
                          #activeHelpKey: #colorMap8
                          #argument: '8-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '4-Plane'
                          #translateLabel: true
                          #activeHelpKey: #colorMap4
                          #argument: '4-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '2-Plane'
                          #translateLabel: true
                          #activeHelpKey: #colorMap2
                          #argument: '2-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '1-Plane'
                          #translateLabel: true
                          #activeHelpKey: #colorMap1
                          #argument: '1-plane'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: '8-Plane + Mask'
                          #translateLabel: true
                          #activeHelpKey: #colorMap8M
                          #argument: '8-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '4-Plane + Mask'
                          #translateLabel: true
                          #activeHelpKey: #colorMap4M
                          #argument: '4-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '2-Plane + Mask'
                          #translateLabel: true
                          #activeHelpKey: #colorMap2M
                          #argument: '2-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '1-Plane + Mask'
                          #translateLabel: true
                          #activeHelpKey: #colorMap1M
                          #argument: '1-plane + mask'
                          #indication: #colorMapMode:value:
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Compress colormap'
                          #translateLabel: true
                          #value: #compressColorMap
                          #activeHelpKey: #compressColormap
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'Settings'
                #translateLabel: true
                #submenu: 
                 #(#Menu
                    
                     #(
                       #(#MenuItem
                          #label: 'Grid Magnification...'
                          #translateLabel: true
                          #value: #doChangeGridMagnification
                          #activeHelpKey: #settingsGridMagnification
                      )
                       #(#MenuItem
                          #label: '-'
                      )
                       #(#MenuItem
                          #label: 'Fonts'
                          #translateLabel: true
                          #submenuChannel: #menuFont
                      )
                    ) nil
                    nil
                )
            )
             #(#MenuItem
                #label: 'History'
                #translateLabel: true
                #activeHelpKey: #history
                #submenuChannel: #menuHistory
            )
             #(#MenuItem
                #label: 'Help'
                #translateLabel: true
                #startGroup: #right
                #activeHelpKey: #help
                #submenuChannel: #menuHelp
            )
          ) nil
          nil
      )

    "Modified: / 31.7.1998 / 02:04:49 / cg"
!

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 #Icon #newIcon)
            )
             #(#MenuItem
                #label: 'loadFromClass'
                #isButton: true
                #value: #doLoadFromClass
                #activeHelpKey: #fileLoadFromClass
                #labelImage: #(#ResourceRetriever #Icon #loadIcon)
            )
             #(#MenuItem
                #label: 'saveMethod'
                #isButton: true
                #value: #doSaveMethod
                #activeHelpKey: #fileSaveMethod
                #enabled: #imageIsLoaded
                #labelImage: #(#ResourceRetriever #Icon #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:
            )
             #(#MenuItem
                #label: 'Paste Under'
                #activeHelpKey: #drawModePasteUnder
                #enabled: #imageIsLoaded
                #argument: 'pasteUnder'
                #indication: #editMode:value:
            )
          ) nil
          nil
      )
! !

!ImageEditor methodsFor:'accessing'!

image
    "returns the current editing image"

    ^imageEditView image
!

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

    postOpenAction := anAction
! !

!ImageEditor methodsFor:'accessing - views'!

colorDataSetView
    "returns the view of the colormap"

    ^(builder componentAt: #colorDataSetView)

    "Created: / 26.7.1998 / 12:02:14 / cg"
!

coordLabel
    "returns the view the coord label"

    ^builder componentAt: #coordLabel
!

imageEditView
    "returns the view of the image"

    ^imageEditView := (builder componentAt: #imageEditView) subViews first 
!

imagePreView
    "returns the preview of the image"

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

!ImageEditor methodsFor:'aspects'!

activityInfoHolder
    ^ self valueOfInfoLabel

    "Modified: / 29.7.1998 / 18:49:03 / cg"
!

colorColumnAdaptor
    ^ self

    "Created: / 26.7.1998 / 12:17:03 / cg"
!

hasClassAndSelectorDefined
    ^ [(Smalltalk at:(imageEditView resourceClass) ifAbsent:nil) notNil
       and:[imageEditView resourceSelector notNil]]

    "Created: / 31.7.1998 / 02:02:54 / cg"
!

hasClassDefined
    ^ [(Smalltalk at:(imageEditView resourceClass) ifAbsent:nil) notNil]

    "Created: / 31.7.1998 / 02:02:22 / cg"
!

imageInfoHolder
    |holder|
    (holder := builder bindingAt:#imageInfoHolder) isNil ifTrue:[
        builder aspectAt:#imageInfoHolder put:(holder :=  '' asValue).
    ].
    ^ holder

    "Modified: / 29.7.1998 / 18:32:08 / cg"
!

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
!

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

    ^ [self hasClassAndSelectorDefined value
       and:[self imageIsLoaded value]]

    "Created: / 31.7.1998 / 02:04:18 / cg"
!

listOfColors
    "returns the list of colors in a List"

    |holder|
    (holder := builder bindingAt:#listOfColors) isNil ifTrue:[
        builder aspectAt:#listOfColors put:(holder :=  List new).
        holder addDependent:self.
    ].
    ^ 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 a 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
    ].                               
    imageEditView selectColors: (Array with: (self listOfColors at: 1) with: (self listOfColors at: 2 ifAbsent: [self listOfColors at: 1])).

    self image mask notNil
    ifTrue:
    [         
        colorMapMode := colorMapMode, ' + mask'.
        (self listOfColors detect: [:clr| clr = (Color basicNew setColorId:0)] ifNone: nil) isNil
        ifTrue:
        [
            self listOfColors addFirst: (Color basicNew setColorId:0).
            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: imageEditView selectedColor).
!

update:something with:aParameter from:changedObject
    super update:something with:aParameter from:changedObject

!

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.

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

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



! !

!ImageEditor methodsFor:'data access'!

blueAtColor:aColor put:newBlue
    "helper used to return a new row element, when blue is changed"

    |byte|

    aColor isNil ifTrue:[^ aColor].  "/ mask cannot be changed
    byte := newBlue clampBetween:0 and:255.
    ^ Color redByte:(aColor redByte) greenByte:(aColor greenByte) blueByte:byte
!

blueFromColor:aColor
    "helper used to access a color as a row in the dataSet view"

    aColor isNil ifTrue:[^ 'none'].
    ^ aColor blueByte ? 'mask'

    "Created: / 26.7.1998 / 12:30:35 / cg"
    "Modified: / 31.7.1998 / 01:11:18 / cg"
!

greenAtColor:aColor put:newGreen
    "helper used to return a new row element, when green is changed"

    |byte|

    aColor isNil ifTrue:[^ aColor].  "/ mask cannot be changed
    byte := newGreen clampBetween:0 and:255.
    ^ Color redByte:(aColor redByte) greenByte:byte blueByte:(aColor blueByte)
!

greenFromColor:aColor
    "helper used to access a color as a row in the dataSet view"

    aColor isNil ifTrue:[^ 'none'].
    ^ aColor greenByte ? 'mask'

    "Created: / 26.7.1998 / 12:30:29 / cg"
    "Modified: / 31.7.1998 / 01:11:31 / cg"
!

redAtColor:aColor put:newRed
    "helper used to return a new row element, when red is changed"

    |byte|

    aColor isNil ifTrue:[^ aColor].  "/ mask cannot be changed
    byte := newRed clampBetween:0 and:255.
    ^ Color redByte:byte greenByte:(aColor greenByte) blueByte:(aColor blueByte)
!

redFromColor:aColor
    "helper used to access a color as a row in the dataSet view"

    aColor isNil ifTrue:[^ 'none'].
    ^ aColor redByte ? 'mask'

    "Modified: / 31.7.1998 / 01:11:35 / cg"
! !

!ImageEditor methodsFor:'help'!

defaultInfoLabel
    "returns the default info label"

    (imageEditView resourceClass isSymbol and: [(Smalltalk at: imageEditView resourceClass) isClass])
    ifTrue:
    [
        ^imageEditView resourceClass, ' >> ', 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"

    (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"

    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"

    (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"

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

!ImageEditor methodsFor:'menu modes'!

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

    ^colorMapMode = aMode
!

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

    ^ imageEditView notNil and:[imageEditView editMode = aMode]
!

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

    imageEditView editMode: aMode
!

mouseKeyColorMode: aMode
    "sets the mouseKeyColorMode to aMode"

    ^imageEditView mouseKeyColorMode = aMode
!

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

    imageEditView mouseKeyColorMode: aMode.

    self selectionOfColor value: (self listOfColors indexOf: 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"

    self imageEditView isNil ifTrue: [^1].
    ^imageEditView magnification x
!

magnification: aValue
    "sets the magnification of the image"

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

selectedColorIndex
    "returns the index of the selected color"

    ^selectedColorIndex
!

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

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

! !

!ImageEditor methodsFor:'startup / release'!

closeRequest
    "close request"

    imageEditView checkModified ifTrue:[super closeRequest]

!

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

    super open.

    imageEditView := (builder componentAt: #imageEditView) subViews first.
    imageEditView masterApplication: self
!

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

    imageEditView masterApplication: self.
    imageEditView imageInfoHolder:(self imageInfoHolder).
    imageEditView activityInfoHolder:(self activityInfoHolder).

    postOpenAction notNil ifTrue: [postOpenAction value].

    super postOpenWith:aBuilder

    "Modified: / 29.7.1998 / 18:49:20 / cg"
! !

!ImageEditor methodsFor:'user actions - colormap'!

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

    |depth numColors newColorMap newImage 
     oldImage image newColors realColorMap oldFileName
     usedColors useNearest usageCounts tmpBits tmpMap quest| 

    self withExecuteCursorDo:[
        oldImage := self image.

        newColorMap := self class listOfColorMaps at:aMode.
        depth       := (newColorMap size log: 2) asInteger. 

        useNearest := false.
        depth == 1 ifTrue:[
            quest := 'Keep colormap (or use standard B&W)'
        ] ifFalse:[
            quest := 'Keep colormap (or use standard)'
        ].
        (self confirm:(resources string:quest)) ifTrue:[
            "/ keep the colormap
            depth > oldImage depth ifTrue:[
                "/ easy - simply copy the part
                numColors := 1 bitShift:oldImage depth.
                0 to:numColors-1 do:[:pixel |
                    newColorMap at:(pixel+1) put:(oldImage colorFromValue:pixel)
                ].
            ] ifFalse:[
                "/ see if all used color fit the new colormap
                usedColors := oldImage usedColors asArray.
                usedColors size <= (1 bitShift:depth) ifTrue:[
                    "/ yea - just install them
                    usedColors keysAndValuesDo:[:idx :clr |
                        newColorMap at:idx put:clr
                    ]
                ] ifFalse:[
                    "/ copy over those that are most often used.
                    oldImage depth ~~ 8 ifTrue:[
                        tmpBits := ByteArray uninitializedNew:(oldImage width*oldImage height).
                        oldImage bits
                            expandPixels:(oldImage depth)
                            width:oldImage width
                            height:oldImage height 
                            into:tmpBits
                            mapping:nil.
                    ] ifFalse:[
                        tmpBits := oldImage bits
                    ].
                    usageCounts := tmpBits usageCounts.
                    tmpMap := Array new:usageCounts size.
                    oldImage colorMap asArray keysAndValuesDo:[:i :clr |
                        tmpMap at:i put:clr
                    ].
                    usageCounts sort:[:a :b | a > b] with:tmpMap.

                    1 to:(1 bitShift:depth) do:[:idx |
                        newColorMap at:idx put:(tmpMap at:idx)
                    ].

                    useNearest := Dialog 
                                        confirmWithCancel:(resources 
                                                              string:'Image requires %1 colors.\ColorMap has only space for %2\\Use nearest (or map to first color) ?'
                                                              with:usedColors size
                                                              with:(1 bitShift:depth)) withCRs
                                        labels:(resources array:#('cancel' 'first' 'nearest')).
                    useNearest isNil ifTrue:[
                        ^ self   "/ cancel
                    ].
                ]
            ]
        ] ifFalse:[
            "/ standard colormap
            usedColors := oldImage usedColors.
            (usedColors conform:[:clr | newColorMap includes:clr]) ifFalse:[
                useNearest := Dialog 
                                    confirmWithCancel:(resources 
                                                           string:'Not all colors are present in the new colormap.\\Map missing ones to nearest (or map to first color) ?'
                                                       ) withCRs
                                    labels:(resources string:#('cancel' 'first' 'nearest')).
                useNearest isNil ifTrue:[
                    ^ self   "/ cancel
                ].
            ].
        ].

        imageEditView makeUndo.

        newImage    := (Image implementorForDepth: depth) new.  
        oldFileName := oldImage fileName.

        Object errorSignal handle:
        [:ex|
            Object errorSignal handle:
            [:ex|
                imageEditView undo.
                ^self warn:(resources string:'Conversion failed !!')
            ]
            do:
            [
                newImage := (Image implementorForDepth: depth) new.
                newImage width:oldImage width height:oldImage height depth:depth.
                newImage colorMap:newColorMap.
                newImage photometric:#palette.
                newImage bits:(ByteArray new:(newImage bytesPerRow * newImage height)).
            
                oldImage colorsFromX:0 y:0 toX:(oldImage width-1) y:(oldImage height-1) do:
                [:x :y :clr |
                    |newColor|

                    (newColorMap includes:clr)
                        ifTrue: [newColor := clr]
                        ifFalse: [
                            useNearest ifTrue:[
                                newColor := clr nearestIn:newColorMap
                            ] ifFalse:[
                                newColor := oldImage colorMap first
                            ]
                        ].
                    newImage colorAtX:x y:y put:newColor.
                ].
                image := newImage
            ].
        ] 
        do:
        [ 
            image := newImage fromImage: oldImage
        ].

        (aMode asString endsWith:'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.            

        (imageEditView image: image) notNil ifTrue:
        [
            image fileName: oldFileName.
            self listOfColors contents: image colorMap.
            self findColorMapMode.
            self updateLabelsAndHistory.
        ]
    ]

    "Modified: / 31.7.1998 / 18:53:26 / cg"
!

compressColorMap
    "calculates a new color map for the image, using only used colors"

    |depth newColorMap newImage oldImage usedColors oldToNew oldBits newBits tmpBits| 

    oldImage := self image.
    depth := oldImage depth.

    oldImage photometric ~~ #palette ifTrue:[
        self information:'Compress colorMap: Only palette images have colormaps.'.
        ^ self
    ].

    usedColors := oldImage usedColors.
    usedColors size == (1 bitShift:depth) ifTrue:[
        self information:'Compress colorMap: All colors are used - no compression.'.
        ^ self
    ].
    usedColors size == oldImage colorMap size ifTrue:[
        self information:'Compress colorMap: Colormap already compressed - no compression.'.
        ^ self
    ].

    imageEditView makeUndo.

    self information:('Compress colorMap: %1 colors used.' bindWith:usedColors size).

    self withExecuteCursorDo:[
        newColorMap := Array new:usedColors size.

        "/ translation table
        oldToNew := ByteArray new:(1 bitShift:depth).
        newColorMap := usedColors asArray.
        oldImage colorMap asArray keysAndValuesDo:[:oldIdx :clr |
            |newPixel|

            (usedColors includes:clr) ifTrue:[
                newPixel := newColorMap indexOf:clr.
                oldToNew at:oldIdx put:newPixel-1.
            ]
        ].

        oldBits := oldImage bits.
        newBits := ByteArray new:(oldBits size).
        depth ~~ 8 ifTrue:[
            "/ expand/compress can only handle 8bits
            tmpBits := ByteArray uninitializedNew:(oldImage width*oldImage height).
            oldBits
                expandPixels:depth
                width:oldImage width
                height:oldImage height 
                into:tmpBits
                mapping:oldToNew.
            tmpBits
                compressPixels:depth 
                width:oldImage width 
                height:oldImage height 
                into:newBits 
                mapping:nil
        ] ifFalse:[
            oldBits
                expandPixels:depth
                width:oldImage width
                height:oldImage height 
                into:newBits
                mapping:oldToNew.
        ].

        newImage := oldImage species new
                        width:oldImage width
                        height:oldImage height
                        depth:depth
                        fromArray:newBits.

        newImage colorMap:newColorMap.  
        newImage fileName:oldImage fileName.
        newImage mask:(oldImage mask copy).

        (imageEditView image:newImage) notNil ifTrue:
        [
            self listOfColors contents: newImage colorMap.
            self findColorMapMode.
            self updateLabelsAndHistory.
        ]
    ]

    "Created: / 28.7.1998 / 20:03:11 / cg"
    "Modified: / 31.7.1998 / 01:13:57 / cg"
! !

!ImageEditor methodsFor:'user actions - editing'!

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

    |cls|

    cls := Smalltalk at:(imageEditView resourceClass) ifAbsent:nil.
    cls isNil ifTrue:[^ self warn:'No Class specified'].
    SystemBrowser 
        openInClass:cls class 
        selector:(imageEditView resourceSelector)

    "Modified: / 31.7.1998 / 02:01:15 / cg"
!

doChangeGridMagnification
    "change grid magnification"

    imageEditView changeGridMagnification
!

doFlipHorizontal
    "flips horizontally current image"

    imageEditView flipHorizontal
!

doFlipVertical
    "flips vertically current image"

    imageEditView flipVertical
!

doMagnifyDown
    "magnifies current image one step down"

    |magHolder mag|

    magHolder := self valueOfMagnification.
    (mag := magHolder value) > 1 ifTrue: [
        magHolder value: mag - 1
    ]

    "Modified: / 26.7.1998 / 20:24:08 / cg"
!

doMagnifyImage
    "magnifies current image"

    imageEditView magnifyImage.   
    self updateInfoLabel
!

doMagnifyUp
    "magnifies current image one step up"

    |magHolder mag|

    magHolder := self valueOfMagnification.
    (mag := magHolder value) < 99 ifTrue: [
        magHolder value: mag + 1
    ]

    "Modified: / 26.7.1998 / 20:23:52 / cg"
!

doNegativeImage
    "negates current image by negating the color map"

    imageEditView negativeImage.
    self listOfColors removeAll.
    self findColorMapMode.     
    imageEditView undoImages removeLast
!

doResizeImage
    "resizes current image"

    imageEditView resizeImage.   
    self updateInfoLabel
!

doRotateImage
    "rotates current image"

    imageEditView rotateImage.
    self updateInfoLabel
!

doUndo
    "reverses last edit action"

    imageEditView undo
! !

!ImageEditor methodsFor:'user actions - loading'!

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

    (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.
        (imageEditView image: image) notNil
        ifTrue:
        [
            self listOfColors contents: cMap.
            self findColorMapMode.
            self updateLabelsAndHistory
        ]
    ]
!

grabScreenImage
    "let user choose an area and grab that are for editing"

    |image img|

    image := Image fromUser.

    image depth > 8 ifTrue:[
        Object errorSignal handle:[:ex |
            self warn:'Could not convert to depth8 image (too many colors)'.
            ^ self
        ] do:[
            image := Depth8Image fromImage:image.
        ]
    ].
    (imageEditView image: image) notNil
    ifTrue:
    [
        self listOfColors contents:(image colorMap).
        self findColorMapMode.
        self updateLabelsAndHistory.
    ]

    "Created: / 29.7.1998 / 21:24:42 / cg"
    "Modified: / 31.7.1998 / 01:14:33 / cg"
! !

!ImageEditor methodsFor:'user actions - saving'!

doPrint
    "prints current image on the current printer"

    imageEditView print
!

doSaveImageFile
    "saves current image to current file"

    imageEditView save
!

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

    imageEditView saveImageFileAs.
    self updateLabelsAndHistory
!

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

    imageEditView saveImageMaskFileAs
!

doSaveMethod
    "saves current image on current class and selector"

    imageEditView saveMethod notNil
    ifTrue:
    [
        self updateLabelsAndHistory
    ]
!

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

    imageEditView saveMethodAs notNil
    ifTrue:
    [
        self updateLabelsAndHistory
    ]
! !

!ImageEditor class methodsFor:'documentation'!

version
    ^ '$Header$'
! !