ca@2176: "{ Package: 'stx:libtool2' }" ca@2176: ca@2176: "{ NameSpace: Tools }" ca@2176: ca@2176: ValueModel subclass:#ViewTreeModel ca@2176: instanceVariableNames:'lockSema selectedSuperItems selection hiddenLevel listOfItems ca@2176: inputEventAction mappedViewAction beforeSelectionChangedAction ca@2457: icons timedUpdateTask selectOnClickHolder testModeChannel ca@2457: hasTargetWidgetChannel' ca@2176: classVariableNames:'' ca@2176: poolDictionaries:'' ca@2176: category:'A-Views-Support' ca@2176: ! ca@2176: ca@2176: HierarchicalList subclass:#ItemList ca@2453: instanceVariableNames:'treeModel eventHook eventHookInitialized showWidgetNames' ca@2176: classVariableNames:'' ca@2176: poolDictionaries:'' ca@2176: privateIn:ViewTreeModel ca@2176: ! ca@2176: ca@2176: !ViewTreeModel class methodsFor:'documentation'! ca@2176: ca@2176: documentation ca@2176: " ca@2176: Instances of ViewTreeModel can be used as model on a View and all ca@2176: it contained subviews for a HierarchicalListView. ca@2176: The model keeps two values, the hierarchical representation of the views ca@2176: and subviews (ViewTreeItems) and the selection, a list of selected ViewTreeItems's. ca@2176: It shows the selected items highlighted. ca@2176: ca@2176: ca@2176: [Instance variables:] ca@2176: lockSema lock selection notifications and redraws ca@2176: ca@2457: testModeChannel true, than running in test mode. ca@2457: ca@2457: hasTargetWidgetChannel true, than any target view is grapped ca@2176: ca@2176: selection selected items or nil ca@2176: ca@2176: hiddenLevel internal use; redrawing the selection ca@2176: only is done if the counter is 0. ca@2176: ca@2176: listOfItems hiearchical list build from existing items. ca@2176: ca@2176: selectedSuperItems list of selected super items; items selected ca@2176: but not contained in another selected item. ca@2176: ca@2176: inputEventAction called for each InputEvent ca@2176: ca@2176: mappedViewAction called for a new mapped view which ca@2176: can not be found in the current item list. ca@2176: ca@2176: beforeSelectionChangedAction called before the selection changed ca@2176: ca@2176: [author:] ca@2176: Claus Atzkern ca@2176: ca@2176: [see also:] ca@2176: ViewTreeItem ca@2176: " ca@2176: ! ca@2176: ca@2176: examples ca@2176: " ca@2176: example 1: pick any window and show views and contained views ca@2176: [exBegin] ca@2176: |top sel model panel| ca@2176: ca@2176: model := ViewTreeModel new. ca@2176: top := StandardSystemView new; extent:440@400. ca@2176: sel := ScrollableView for:HierarchicalListView miniScroller:true origin:0.0@0.0 corner:1.0@1.0 in:top. ca@2176: sel bottomInset:24. ca@2176: ca@2176: panel := HorizontalPanelView origin:0.0@1.0 corner:1.0@1.0 in:top. ca@2176: panel topInset:-24. ca@2176: panel horizontalLayout:#fitSpace. ca@2176: ca@2176: Button label:'Exit' action:[model rootItem:nil. top destroy] in:panel. ca@2176: Button label:'Pick Views' action:[ |win| ca@2176: ( (win := Screen current viewFromUser) notNil ca@2176: and:[(win := win topView) ~~ Screen current rootView ca@2176: and:[win ~~ top]] ca@2176: ) ifTrue:[ ca@2176: model rootItem:(ViewTreeItem buildViewsFrom:win) ca@2176: ] ifFalse:[ ca@2176: model rootItem:nil ca@2176: ] ca@2176: ] in:panel. ca@2176: ca@2176: sel multipleSelectOk:true. ca@2176: sel list:model listOfItems. ca@2176: sel model:model. ca@2176: sel useIndex:false. ca@2176: ca@2176: sel doubleClickAction:[:i| |el| ca@2176: el := model listOfItems at:i. ca@2176: el spec notNil ifTrue:[ el spec inspect ] ifFalse:[ el widget inspect ] ca@2176: ]. ca@2176: sel indicatorAction:[:i| (model listOfItems at:i) toggleExpand ]. ca@2176: ca@2176: model inputEventAction:[:anEvent| |item| ca@2176: anEvent isButtonEvent ifTrue:[ ca@2176: anEvent isButtonPressEvent ifTrue:[ ca@2176: model selectedItem:(model listOfItems detectItemRespondsToView:(anEvent view)). ca@2176: ] ifFalse:[ ca@2176: anEvent type == #'buttonMultiPress:x:y:' ifTrue:[ ca@2176: (item := model selectedItem) notNil ifTrue:[item widget inspect] ca@2176: ] ca@2176: ] ca@2176: ] ca@2176: ]. ca@2176: ca@2176: top openAndWait. ca@2176: [[top shown] whileTrue:[Delay waitForSeconds:0.5]. model rootItem:nil] forkAt:8 ca@2176: ca@2176: [exEnd] ca@2176: " ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'accessing'! ca@2176: ca@2176: application:anApplication ca@2176: listOfItems application:anApplication. ca@2176: ! ca@2176: ca@2176: rootItem ca@2176: "get the rootItem the event viewer is established on ca@2176: " ca@2176: ^ listOfItems root ca@2176: ! ca@2176: ca@2176: rootItem:anItem ca@2176: "set the rootItem the event viewer is established on ca@2176: " ca@2176: |expanded| ca@2176: ca@2176: timedUpdateTask := nil. ca@2457: self deselect. ca@2176: ca@2176: lockSema critical:[ ca@2176: anItem notNil ifTrue:[ expanded := anItem isExpanded ] ca@2176: ifFalse:[ expanded := false ]. ca@2176: ca@2176: self value:nil. ca@2176: listOfItems root:anItem. ca@2176: ca@2176: anItem notNil ifTrue:[ ca@2176: timedUpdateTask := Process for:[ self timedUpdateTaskCycle ] priority:8. ca@2176: timedUpdateTask name:'Update'. ca@2176: timedUpdateTask resume. ca@2176: ]. ca@2176: ]. ca@2176: ca@2176: (expanded and:[anItem notNil]) ifTrue:[ ca@2176: anItem expand ca@2176: ]. ca@2176: ^ anItem ca@2176: ! ca@2176: ca@2176: rootView ca@2176: "get the top widget the event viewer is established on, a View ca@2176: " ca@2176: ^ listOfItems rootView ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'accessing actions'! ca@2176: ca@2176: beforeSelectionChangedAction ca@2176: "none argument action which is called before ca@2176: the selection changed ca@2176: " ca@2176: ^ beforeSelectionChangedAction ca@2176: ! ca@2176: ca@2176: beforeSelectionChangedAction:aNoneArgBlock ca@2176: "none argument action which is called before ca@2176: the selection changed ca@2176: " ca@2176: beforeSelectionChangedAction := aNoneArgBlock. ca@2176: ! ca@2176: ca@2176: inputEventAction ca@2176: "called for each input event; the argument to the action is the WindowEvent ca@2176: " ca@2176: ^ inputEventAction ca@2176: ! ca@2176: ca@2176: inputEventAction:aOneArgActionTheEvent ca@2176: "called for each input event; the argument to the action is the WindowEvent ca@2176: " ca@2176: inputEventAction := aOneArgActionTheEvent. ca@2176: ! ca@2176: ca@2176: mappedViewAction ca@2176: "called for a new mapped view which can not be found ca@2176: in the current item list ca@2176: " ca@2176: ^ mappedViewAction ca@2176: ! ca@2176: ca@2176: mappedViewAction:aOneArgBlockTheMappedView ca@2176: "called for a new mapped view which can not be found ca@2176: in the current item list ca@2176: " ca@2176: mappedViewAction := aOneArgBlockTheMappedView ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'accessing look'! ca@2176: ca@2176: iconAt:aKey ifNonePut:aNoneArgBlock ca@2176: |icon view| ca@2176: ca@2176: icon := icons at:aKey ifAbsent:nil. ca@2176: icon notNil ifTrue:[^ icon]. ca@2176: ca@2176: icon := aNoneArgBlock value. ca@2176: icon isNil ifTrue:[^ nil]. ca@2176: ca@2176: view := self rootView. ca@2176: view isNil ifTrue:[^ icon]. ca@2176: ca@2176: icon := icon copy onDevice:(view device). ca@2176: icon isImage ifTrue:[ ca@2176: icon clearMaskedPixels. ca@2176: ]. ca@2176: icons at:aKey put:icon. ca@2176: ^ icon ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'accessing visibility'! ca@2176: ca@2176: signalHiddenLevel ca@2176: "show the selection if signaled; increments hiddenLevel ca@2176: see: #waitHiddenLevel ca@2176: " ca@2176: (hiddenLevel := hiddenLevel - 1) < 1 ifTrue:[ ca@2176: hiddenLevel := 0. ca@2176: self invalidateSelection. ca@2176: ]. ca@2176: ! ca@2176: ca@2176: waitHiddenLevel ca@2176: "hide the selection until signaled; increments hiddenLevel ca@2176: see: #signalHiddenLevel ca@2176: " ca@2176: self redrawUnselected:selection andLock:true ca@2176: ! ca@2176: ca@2176: withSelectionHiddenDo:aNoneArgumentBlock ca@2176: "apply block with selection hidden ca@2176: " ca@2176: ca@2176: [ self waitHiddenLevel. ca@2176: ca@2176: aNoneArgumentBlock value ca@2176: ca@2176: ] valueNowOrOnUnwindDo:[ ca@2176: self signalHiddenLevel. ca@2176: ]. ca@2176: ! ! ca@2176: ca@2457: !ViewTreeModel methodsFor:'aspects'! ca@2457: ca@2457: hasTargetWidgetChannel ca@2457: "answer the channel which is set to true if a target widget exists" ca@2457: ca@2457: ^ hasTargetWidgetChannel ca@2457: ! ca@2457: ca@2457: listOfItems ca@2457: "hiearchical list build from existing items" ca@2457: ca@2457: ^ listOfItems ca@2457: ! ca@2457: ca@2457: selectOnClickHolder ca@2457: "boolean holder, which indicates whether the selection will change on click ca@2457: " ca@2457: ^ selectOnClickHolder ca@2457: ! ca@2457: ca@2457: testModeChannel ca@2457: "answer a boolean channel which describes the behaviour how to process ca@2457: events on the target view. ca@2457: ca@2457: false: all input events are eaten and the selection is shown on the target view. ca@2457: true: no input events are eaten and no selection is shown on the target view." ca@2457: ca@2457: ^ testModeChannel ca@2457: ! ! ca@2457: ca@2176: !ViewTreeModel methodsFor:'change & update'! ca@2176: ca@2457: targetWidgetChanged ca@2457: hasTargetWidgetChannel value:(self rootItem notNil). ca@2457: ! ca@2457: ca@2176: timedUpdateTaskCycle ca@2176: |view myTaskId| ca@2176: ca@2176: myTaskId := timedUpdateTask. ca@2176: ca@2176: listOfItems root notNil ifTrue:[ ca@2176: view := listOfItems root widget. ca@2176: ]. ca@2176: ca@2176: [ view notNil ] whileTrue:[ ca@2176: Delay waitForSeconds:0.5. ca@2176: ca@2176: (myTaskId == timedUpdateTask and:[view id notNil]) ifFalse:[ ca@2176: view := nil. ca@2176: ] ifTrue:[ ca@2176: (view sensor hasUserEvent:#updateChildren for:self) ifFalse:[ ca@2176: view sensor pushUserEvent:#updateChildren for:self. ca@2176: ]. ca@2176: ]. ca@2176: ]. ca@2176: timedUpdateTask == myTaskId ifTrue:[ ca@2176: timedUpdateTask := nil. ca@2176: listOfItems root:nil. ca@2176: ]. ca@2176: ! ca@2176: ca@2457: update:something with:someArgument from:aModel ca@2457: ca@2457: aModel == testModeChannel ifTrue:[ ca@2457: (hiddenLevel == 0 and:[selection size > 0]) ifTrue:[ ca@2457: testModeChannel value ifTrue:[ ca@2457: self redrawUnselected:selection andLock:false checkTestMode:false. ca@2457: ] ifFalse:[ ca@2457: self invalidateSelection. ca@2457: ]. ca@2457: ]. ca@2457: ^ self ca@2457: ]. ca@2457: super update:something with:someArgument from:aModel. ca@2457: ! ca@2457: ca@2176: updateChildren ca@2176: |rootItem| ca@2176: ca@2176: rootItem := listOfItems root. ca@2176: rootItem isNil ifTrue:[^ self]. ca@2176: ca@2176: rootItem exists ifFalse:[ ca@2176: listOfItems root:nil. ca@2176: ] ifTrue:[ ca@2176: rootItem updateChildren. ca@2176: ]. ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'event processing'! ca@2176: ca@2176: processEvent:anEvent ca@2176: "catch and process all WindowEvents for the rootComponent and its contained ca@2176: widgets; redraw selection in case of damage .... ca@2176: " ca@2457: |evView item rootView testMode| ca@2176: ca@2176: evView := anEvent view. ca@2176: evView isNil ifTrue:[ ca@2176: (anEvent isMessageSendEvent and:[anEvent receiver == self]) ifFalse:[ ca@2176: ^ false ca@2176: ]. ca@2176: anEvent value. ca@2176: ^ true. ca@2176: ]. ca@2176: rootView := listOfItems rootView. ca@2176: rootView isNil ifTrue:[ ^ false ]. ca@2176: ca@2176: anEvent isConfigureEvent ifTrue:[ ca@2176: hiddenLevel == 0 ifTrue:[ ca@2176: self redrawUnselected:selection andLock:false. ca@2176: ]. ca@2176: ^ false ca@2176: ]. ca@2176: ca@2176: "/ check whether view is contained within the rootView ca@2176: (evView == rootView or:[evView isComponentOf:rootView]) ifFalse:[ ca@2176: ^ false ca@2176: ]. ca@2176: ca@2176: anEvent isInputEvent ifFalse:[ ca@2176: anEvent isDamage ifTrue:[ ca@2176: hiddenLevel == 0 ifTrue:[self invalidateSelection]. ca@2176: ^ false ca@2176: ]. ca@2176: ca@2176: anEvent isMapEvent ifTrue:[ ca@2176: mappedViewAction notNil ifTrue:[ ca@2176: item := listOfItems recursiveDetect:[:el| el widget == evView]. ca@2176: item isNil ifTrue:[ mappedViewAction value:evView ] ca@2176: ]. ca@2176: ^ false ca@2176: ]. ca@2176: ca@2176: anEvent type == #terminate ifTrue:[ ca@2176: item := listOfItems recursiveDetect:[:el| el widget == evView]. ca@2176: item notNil ifTrue:[ self processTerminateForItem:item ]. ca@2176: ^ false ca@2176: ]. ca@2176: ^ false ca@2176: ]. ca@2457: testMode := testModeChannel value. ca@2457: ca@2176: anEvent isFocusEvent ifTrue:[ ca@2176: evView == rootView ifTrue:[ ca@2176: self invalidateSelection ca@2176: ]. ca@2176: ^ testMode not. ca@2176: ]. ca@2176: anEvent isPointerEnterLeaveEvent ifTrue:[ ^ testMode not ]. ca@2176: ca@2176: testMode ifFalse:[ ca@2176: inputEventAction notNil ifTrue:[ inputEventAction value:anEvent ]. ca@2176: ] ifTrue:[ ca@2176: anEvent isButtonPressEvent ifTrue:[ ca@2176: selectOnClickHolder value ifTrue:[ ca@2176: self selectItem:(listOfItems detectItemRespondsToView:evView). ca@2176: ]. ca@2176: ] ca@2176: ]. ca@2176: ca@2176: (hiddenLevel ~~ 0 and:[anEvent isButtonReleaseEvent]) ifTrue:[ ca@2176: hiddenLevel := 1. ca@2176: self signalHiddenLevel. ca@2176: ]. ca@2176: ca@2176: ^ testMode not ca@2176: ! ca@2176: ca@2176: processTerminateForItem:anItem ca@2176: "received terminate for an item ca@2176: " ca@2176: anItem remove. ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'initialization'! ca@2176: ca@2176: initialize ca@2176: "setup the default attributes ca@2176: " ca@2176: super initialize. ca@2176: ca@2457: hiddenLevel := 0. ca@2457: lockSema := RecursionLock new. ca@2457: listOfItems := ItemList new on:self. ca@2457: selectedSuperItems := #(). ca@2457: icons := IdentityDictionary new. ca@2457: ca@2457: hasTargetWidgetChannel := false asValue. ca@2457: selectOnClickHolder := true asValue. ca@2457: ca@2457: testModeChannel := false asValue. ca@2457: testModeChannel addDependent:self. ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'private selection'! ca@2176: ca@2176: invalidateSelection ca@2176: "invalidate the current selection ca@2176: " ca@2176: |topView| ca@2176: ca@2457: testModeChannel value ifTrue:[ ^ self ]. "/ test whether running testMode ca@2176: ca@2176: ( hiddenLevel == 0 ca@2176: and:[selection notNil ca@2176: and:[(topView := listOfItems rootView) notNil ca@2176: and:[topView shown]]] ca@2176: ) ifTrue:[ ca@2176: topView sensor pushUserEvent:#redrawSelection for:self withArguments:#() ca@2176: ] ca@2176: ! ca@2176: ca@2176: recursiveRepair:theDamages startIn:aView relativeTo:aRootView ca@2176: "repair all views and contained views, which intersects the damage. ca@2176: !!!! all damages repaired are removed from the list of damages !!!! ca@2176: " ca@2176: |color relOrg damage subViews repaired ca@2176: bwWidth "{ Class:SmallInteger }" ca@2176: x "{ Class:SmallInteger }" ca@2176: y "{ Class:SmallInteger }" ca@2176: w "{ Class:SmallInteger }" ca@2176: h "{ Class:SmallInteger }" ca@2176: relOrgX "{ Class:SmallInteger }" ca@2176: relOrgY "{ Class:SmallInteger }" ca@2176: width "{ Class:SmallInteger }" ca@2176: height "{ Class:SmallInteger }" ca@2176: size "{ Class:SmallInteger }" ca@2176: | ca@2176: (aView shown and:[theDamages notEmpty]) ifFalse:[ ^ self ]. ca@2176: ca@2176: subViews := aView subViews. ca@2176: ca@2176: subViews size ~~ 0 ifTrue:[ ca@2176: subViews reverseDo:[:v| self recursiveRepair:theDamages startIn:v relativeTo:aRootView ]. ca@2176: theDamages isEmpty ifTrue:[ ^ self ]. ca@2176: ]. ca@2176: ca@2176: relOrg := aView originRelativeTo:aRootView. ca@2176: bwWidth := aView borderWidth. ca@2176: size := theDamages size. ca@2176: ca@2176: "/ compute relative origin starting from border left@top ca@2176: relOrgX := relOrg x - bwWidth. ca@2176: relOrgY := relOrg y - bwWidth. ca@2176: width := aView width + bwWidth + bwWidth. ca@2176: height := aView height + bwWidth + bwWidth. ca@2176: ca@2176: size to:1 by:-1 do:[:anIndex| ca@2176: repaired := damage := theDamages at:anIndex. ca@2176: ca@2176: "/ compute the rectangle into the view ca@2176: y := damage top - relOrgY. ca@2176: x := damage left - relOrgX. ca@2176: w := damage width. ca@2176: h := damage height. ca@2176: ca@2176: x < 0 ifTrue:[ w := w + x. x := 0. repaired := nil ]. ca@2176: y < 0 ifTrue:[ h := h + y. y := 0. repaired := nil ]. ca@2176: x + w > width ifTrue:[ w := width - x. repaired := nil ]. ca@2176: y + h > height ifTrue:[ h := height - y. repaired := nil ]. ca@2176: ca@2176: (w > 0 and:[h > 0]) ifTrue:[ ca@2176: bwWidth ~~ 0 ifTrue:[ ca@2176: color isNil ifTrue:[ ca@2176: "/ must force redraw of border ca@2176: color := aView borderColor. ca@2176: aView borderColor:(Color colorId:1). ca@2176: aView borderColor:color. ca@2176: ]. ca@2176: w := w - bwWidth. ca@2176: h := h - bwWidth. ca@2176: ca@2176: (x := x - bwWidth) < 0 ifTrue:[w := w + x. x := 0]. ca@2176: (y := y - bwWidth) < 0 ifTrue:[h := h + y. y := 0]. ca@2176: ca@2176: h > 0 ifFalse:[w := 0]. "/ later testing on width only ca@2176: ]. ca@2176: ca@2176: w > 0 ifTrue:[ ca@2176: aView clearRectangleX:x y:y width:w height:h. ca@2176: aView exposeX:x y:y width:w height:h ca@2176: ]. ca@2176: repaired notNil ifTrue:[ theDamages removeFromIndex:anIndex toIndex:anIndex ]. ca@2176: ] ca@2176: ]. ca@2176: ! ca@2176: ca@2176: redrawSelection ca@2176: "redraw all items selected ca@2176: " ca@2176: |topView size| ca@2176: ca@2457: testModeChannel value ifTrue:[ ^ self ]. "/ test whether running testMode ca@2176: ca@2176: ( hiddenLevel == 0 ca@2176: and:[(size := selection size) > 0 ca@2176: and:[(topView := listOfItems rootView) notNil ca@2176: and:[topView shown ca@2176: and:[(topView sensor hasEvent:#redrawSelection for:self) not]]]] ca@2176: ) ifFalse:[ ca@2176: ^ self ca@2176: ]. ca@2176: ca@2176: lockSema critical:[ ca@2457: |list| ca@2457: ca@2457: list := selection. ca@2176: ca@2457: list size > 0 ifTrue:[ ca@2457: topView paint:(Color black). ca@2457: topView clippedByChildren:false. ca@2176: ca@2457: list keysAndValuesReverseDo:[:anIndex :anItem| ca@2457: (anIndex == 1 and:[size > 1]) ifTrue:[ topView paint:(Color red) ]. ca@2457: ca@2457: anItem handlesDo:[:aRect :what| ca@2457: what isNil ifTrue:[topView displayRectangle:aRect] ca@2457: ifFalse:[topView fillRectangle:aRect] ca@2457: ] ca@2457: ]. ca@2457: topView clippedByChildren:true. ca@2176: ]. ca@2176: ]. ca@2176: ! ca@2176: ca@2176: redrawUnselected:aList andLock:doLock ca@2176: "redraw all items unselected; if doLock is true, the hiddenLevel ca@2176: is incremented and thus the select mechanism is locked. ca@2176: " ca@2457: self redrawUnselected:aList andLock:doLock checkTestMode:true. ca@2457: ! ca@2457: ca@2457: redrawUnselected:aList andLock:doLock checkTestMode:checkTestMode ca@2457: "redraw all items unselected; if doLock is true, the hiddenLevel ca@2457: is incremented and thus the select mechanism is locked. ca@2457: " ca@2176: |rootView damages subViews x y w h| ca@2176: ca@2176: doLock ifTrue:[ ca@2176: hiddenLevel := hiddenLevel + 1. ca@2176: hiddenLevel ~~ 1 ifTrue:[^ self]. ca@2176: ] ifFalse:[ ca@2176: hiddenLevel ~~ 0 ifTrue:[^ self]. ca@2176: ]. ca@2457: checkTestMode ifTrue:[ ca@2457: testModeChannel value ifTrue:[ ^ self ]. "/ test whether running testMode ca@2457: ]. ca@2176: ca@2176: ( aList size ~~ 0 ca@2176: and:[(rootView := listOfItems rootView) notNil ca@2176: and:[rootView shown]] ca@2176: ) ifFalse:[ ca@2176: ^ self ca@2176: ]. ca@2176: ca@2176: lockSema critical:[ ca@2176: damages := OrderedCollection new:(8 * aList size). ca@2176: ca@2176: aList do:[:item| ca@2176: item handlesDo:[:handle :what| ca@2176: damages reverseDo:[:el| ca@2176: (el intersects:handle) ifTrue:[ ca@2176: damages removeIdentical:el. ca@2176: ca@2176: handle left:(handle left min:el left) ca@2176: right:(handle right max:el right) ca@2176: top:(handle top min:el top) ca@2176: bottom:(handle bottom max:el bottom) ca@2176: ] ca@2176: ]. ca@2176: damages add:handle ca@2176: ] ca@2176: ]. ca@2176: ca@2176: damages do:[:el| ca@2176: x := el left. ca@2176: y := el top. ca@2176: w := el width. ca@2176: h := el height. ca@2176: ca@2176: rootView clearRectangleX:x y:y width:w height:h. ca@2176: rootView exposeX:x y:y width:w height:h. ca@2176: ]. ca@2176: ca@2176: (subViews := rootView subViews) notNil ifTrue:[ ca@2176: subViews reverseDo:[:v| self recursiveRepair:damages startIn:v relativeTo:rootView ]. ca@2176: ]. ca@2176: ]. ca@2176: ! ! ca@2176: ca@2457: !ViewTreeModel methodsFor:'queries'! ca@2457: ca@2457: isInTestMode ca@2457: "answer false, all input events are eaten and the selection is shown on the target view. ca@2457: answer true, no input events are eaten and no selection is shown on the target view." ca@2457: ca@2457: ^ testModeChannel value ca@2457: ! ! ca@2457: ca@2176: !ViewTreeModel methodsFor:'selection accessing'! ca@2176: ca@2176: at:anIndex ca@2176: "returns the selected item at an index or nil ca@2176: " ca@2176: selection notNil ifTrue:[ ca@2176: ^ selection at:anIndex ifAbsent:nil ca@2176: ]. ca@2176: ^ nil ca@2176: ! ca@2176: ca@2176: at:anIndex ifAbsent:aBlock ca@2176: "returns the selected item at an index or the result of the block ca@2176: " ca@2176: selection notNil ifTrue:[ ca@2176: ^ selection at:anIndex ifAbsent:aBlock ca@2176: ]. ca@2176: ^ aBlock value ca@2176: ! ca@2176: ca@2176: first ca@2176: "returns the first selected item or nil ca@2176: " ca@2176: ^ self at:1 ca@2176: ! ca@2176: ca@2176: last ca@2176: "returns the last selected item or nil ca@2176: " ca@2176: ^ selection notNil ifTrue:[selection last] ifFalse:[nil] ca@2176: ! ca@2176: ca@2176: selectedItem ca@2176: "returns the single selected item or nil (size ~~ 1 nil is returned) ca@2176: " ca@2176: ^ selection size == 1 ifTrue:[selection at:1] ifFalse:[nil] ca@2176: ! ca@2176: ca@2176: selectedSuperItems ca@2176: "returs the list of selected superItems; items selected ca@2176: but not contained in another selected item. ca@2176: " ca@2176: ^ selectedSuperItems ca@2176: ! ca@2176: ca@2176: size ca@2176: "returns the number of items selected ca@2176: " ca@2176: ^ selection size ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'selection adding & removing'! ca@2176: ca@2176: add:item ca@2176: "add an item to the current selection ca@2176: " ca@2176: |newSelect| ca@2176: ca@2176: item isNil ifTrue:[^ item]. ca@2176: ca@2176: lockSema critical:[ ca@2176: selection isNil ifTrue:[ ca@2176: newSelect := Array with:item. ca@2176: ] ifFalse:[ ca@2176: (self includes:item) ifFalse:[ ca@2176: newSelect := selection copyWith:item ca@2176: ] ca@2176: ]. ca@2176: ca@2176: newSelect size ~~ selection size ifTrue:[ ca@2176: item makeVisible. ca@2176: self value:newSelect ca@2176: ] ca@2176: ]. ca@2176: ^ item ca@2176: ! ca@2176: ca@2176: addAll:aCollectionOfItems ca@2176: "add a collection of items to the current selection ca@2176: " ca@2176: |newSelect| ca@2176: ca@2176: aCollectionOfItems size == 0 ifTrue:[ ^ aCollectionOfItems ]. ca@2176: ca@2176: lockSema critical:[ ca@2176: selection isNil ifTrue:[ ca@2176: newSelect := Array withAll:aCollectionOfItems. ca@2176: ] ifFalse:[ ca@2176: newSelect := OrderedCollection withAll:selection. ca@2176: ca@2176: aCollectionOfItems do:[:el| ca@2176: (selection includesIdentical:el) ifFalse:[newSelect add:el] ca@2176: ]. ca@2176: ]. ca@2176: self value:newSelect. ca@2176: ]. ca@2176: ^ aCollectionOfItems ca@2176: ! ca@2176: ca@2176: deselect ca@2176: "clear the selection ca@2176: " ca@2176: self value:nil. ca@2176: ! ca@2176: ca@2176: remove:item ca@2176: "remove the item from the current selection ca@2176: " ca@2176: |newSelect| ca@2176: ca@2176: item isNil ifTrue:[^ nil]. ca@2176: ca@2176: lockSema critical:[ ca@2176: (selection notNil and:[selection includesIdentical:item]) ifTrue:[ ca@2176: selection size == 1 ifTrue:[ newSelect := nil ] ca@2176: ifFalse:[ newSelect := selection copyWithout:item ]. ca@2176: ca@2176: self value:newSelect ca@2176: ]. ca@2176: ]. ca@2176: ^ item ca@2176: ! ca@2176: ca@2176: removeAll ca@2176: "clear the selection ca@2176: " ca@2176: self deselect. ca@2176: ! ca@2176: ca@2176: removeAll:loItems ca@2176: "remove all items of the collection from the current selection ca@2176: " ca@2176: |newSelect| ca@2176: ca@2176: selection isNil ifTrue:[ ^ loItems ]. ca@2176: loItems size == 0 ifTrue:[ ^ loItems ]. ca@2176: ca@2176: lockSema critical:[ ca@2176: selection notNil ifTrue:[ ca@2176: newSelect := selection select:[:el| (loItems includesIdentical:el) not ]. ca@2176: self value:newSelect. ca@2176: ] ca@2176: ]. ca@2176: ^ loItems ca@2176: ! ca@2176: ca@2176: selectAll ca@2176: "select all items ca@2176: " ca@2176: |root newSelection| ca@2176: ca@2176: root := listOfItems root. ca@2176: ca@2176: root isNil ifTrue:[ ca@2176: newSelection := nil ca@2176: ] ifFalse:[ ca@2176: newSelection := OrderedCollection new. ca@2176: root recursiveDo:[:el| newSelection add:el ]. ca@2176: ]. ca@2176: self value:newSelection. ca@2176: ! ca@2176: ca@2176: selectItem:anItem ca@2176: "set the current selection to the item ca@2176: " ca@2176: self value:anItem ca@2176: ! ca@2176: ca@2176: selectRootItem ca@2176: "set the current selection to the root item ca@2176: " ca@2176: self value:(self rootItem). ca@2176: ! ca@2176: ca@2176: selectedItem:anItem ca@2176: "set the current selection to the item ca@2176: " ca@2176: self selectItem:anItem. ca@2176: ! ca@2176: ca@2176: toggleSelectItem:anItem ca@2176: "toggle selection-state of the item; add or remove the item from the ca@2176: current selection. ca@2176: " ca@2176: anItem notNil ifTrue:[ ca@2176: (self includes:anItem) ifTrue:[self remove:anItem] ca@2176: ifFalse:[self add:anItem] ca@2176: ]. ca@2176: ^ anItem ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'selection enumerating'! ca@2176: ca@2176: collect:aBlock ca@2176: "for each element in the selection, evaluate the argument, aBlock ca@2176: and return a new collection with the results ca@2176: " ca@2176: |res| ca@2176: ca@2176: res := OrderedCollection new. ca@2176: self do:[:el| res add:(aBlock value:el)]. ca@2176: ^ res ca@2176: ! ca@2176: ca@2176: do:aOneArgBlock ca@2176: "evaluate the argument, aBlock for each item in the selection ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[^ nil]. ca@2176: ^ cashedSelection do:aOneArgBlock ca@2176: ! ca@2176: ca@2176: from:start do:aOneArgBlock ca@2176: "evaluate the argument, aBlock for the items starting at index start ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[^ nil]. ca@2176: ^ cashedSelection from:start do:aOneArgBlock ca@2176: ! ca@2176: ca@2176: from:start to:stop do:aOneArgBlock ca@2176: "evaluate the argument, aBlock for the items with index start to ca@2176: stop in the selection. ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[^ nil]. ca@2176: ^ cashedSelection from:start to:stop do:aOneArgBlock ca@2176: ! ca@2176: ca@2176: reverseDo:aOneArgBlock ca@2176: "evaluate the argument, aBlock for each item in the selection ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[^ nil]. ca@2176: ^ cashedSelection reverseDo:aOneArgBlock ca@2176: ! ca@2176: ca@2176: select:aBlock ca@2176: "return a new collection with all elements from the selection, for which ca@2176: the argument aBlock evaluates to true. ca@2176: " ca@2176: |res| ca@2176: ca@2176: res := OrderedCollection new. ca@2176: self do:[:el| (aBlock value:el) ifTrue:[res add:el] ]. ca@2176: ^ res ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'selection protocol'! ca@2176: ca@2176: changed:aParameter with:oldSelection ca@2176: "update the visibility staus of the current selection ca@2176: " ca@2176: |unselected rootView rootItem selSize| ca@2176: ca@2176: selSize := selection size. ca@2176: ca@2176: selSize == 0 ifTrue:[ ca@2176: selectedSuperItems := #(). ca@2176: ] ifFalse:[ ca@2176: selSize == 1 ifTrue:[ ca@2176: selectedSuperItems := Array with:(selection at:1). ca@2176: ] ifFalse:[ ca@2176: rootItem := listOfItems root. ca@2176: ca@2176: (selection includesIdentical:rootItem) ifTrue:[ ca@2176: selectedSuperItems := Array with:rootItem. ca@2176: ] ifFalse:[ ca@2176: selectedSuperItems := OrderedCollection new:selSize. ca@2176: ca@2176: selection do:[:anItem| ca@2176: anItem parentsDetect:[:el| selection includesIdentical:el ] ca@2176: ifNone:[ selectedSuperItems add:anItem ]. ca@2176: ]. ca@2176: ] ca@2176: ] ca@2176: ]. ca@2176: ca@2176: ( hiddenLevel == 0 ca@2176: and:[(rootView := listOfItems rootView) notNil ca@2176: and:[rootView shown]] ca@2176: ) ifTrue:[ ca@2176: selSize == 0 ifTrue:[ ca@2176: "/ must redraw the old selection unselected ca@2176: self redrawUnselected:oldSelection andLock:false ca@2176: ] ifFalse:[ ca@2176: self invalidateSelection. ca@2176: ca@2176: oldSelection size ~~ 0 ifTrue:[ ca@2176: "/ must redraw all elements no longer in the selection ca@2176: unselected := oldSelection select:[:el| (selection includesIdentical:el) not ]. ca@2176: self redrawUnselected:unselected andLock:false. ca@2176: ] ca@2176: ] ca@2176: ]. ca@2176: super changed:aParameter with:oldSelection. ca@2176: ! ca@2176: ca@2176: setValue:aNewSelection ca@2176: "set the selection without notifying ca@2176: " ca@2176: |newSelect idx| ca@2176: ca@2176: newSelect := nil. ca@2176: ca@2176: aNewSelection notNil ifTrue:[ ca@2176: lockSema critical:[ ca@2176: aNewSelection isCollection ifFalse:[ ca@2176: (selection size == 1 and:[selection first == aNewSelection]) ifTrue:[ ca@2176: newSelect := selection ca@2176: ] ifFalse:[ ca@2176: newSelect := Array with:aNewSelection. ca@2176: ] ca@2176: ] ifTrue:[ ca@2176: aNewSelection notEmpty ifTrue:[ ca@2176: aNewSelection size ~~ selection size ifTrue:[ ca@2176: newSelect := aNewSelection copy. ca@2176: ] ifFalse:[ ca@2176: idx := selection findFirst:[:el| (aNewSelection includesIdentical:el) not ]. ca@2176: ca@2176: idx ~~ 0 ifTrue:[newSelect := aNewSelection copy] ca@2176: ifFalse:[newSelect := selection ]. ca@2176: ] ca@2176: ] ca@2176: ] ca@2176: ]. ca@2176: ]. ca@2176: newSelect ~~ selection ifTrue:[ ca@2176: beforeSelectionChangedAction value. ca@2176: selection := newSelect. ca@2176: selection notNil ifTrue:[selection do:[:el| el makeVisible]] ca@2176: ]. ca@2176: ! ca@2176: ca@2176: triggerValue:aValue ca@2176: "set my value & send change notifications to my dependents. ca@2176: Send the change message even if the value didn't change. ca@2176: " ca@2176: |oldSelection| ca@2176: ca@2176: lockSema critical:[ ca@2176: oldSelection := selection. ca@2176: self setValue:aValue. ca@2176: self changed:#value with:oldSelection ca@2176: ] ca@2176: ! ca@2176: ca@2176: value ca@2176: "returns the current selection ca@2176: " ca@2176: ^ selection ? #() ca@2176: ! ca@2176: ca@2176: value:aValue ca@2176: "change the current selection and send change notifications to my ca@2176: dependents if it changed. ca@2176: " ca@2176: |oldSelection| ca@2176: ca@2176: lockSema critical:[ ca@2176: oldSelection := selection. ca@2176: self setValue:aValue. ca@2176: ca@2176: oldSelection == selection ifFalse:[ ca@2176: self changed:#value with:oldSelection ca@2176: ] ca@2176: ]. ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'selection searching'! ca@2176: ca@2176: detect:aBlock ca@2176: "evaluate the argument, aBlock for each item in the selection until ca@2176: the block returns true; in this case return the element which caused ca@2176: the true evaluation. ca@2176: If none of the evaluations returns true, an error is raised ca@2176: " ca@2176: ^ self detect:aBlock ifNone:[self errorNotFound] ca@2176: ! ca@2176: ca@2176: detect:aBlock ifNone:exceptionBlock ca@2176: "evaluate the argument, aBlock for each item in the selection until the ca@2176: block returns true; in this case return the element which caused the ca@2176: true evaluation. ca@2176: If none of the evaluations returns true, the result of the evaluation ca@2176: of the exceptionBlock is returned ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[ ^ exceptionBlock value ]. ca@2176: ^ cashedSelection detect:aBlock ifNone:exceptionBlock ca@2176: ! ca@2176: ca@2176: detectLast:aBlock ca@2176: "evaluate the argument, aBlock for each item in the selection until ca@2176: the block returns true; in this case return the element which caused ca@2176: the true evaluation. The items are processed in reverse order. ca@2176: If none of the evaluations returns true, an error is raised ca@2176: " ca@2176: ^ self detectLast:aBlock ifNone:[self errorNotFound] ca@2176: ! ca@2176: ca@2176: detectLast:aBlock ifNone:exceptionBlock ca@2176: "evaluate the argument, aBlock for each item in the selection until ca@2176: the block returns true; in this case return the element which caused ca@2176: the true evaluation. The items are processed in reverse order. ca@2176: If none of the evaluations returns true, the result of the evaluation ca@2176: of the exceptionBlock is returned ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[ ^ exceptionBlock value ]. ca@2176: ^ cashedSelection detectLast:aBlock ifNone:exceptionBlock ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel methodsFor:'selection testing'! ca@2176: ca@2176: includes:anItem ca@2176: "returns true if the item is in the current selection ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[^ false]. ca@2176: ^ cashedSelection includesIdentical:anItem ca@2176: ! ca@2176: ca@2176: includesAll:aCollection ca@2176: "return true, if all items of the collection are included in the current selection ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: aCollection size ~~ 0 ifTrue:[ ca@2176: cashedSelection := selection. ca@2176: cashedSelection isNil ifTrue:[ ^ false ]. ca@2176: ca@2176: aCollection do:[:el| ca@2176: (cashedSelection includesIdentical:el) ifFalse:[^ false] ca@2176: ] ca@2176: ]. ca@2176: ^ true ca@2176: ! ca@2176: ca@2176: includesAny:aCollection ca@2176: "return true, if the any item of the collection is in the current selection ca@2176: " ca@2176: |cashedSelection| ca@2176: ca@2176: aCollection notNil ifTrue:[ ca@2176: cashedSelection := selection. ca@2176: ca@2176: cashedSelection notNil ifTrue:[ ca@2176: aCollection do:[:el| ca@2176: (cashedSelection includesIdentical:el) ifTrue:[^ true] ca@2176: ] ca@2176: ] ca@2176: ]. ca@2176: ^ false ca@2176: ! ca@2176: ca@2176: includesIdentical:anItem ca@2176: "returns true if the item is in the current selection ca@2176: " ca@2176: ^ self includes:anItem ca@2176: ! ca@2176: ca@2176: isEmpty ca@2176: "returns true if the current selection is empty ca@2176: " ca@2176: ^ selection size == 0 ca@2176: ! ca@2176: ca@2176: isSelected:anItem ca@2176: "returns true if the item is in the current selection ca@2176: " ca@2176: ^ self includes:anItem ca@2176: ! ca@2176: ca@2176: notEmpty ca@2176: "returns true if the current selection is not empty ca@2176: " ca@2176: ^ selection size ~~ 0 ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel::ItemList class methodsFor:'documentation'! ca@2176: ca@2176: documentation ca@2176: " ca@2176: Kind of HierarchicalList class which contains all the visible ca@2176: ViewTreeItem's and the root, the anchor of the hierarchical list. ca@2176: ca@2176: [Instance variables:] ca@2176: treeModel all events are delegated to ca@2176: eventHook save and resore the pre/post -EventHook ca@2176: ca@2176: ca@2176: [author:] ca@2176: Claus Atzkern ca@2176: ca@2176: [see also:] ca@2176: HierarchicalList ca@2176: ViewTreeModel ca@2176: ViewTreeItem ca@2176: " ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel::ItemList methodsFor:'accessing'! ca@2176: ca@2176: root:theRoot ca@2176: "set the root item; delegate events to my treeModel ca@2176: " ca@2176: |rootView| ca@2176: ca@2176: theRoot == root ifTrue:[^ self]. ca@2176: ca@2176: rootView := self rootView. ca@2176: super root:theRoot. ca@2176: ca@2176: rootView notNil ifTrue:[ |wgrp| ca@2176: wgrp := rootView windowGroup. ca@2176: ca@2176: wgrp notNil ifTrue:[ ca@2176: wgrp removePreEventHook:treeModel. ca@2176: wgrp removePostEventHook:self. ca@2176: ]. ca@2176: ]. ca@2176: ca@2176: super root:theRoot. ca@2176: rootView := self rootView. ca@2176: ca@2176: rootView notNil ifTrue:[ ca@2176: "must setup a task because there might not exist a windowGroup at the moment ca@2176: " ca@2176: [ |wgrp| ca@2176: ca@2176: [rootView == self rootView] whileTrue:[ ca@2176: wgrp := rootView windowGroup. ca@2176: wgrp notNil ifTrue:[ ca@2176: rootView := nil. ca@2176: wgrp addPreEventHook:treeModel. ca@2176: wgrp addPostEventHook:self. ca@2176: ] ifFalse:[ ca@2176: Delay waitForMilliseconds:100. ca@2176: ]. ca@2176: ]. ca@2176: ca@2176: ] forkAt:(Processor userSchedulingPriority + 2). ca@2176: ]. ca@2457: treeModel notNil ifTrue:[ ca@2457: treeModel targetWidgetChanged. ca@2457: ]. ca@2457: ca@2176: ^ root. ca@2176: ! ca@2176: ca@2176: rootView ca@2176: "returns the widget assigned to the root or nil ca@2176: " ca@2176: ^ root notNil ifTrue:[root widget] ifFalse:[nil] ca@2176: ! ca@2176: ca@2176: treeModel ca@2176: "returne the treeModel, a ViewTreeModel ca@2176: " ca@2176: ^ treeModel ca@2176: ! ! ca@2176: ca@2453: !ViewTreeModel::ItemList methodsFor:'accessing look'! ca@2453: ca@2453: additionalLabelForItem:anItem ca@2453: "answer the additional lable for an item or nil" ca@2453: ca@2453: |applClass| ca@2453: ca@2453: showWidgetNames == true ifTrue:[ ca@2453: ^ '[', anItem widget name, ']' ca@2453: ]. ca@2453: ca@2453: anItem isApplicationClass ifTrue:[ ca@2453: applClass := anItem applicationClass. ca@2453: ca@2453: applClass notNil ifTrue:[ ca@2453: ^ ('[', applClass name, ']') ca@2453: ]. ca@2453: ]. ca@2453: ^ nil ca@2453: ! ca@2453: ca@2453: showWidgetNames ca@2453: "answer true if the additional text is the widget name ca@2453: otherwise the name of the application" ca@2453: ca@2453: ^ showWidgetNames ? false ca@2453: ! ca@2453: ca@2453: showWidgetNames:aBoolean ca@2453: "set true if the additional text is the widget name ca@2453: otherwise the name of the application" ca@2453: ca@2453: self showWidgetNames == aBoolean ifFalse:[ ca@2453: showWidgetNames := aBoolean. ca@2453: ca@2453: root notNil ifTrue:[ ca@2453: root recursiveAdditionalNameBehaviourChanged. ca@2453: self changed. ca@2453: ]. ca@2453: ]. ca@2453: ! ! ca@2453: ca@2176: !ViewTreeModel::ItemList methodsFor:'event processing'! ca@2176: ca@2176: processEvent:anEvent ca@2176: "post process event ca@2176: " ca@2457: ^ treeModel isInTestMode not ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel::ItemList methodsFor:'instance creation'! ca@2176: ca@2176: on:aModel ca@2176: "set the model, a ViewTreeModel ca@2176: " ca@2176: treeModel := aModel. ca@2176: showRoot := true. ca@2453: showWidgetNames := false. ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel::ItemList methodsFor:'searching'! ca@2176: ca@2176: detectItemRespondsToView:aView ca@2176: "returns the bottom-most item which contains the view ca@2176: " ca@2176: |view item topView| ca@2176: ca@2176: root notNil ifTrue:[ ca@2176: view := aView. ca@2176: topView := root widget. ca@2176: ca@2176: [ view notNil ] whileTrue:[ ca@2176: topView == view ifTrue:[^ root]. ca@2176: item := root recursiveDetect:[:el| el widget == view ]. ca@2176: item notNil ifTrue:[^ item]. ca@2176: view := view superView ca@2176: ] ca@2176: ]. ca@2176: ^ nil ca@2176: ! ca@2176: ca@2176: recursiveDetect:aOneOrgBlock ca@2176: "recursive find the first child, for which evaluation ca@2176: of the block returns true; if none nil is returned ca@2176: " ca@2176: root notNil ifTrue:[ ca@2176: (aOneOrgBlock value:root) ifTrue:[ ^ root ]. ca@2176: ^ root recursiveDetect:aOneOrgBlock ca@2176: ]. ca@2176: ^ nil ca@2176: ! ! ca@2176: ca@2176: !ViewTreeModel class methodsFor:'documentation'! ca@2176: ca@2176: version ca@2176: ^ '$Header$' ca@2176: ! !