ImageEditor.st
author Claus Gittinger <cg@exept.de>
Fri, 20 Dec 2002 16:00:55 +0100
changeset 1667 f02a0dad97c5
parent 1656 f5c0716efd78
child 1682 f1b2491c9dc2
permissions -rw-r--r--
added #decimal columnAlignment.

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

"{ Package: 'stx:libtool2' }"

ToolApplicationModel subclass:#ImageEditor
	instanceVariableNames:'imageEditView colorMapMode editMode mouseKeyColorMode
		selectedColorIndex postOpenAction imageSeqNr drawingColormap'
	classVariableNames:'LastDirectory LastSizeString MaskClipboard LastColormapMode
		DefaultRelativeSizes'
	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
    "

    |editor|

    editor := self new.
    editor allButOpen.
    aSelector notNil ifTrue:[
        editor loadFromMessage: aClass name, ' ', aSelector.
    ] ifFalse:[
        editor resourceClass:aClass
    ].
    editor openWindow

    "Modified: / 16.3.1999 / 21:33:49 / cg"
!

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

    |editor|

    editor := self new.
    editor allButOpen.
    editor loadFromFile: aFileName.
    editor openWindow

    "Modified: / 16.3.1999 / 21:33:25 / cg"
!

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

    |editor|

    editor := self new.
    editor allButOpen.
    editor loadFromImage: anImage.
    editor openWindow

    "Modified: / 11.3.1999 / 16:18:33 / cg"
! !

!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'!

flyByHelpSpec
    <resource: #help>

    ^super flyByHelpSpec addPairsFrom:#(

#drawModeBox
'Rectangle'

#drawModeCopy
'Copy'

#drawModeFill
'Flood-Fill'

#drawModeFilledBox
'Filled Rectangle'

#drawModePaste
'Paste'

#drawModePasteUnder
'Paste Under'

#drawModePasteWithMask
'Paste with Mask'

#drawModePoint
'Point'

#drawModeSpecial
'Special Operations'

#fileGrabImage
'Pick from Screen.'

#fileLoadFromClass
'Load from Method'

#fileLoadFromFile
'Load from File.'

#fileNewImage
'New Image'

#filePrint
'Print'

#fileSaveAs
'Save to File.'

#fileSaveMaskAs
'Save to File.'

#fileSaveMethod
'Save as Method'

#fileSaveMethodAs
'Save as Method'

)
!

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.'

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

#colorMap2
'Convert to depth-2 image.'

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

#colorMap4
'Convert to depth-4 image.'

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

#colorMap8
'Convert to depth-8 image.'

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

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

#compressColormap
'Remove unneeded entries from the colorMap.'

#cropAll
'Find and remove all borders.'

#cropBottom
'Find and remove bottom border.'

#cropLeft
'Find and remove left border.'

#cropManual
'Specify border(s) to remove.'

#cropRight
'Find and remove right border.'

#cropTop
'Find and remove top border.'

#drawModeBox
'Rectangle Drawing Mode.'

#drawModeCopy
'Area Copy Mode.'

#drawModeFill
'Flood Fill Mode.'

#drawModeFilledBox
'Filled Rectangle Drawing Mode.'

#drawModePaste
'Paste Mode.'

#drawModePasteUnder
'Paste-Under Mode.'

#drawModePasteWithMask
'Paste-with-Mask Mode.'

#drawModePoint
'Point Drawing Mode.'

#drawModeSpecial
'Special Operations'

#editFlipHorizontal
'Flip the image horizontally.'

#editFlipVertical
'Flip 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.'

#fileSaveButtonImageAs
'Save an image of a button with the image to a file (for html use).'

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

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

#magnificationNumber
'Shows the current magnification.'

#magnifyImageDown
'Decrease magnification.'

#magnifyImageUp
'Increase magnification.'

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

#previewView
'Shows a preview of the image.'

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

)

    "Modified: / 7.9.1998 / 18:20:26 / cg"
! !

!ImageEditor class methodsFor:'image specs'!

copyIcon
    "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."

    "
     self copyIcon inspect
     ImageEditor openOnClass:self andSelector:#copyIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class copyIcon'
        ifAbsentPut:[(Depth2Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@** @@J)UUTB*Z*)@*&**PJ)**$B*Z*)@*&**PJ)**$@@Z*)@@F**P@AUUT@@@@@@b') ; colorMapFromArray:#[0 0 0 0 0 128 255 255 255]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@C? O>@??3??O?<??3??O?<??3??@_<A?0@@@@a') ; yourself); yourself]
!

fillIcon
    "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."

    "
     self fillIcon inspect
     ImageEditor openOnClass:self andSelector:#fillIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class fillIcon'
        ifAbsentPut:[(Depth2Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(2 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@ @@@@*H@@D*(@@DUUP@EAUU@AAEU@@@@U@@DDA@@@@@@@@PP@@@@@@@@@@@@@@b') ; colorMapFromArray:#[0 0 0 255 0 0 255 255 255]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'C @Q@BN@I<@?8C?0[?!!G<@O P\@@ D@@@@@@@@@a') ; yourself); yourself]
!

fillRectIcon
    "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."

    "
     self fillRectIcon inspect
     ImageEditor openOnClass:self andSelector:#fillRectIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class fillRectIcon'
        ifAbsentPut:[(Depth1Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@A@@@@@@@@@@@@@@@@@@@@@@@@@@D@@@@a') ; colorMapFromArray:#[0 0 0 255 0 0]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@C?0O?@?<C?0O?@?<C?0O?@?<C?0@@@@@@@a') ; yourself); yourself]
!

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

    "
     self leftMouseKeyIcon inspect
     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:'@@@@@@@@@@@@@@@@@EJJ@@AR" @@T((@@@@@@@B** @@**(@@J**@@B** @@**(@@J**@@@**@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 0 0 255 255 255]; 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]
!

pasteIcon
    "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."

    "
     self pasteIcon inspect
     ImageEditor openOnClass:self andSelector:#pasteIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class pasteIcon'
        ifAbsentPut:[(Depth4Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(4 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@AU@@@@@CHE@E@2@@@ QDQD@0@@L@@@@@H@@BL#H2L#@@@QDQDQL @@D3L@@@@@@AL3@3M&X@@SL0L0A&@@D3LC@@A @AL3@3@FX@@QDPY&Y&
@@@@@@@@@@@b') ; colorMapFromArray:#[0 0 0 0 0 128 128 128 0 128 128 128 212 208 200 255 255 0 255 255 255]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'C0A?8O?0??C?<O?0??C?>O?8??#?>O?8_? G>@@a') ; yourself); yourself]
!

pasteUnderIcon
    "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."

    "
     self pasteUnderIcon inspect
     ImageEditor openOnClass:self andSelector:#pasteUnderIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class pasteUnderIcon'
        ifAbsentPut:[(Depth4Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(4 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@AU@@@@@CHE@E@2@@@ QDQD@0@@L@@@@@H@@BL#H2L#@@@QDQDQL @@D3L@@@@@@AL3@3M&X@@SL0L0A&@@D3LC@@A @AL3@3@FX@@QDPY&Y&
@@@@@@@@@@@b') ; colorMapFromArray:#[0 0 0 0 0 128 128 128 0 128 128 128 212 208 200 255 255 0 255 255 255]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'C0A?8O?0??C?<O?0??C?>O?8??#?>O?8_? G>@@a') ; yourself); yourself]
!

pasteWithMaskIcon
    "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."

    "
     self pasteWithMaskIcon inspect
     ImageEditor openOnClass:self andSelector:#pasteWithMaskIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class pasteWithMaskIcon'
        ifAbsentPut:[(Depth4Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(4 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@AU@@@@@CHE@E@2@@@ QDQD@0@@L@@@@@H@@BL#H2L#@@@QDQDQL @@D3L@@@@@@AL3@3M&X@@SL0L3A&@@D3LCL0A @AL3@3LFX@@QDPY&Y&
@@@@@@@@@@@b') ; colorMapFromArray:#[0 0 0 0 0 128 128 128 0 128 128 128 212 208 200 255 255 0 255 255 255]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'C0A?8O?0??C?<O?0??C?>O?8??#?>O?8_? G>@@a') ; yourself); yourself]
!

pointIcon
    "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."

    "
     self pointIcon inspect
     ImageEditor openOnClass:self andSelector:#pointIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class pointIcon'
        ifAbsentPut:[(Depth1Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@A@@@@@@@@@@@@@@@C@@@@@B@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 255 255]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@0@G@@8@G@@8@G@@8@G@@X@B@@@@@@@@@a') ; yourself); yourself]
!

rectIcon
    "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."

    "
     self rectIcon inspect
     ImageEditor openOnClass:self andSelector:#rectIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class rectIcon'
        ifAbsentPut:[(Depth1Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@A@@@@@@@@@@@@@@@C@@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 0 0]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@C?0HA@ DB@PHA@ DB@PHA@ DC?0@@@@@@@a') ; 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."

    "
     self rightMouseKeyIcon inspect
     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:'@@@@@@@@@@@@@@@@@JJE@@B"!!P@@((T@@@@@@@B** @@**(@@J**@@B** @@**(@@J**@@@**@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 0 0 255 255 255]; 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]
!

specialIcon
    "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."

    "
     self specialIcon inspect
     ImageEditor openOnClass:self andSelector:#specialIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:#'ImageEditor class specialIcon'
        ifAbsentPut:[(Depth1Image new) width: 14; height: 14; photometric:(#palette); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@L@@@@B@@@@@0@@@@@@@@@@@@@@@P@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 255 0 0]; mask:((Depth1Image new) width: 14; height: 14; photometric:(#blackIs0); bitsPerSample:(#(1 )); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@L@@0@G @^@A8@G @L@@0@@@@L@@0@@@@@a') ; yourself); yourself]
! !

!ImageEditor class methodsFor:'interface specs'!

changeHLSDialogSpec
    "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:#changeHLSDialogSpec
     ImageEditor new openInterface:#changeHLSDialogSpec
    "

    <resource: #canvas>

    ^ 
     #(#FullSpec
        #name: #changeHLSDialogSpec
        #window: 
       #(#WindowSpec
          #label: 'HLS Edit Dialog'
          #name: 'HLS Edit Dialog'
          #min: #(#Point 10 10)
          #max: #(#Point 800 478)
          #bounds: #(#Rectangle 16 46 328 229)
        )
        #component: 
       #(#SpecCollection
          #collection: #(
           #(#LabelSpec
              #label: 'Hue-Shift:'
              #name: 'HueLabel'
              #layout: #(#LayoutFrame 20 0 21 0 120 0 43 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'HueShiftEntryField'
              #layout: #(#LayoutFrame 123 0 21 0 166 0 43 0)
              #tabable: true
              #model: #hueShiftAmount
              #type: #numberInRange
              #minValue: 0
              #maxValue: 360
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#ThumbWheelSpec
              #name: 'HueWheel'
              #layout: #(#LayoutFrame 180 0 22 0 297 0 42 0)
              #model: #hueShiftAmount
              #orientation: #horizontal
              #step: 1
              #endlessRotation: true
            )
           #(#LabelSpec
              #label: 'Light Factor:'
              #name: 'LightLabel'
              #layout: #(#LayoutFrame 18 0 50 0 120 0 72 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'LightEntryField'
              #layout: #(#LayoutFrame 123 0 50 0 166 0 72 0)
              #tabable: true
              #model: #lightAmount
              #type: #numberInRange
              #minValue: 0
              #maxValue: 1000
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#ThumbWheelSpec
              #name: 'LightWheel'
              #layout: #(#LayoutFrame 180 0 51 0 297 0 71 0)
              #model: #lightAmount
              #orientation: #horizontal
              #stop: 1000
              #step: 1
            )
           #(#LabelSpec
              #label: 'Saturation Factor:'
              #name: 'SaturationLabel'
              #layout: #(#LayoutFrame 9 0 79 0 120 0 101 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'SaturationEntryField'
              #layout: #(#LayoutFrame 123 0 79 0 166 0 101 0)
              #tabable: true
              #model: #saturationAmount
              #type: #numberInRange
              #minValue: 0
              #maxValue: 1000
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#ThumbWheelSpec
              #name: 'SaturationWheel'
              #layout: #(#LayoutFrame 180 0 80 0 297 0 100 0)
              #model: #saturationAmount
              #orientation: #horizontal
              #stop: 1000
              #step: 1
            )
           #(#LabelSpec
              #name: 'HueColorLabel'
              #layout: #(#LayoutFrame 10 0.0 109 0 -10 1.0 148 0)
              #translateLabel: true
              #backgroundChannel: #hlsColor
            )
           #(#HorizontalPanelViewSpec
              #name: 'HorizontalPanel1'
              #layout: #(#LayoutFrame 0 0.0 -30 1 0 1.0 0 1)
              #horizontalLayout: #fitSpace
              #verticalLayout: #center
              #horizontalSpace: 3
              #verticalSpace: 3
              #reverseOrderIfOKAtLeft: true
              #component: 
             #(#SpecCollection
                #collection: #(
                 #(#ActionButtonSpec
                    #label: 'Cancel'
                    #name: 'Button1'
                    #translateLabel: true
                    #tabable: true
                    #model: #cancel
                    #extent: #(#Point 151 22)
                  )
                 #(#ActionButtonSpec
                    #label: 'OK'
                    #name: 'Button2'
                    #translateLabel: true
                    #tabable: true
                    #model: #accept
                    #extent: #(#Point 152 22)
                  )
                 )
               
              )
            )
           )
         
        )
      )
!

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 81 0 288 0 381 0 406 0)
              #label: 'New Image'
              #min: #(#Point 10 10)
              #max: #(#Point 1152 900)
              #bounds: #(#Rectangle 81 288 382 407)
              #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 0 0.0 10 0.0 0 1 35 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 0 0.0 10 0.0 0 1 35 0.0)
                                        #model: #selectionOfColorMap
                                        #comboList: #listOfColorMaps
                                        #useIndex: false
                                    )
                                  )
                              )
                              #label: 'Color Map'
                              #labelPosition: #topLeft
                              #style: #(#FontDescription #helvetica #medium #roman 12)
                          )
                        )
                    )
                    #level: 1
                )
                 #(#UISubSpecification
                    #name: 'windowSpecForCommitWithoutChannels'
                    #layout: #(#LayoutFrame 2 0.0 -26 1 -2 1.0 -2 1.0)
                    #minorKey: #windowSpecForCommitWithoutChannels
                )
              )
          )
      )
!

gropDialogSpec
    "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:#gropDialogSpec
     ImageEditor new openInterface:#gropDialogSpec
    "

    <resource: #canvas>

    ^ 
     #(#FullSpec
        #name: #gropDialogSpec
        #window: 
       #(#WindowSpec
          #label: 'Crop Border(s)'
          #name: 'Crop Border(s)'
          #min: #(#Point 10 10)
          #max: #(#Point 800 478)
          #bounds: #(#Rectangle 16 46 261 229)
        )
        #component: 
       #(#SpecCollection
          #collection: #(
           #(#LabelSpec
              #label: 'Left:'
              #name: 'GropLeftLabel'
              #layout: #(#LayoutFrame 14 0 21 0 90 0 43 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'GropLeftEntryField'
              #layout: #(#LayoutFrame 95 0 21 0 132 0 43 0)
              #tabable: true
              #model: #left
              #type: #number
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#ActionButtonSpec
              #label: 'Now'
              #name: 'GropLeftNowButton'
              #layout: #(#LayoutFrame 148 0 21 0 221 0 43 0)
              #translateLabel: true
              #tabable: true
              #model: #gropLeftNow
            )
           #(#LabelSpec
              #label: 'Right:'
              #name: 'GropRightLabel'
              #layout: #(#LayoutFrame 14 0 51 0 90 0 73 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'GropRightEntryField'
              #layout: #(#LayoutFrame 95 0 51 0 132 0 73 0)
              #tabable: true
              #model: #right
              #type: #number
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#ActionButtonSpec
              #label: 'Now'
              #name: 'GropRightButton'
              #layout: #(#LayoutFrame 148 0 51 0 221 0 73 0)
              #translateLabel: true
              #tabable: true
              #model: #gropRightNow
            )
           #(#LabelSpec
              #label: 'Top:'
              #name: 'GropTopLabel'
              #layout: #(#LayoutFrame 14 0 81 0 90 0 103 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'GropTopEntryField'
              #layout: #(#LayoutFrame 95 0 81 0 132 0 103 0)
              #tabable: true
              #model: #top
              #type: #number
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#ActionButtonSpec
              #label: 'Now'
              #name: 'GropTopButton'
              #layout: #(#LayoutFrame 148 0 81 0 221 0 103 0)
              #translateLabel: true
              #tabable: true
              #model: #gropTopNow
            )
           #(#LabelSpec
              #label: 'Bottom:'
              #name: 'GropBottomLabel'
              #layout: #(#LayoutFrame 14 0 111 0 90 0 133 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'GropBottomEntryField'
              #layout: #(#LayoutFrame 95 0 111 0 132 0 133 0)
              #tabable: true
              #model: #bottom
              #type: #number
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#ActionButtonSpec
              #label: 'Now'
              #name: 'GropBottomButton'
              #layout: #(#LayoutFrame 148 0 111 0 221 0 133 0)
              #translateLabel: true
              #tabable: true
              #model: #gropBottomNow
            )
           #(#HorizontalPanelViewSpec
              #name: 'HorizontalPanel1'
              #layout: #(#LayoutFrame 0 0.0 -30 1 0 1.0 0 1)
              #horizontalLayout: #fitSpace
              #verticalLayout: #center
              #horizontalSpace: 3
              #verticalSpace: 3
              #reverseOrderIfOKAtLeft: true
              #component: 
             #(#SpecCollection
                #collection: #(
                 #(#ActionButtonSpec
                    #label: 'Cancel'
                    #name: 'Button1'
                    #translateLabel: true
                    #tabable: true
                    #model: #cancel
                    #extent: #(#Point 77 22)
                  )
                 #(#ActionButtonSpec
                    #label: 'Apply'
                    #name: 'Button3'
                    #translateLabel: true
                    #tabable: true
                    #model: #applyAction
                    #extent: #(#Point 78 22)
                  )
                 #(#ActionButtonSpec
                    #label: 'OK'
                    #name: 'Button2'
                    #translateLabel: true
                    #tabable: true
                    #model: #accept
                    #extent: #(#Point 78 22)
                  )
                 )
               
              )
            )
           )
         
        )
      )
!

shiftDialogSpec
    "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:#shiftDialogSpec
     ImageEditor new openInterface:#shiftDialogSpec
    "

    <resource: #canvas>

    ^ 
     #(#FullSpec
        #name: #shiftDialogSpec
        #window: 
       #(#WindowSpec
          #label: 'Shift'
          #name: 'Shift'
          #min: #(#Point 10 10)
          #max: #(#Point 800 478)
          #bounds: #(#Rectangle 16 46 261 229)
        )
        #component: 
       #(#SpecCollection
          #collection: #(
           #(#LabelSpec
              #label: 'Amount:'
              #name: 'AmountLabel'
              #layout: #(#LayoutFrame 14 0 21 0 90 0 43 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'AmountEntryField'
              #layout: #(#LayoutFrame 95 0 21 0 132 0 43 0)
              #tabable: true
              #model: #shiftAmount
              #type: #number
              #acceptChannel: #acceptChannel
              #acceptOnPointerLeave: false
            )
           #(#HorizontalPanelViewSpec
              #name: 'HorizontalPanel1'
              #layout: #(#LayoutFrame 0 0.0 -30 1 0 1.0 0 1)
              #horizontalLayout: #fitSpace
              #verticalLayout: #center
              #horizontalSpace: 3
              #verticalSpace: 3
              #reverseOrderIfOKAtLeft: true
              #component: 
             #(#SpecCollection
                #collection: #(
                 #(#ActionButtonSpec
                    #label: 'Cancel'
                    #name: 'Button1'
                    #translateLabel: true
                    #tabable: true
                    #model: #cancel
                    #extent: #(#Point 118 22)
                  )
                 #(#ActionButtonSpec
                    #label: 'OK'
                    #name: 'Button2'
                    #translateLabel: true
                    #tabable: true
                    #model: #accept
                    #extent: #(#Point 118 22)
                  )
                 )
               
              )
            )
           #(#ArrowButtonSpec
              #name: 'ArrowButton1'
              #layout: #(#LayoutFrame 105 0 63 0 127 0 85 0)
              #model: #shiftUpNow
              #isTriggerOnDown: true
              #actionValue: ''
              #direction: #up
            )
           #(#ArrowButtonSpec
              #name: 'ArrowButton2'
              #layout: #(#LayoutFrame 84 0 86 0 106 0 108 0)
              #model: #shiftLeftNow
              #isTriggerOnDown: true
              #actionValue: ''
              #direction: #left
            )
           #(#ArrowButtonSpec
              #name: 'ArrowButton3'
              #layout: #(#LayoutFrame 126 0 86 0 148 0 108 0)
              #model: #shiftRightNow
              #isTriggerOnDown: true
              #actionValue: ''
              #direction: #right
            )
           #(#ArrowButtonSpec
              #name: 'ArrowButton4'
              #layout: #(#LayoutFrame 105 0 107 0 127 0 129 0)
              #model: #shiftDownNow
              #isTriggerOnDown: true
              #actionValue: ''
              #direction: #down
            )
           )
         
        )
      )
!

ungropDialogSpec
    "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:#ungropDialogSpec
     ImageEditor new openInterface:#ungropDialogSpec
    "

    <resource: #canvas>

    ^ 
     #(#FullSpec
        #name: #ungropDialogSpec
        #window: 
       #(#WindowSpec
          #label: 'Add Border(s)'
          #name: 'Add Borders'
          #min: #(#Point 10 10)
          #max: #(#Point 800 478)
          #bounds: #(#Rectangle 16 46 261 229)
        )
        #component: 
       #(#SpecCollection
          #collection: #(
           #(#LabelSpec
              #label: 'Left:'
              #name: 'Label1'
              #layout: #(#LayoutFrame 14 0 21 0 90 0 43 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'EntryField1'
              #layout: #(#LayoutFrame 95 0 21 0 132 0 43 0)
              #model: #left
              #type: #number
              #acceptOnPointerLeave: false
            )
           #(#LabelSpec
              #label: 'Right:'
              #name: 'Label2'
              #layout: #(#LayoutFrame 14 0 51 0 90 0 73 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'EntryField2'
              #layout: #(#LayoutFrame 95 0 51 0 132 0 73 0)
              #model: #right
              #type: #number
              #acceptOnPointerLeave: false
            )
           #(#LabelSpec
              #label: 'Top:'
              #name: 'Label3'
              #layout: #(#LayoutFrame 14 0 81 0 90 0 103 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'EntryField3'
              #layout: #(#LayoutFrame 95 0 81 0 132 0 103 0)
              #model: #top
              #type: #number
              #acceptOnPointerLeave: false
            )
           #(#LabelSpec
              #label: 'Bottom:'
              #name: 'Label4'
              #layout: #(#LayoutFrame 14 0 111 0 90 0 133 0)
              #translateLabel: true
              #adjust: #right
            )
           #(#InputFieldSpec
              #name: 'EntryField4'
              #layout: #(#LayoutFrame 95 0 111 0 132 0 133 0)
              #model: #bottom
              #type: #number
              #acceptOnPointerLeave: false
            )
           #(#HorizontalPanelViewSpec
              #name: 'HorizontalPanel1'
              #layout: #(#LayoutFrame 0 0.0 -30 1 0 1.0 0 1)
              #horizontalLayout: #fitSpace
              #verticalLayout: #center
              #horizontalSpace: 3
              #verticalSpace: 3
              #reverseOrderIfOKAtLeft: true
              #component: 
             #(#SpecCollection
                #collection: #(
                 #(#ActionButtonSpec
                    #label: 'Cancel'
                    #name: 'Button1'
                    #translateLabel: true
                    #model: #cancel
                    #extent: #(#Point 118 22)
                  )
                 #(#ActionButtonSpec
                    #label: 'OK'
                    #name: 'Button2'
                    #translateLabel: true
                    #model: #accept
                    #extent: #(#Point 118 22)
                  )
                 )
               
              )
            )
           )
         
        )
      )
!

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
        #name: #windowSpec
        #window: 
       #(#WindowSpec
          #label: 'Image Editor'
          #name: 'Image Editor'
          #min: #(#Point 400 320)
          #bounds: #(#Rectangle 16 46 466 396)
          #menu: #menu
        )
        #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: 'horizontalPanel'
              #layout: #(#LayoutFrame 0 0.0 34 0.0 0 1.0 -26 1.0)
              #snapMode: #both
              #barLevel: 1
              #component: 
             #(#SpecCollection
                #collection: #(
                 #(#ViewSpec
                    #name: 'leftView'
                    #level: 1
                    #component: 
                   #(#SpecCollection
                      #collection: #(
                       #(#VariableVerticalPanelSpec
                          #name: 'verticalPanel'
                          #layout: #(#LayoutFrame 0 0.0 0 0.0 0 1.0 0 1.0)
                          #level: 0
                          #snapMode: #both
                          #component: 
                         #(#SpecCollection
                            #collection: #(
                             #(#ViewSpec
                                #name: 'View1'
                                #component: 
                               #(#SpecCollection
                                  #collection: #(
                                   #(#MenuPanelSpec
                                      #name: 'MouseButtonColorToolBar'
                                      #layout: #(#LayoutFrame 0 0.0 0 0 0 1.0 24 0)
                                      #level: 0
                                      #menu: #menuMouseButtonColors
                                    )
                                   #(#DataSetSpec
                                      #name: 'colorDataSetView'
                                      #layout: #(#LayoutFrame 0 0.0 26 0.0 0 1.0 0 1.0)
                                      #activeHelpKey: #colorMapTable
                                      #model: #selectionOfColor
                                      #menu: #colorMapMenu
                                      #style: #(#FontDescription #helvetica #medium #roman 10)
                                      #hasHorizontalScrollBar: true
                                      #hasVerticalScrollBar: true
                                      #miniScrollerHorizontal: true
                                      #miniScrollerVertical: true
                                      #dataList: #listOfColors
                                      #has3Dsepartors: true
                                      #has3Dseparators: true
                                      #verticalSpacing: 1
                                      #columns: 
                                     #(#OrderedCollection
                                        
                                       #(#DataSetColumnSpec
                                          #labelButtonType: #Button
                                          #rendererType: #rowSelector
                                          #backgroundSelector: #theColorItSelf:
                                        ) 
                                       #(#DataSetColumnSpec
                                          #label: 'R'
                                          #labelAlignment: #left
                                          #labelButtonType: #Button
                                          #columnAlignment: #right
                                          #editorType: #InputField
                                          #type: #number
                                          #model: #redFromColor:
                                          #writeSelector: #redAtColor:put:
                                          #selectSelector: #canSelectRedInColor:
                                        )
                                        
                                       #(#DataSetColumnSpec
                                          #label: 'G'
                                          #labelAlignment: #left
                                          #labelButtonType: #Button
                                          #columnAlignment: #right
                                          #editorType: #InputField
                                          #type: #number
                                          #model: #greenFromColor:
                                          #writeSelector: #greenAtColor:put:
                                          #selectSelector: #canSelectGreenInColor:
                                        ) 
                                       #(#DataSetColumnSpec
                                          #label: 'B'
                                          #labelAlignment: #left
                                          #labelButtonType: #Button
                                          #columnAlignment: #right
                                          #editorType: #InputField
                                          #type: #number
                                          #model: #blueFromColor:
                                          #writeSelector: #blueAtColor:put:
                                          #selectSelector: #canSelectBlueInColor:
                                        )
                                      )
                                      #columnAdaptor: #colorColumnAdaptor
                                    )
                                   )
                                 
                                )
                              )
                             #(#ArbitraryComponentSpec
                                #name: 'imagePreView'
                                #activeHelpKey: #previewView
                                #menu: #previewMenu
                                #hasHorizontalScrollBar: true
                                #hasVerticalScrollBar: true
                                #miniScrollerHorizontal: true
                                #miniScrollerVertical: true
                                #hasBorder: false
                                #component: #ImageView
                              )
                             )
                           
                          )
                          #handles: #(#Any 0.5 1.0)
                        )
                       )
                     
                    )
                  )
                 #(#ViewSpec
                    #name: 'rightView'
                    #component: 
                   #(#SpecCollection
                      #collection: #(
                       #(#MenuPanelSpec
                          #name: 'ToolBar1'
                          #layout: #(#LayoutFrame 0 0 0 0.0 28 0 0 1.0)
                          #level: 1
                          #menu: #toolsMenuToolbar
                          #verticalLayout: true
                          #centerItems: true
                          #textDefault: true
                        )
                       #(#ViewSpec
                          #name: 'editingView'
                          #layout: #(#LayoutFrame 28 0.0 0 0.0 0 1.0 0 1.0)
                          #level: 1
                          #component: 
                         #(#SpecCollection
                            #collection: #(
                             #(#ArbitraryComponentSpec
                                #name: 'imageEditView'
                                #layout: #(#LayoutFrame 2 0.0 2 0.0 -2 1.0 -24 1.0)
                                #hasHorizontalScrollBar: true
                                #hasVerticalScrollBar: true
                                #hasBorder: false
                                #component: #ImageEditView
                              )
                             #(#LabelSpec
                                #name: 'coordLabel'
                                #layout: #(#LayoutFrame 2 0.0 -22 1 -83 1.0 0 1.0)
                                #level: -1
                                #labelChannel: #imageInfoHolder
                                #resizeForLabel: false
                                #adjust: #left
                              )
                             #(#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
                                #acceptOnPointerLeave: false
                              )
                             )
                           
                          )
                        )
                       )
                     
                    )
                  )
                 )
               
              )
              #handles: #(#Any 0.3 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'!

colorMapMenu
    "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:#menuSpec
     (Menu new fromLiteralArrayEncoding:(ImageEditor menuSpec)) startUp
    "

    <resource: #menu>

    ^
     
       #(#Menu
          
           #(
             #(#MenuItem
                #label: 'Add Color'
                #translateLabel: true
                #value: #addColorToColormap
            )
             #(#MenuItem
                #label: '-'
            )
             #(#MenuItem
                #label: 'Inspect Color'
                #translateLabel: true
                #value: #inspectColor
            )
"/             #(#MenuItem
"/                #label: 'Make Brighter'
"/                #translateLabel: true
"/                #value: #makeSelectedColorBrighter
"/            )
"/             #(#MenuItem
"/                #label: 'Make Darker'
"/                #translateLabel: true
"/                #value: #makeSelectedColorDarker
"/            )
          ) nil
          nil
      )

    "Created: / 12.3.1999 / 00:19:00 / cg"
!

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
            #activeHelpKey: #file
            #label: '&File'
            #translateLabel: true
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #activeHelpKey: #fileNewImage
                  #label: 'New...'
                  #itemValue: #doNewImage
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #fileLoadFromClass
                  #label: 'Load...'
                  #itemValue: #doLoadFromClass
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #fileLoadFromFile
                  #label: 'Load from File...'
                  #itemValue: #doLoadFromFile
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #fileGrabImage
                  #label: 'Grab from Screen...'
                  #itemValue: #grabScreenImage
                  #translateLabel: true
                )
               #(#MenuItem
                  #enabled: #imageHasNextImage
                  #label: 'Next in Sequence'
                  #itemValue: #nextImageInSequence
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #fileSaveMethod
                  #enabled: #imageIsLoadedAndClassDefined
                  #label: 'Save'
                  #itemValue: #doSaveMethod
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #fileSaveMethodAs
                  #enabled: #imageIsLoaded
                  #label: 'Save As...'
                  #itemValue: #doSaveMethodAs
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #fileSaveAs
                  #enabled: #imageIsLoaded
                  #label: 'Save to File...'
                  #itemValue: #doSaveImageFileAs
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #fileSaveMaskAs
                  #enabled: #imageIsLoaded
                  #label: 'Save Mask to File...'
                  #itemValue: #doSaveImageMaskFileAs
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #fileSaveButtonImageAs
                  #enabled: #imageIsLoaded
                  #label: 'Save as Button to File...'
                  #itemValue: #doSaveButtonImageToFileAs
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #fileShowStoreString
                  #enabled: #imageIsLoaded
                  #label: 'Show storeString'
                  #itemValue: #doShowStoreString
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #fileEditMask
                  #enabled: #imageIsLoaded
                  #label: 'Edit Mask'
                  #itemValue: #doEditMask
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #filePrint
                  #enabled: #imageIsLoaded
                  #label: 'Print'
                  #itemValue: #doPrint
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #fileBrowseClass
                  #enabled: #hasClassDefined
                  #label: 'Browse Class'
                  #itemValue: #doBrowseClass
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #fileExit
                  #label: 'Exit'
                  #itemValue: #closeRequest
                  #translateLabel: true
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #activeHelpKey: #edit
            #enabled: #imageIsLoaded
            #label: 'Edit'
            #translateLabel: true
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #activeHelpKey: #editUndo
                  #enabled: #valueOfCanUndo
                  #label: 'Undo'
                  #itemValue: #doUndo
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #editFlipVertical
                  #label: 'Flip - Vertical'
                  #itemValue: #doFlipVertical
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #editFlipHorizontal
                  #label: 'Flip - Horizontal'
                  #itemValue: #doFlipHorizontal
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #activeHelpKey: #editResize
                  #label: 'Resize...'
                  #itemValue: #doResizeImage
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #editMagnifyImage
                  #label: 'Magnify...'
                  #itemValue: #doMagnifyImage
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #editMagnifyImage
                  #label: 'Magnify By...'
                  #itemValue: #doMagnifyImageBy
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #editRotate
                  #label: 'Rotate...'
                  #itemValue: #doRotateImage
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #label: 'Invert'
                  #itemValue: #doNegativeImage
                  #translateLabel: true
                )
               #(#MenuItem
                  #label: '-'
                )
               #(#MenuItem
                  #label: 'Crop'
                  #translateLabel: true
                  #submenu: 
                 #(#Menu
                    #(
                     #(#MenuItem
                        #activeHelpKey: #cropAll
                        #label: 'All'
                        #itemValue: #doCropAll
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: '-'
                      )
                     #(#MenuItem
                        #activeHelpKey: #cropLeft
                        #label: 'Left'
                        #itemValue: #doCropLeft
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #activeHelpKey: #cropRight
                        #label: 'Right'
                        #itemValue: #doCropRight
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #activeHelpKey: #cropTop
                        #label: 'Top'
                        #itemValue: #doCropTop
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #activeHelpKey: #cropBottom
                        #label: 'Bottom'
                        #itemValue: #doCropBottom
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: '-'
                      )
                     #(#MenuItem
                        #activeHelpKey: #cropManual
                        #label: 'Manual...'
                        #itemValue: #doCropManual
                        #translateLabel: true
                      )
                     )
                    nil
                    nil
                  )
                )
               #(#MenuItem
                  #activeHelpKey: #shiftManual
                  #label: 'Shift...'
                  #itemValue: #doShiftManual
                  #translateLabel: true
                )
               #(#MenuItem
                  #activeHelpKey: #uncropManual
                  #label: 'Uncrop (Add Border)...'
                  #itemValue: #doUnCropManual
                  #translateLabel: true
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #enabled: #imageIsLoaded
            #label: 'Mode'
            #translateLabel: true
            #submenuChannel: #modeMenu
          )
         #(#MenuItem
            #activeHelpKey: #colorMap
            #enabled: #imageIsLoaded
            #label: 'ColorMap'
            #translateLabel: true
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #label: 'Depth'
                  #translateLabel: true
                  #submenu: 
                 #(#Menu
                    #(
                     #(#MenuItem
                        #activeHelpKey: #colorMap8
                        #label: '8-Plane'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '8-plane'
                        #choice: #colorMapMode
                        #choiceValue: '8-plane'
                      )
                     #(#MenuItem
                        #activeHelpKey: #colorMap4
                        #label: '4-Plane'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '4-plane'
                        #choice: #colorMapMode
                        #choiceValue: '4-plane'
                      )
                     #(#MenuItem
                        #activeHelpKey: #colorMap2
                        #label: '2-Plane'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '2-plane'
                        #choice: #colorMapMode
                        #choiceValue: '2-plane'
                      )
                     #(#MenuItem
                        #activeHelpKey: #colorMap1
                        #label: '1-Plane'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '1-plane'
                        #choice: #colorMapMode
                        #choiceValue: '1-plane'
                      )
                     #(#MenuItem
                        #label: '-'
                      )
                     #(#MenuItem
                        #activeHelpKey: #colorMap8M
                        #label: '8-Plane + Mask'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '8-plane + mask'
                        #choice: #colorMapMode
                        #choiceValue: '8-plane + mask'
                      )
                     #(#MenuItem
                        #activeHelpKey: #colorMap4M
                        #label: '4-Plane + Mask'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '4-plane + mask'
                        #choice: #colorMapMode
                        #choiceValue: '4-plane + mask'
                      )
                     #(#MenuItem
                        #activeHelpKey: #colorMap2M
                        #label: '2-Plane + Mask'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '2-plane + mask'
                        #choice: #colorMapMode
                        #choiceValue: '2-plane + mask'
                      )
                     #(#MenuItem
                        #activeHelpKey: #colorMap1M
                        #label: '1-Plane + Mask'
                        #itemValue: #colorMapMode:
                        #translateLabel: true
                        #argument: '1-plane + mask'
                        #choice: #colorMapMode
                        #choiceValue: '1-plane + mask'
                      )
                     )
                    nil
                    nil
                  )
                )
               #(#MenuItem
                  #label: 'Colors'
                  #translateLabel: true
                  #submenu: 
                 #(#Menu
                    #(
                     #(#MenuItem
                        #activeHelpKey: #compressColormap
                        #enabled: #hasColormap
                        #label: 'Compress Colormap'
                        #itemValue: #compressColorMap
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #enabled: #hasColormap
                        #label: 'Sort Colormap'
                        #itemValue: #sortColorMap
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: 'Reduce Number of Colors...'
                        #itemValue: #reduceNumberOfColors
                        #translateLabel: true
                      )
                     )
                    nil
                    nil
                  )
                )
               #(#MenuItem
                  #label: 'Process'
                  #translateLabel: true
                  #submenu: 
                 #(#Menu
                    #(
                     #(#MenuItem
                        #label: 'Make GrayScale'
                        #itemValue: #makeGrayScale
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: 'Make Brighter'
                        #itemValue: #makeBrighter
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: 'Make Darker'
                        #itemValue: #makeDarker
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: 'Make Inverse'
                        #itemValue: #makeInverse
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: '-'
                      )
                     #(#MenuItem
                        #label: 'Change HLS...'
                        #itemValue: #changeHLS
                        #translateLabel: true
                      )
                     )
                    nil
                    nil
                  )
                )
               #(#MenuItem
                  #label: 'Mask'
                  #translateLabel: true
                  #submenu: 
                 #(#Menu
                    #(
                     #(#MenuItem
                        #activeHelpKey: #copyMask
                        #enabled: #hasMask
                        #label: 'Copy Mask'
                        #itemValue: #copyMask
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #activeHelpKey: #pasteMask
                        #enabled: #hasMask
                        #label: 'Paste Mask'
                        #itemValue: #pasteMask
                        #translateLabel: true
                      )
                     #(#MenuItem
                        #label: 'Clear Masked Pixels'
                        #itemValue: #clearMaskedPixels
                        #translateLabel: true
                      )
                     )
                    nil
                    nil
                  )
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #label: 'Settings'
            #translateLabel: true
            #submenu: 
           #(#Menu
              #(
               #(#MenuItem
                  #activeHelpKey: #settingsGridMagnification
                  #label: 'Grid Magnification Limit...'
                  #itemValue: #doChangeGridMagnification
                  #translateLabel: true
                )
               )
              nil
              nil
            )
          )
         #(#MenuItem
            #activeHelpKey: #history
            #label: 'History'
            #translateLabel: true
            #submenuChannel: #menuHistory
          )
         #(#MenuItem
            #activeHelpKey: #help
            #label: 'Help'
            #translateLabel: true
            #startGroup: #right
            #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
                #enabled: #imageIsLoaded
                #labelImage: #(#ResourceRetriever nil #leftMouseKeyIcon)
                #choice: #mouseKeyColorMode
                #choiceValue: 1
            )
             #(#MenuItem
                #label: 'Right Mouse Button'
                #nameKey: #rightMouseKeyButton
                #activeHelpKey: #mouseKeyColorMode
                #enabled: #imageIsLoaded
                #labelImage: #(#ResourceRetriever nil #rightMouseKeyIcon)
                #choice: #mouseKeyColorMode
                #choiceValue: 2
            )
          ) 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
            #activeHelpKey: #fileNewImage
            #label: 'newImage'
            #itemValue: #doNewImage
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #Icon #newIcon)
          )
         #(#MenuItem
            #activeHelpKey: #fileLoadFromClass
            #label: 'loadFromClass'
            #itemValue: #doLoadFromClass
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #Icon #loadIcon)
          )
         #(#MenuItem
            #activeHelpKey: #fileSaveMethod
            #enabled: #imageIsLoaded
            #label: 'saveMethod'
            #itemValue: #doSaveMethod
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #Icon #saveIcon)
          )
         #(#MenuItem
            #label: ''
          )
         #(#MenuItem
            #label: ''
          )
         #(#MenuItem
            #activeHelpKey: #editUndo
            #enabled: #valueOfCanUndo
            #label: 'Undo'
            #itemValue: #doUndo
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ToolbarIconLibrary #undo16x16Icon2)
          )
         )
        nil
        nil
      )
!

modeMenu
    "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:#modeMenu
     (Menu new fromLiteralArrayEncoding:(ImageEditor modeMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #activeHelpKey: #drawModePoint
            #enabled: #imageIsLoaded
            #label: 'Point'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #point
          )
         #(#MenuItem
            #activeHelpKey: #drawModeBox
            #enabled: #imageIsLoaded
            #label: 'Rect'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #box
          )
         #(#MenuItem
            #activeHelpKey: #drawModeFilledBox
            #enabled: #imageIsLoaded
            #label: 'Filled Rectangle'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #filledBox
          )
         #(#MenuItem
            #activeHelpKey: #drawModeFill
            #enabled: #imageIsLoaded
            #label: 'Fill'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #fill
          )
         #(#MenuItem
            #activeHelpKey: #drawModeCopy
            #enabled: #imageIsLoaded
            #label: 'Copy'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #copy
          )
         #(#MenuItem
            #activeHelpKey: #drawModePaste
            #enabled: #imageIsLoaded
            #label: 'Paste'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #paste
          )
         #(#MenuItem
            #activeHelpKey: #drawModePasteUnder
            #enabled: #imageIsLoaded
            #label: 'Paste Under'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #pasteUnder
          )
         #(#MenuItem
            #activeHelpKey: #drawModePasteWithMask
            #enabled: #imageIsLoaded
            #label: 'Paste with Mask'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #pasteWithMask
          )
         #(#MenuItem
            #activeHelpKey: #drawModeSpecial
            #enabled: #imageIsLoaded
            #label: 'Special'
            #translateLabel: true
            #choice: #editMode
            #choiceValue: #specialOperation
          )
         )
        nil
        nil
      )
!

previewMenu
    "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:#previewMenu
     (Menu new fromLiteralArrayEncoding:(ImageEditor previewMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #label: 'TileMode'
            #translateLabel: true
            #indication: #tileModeHolder
          )
         )
        nil
        nil
      )
!

toolsMenuToolbar
    "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:#toolsMenuToolbar
     (Menu new fromLiteralArrayEncoding:(ImageEditor toolsMenuToolbar)) startUp
    "

    <resource: #menu>

    ^ 
     #(#Menu
        #(
         #(#MenuItem
            #activeHelpKey: #drawModePoint
            #enabled: #imageIsLoaded
            #label: 'Point'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #pointIcon)
            #choice: #editMode
            #choiceValue: #point
          )
         #(#MenuItem
            #activeHelpKey: #drawModeBox
            #enabled: #imageIsLoaded
            #label: 'Rect'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #rectIcon)
            #choice: #editMode
            #choiceValue: #box
          )
         #(#MenuItem
            #activeHelpKey: #drawModeFilledBox
            #enabled: #imageIsLoaded
            #label: 'FillRect'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #fillRectIcon)
            #choice: #editMode
            #choiceValue: #filledBox
          )
         #(#MenuItem
            #activeHelpKey: #drawModeFill
            #enabled: #imageIsLoaded
            #label: 'Fill'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #fillIcon)
            #choice: #editMode
            #choiceValue: #fill
          )
         #(#MenuItem
            #activeHelpKey: #drawModeCopy
            #enabled: #imageIsLoaded
            #label: 'Copy'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #copyIcon)
            #choice: #editMode
            #choiceValue: #copy
          )
         #(#MenuItem
            #activeHelpKey: #drawModePaste
            #enabled: #imageIsLoaded
            #label: 'Paste'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #pasteIcon)
            #choice: #editMode
            #choiceValue: #paste
          )
         #(#MenuItem
            #activeHelpKey: #drawModePasteUnder
            #enabled: #imageIsLoaded
            #label: 'Paste Under'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #pasteUnderIcon)
            #choice: #editMode
            #choiceValue: #pasteUnder
          )
         #(#MenuItem
            #activeHelpKey: #drawModePasteWithMask
            #enabled: #imageIsLoaded
            #label: 'Paste With Mask'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #pasteWithMaskIcon)
            #choice: #editMode
            #choiceValue: #pasteWithMask
          )
         #(#MenuItem
            #activeHelpKey: #drawModeSpecial
            #enabled: #imageIsLoaded
            #label: 'Special'
            #translateLabel: true
            #isButton: true
            #labelImage: #(#ResourceRetriever #ImageEditor #specialIcon)
            #choice: #editMode
            #choiceValue: #specialOperation
          )
         )
        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
!

resourceClass:aClass
    imageEditView resourceClass:aClass
! !

!ImageEditor methodsFor:'accessing-views'!

colorDataSetView
    "returns the view of the colormap"

    ^(self componentAt: #colorDataSetView)

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

coordLabel
    "returns the view the coord label"

    ^self componentAt: #coordLabel
!

imageEditView
    "returns the view of the image"

    imageEditView isNil ifTrue:[
        imageEditView := (self componentAt: #imageEditView) scrolledView.
        imageEditView addDependent:self.
    ].
    ^ imageEditView

    "Modified: / 10.2.2000 / 23:19:20 / cg"
!

imagePreView
    "returns the preview of the image"

    ^ (self 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
    ^ [
        |clsName|

        (clsName := imageEditView resourceClass) size > 0
        and:[(Smalltalk at:clsName ifAbsent:nil) notNil
        and:[imageEditView resourceSelector notNil]]
      ]

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

hasClassDefined
    ^ [
        |clsName|

        (clsName := imageEditView resourceClass) size > 0
        and:[(Smalltalk at:clsName ifAbsent:nil) notNil]
      ]

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

hasColormap
    ^ [self image notNil and:[self image colorMap notNil]]

    "Created: / 30.9.1998 / 23:53:55 / cg"
!

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

    ^ [self image notNil
       and:[self image imageSequence notNil]]

    "Created: / 31.7.1998 / 02:04:18 / 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
!

tileModeHolder
    |holder|
    (holder := builder bindingAt:#tileModeHolder) isNil ifTrue:[
        builder aspectAt:#tileModeHolder put:(holder := false asValue).
        holder addDependent:self.
    ].
    ^ 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"

    |image listOfColors|

    image := self image.
    image depth > 8 ifTrue: [
        colorMapMode value: ''. 
        self listOfColors removeAll. 
        ^ nil
    ].

    colorMapMode value:(image depth printString, '-plane').

    (listOfColors := self listOfColors) isEmpty ifTrue:[   
        self colorMapMode: colorMapMode value.
        listOfColors := self listOfColors.
        image := self image.
    ].                               
    imageEditView drawingColors:(Array
                                    with: (listOfColors at:1) 
                                    with: (listOfColors at:2 ifAbsent:[listOfColors at:1])).

    image mask notNil ifTrue: [             
        colorMapMode value:(colorMapMode value, ' + mask').

        (listOfColors detect: [:clr| clr = (Color colorId:0)] ifNone: nil) isNil
        ifTrue:[
            listOfColors addFirst:(Color colorId:0).
            imageEditView drawingColors: (Array 
                                            with:(listOfColors at:2 ifAbsent:[listOfColors at:1]) 
                                            with:(listOfColors at:1)).
        ]
    ].
    self selectionOfColor 
        setValue: 0;
        value: (self listOfColors indexOf: imageEditView selectedColor).
!

update:something with:aParameter from:changedObject
    |clrIndex img |

    img := self image.

    changedObject == self tileModeHolder ifTrue:[
        self imagePreView tileMode:(changedObject value) tileOffset:(img extent).
        self imagePreView clear; invalidate.
        ^ self
    ].

    changedObject == self listOfColors ifTrue:[
        something == #at: ifTrue:[
            "/ colormap entry changed at aParameter

            clrIndex := aParameter.
            (self hasMask) ifTrue:[
                clrIndex := clrIndex - 1.
            ].

            (img colorMap ? drawingColormap) at:clrIndex put:(changedObject at:aParameter).
            self colorMapChanged.
            ^ self
        ].
    ].

    changedObject == imageEditView undoImages ifTrue:[
        self valueOfCanUndo value:(changedObject notEmpty).
        ^ self.
    ].

    changedObject == imageEditView ifTrue:[
        something == #imageColors ifTrue:[
            self listOfColors contents:img colorMap.
            self findColorMapMode.
            ^ self.
        ].
        something == #image ifTrue:[
            self imagePreView image:img.
            self listOfColors contents:img colorMap.
            self findColorMapMode.
            self tileModeHolder value ifTrue:[
                self imagePreView tileMode:true tileOffset:(img extent).
            ].
            ^ self.
        ].
        something == #subImageIn ifTrue:[
            self imagePreView image ~~ img ifTrue:[
                self halt:'should not happen'.
            ].
            self tileModeHolder value ifTrue:[
                self imagePreView invalidate.
            ] ifFalse:[
                self imagePreView invalidate:aParameter.
            ].
            ^ self.
        ].
        something == #selectedColor ifTrue:[
            (aParameter = (Color colorId:0)) ifTrue:[
                self halt.
            ].
            clrIndex := self listOfColors indexOf:aParameter.
            self selectionOfColor value:clrIndex.
            ^ self.
        ].
        ^ self.
    ].

    changedObject == imageEditView image ifTrue:[
        self halt:'to be implemented'.
        ^ self.
    ].

    super update:something with:aParameter from:changedObject

    "Modified: / 10.2.2000 / 23:36:49 / cg"
!

updateForNoImage
    "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'!

atColor:aOldColor put:aNewColor
    "a color changed to a new color
    "
    |index list|

    list  := self listOfColors.
    index := list identityIndexOf:aOldColor.

    index ~~ 0 ifTrue:[
        list at:index put:aNewColor
    ] ifFalse:[
        self halt:'should not happen'.
        list add:aNewColor
    ].
!

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

    |byte|

    aColor isNil ifTrue:[^ self].       "/ mask cannot be changed
    byte := newBlue clampBetween:0 and:255.
    byte = aColor blueByte ifTrue:[^ self ].

    self atColor:aColor
             put:(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"
!

canSelectBlueInColor:aColor
    ^ aColor blueByte notNil

    "Created: / 7.8.1998 / 22:50:34 / cg"
    "Modified: / 7.8.1998 / 22:52:57 / cg"
!

canSelectGreenInColor:aColor
    ^ aColor greenByte notNil

    "Created: / 7.8.1998 / 22:50:22 / cg"
    "Modified: / 7.8.1998 / 22:52:46 / cg"
!

canSelectRedInColor:aColor
    ^ aColor redByte notNil

    "Created: / 7.8.1998 / 22:50:00 / cg"
    "Modified: / 7.8.1998 / 22:51:03 / cg"
!

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

    |byte|

    aColor isNil ifTrue:[^ self].       "/ mask cannot be changed
    byte := newGreen clampBetween:0 and:255.
    byte = aColor greenByte ifTrue:[^ self].

    self atColor:aColor
             put:(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:[^ self].       "/ mask cannot be changed
    byte := newRed clampBetween:0 and:255.
    byte = aColor redByte ifTrue:[^ self].

    self atColor:aColor
             put:(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"
!

theColorItSelf:aColor
    ^ aColor
! !

!ImageEditor methodsFor:'help'!

defaultInfoLabel
    "returns the default info label"

    |resourceClass|

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

    |img|

    self withCursor:Cursor wait do:[
        (imageEditView loadFromFile: aFileName) notNil ifTrue:[
            (img := self image) notNil ifTrue:[          
                img colorMap notNil ifTrue:[
                    self listOfColors contents:(img usedColors asSet asOrderedCollection).
                ] ifFalse:[
                    self listOfColors removeAll.
                ].
                self findColorMapMode.     
                self updateLabelsAndHistory.
                img := img onDevice:device.
                imageEditView image:img.
            ] ifFalse:[
                self updateForNoImage
            ].
        ]
    ]

    "Modified: / 16.3.1999 / 21:44:26 / cg"
!

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

    |img|

    anImage notNil ifTrue:[    
        img := anImage onDevice:device.
    ].

    imageEditView image:img.

    img notNil ifTrue:[    
        img colorMap notNil ifTrue:[
            self listOfColors contents:(img usedColors asSet asOrderedCollection).
        ] ifFalse:[
            self listOfColors removeAll.
        ].
        self findColorMapMode.
        self updateLabelsAndHistory.
    ] ifFalse:[
        self updateForNoImage
    ]

    "Modified: / 16.3.1999 / 21:43:56 / cg"
!

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

    |img|

    (imageEditView loadFromMessage: aMessage) notNil ifTrue:[
        (img := self image) notNil ifTrue:[
            img := img onDevice:device.
            self listOfColors contents:(img usedColors asSet asOrderedCollection).
            self findColorMapMode.
            self updateLabelsAndHistory.
            imageEditView image:img.
        ] ifFalse:[
            self updateForNoImage
        ]
    ]

    "Modified: / 16.3.1999 / 21:44:41 / cg"
!

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.

    "Modified: / 16.3.1999 / 21:45:07 / cg"
! !

!ImageEditor methodsFor:'menu modes'!

colorMapMode
    "returns colorMapMode"

    colorMapMode isNil ifTrue: [colorMapMode := '' asValue].

    ^colorMapMode
!

editMode
    "returns editMode"

    editMode isNil ifTrue: [
        editMode := #point asValue.
        editMode onChangeEvaluate:[imageEditView editMode:(editMode value)]
    ].

    ^editMode
!

mouseKeyColorMode
    "returns mouseKeyColorMode"

    mouseKeyColorMode isNil ifTrue:[
        mouseKeyColorMode := 1 asValue.
        mouseKeyColorMode onChangeEvaluate: [
            imageEditView mouseKeyColorMode:mouseKeyColorMode value. 
            self selectionOfColor value: (self listOfColors indexOf:imageEditView selectedColor).
        ]
    ].

    ^mouseKeyColorMode

    "Modified: / 10.2.2000 / 23:16:42 / cg"
! !

!ImageEditor methodsFor:'private'!

updateImage
    |img|

    img := imageEditView image.
    imageEditView image:img.
    self fetchImageData.
!

updateImagePreView
    self tileModeHolder value ifTrue:[
        self imagePreView tileMode:true tileOffset:(self image extent).
    ]
! !

!ImageEditor methodsFor:'queries'!

hasMask
    ^ colorMapMode value notNil and:[colorMapMode value endsWith:'mask']

    "Created: / 18.8.1998 / 17:17:38 / cg"
!

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"

    |clr pixel|

    selectedColorIndex := anIndex.
    clr := self listOfColors at:anIndex ifAbsent:nil.
    clr isNil ifTrue:[^ self].

    pixel := anIndex - 1.
    (self listOfColors at:1) = Color noColor ifTrue:[
        anIndex == 1 ifTrue:[
            pixel := nil.       "/ mask
        ] ifFalse:[
            pixel := pixel - 1
        ]
    ].
    imageEditView selectedColorIndex:pixel.
    imageEditView selectedColor:clr.

! !

!ImageEditor methodsFor:'startup / release'!

closeDownViews
    builder notNil ifTrue:[
        DefaultRelativeSizes :=
            Array 
                with:(builder componentAt:#horizontalPanel) relativeCorners    
                with:(builder componentAt:#verticalPanel) relativeCorners.
    ].
    super closeDownViews
!

closeRequest
    "close request"

    imageEditView checkModified ifTrue:[super closeRequest]

!

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

    super open.

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

postBuildWith:aBuilder
    super postBuildWith:aBuilder.

    DefaultRelativeSizes notNil ifTrue:[
        (aBuilder componentAt:#horizontalPanel) relativeCorners:DefaultRelativeSizes first.
        (aBuilder componentAt:#verticalPanel) relativeCorners:DefaultRelativeSizes second.
    ].
!

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

    imageEditView undoImages addDependent:self.
    imageEditView imageInfoHolder:(self imageInfoHolder).
    imageEditView activityInfoHolder:(self activityInfoHolder).
    imageEditView clickInfoCallBack:[:button :point | 
                        |mouseButtonColorToolBar|

                        mouseButtonColorToolBar := self componentAt:#MouseButtonColorToolBar.
                        (mouseButtonColorToolBar itemAt:button) toggleIndication.
                        mouseButtonColorToolBar do: [:i| i updateIndicators].
                  ].
    imageEditView addDependent:self.

    postOpenAction notNil ifTrue: [postOpenAction value].

    super postOpenWith:aBuilder.

    aBuilder keyboardProcessor menuBar:nil.

    "Modified: / 10.2.2000 / 23:17:43 / cg"
! !

!ImageEditor methodsFor:'user actions - colormap'!

addColorToColormap
    |depth img cMap newColorMap newImage oldCListSize newMode listOfColors|

    img := self image.
    img isNil ifTrue:[
        self warn:'No Image.'.
        ^ self
    ].

    depth := img depth.
    cMap := img colorMap.
    cMap isNil ifTrue:[
        drawingColormap isNil ifTrue:[
            self information:'Image has no colormap.\The shown colorMap is for drawing only.' withCRs.
            drawingColormap := OrderedCollection new.
        ].
        drawingColormap add:(Color black).
        self listOfColors contents:drawingColormap.
        self selectionOfColor value:(drawingColormap size).

        "/ self warn:'Image has no colormap.\Change colorMap mode first.' withCRs.
        ^ self
    ].

    ("(depth == 1)" false
    or:[cMap size == (1 bitShift:depth)]) ifTrue:[
        depth >= 8 ifTrue:[
            self warn:'No space for more colors in colormap.'.
            ^ self
        ].
        (self confirm:'No space for more colors in colormap.\Change depth first.' withCRs)
        ifFalse:[
            ^ self
        ].

        imageEditView makeUndo.
        newMode := (depth*2) printString , (colorMapMode value copyFrom:2).
        self colorMapMode:newMode.
    ] ifFalse:[
        imageEditView makeUndo.
    ].

    cMap := cMap asArray.
    listOfColors := self listOfColors.
    oldCListSize := listOfColors size.

"/    (colorMapMode value asString endsWith:'mask') ifTrue:[
"/        cMap last = Color noColor ifTrue:[
"/            cMap := cMap copyWithoutLast:1
"/        ] ifFalse:[
"/            cMap first = Color noColor ifTrue:[
"/                cMap := cMap copyFrom:2
"/            ]
"/        ].
"/    ].
"/

    newColorMap := cMap copyWith:(Color black).

    newImage := img species new
                    width:img width
                    height:img height
                    depth:depth
                    fromArray:img bits.

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

    (imageEditView image:newImage) notNil ifTrue:[
        listOfColors contents: newImage colorMap.
        self findColorMapMode.
        "/ mhmh - somehow, we get two colors added ... (sigh findColorMapMode adds another one ...)
        listOfColors size > (oldCListSize + 1) ifTrue:[
            listOfColors removeLast
        ].
        self selectionOfColor value:(listOfColors size).
        self updateLabelsAndHistory.
    ]

    "Created: / 12.3.1999 / 00:20:28 / cg"
    "Modified: / 16.3.1999 / 21:57:26 / cg"
!

changeHLS
    "interactive Hue/Light/Saturation editing"

    |bindings hueShift lightValue saturationValue originalColormap firstChange acceptChannel 
     shiftAction avgColorHolder avgColor shiftedColor shiftProcess readySema|

    readySema := Semaphore new.
    [
        originalColormap := imageEditView image colorMap copy.
        avgColor := imageEditView image averageColor.
        readySema signal.
    ] forkAt:7.

    acceptChannel := TriggerValue new.
    avgColorHolder := avgColor asValue.

    firstChange := true.

    shiftedColor := [:clr :hShift :lFactor :sFactor |
                        Color 
                                hue:((clr hue) ? 0 + hShift) 
                                light:((clr light * lFactor / 100) min:100)
                                saturation:((clr saturation * sFactor / 100) min:100)].

    shiftAction := 
        [
            |hShift lFactor sFactor|

            acceptChannel value:true.

            firstChange ifTrue:[
                imageEditView makeUndo.
                firstChange := false.
            ].
            readySema notNil ifTrue:[readySema wait. readySema := nil].

            hShift := hueShift value.
            lFactor := lightValue value.
            sFactor := saturationValue value.

            avgColorHolder value:(shiftedColor value:avgColor value:hShift value:lFactor value:sFactor).

            shiftProcess notNil ifTrue:[
                shiftProcess terminate.
                shiftProcess waitUntilTerminated.
                shiftProcess := nil.
            ].
            shiftProcess := 
                [
                    [
                        imageEditView image 
                            colorMap:originalColormap copy;
                            colorMapProcessing:[:clr | shiftedColor value:clr value:hShift value:lFactor value:sFactor].
                        self updateImage.
                        self updateInfoLabel.
                    ] ensure:[ shiftProcess := nil ].    
                ] forkAt:7.
        ].

    bindings := IdentityDictionary new.
    bindings at:#hueShiftAmount put:(hueShift := 0 asValue).
    hueShift onChangeEvaluate:shiftAction.

    bindings at:#lightAmount put:(lightValue := 100 asValue).
    lightValue onChangeEvaluate:shiftAction.

    bindings at:#saturationAmount put:(saturationValue := 100 asValue).
    saturationValue onChangeEvaluate:shiftAction.

    bindings at:#acceptChannel put:acceptChannel.
    bindings at:#hlsColor put:avgColorHolder.

    (self openDialogInterface:#changeHLSDialogSpec withBindings:bindings) 
    ifFalse:[ 
        firstChange ~~ true ifTrue:[
            imageEditView undo
        ]
    ].
!

clearMaskedPixels
    "clear all masked pixels (to pixelValue 0)"

    |newImage oldImage| 

    oldImage := self image.

    imageEditView makeUndo.

    self withExecuteCursorDo:[
        newImage := oldImage clearMaskedPixels.
        0 to:newImage height - 1 do:[:y |
            0 to:newImage width - 1 do:[:x |
                (newImage maskAtX:x y:y) == 0 ifTrue:[
                    newImage pixelAtX:x y:y put:0
                ]
            ]
        ].

        (imageEditView image:newImage) notNil ifTrue:[
            self fetchImageData.
        ]
    ]
!

colorMapChanged
    |img|

    img := self image.
    img release.

    self imageEditView invalidate.
    self imagePreView invalidate.

"/    (imageEditView image:img) notNil ifTrue:[
"/        self updateLabelsAndHistory.
"/        self imagePreView image:img
"/    ]

    "Created: / 7.8.1998 / 22:26:10 / cg"
    "Modified: / 18.8.1998 / 17:08:55 / cg"
!

colorMapMode:aMode
    "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
     prevMode maskThreshold maskImage| 

    self withExecuteCursorDo:[
        oldImage := self image.

        prevMode := colorMapMode value.
        oldImage depth > 8 ifTrue:[
            prevMode := nil
        ].

        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:[
            prevMode isNil ifTrue:[
                quest := 'Compute colormap (or use standard)'
            ] ifFalse:[
                quest := 'Keep colormap (or use standard)'
            ]
        ].
        ((prevMode = aMode)
        or:[depth > oldImage depth
        or:[self confirm:(resources string:quest)]]) ifTrue:[

            "/ keep the colormap
            newColorMap atAllPut:Color black.
            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 usedColorsMax:(1 bitShift:depth).
                (usedColors notNil and:[usedColors size > (1 bitShift:depth)]) ifTrue:[
                    usedColors := oldImage realUsedColors
                ].
                (usedColors notNil and:[usedColors size <= (1 bitShift:depth)]) ifTrue:[
                    "/ yea - just install them
                    usedColors asArray 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:[
                        oldImage depth == 8 ifTrue:[
                            tmpBits := oldImage bits
                        ] ifFalse:[
                            colorMapMode value:prevMode.
                            self warn:('Too many used colors in image (', oldImage usedColors size printString , ').').
                            ^ self
"/                            (self confirm:('Too many used colors in image (', oldImage usedColors size printString , ').\\Dither ?' withCRs))
"/                            ifFalse:[.
"/                                ^ self
"/                            ].
"/                            self image: (Image newForDepth:depth) fromImage:oldImage.
"/                            ^ self.
                        ]
                    ].
                    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:[
                        colorMapMode value:prevMode.
                        ^ 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:[
                    colorMapMode value:prevMode.
                    ^ self   "/ cancel
                ].
            ].
        ].

        imageEditView makeUndo.

        newImage    := Image newForDepth:depth.
        newImage depth:depth.
        oldFileName := oldImage fileName.

        Image imageErrorSignal handle:[:ex|
            Color colorErrorSignal handle:[:ex|
                colorMapMode value:prevMode.
                imageEditView undo.
                ^ self warn:(resources string:'Conversion failed !!')
            ] do:[
                newImage := Image newForDepth:depth.
                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: [
                            newColor := clr nearestIn:newColorMap.
                            useNearest ifFalse:[
                                (newColor deltaFrom:clr) > 0.5 ifTrue:[
                                    newColor := oldImage colorFromValue:0
                                ]
                            ]
                        ].
                    newImage colorAtX:x y:y put:newColor.
                ].
                image := newImage
            ].
        ] do:[ 
            image := newImage fromImage:oldImage
        ].

        (aMode asString endsWith:'mask') ifTrue:[
            image mask isNil ifTrue:[
                (self confirm:'Generate mask from black ?') ifTrue:[
                    maskThreshold := 0.1.
                    maskImage := Depth1Image fromImage:(image asThresholdMonochromeImage:maskThreshold). 
                ] ifFalse:[
                    maskImage := Depth1Image extent:image extent.
                    maskImage data:(ByteArray 
                                        new:(maskImage bytesPerRow * maskImage height)
                                        withAll:16rFF).

"/                    maskImage fillRectangle:(image bounds) withColor:(Color colorId:1).
                ].
                image mask:maskImage.
            ].
        ] 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.   
        image fileName: oldFileName.

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

    "Modified: / 16.3.1999 / 21:55:39 / 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 realUsedColors.
    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.
        newColorMap sort:[:a :b |
                                a redByte == b redByte ifTrue:[
                                    a greenByte == b greenByte ifTrue:[
                                        a blueByte < b blueByte
                                    ] ifFalse:[
                                        a greenByte < b greenByte 
                                    ]
                                ] ifFalse:[
                                    a redByte < b redByte 
                                ]
                          ].

        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 fetchImageData.
"/            self listOfColors contents: newImage colorMap.
"/            self findColorMapMode.
"/            self updateLabelsAndHistory.
        ]
    ]

    "Created: / 28.7.1998 / 20:03:11 / cg"
    "Modified: / 15.9.1998 / 17:53:32 / cg"
!

copyMask
    |mask|

    mask := self image mask.
    MaskClipboard := mask subImageIn: (0@0 extent:mask extent).
!

fetchImageData
    |image|

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

inspectColor
    | img clrIndex|

    img := self image.
    img isNil ifTrue:[
        self warn:'No Image.'.
        ^ self
    ].
    clrIndex := self selectionOfColor value.
    img mask notNil ifTrue: [ clrIndex := clrIndex - 1 ].
    (img colorFromValue:clrIndex-1) inspect
!

makeBrighter
    | anyChange|

    self withExecuteCursorDo:[
        anyChange := imageEditView makeBrighter.
        anyChange ifFalse:[
            Dialog warn:'Image unchanged'.
        ] ifTrue:[
            self updateImage.
        ]
    ].
!

makeDarker
    | anyChange|

    self withExecuteCursorDo:[
        anyChange := imageEditView makeDarker.
        anyChange ifFalse:[
            Dialog warn:'Image unchanged'.
        ] ifTrue:[
            self updateImage.
        ]
    ].
!

makeGrayScale
    |anyChange|

    self withExecuteCursorDo:[
        anyChange := imageEditView makeGrayScale.
        anyChange ifFalse:[
            Dialog warn:'Image unchanged'.
        ] ifTrue:[
            self updateImage.
        ]
    ].
!

makeInverse
    | anyChange|

    self withExecuteCursorDo:[
        anyChange := imageEditView makeInverse.
        anyChange ifFalse:[
            Dialog warn:'Image unchanged'.
        ] ifTrue:[
            self updateImage.
        ]
    ].
!

makeSelectedColorBrighter
    |img cMap newImage clr|

    img := self image.
    cMap := img colorMap.
    cMap isNil ifTrue:[
        self warn:'Image has no colormap\change colorMap mode first.' withCRs.
        ^ self
    ].

    imageEditView makeUndo.

    cMap := cMap asArray.
    clr := cMap at:imageEditView selectedColorIndex.
    cMap at:imageEditView selectedColorIndex put:clr lightened.

    newImage := img species new
                    width:img width
                    height:img height
                    depth:nil
                    fromArray:img bits.

    newImage colorMap:cMap.  
    newImage fileName:img fileName.
    newImage mask:(img mask copy).

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

    "Created: / 12.3.1999 / 00:20:28 / cg"
    "Modified: / 16.3.1999 / 21:57:26 / cg"
!

makeSelectedColorDarker
    |img cMap clr newImage|

    img := self image.
    cMap := img colorMap.
    cMap isNil ifTrue:[
        self warn:'Image has no colormap\change colorMap mode first.' withCRs.
        ^ self
    ].
    imageEditView makeUndo.

    cMap := cMap asArray.
    clr := cMap at:imageEditView selectedColorIndex.
    cMap at:imageEditView selectedColorIndex put:clr darkened.

    newImage := img species new
                    width:img width
                    height:img height
                    depth:nil
                    fromArray:img bits.

    newImage colorMap:cMap.  
    newImage fileName:img fileName.
    newImage mask:(img mask copy).

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

    "Created: / 12.3.1999 / 00:20:28 / cg"
    "Modified: / 16.3.1999 / 21:57:26 / cg"
!

pasteMask
    |img mask|

    imageEditView makeUndo.

    img := self image.
    mask := img mask.

    mask 
         copyFrom:MaskClipboard
         x:0 y:0
         toX:0 y:0 
         width:(mask width min:MaskClipboard width)
         height:(mask height min:MaskClipboard height).
    img mask:mask.
    (imageEditView image:img copy) notNil ifTrue:[
        self fetchImageData.
    ]
!

reduceNumberOfColors
    |s n anyChange img|

    s := Dialog request:'Number of color bits to strip (1-7) ?'.
    s size == 0 ifTrue:[^ self].
    n := Integer readFrom:s onError:0.
    (n between:1 and:7) ifFalse:[
        Dialog warn:'Image unchanged'.
        ^ self
    ].

    self withExecuteCursorDo:[
        anyChange := imageEditView reduceColorResolutionBy:n.
        anyChange ifFalse:[
            Dialog warn:'Image unchanged'.
        ] ifTrue:[
            img := imageEditView image.
            imageEditView image:img.

            self fetchImageData.
"/            self listOfColors contents:(img colorMap).
"/            self findColorMapMode.
"/            self updateLabelsAndHistory.
            Dialog information:(img usedColors size printString , ' colors used.')
        ]
    ].
!

sortColorMap
    "calculates a new color map for the image, sorting 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 realColorMap.

    imageEditView makeUndo.

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

        "/ translation table
        oldToNew := ByteArray new:(1 bitShift:depth).
        newColorMap := usedColors asArray.
        newColorMap sort:[:a :b |
                                a redByte == b redByte ifTrue:[
                                    a greenByte == b greenByte ifTrue:[
                                        a blueByte < b blueByte
                                    ] ifFalse:[
                                        a greenByte < b greenByte 
                                    ]
                                ] ifFalse:[
                                    a redByte < b redByte 
                                ]
                          ].

        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 fetchImageData.
"/            self listOfColors contents: newImage colorMap.
"/            self findColorMapMode.
"/            self updateLabelsAndHistory.
        ]
    ]

    "Modified: / 15.9.1998 / 17:53:32 / cg"
    "Created: / 30.9.1998 / 23:51:23 / 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'].
    UserPreferences systemBrowserClass
        openInClass:cls class 
        selector:(imageEditView resourceSelector)

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

doCropAll
    "find all borders and cut them off"

    imageEditView cropLeft:true right:true top:true bottom:true.   
    self updateInfoLabel

    "Modified: / 7.9.1998 / 14:26:23 / cg"
    "Created: / 7.9.1998 / 16:33:43 / cg"
!

doCropBottom
    "find a bottom border and cut it off"

    imageEditView cropLeft:false right:false top:false bottom:true.   
    self updateInfoLabel

    "Created: / 7.9.1998 / 13:00:20 / cg"
    "Modified: / 7.9.1998 / 14:26:23 / cg"
!

doCropLeft
    "find a left border and cut it off"

    imageEditView cropLeft:true right:false top:false bottom:false.   
    self updateInfoLabel

    "Created: / 7.9.1998 / 13:00:14 / cg"
    "Modified: / 7.9.1998 / 14:26:34 / cg"
!

doCropManual
    "let user specify borders and cut them off"

    |bindings left top right bottom img firstChange gropAction acceptChannel|

    acceptChannel := TriggerValue new.

    firstChange := true.

    gropAction := 
        [:lV :rV :tV :bV | |l r t b|
            acceptChannel value:true.

            l := lV value.
            r := rV value.
            t := tV value.
            b := bV value.
            (l + r + t + b) == 0 ifTrue:[
                Screen current beep
            ] ifFalse:[
                img := imageEditView image.
                firstChange ifTrue:[
                    imageEditView makeUndo.
                    firstChange := false.
                ].
                imageEditView
                    makeSubImageX:l y:t 
                    width:(img width - l - r)
                    height:(img height - t - b).
                self updateInfoLabel
            ].
        ].

    bindings := IdentityDictionary new.
    bindings at:#left put:(left := 1 asValue).
    bindings at:#right put:(right := 1 asValue).
    bindings at:#top put:(top := 1 asValue).
    bindings at:#bottom put:(bottom := 1 asValue).
    bindings at:#acceptChannel put:acceptChannel.

    bindings at:#gropLeftNow   put:[ gropAction value:left value:0 value:0 value:0 ].
    bindings at:#gropRightNow  put:[ gropAction value:0 value:right value:0 value:0 ].
    bindings at:#gropTopNow    put:[ gropAction value:0 value:0 value:top value:0 ].
    bindings at:#gropBottomNow put:[ gropAction value:0 value:0 value:0 value:bottom ].

    bindings at:#applyAction   put:[ gropAction value:left value:right value:top value:bottom ].

    (self openDialogInterface:#gropDialogSpec withBindings:bindings) 
    ifFalse:[ 
        firstChange ~~ true ifTrue:[
            imageEditView undo.
            self updateImagePreView.
        ]
    ].

    "Created: / 7.9.1998 / 18:16:07 / cg"
    "Modified: / 7.9.1998 / 18:20:42 / cg"
!

doCropRight
    "find a right border and cut it off"

    imageEditView cropLeft:false right:true top:false bottom:false.   
    self updateInfoLabel

    "Created: / 7.9.1998 / 13:00:14 / cg"
    "Modified: / 7.9.1998 / 14:26:44 / cg"
!

doCropTop
    "find a top border and cut it off"

    imageEditView cropLeft:false right:false top:true bottom:false.   
    self updateInfoLabel

    "Created: / 7.9.1998 / 13:00:19 / cg"
    "Modified: / 7.9.1998 / 14:26:52 / cg"
!

doEditMask
    ""

    self image mask edit
!

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 the current image to a new size"

    |box newSize image|

    image := imageEditView image.

    box := EnterBox new.
    box title:(resources string:'Images new size:').
    box okText:(resources string:'OK').
    box abortText:(resources string:'Cancel').
    box initialText:image extent printString.
    box showAtPointer.
    (box accepted 
    and: [(newSize := Object readFromString:(box contents) onError:nil) notNil])
    ifTrue:[
        newSize isPoint ifFalse:[
            self warn:'please enter the new size as ''x @ y''.'.
            ^ self.    
        ].
        imageEditView magnifyImageTo:newSize.
    ].

    self updateInfoLabel
!

doMagnifyImageBy
    "magnifies the current image (by a scale)"

    |box oldSize newSize scale image|

    image := imageEditView image.
    oldSize := image extent.

    box := EnterBox new.
    box title:(resources string:'Scale factor (<1 to shrink; >1 to magnify):').
    box okText:(resources string:'OK').
    box abortText:(resources string:'Cancel').
    box initialText:1 printString.
    box showAtPointer.

    (box accepted 
    and: [(scale := Object readFromString:(box contents) onError:nil) notNil])
    ifTrue:[
        scale isNumber ifFalse:[
            self warn:'please enter a scale factor (<1 to shrink; >1 to magnify).'.
            ^ self.    
        ].
        newSize := oldSize * scale.
        imageEditView magnifyImageTo:newSize.
    ].

    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"

    self image depth ~~ 1 ifTrue:[
        Dialog warn:'Only useful for depth 1 images'.
        ^ self
    ].
    imageEditView negativeImage.
    self listOfColors removeAll.
    self findColorMapMode.     
    imageEditView undoImages removeLast
!

doResizeImage
    "resizes the current image"

    |box newSize image|

    image := imageEditView image.

    box := EnterBox new.
    box title:(resources string:'Images new size:').
    box okText:(resources string:'OK').
    box abortText:(resources string:'Cancel').
    box initialText:image extent printString.
    box showAtPointer.
    (box accepted 
    and: [(newSize := Object readFromString:(box contents) onError:nil) notNil])
    ifTrue:[
        imageEditView resizeImageTo:newSize.
    ].
!

doRotateImage
    "rotates current image"

    |box rotation|

    box := EnterBox new.
    box title:(resources string:'Rotate by (degrees, clockwise):').
    box okText:(resources string:'OK').
    box abortText:(resources string:'Cancel').
    box initialText: '0'.
    box showAtPointer.
    (box accepted and: [(rotation := Object readFromString: box contents onError:nil) notNil])
    ifTrue:[
        imageEditView rotateImageBy:rotation.
        self updateInfoLabel.
    ].
!

doShiftManual
    "let user specify amount and shift"

    |bindings amount img firstChange shiftAction acceptChannel|

    acceptChannel := TriggerValue new.

    firstChange := true.

    shiftAction := 
        [:shiftH :shiftV | 
            acceptChannel value:true.

            img := imageEditView image.
            firstChange ifTrue:[
                imageEditView makeUndo.
                firstChange := false.
            ].
            imageEditView shiftImageHorizontal:shiftH value vertical:shiftV value.
            self updateInfoLabel
        ].

    bindings := IdentityDictionary new.
    bindings at:#shiftAmount put:(amount := 1 asValue).
    bindings at:#acceptChannel put:acceptChannel.

    bindings at:#shiftLeftNow   put:[ shiftAction value:(-1*amount value) value:0 ].
    bindings at:#shiftRightNow  put:[ shiftAction value:amount value value:0 ].
    bindings at:#shiftUpNow     put:[ shiftAction value:0 value:(-1*amount value) ].
    bindings at:#shiftDownNow   put:[ shiftAction value:0 value:amount value ].

    (self openDialogInterface:#shiftDialogSpec withBindings:bindings) 
    ifFalse:[ 
        firstChange ~~ true ifTrue:[
          imageEditView undo
        ]
    ].

    "Created: / 7.9.1998 / 18:16:07 / cg"
    "Modified: / 7.9.1998 / 18:20:42 / cg"
!

doUnCropManual
    "let user specify borders and add them"

    |bindings left top right bottom img|

    bindings := IdentityDictionary new.
    bindings at:#left put:(left := 0 asValue).
    bindings at:#right put:(right := 0 asValue).
    bindings at:#top put:(top := 0 asValue).
    bindings at:#bottom put:(bottom := 0 asValue).
    (self openDialogInterface:#ungropDialogSpec withBindings:bindings)
    ifTrue:[
        left := left value.
        right := right value.
        top := top value.
        bottom := bottom value.
        img := imageEditView image.

        imageEditView
            makeBorderedImageX:left y:top 
            width:(img width + left + right)
            height:(img height + top + bottom).
        self updateInfoLabel
    ].

    "Created: / 7.9.1998 / 18:16:07 / cg"
    "Modified: / 7.9.1998 / 18:20:42 / cg"
!

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"

    |img|

    (imageEditView loadFromClass) notNil ifTrue:[
        imageSeqNr := nil.
        (img := self image) notNil ifTrue: [
            self listOfColors contents: img usedColors asSet asOrderedCollection.
            self findColorMapMode.
            self updateLabelsAndHistory.
        ] ifFalse: [
            self updateForNoImage
        ].
    ]
!

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

    |img file dir filters|

    imageSeqNr := nil.
    img := self image.
    img notNil ifTrue: [
        file := img fileName
    ] ifFalse:[
        dir := LastDirectory
    ].

    filters := FileSelectionBrowser loadImageFileNameFilters.

    file notNil ifTrue:[
        file := FileSelectionBrowser
                    request:'Load Image From'
                    fileName:file
                    withFileFilters:filters.
    ] ifFalse:[
        file := FileSelectionBrowser
                    request:'Load Image From'
                    inDirectory:dir
                    withFileFilters:filters.
    ].
    file notNil ifTrue:[
        LastDirectory := file asFilename directoryName.
        self loadFromFile:file
    ]
!

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

    |aspects width height cMapString cMap imageClass image szString|

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

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

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

        LastSizeString := szString.
        LastColormapMode := cMapString.

        (colorMapMode value endsWith: '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"

    Processor 
        addTimedBlock:[
            |image d8image img|

            imageSeqNr := nil.
            image := Image fromUser.
            image isNil ifFalse:[
                image depth > 8 ifTrue:[
                    Object errorSignal handle:[:ex | |sig|
                        (sig := ex signal) == HaltInterrupt ifTrue:[ex reject].
                        sig == Signal noHandlerSignal ifTrue:[ex reject].

                        self warn:'Could not convert to depth8 image (too many colors)'.
                        d8image := nil.
                    ] do:[
                        d8image := Depth8Image new.
                        d8image fromImage:image.
                    ].
                    d8image notNil ifTrue:[
                        image := d8image
                    ]
                ].
                (imageEditView image:image) notNil ifTrue:[
                    self listOfColors contents:(image colorMap).
                    self findColorMapMode.
                    self updateLabelsAndHistory.
                ]
             ] 
        ] 
        afterSeconds:1

    "Created: / 29.7.1998 / 21:24:42 / cg"
    "Modified: / 16.11.2001 / 16:21:19 / cg"
!

nextImageInSequence
    "display the next image in the image sequence"
    |img seq frame listOfColors|

    imageEditView releaseUndos.

    seq := self image imageSequence.
    imageSeqNr isNil ifTrue:[
        imageSeqNr := 1.
    ].
    imageSeqNr := imageSeqNr + 1.
    imageSeqNr > seq size ifTrue:[
        self information:'Back to first image in sequence'.
        imageSeqNr := 1.
    ].
    frame := seq at:imageSeqNr.
    imageEditView image:(frame image).

    (img := self image) notNil ifTrue:[          
        listOfColors := self listOfColors.
        img colorMap notNil ifTrue:[
            listOfColors contents:(img usedColors asSet asOrderedCollection).
        ] ifFalse:[
            listOfColors removeAll.
        ].
        self findColorMapMode.     
        self updateLabelsAndHistory.
        img := img onDevice:device.
        imageEditView image:img.
    ] ifFalse:[
        self updateForNoImage
    ].

! !

!ImageEditor methodsFor:'user actions - saving'!

doPrint
    "prints current image on the current printer"

    imageEditView print
!

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

    imageEditView saveButtonImageToFileAs.
    self updateLabelsAndHistory.
!

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
    ]
!

doShowStoreString
    "opens a dialog showing the storeString
     (sometimes useful to embed an image into source code)"

    |img|

    img := imageEditView image.
    TextBox openOn:img storeString
! !

!ImageEditor methodsFor:'user actions - settings'!

doChangeGridMagnification
    "change grid magnification"

    |box oldGridLimit newGridLimit|

    oldGridLimit := imageEditView class gridMagnificationLimit asPoint.

    box := EnterBox new.
    box title:(resources string:'Grid Magnification Limit:').
    box okText:(resources string:'OK').
    box abortText:(resources string:'Cancel').
    box initialText:(oldGridLimit x printString).
    box showAtPointer.

    (box accepted 
    and: [(newGridLimit := Number readFromString:(box contents) onError:[2]) notNil]
    ) ifTrue:[
        newGridLimit := (99 min: (2 max:newGridLimit)) asPoint.
        imageEditView class gridMagnificationLimit:newGridLimit.
        imageEditView invalidate
    ]

! !

!ImageEditor class methodsFor:'documentation'!

version
    ^ '$Header$'
! !