FlyByHelp.st
author Claus Gittinger <cg@exept.de>
Sun, 25 Oct 2009 02:21:08 +0200
changeset 2777 4cdef981fdb3
parent 2774 7894ba397774
child 2778 8b91cf06a145
permissions -rw-r--r--
changed: #initiateHelpFor:at:now:

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

ActiveHelp subclass:#FlyByHelp
	instanceVariableNames:'currentFrame currentView currentHelpView showProcess closeProcess'
	classVariableNames:''
	poolDictionaries:''
	category:'Interface-Help'
!

!FlyByHelp class methodsFor:'documentation'!

copyright
"
 COPYRIGHT (c) 2001 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.
"
!

examples
"
     FlyByHelp isActive

     FlyByHelp stop
     FlyByHelp start
"
! !

!FlyByHelp class methodsFor:'initialization'!

initialize
    "set default delay & help-display times"

    ShowTime := 10.
    DelayTime := 0.4.

    "
     FlyByHelp initialize
    "

    "Modified: 27.4.1996 / 15:07:27 / cg"
! !

!FlyByHelp class methodsFor:'accessing'!

currentlyShownView
    ^ TheOneAndOnlyHelpListener currentlyShownView
! !

!FlyByHelp methodsFor:'defaults'!

flyByHelpTimeoutMillis
    "abort flyby help text generation, if no text can be generated within that
     time delta. This is used to abort long-lasting parsing/scanning/analysis in
     large methods when the mouse is moved over some syntactic constructs, and
     it takes too long to parse...
     The returned time is given in ms"

    ^ 300
! !

!FlyByHelp methodsFor:'event handling'!

buttonMotion:buttonAndModifierState x:x y:y view:aView
    aView == currentHelpView ifTrue:[
        "/ the help-bubble itself
        ^ false
    ].

    "/ don't start tooltip, if this view is not active
    "/ aView topView isActive ifFalse:[^ false].

    ^ super buttonMotion:buttonAndModifierState x:x y:y view:aView
!

buttonPress:button x:x y:y view:aView
    aView == currentHelpView ifTrue:[
        self hideHelp.
        ^ true
    ].

    ^ super buttonPress:button x:x y:y view:aView
!

keyPress:key x:x y:y view:aView
    aView == currentHelpView ifTrue:[
        key == #Escape ifTrue:[
            self hideHelp.
            ^ true
        ].
    ].
    ^ false
!

mouseWheelMotion:state x:x y:y amount:amount deltaTime:dTime view:aView
    currentHelpView notNil ifTrue:[
        self handleMouseIn:aView x:x y:y.
    ].
    ^ false
!

pointerLeave:state view:aView
    aView == currentHelpView ifTrue:[^ true].
    ^ super pointerLeave:state view:aView
! !

!FlyByHelp methodsFor:'help texts'!

helpTextFromModel:aModel view:aView at:aPointOrNil 
    "helper: ask aModel for its helpText."

    |text|

    aPointOrNil notNil ifTrue:[
        (aModel respondsTo:#flyByHelpTextFor:at:) ifTrue:[
            text := aModel flyByHelpTextFor:aView at:aPointOrNil.
            text notNil ifTrue:[^ text].
        ].
    ].
    (aModel respondsTo:#flyByHelpTextFor:) ifTrue:[
        text := aModel flyByHelpTextFor:aView.
        text notNil ifTrue:[^ text].
    ].
    ^ nil
!

helpTextFromView:aView at:aPointOrNil 
    "helper: ask aView for its helpText."

    |text key app|

    aPointOrNil notNil ifTrue:[
        (aView respondsTo:#flyByHelpTextAt:) ifTrue:[
            text := aView flyByHelpTextAt:aPointOrNil.
            text notNil ifTrue:[^ text].
        ].
    ].
    (aView respondsTo:#flyByHelpText) ifTrue:[
        text := aView flyByHelpText.
        text notNil ifTrue:[^ text].
    ].
    (aView respondsTo:#helpKey) ifTrue:[
        key := aView helpKey.
        key notNil ifTrue:[
            app := aView application.
            app isNil ifTrue:[
                app := Error handle:[:ex | nil] do:[ aView windowGroup mainGroup application ].
            ].
            app notNil ifTrue:[
                ^ app flyByHelpTextForKey:key
            ].
            ^ aView resources string:key
        ].
    ].
    ^ nil.
! !

!FlyByHelp methodsFor:'private'!

hideIfPointerLeft:aView
    "hide help, if the pointer is not in aView"

    |whereOnScreen|

    currentFrame notNil ifTrue:[
        whereOnScreen := aView graphicsDevice pointerPosition.

        (currentFrame notNil
        and:[(currentFrame insetBy:1@1) containsPoint:whereOnScreen]) ifFalse:[
            self hideHelp.
        ].
    ].

    "Modified: 28.5.1996 / 20:18:28 / cg"
!

initiateHelpFor:aView at:aPointOrNil now:showItNow
    "ask aView for helpText, passing x/y coordinates;
     start a timeout process to display this helpText after some delay;
     Normally used internally, but can also be used by widgets to force 
     re-negotiation of the displayed helpText 
     (for example in a menu, when the selection changes)"

    |text delayTime now|

    (self interestedIn:aView) ifFalse:[
        ^ self
    ].

    now := Timestamp now.

    "/ do not allow for more than 200 ms to be spent in the
    "/ helpText gatherer (the codeView parses the code for the variable under the cursor)
    [
        text := self helpTextFor:aView at:aPointOrNil.
    ] valueWithWatchDog:[ ^ self ] afterMilliseconds:(self flyByHelpTimeoutMillis).

    lastHelpText = text ifTrue:[
        self toolTipFollowsMouse ifFalse:[
            ^ self
        ]
    ].

    text size > 0 ifTrue:[
        delayTime := self delayTime.
        (showItNow not and:[delayTime > 0]) ifTrue:[
            self stopHelpDisplayProcess.
            showProcess := 
                [
                    Delay waitForSeconds:delayTime.
                    [
                        aView device anyButtonPressed ifFalse:[
                            showProcess := nil.
                            self showHelp:text for:aView
                        ]
                    ] ensure:[
                        showProcess := nil.
                    ]
                ] forkAt:(Processor userSchedulingPriority + 1).
        ] ifFalse:[
            self showHelp:text for:aView
        ]
    ] ifFalse:[
        self hideHelp
    ].
!

interestedIn:aView
    "return true, if I am interested in aView (either listeningForAll,
     or in my list of apps)"

    aView isNil ifTrue:[^ false].
    "/ aView topView == currentHelpView ifTrue:[^ false].

    ^ super interestedIn:aView
! !

!FlyByHelp methodsFor:'queries'!

currentlyShownView
    ^ currentHelpView
!

toolTipFollowsMouse
    ^ false
! !

!FlyByHelp methodsFor:'show & hide help'!

hideHelp
    "hide the help text"

    |p|

    lastHelpText := nil.
    self stopHelpDisplayProcess.

    currentHelpView notNil ifTrue:[
        [
            currentHelpView destroy.
            currentHelpView := nil.
            currentView := nil.
        ] valueUninterruptably
    ].
    currentFrame := nil.
    closeProcess notNil ifTrue:[
        p := closeProcess. closeProcess := nil.
        p terminate.
    ]

    "Modified: 28.6.1997 / 14:03:50 / cg"
!

showHelp:aHelpText for:view
    "show the help text for aView"

    |wg org p v dev|

    (wg := view windowGroup) notNil ifTrue:[
        wg isInModalLoop ifTrue:[
"/ Transcript showCR:'1'.
            wg isModal ifFalse:[
"/ Transcript showCR:'2'.
                ^ self
            ].
        ].
    ].

    view == currentView ifTrue:[
        lastHelpText = aHelpText ifTrue:[
            ^ self
        ]
    ].

    lastHelpText := aHelpText.

    closeProcess notNil ifTrue:[
        p := closeProcess. closeProcess := nil.
        p terminate.
    ].
    currentHelpView notNil ifTrue:[
        self hideHelp
    ].

    org := view originRelativeTo:nil.
    currentFrame := org extent:view extent.
    org :=org + (view extent // 2).

    dev := view graphicsDevice.

    v := ActiveHelpView for:aHelpText onDevice:dev.

    org := dev pointerPosition.
    org := org + (0@18"24").
    (org x + v width) > dev width ifTrue:[
        org := (org x - v width) @ org y
    ].
    (org y + v height) > dev height ifTrue:[
        org := org x @ (org y - v height).
    ].

    v origin:org.
    v realize.
    v enableButtonMotionEvents.
    v enableMotionEvents.
    currentHelpView := v.
    currentView := view.

    self showTime notNil ifTrue:[
        closeProcess := [
                [
                    (Delay forSeconds:self showTime) wait.
                    [
                        currentHelpView notNil ifTrue:[
                            currentHelpView destroy.
                            currentHelpView := nil.
                        ]
                    ] valueUninterruptably
                ] ifCurtailed:[
                    closeProcess := nil.
                ].
            ] newProcess.
        closeProcess priority:(Processor userSchedulingPriority + 1).
        closeProcess resume.
    ].

    "Modified: / 31-08-1995 / 19:20:45 / claus"
    "Modified: / 16-03-2004 / 15:27:50 / cg"
!

stopHelpDisplayProcess
    |p|

    (p := showProcess) notNil ifTrue:[
        showProcess := nil.
        p terminate.
    ].

    "Created: 28.6.1997 / 14:03:17 / cg"
! !

!FlyByHelp class methodsFor:'documentation'!

version
    ^ '$Header: /cvs/stx/stx/libview2/FlyByHelp.st,v 1.34 2009-10-25 00:21:08 cg Exp $'
!

version_CVS
    ^ '$Header: /cvs/stx/stx/libview2/FlyByHelp.st,v 1.34 2009-10-25 00:21:08 cg Exp $'
! !

FlyByHelp initialize!