#REFACTORING by cg
class: SimpleView
changed:
#relativeCorner:
#relativeOrigin:
"{ Encoding: utf8 }"
"
COPYRIGHT (c) 1992 by Claus Gittinger
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:libview' }"
"{ NameSpace: Smalltalk }"
GraphicsMedium subclass:#DisplaySurface
instanceVariableNames:'viewBackground cursor eventMask moreAttributes renderer uuid
backed flags delegate updateRegion'
classVariableNames:'GotExposeFlagMask SaveUnderFlagMask'
poolDictionaries:''
category:'Graphics-Support'
!
!DisplaySurface class methodsFor:'documentation'!
copyright
"
COPYRIGHT (c) 1992 by Claus Gittinger
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 abstract class describes stuff common to any Window on a display
device. i.e. RootWindow, TopWindows, PopUps and Subwindows.
That is, they have a viewBackground, cursor etc. and especially events.
A special feature is the delegate field, which allows key- and button
events to be stolen from a view.
If the delegate is non-nil, these events will be sent to it instead.
So you can change a view's behavior even if it was not initially designed
for it. Also, controller functionality could be simulated using delegates.
[instance variables:]
viewBackground <Color|Form|Image> the view's background
cursor <Cursor> the cursor
eventMask mask specifying the enabled
events.
moreAttributes optional dictionary, allowing for more attributes
to be store there
fields:
middleButtonMenu a fix popup menu for the middle button.
keyCommands not yet supported
backed one of #always, #whenMapped or #never
corresponds to X's backing store mechanism.
May be ignored by other devices.
flags flag bits
saveUnder bit 1; 1 if saveunder store for
that view is enabled
gotExpose bit 2; 1 if an expose/noExpose event arrived
(for exposure handling after a scroll)
delegate for event delegation
[see also:]
DeviceWorkstation
WindowGroup
StandardSYstemView SimpleView View
[author:]
Claus Gittinger
"
! !
!DisplaySurface class methodsFor:'initialization'!
initialize
SaveUnderFlagMask := 1.
GotExposeFlagMask := 2.
! !
!DisplaySurface methodsFor:'accessing'!
addUpdateRectangle:newRectangle
"return true, if the newRectangle is not already contained in the updateRegion
(i.e. if it added any pixels to the region"
|closure lastRect numRect lastTop lastBottom lastLeft lastRight
newTop newBottom newLeft newRight|
updateRegion isNil ifTrue:[
updateRegion := OrderedCollection with:newRectangle.
^ true
].
(updateRegion contains:[:oldRectangle | (newRectangle isContainedIn:oldRectangle)]) ifTrue:[
^ false.
].
numRect := updateRegion size.
numRect > 20 ifTrue:[
closure := updateRegion
inject:newRectangle
into:[:boundsSoFar :thisRectangle |
boundsSoFar quickMerge:thisRectangle
].
updateRegion := OrderedCollection with:closure.
^ true
].
lastRect := updateRegion at:numRect.
lastTop := lastRect top.
lastBottom := lastRect bottom.
lastLeft := lastRect left.
lastRight := lastRect right.
newTop := newRectangle top.
newBottom := newRectangle bottom.
newLeft := newRectangle left.
newRight := newRectangle right.
lastTop = newTop ifTrue:[
lastBottom = newBottom ifTrue:[
lastLeft <= newLeft ifTrue:[
lastRight >= newLeft ifTrue:[
updateRegion at:numRect put:(lastRect copy right:newRight).
^ false "/ true
]
]
].
].
lastLeft = newLeft ifTrue:[
lastRight = newRight ifTrue:[
lastTop <= newTop ifTrue:[
lastBottom >= newTop ifTrue:[
updateRegion at:numRect put:(lastRect copy bottom:newBottom).
^ false "/ true
]
]
].
].
updateRegion add:newRectangle.
^ true
!
application
"to be redefined in real widgets.
Here nil is returned as fallback"
^ nil
"Created: / 15-03-2017 / 10:42:10 / cg"
!
basicViewBackground:aColorOrFormOrViewBackground
"set the viewBackground to something, a color, image or form.
The viewBackground is the color or pattern with which exposed
regions are filled - do not confuse this with the drawing background
color, which is used with opaque drawing."
viewBackground ~~ aColorOrFormOrViewBackground ifTrue:[
viewBackground := aColorOrFormOrViewBackground.
self drawableId notNil ifTrue:[
self setViewBackground
]
]
"Modified (format): / 12-02-2017 / 12:57:07 / cg"
!
controller
"return nil - I have no controller"
^ nil
!
depth
"return the depth in pixels of the view.
Notice, that this is currently the devices depth,
but support for mixed depth views is being prepared.
(especially useful on SGI, with 24bit view)"
^ device depth
!
insideColor:aColor
"set the view's background color - ST-80 compatibility"
self viewBackground:aColor.
self background:aColor
!
keyboardMap
"by default, use my devices standard mapping.
However, subclasses may redefine this, to return their own
keyboard map (for example a terminalView may want treat CTRL-C as regular key)"
^ device keyboardMap
!
renderer
"warning: this is experimental and not yet implemented"
^ renderer
!
renderer:something
"warning: this is experimental and not yet implemented"
renderer := something.
!
sensor
"I can only return a SynchronousWindowSensor,
since I do not have a window group"
^ SynchronousWindowSensor new
!
setViewBackground
"install the viewBackground for the receiver on the device"
|id devBgPixmap bgPixmap w h colorMap
pixmapDepth deviceDepth defBG|
self drawableId notNil ifTrue:[
viewBackground isColor ifTrue:[
viewBackground := viewBackground onDevice:self graphicsDevice.
id := viewBackground colorId.
"
a real color (i.e. one supported by the device) ?
"
id notNil ifTrue:[
self graphicsDevice setWindowBackground:id in:self drawableId.
^ self
].
"
no, a dithered one - must have a dither-pattern
(which is ready for the device, since viewBackground
is already assigned to the device)
"
bgPixmap := viewBackground ditherForm.
] ifFalse:[
viewBackground notNil ifTrue:[
viewBackground isViewBackground ifTrue:[
^ self.
].
"
assume, it can convert itself to a form
"
bgPixmap := viewBackground asFormOn:self graphicsDevice.
bgPixmap isNil ifTrue:[
"/ assume it knows how to draw itself
^ self
].
].
].
"
must now have:
a dithered color or bitmap or pixmap
"
bgPixmap isNil ifTrue:[
'DisplaySurface [warning]: background not convertable - ignored' errorPrintCR.
^ self
].
"/ if the device does not support background pixmaps,
"/ set the backgroundColor to the default background.
"/ this will avoid flicker in win32 systems,
"/ since that background is drawn directly in the
"/ devices expose event handling.
"/ (in contrast, the pixmap filling is done by the
"/ window itself in its expose event handler)
(self graphicsDevice supportsViewBackgroundPixmap:bgPixmap) ifFalse:[
defBG := View defaultViewBackgroundColor.
defBG isColor ifTrue:[
defBG := defBG onDevice:self graphicsDevice.
id := defBG colorId.
id notNil ifTrue:[
self graphicsDevice setWindowBackground:id in:self drawableId.
].
].
^ self
].
w := bgPixmap width.
h := bgPixmap height.
deviceDepth := self depth.
pixmapDepth := bgPixmap depth.
(pixmapDepth ~~ deviceDepth) ifTrue:[
(pixmapDepth ~~ 1) ifTrue:[
'DisplaySurface [warning]: Bad dither depth (must be one or devices depth)' errorPrintCR.
^ self
].
"
convert it into a deep form
"
colorMap := bgPixmap colorMap.
devBgPixmap := Form width:w height:h depth:deviceDepth onDevice:self graphicsDevice.
devBgPixmap isNil ifTrue:[
'DisplaySurface [warning]: could not create a device form for viewBackground' infoPrintCR.
^ self
].
devBgPixmap paint:(colorMap at:1).
devBgPixmap fillRectangleX:0 y:0 width:w height:h.
devBgPixmap foreground:(colorMap at:2) background:(colorMap at:1).
devBgPixmap copyPlaneFrom:bgPixmap x:0 y:0 toX:0 y:0 width:w height:h.
bgPixmap := devBgPixmap.
] ifFalse:[
(pixmapDepth == 1) ifTrue:[
"
although depth matches,
values in the dither are to be interpreted via the ditherForms
colormap, which is not always the same as blackpixel/whitepixel ...
"
colorMap := bgPixmap colorMap.
(colorMap at:1) colorId == self graphicsDevice whitepixel ifTrue:[
(colorMap at:2) colorId == self graphicsDevice blackpixel ifTrue:[
"
ok, can use it
"
self graphicsDevice setWindowBackgroundPixmap:(bgPixmap drawableId) in:self drawableId.
^ self
]
].
"
no, must invert it
"
devBgPixmap := Form width:w height:h depth:deviceDepth onDevice:self graphicsDevice.
devBgPixmap paint:(colorMap at:2) on:(colorMap at:1).
devBgPixmap copyPlaneFrom:bgPixmap x:0 y:0 toX:0 y:0 width:w height:h.
bgPixmap := devBgPixmap.
]
].
self graphicsDevice setWindowBackgroundPixmap:(bgPixmap drawableId) in:self drawableId.
]
"Modified: / 23-01-2011 / 01:44:38 / cg"
!
updateRegion
^ updateRegion
!
updateRegion:something
updateRegion := something.
!
viewBackground
"return the viewBackground"
^ viewBackground
!
viewBackground:aColorOrFormOrViewBackground
"set the viewBackground to something, a color, image or form.
The viewBackground is the color or pattern with which exposed
regions are filled - do not confuse this with the drawing background
color, which is used with opaque drawing."
viewBackground ~~ aColorOrFormOrViewBackground ifTrue:[
self basicViewBackground:aColorOrFormOrViewBackground
]
"Modified (format): / 12-02-2017 / 12:57:01 / cg"
!
viewBackgroundAndClear:something
"set the viewBackground to something, a color, image or form.
and clear the View.
The viewBackground is the color or pattern with which exposed
regions are filled - do not confuse this with the drawing background
color, which is used with opaque drawing."
self viewBackground:something.
self clearView.
"Created: 27.4.1996 / 14:09:08 / cg"
!
viewGravity
"return the view's gravity"
^ #NorthWest
!
viewOrigin
"0@0 here, since by default we cannot be scrolled"
^ 0 @ 0
!
widget
"ST-80 compatibility"
^ self
!
windowClass:classString name:nameString
gc windowClass:classString name:nameString.
!
windowGroup
"return nil - I have no windowGroup"
^ nil
!
windowName:aString
gc windowName:aString.
! !
!DisplaySurface methodsFor:'accessing-cursor'!
cursor
"return the view's cursor"
^ cursor
!
cursor:aCursor
"set the view's cursor. This cursor will be automatically displayed whenever
the mouse-pointer enters the receiver.
Cursors are typically set at view creation time and left as installed."
self cursor:aCursor now:true
"
|v|
v := View new.
v cursor:(Cursor wait).
v open.
[v shown] whileFalse:[Processor yield].
[v shown] whileTrue:[
(Delay forSeconds:1) wait.
v cursor:(Cursor normal).
(Delay forSeconds:1) wait.
v cursor:(Cursor wait).
]
"
"Modified: 14.12.1995 / 21:28:14 / cg"
!
cursor:aCursor now:showImmediately
"set the view's cursor. This cursor will be automatically displayed whenever
the mouse-pointer enters the receiver.
Cursors are typically set at view creation time and left as installed.
The showImmediately parameter controls if the request is to be buffered,
or enforced immediately. Under XWindows, that makes a performance difference if many
cursors (a whole group) are set, because the setting involves an XServer round trip."
aCursor notNil ifTrue:[
(aCursor ~~ cursor) ifTrue:[
cursor := aCursor.
self drawableId notNil ifTrue:[
self setCursor.
(showImmediately and:[realized]) ifTrue:[
"flush, to make cursor immediately visible"
self flush
]
]
]
]
"
|v|
v := View new.
v cursor:(Cursor wait).
v open.
[v shown] whileFalse:[Processor yield].
[v shown] whileTrue:[
(Delay forSeconds:1) wait.
v cursor:(Cursor normal).
(Delay forSeconds:1) wait.
v cursor:(Cursor wait).
]
"
"Created: 14.12.1995 / 21:28:00 / cg"
"Modified: 28.3.1997 / 13:47:58 / cg"
!
setCursor
|id|
cursor isNil ifTrue:[ ^ self].
cursor := cursor onDevice:device.
cursor isNil ifTrue:[ ^ self].
id := cursor id.
id isNil ifTrue:[
'DisplaySurface [warning]: nil cursorId ignored; shape=' errorPrint.
cursor shape errorPrintCR.
^ self.
].
gc setCursorId:id .
!
withCursor:aCursor do:aBlock
"evaluate aBlock showing aCursor until ready; then restore the old cursor
and return the value as returned by aBlock.
Notice, that this method only changes the cursor for a SINGLE (sub-)view.
Most applications want to have the cursor changed in all of its views.
Use 'aView windowGroup withCursor:do:' or 'aView topView withCursor:do:'
to accomplish this."
|savedCursor|
cursor == aCursor ifTrue:[
^ aBlock value
].
savedCursor := cursor.
^ [
self cursor:aCursor now:true.
aBlock value
] ensure:[
self cursor:savedCursor now:true
]
"Modified: / 27-02-1997 / 17:20:43 / cg"
"Modified (comment): / 17-01-2018 / 15:18:35 / mawalch"
!
withExecuteCursorDo:aBlock
"evaluate aBlock while showing an execute cursor in all my views.
Return the value as returned by aBlock."
^ self withCursor:(Cursor execute) do:aBlock
"Created: 10.1.1996 / 13:53:03 / cg"
!
withReadCursorDo:aBlock
"evaluate aBlock while showing a readCursor in all my views.
Return the value as returned by aBlock."
^ self withCursor:(Cursor read) do:aBlock
"Modified: 14.12.1995 / 20:57:40 / cg"
"Created: 10.1.1996 / 13:52:52 / cg"
!
withVisibleCursor:aCursor do:aBlock
"evaluate aBlock, showing a aCursor.
Return the value of aBlock.
Ensure, that the cursor is visible for the user for a minimal amount of time
(even if the computation would be too quick for a cursor to be visible,
show the execute cursor for some time as a feedback)."
|ret|
cursor == aCursor ifTrue:[
^ aBlock value
].
self
withCursor:aCursor do:[
|timeToExecute remainingShowTime|
timeToExecute := Time millisecondsToRun:[ ret := aBlock value].
remainingShowTime := UserPreferences current waitCursorVisibleTime - timeToExecute.
remainingShowTime > 0 ifTrue:[
Delay waitForMilliseconds:remainingShowTime.
].
].
^ ret.
"Modified (comment): / 12-09-2011 / 12:14:29 / cg"
!
withWaitCursorDo:aBlock
"evaluate aBlock while showing a waitCursor in all my views.
Return the value as returned by aBlock."
^ self withCursor:(Cursor wait) do:aBlock
"Created: 10.1.1996 / 13:51:08 / cg"
!
withWriteCursorDo:aBlock
"evaluate aBlock while showing a writeCursor in all my views.
Return the value as returned by aBlock."
^ self withCursor:(Cursor write) do:aBlock
"Modified: / 14-12-1995 / 20:57:40 / cg"
"Created: / 27-07-2012 / 09:43:08 / cg"
! !
!DisplaySurface methodsFor:'accessing-hierarchy'!
delegate
"return the delegate - that's the one getting keyboard and button events.
See dispatchEvent:... method"
^ delegate
!
delegate:someOne
"set the delegate - keyboard- and button events will be forwarded to
that object if it is interested in them.
See the dispatchEvent... method."
delegate := someOne
!
superView
"return the superView - nil here"
^ nil
!
topComponent
"return the topView - that the one with no superview"
^ self
"Created: 9.5.1996 / 01:39:43 / cg"
!
topView
"return the topView - that's the one with no superview"
^ self
! !
!DisplaySurface methodsFor:'accessing-limits'!
maxExtent
"return the view's maximum extent - this is nil here.
Only standardSystemViews support this."
^ nil
!
maxExtent:extent
"set the view's maximum extent - ignored here.
Only standardSystemViews support this."
^ self
!
minExtent
"return the view's minimum extent - this is nil here.
Only standardSystemViews support this."
^ nil
!
minExtent:extent
"set the view's minimum extent - ignored here.
Only standardSystemViews support this."
^ self
! !
!DisplaySurface methodsFor:'accessing-misc'!
automationUUID
"return my uuid (for automation and the polonium st/x capture/replay tool).
This is a widget id, as specified in the uispec (if it is defined there).
Will be the same for all instances
(i.e. the same in different instances of the application).
Contrast this with the instanceUUID, which is unique for each view.
Notice: old uiSpecs do not provide/define a uuid, and nil is returned;
so the application's specs must be re-edited and saved again."
uuid isNil ifTrue:[^ uuid].
^ uuid asUUID
"
Transcript topView automationUUID
Transcript topView instanceUUID
"
"Created: / 09-11-2017 / 22:33:10 / cg"
"Modified (comment): / 05-12-2018 / 17:50:03 / Stefan Vogel"
!
automationUUID:aUuidOrUuidString
"set my uuid (for automation and the pollonium st/x capture/replay tool).
This is a widget id, as specified in the uispec (if it is definedthere).
Will be the same for all instances
(i.e. the same in different instances of the application).
Contrast this with the instanceUUID, which is unique for each view.
Notice: old uiSpecs do not provide/define a uuid, and nil is returned from uuid;
so the application's specs must be re-edited and saved again."
uuid := aUuidOrUuidString
"Created: / 09-11-2017 / 22:33:14 / cg"
!
backingStore:how
"turn on/off backingStore (saving my pixels)
how may true/false, but also #always, #whenMapped or #never."
how ~~ backed ifTrue:[
backed := how.
super backingStore:how.
]
!
eventMask
"return a (numeric) mask of allowed events -
this is X-specific and will be removed / replaced by symbolic values)"
^ eventMask
!
eventMask:aMask
"set a (numeric) mask of allowed events -
this is X-specific and will be removed / replaced by symbolic values)"
eventMask := aMask
!
getKeyboardFocus
"tell the Display to assign keyboard focus to the receiver"
self shown ifTrue:[
device setInputFocusTo:self drawableId.
].
"Modified: / 15.3.1999 / 08:25:10 / cg"
!
instanceUUID
"return my unique uuid.
This will be assigned once and only once per instance
(i.e. the same type of view within different applications will
have different instanceUUIDs)"
|id|
(id := self getAttribute:#instanceUUID) isNil ifTrue:[
self setAttribute:#instanceUUID to:(id := UUID new).
].
^ id
"
Transcript topView getAttribute:#instanceUuid
Transcript topView instanceUuid
"
"Created: / 09-11-2017 / 22:32:07 / cg"
!
isInputOnly
"return true, if the receiver is an input only view - that is:
the view will realize as a transparent view, into which you cannot
draw, but get events as usual. Thich can be used to catch events away from
others, which where never meant to work in such a setup.
(for example, if you want to manipulate views in some DrawTool-like manner).
This uses a special X feature, which might not be supported in the near future
or on other plattforms."
^ false
"Created: 1.6.1996 / 13:21:51 / cg"
!
preferredDepth
"return a non nil integer, if a specific depth is wanted in this view.
Return nil if we do not care (i.e. the display's default is wanted).
This is experimental and may change/vanish - do not use it."
^ nil
"Modified (comment): / 01-09-2017 / 09:57:44 / cg"
!
preferredVisual
"return a non nil id, if a specific visual is wanted in this view.
Return nil if we do not care (i.e. the display's default is wanted).
This is experimental and may change/vanish - do not use it."
^ nil
"Modified (comment): / 01-09-2017 / 09:57:49 / cg"
!
saveUnder
"return the saveUnder flag"
^ flags bitTest:SaveUnderFlagMask
!
saveUnder:aBoolean
"turn on/off saveUnder (saving pixels under myself)
- used for temporary views (i.e. PopUps and ModalBoxes)"
aBoolean ifTrue:[
flags := flags bitOr:SaveUnderFlagMask.
] ifFalse:[
flags := flags bitClear:SaveUnderFlagMask.
].
gc saveUnder:aBoolean.
!
setPointerPosition:aRelativePoint
"set the pointer to aRelativePoint relative to the view's origin.
Warning: moving the mouse pointer may lead to the mouse falling off the table
after a while. So it should be used very very seldom (if at all)"
device setPointerPosition:aRelativePoint in:self drawableId.
"
Transcript setPointerPosition:Transcript extent // 2.
Screen current rootView setPointerPosition:100@100.
"
"Modified (comment): / 14-09-2018 / 17:59:18 / Claus Gittinger"
!
uuid
<resource: #obsolete>
^ self automationUUID
"
Transcript topView uuid
Transcript topView instanceUuid
"
"Modified: / 09-11-2017 / 22:33:24 / cg"
!
uuid:aUuidOrUuidString
<resource: #obsolete>
^ self automationUUID:aUuidOrUuidString
"Modified: / 09-11-2017 / 22:34:12 / cg"
! !
!DisplaySurface methodsFor:'accessing-names'!
icon
"return the view's icon - this is nil here.
Only standardSystemViews support labels and icons."
^ nil
!
icon:aBitmap
"set the view's icon - ignored here.
Only standardSystemViews support labels and icons."
^ self
!
iconLabel
"return the view's icon label - this is nil here.
Only standardSystemViews support labels and icons."
^ nil
!
iconLabel:aLabel
"set the view's icon label - ignored here.
Only standardSystemViews support labels and icons."
^ self
!
iconMask
"return the view's iconMask - this is nil here.
Only standardSystemViews may support labels and icons."
^ nil
!
iconView
"return the view's iconView - this is nil here.
Only standardSystemViews support labels and icons."
^ nil
!
iconView:aView
"set the view's icon view - ignored here.
Only standardSystemViews support labels and icons."
^ self
!
label
"return the view's label - this is nil here.
Only standardSystemViews support labels and icons."
^ nil
!
label:aLabel
"set the view's label - ignored here.
Only standardSystemViews support labels and icons."
^ self
!
label:aLabel iconLabel:iconLabel
"set both the label and the iconLabel.
Only standardSystemViews support labels and icons."
self label:aLabel.
self iconLabel:iconLabel
! !
!DisplaySurface methodsFor:'accessing-private'!
clearGotExposeFlag
"internal; used to wait for an expose-event after a scroll operation"
flags := flags bitClear:GotExposeFlagMask.
!
getAttribute:key
"a place for additional attributes;
allows for non-instvar slots to be added"
moreAttributes isNil ifTrue:[ ^ nil].
^ moreAttributes at:key ifAbsent:nil.
!
gotExpose
"internal; used to wait for an expose-event after a scroll operation"
^ flags bitTest:GotExposeFlagMask.
!
setAttribute:key to:newValue
"a place for additional attributes;
allows for non-instvar slots to be added"
newValue isNil ifTrue:[
moreAttributes notNil ifTrue:[
moreAttributes removeKey:key ifAbsent:[].
moreAttributes := moreAttributes asNilIfEmpty
]
] ifFalse:[
moreAttributes isNil ifTrue:[
moreAttributes := IdentityDictionary new.
].
moreAttributes at:key put:newValue.
].
!
setGotExposeFlag
"internal; used to wait for an expose-event after a scroll operation"
flags := flags bitOr:GotExposeFlagMask.
! !
!DisplaySurface methodsFor:'button menus'!
getMiddleButtonMenu
"return the menu associated with the middle mouse button.
This is a possibly obsolete hook for views which do not define their own menu,
but are configured from the outside. Nowadays, widgets provide their own menu
or are configured using a menuHolder."
^ self getAttribute:#middleButtonMenu
"Created: / 07-07-2011 / 18:16:21 / cg"
!
middleButtonMenu
"return the menu associated with the middle mouse button.
Here, return a hooked on menu, but usually redefined to provide a widget-specific
menu."
^ self getMiddleButtonMenu
"Modified (comment): / 07-07-2011 / 18:18:28 / cg"
!
middleButtonMenu:aMenu
"associate aMenu with the middle mouse button.
This is a possibly obsolete hook for views which do not define their own menu,
but are configured from the outside. Nowadays, widgets provide their own menu
or are configured using a menuHolder."
|oldMenu|
(oldMenu := self getMiddleButtonMenu) notNil ifTrue:[
oldMenu isArray ifFalse:[
oldMenu destroy
]
].
self setMiddleButtonMenu:aMenu
"Modified: / 07-07-2011 / 18:18:00 / cg"
!
setMiddleButtonMenu:aMenu
"associate aMenu with the middle mouse button.
Do not destroy the old menu if any"
self setAttribute:#middleButtonMenu to:aMenu
! !
!DisplaySurface methodsFor:'clipboard'!
getClipboardObject
"return the object selection
- either the local one, or the display's clipBoard buffer."
^ device getClipboardObjectFor:self drawableId.
"Modified: / 13-02-1997 / 13:18:50 / cg"
"Modified (comment): / 01-09-2017 / 09:57:21 / cg"
!
getClipboardText
"return the copyBuffers contents
- either the local one, or the display's clipBoard buffer."
^ self getClipboardText:#clipboard
"Modified (comment): / 01-09-2017 / 09:57:24 / cg"
!
getClipboardText:selectionBufferSymbol
"return the text selection - either the local one, or one of the display's
clipBoard buffers determined by selectionBufferSymbol, which should be one of:
#clipboard
or:
#selection.
Return aString or nil if there is no selection"
^ device getClipboardText:selectionBufferSymbol for:self drawableId.
"Modified (comment): / 01-09-2017 / 09:57:31 / cg"
!
getSelection
<resource: #obsolete>
self obsoleteMethodWarning:'use #getClipboardObject'.
^ self getClipboardObject
!
getTextSelection
"return the copyBuffers contents
- either the local one, or the display's clipBoard buffer."
<resource: #obsolete>
self obsoleteMethodWarning:'use #getClipboardText'.
^ self getClipboardText
"Modified (comment): / 01-09-2017 / 09:57:35 / cg"
!
getTextSelection:selectionBufferSymbol
"return the text selection - either the local one, or one of the display's
clipBoard buffers determined by selectionBufferSymbol, which should be one of:
#clipboard
or:
#selection.
Return aString or nil if there is no selection"
<resource: #obsolete>
self obsoleteMethodWarning:'#use getClipboardText:'.
^ self getClipboardText:selectionBufferSymbol
"Modified (comment): / 01-09-2017 / 09:57:38 / cg"
!
setClipboardObject:something
"set the object selection - both the local one, and tell the display
that we have changed it (i.e. place it into the clipBoard)."
device setClipboardObject:something ownerView:self.
!
setClipboardText:aString
"set the text selection - both the local one, and tell the display
that we have changed it (i.e. place it into the clipBoard)."
device setClipboardText:aString ownerView:self
"Modified (format): / 21-11-2016 / 23:36:59 / cg"
!
setSelection:something
"set the object selection - both the local one, and tell the display
that we have changed it (i.e. place it into the clipBoard)."
<resource: #obsolete>
self obsoleteMethodWarning:'use setClipboardObject:'.
device setClipboardObject:something ownerView:self.
!
setTextSelection:something
"set the text selection - both the local one, and tell the display
that we have changed it (i.e. place it into the clipBoard)."
<resource: #obsolete>
self obsoleteMethodWarning:'use setClipboardText:'.
device setClipboardText:something ownerView:self
! !
!DisplaySurface methodsFor:'drawing'!
clearDeviceRectangleX:x y:y width:w height:h
"clear a rectangular area to viewBackground -
redefined since GraphicsMedium fills with background
- not viewBackground as we want here."
|oldPaint org patternOffsetX patternOffsetY|
(w <= 0 or:[h <= 0]) ifTrue:[^ self].
gc isNil ifTrue:[
"nothing to clear"
^ self.
].
viewBackground isNil ifTrue:[^ self]. "/ how can this happen?
viewBackground isColor ifFalse:[
viewBackground isViewBackground ifTrue:[
oldPaint := self paint.
self
paint:gc background;
fillDeviceRectangleX:x y:y width:w height:h;
paint:oldPaint.
viewBackground fillRectangleX:x y:y width:w height:h in:self.
^ self.
].
org := self viewOrigin.
(device supportsMaskedDrawingWith:viewBackground) ifFalse:[
"/ hand-fill: the device cannot draw with a bitmap pattern underneath.
self fillDeviceRectangleWithPattern:viewBackground x:x y:y width:w height:h patternOffset:org.
^ self.
].
patternOffsetX := (org x rounded \\ viewBackground width).
patternOffsetY := (org y rounded \\ viewBackground height).
(patternOffsetX ~= 0 or:[patternOffsetY ~= 0]) ifTrue:[
(device supportsMaskedDrawingWithOffset:viewBackground) ifFalse:[
"/ hand-fill: the device cannot draw with a shifted bitmap pattern underneath.
self fillDeviceRectangleWithPattern:viewBackground x:x y:y width:w height:h patternOffset:org.
^ self.
].
].
gc setDeviceMaskOriginX:patternOffsetX negated y:patternOffsetY negated.
].
"
fill in device coordinates - not logical coordinates
"
oldPaint := self paint.
self
paint:viewBackground;
fillDeviceRectangleX:x y:y width:w height:h "with:viewBackground";
paint:oldPaint.
"Modified: / 4.5.1999 / 13:00:34 / cg"
!
clearRectangleX:x y:y width:w height:h
"clear a rectangular area to viewBackground -
redefined since GraphicsMedium fills with background
- not viewBackground as we want here."
|pX pY pW pH currentTransformation|
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
pX := currentTransformation applyToX:x.
pY := currentTransformation applyToY:y.
pW := currentTransformation applyScaleX:w.
pH := currentTransformation applyScaleY:h.
] ifFalse:[
pX := x.
pY := y.
pW := w.
pH := h.
].
^ self clearDeviceRectangleX:pX rounded y:pY rounded width:pW ceiling height:pH ceiling.
"Modified: / 30.10.1998 / 15:00:37 / cg"
!
fillDeviceRectangleWithPattern:aPixmap x:xIn y:yIn width:wIn height:hIn patternOffset:pattOffs
"fill a rectangular area with some pattern.
A helper for devices which do not support pixmap drawing (i.e. win95).
This is never invoked with X11 or Win-NT/XP/Vista systems.
Caller must ensure that aPixmap is really a form.
CG: mhm it seems that XQuartz has a bug and also has problems doing this.
therefore it is actually not obsolete."
|r b
pW "{ Class: SmallInteger }"
pH "{ Class: SmallInteger }"
xR0
xR "{ Class: SmallInteger }"
yR "{ Class: SmallInteger }"
xE "{ Class: SmallInteger }"
yE "{ Class: SmallInteger }"
oldFg oldBg oldClip x y w h fg bg clrMap
offsX "{ Class: SmallInteger }"
offsY "{ Class: SmallInteger }"
oX "{ Class: SmallInteger }"
oY "{ Class: SmallInteger }"|
x := xIn.
y := yIn.
w := wIn.
h := hIn.
x := x max:0.
y := y max:0.
r := (xIn + w) min:width.
b := (yIn + h) min:height.
pW := aPixmap width.
pH := aPixmap height.
oldClip := self deviceClippingBoundsOrNil.
oldClip notNil ifTrue:[
x := x max:oldClip left.
y := y max:oldClip top.
r := r min:oldClip right.
b := b min:oldClip bottom.
].
w := r-x.
h := b-y.
yR := (y // pH) * pH.
yE := y+h.
yR >= yE ifTrue:[^ self].
xR0 := (x // pW) * pW.
xE := x+w.
xR0 >= xE ifTrue:[^ self].
aPixmap depth == 1 ifTrue:[
oldFg := gc foreground.
oldBg := gc background.
(clrMap := aPixmap colorMap) notNil ifTrue:[
bg := clrMap at:1.
fg := clrMap at:2.
] ifFalse:[
bg := self whiteColor.
fg := self blackColor.
].
gc foreground:fg background:bg.
].
self deviceClippingBounds:(Rectangle left:x top:y width:w height:h).
offsX := (xR0 + pattOffs x) \\ pW.
offsY := (yR + pattOffs y) \\ pH.
oY := offsY.
[yR < yE] whileTrue:[
xR := xR0.
oX := offsX.
[xR < xE] whileTrue:[
self
copyFrom:aPixmap
x:oX y:oY
toX:xR y:yR
width:(pW - oX) height:(pH - oY)
async:true.
xR := xR + pW - oX.
oX := 0.
].
yR := yR + pH - oY.
oY := 0.
].
oldFg notNil ifTrue:[
gc foreground:oldFg background:oldBg.
].
self deviceClippingBounds:oldClip.
"Created: / 6.9.1998 / 14:00:50 / cg"
"Modified: / 4.5.1999 / 20:38:07 / ps"
"Modified: / 4.5.1999 / 20:40:12 / cg"
!
fillRectangleWithPattern:aPixmap x:x y:y width:w height:h patternOffset:pattOffs
"fill a rectangular area with aPixmap.
A helper for devices which do not support pixmap filling (i.e. win95 screens).
This is never invoked with X11 or Win-NT/XP/Vista systems.
Caller must ensure that the aPixmap is really a form.
CG: mhm it seems that XQuartz has a bug and also has problems doing this.
therefore it is actually not obsolete."
|pX pY nW nH currentTransformation|
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
pX := currentTransformation applyToX:x.
pY := currentTransformation applyToY:y.
nW := currentTransformation applyScaleX:w.
nH := currentTransformation applyScaleY:h.
nW < 0 ifTrue:[
nW := nW abs.
pX := pX - nW.
].
nH < 0 ifTrue:[
nH := nH abs.
pY := pY - nH.
].
] ifFalse:[
pX := x.
pY := y.
nW := w.
nH := h.
].
pX := pX rounded.
pY := pY rounded.
nW := nW rounded.
nH := nH rounded.
self
fillDeviceRectangleWithPattern:aPixmap
x:pX y:pY width:nW height:nH
patternOffset:pattOffs
"Modified: 4.6.1996 / 17:58:49 / cg"
!
redraw
"nothing done here"
^ self
! !
!DisplaySurface methodsFor:'enable/disable events'!
compressMotionEvents:aBoolean
"enable/disable motion event compression
(i.e. replacing all motion events by the last one).
Compression makes almost always sense, except when
doing things like freehand drawing"
|windowGroup|
windowGroup := self windowGroup.
windowGroup notNil ifTrue:[
windowGroup sensor compressMotionEvents:aBoolean.
].
!
disableButtonEvents
"disable all button events"
self disableEvent:#buttonPress; disableEvent:#buttonRelease
"Modified: 29.4.1996 / 11:09:25 / cg"
!
disableButtonMotionEvents
"disable button motion-while-button-is-pressed events"
self disableEvent:#buttonMotion
!
disableButtonPressEvents
"disable button press events"
self disableEvent:#buttonPress
!
disableButtonReleaseEvents
"disable button release events"
self disableEvent:#buttonRelease
!
disableEnterLeaveEvents
"disable both mouse-pointer enter and leave events"
self disableEvent:#enter; disableEvent:#leave
"Modified: 29.4.1996 / 11:09:37 / cg"
!
disableEvent:anEventSymbol
"disable an event -
this is a private (internal) method not to be used externally.
for a list of allowed event symbols see Workstation class"
eventMask := eventMask bitAnd:(self graphicsDevice eventMaskFor:anEventSymbol) bitInvert.
self drawableId notNil ifTrue:[
self graphicsDevice setEventMask:eventMask in:self drawableId
]
!
disableMotionEvents
"disable mouse-pointer motion events"
self disableEvent:#pointerMotion
!
enableButtonEvents
"enable both mouse button press and release events.
These are enabled by default anyway."
self enableEvent:#buttonPress; enableEvent:#buttonRelease
"Modified: 29.4.1996 / 11:09:46 / cg"
!
enableButtonMotionEvents
"enable mouse-pointer motion-while-button-is-pressed events.
These are enabled by default anyway."
self enableEvent:#buttonMotion
!
enableButtonPressEvents
"enable mouse button press events.
These are enabled by default anyway."
self enableEvent:#buttonPress
!
enableButtonReleaseEvents
"enable mouse button release events.
These are enabled by default anyway."
self enableEvent:#buttonRelease
!
enableEnterEvents
"enable mouse-pointer enter events"
self enableEvent:#enter
!
enableEnterLeaveEvents
"enable both mouse-pointer enter and leave events"
self enableEvent:#enter; enableEvent:#leave
"Modified: 29.4.1996 / 11:09:55 / cg"
!
enableEvent:anEventSymbol
"enable an event -
this is a private (internal) method not to be used externally.
for a list of allowed event symbols see Workstation class"
eventMask := (eventMask ? 0) bitOr:(self graphicsDevice eventMaskFor:anEventSymbol).
self drawableId notNil ifTrue:[
self graphicsDevice setEventMask:eventMask in:self drawableId
]
!
enableFocusEvents
"enable keyboard focus change events"
self enableEvent:#focusChange
!
enableKeyEvents
"this is a compatibility leftover -
starting with 2.10.3, keyPress is always enabled to allow
^C processing."
!
enableKeyPressEvents
"this is a compatibility leftover -
starting with 2.10.3, keyPress is always enabled to allow
^C processing."
!
enableKeyReleaseEvents
"enable key release events"
self enableEvent:#keyRelease
!
enableLeaveEvents
"enable mouse-pointer leave events"
self enableEvent:#leave
!
enableMotionEvents
"enable mouse-pointer motion events (those without a pressed button)"
self enableEvent:#pointerMotion
! !
!DisplaySurface methodsFor:'event dispatching'!
dispatchEvent:event
"dispatch an event"
^ self
dispatchEvent:event
withFocusOn:nil
delegate:true
"Modified: / 20.5.1998 / 23:01:15 / cg"
!
dispatchEvent:type arguments:arguments
<resource: #obsolete>
"dispatch an event.
Obsoleted - now we always pass around event objects."
"/ XXXX: TO BE ELIMINATED
^ self
dispatchEvent:nil
type:type
arguments:arguments
withFocusOn:nil
delegate:true
"Modified: / 20.5.1998 / 22:50:31 / cg"
!
dispatchEvent:ev type:type arguments:argArray withFocusOn:focusView delegate:doDelegate
"dispatch the event represented by type and arguments either to my delegate,
or to my controller (which may be myself, if I implement the controller functionality myself).
If focusView is nonNil, and it is a keyboard event, it is forwarded to this
view (but not if there was a delegate in the first place).
If doDelegate is true, keyboard and button events are forwarded to a
delegate object (if non-nil). DoDelegate may be passed as true, to
handle events which are already delegated.
If there is a delegate, only messages which are understood by it are
forwarded. Also, the delegate is asked if it is willing to handle the event
before.
Delegated messages get the original view as an extra argument.
Delegation has higher priority than both controller or focusView
forwarding."
|delegate selector delegateMessage delegateQuery
eventReceiver controller deviceMessage
isKeyEvent isButtonEvent isMouseWheelEvent isPointerEvent isExposeEvent
rect x y w h delegatedEvent rgn
eventForLastEventQuery|
type == #damage ifTrue:[
"/ OLDdamage scheme - this will vanish
self shown ifTrue:[
rect := argArray.
x := rect left.
y := rect top.
w := rect width.
h := rect height.
gc transformation notNil ifTrue:[
self deviceExposeX:x y:y width:w height:h
] ifFalse:[
self exposeX:x y:y width:w height:h
]
].
^ self
].
type == #newDamage ifTrue:[
"/ New damage scheme
rgn := updateRegion.
updateRegion := nil.
self shown ifTrue:[
rgn notNil ifTrue:[
rgn do:[:rect |
"/ cg: I think the check for being realized may not be enough;
"/ there is a race here, if the view gets closed, while in the loop...
realized ifTrue:[
x := rect left.
y := rect top.
w := rect width.
h := rect height.
gc transformation notNil ifTrue:[
self deviceExposeX:x y:y width:w height:h
] ifFalse:[
self exposeX:x y:y width:w height:h
]
]
]
]
].
^ self
].
isKeyEvent := isButtonEvent := isPointerEvent := isMouseWheelEvent := isExposeEvent := false.
(type == #'keyPress:x:y:') ifTrue:[
isKeyEvent := true.
deviceMessage := #'deviceKeyPress:x:y:'.
delegateMessage := #'keyPress:x:y:view:'.
delegateQuery := #'handlesKeyPress:inView:'.
] ifFalse:[ (type == #'keyRelease:x:y:') ifTrue:[
isKeyEvent := true.
deviceMessage := #'deviceKeyRelease:x:y:'.
delegateMessage := #'keyRelease:x:y:view:'.
delegateQuery := #'handlesKeyRelease:inView:'.
] ifFalse:[ (type == #'buttonMotion:x:y:') ifTrue:[
isButtonEvent := true.
deviceMessage := #'deviceButtonMotion:x:y:'.
delegateMessage := #'buttonMotion:x:y:view:'.
delegateQuery := #'handlesButtonMotion:inView:'.
] ifFalse:[ (type == #'buttonPress:x:y:') ifTrue:[
isButtonEvent := true.
deviceMessage := #'deviceButtonPress:x:y:'.
delegateMessage := #'buttonPress:x:y:view:'.
delegateQuery := #'handlesButtonPress:inView:'.
] ifFalse:[ (type == #'buttonRelease:x:y:') ifTrue:[
isButtonEvent := true.
deviceMessage := #'deviceButtonRelease:x:y:'.
delegateMessage := #'buttonRelease:x:y:view:'.
delegateQuery := #'handlesButtonRelease:inView:'.
] ifFalse:[ (type == #'buttonShiftPress:x:y:') ifTrue:[
isButtonEvent := true.
deviceMessage := #'deviceButtonShiftPress:x:y:'.
delegateMessage := #'buttonShiftPress:x:y:view:'.
delegateQuery := #'handlesButtonShiftPress:inView:'.
] ifFalse:[ (type == #'buttonMultiPress:x:y:') ifTrue:[
isButtonEvent := true.
deviceMessage := #'deviceButtonMultiPress:x:y:'.
delegateMessage := #'buttonMultiPress:x:y:view:'.
delegateQuery := #'handlesButtonMultiPress:inView:'.
] ifFalse:[ (type == #mouseWheelMotion:x:y:amount:deltaTime:) ifTrue:[
isMouseWheelEvent := true.
deviceMessage := type.
delegateMessage := #mouseWheelMotion:x:y:amount:deltaTime:view:.
delegateQuery := #handlesMouseWheelMotion:inView:.
] ifFalse:[ (type == #'pointerEnter:x:y:') ifTrue:[
isPointerEvent := true.
deviceMessage := #'devicePointerEnter:x:y:'.
delegateMessage := #'pointerEnter:x:y:view:'.
delegateQuery := #'handlesPointerEnter:inView:'.
] ifFalse:[ (type == #'pointerLeave:') ifTrue:[
isPointerEvent := true.
deviceMessage := type.
delegateMessage := #'pointerLeave:view:'.
delegateQuery := #'handlesPointerLeave:inView:'.
] ifFalse:[ (type == #'exposeX:y:width:height:') ifTrue:[
isExposeEvent := true.
deviceMessage := #'deviceExposeX:y:width:height:'.
] ifFalse:[ (type == #'graphicsExposeX:y:width:height:final:') ifTrue:[
isExposeEvent := true.
deviceMessage := #'deviceGraphicsExposeX:y:width:height:final:'.
]]]]]]]]]]]].
"
if there is a focusView, and its a keyboard event, pass it
to that view (or its controller, or its delegate).
In this case, a coordinate which is outside of
the focusView (0 @ 0) is passed as x/y coordinates.
"
((isKeyEvent "or:[isMouseWheelEvent]")
and:[focusView notNil and:[self ~~ focusView]]) ifTrue:[
delegatedEvent := ev shallowCopy.
delegatedEvent delegatedFrom:ev.
delegatedEvent arguments:(ev arguments shallowCopy).
delegatedEvent x:-1; y:-1.
focusView
dispatchEvent:delegatedEvent
type:delegatedEvent type
arguments:(delegatedEvent arguments)
withFocusOn:nil
delegate:doDelegate.
^ self
].
doDelegate ifTrue:[
"
handle delegated messages
"
(isKeyEvent
or:[isButtonEvent
or:[isMouseWheelEvent
or:[isPointerEvent]]]) ifTrue:[
delegate := self delegate.
"
what a kludge - sending to delegate requires
another selector and an additional argument ...
"
(delegate notNil) ifTrue:[
"
is the delegate interested in that event ?
(if it does not respond to the handlesXXX message, assume: NO)
"
(delegate askFor:delegateQuery with:(argArray at:1) with:self) ifTrue:[
"
mhmh ... have to convert to logical coordinates
"
|currentTransformation|
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
argArray size > 2 ifTrue:[
argArray at:2 put:(currentTransformation applyInverseToX:(argArray at:2)).
argArray at:3 put:(currentTransformation applyInverseToY:(argArray at:3)).
].
].
argArray isNil ifTrue:[
delegate perform:delegateMessage with:self
] ifFalse:[
delegate perform:delegateMessage withArguments:(argArray copyWith:self)
].
^ self
]
].
].
].
"
if I am not shown, ignore input events
"
(isKeyEvent
or:[isButtonEvent
or:[isMouseWheelEvent
or:[isPointerEvent]]]) ifTrue:[
realized ifFalse:[
^ self
]
].
"
if there is a controller, that one gets all user events
"
eventReceiver := self.
((controller := self controller) notNil and:[controller ~~ eventReceiver]) ifTrue:[
(isKeyEvent
or:[isButtonEvent
or:[isMouseWheelEvent
or:[isPointerEvent
or:[(type == #focusIn)
or:[(type == #focusOut)]]]]]) ifTrue:[
eventReceiver := controller.
]
].
"
finally, another one:
if I have a transformation, edit the selector from #foo to #deviceFoo...
This allows for the event to be handled either in device or
logical coordinates. (since the deviceFoo-messages default implementation
in DisplaySurface translates and resends).
Actually, I could always send deviceXXX without speed penalty
(event sending is no high frequency operation), but that just adds
another context to any debuggers walkback, making things less clear.
"
selector := type.
(isKeyEvent
or:[isButtonEvent
or:[isMouseWheelEvent
or:[isPointerEvent
or:[isExposeEvent]]]]) ifTrue:[
gc transformation notNil ifTrue:[
selector := deviceMessage
]
].
"sr: #ev is nil when called via #dispatchEvent:arguments:
I think we should pass the 'triggering' event at least"
eventForLastEventQuery := ev.
eventForLastEventQuery isNil ifTrue:[
eventForLastEventQuery := WindowGroup lastEvent.
].
WindowGroup lastEventQuerySignal
answer:eventForLastEventQuery
do:[
eventReceiver perform:selector withArguments:argArray
].
"Created: / 20-05-1998 / 22:46:25 / cg"
"Modified: / 17-08-2017 / 09:47:01 / cg"
"Modified: / 21-09-2018 / 11:39:47 / Claus Gittinger"
"Modified: / 01-10-2018 / 14:17:18 / sr"
!
dispatchEvent:event withFocusOn:focusViewOrNil
"dispatch the event"
^ self
dispatchEvent:event
withFocusOn:focusViewOrNil
delegate:true
"Modified: / 20.5.1998 / 23:01:15 / cg"
!
dispatchEvent:event withFocusOn:focusViewOrNil delegate:doDelegate
"dispatch the event"
^ self
dispatchEvent:event
type:event type
arguments:(event arguments)
withFocusOn:focusViewOrNil
delegate:doDelegate
"Modified: / 20.5.1998 / 23:01:15 / cg"
! !
!DisplaySurface methodsFor:'event handling'!
activateMenu
"if there is a menu, show it."
|menu|
(menu := self middleButtonMenu) notNil ifTrue:[
self activateMenu:menu
]
"Created: / 01-03-1996 / 13:24:55 / cg"
"Modified: / 15-03-2017 / 10:41:26 / cg"
"Modified: / 02-03-2019 / 11:40:45 / Claus Gittinger"
!
activateMenu:aMenu
"if aMenu is a menu, show it."
|menu|
(menu := aMenu) notNil ifTrue:[
menu isBlock ifTrue:[ menu := menu value ].
menu isArray ifTrue:[
"/ a spec array
menu := menu decodeAsLiteralArray.
menu findGuiResourcesIn:(self application ? self) for:self.
menu receiver:self.
].
menu showAtPointer
]
"Created: / 02-03-2019 / 11:40:29 / Claus Gittinger"
!
buttonMotion:state x:x y:y
"mouse was moved while button is pressed - do nothing here"
^ self
!
buttonMultiPress:button x:x y:y
"button was pressed fast after previous press - default to press-again"
^ self buttonPress:button x:x y:y
!
buttonPress:button x:x y:y
"button was pressed - if it's middle button and there is a menu, show it."
(button == 2) ifTrue:[
UserPreferences current showRightButtonMenuOnRelease ifFalse:[
self activateMenu.
].
]
"Modified: / 01-03-1996 / 13:25:07 / cg"
"Modified (comment): / 13-02-2017 / 20:01:10 / cg"
!
buttonRelease:button x:x y:y
(button == 2) ifTrue:[
UserPreferences current showRightButtonMenuOnRelease ifTrue:[
self activateMenu.
].
].
!
catchExpose
"this MUST be sent BEFORE doing a bit-blt copy (i.e. copyFrom...),
to tell the sensor that incoming expose events are to be remembered.
Sometime after the bit-blt, waitForExpose should be sent, to finally
suspend until the expose/noExpose event arrives.
This is an X speciality - for devices that do not need this kind of
asynchronous bit-blt confirmation, this is a noop.
Answer true if the sender is required to do a waitForExpose, false if not.
"
|wg|
device scrollsAsynchronous ifFalse:[
self setGotExposeFlag.
^ false
].
self setGraphicsExposures:true.
self clearGotExposeFlag.
wg := self windowGroup.
wg notNil ifTrue:[
"
must process any pending expose events, since
usually the origin is changed soon so that previous
expose events coordinates are invalid
"
wg processRealExposeEventsFor:self.
wg sensor catchExposeFor:self.
^ true
].
^ false.
"Modified: / 06-08-1997 / 19:50:15 / cg"
"Modified (comment): / 30-05-2017 / 18:58:45 / mawalch"
!
coveredBy:aView
"the receiver has been covered by another view;
we are not interested in that here (but see modalBox for more)."
!
deviceButtonMotion:state x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a #buttonMotion with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #buttonMotion:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
].
].
self buttonMotion:state x:lx y:ly
"Modified: 20.5.1996 / 17:45:25 / cg"
"Modified: 14.10.1996 / 22:25:12 / stefan"
!
deviceButtonMultiPress:butt x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a buttonMultiPress with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #buttonMultiPress:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
].
].
self buttonMultiPress:butt x:lx y:ly
"Modified: 20.5.1996 / 17:45:22 / cg"
"Modified: 14.10.1996 / 22:25:22 / stefan"
!
deviceButtonPress:butt x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a buttonPress with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #buttonPress:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
].
].
self buttonPress:butt x:lx y:ly
"Modified: 20.5.1996 / 17:45:18 / cg"
"Modified: 14.10.1996 / 22:25:30 / stefan"
!
deviceButtonRelease:butt x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a buttonRelease with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #buttonRelease:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
].
].
self buttonRelease:butt x:lx y:ly
"Modified: 20.5.1996 / 17:45:14 / cg"
"Modified: 14.10.1996 / 22:25:37 / stefan"
!
deviceButtonShiftPress:butt x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a buttonShiftPress with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #buttonShiftPress:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
].
].
self buttonShiftPress:butt x:lx y:ly
"Modified: 20.5.1996 / 17:45:09 / cg"
"Modified: 14.10.1996 / 22:25:49 / stefan"
!
deviceExposeX:x y:y width:w height:h
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send an expose with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #exposeX:x:y:width:height:"
|lx ly lw lh currentTransformation|
lx := x.
ly := y.
lw := w.
lh := h.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
lw := currentTransformation applyInverseScaleX:lw.
lh := currentTransformation applyInverseScaleY:lh.
].
self exposeX:lx y:ly width:lw height:lh
"Modified: 13.5.1996 / 11:31:44 / cg"
"Modified: 14.10.1996 / 22:26:00 / stefan"
!
deviceGraphicsExposeX:x y:y width:w height:h final:final
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a graphicsExpose with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #graphicsExposeX:x:y:width:height:"
|lx ly lw lh currentTransformation|
lx := x.
ly := y.
lw := w.
lh := h.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
lw := currentTransformation applyInverseScaleX:lw.
lh := currentTransformation applyInverseScaleY:lh.
].
self graphicsExposeX:lx y:ly width:lw height:lh final:final
"Modified: 13.5.1996 / 11:31:54 / cg"
"Modified: 14.10.1996 / 22:26:08 / stefan"
!
deviceKeyPress:key x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a keyPress with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #keyPress:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
]
].
self keyPress:key x:lx y:ly
"Modified: 20.5.1996 / 17:44:59 / cg"
"Modified: 14.10.1996 / 22:26:17 / stefan"
!
deviceKeyRelease:key x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a keyRelease with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #keyRelease:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
]
].
self keyRelease:key x:lx y:ly
"Modified: 20.5.1996 / 17:44:42 / cg"
"Modified: 14.10.1996 / 22:26:28 / stefan"
!
devicePointerEnter:state x:x y:y
"this is the low-level (untransformed) event as received
from the device (i.e. coordinates are in device coordinates).
If there is a transformation, apply the inverse
and send a pointerEnter with the logical coordinates.
Views which are interested in deviceCoordinates should
redefine this method -
those which are interested in logical coordinates
should redefine #pointerEnter:x:y:"
|lx ly currentTransformation|
lx := x.
ly := y.
currentTransformation := gc transformation.
currentTransformation notNil ifTrue:[
lx notNil ifTrue:[
lx := currentTransformation applyInverseToX:lx.
ly := currentTransformation applyInverseToY:ly.
]
].
self pointerEnter:state x:lx y:ly
"Modified: 20.5.1996 / 17:44:54 / cg"
"Modified: 14.10.1996 / 22:26:35 / stefan"
!
exposeX:x y:y width:w height:h
"an expose event - nothing done here"
^ self
!
focusIn
"got keyboard focus - do nothing here"
^ self
!
focusOut
"lost keyboard focus - do nothing here"
^ self
!
graphicsExposeX:x y:y width:w height:h final:final
"an expose event after a scroll - do normal redraw processing"
self exposeX:x y:y width:w height:h
!
hotkeyWithId:aId rawKey:aKey
"default action is to do nothing - may be reimplemented "
^ self
!
keyPress:key x:x y:y
"a key was pressed in this view.
Here only keyCommands are handled - more action has to
be implemented by redefining this method"
|action rest restKey keyCommands|
(keyCommands := self keyCommands) notNil ifTrue:[
action := keyCommands at:key ifAbsent:[nil].
action value
].
key isSymbol ifTrue:[
(key startsWith:'Basic') ifTrue:[
"/ an unhandled BasicFoo key;
"/ retry as Foo
rest := key withoutPrefix:'Basic'.
restKey := rest asSymbolIfInterned.
restKey notNil ifTrue:[
self keyPress:restKey x:x y:y
]
].
].
"Modified: 6.11.1996 / 17:51:15 / cg"
!
keyRelease:key x:x y:y
"default action is to do nothing"
key isSymbol ifTrue:[
(key startsWith:#Basic) ifTrue:[
"/ an unhandled BasicFoo key;
"/ retry as Foo
self keyRelease:(key copyFrom:#Basic size) asSymbol x:x y:y
].
].
^ self
"Modified: 6.11.1996 / 17:51:20 / cg"
!
mouseWheelMotion:buttonState x:x y:y amount:amount deltaTime:dTime
"ignored"
^ self
!
noExpose
"a no expose event after a scroll (event-mode only)"
self setGotExposeFlag.
!
pointerEnter:state x:x y:y
"mouse cursor entered view - do nothing here"
^ self
!
pointerLeave:state
"mouse cursor left view - do nothing here"
^ self
!
resizeRequest
^ self
!
waitForExpose
"wait until an expose event arrives (to wait for scroll-finish)"
|wg endPollTime pollDelay|
device scrollsAsynchronous ifFalse:[
self setGotExposeFlag.
^ self
].
wg := self windowGroup.
wg notNil ifTrue:[
"/
"/ a normal (suspendable) view.
"/ wait by doing a real wait
"/
wg sensor waitForExposeFor:self
] ifFalse:[
"/
"/ a pure event driven view.
"/ wait by doing a direct dispatch loop until the event arrives.
"/ i.e. poll for the event
"/
device isWindowsPlatform ifTrue:[
pollDelay := 0.5.
] ifFalse:[
pollDelay := 1.
].
endPollTime := Timestamp now addSeconds:pollDelay.
[self gotExpose] whileFalse:[
realized ifTrue:[
(device exposeEventPendingFor:gc drawableId withSync:true) ifTrue:[
device dispatchExposeEventFor:gc drawableId.
].
].
realized ifFalse:[
self setGotExposeFlag.
^ self
].
"/ break out of the poll after a while
Timestamp now > endPollTime ifTrue:[
'DisplaySurface [warning]: lost expose event' errorPrintCR.
self setGotExposeFlag.
^ self
].
Processor yield.
].
]
"Modified: / 9.1.1999 / 01:58:09 / cg"
! !
!DisplaySurface methodsFor:'initialization & release'!
destroy
"view is about to be destroyed -
first destroy menu if there is one and also destroy the GC.
then the view is physically destroyed."
self
middleButtonMenu:nil;
keyCommands:nil.
device notNil ifTrue:[
device removeKnownView:self withId:nil.
].
super destroy.
!
destroyed
"view has been destroyed by someone else"
|id|
(id := self drawableId) notNil ifTrue:[
gc setId:nil.
self graphicsDevice removeKnownView:self withId:id.
realized := false.
].
self destroy
"Modified: 22.3.1997 / 14:56:34 / cg"
!
initCursor
"default cursor for all views"
cursor := Cursor normal
!
initStyle
"nothing done here"
^ self
!
initialize
"initialize defaults"
<modifier: #super> "must be called if redefined"
super initialize.
eventMask := 0.
device notNil ifTrue:[
eventMask := device defaultEventMask.
].
gc notNil ifTrue:[ viewBackground := gc background].
backed := false.
flags := 0.
self initCursor
"Modified: / 08-02-2017 / 00:26:56 / cg"
!
prepareForReinit
gc notNil ifTrue:[
gc prepareForReinit.
].
!
reAdjustGeometry
"sent late during snapin processing, nothing done here"
^ self
!
recreate
"recreate (i.e. tell X about me) after a snapin or a migration"
viewBackground isColor ifTrue:[
viewBackground := viewBackground onDevice:device.
].
super recreate.
cursor notNil ifTrue:[
cursor := cursor onDevice:device.
].
"Modified: 28.3.1997 / 13:48:06 / cg"
!
reinitStyle
"nothing done here"
^ self
!
releaseDeviceResources
super destroy.
! !
!DisplaySurface methodsFor:'keyboard commands'!
addActionForKey:aKey action:aBlock
"define a keyboard command function"
|keyCommands|
(keyCommands := self keyCommands) isNil ifTrue:[
self keyCommands:(keyCommands := IdentityDictionary new).
].
keyCommands at:aKey put:aBlock
!
keyCommands
^ self getAttribute:#keyCommands
!
keyCommands:aDictionaryOrNil
self setAttribute:#keyCommands to:aDictionaryOrNil
!
removeActionForKey:aKey
|keyCommands|
(keyCommands := self keyCommands) notNil ifTrue:[
keyCommands removeKey:aKey ifAbsent:nil
]
! !
!DisplaySurface methodsFor:'queries'!
buttonMotionEventPending
"return true, if a button motion event is pending.
Normally, you don't want to use this, since no polling is needed
(not even for mouse-tracking).
Also, don't use it, since it does not honor the windowGroup,
but goes directly to the device instead.
Actually, its a historical leftover"
device flush.
^ device eventPending:#buttonMotion for:gc drawableId
!
buttonReleaseEventPending
"return true, if a button release event is pending.
Don't use it, since it does not honor the windowGroup, but
goes directly to the device instead.
Actually, its a historical leftover"
device flush.
^ device eventPending:#buttonRelease for:gc drawableId
!
exposeEventPending
"return true, if an expose event is pending."
|windowGroup|
windowGroup := self windowGroup.
windowGroup notNil ifTrue:[
(windowGroup sensor hasExposeEventFor:self) ifTrue:[^ true].
].
^ device eventPending:#expose for:gc drawableId
"Modified: / 15.9.1998 / 23:18:16 / cg"
!
redrawsFull
^ false
"Created: 4.3.1996 / 14:17:22 / cg"
!
shown
"return true if at least a part of myself is currently visible
(I am mapped and not completely covered).
Assumed that I am always visible when realized."
^ realized
! !
!DisplaySurface methodsFor:'queries-contents'!
heightOfContents
"return the height of the contents in pixels.
Since we don't know here, just return the view's size.
This will make your scrollbars show 100%-visible.
Must be redefined in subviews to make scrollbars really work."
^ self height
!
widthOfContents
"return the width of the contents in pixels.
Since we don't know here, just return the view's size.
This will make your scrollbars show 100%-visible.
Must be redefined in subviews to make scrollbars really work."
^ self width
!
xOriginOfContents
"return the x-origin of the contents in pixels.
Since we don't know here, just return 0 for left.
Must be redefined in subviews to make scrollbars really work."
^ 0
!
yOriginOfContents
"return the y-origin of the contents in pixels.
Since we don't know here, just return 0 for top.
Must be redefined in subviews to make scrollbars really work."
^ 0
! !
!DisplaySurface methodsFor:'testing'!
isDebugView
^ false
!
isPopUpView
"return true, if this view should be put on top (raised) automatically.
usually this is true for alertBoxes etc."
^ false
"Created: / 22.1.1998 / 15:01:32 / stefan"
!
isRootView
"return true, if the receiver is a root view
false is returned here, this is only redefined in DisplayRootView."
^ false
"Modified: 28.5.1996 / 19:27:34 / cg"
"Created: 5.7.1996 / 14:58:55 / cg"
!
isTopView
"return true, if the receiver is some kind of topview;
false is returned here; redefined in some subclasses."
^ false
"Modified: 28.5.1996 / 19:27:34 / cg"
"Created: 22.3.1997 / 14:45:29 / cg"
!
isView
"return true, if the receiver is some kind of view;
true is returned here."
^ true
"Modified: 28.5.1996 / 19:27:34 / cg"
! !
!DisplaySurface methodsFor:'user interaction & notifications'!
beep
"output an audible beep or bell on my screen device"
device beep
"Created: 28.5.1996 / 16:16:13 / cg"
"Modified: 28.5.1996 / 16:58:25 / cg"
!
beepInEditor
"output an audible beep or bell on my screen device, if enabled.
Beep to wakeup the user after some error/not found condition in an editor.
Use this ONLY to beep in response to a bad user operation (not system failure beeps).
Can be disabled via the settings if too annoying"
device beepInEditor
"Created: 28.5.1996 / 16:16:13 / cg"
"Modified: 28.5.1996 / 16:58:25 / cg"
!
showActivity:aMessage
"this is sent indirectly by the activityNotification mechanism.
Defined here as a fallback, if ever sent to non topviews."
Transcript showCR:aMessage
"Modified: 18.5.1996 / 15:44:33 / cg"
! !
!DisplaySurface class methodsFor:'documentation'!
version
^ '$Header$'
!
version_CVS
^ '$Header$'
! !
DisplaySurface initialize!