AssistantApplication.st
author Claus Gittinger <cg@exept.de>
Wed, 21 Oct 2009 15:32:50 +0200
changeset 3809 3c92fe6d04a5
parent 3808 1addc5e0331e
child 3810 0df3ba9c453a
permissions -rw-r--r--
added: #flyByHelpSpec #flyByHelpTextForKey: changed: #windowSpec

"{ Package: 'stx:libwidg2' }"

ApplicationModel subclass:#AssistantApplication
	instanceVariableNames:'currentPageSpecHolder backButtonEnabled forwardButtonEnabled
		backButtonVisible forwardButtonVisible finishButtonVisible
		finishButtonEnabled currentPageInfoHTMLHolder
		currentPageIndexHolder backButtonLabelHolder
		nextButtonLabelHolder pageLabelsInList'
	classVariableNames:''
	poolDictionaries:''
	category:'Views-DialogBoxes'
!

!AssistantApplication class methodsFor:'documentation'!

documentation
"
    an easy to use framework for assistant-dialog applications.
    These are multipage applications, in which the user is able to navigate
    along achain of pages as specified in the AssistantSpec

    see stx:doc/coding:DemoAssistant for a demo example.
    see ProjectBuilderAssistantApplication for a concrete example.
"
! !

!AssistantApplication class methodsFor:'help'!

flyByHelpSpec
    <resource: #help>

    ^ super flyByHelpSpec addPairsFrom:#(

#backButton
'Navigate to the previous page %(previousPageInfo)'

#nextButton
'Navigate to the next page %(nextPageInfo)'

)
! !

!AssistantApplication class methodsFor:'image specs'!

bulletIcon
    "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 bulletIcon inspect
     ImageEditor openOnClass:self andSelector:#bulletIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:'AssistantApplication class bulletIcon'
        ifAbsentPut:[(Depth2Image new) width: 16; height: 16; photometric:(#palette); bitsPerSample:(#[2]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@@@@% @@BPA @@P@D@@ @@ @D@@D@A@@A@@ @@ @A@@P@@$@X@@@% @@@@@@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 84 84 84 170 170 170 255 255 255]; mask:((Depth1Image new) width: 16; height: 16; photometric:(#blackIs0); bitsPerSample:(#[1]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@<@O<@?0G? _>A?8G? O<@?0@<@@@@@@@@@b') ; yourself); yourself]
!

leftArrowIcon
    "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 leftArrowIcon inspect
     ImageEditor openOnClass:self andSelector:#leftArrowIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:'AssistantApplication class leftArrowIcon'
        ifAbsentPut:[(Depth2Image new) width: 16; height: 16; photometric:(#palette); bitsPerSample:(#[2]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@@@@@@@@@@@@@@D@@@@G@@@@G??<@A???@@G@@@@@P@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 84 84 84 170 170 170 255 255 255]; mask:((Depth1Image new) width: 16; height: 16; photometric:(#blackIs0); bitsPerSample:(#[1]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@A@@L@A0@O@A?>O?9??''?>O?8_? <@A0@C@@D@@@@b') ; yourself); yourself]
!

rightArrowIcon
    "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 rightArrowIcon inspect
     ImageEditor openOnClass:self andSelector:#rightArrowIcon
     Icon flushCachedIcons
    "

    <resource: #image>

    ^Icon
        constantNamed:'AssistantApplication class rightArrowIcon'
        ifAbsentPut:[(Depth2Image new) width: 16; height: 16; photometric:(#palette); bitsPerSample:(#[2]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@P@@@@M@@O??4@C??=@@@@M@@@@A@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@a') ; colorMapFromArray:#[0 0 0 84 84 84 170 170 170 255 255 255]; mask:((Depth1Image new) width: 16; height: 16; photometric:(#blackIs0); bitsPerSample:(#[1]); samplesPerPixel:(1); bits:(ByteArray fromPackedString:'@@@@ @C@@N@@<G?8_?1??''?>_?1?>@C0@N@@0@B@@@@b') ; yourself); yourself]
! !

!AssistantApplication class methodsFor:'interface specs'!

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:AssistantApplication andSelector:#windowSpec
     AssistantApplication new openInterface:#windowSpec
     AssistantApplication open
    "

    <resource: #canvas>

    ^ 
     #(FullSpec
        name: windowSpec
        window: 
       (WindowSpec
          label: 'NewApplication'
          name: 'NewApplication'
          bounds: (Rectangle 0 0 534 440)
          menu: mainMenu
        )
        component: 
       (SpecCollection
          collection: (
           (MenuPanelSpec
              name: 'ToolBar1'
              layout: (LayoutFrame 0 0 0 0 0 1 40 0)
              menu: toolbarMenu
              textDefault: true
            )
           (ViewSpec
              name: 'Box1'
              layout: (LayoutFrame 0 0 40 0 0 1 -25 1)
              component: 
             (SpecCollection
                collection: (
                 (VariableHorizontalPanelSpec
                    name: 'VariableHorizontalPanel1'
                    layout: (LayoutFrame 0 0 0 0 0 1 -40 1)
                    component: 
                   (SpecCollection
                      collection: (
                       (SequenceViewSpec
                          name: 'List1'
                          level: 0
                          enableChannel: false
                          hasHorizontalScrollBar: true
                          hasVerticalScrollBar: true
                          useIndex: true
                          sequenceList: pageLabelsInList
                        )
                       (SubCanvasSpec
                          name: 'SubCanvas1'
                          hasHorizontalScrollBar: false
                          hasVerticalScrollBar: false
                          miniScrollerHorizontal: false
                          miniScrollerVertical: false
                          specHolder: currentPageSpecHolder
                          createNewBuilder: false
                        )
                       )
                     
                    )
                    handles: (Any 0.5 1.0)
                  )
                 (HorizontalPanelViewSpec
                    name: 'ButtonPanel1'
                    layout: (LayoutFrame 0 0 -40 1 0 0.5 0 1)
                    horizontalLayout: centerMax
                    verticalLayout: center
                    horizontalSpace: 3
                    verticalSpace: 3
                    component: 
                   (SpecCollection
                      collection: (
                       (ActionButtonSpec
                          label: 'Back'
                          name: 'Button1'
                          activeHelpKey: backButton
                          visibilityChannel: backButtonVisible
                          translateLabel: true
                          labelChannel: backButtonLabelHolder
                          model: goBackward
                          enableChannel: backButtonEnabled
                          extent: (Point 132 22)
                        )
                       (ActionButtonSpec
                          label: 'Next'
                          name: 'Button2'
                          activeHelpKey: nextButton
                          visibilityChannel: forwardButtonVisible
                          translateLabel: true
                          labelChannel: nextButtonLabelHolder
                          model: goForward
                          enableChannel: forwardButtonEnabled
                          extent: (Point 132 22)
                        )
                       )
                     
                    )
                  )
                 (HorizontalPanelViewSpec
                    name: 'ButtonPanel2'
                    layout: (LayoutFrame 0 0.5 -40 1 0 1 0 1)
                    horizontalLayout: centerMax
                    verticalLayout: center
                    horizontalSpace: 3
                    verticalSpace: 3
                    ignoreInvisibleComponents: false
                    component: 
                   (SpecCollection
                      collection: (
                       (ActionButtonSpec
                          label: 'Finish'
                          name: 'Button7'
                          visibilityChannel: finishButtonVisible
                          translateLabel: true
                          resizeForLabel: true
                          model: doFinish
                          enableChannel: finishButtonEnabled
                          extent: (Point 65 22)
                        )
                       (ActionButtonSpec
                          label: 'Cancel'
                          name: 'Button8'
                          translateLabel: true
                          resizeForLabel: true
                          model: doCancel
                          extent: (Point 65 22)
                        )
                       )
                     
                    )
                  )
                 )
               
              )
            )
           (LabelSpec
              label: 'Label'
              name: 'Label1'
              layout: (LayoutFrame 1 0 -25 1 -1 1 -1 1)
              level: -1
              translateLabel: true
              labelChannel: infoHolder
              adjust: left
            )
           )
         
        )
      )
! !

!AssistantApplication class methodsFor:'menu specs'!

mainMenu
    "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:AssistantApplication andSelector:#mainMenu
     (Menu new fromLiteralArrayEncoding:(AssistantApplication mainMenu)) startUp
    "

    <resource: #menu>

    ^ 
     #(Menu
        (
         (MenuItem
            label: 'File'
            translateLabel: true
            submenu: 
           (Menu
              (
               (MenuItem
                  label: 'Exit'
                  itemValue: closeRequest
                  translateLabel: true
                )
               )
              nil
              nil
            )
          )
         (MenuItem
            label: 'Help'
            translateLabel: true
            startGroup: right
            submenu: 
           (Menu
              (
               (MenuItem
                  label: 'Documentation'
                  itemValue: openDocumentation
                  translateLabel: true
                )
               (MenuItem
                  label: '-'
                )
               (MenuItem
                  label: 'About this Application...'
                  itemValue: openAboutThisApplication
                  translateLabel: true
                )
               )
              nil
              nil
            )
          )
         )
        nil
        nil
      )
! !

!AssistantApplication class methodsFor:'required protocol'!

assistantSpec
    "must return a sequenceable collection of assistantPageSpecs - one for each page"

    self subclassResponsibility
! !

!AssistantApplication methodsFor:'actions'!

canGoBackward
    |currentPageSpec previousPageSpec|

    "/ see if we are allowed to leave...
    currentPageSpec := self currentPageSpec.
    currentPageSpec canLeaveQuerySelector notNil ifTrue:[
        (self perform:currentPageSpec canLeaveQuerySelector) ifFalse:[^ false].
    ].

    "/ see if we are allowed to enter the next...
    previousPageSpec := self previousEnabledPageSpec.
    previousPageSpec isNil ifTrue:[^ false].

    previousPageSpec canEnterQuerySelector notNil ifTrue:[
        (self perform:previousPageSpec canEnterQuerySelector) ifFalse:[^ false].
    ].
    ^ true
!

canGoForward
    |currentPageSpec nextPageSpec|

    self currentPageIndex == self numberOfPages ifTrue:[^ false].

    "/ see if we are allowed to leave...
    currentPageSpec := self currentPageSpec.
    currentPageSpec canLeaveQuerySelector notNil ifTrue:[
        (self perform:currentPageSpec canLeaveQuerySelector) ifFalse:[^ false].
    ].

    "/ see if we are allowed to enter the next...
    nextPageSpec := self nextEnabledPageSpec.
    nextPageSpec isNil ifTrue:[^ false].

    nextPageSpec canEnterQuerySelector notNil ifTrue:[
        (self perform:nextPageSpec canEnterQuerySelector) ifFalse:[^ false].
    ].
    ^ true
!

doCancel
    self closeRequest
!

doFinish
    self closeRequest
!

goBackward
    |currentPageSpec|

    (self canGoBackward) ifFalse:[^ self].

    currentPageSpec := self currentPageSpec.
    currentPageSpec leaveCallbackSelector notNil ifTrue:[
        self perform:currentPageSpec leaveCallbackSelector
    ].

    self currentPageIndexHolder value:((self indexOfPreviousEnabledPageSpec) max:1).

    currentPageSpec := self currentPageSpec.
    currentPageSpec enterCallbackSelector notNil ifTrue:[
        self perform:currentPageSpec enterCallbackSelector
    ].

    self updateButtonEnableState.
!

goForward
    |currentPageSpec|

    (self canGoForward) ifFalse:[^ self].

    currentPageSpec := self currentPageSpec.
    currentPageSpec leaveCallbackSelector notNil ifTrue:[
        self perform:currentPageSpec leaveCallbackSelector
    ].

    self currentPageIndexHolder value:(self indexOfNextEnabledPageSpec).

    currentPageSpec := self currentPageSpec.
    currentPageSpec enterCallbackSelector notNil ifTrue:[
        self perform:currentPageSpec enterCallbackSelector
    ].

    self updateButtonEnableState.
! !

!AssistantApplication methodsFor:'aspects'!

assistantSpec
    ^ self class assistantSpec
!

backButtonEnabled
    <resource: #uiAspect>

    backButtonEnabled isNil ifTrue:[
        backButtonEnabled := false asValue.
    ].
    ^ backButtonEnabled.
!

backButtonLabelHolder
    <resource: #uiAspect>

    backButtonLabelHolder isNil ifTrue:[
        backButtonLabelHolder := self backButtonLabel asValue.
    ].
    ^ backButtonLabelHolder.
!

backButtonVisible
    <resource: #uiAspect>

    backButtonVisible isNil ifTrue:[
        backButtonVisible := true asValue.
    ].
    ^ backButtonVisible.
!

currentPageIndexHolder
    <resource: #uiAspect>

    currentPageIndexHolder isNil ifTrue:[
        currentPageIndexHolder := 1 asValue.
        currentPageIndexHolder onChangeSend:#updateCanvas to:self
    ].
    ^ currentPageIndexHolder.
!

currentPageInfoHTMLHolder
    <resource: #uiAspect>

    currentPageInfoHTMLHolder isNil ifTrue:[
        currentPageInfoHTMLHolder := ValueHolder new.
    ].
    ^ currentPageInfoHTMLHolder.
!

currentPageSpecHolder
    <resource: #uiAspect>

    currentPageSpecHolder isNil ifTrue:[
        currentPageSpecHolder := nil asValue.
    ].
    ^ currentPageSpecHolder.
!

finishButtonEnabled
    <resource: #uiAspect>

    finishButtonEnabled isNil ifTrue:[
        finishButtonEnabled := false asValue.
    ].
    ^ finishButtonEnabled.
!

finishButtonVisible
    <resource: #uiAspect>

    finishButtonVisible isNil ifTrue:[
        finishButtonVisible := false asValue.
    ].
    ^ finishButtonVisible.
!

forwardButtonEnabled
    <resource: #uiAspect>

    forwardButtonEnabled isNil ifTrue:[
        forwardButtonEnabled := false asValue.
    ].
    ^ forwardButtonEnabled.
!

forwardButtonVisible
    <resource: #uiAspect>

    forwardButtonVisible isNil ifTrue:[
        forwardButtonVisible := true asValue.
    ].
    ^ forwardButtonVisible.
!

nextButtonLabelHolder
    <resource: #uiAspect>

    nextButtonLabelHolder isNil ifTrue:[
        nextButtonLabelHolder := self nextButtonLabel asValue.
    ].
    ^ nextButtonLabelHolder.
!

pageLabelsInList
    pageLabelsInList isNil ifTrue:[
        pageLabelsInList := List new.
        pageLabelsInList contents:(self pageLabelsInListWithBullet).
    ].
    ^ pageLabelsInList
!

pageLabelsInListWithBullet
    |newList|

    newList := self pageLabels.
    newList := newList with:(1 to:newList size) 
                collect:[:entry :index |
                    index == self currentPageIndex ifTrue:[
                        LabelAndTwoIcons new 
                            string:entry;
                            icon:(self class rightArrowIcon);
                            image2:(self class leftArrowIcon)
                    ] ifFalse:[
                        (self isPageEnabledAtIndex:index) ifFalse:[
                            entry asText colorizeAllWith:(Button defaultDisabledForegroundColor) "Color grey".
                        ] ifTrue:[
                            entry
                        ].
                    ].
                ].
    ^ newList
! !

!AssistantApplication methodsFor:'help'!

flyByHelpTextForKey:aKey
    <resource: #help>

    |text bindings nextSpec prevSpec|

    text := super flyByHelpTextForKey:aKey.

    nextSpec := self nextEnabledPageSpec.
    prevSpec := self previousEnabledPageSpec.

    bindings := Dictionary new.
    bindings 
        at:#'previousPageInfo' 
        put:(prevSpec notNil ifTrue:[ '"',prevSpec pageTitle,'"' ] ifFalse:['']).
    bindings 
        at:#'nextPageInfo'
        put:(nextSpec notNil ifTrue:[ '"',nextSpec pageTitle,'"' ] ifFalse:['']).

    ^ self resources string:text withArgs:bindings.    
! !

!AssistantApplication methodsFor:'initialization & release'!

postBuildWith:aBuilder
    |maxCanvasExtent|

    super postBuildWith:aBuilder.
    self updateCanvas.

    maxCanvasExtent := (self assistantSpec 
                            collect:[:eachPageSpec | 
                                |windowSpecSelector specArray windowSpec|

                                windowSpecSelector := eachPageSpec windowSpecSelector.
                                specArray := self perform:windowSpecSelector ifNotUnderstood:[self class perform:windowSpecSelector].
                                windowSpec := specArray decodeAsLiteralArray.
                                windowSpec window bounds extent.
                            ]
                       ) max.

    self window extent:(maxCanvasExtent + (200 @ 40)).
! !

!AssistantApplication methodsFor:'look'!

backButtonLabel
    ^ LabelAndIcon 
        label:(resources string:'Back')
        icon:(self backButtonEnabled value
                ifTrue:[ToolbarIconLibrary leftArrow24x24Icon]
                ifFalse:[ToolbarIconLibrary leftArrow24x24disabledIcon])
!

nextButtonLabel
    ^ LabelAndTwoIcons new 
        string:(resources string:'Next');
        image2:(self forwardButtonEnabled value
                ifTrue:[ToolbarIconLibrary rightArrow24x24Icon]
                ifFalse:[ToolbarIconLibrary rightArrow24x24disabledIcon])
!

pageLabels
    ^ self assistantSpec collect:[:specEntry | specEntry pageTitle].
! !

!AssistantApplication methodsFor:'private'!

canFinish
    |pageIndex pageSpec|

    pageIndex := self currentPageIndexHolder value.
    pageSpec := self assistantSpec at:pageIndex.
    ^ pageSpec canLeaveQuerySelector isNil
    or:[ self perform:pageSpec canLeaveQuerySelector ]
!

currentPageIndex
    ^ self currentPageIndexHolder value
!

currentPageSpec
    ^ self assistantSpec at:(self currentPageIndex).
!

indexOfNextEnabledPageSpec
    |delta currentPageIndex|

    currentPageIndex := self currentPageIndex.
    delta := 1.
    [ 
        (currentPageIndex + delta) <= (self numberOfPages)
        and:[ (self isPageEnabledAtIndex:(currentPageIndex + delta)) not ]
    ] whileTrue:[
        delta := delta + 1.
    ].
    (currentPageIndex + delta) <= (self numberOfPages) ifTrue:[
        ^ (currentPageIndex + delta).
    ].
    ^ nil
!

indexOfPreviousEnabledPageSpec
    |delta currentPageIndex|

    currentPageIndex := self currentPageIndex.
    delta := 1.
    [ 
        (currentPageIndex - delta) > 0
        and:[ (self isPageEnabledAtIndex:(currentPageIndex - delta)) not ]
    ] whileTrue:[
        delta := delta + 1.
    ].
    (currentPageIndex - delta) > 0 ifTrue:[
        ^ (currentPageIndex - delta).
    ].
    ^ nil
!

isPageEnabledAtIndex:index
    |isEnabledSelector|

    isEnabledSelector := (self assistantSpec at:index) isEnabledQuerySelector.
    ^ isEnabledSelector isNil or:[ (self perform:isEnabledSelector) ]
!

nextEnabledPageSpec
    |index|

    index := self indexOfNextEnabledPageSpec.
    index isNil ifTrue:[^ nil].
    ^ self assistantSpec at:index.
!

nextPageSpec
    ^ self assistantSpec at:(self currentPageIndex + 1).
!

numberOfPages
    ^ self assistantSpec size
!

previousEnabledPageSpec
    |index|

    index := self indexOfPreviousEnabledPageSpec.
    index isNil ifTrue:[^ nil].
    ^ self assistantSpec at:index.
!

previousPageSpec
    ^ self assistantSpec at:(self currentPageIndex - 1).
!

updateButtonEnableState
    self backButtonEnabled value:(self canGoBackward).
    self forwardButtonEnabled value:(self canGoForward).
    self backButtonLabelHolder value:(self backButtonLabel).
    self nextButtonLabelHolder value:(self nextButtonLabel).
!

updateCanvas
    |pageIndex pageSpec sel uiSpec|

    pageIndex := self currentPageIndex.
    pageSpec := self currentPageSpec.
    sel := pageSpec windowSpecSelector.
    uiSpec := self perform:sel ifNotUnderstood:[ self class perform:sel ].

    self pageLabelsInList contents:(self pageLabelsInListWithBullet).
    self currentPageSpecHolder value:uiSpec.
    self window label:(pageSpec pageTitle).

    self finishButtonVisible value:(pageIndex == self numberOfPages).
    self backButtonEnabled value:(pageIndex > 1).
    self forwardButtonEnabled value:(pageIndex < self numberOfPages).
    self finishButtonEnabled value:(pageIndex == self numberOfPages and:[ self canFinish ]).

    self backButtonLabelHolder value:(self backButtonLabel).
    self nextButtonLabelHolder value:(self nextButtonLabel).
! !

!AssistantApplication class methodsFor:'documentation'!

version_CVS
    ^ '$Header: /cvs/stx/stx/libwidg2/AssistantApplication.st,v 1.7 2009-10-21 13:32:50 cg Exp $'
! !