MenuItem.st
author convert-repo
Thu, 06 Dec 2018 04:35:51 +0000
changeset 4215 bc67d59fcf46
parent 4168 0656fd9bc4b2
child 4216 91c9929f64e8
permissions -rw-r--r--
update tags

"{ Encoding: utf8 }"

"
 COPYRIGHT (c) 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:libview2' }"

"{ NameSpace: Smalltalk }"

Object subclass:#MenuItem
	instanceVariableNames:'flags activeHelpKey enabled label itemValue nameKey startGroup
		isVisible indication submenu submenuChannel submenuProvider
		shortcutKey labelImage accessCharacterPosition argument choice
		choiceValue font auxValue uuid'
	classVariableNames:'FLAG_FORCE_MENUINDICATOR FLAG_TRANSLATE_LABEL FLAG_ISBUTTON
		FLAG_HIDE_ONACTIVATED FLAG_TRIGGER_ONDOWN
		FLAG_SHOW_BUSYCURSOR_WHILE_PERFORMING FLAG_KEEP_LINKEDMENU
		FLAG_SEND_TO_ORIGINATOR FLAG_IGNORE_MNEMONIC_KEYS
		FLAG_IGNORE_SHORTCUTS FLAG_IS_MENUSLICE FLAG_HORIZONTAL_LAYOUT
		FLAG_NOHIDE_ONACTIVATED FLAG_NOTRANSLATE_LABEL'
	poolDictionaries:''
	category:'Views-Support'
!

!MenuItem class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 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
"
    This together with MenuPanel will eventually replace
    most of the MenuView and PopUpMenu stuff.
    (and hopefully be ST-80 compatible ...)

    For now, only a subset of the full protocol is implemented.

    new:
        %(xx) in label text will be expanded by asking the application
        for the xx labelFor:-aspect.

    [author:]
        Claus Gittinger

    [instance variables:]
        label: MUST be a CharacterArray.

    [see also:]
        MenuItem
        PopUpMenu
"
! !

!MenuItem class methodsFor:'instance creation'!

label:aString
    "create and return a new menuItem, given a label string"

    ^ self new
        label:aString;
        yourself

    "Created: / 14-08-1998 / 19:19:14 / cg"
!

label:labelString choice:choiceAspect choiceValue:selectorOrValue
    "create and return a new menuItem, given its label and choice/value.
     This will create a Radio-Button-like item."

    ^ (self new)
        label:labelString; choice:choiceAspect; choiceValue:selectorOrValue;
        yourself

    "Modified (format): / 26-04-2012 / 12:06:41 / cg"
!

label:labelString itemValue:selectorOrValue
    "create and return a new menuItem, given its label and value"

    ^ (self new)
        label:labelString itemValue:selectorOrValue;
        yourself

    "Created: / 09-09-2012 / 13:21:48 / cg"
!

label:labelString itemValue:selectorOrValue argument:messageArg
    "create and return a new menuItem, given its label and value
     and an argument to be passed with the action"

    ^ (self new)
        label:labelString;
        itemValue:selectorOrValue argument:messageArg;
        yourself

    "Created: / 09-09-2012 / 13:22:31 / cg"
!

label:labelString itemValue:selectorOrValue enabled:enabledHolder
    "create and return a new menuItem, given its label, value
     and enabled holder, which can be a boolean, a boolean valueHolder,
     a block or a selector (to be sent to the application)"

    ^ (self new)
        label:labelString itemValue:selectorOrValue enabled:enabledHolder

    "Created: / 09-09-2012 / 13:23:05 / cg"
!

label:labelString itemValue:selectorOrValue translateLabel:translateLabel
    "create and return a new menuItem, given its label, value and translateLabel flag"

    ^ (self new)
        label:labelString
        itemValue:selectorOrValue
        translateLabel:translateLabel
!

label:labelString submenuChannel:aSymbolOrValueHolder
    "create and return a new menuItem, given its label and value"

    ^ (self new)
        label:labelString submenuChannel:aSymbolOrValueHolder;
        yourself
!

label:labelString value:selectorOrValue
    <resource: #obsolete>
    "create and return a new menuItem, given its label and value"

    ^ self label:labelString itemValue:selectorOrValue

    "Created: / 04-08-1998 / 17:34:18 / cg"
!

label:labelString value:selectorOrValue argument:messageArg
    <resource: #obsolete>
    "create and return a new menuItem, given its label and value"

    ^ (self new)
        label:labelString; value:selectorOrValue; argument:messageArg;
        yourself

    "Created: / 26-04-2012 / 12:06:34 / cg"
!

label:labelString value:selectorOrValue enabled:enabledHolder
    <resource: #obsolete>
    "create and return a new menuItem, given its label and value"

    ^ (self new)
        label:labelString; value:selectorOrValue;
        enabled:enabledHolder;
        yourself

    "Created: / 29-10-2010 / 12:21:27 / cg"
!

labeled:aString
    <resource: #obsolete>

    "create and return a new menuItem, given a label string"

    ^ self label:aString

    "Modified: / 26-04-2012 / 12:07:12 / cg"
!

separator
    "create and return a new menuItem for a '----' separator"

    ^ self separator:'-'

    "Modified: / 19-07-2017 / 09:17:30 / cg"
!

separator2
    "create and return a new menuItem for a '====' separator"

    ^ self separator:'='

    "Created: / 19-07-2017 / 09:16:54 / cg"
!

separator:sepString
    "create and return a new menuItem for a separator"

    ^ self new
        label:sepString; translateLabel:false;
        yourself

    "Created: / 19-07-2017 / 09:17:21 / cg"
! !

!MenuItem class methodsFor:'class initialization'!

initialize
    FLAG_HORIZONTAL_LAYOUT                := 16r0001.
    FLAG_NOTRANSLATE_LABEL                := 16r0002.
    FLAG_ISBUTTON                         := 16r0004.
    FLAG_NOHIDE_ONACTIVATED               := 16r0008.
    FLAG_TRIGGER_ONDOWN                   := 16r0010.
    FLAG_SHOW_BUSYCURSOR_WHILE_PERFORMING := 16r0020.
    FLAG_KEEP_LINKEDMENU                  := 16r0040.
    FLAG_SEND_TO_ORIGINATOR               := 16r0080.
    FLAG_IGNORE_MNEMONIC_KEYS             := 16r0100.
    FLAG_IGNORE_SHORTCUTS                 := 16r0200.
    FLAG_IS_MENUSLICE                     := 16r0400.
    FLAG_FORCE_MENUINDICATOR              := 16r0800.

    "Created: / 09-08-2018 / 16:36:25 / Claus Gittinger"
    "Modified: / 14-08-2018 / 12:29:30 / Claus Gittinger"
! !

!MenuItem class methodsFor:'constants'!

supportedStartGroupValues
    "start group #left #right #conditionalRight ...
     At the moment only #left/nil, #right/#conditionalRight are implemented.

     The meanings are:
        nil                 - default under control of the menu
        #left               - left align
        #right              - place at the right far end
        #conditionalRight   - controlled by a styleSheet variable;
                              like #right under all non-win32 systems,
                              ignored on win32.
                              Use with help-menu, which should be at the far right on some
                              viewStyles, but not under win32."

    ^ #(nil #left #right #conditionalRight)

    "Created: / 16-10-2006 / 13:09:27 / cg"
! !

!MenuItem methodsFor:'Compatibility-ST80'!

enablementSelector:aSymbol
    "dummy for now - for visualworks compatibility (specs)"

    "/ self halt.
!

helpText:aString
    "dummy for now - for visualworks compatibility (specs)"

    activeHelpKey := aString.
!

indicationSelector:aSymbol
    "dummy for now - for visualworks compatibility (specs)"

    "/ self halt.
!

isEnabled:aBoolean
    self enabled:aBoolean

    "Created: / 27.10.1997 / 16:34:55 / cg"
!

shortcutModifiers:something
    "dummy for now - for visualworks compatibility (specs)"

    "/ self halt.
!

value
    "obsolete - please use #itemValue (value is bad: it prevents us from using a valueHolder)"

    ^ itemValue

    "Created: 25.2.1997 / 19:50:14 / cg"
!

value:something
    "obsolete - please use #itemValue: (value is bad: it prevents us from using a valueHolder)"

    itemValue := something

    "Created: 25.2.1997 / 19:11:13 / cg"
! !

!MenuItem methodsFor:'accessing'!

accessCharacterPosition
    "get the index of the access character in the label text or string, or nil if none"

    ^ accessCharacterPosition
!

accessCharacterPosition:index 
    "set the index of the access character in the label text or string, or nil if none"

    accessCharacterPosition := index
!

activeHelpKey
    "the key used as index (and argument) to the helpSpec"

    ^ activeHelpKey
!

activeHelpKey:aKey
    "the key used as index (and argument) to the helpSpec.
     New: may also be an association; then the assoc's key is the help-text provider,
     and the assoc's value is the key in that provider's helpspec."

    activeHelpKey := aKey

    "Modified (comment): / 10-08-2018 / 11:39:30 / Claus Gittinger"
!

argument
    "get argument given to the value (selector)"

    ^ argument
!

argument:something
    "set argument given to the value (selector)"

    |arg|

    (arg := something) notNil ifTrue:[
        arg isString ifTrue:[
            (arg size == 0 or:[ (arg indexOfNonSeparatorStartingAt:1) == 0 ]) ifTrue:[
                arg := nil
            ]
        ]
    ].
    argument := arg
!

auxValue
    "an additional, arbitrary value"

    ^ auxValue
!

auxValue:something
    "set the auxValue - an arbitrary user value"

    auxValue := something
!

choice:something
    choice := something.

    "Created: / 14.8.1998 / 14:32:06 / cg"
!

choiceValue
    ^ choiceValue

    "Created: / 14.8.1998 / 15:39:26 / cg"
!

choiceValue:something
    choiceValue := something.

    "Created: / 14.8.1998 / 15:39:26 / cg"
!

hierarchicalUUID

    ^ self uuid
!

ignoreMnemonicKeys
    "if true, mnemonic (access character) in the submenus under the item are ignored;
     Set this to speedup accelerator key processing for slow dynamci menus.
     The default is set to false"

    ^ (flags ? 0) bitTest:FLAG_IGNORE_MNEMONIC_KEYS "/ ignoreMnemonicKeys ? false

    "Modified (comment): / 08-09-2011 / 04:30:19 / cg"
    "Modified: / 09-08-2018 / 16:41:53 / Claus Gittinger"
!

ignoreMnemonicKeys:aBoolean
    "if true, mnemonic (access character) in the submenus under the item are ignored;
     Set this to speedup accelerator key processing for slow dynamci menus.
     The default is false"

    "/ ignoreMnemonicKeys := aBoolean.
    flags := (flags ? 0) changeMask:FLAG_IGNORE_MNEMONIC_KEYS to:aBoolean.

    "Modified (comment): / 08-09-2011 / 04:52:23 / cg"
    "Modified: / 09-08-2018 / 16:43:08 / Claus Gittinger"
!

ignoreShortcutKeys
    "if true, shortcutKeys (accelerators) in the submenus under the item are ignored;
     the default is set to false"

    ^ (flags ? 0) bitTest:FLAG_IGNORE_SHORTCUTS "/ ignoreShortcutKeys ? false

    "Modified: / 09-08-2018 / 16:43:26 / Claus Gittinger"
!

ignoreShortcutKeys:aBoolean
    "if true, shortcutKeys (accelerators) in the submenus under the item are ignored;
     the default is set to false"

    "/ ignoreShortcutKeys := aBoolean.
    flags := (flags ? 0) changeMask:FLAG_IGNORE_SHORTCUTS to:aBoolean.

    "Modified: / 09-08-2018 / 16:43:49 / Claus Gittinger"
!

indication
    "value of the items on/off indicator (CheckToggle).
     Usually a valueHolder"

    ^ indication

    "Created: 25.2.1997 / 20:59:28 / cg"
!

indication:someValueHolder
    "value of the items on/off indicator (CheckToggle).
     someValueHolder is usually a value holder"

    indication := someValueHolder.

    "Created: 25.2.1997 / 20:59:28 / cg"
!

isVisible
    "a visibility holder; either a block, boolean or value holder.
     If the menu is permanently visible, it should be a valueHolder,
     as the value is otherwise only checked for before the menu is opened"

    ^ isVisible ? true
!

isVisible:something
    "a visibility holder; either a block, boolean or value holder.
     If the menu is permanently visible, it should be a valueHolder,
     as the value is otherwise only checked for before the menu is opened"

    isVisible := something
!

itemValue
    "the item's action; if it is a block, that is evaluated;
     if a symbol, that message is sent to the menu's receiver"

    ^ itemValue

    "Created: 25.2.1997 / 19:50:14 / cg"
!

itemValue:aBlockOrSymbol
    "set the item's action; if it is a block, that is evaluated;
     if a symbol, that message is sent to the menu's receiver"

    itemValue := aBlockOrSymbol

    "Created: 25.2.1997 / 19:11:13 / cg"
!

itemValue:selectorOrBlock argument:anArgument
    "set the item's action and an argument; if it is ablock, that is evaluated;
     if a symbol, that message is sent to the menu's receiver"

    itemValue := selectorOrBlock.
    argument := anArgument

    "Created: / 09-09-2012 / 13:05:14 / cg"
!

label:aString icon:anImage
    self label:aString.
    self resourceRetriever: (ResourceRetriever icon:anImage string:'')
!

label:labelString itemValue:selectorOrBlock
    label := labelString.
    itemValue := selectorOrBlock.

    "Created: / 09-09-2012 / 13:18:34 / cg"
    "Modified (comment): / 14-02-2018 / 12:55:40 / mawalch"
!

label:labelString itemValue:selectorOrBlock argument:something
    "define label, value and an argument to be passed with the action"

    self assert:(labelString isNil or:[labelString isString]).

    label := labelString.
    itemValue := selectorOrBlock.
    argument := something.

    "Created: / 09-09-2012 / 13:19:36 / cg"
    "Modified: / 24-01-2018 / 14:34:10 / mawalch"
!

label:labelString itemValue:selectorOrBlock enabled:enabledHolder
    "define label, value
     and enabled holder, which can be a boolean, a boolean valueHolder,
     a block or a selector (to be sent to the application)"

    self assert:(labelString isNil or:[labelString isString]).

    label := labelString.
    itemValue := selectorOrBlock.
    enabled := enabledHolder.

    "Modified: / 24-01-2018 / 14:34:18 / mawalch"
!

label:labelString itemValue:selectorOrBlock translateLabel:translateLabelBoolean
    self assert:(labelString isNil or:[labelString isString]).

    label := labelString.
    itemValue := selectorOrBlock.
    self translateLabel:translateLabelBoolean.

    "Modified: / 24-01-2018 / 14:34:24 / mawalch"
    "Modified: / 09-08-2018 / 17:27:37 / Claus Gittinger"
!

labelImage
    ^ labelImage
!

labelImage:something
    labelImage := something.

    "/ VW compatibility: if I have a label also,
    "/ this will become a LabelAndImage.
    (something isImage and:[label notNil]) ifTrue:[
        labelImage := LabelAndIcon icon:labelImage string:label.
    ].

    "Modified: / 26-09-2017 / 12:29:32 / stefan"
!

nameKey
    ^ nameKey


!

nameKey:aNameKey
    self assert:(aNameKey isNil or:[aNameKey isString]).
    nameKey := aNameKey.

    "Modified: / 14-02-2018 / 12:24:56 / mawalch"
!

rawLabel
    ^ label

    "Created: 25.2.1997 / 19:48:16 / cg"
!

rawLabel:aString
    self assert:(aString isNil or:[aString isString]).

    label := aString

    "Created: / 25-02-1997 / 19:11:02 / cg"
    "Modified: / 24-01-2018 / 14:34:36 / mawalch"
!

resourceRetriever
    |retriever|

    retriever := labelImage.
    (retriever notNil and:[retriever isKindOf:ResourceRetriever]) ifTrue:[
        ^ retriever
    ].
    ^ nil
!

resourceRetriever:aRetriever
    self labelImage:aRetriever
!

shortcutKey
    "get the key to press to select the menu item from the keyboard"

    ^ shortcutKey
!

shortcutKey:something
    "set the  key to press to select the menu item from the keyboard"

    shortcutKey := something.
!

shortcutKeyCharacter
    "Backward compatibility; same as #shortcutKey.
     get the  key to press to select the menu item from the keyboard"

    ^ self shortcutKey
!

shortcutKeyCharacter:something
    "Backward compatibility; same as #shortcutKey:.
     set the  key to press to select the menu item from the keyboard"

    self shortcutKey:something.
!

submenu
    "get the submenu or nil"

    ^ submenu value

    "Created: / 25.2.1997 / 20:59:28 / cg"
    "Modified: / 19.6.1998 / 00:33:58 / cg"
!

submenu:aMenu
    "set the submenu or nil"

    (aMenu isValueModel or:[ aMenu isBlock ]) ifTrue:[
        submenuChannel notNil ifTrue:[
            "/ programmers error ?
            "/ how can I decide which one to use if there is both
            "/ a channel and a subMenu ...
            self error:'overwriting submenuchannel' mayProceed:true.
        ].
        submenuChannel := aMenu
    ] ifFalse:[
        submenu := aMenu.
    ]

    "Created: / 25.2.1997 / 20:59:28 / cg"
    "Modified: / 5.2.2000 / 16:53:28 / cg"
!

submenuProvider
    "useful if a sub-menu is plugged in from another application (i.e. when
     a submenu is fetched via a channel AND the top-menus application cannot provide
     it"

    ^ submenuProvider

    "Created: / 22-09-2010 / 13:55:55 / cg"
!

submenuProvider:something
    "useful if a sub-menu is plugged in from another application (i.e. when
     a submenu is fetched via a channel AND the top-menus application cannot provide
     it"

    submenuProvider := something.

    "Created: / 22-09-2010 / 13:55:40 / cg"
!

uuid
    "automationID
     return my uuid component to be used for resource-access.
     This is a unique and once-assigned-never-changed uuid by which
     UI automators can access the menu item."

    ^ uuid

    "Modified (comment): / 09-08-2018 / 16:47:39 / Claus Gittinger"
!

uuid:aUUID
    "automationID
     set my uuid component to be used for resource-access
     (called by menuBuilder).
     This is a unique and once-assigned-never-changed uuid by which
     UI automators can access the menu item."

    uuid := aUUID

    "Modified (comment): / 09-08-2018 / 16:47:34 / Claus Gittinger"
! !

!MenuItem methodsFor:'accessing-behavior'!

beOff
    "set indication off"

    self indication:false
!

beOn
    "set indication on"

    self indication:true
!

choice
    "return the menu items choice indicator (RadioButton)"

    ^ choice

    "Created: / 14.8.1998 / 14:34:55 / cg"
    "Modified: / 14.8.1998 / 15:11:57 / cg"
!

disable
    enabled := false

    "Created: 25.2.1997 / 19:39:09 / cg"
!

enable
    enabled := true

    "Created: 25.2.1997 / 19:39:00 / cg"
!

enabled
    "returns a boolean, valueHolder or block"

    ^ enabled ? true

    "Modified: / 12-07-2018 / 22:29:33 / Claus Gittinger"
!

enabled:enabledHolder
    "enabledHolder can be a boolean, a boolean valueHolder,
     a block or a selector (to be sent to the application)"

    enabled := enabledHolder
!

hideMenuOnActivated
    "hide the menu when the item was activated; the default is true"

    "/ notice: here the default is true
    ^ ((flags ? 0) bitTest:FLAG_NOHIDE_ONACTIVATED) not "/ hideMenuOnActivated ? true

    "Modified: / 14-08-2018 / 12:28:18 / Claus Gittinger"
!

hideMenuOnActivated:aBoolean
   "hide the menu when the item was activated; the default is true"

    "/ notice: here the default is true

    "/ hideMenuOnActivated := aBool ? true
    flags := (flags ? 0) changeMask:FLAG_NOHIDE_ONACTIVATED to:aBoolean not.

    "Modified: / 14-08-2018 / 12:28:22 / Claus Gittinger"
!

keepLinkedMenu
    "if true, a linked submenu menu is only computed once (slightly faster),
     but then, the linked menu cannot be dynamic"

    ^ (flags ? 0) bitTest:FLAG_KEEP_LINKEDMENU "/ keepLinkedMenu ? false

    "Modified: / 09-08-2018 / 16:52:56 / Claus Gittinger"
!

keepLinkedMenu:aBoolean
    "if true, a linked submenu menu is only computed once (slightly faster),
     but then, the linked menu cannot be dynamic"

    "/ keepLinkedMenu := aBoolean ? false.
    flags := (flags ? 0) changeMask:FLAG_KEEP_LINKEDMENU to:aBoolean.

    "Modified: / 09-08-2018 / 16:53:12 / Claus Gittinger"
!

sendToOriginator
    "if true, the message is sent to the originating widget;
     otherwise (the default), it it sent to the receiver/application."

    ^ (flags ? 0) bitTest:FLAG_SEND_TO_ORIGINATOR "/ sendToOriginator ? false

    "Modified: / 09-08-2018 / 16:51:28 / Claus Gittinger"
!

sendToOriginator:aBoolean
    "if true, the message is sent to the originating widget;
     otherwise (the default), it it sent to the receiver/application."

    "/ sendToOriginator := aBoolean
    flags := (flags ? 0) changeMask:FLAG_SEND_TO_ORIGINATOR to:aBoolean.

    "Modified: / 09-08-2018 / 16:51:51 / Claus Gittinger"
!

showBusyCursorWhilePerforming
    "get the flag which controls if a busy cursor is to be shown
     while performing the menu action. Defaults to false."

    ^ (flags ? 0) bitTest:FLAG_SHOW_BUSYCURSOR_WHILE_PERFORMING "/ showBusyCursorWhilePerforming ? false

    "Modified: / 09-08-2018 / 16:52:14 / Claus Gittinger"
!

showBusyCursorWhilePerforming:aBoolean
    "set/clear the flag which controls if a busy cursor is to be shown
     while performing the menu action. Defaults to false."

    "/ showBusyCursorWhilePerforming := aBoolean
    flags := (flags ? 0) changeMask:FLAG_SHOW_BUSYCURSOR_WHILE_PERFORMING to:aBoolean.

    "Modified: / 09-08-2018 / 16:52:28 / Claus Gittinger"
!

translateLabel
    "returns true if the label is to be translated.
     The default is true"

    "/ notice: here the default is true
    ^ ((flags ? 0) bitTest:FLAG_NOTRANSLATE_LABEL) not "/ translateLabel ? true

    "Modified: / 14-08-2018 / 12:29:23 / Claus Gittinger"
!

translateLabel:aBoolean
    "sets whether label is translated"

    "/ notice: here the default is true

    "/ translateLabel := aBoolean
    flags := (flags ? 0) changeMask:FLAG_NOTRANSLATE_LABEL to:aBoolean not.

    "Modified: / 14-08-2018 / 12:29:12 / Claus Gittinger"
!

triggerOnDown
   "returns true if the action is triggered when pressed.
    The default is false (trigger when released)"

    ^ (flags ? 0) bitTest:FLAG_TRIGGER_ONDOWN "/ triggerOnDown ? false

    "Modified: / 09-08-2018 / 16:53:49 / Claus Gittinger"
!

triggerOnDown:aBoolean
   "controls if the action is triggered when pressed or released"

    "/ triggerOnDown := aBoolean.
    flags := (flags ? 0) changeMask:FLAG_TRIGGER_ONDOWN to:aBoolean.

    "Modified: / 09-08-2018 / 16:54:10 / Claus Gittinger"
! !

!MenuItem methodsFor:'accessing-channels'!

label:labelString submenuChannel:aSymbolOrValueHolder
    self assert:(labelString isNil or:[labelString isString]).

    label := labelString.
    submenuChannel := aSymbolOrValueHolder.

    "Modified: / 24-01-2018 / 14:36:54 / mawalch"
!

submenuChannel
    "get the submenuChannel or nil"

    ^ submenuChannel
!

submenuChannel:aMenuHolder
    "set the submenuChannel or nil"

    submenuChannel := aMenuHolder.
! !

!MenuItem methodsFor:'accessing-look'!

font
    "get the specific font for an item or nil"

    ^ font
!

font:aFont
    "set a specific font for an item"

    font := aFont.
!

hasMenuIndicator
    "returns whether item should always show a menu-indicator (arrow)"

    ^ (flags ? 0) bitTest:FLAG_FORCE_MENUINDICATOR "/ forceMenuIndicator ? false

    "Created: / 09-08-2018 / 16:35:05 / Claus Gittinger"
!

hasMenuIndicator:aBoolean
    "force the item to always show a menu-indicator (arrow)"

    flags := (flags ? 0) changeMask:FLAG_FORCE_MENUINDICATOR to:aBoolean.

    "Created: / 09-08-2018 / 16:49:11 / Claus Gittinger"
!

horizontalLayout
    "by default, submenus have a vertical layout;
     if true, the submenu has a horizontal layout."

    ^ (flags ? 0) bitTest:FLAG_HORIZONTAL_LAYOUT "/ horizontalLayout ? false

    "Modified: / 09-08-2018 / 16:46:50 / Claus Gittinger"
!

horizontalLayout:aBoolean
    "by default, submenus have a vertical layout;
     if true, the submenu has a horizontal layout."

    "/ horizontalLayout := aBoolean.
    flags := (flags ? 0) changeMask:FLAG_HORIZONTAL_LAYOUT to:aBoolean.

    "Modified: / 09-08-2018 / 16:47:05 / Claus Gittinger"
!

icon:anImage
    self resourceRetriever: (ResourceRetriever icon:anImage)
!

isButton
    "returns whether item looks like a Button
    "
    ^ (flags ? 0) bitTest:FLAG_ISBUTTON "/ isButton ? false

    "Modified: / 09-08-2018 / 16:49:32 / Claus Gittinger"
!

isButton:aBoolean
    "sets whether item looks like a Button
    "
    "/ isButton := aBoolean
    flags := (flags ? 0) changeMask:FLAG_ISBUTTON to:aBoolean.

    "Modified: / 09-08-2018 / 16:49:56 / Claus Gittinger"
!

isMenuSlice
    "returns true if the menu is a slice,
     building its items into its container menu
    "
    ^ submenuChannel notNil 
    and:[ (flags ? 0) bitTest:FLAG_IS_MENUSLICE "isMenuSlice == true"].

    "Modified: / 09-08-2018 / 16:44:37 / Claus Gittinger"
!

isMenuSlice:aBoolean
    "/ isMenuSlice := aBoolean.
    flags := (flags ? 0) changeMask:FLAG_IS_MENUSLICE to:aBoolean.

    "Modified: / 09-08-2018 / 16:44:58 / Claus Gittinger"
!

label
    ^ self filteredLabel "/ label

    "Created: / 25.2.1997 / 19:48:16 / cg"
    "Modified: / 19.6.1998 / 00:02:55 / cg"
!

label:aString
    label := aString.

    "Created: / 25-02-1997 / 19:55:16 / cg"
    "Modified: / 14-02-2018 / 12:23:41 / mawalch"
!

startGroup
    "start group #left #right #conditionalRight ... or nil
     at the moment only #right and #conditionalRight are implemented"

    ^ startGroup ? #left

    "Modified: / 16-10-2006 / 13:05:52 / cg"
!

startGroup:aSymbol
    "start group #left #right #conditionalRight ...
     at the moment only #right and #conditionalRight are implemented.
     The meanings are:
        nil                 - default under control of the menu
        #left               - left align
        #right              - place at the right far end
        #conditionalRight   - controlled by a styleSheet variable;
                              like #right under all non-win32 systems,
                              ignored on win32.
                              Use with help-menu, which should be at the far right on some
                              viewStyles, but not under win32.
    "

    (self class supportedStartGroupValues includes:aSymbol) ifTrue:[
        startGroup := aSymbol
    ] ifFalse:[
        self warn:('unsupported group: ', aSymbol printString ).
        startGroup := #left
    ]

    "Modified: / 16-10-2006 / 13:09:45 / cg"
! !

!MenuItem methodsFor:'accessing-resource'!

findGuiResourcesIn:aResourceContainerOrApplication
    "resolve national language translations from aResourceContainerOrApplication"

    self
        findGuiResourcesIn:aResourceContainerOrApplication
        rememberResourcesIn:nil
!

findGuiResourcesIn:aResourceContainerOrApplication rememberResourcesIn:aValueHolderOrNil
    "resolve national language translations from aResourceContainerOrApplication.
     In addition, expand %(xxx) by asking the application for an xxx aspect."

    |retriever m itemFont|

    (aResourceContainerOrApplication isNil or:[self isMenuSlice]) ifTrue:[
        ^ self.
    ].

    "while traversing all the menus, resolve symbolic fonts too"
    itemFont := self font.
    itemFont isSymbol ifTrue:[
        itemFont := aResourceContainerOrApplication resolveFont:itemFont.
        self font:itemFont.
    ].

    (self translateLabel and:[label ~= '-']) ifTrue:[
        label := ResourceRetriever
                        findResourceLabel:label
                        in:aResourceContainerOrApplication
                        rememberResourcesIn:aValueHolderOrNil.
    ].
    (label notNil and:[label includesString:'%(']) ifTrue:[
        |fetchDict|

        fetchDict := VirtualDictionary new.
        fetchDict
            fetchBlock:[:key |
                (aResourceContainerOrApplication labelFor:key asSymbol) value
            ].
        label := label bindWithArguments:fetchDict.
    ].

    (retriever := self resourceRetriever) notNil ifTrue:[
        retriever findGuiResourcesIn:aResourceContainerOrApplication.
        retriever labelText notNil ifTrue: [retriever labelText:label].
    ].
    (m := self submenu) notNil ifTrue:[
        m := m value.
        m notNil ifTrue:[
            m findGuiResourcesIn:aResourceContainerOrApplication rememberResourcesIn:aValueHolderOrNil
        ]
    ]

    "Modified: / 18-06-1998 / 16:54:25 / cg"
    "Modified: / 28-12-2017 / 18:36:31 / stefan"
!

receiver:aReceiver perform:aSelector with:anArgument ifNone:aBlock
    "send the one-arg-message aSelector to the application;
     the result returned from the send or nil is returned
    "
    |result|

    MessageNotUnderstood handle:[:ex|
        ex selector ~~ aSelector ifTrue:[
            ex reject
        ].
        result := aBlock value.
    ] do:[
        aSelector numArgs == 0 ifTrue:[
            result := aReceiver perform:aSelector.
        ] ifFalse:[
            result := aReceiver perform:aSelector with:anArgument.
        ].
    ].
    ^ result
!

resolveSliceMenuItemsIn:aReceiver for:originalReceiver rememberResourcesIn:aValueHolderOrNil
    |menu|

    self isMenuSlice ifFalse:[
        self error:'not a slice menu' mayProceed:true.
        ^ Array with:self.
    ].

    aReceiver isNil ifTrue:[
        ^ nil
    ].
    menu := self
                receiver:aReceiver perform:submenuChannel with:argument
                ifNone:[
                    self
                        receiver:(aReceiver class) perform:submenuChannel with:argument 
                        ifNone:[
                            "/ ('MenuItem [info]: no aspect for slice ',submenuChannel) infoPrintCR.
                            nil
                        ].
                ].

    menu := menu value.
    menu isNil ifTrue:[^ nil].

    menu isCollection ifTrue:[
        menu := Menu decodeFromLiteralArray:menu.
    ].
    menu findGuiResourcesIn:originalReceiver rememberResourcesIn:aValueHolderOrNil.
    ^ menu items

    "Created: / 21-07-2017 / 19:46:54 / cg"
!

resolveSliceMenuItemsIn:aReceiver rememberResourcesIn:aValueHolderOrNil
    ^ self resolveSliceMenuItemsIn:aReceiver for:aReceiver rememberResourcesIn:aValueHolderOrNil

    "Modified: / 21-07-2017 / 19:48:45 / cg"
! !

!MenuItem methodsFor:'encoding & decoding'!

fromLiteralArrayEncoding:aLiteralEncodedArray
    "read my contents from a aLiteralEncodedArray"

    |selector value retriever|

    2 to:aLiteralEncodedArray size by:2 do:[:i |
        selector := aLiteralEncodedArray at:i.
        value    := (aLiteralEncodedArray at:i+1).
        selector == #argument: ifFalse:[
            value := value decodeAsLiteralArray
        ].
        self perform:selector with:value
    ].
    "/ kludge in case someone forgets to call findGUIResourcesIn:
    (retriever := self resourceRetriever) notNil ifTrue:[
        retriever labelText notNil ifTrue: [retriever labelText: label].
    ].

    "
     #( #MenuItem #rawLabel: 'left' #nameKey: 'identifier'  #value: #left )
         decodeAsLiteralArray
    "

    "Modified: / 4.2.2000 / 12:50:28 / cg"
!

skippedInLiteralEncoding
    "define the inst-slots which are skipped when generating a literalArrayEncoding;
     (to skip the ones with default values.)"

    |coll|

    coll := super skippedInLiteralEncoding asOrderedCollection.

    coll add:#flags.
    
    label isNil ifTrue:[ coll add:#label ].
    (self translateLabel or:[#('' '-' '=') includes:label]) ifTrue:[ coll add:#translateLabel ].
    self isButton ifFalse:[ coll add:#isButton ].
    self triggerOnDown ifFalse:[ coll add:#triggerOnDown ].
    self hideMenuOnActivated ifTrue:[ coll add:#hideMenuOnActivated].
    nameKey isNil ifTrue:[ coll add:#nameKey ].
    (self isVisible == true) ifTrue:[coll add:#isVisible].
    (startGroup isNil or:[startGroup == #left]) ifTrue:[coll add:#startGroup].
    itemValue isNil ifTrue:[ coll add:#itemValue].
    activeHelpKey isNil ifTrue:[ coll add:#activeHelpKey].
    enabled == true "could be a symbol" ifTrue:[ coll add:#enabled].
    shortcutKey isNil ifTrue:[ coll add:#shortcutKeyCharacter].
    font isNil ifTrue:[ coll add:#font].
    accessCharacterPosition isNil ifTrue:[coll add:#accessCharacterPosition].
    self horizontalLayout ifFalse:[coll add:#horizontalLayout].
    self showBusyCursorWhilePerforming ifFalse:[ coll add:#showBusyCursorWhilePerforming ].
    argument isNil ifTrue:[ coll add:#argument].
    self resourceRetriever isNil ifTrue:[ coll add:#resourceRetriever].
    indication isNil ifTrue:[ coll add:#indication].
    choice isNil ifTrue:[coll add:#choice].
    choiceValue isNil ifTrue:[ coll add:#choiceValue].
    auxValue isNil ifTrue:[ coll add:#auxValue ].
    submenuChannel isNil ifTrue:[ coll add:#submenuChannel].
    self keepLinkedMenu ifFalse:[ coll add:#keepLinkedMenu].
    submenu value isNil ifTrue:[coll add:#submenu ].
    self sendToOriginator ifFalse:[coll add:#sendToOriginator ].

    self ignoreShortcutKeys ifFalse:[coll add:#ignoreShortcutKeys ].
    self ignoreMnemonicKeys ifFalse:[coll add:#ignoreMnemonicKeys ].
    self hasMenuIndicator ifFalse:[coll add:#hasMenuIndicator ].

    self isMenuSlice ifFalse:[coll add:#isMenuSlice].

    ^ coll

    "Modified: / 22-08-2012 / 21:22:20 / cg"
    "Modified: / 09-08-2018 / 17:34:37 / Claus Gittinger"
!

virtualSlotsInLiteralEncoding
    "defines additional virtual slots in the literalEncoding.
     These are not instvars, but accessed via getters and setters during
     store and load.
     Use this when flags encode values which were previously encoded as boolean instvars,
     to remain backward compatible"

    ^ #( ignoreMnemonicKeys ignoreShortcutKeys hideMenuOnActivated
         keepLinkedMenu sendToOriginator showBusyCursorWhilePerforming triggerOnDown
         hasMenuIndicator horizontalLayout isButton isMenuSlice translateLabel)

    "
     self new literalArrayEncoding
     self new ignoreMnemonicKeys:true; literalArrayEncoding
     (self new ignoreMnemonicKeys:true; literalArrayEncoding) decodeAsLiteralArray
    "

    "Created: / 09-08-2018 / 17:24:19 / Claus Gittinger"
! !

!MenuItem methodsFor:'printing & storing'!

printOn:aStream
    aStream nextPutAll:self classNameWithArticle.
    aStream nextPutAll:'('.
    aStream nextPutAll:label storeString.
    aStream nextPutAll:')'
! !

!MenuItem methodsFor:'private-accessing'!

filteredLabel
    "return the label without any &-chars"

    |rawLabel l in out c emp e|

    rawLabel := self rawLabel.
    rawLabel isString ifFalse:[^ rawLabel].

    "/ be careful to preserve any emphasis ...
    "/ bad kludge ...
    rawLabel isText ifTrue:[
        emp := RunArray new.
    ].

    out := WriteStream on:''.
    in := rawLabel readStream.
    [in atEnd] whileFalse:[
        emp notNil ifTrue:[
            e := rawLabel emphasisAt:(in position + 1).
        ].
        c := in next.
        c bitsPerCharacter > out collection bitsPerCharacter ifTrue:[
            out setCollection:(c stringSpecies fromString:out collection).
        ].
        c == $& ifTrue:[
            in peek == $& ifTrue:[
                out nextPut:c.
                emp notNil ifTrue:[
                    emp add:e
                ]
            ]
        ] ifFalse:[
            out nextPut:c.
            emp notNil ifTrue:[
                emp add:e
            ]
        ]
    ].
    l := out contents.
    emp notNil ifTrue:[
        ^ Text string:l emphasisCollection:emp
    ].
    ^ l.

    "Created: / 19.6.1998 / 00:02:10 / cg"
    "Modified: / 20.6.1998 / 17:15:18 / cg"
! !

!MenuItem methodsFor:'queries'!

hasChoice
    "test whether choice exists
    "
    ^ self choice notNil

    "Created: / 14-08-1998 / 14:34:29 / cg"
    "Modified (format): / 09-08-2018 / 16:50:14 / Claus Gittinger"
!

hasIndication
    "test whether indication on/off exists
    "
    ^ self indication notNil

    "Modified: / 28-10-2010 / 23:30:52 / cg"
!

hasSubmenu
    ^ self submenu notNil

    "Created: 25.2.1997 / 20:56:20 / cg"
!

isEnabled
    ^ enabled value ? true

    "Created: 25.2.1997 / 19:39:17 / cg"
!

isHidden
    "not yet supported"

    ^ false

    "Created: / 27.10.1997 / 15:13:43 / cg"
!

isMenuItem
    ^ true
!

isOff
    "test whether indication on/off exists and is off
    "
    |indication|

    indication := self indication.
    ^ indication value == false

    "Modified: / 28-10-2010 / 23:30:57 / cg"
!

isOn
    "test whether indication on/off exists and is on
    "
    |indication|

    indication := self indication.
    ^ indication value == true

    "Modified: / 28-10-2010 / 23:31:02 / cg"
!

isSeparatorItem
    ^ #('' '-' '=') includes:label

    "Created: 25.2.1997 / 19:39:17 / cg"
! !

!MenuItem methodsFor:'utilities'!

replaceArgument: oldValue with: newValue

    "Recusively Replace argument in menu items where
     current argument is equal to oldValue by newValue"

    argument = oldValue ifTrue:[
        argument := newValue.
    ].
    submenu notNil ifTrue:[
        submenu replaceArgument: oldValue with: newValue
    ].

    "Created: / 12-10-2011 / 20:12:28 / Jan Vrany <jan.vrany@fit.cvut.cz>"
! !

!MenuItem class methodsFor:'documentation'!

version
    ^ '$Header$'
!

version_CVS
    ^ '$Header$'
! !


MenuItem initialize!