|
1 " |
|
2 COPYRIGHT (c) 1989-92 by Claus Gittinger |
|
3 All Rights Reserved |
|
4 |
|
5 This software is furnished under a license and may be used |
|
6 only in accordance with the terms of that license and with the |
|
7 inclusion of the above copyright notice. This software may not |
|
8 be provided or otherwise made available to, or used by, any |
|
9 other person. No title to or ownership of the software is |
|
10 hereby transferred. |
|
11 " |
|
12 |
|
13 View subclass:#ObjectView |
|
14 instanceVariableNames:'contents |
|
15 sorted |
|
16 lastButt lastPointer lastButtonTime |
|
17 pressAction releaseAction |
|
18 shiftPressAction doublePressAction |
|
19 motionAction keyPressAction |
|
20 selection |
|
21 gridShown gridPixmap |
|
22 scaleShown scaleMetric |
|
23 groupRectangleFrame |
|
24 leftHandCursor readCursor oldCursor |
|
25 movedObject moveStartPoint |
|
26 moveDelta |
|
27 buffer |
|
28 documentFormat |
|
29 leftMarginForScale topMarginForScale |
|
30 canDragOutOfView rootMotion rootView aligning' |
|
31 classVariableNames:'' |
|
32 poolDictionaries:'' |
|
33 category:'Views-Basic' |
|
34 ! |
|
35 |
|
36 ObjectView comment:' |
|
37 |
|
38 COPYRIGHT (c) 1989-92 by Claus Gittinger |
|
39 All Rights Reserved |
|
40 |
|
41 a View which can hold DisplayObjects, can make selections, move them around etc. |
|
42 this is an abstract class providing common mechanisms - actual instances are |
|
43 DrawView, DirectoryView, LogicView or DocumentView. |
|
44 |
|
45 %W% %E% |
|
46 written spring/summer 89 by claus |
|
47 '! |
|
48 |
|
49 !ObjectView class methodsFor:'defaults'! |
|
50 |
|
51 hitDelta |
|
52 "when clicking an object, allow for hitDelta pixels around object; |
|
53 0 is exact; 1*pixelPerMillimeter is good for draw programs" |
|
54 ^ 0 |
|
55 ! ! |
|
56 |
|
57 !ObjectView methodsFor:'initialization'! |
|
58 |
|
59 initialize |
|
60 |pixPerMM| |
|
61 |
|
62 super initialize. |
|
63 |
|
64 viewBackground := White. |
|
65 |
|
66 bitGravity := #NorthWest. |
|
67 contents := OrderedCollection new. |
|
68 gridShown := false. |
|
69 scaleShown := false. |
|
70 canDragOutOfView := false. |
|
71 rootView := DisplayRootView new. |
|
72 rootView noClipByChildren. |
|
73 rootMotion := false. |
|
74 (Language == #english) ifTrue:[ |
|
75 documentFormat := 'letter'. |
|
76 scaleMetric := #inch |
|
77 ] ifFalse:[ |
|
78 documentFormat := 'a4'. |
|
79 scaleMetric := #mm |
|
80 ]. |
|
81 pixPerMM := self verticalPixelPerMillimeter:1. |
|
82 topMarginForScale := ((pixPerMM * 2.0) + 0.5) asInteger. |
|
83 pixPerMM := self horizontalPixelPerMillimeter:1. |
|
84 leftMarginForScale := ((pixPerMM * 2.0) + 0.5) asInteger. |
|
85 readCursor := Cursor read. |
|
86 leftHandCursor := Cursor leftHand. |
|
87 sorted := false. |
|
88 aligning := false |
|
89 ! |
|
90 |
|
91 initEvents |
|
92 self backingStore:true. |
|
93 self enableButtonEvents. |
|
94 self enableButtonMotionEvents |
|
95 ! ! |
|
96 |
|
97 !ObjectView methodsFor:'queries'! |
|
98 |
|
99 heightOfContentsInMM |
|
100 "answer the height of the document in millimeters" |
|
101 |
|
102 (documentFormat = 'a3') ifTrue:[ |
|
103 ^ 420 |
|
104 ]. |
|
105 (documentFormat = 'a4') ifTrue:[ |
|
106 ^ 296 |
|
107 ]. |
|
108 (documentFormat = 'a5') ifTrue:[ |
|
109 ^ 210 |
|
110 ]. |
|
111 (documentFormat = 'letter') ifTrue:[ |
|
112 ^ 11 * 25.4 |
|
113 ]. |
|
114 "assuming window size is document size" |
|
115 ^ (height / self verticalPixelPerMillimeter:1) asInteger |
|
116 ! |
|
117 |
|
118 widthOfContentsInMM |
|
119 "answer the width of the document in millimeters" |
|
120 |
|
121 (documentFormat = 'a3') ifTrue:[ |
|
122 ^ 296 |
|
123 ]. |
|
124 (documentFormat = 'a4') ifTrue:[ |
|
125 ^ 210 |
|
126 ]. |
|
127 (documentFormat = 'a5') ifTrue:[ |
|
128 ^ 148 |
|
129 ]. |
|
130 (documentFormat = 'letter') ifTrue:[ |
|
131 ^ 8.5 * 25.4 |
|
132 ]. |
|
133 "assuming window size is document size" |
|
134 ^ (width / self horizontalPixelPerMillimeter:1) asInteger |
|
135 ! |
|
136 |
|
137 heightOfContents |
|
138 "answer the height of the document in pixels" |
|
139 |
|
140 ^ ((self heightOfContentsInMM |
|
141 * (self verticalPixelPerMillimeter:1)) + 0.5) asInteger |
|
142 ! |
|
143 |
|
144 widthOfContents |
|
145 "answer the width of the document in pixels" |
|
146 |
|
147 ^ ((self widthOfContentsInMM |
|
148 * (self horizontalPixelPerMillimeter:1)) + 0.5) asInteger |
|
149 ! ! |
|
150 |
|
151 !ObjectView methodsFor:'drawing'! |
|
152 |
|
153 redraw |
|
154 "redraw complete View" |
|
155 |
|
156 realized ifTrue:[ |
|
157 gridShown ifTrue:[ |
|
158 self redrawGrid |
|
159 ] ifFalse:[ |
|
160 self fill:viewBackground |
|
161 ]. |
|
162 scaleShown ifTrue:[ |
|
163 self redrawScale |
|
164 ]. |
|
165 self redrawObjects |
|
166 ] |
|
167 ! |
|
168 |
|
169 redrawGrid |
|
170 "redraw the grid" |
|
171 |
|
172 gridPixmap notNil ifTrue:[ |
|
173 self drawOpaqueForm:gridPixmap x:0 y:0 |
|
174 ] |
|
175 ! |
|
176 |
|
177 redrawHorizontalScale |
|
178 "redraw the horizontal scale" |
|
179 |
|
180 |x mmH short step xRounded shortLen longLen len| |
|
181 |
|
182 self clearRectangle:((0 @ 0) corner:(width @ topMarginForScale)). |
|
183 scaleShown ifFalse:[^ self]. |
|
184 (scaleMetric == #mm) ifTrue:[ |
|
185 "long blibs every centimeter; short ones every half" |
|
186 |
|
187 mmH := self horizontalPixelPerMillimeter. |
|
188 step := mmH * 5.0. |
|
189 x := step. |
|
190 short := true. |
|
191 shortLen := (topMarginForScale / 2) asInteger. |
|
192 longLen := topMarginForScale. |
|
193 [x < width] whileTrue:[ |
|
194 xRounded := (x + 0.5) asInteger. |
|
195 short ifTrue:[ |
|
196 len := shortLen |
|
197 ] ifFalse:[ |
|
198 len := longLen |
|
199 ]. |
|
200 self displayLineFromX:xRounded y:0 toX:xRounded y:len. |
|
201 short := short not. |
|
202 x := x + step |
|
203 ] |
|
204 ] |
|
205 ! |
|
206 |
|
207 redrawVerticalScale |
|
208 "redraw the vertical scale" |
|
209 |
|
210 |y mmV short step yRounded shortLen longLen len| |
|
211 |
|
212 self clearRectangle:((0 @ 0) corner:(leftMarginForScale @ height)). |
|
213 scaleShown ifFalse:[^ self]. |
|
214 (scaleMetric == #mm) ifTrue:[ |
|
215 "long blibs every centimeter; short ones every half" |
|
216 |
|
217 mmV := self verticalPixelPerMillimeter. |
|
218 step := mmV * 5.0. |
|
219 y := step. |
|
220 short := true. |
|
221 shortLen := (leftMarginForScale / 2) asInteger. |
|
222 longLen := leftMarginForScale. |
|
223 [y < height] whileTrue:[ |
|
224 yRounded := (y + 0.5) asInteger. |
|
225 short ifTrue:[ |
|
226 len := shortLen |
|
227 ] ifFalse:[ |
|
228 len := longLen |
|
229 ]. |
|
230 self displayLineFromX:0 y:yRounded toX:len y:yRounded. |
|
231 short := short not. |
|
232 y := y + step |
|
233 ] |
|
234 ] |
|
235 ! |
|
236 |
|
237 redrawScale |
|
238 "redraw the scales" |
|
239 |
|
240 self redrawHorizontalScale. |
|
241 self redrawVerticalScale |
|
242 ! |
|
243 |
|
244 redrawObjectsOn:aGC |
|
245 "redraw all objects on a graphic context" |
|
246 |
|
247 |vFrame org| |
|
248 |
|
249 (aGC == self) ifTrue:[ |
|
250 realized ifFalse:[^ self]. |
|
251 org := viewOrigin + (leftMarginForScale @ topMarginForScale). |
|
252 vFrame := Rectangle origin:org |
|
253 corner:(viewOrigin + (width @ height)). |
|
254 |
|
255 self redrawObjectsIntersecting:vFrame |
|
256 ] ifFalse:[ |
|
257 "loop over pages" |
|
258 |
|
259 org := 0 @ 0. |
|
260 vFrame := Rectangle origin:org |
|
261 corner:(org + (width @ height)). |
|
262 |
|
263 self redrawObjectsIntersecting:vFrame |
|
264 ] |
|
265 ! |
|
266 |
|
267 redrawObjects |
|
268 "redraw all objects" |
|
269 |
|
270 self redrawObjectsOn:self |
|
271 ! |
|
272 |
|
273 redrawObjectsIntersecting:aRectangle |
|
274 "redraw all objects which have part of themself in aRectangle" |
|
275 |
|
276 self objectsIntersecting:aRectangle do:[:theObject | |
|
277 self show:theObject |
|
278 ] |
|
279 ! |
|
280 |
|
281 redrawObjectsIntersectingVisible:aRectangle |
|
282 "redraw all objects which have part of themself in a vis rectangle" |
|
283 |
|
284 self objectsIntersectingVisible:aRectangle do:[:theObject | |
|
285 self show:theObject |
|
286 ] |
|
287 |
|
288 ! |
|
289 |
|
290 redrawObjectsAbove:anObject intersecting:aRectangle |
|
291 "redraw all objects which have part of themself in aRectangle |
|
292 and are above (in front of) anObject" |
|
293 |
|
294 self objectsAbove:anObject intersecting:aRectangle do:[:theObject | |
|
295 self show:theObject |
|
296 ] |
|
297 ! |
|
298 |
|
299 redrawObjectsAbove:anObject intersectingVisible:aRectangle |
|
300 "redraw all objects which have part of themself in a vis rectangle |
|
301 and are above (in front of) anObject" |
|
302 |
|
303 self objectsAbove:anObject intersectingVisible:aRectangle do:[:theObject | |
|
304 self show:theObject |
|
305 ] |
|
306 ! |
|
307 |
|
308 redrawObjectsIn:aRectangle |
|
309 "redraw all objects which have part of themselfes in aRectangle |
|
310 draw only in (i.e. clip output to) aRectangle" |
|
311 |
|
312 |visRect| |
|
313 |
|
314 realized ifTrue:[ |
|
315 visRect := Rectangle origin:(aRectangle origin - viewOrigin) |
|
316 extent:(aRectangle extent). |
|
317 self clippedTo:visRect do:[ |
|
318 gridShown ifTrue:[ |
|
319 self redrawGrid |
|
320 ] ifFalse:[ |
|
321 self paint:viewBackground. |
|
322 self fillRectangle:visRect |
|
323 ]. |
|
324 self redrawObjectsIntersecting:aRectangle |
|
325 ] |
|
326 ] |
|
327 ! |
|
328 |
|
329 redrawObjectsInVisible:visRect |
|
330 "redraw all objects which have part of themselfes in a vis rectangle |
|
331 draw only in (i.e. clip output to) aRectangle" |
|
332 |
|
333 realized ifTrue:[ |
|
334 self clippedTo:visRect do:[ |
|
335 gridShown ifTrue:[ |
|
336 self redrawGrid |
|
337 ] ifFalse:[ |
|
338 self paint:viewBackground. |
|
339 self fillRectangle:visRect |
|
340 ]. |
|
341 self redrawObjectsIntersectingVisible:visRect |
|
342 ] |
|
343 ] |
|
344 ! |
|
345 |
|
346 redrawObjectsAbove:anObject in:aRectangle |
|
347 "redraw all objects which have part of themselfes in aRectangle |
|
348 and are above (in front of) anObject. |
|
349 draw only in (i.e. clip output to) aRectangle" |
|
350 |
|
351 realized ifTrue:[ |
|
352 self clippedTo:aRectangle do:[ |
|
353 self redrawObjectsAbove:anObject intersecting:aRectangle |
|
354 ] |
|
355 ] |
|
356 ! |
|
357 |
|
358 redrawObjectsAbove:anObject inVisible:aRectangle |
|
359 "redraw all objects which have part of themselfes in a vis rectangle |
|
360 and are above (in front of) anObject. |
|
361 draw only in (i.e. clip output to) aRectangle" |
|
362 |
|
363 realized ifTrue:[ |
|
364 self clippedTo:aRectangle do:[ |
|
365 self redrawObjectsAbove:anObject intersectingVisible:aRectangle |
|
366 ] |
|
367 ] |
|
368 ! |
|
369 |
|
370 show:anObject |
|
371 "show the object, either selected or not" |
|
372 |
|
373 (self isSelected:anObject) ifTrue:[ |
|
374 self showSelected:anObject |
|
375 ] ifFalse:[ |
|
376 self showUnselected:anObject |
|
377 ] |
|
378 ! |
|
379 |
|
380 showDragging:something offset:anOffset |
|
381 "show an object while dragging" |
|
382 |
|
383 |drawOffset top drawer| |
|
384 |
|
385 canDragOutOfView ifTrue:[ |
|
386 "drag in root-window" |
|
387 |
|
388 top := self topView. |
|
389 drawOffset := device translatePoint:anOffset |
|
390 from:(self id) to:(rootView id). |
|
391 drawer := rootView |
|
392 ] ifFalse:[ |
|
393 drawOffset := anOffset. |
|
394 drawer := self |
|
395 ]. |
|
396 self forEach:something do:[:anObject | |
|
397 anObject drawDragIn:drawer offset:drawOffset |
|
398 ] |
|
399 ! |
|
400 |
|
401 showSelected:anObject |
|
402 "show an object as selected" |
|
403 |
|
404 shown ifTrue:[anObject drawSelectedIn:self] |
|
405 ! |
|
406 |
|
407 showUnselected:anObject |
|
408 "show an object as unselected" |
|
409 |
|
410 shown ifTrue:[anObject drawIn:self] |
|
411 ! ! |
|
412 |
|
413 !ObjectView methodsFor:'selections'! |
|
414 |
|
415 selectionDo:aBlock |
|
416 "apply block to every object in selection" |
|
417 |
|
418 self forEach:selection do:aBlock |
|
419 ! |
|
420 |
|
421 showSelection |
|
422 "show the selection - draw hilights - whatever that is" |
|
423 |
|
424 self selectionDo:[:object | |
|
425 self showSelected:object |
|
426 ] |
|
427 ! |
|
428 |
|
429 hideSelection |
|
430 "hide the selection - undraw hilights - whatever that is" |
|
431 |
|
432 self selectionDo:[:object | |
|
433 self showUnselected:object |
|
434 ] |
|
435 ! |
|
436 |
|
437 unselect |
|
438 "unselect - hide selection; clear selection buffer" |
|
439 |
|
440 self hideSelection. |
|
441 selection := nil |
|
442 ! |
|
443 |
|
444 select:something |
|
445 "select something - hide previouse selection, set to something and hilight" |
|
446 |
|
447 (selection == something) ifFalse:[ |
|
448 self hideSelection. |
|
449 selection := something. |
|
450 self showSelection |
|
451 ] |
|
452 ! |
|
453 |
|
454 selectAll |
|
455 "select all objects" |
|
456 |
|
457 self hideSelection. |
|
458 selection := contents. |
|
459 self showSelection |
|
460 ! |
|
461 |
|
462 addToSelection:anObject |
|
463 "add anObject to the selection" |
|
464 |
|
465 (selection isKindOf:Collection) ifFalse:[ |
|
466 selection := OrderedCollection with:selection |
|
467 ]. |
|
468 selection add:anObject. |
|
469 self showSelected:anObject |
|
470 ! |
|
471 |
|
472 removeFromSelection:anObject |
|
473 "remove anObject from the selection" |
|
474 |
|
475 (selection isKindOf:Collection) ifTrue:[ |
|
476 selection remove:anObject ifAbsent:[nil]. |
|
477 (selection size == 1) ifTrue:[ |
|
478 selection := selection first |
|
479 ] |
|
480 ] ifFalse:[ |
|
481 (selection == anObject) ifTrue:[ |
|
482 selection := nil |
|
483 ] |
|
484 ]. |
|
485 self showUnselected:anObject |
|
486 ! |
|
487 |
|
488 selectAllIntersecting:aRectangle |
|
489 "select all objects touched by aRectangle" |
|
490 |
|
491 self hideSelection. |
|
492 selection := OrderedCollection new. |
|
493 |
|
494 self objectsIntersecting:aRectangle do:[:theObject | |
|
495 selection add:theObject |
|
496 ]. |
|
497 (selection size == 0) ifTrue:[ |
|
498 selection := nil |
|
499 ] ifFalse:[ |
|
500 (selection size == 1) ifTrue:[selection := selection first] |
|
501 ]. |
|
502 self showSelection |
|
503 ! |
|
504 |
|
505 selectAllIn:aRectangle |
|
506 "select all objects fully in aRectangle" |
|
507 |
|
508 self hideSelection. |
|
509 selection := OrderedCollection new. |
|
510 self objectsIn:aRectangle do:[:theObject | |
|
511 selection add:theObject |
|
512 ]. |
|
513 (selection size == 0) ifTrue:[ |
|
514 selection := nil |
|
515 ] ifFalse:[ |
|
516 (selection size == 1) ifTrue:[selection := selection first] |
|
517 ]. |
|
518 self showSelection |
|
519 ! |
|
520 |
|
521 withSelectionHiddenDo:aBlock |
|
522 "evaluate aBlock while selection is hidden" |
|
523 |
|
524 |sel| |
|
525 |
|
526 sel := selection. |
|
527 self unselect. |
|
528 aBlock value. |
|
529 self select:sel |
|
530 ! ! |
|
531 |
|
532 !ObjectView methodsFor:'testing objects'! |
|
533 |
|
534 findObjectAt:aPoint |
|
535 "find the last object (by looking from back to front) which is hit by |
|
536 the argument, aPoint - this is the topmost object hit" |
|
537 |
|
538 |hdelta| |
|
539 |
|
540 hdelta := self class hitDelta. |
|
541 contents reverseDo:[:object | |
|
542 (object isHitBy:aPoint withDelta:hdelta) ifTrue:[^ object] |
|
543 ]. |
|
544 ^ nil |
|
545 ! |
|
546 |
|
547 findObjectAtVisible:aPoint |
|
548 "find the last object (by looking from back to front) which is hit by |
|
549 a visible point - this is the topmost object hit" |
|
550 |
|
551 ^ self findObjectAt:(aPoint + viewOrigin) |
|
552 ! |
|
553 |
|
554 findObjectAt:aPoint suchThat:aBlock |
|
555 "find the last object (back to front ) which is hit by |
|
556 the argument, aPoint and for which the testBlock, aBlock evaluates to |
|
557 true" |
|
558 |
|
559 |hdelta| |
|
560 |
|
561 hdelta := self class hitDelta. |
|
562 contents reverseDo:[:object | |
|
563 (object isHitBy:aPoint withDelta:hdelta) ifTrue:[ |
|
564 (aBlock value:object) ifTrue:[^ object] |
|
565 ] |
|
566 ]. |
|
567 ^ nil |
|
568 ! |
|
569 |
|
570 findObjectAtVisible:aPoint suchThat:aBlock |
|
571 "find the last object (back to front ) which is hit by |
|
572 the argument, aPoint and for which the testBlock, aBlock evaluates to |
|
573 true" |
|
574 |
|
575 ^ self findObjectAt:(aPoint + viewOrigin) suchThat:aBlock |
|
576 ! |
|
577 |
|
578 frameOf:anObjectOrCollection |
|
579 "answer the maximum extent defined by the argument, anObject or a |
|
580 collection of objects" |
|
581 |
|
582 |first frameAll| |
|
583 |
|
584 anObjectOrCollection isNil ifTrue:[^ nil ]. |
|
585 first := true. |
|
586 self forEach:anObjectOrCollection do:[:theObject | |
|
587 first ifTrue:[ |
|
588 frameAll := theObject frame. |
|
589 first := false |
|
590 ] ifFalse:[ |
|
591 frameAll := frameAll merge:(theObject frame) |
|
592 ] |
|
593 ]. |
|
594 ^ frameAll |
|
595 ! |
|
596 |
|
597 canMove:something |
|
598 "return true, if the argument, anObject or a collection can be moved" |
|
599 |
|
600 (something isKindOf:Collection) ifTrue:[ |
|
601 self forEach:something do:[:theObject | |
|
602 (theObject canBeMoved) ifFalse:[^ false] |
|
603 ]. |
|
604 ^ true |
|
605 ]. |
|
606 ^ something canBeMoved |
|
607 ! |
|
608 |
|
609 isSelected:anObject |
|
610 "return true, if the argument, anObject is in the selection" |
|
611 |
|
612 selection isNil ifTrue:[^ false]. |
|
613 (selection == anObject) ifTrue:[^ true]. |
|
614 (selection isKindOf:Collection) ifTrue:[ |
|
615 ^ (selection identityIndexOf:anObject startingAt:1) ~~ 0 |
|
616 ]. |
|
617 ^ false |
|
618 ! |
|
619 |
|
620 objectIsObscured:objectToBeTested |
|
621 "return true, if the argument, anObject is obscured (partially or whole) |
|
622 by any other object" |
|
623 |
|
624 |frameToBeTested frameleft frameright frametop framebot |
|
625 objectsFrame startIndex| |
|
626 |
|
627 (objectToBeTested == (contents last)) ifTrue:[ |
|
628 "quick return if object is on top" |
|
629 ^ false |
|
630 ]. |
|
631 |
|
632 frameToBeTested := self frameOf:objectToBeTested. |
|
633 frameleft := frameToBeTested left. |
|
634 frameright := frameToBeTested right. |
|
635 frametop := frameToBeTested top. |
|
636 framebot := frameToBeTested bottom. |
|
637 |
|
638 "check objects after the one to check" |
|
639 |
|
640 startIndex := contents identityIndexOf:objectToBeTested ifAbsent:[self error]. |
|
641 contents from:(startIndex + 1) to:(contents size) do:[:object | |
|
642 objectsFrame := self frameOf:object. |
|
643 (objectsFrame right < frameleft) ifFalse:[ |
|
644 (objectsFrame left > frameright) ifFalse:[ |
|
645 (objectsFrame bottom < frametop) ifFalse:[ |
|
646 (objectsFrame top > framebot) ifFalse:[ |
|
647 ^ true |
|
648 ] |
|
649 ] |
|
650 ] |
|
651 ] |
|
652 ]. |
|
653 ^ false |
|
654 ! |
|
655 |
|
656 isObscured:something |
|
657 "return true, if the argument something, anObject or a collection of |
|
658 objects is obscured (partially or whole) by any other object" |
|
659 |
|
660 self forEach:something do:[:anObject | |
|
661 (self objectIsObscured:anObject) ifTrue:[ |
|
662 ^ true |
|
663 ] |
|
664 ]. |
|
665 ^ false |
|
666 ! ! |
|
667 |
|
668 !ObjectView methodsFor:'layout manipulation'! |
|
669 |
|
670 move:something to:aPoint in:aView |
|
671 "can only happen when dragOutOfView is true |
|
672 - should be redefined in subclasses" |
|
673 |
|
674 self notify:'cannot move object(s) out of view' |
|
675 ! |
|
676 |
|
677 move:something to:aPoint inAlienViewId:aViewId |
|
678 "can only happen when dragOutOfView is true |
|
679 - should be redefined in subclasses" |
|
680 |
|
681 self notify:'cannot move object(s) to alien views' |
|
682 ! |
|
683 |
|
684 move:something by:delta |
|
685 "change the position of something, an Object or Collection |
|
686 by delta, aPoint" |
|
687 |
|
688 (delta x == 0) ifTrue:[ |
|
689 (delta y == 0) ifTrue:[^ self] |
|
690 ]. |
|
691 |
|
692 self forEach:something do:[:anObject | |
|
693 self moveObject:anObject by:delta |
|
694 ] |
|
695 ! |
|
696 |
|
697 moveObject:anObject by:delta |
|
698 "change the position of anObject by delta, aPoint" |
|
699 |
|
700 self moveObject:anObject to:(anObject origin + delta) |
|
701 ! |
|
702 |
|
703 moveObject:anObject to:newOrigin |
|
704 "move anObject to newOrigin, aPoint" |
|
705 |
|
706 |oldOrigin oldFrame newFrame |
|
707 objectsIntersectingOldFrame objectsIntersectingNewFrame |
|
708 wasObscured isObscured intersects |
|
709 vx vy oldLeft oldTop w h newLeft newTop| |
|
710 |
|
711 anObject isNil ifTrue:[^ self]. |
|
712 anObject canBeMoved ifFalse:[^ self]. |
|
713 |
|
714 oldOrigin := anObject origin. |
|
715 (oldOrigin = newOrigin) ifTrue:[^ self]. |
|
716 |
|
717 oldFrame := self frameOf:anObject. |
|
718 objectsIntersectingOldFrame := self objectsIntersecting:oldFrame. |
|
719 wasObscured := self isObscured:anObject. |
|
720 |
|
721 anObject moveTo:newOrigin. |
|
722 |
|
723 newFrame := self frameOf:anObject. |
|
724 objectsIntersectingNewFrame := self objectsIntersecting:newFrame. |
|
725 |
|
726 "try to redraw the minimum possible" |
|
727 |
|
728 "if no other object intersects both frames we can do a copy:" |
|
729 |
|
730 intersects := oldFrame intersects:newFrame. |
|
731 intersects ifFalse:[ |
|
732 gridShown ifFalse:[ |
|
733 (objectsIntersectingOldFrame size == 1) ifTrue:[ |
|
734 (objectsIntersectingNewFrame size == 1) ifTrue:[ |
|
735 vx := viewOrigin x. |
|
736 vy := viewOrigin y. |
|
737 oldLeft := oldFrame left - vx. |
|
738 oldTop := oldFrame top - vy. |
|
739 newLeft := newFrame left - vx. |
|
740 newTop := newFrame top - vy. |
|
741 w := oldFrame width. |
|
742 h := oldFrame height. |
|
743 ((newLeft < width) and:[newTop < height]) ifTrue:[ |
|
744 ((newLeft >= 0) and:[newTop >= 0]) ifTrue:[ |
|
745 self copyFrom:self x:oldLeft y:oldTop |
|
746 toX:newLeft y:newTop |
|
747 width:w height:h. |
|
748 self waitForExpose |
|
749 ] |
|
750 ]. |
|
751 ((oldLeft < width) and:[oldTop < height]) ifTrue:[ |
|
752 ((oldLeft >= 0) and:[oldTop >= 0]) ifTrue:[ |
|
753 self fillRectangleX:oldLeft y:oldTop width:w height:h |
|
754 with:viewBackground |
|
755 ] |
|
756 ]. |
|
757 ^ self |
|
758 ] |
|
759 ] |
|
760 ] |
|
761 ]. |
|
762 isObscured := self isObscured:anObject. |
|
763 (oldFrame intersects:newFrame) ifTrue:[ |
|
764 isObscured ifFalse:[ |
|
765 self redrawObjectsIn:oldFrame. |
|
766 self show: anObject |
|
767 ] ifTrue:[ |
|
768 self redrawObjectsIn:(oldFrame merge:newFrame) |
|
769 ] |
|
770 ] ifFalse:[ |
|
771 self redrawObjectsIn:oldFrame. |
|
772 isObscured ifFalse:[ |
|
773 self show: anObject |
|
774 ] ifTrue:[ |
|
775 self redrawObjectsIn:newFrame |
|
776 ] |
|
777 ] |
|
778 ! |
|
779 |
|
780 objectToFront:anObject |
|
781 "bring the argument, anObject to front" |
|
782 |
|
783 |wasObscured| |
|
784 |
|
785 anObject notNil ifTrue:[ |
|
786 wasObscured := self isObscured:anObject. |
|
787 contents remove:anObject. |
|
788 contents addLast:anObject. |
|
789 wasObscured ifTrue:[ |
|
790 self redrawObjectsIn:(anObject frame) |
|
791 ] |
|
792 ] |
|
793 ! |
|
794 |
|
795 toFront:something |
|
796 "bring the argument, anObject or a collection of objects to front" |
|
797 |
|
798 self forEach:something do:[:anObject | |
|
799 self objectToFront:anObject |
|
800 ] |
|
801 ! |
|
802 |
|
803 selectionToFront |
|
804 "bring the selection to front" |
|
805 |
|
806 self toFront:selection |
|
807 ! |
|
808 |
|
809 objectToBack:anObject |
|
810 "bring the argument, anObject to back" |
|
811 |
|
812 anObject notNil ifTrue:[ |
|
813 contents remove:anObject. |
|
814 contents addFirst:anObject. |
|
815 (self isObscured:anObject) ifTrue:[ |
|
816 self redrawObjectsIn:(anObject frame) |
|
817 ] |
|
818 ] |
|
819 ! |
|
820 |
|
821 toBack:something |
|
822 "bring the argument, anObject or a collection of objects to back" |
|
823 |
|
824 self forEach:something do:[:anObject | |
|
825 self objectToBack:anObject |
|
826 ] |
|
827 ! |
|
828 |
|
829 selectionToBack |
|
830 "bring the selection to back" |
|
831 |
|
832 self toBack:selection |
|
833 ! |
|
834 |
|
835 alignLeft:something |
|
836 |leftMost| |
|
837 |
|
838 leftMost := 999999. |
|
839 self forEach:something do:[:anObject | |
|
840 leftMost := leftMost min:(anObject frame left) |
|
841 ]. |
|
842 self withSelectionHiddenDo:[ |
|
843 self forEach:something do:[:anObject | |
|
844 self moveObject:anObject to:(leftMost @ (anObject frame top)) |
|
845 ] |
|
846 ] |
|
847 ! |
|
848 |
|
849 alignRight:something |
|
850 |rightMost| |
|
851 |
|
852 rightMost := -999999. |
|
853 self forEach:something do:[:anObject | |
|
854 rightMost := rightMost max:(anObject frame right) |
|
855 ]. |
|
856 self withSelectionHiddenDo:[ |
|
857 self forEach:something do:[:anObject | |
|
858 self moveObject:anObject to:(rightMost - (anObject frame width)) |
|
859 @ (anObject frame top) |
|
860 ] |
|
861 ] |
|
862 ! |
|
863 |
|
864 alignTop:something |
|
865 |topMost| |
|
866 |
|
867 topMost := 999999. |
|
868 self forEach:something do:[:anObject | |
|
869 topMost := topMost min:(anObject frame top) |
|
870 ]. |
|
871 self withSelectionHiddenDo:[ |
|
872 self forEach:something do:[:anObject | |
|
873 self moveObject:anObject to:((anObject frame left) @ topMost) |
|
874 ] |
|
875 ] |
|
876 ! |
|
877 |
|
878 alignBottom:something |
|
879 |botMost| |
|
880 |
|
881 botMost := -999999. |
|
882 self forEach:something do:[:anObject | |
|
883 botMost := botMost max:(anObject frame bottom) |
|
884 ]. |
|
885 self withSelectionHiddenDo:[ |
|
886 self forEach:something do:[:anObject | |
|
887 self moveObject:anObject to:(anObject frame left) |
|
888 @ |
|
889 (botMost - (anObject frame height)) |
|
890 ] |
|
891 ] |
|
892 ! |
|
893 |
|
894 selectionAlignLeft |
|
895 "align selected objects left" |
|
896 |
|
897 self alignLeft:selection |
|
898 ! |
|
899 |
|
900 selectionAlignRight |
|
901 "align selected objects right" |
|
902 |
|
903 self alignRight:selection |
|
904 ! |
|
905 |
|
906 selectionAlignTop |
|
907 "align selected objects at top" |
|
908 |
|
909 self alignTop:selection |
|
910 ! |
|
911 |
|
912 selectionAlignBottom |
|
913 "align selected objects at bottom" |
|
914 |
|
915 self alignBottom:selection |
|
916 ! ! |
|
917 |
|
918 !ObjectView methodsFor:'adding / removing'! |
|
919 |
|
920 deleteSelection |
|
921 "delete the selection" |
|
922 |
|
923 buffer := selection. |
|
924 self unselect. |
|
925 self remove:buffer. |
|
926 selection := nil |
|
927 ! |
|
928 |
|
929 pasteBuffer |
|
930 "add the objects in the paste-buffer" |
|
931 |
|
932 self unselect. |
|
933 self addSelected:buffer |
|
934 ! |
|
935 |
|
936 copySelection |
|
937 "copy the selection into the paste-buffer" |
|
938 |
|
939 buffer := OrderedCollection new. |
|
940 self selectionDo:[:object | |
|
941 buffer add:(object copy) |
|
942 ]. |
|
943 self forEach:buffer do:[:anObject | |
|
944 anObject moveTo:(anObject origin + (8 @ 8)) |
|
945 ] |
|
946 ! |
|
947 |
|
948 addSelected:something |
|
949 "add something, anObject or a collection of objects to the contents |
|
950 and select it" |
|
951 |
|
952 self add:something. |
|
953 self select:something |
|
954 ! |
|
955 |
|
956 addWithoutRedraw:something |
|
957 "add something, anObject or a collection of objects to the contents |
|
958 do not redraw" |
|
959 |
|
960 self forEach:something do:[:anObject | |
|
961 self addObjectWithoutRedraw:anObject |
|
962 ] |
|
963 ! |
|
964 |
|
965 addObjectWithoutRedraw:anObject |
|
966 "add the argument, anObject to the contents - no redraw" |
|
967 |
|
968 anObject notNil ifTrue:[ |
|
969 contents addLast:anObject |
|
970 ] |
|
971 ! |
|
972 |
|
973 add:something |
|
974 "add something, anObject or a collection of objects to the contents |
|
975 with redraw" |
|
976 |
|
977 self forEach:something do:[:anObject | |
|
978 self addObject:anObject |
|
979 ] |
|
980 ! |
|
981 |
|
982 addObject:anObject |
|
983 "add the argument, anObject to the contents - with redraw" |
|
984 |
|
985 anObject notNil ifTrue:[ |
|
986 contents addLast:anObject. |
|
987 "its on top - only draw this one" |
|
988 realized ifTrue:[ |
|
989 self showUnselected:anObject |
|
990 ] |
|
991 ] |
|
992 ! |
|
993 |
|
994 remove:something |
|
995 "remove something, anObject or a collection of objects from the contents |
|
996 do redraw" |
|
997 |
|
998 self forEach:something do:[:anObject | |
|
999 self removeObject:anObject |
|
1000 ] |
|
1001 ! |
|
1002 |
|
1003 removeObject:anObject |
|
1004 "remove the argument, anObject from the contents - no redraw" |
|
1005 |
|
1006 anObject notNil ifTrue:[ |
|
1007 self removeFromSelection:anObject. |
|
1008 contents remove:anObject. |
|
1009 realized ifTrue:[ |
|
1010 self redrawObjectsIn:(anObject frame) |
|
1011 ] |
|
1012 ] |
|
1013 ! |
|
1014 |
|
1015 removeWithoutRedraw:something |
|
1016 "remove something, anObject or a collection of objects from the contents |
|
1017 do not redraw" |
|
1018 |
|
1019 self forEach:something do:[:anObject | |
|
1020 self removeObjectWithoutRedraw:anObject |
|
1021 ] |
|
1022 ! |
|
1023 |
|
1024 removeObjectWithoutRedraw:anObject |
|
1025 "remove the argument, anObject from the contents - no redraw" |
|
1026 |
|
1027 anObject notNil ifTrue:[ |
|
1028 self removeFromSelection:anObject. |
|
1029 contents remove:anObject |
|
1030 ] |
|
1031 ! |
|
1032 |
|
1033 removeAllWithoutRedraw |
|
1034 "remove all - no redraw" |
|
1035 |
|
1036 selection := nil. |
|
1037 contents := OrderedCollection new |
|
1038 ! |
|
1039 |
|
1040 removeAll |
|
1041 "remove all - redraw" |
|
1042 |
|
1043 self removeAllWithoutRedraw. |
|
1044 self redraw |
|
1045 ! ! |
|
1046 |
|
1047 !ObjectView methodsFor:'misc'! |
|
1048 |
|
1049 setDefaultActions |
|
1050 motionAction := [:movePoint | nil]. |
|
1051 releaseAction := [nil] |
|
1052 ! |
|
1053 |
|
1054 setRectangleDragActions |
|
1055 motionAction := [:movePoint | self doRectangleDrag:movePoint]. |
|
1056 releaseAction := [self endRectangleDrag] |
|
1057 ! |
|
1058 |
|
1059 setMoveActions |
|
1060 motionAction := [:movePoint | self doObjectMove:movePoint]. |
|
1061 releaseAction := [self endObjectMove] |
|
1062 ! |
|
1063 |
|
1064 forEach:aCollection do:aBlock |
|
1065 "apply block to every object in a collectioni; |
|
1066 (adds a check for non-collection)" |
|
1067 |
|
1068 aCollection isNil ifTrue:[^self]. |
|
1069 (aCollection isKindOf:Collection) ifTrue:[ |
|
1070 aCollection do:[:object | |
|
1071 object notNil ifTrue:[ |
|
1072 aBlock value:object |
|
1073 ] |
|
1074 ] |
|
1075 ] ifFalse: [ |
|
1076 aBlock value:aCollection |
|
1077 ] |
|
1078 ! |
|
1079 |
|
1080 objectsInVisible:aRectangle do:aBlock |
|
1081 "do something to every object which is completely in a |
|
1082 visible rectangle" |
|
1083 |
|
1084 |absRect| |
|
1085 |
|
1086 absRect := Rectangle left:(aRectangle left + viewOrigin x) |
|
1087 top:(aRectangle top + viewOrigin y) |
|
1088 width:(aRectangle width) |
|
1089 height:(aRectangle height). |
|
1090 self objectsIn:absRect do:aBlock |
|
1091 ! |
|
1092 |
|
1093 objectsIn:aRectangle do:aBlock |
|
1094 "do something to every object which is completely in a rectangle" |
|
1095 |
|
1096 |bot| |
|
1097 |
|
1098 sorted ifTrue:[ |
|
1099 bot := aRectangle bottom. |
|
1100 contents do:[:theObject | |
|
1101 (theObject isContainedIn:aRectangle) ifTrue:[ |
|
1102 aBlock value:theObject |
|
1103 ] ifFalse:[ |
|
1104 theObject frame top > bot ifTrue:[^ self] |
|
1105 ] |
|
1106 ]. |
|
1107 ^ self |
|
1108 ]. |
|
1109 |
|
1110 contents do:[:theObject | |
|
1111 (theObject isContainedIn:aRectangle) ifTrue:[ |
|
1112 aBlock value:theObject |
|
1113 ] |
|
1114 ] |
|
1115 ! |
|
1116 |
|
1117 visibleObjectsDo:aBlock |
|
1118 "do something to every visible object" |
|
1119 |
|
1120 |absRect| |
|
1121 |
|
1122 absRect := Rectangle left:viewOrigin x |
|
1123 top:viewOrigin y |
|
1124 width:width |
|
1125 height:height. |
|
1126 self objectsIntersecting:absRect do:aBlock |
|
1127 ! |
|
1128 |
|
1129 numberOfObjectsIntersectingVisible:aRectangle |
|
1130 "answer the number of objects intersecting the argument, aRectangle" |
|
1131 |
|
1132 |absRect| |
|
1133 |
|
1134 absRect := Rectangle |
|
1135 left:(aRectangle left + viewOrigin x) |
|
1136 top:(aRectangle top + viewOrigin y) |
|
1137 width:(aRectangle width) |
|
1138 height:(aRectangle height). |
|
1139 |
|
1140 ^ self numberOfObjectsIntersecting:aRectangle |
|
1141 ! |
|
1142 |
|
1143 numberOfObjectsIntersecting:aRectangle |
|
1144 "answer the number of objects intersecting the argument, aRectangle" |
|
1145 |
|
1146 |tally| |
|
1147 |
|
1148 tally := 0. |
|
1149 contents do:[:theObject | |
|
1150 (theObject frame intersects:aRectangle) ifTrue:[ |
|
1151 tally := tally + 1 |
|
1152 ] |
|
1153 ]. |
|
1154 ^ tally |
|
1155 ! |
|
1156 |
|
1157 objectsIntersecting:aRectangle |
|
1158 "answer a Collection of objects intersecting the argument, aRectangle" |
|
1159 |
|
1160 |newCollection| |
|
1161 |
|
1162 newCollection := OrderedCollection new. |
|
1163 self objectsIntersecting:aRectangle do:[:theObject | |
|
1164 newCollection add:theObject |
|
1165 ]. |
|
1166 (newCollection size == 0) ifTrue:[^ nil]. |
|
1167 ^ newCollection |
|
1168 ! |
|
1169 |
|
1170 objectsIntersectingVisible:aRectangle |
|
1171 "answer a Collection of objects intersecting a visible aRectangle" |
|
1172 |
|
1173 |absRect| |
|
1174 |
|
1175 absRect := Rectangle left:(aRectangle left + viewOrigin x) |
|
1176 top:(aRectangle top + viewOrigin y) |
|
1177 width:(aRectangle width) |
|
1178 height:(aRectangle height). |
|
1179 ^ self objectsIntersecting:absRect |
|
1180 ! |
|
1181 |
|
1182 objectsIntersecting:aRectangle do:aBlock |
|
1183 "do something to every object which intersects a rectangle" |
|
1184 |
|
1185 |f top bot |
|
1186 firstIndex "{ Class: SmallInteger }" |
|
1187 delta "{ Class: SmallInteger }" |
|
1188 theObject |
|
1189 nObjects "{ Class: SmallInteger }"| |
|
1190 |
|
1191 sorted ifFalse:[ |
|
1192 "have to check every object" |
|
1193 contents do:[:theObject | |
|
1194 (theObject frame intersects:aRectangle) ifTrue:[ |
|
1195 aBlock value:theObject |
|
1196 ] |
|
1197 ]. |
|
1198 ^ self |
|
1199 ]. |
|
1200 nObjects := contents size. |
|
1201 (nObjects == 0) ifTrue:[^ self]. |
|
1202 |
|
1203 "can break, when 1st object below aRectangle is reached" |
|
1204 bot := aRectangle bottom. |
|
1205 top := aRectangle top. |
|
1206 |
|
1207 "binary search an object in aRectangle ..." |
|
1208 delta := nObjects // 2. |
|
1209 firstIndex := delta. |
|
1210 (firstIndex == 0) ifTrue:[ |
|
1211 firstIndex := 1 |
|
1212 ]. |
|
1213 theObject := contents at:firstIndex. |
|
1214 (theObject frame bottom < top) ifTrue:[ |
|
1215 [theObject frame bottom < top and:[delta > 1]] whileTrue:[ |
|
1216 delta := delta // 2. |
|
1217 firstIndex := firstIndex + delta. |
|
1218 theObject := contents at:firstIndex |
|
1219 ] |
|
1220 ] ifFalse:[ |
|
1221 [theObject frame top > bot and:[delta > 1]] whileTrue:[ |
|
1222 delta := delta // 2. |
|
1223 firstIndex := firstIndex - delta. |
|
1224 theObject := contents at:firstIndex |
|
1225 ] |
|
1226 ]. |
|
1227 "now, theObject at:firstIndex is in aRectangle; go backward to the object |
|
1228 following first non-visible" |
|
1229 |
|
1230 [theObject frame bottom > top and:[firstIndex > 1]] whileTrue:[ |
|
1231 firstIndex := firstIndex - 1. |
|
1232 theObject := contents at:firstIndex |
|
1233 ]. |
|
1234 |
|
1235 firstIndex to:nObjects do:[:index | |
|
1236 theObject := contents at:index. |
|
1237 f := theObject frame. |
|
1238 (f intersects:aRectangle) ifTrue:[ |
|
1239 aBlock value:theObject |
|
1240 ] ifFalse:[ |
|
1241 (f top > bot) ifTrue:[^ self] |
|
1242 ] |
|
1243 ] |
|
1244 ! |
|
1245 |
|
1246 objectsIntersectingVisible:aRectangle do:aBlock |
|
1247 "do something to every object which intersects a visible rectangle" |
|
1248 |
|
1249 |absRect| |
|
1250 |
|
1251 absRect := Rectangle left:(aRectangle left + viewOrigin x) |
|
1252 top:(aRectangle top + viewOrigin y) |
|
1253 width:(aRectangle width) |
|
1254 height:(aRectangle height). |
|
1255 self objectsIntersecting:absRect do:aBlock |
|
1256 ! |
|
1257 |
|
1258 objectsBelow:objectToBeTested do:aBlock |
|
1259 "do something to every object below objectToBeTested |
|
1260 (does not mean obscured by - simply below in hierarchy)" |
|
1261 |
|
1262 |endIndex| |
|
1263 |
|
1264 endIndex := contents identityIndexOf:objectToBeTested ifAbsent:[self error]. |
|
1265 contents from:1 to:(endIndex - 1) do:aBlock |
|
1266 ! |
|
1267 |
|
1268 objectsAbove:objectToBeTested do:aBlock |
|
1269 "do something to every object above objectToBeTested |
|
1270 (does not mean obscured - simply above in hierarchy)" |
|
1271 |
|
1272 |startIndex| |
|
1273 |
|
1274 startIndex := contents identityIndexOf:objectToBeTested |
|
1275 ifAbsent:[self error]. |
|
1276 contents from:startIndex to:(contents size) do:aBlock |
|
1277 ! |
|
1278 |
|
1279 objectsAbove:anObject intersecting:aRectangle do:aBlock |
|
1280 "do something to every object above objectToBeTested |
|
1281 and intersecting aRectangle" |
|
1282 |
|
1283 self objectsAbove:anObject do:[:theObject | |
|
1284 (theObject frame intersects:aRectangle) ifTrue:[ |
|
1285 aBlock value:theObject |
|
1286 ] |
|
1287 ] |
|
1288 ! |
|
1289 |
|
1290 rectangleForScroll |
|
1291 "find the area occupied by visible objects" |
|
1292 |
|
1293 |left right top bottom frame oLeft oRight oTop oBottom orgX orgY| |
|
1294 |
|
1295 orgX := viewOrigin x. |
|
1296 orgY := viewOrigin y. |
|
1297 left := 9999. |
|
1298 right := 0. |
|
1299 top := 9999. |
|
1300 bottom := 0. |
|
1301 self visibleObjectsDo:[:anObject | |
|
1302 frame := anObject frame. |
|
1303 oLeft := frame left - orgX. |
|
1304 oRight := frame right - orgX. |
|
1305 oTop := frame top - orgY. |
|
1306 oBottom := frame bottom - orgY. |
|
1307 (oLeft < left) ifTrue:[left := oLeft]. |
|
1308 (oRight > right) ifTrue:[right := oRight]. |
|
1309 (oTop < top) ifTrue:[top := oTop]. |
|
1310 (oBottom > bottom) ifTrue:[bottom := oBottom] |
|
1311 ]. |
|
1312 (left < margin) ifTrue:[left := margin]. |
|
1313 (top < margin) ifTrue:[top := margin]. |
|
1314 (right > (width - margin)) ifTrue:[right := width - margin]. |
|
1315 (bottom > (height - margin)) ifTrue:[bottom := height - margin]. |
|
1316 |
|
1317 ((left > right) or:[top > bottom]) ifTrue:[^ nil]. |
|
1318 |
|
1319 ^ Rectangle left:left right:right top:top bottom:bottom |
|
1320 ! ! |
|
1321 |
|
1322 !ObjectView methodsFor:'view manipulation'! |
|
1323 |
|
1324 showScale |
|
1325 "show the scale" |
|
1326 |
|
1327 scaleShown := true. |
|
1328 self redrawScale |
|
1329 ! |
|
1330 |
|
1331 hideScale |
|
1332 "hide the scale" |
|
1333 |
|
1334 scaleShown := false. |
|
1335 self redrawScale |
|
1336 ! |
|
1337 |
|
1338 millimeterMetric |
|
1339 (scaleMetric == #inch) ifTrue:[ |
|
1340 scaleMetric := #mm. |
|
1341 gridShown ifTrue:[ |
|
1342 self defineGrid. |
|
1343 self redraw |
|
1344 ] |
|
1345 ] |
|
1346 ! |
|
1347 |
|
1348 inchMetric |
|
1349 (scaleMetric == #mm) ifTrue:[ |
|
1350 scaleMetric := #inch. |
|
1351 gridShown ifTrue:[ |
|
1352 self defineGrid. |
|
1353 self redraw |
|
1354 ] |
|
1355 ] |
|
1356 ! |
|
1357 |
|
1358 defineGrid |
|
1359 "define the grid pattern" |
|
1360 |
|
1361 |mmH mmV gridW gridH xp yp y x |
|
1362 bigStepH bigStepV littleStepH littleStepV hires |
|
1363 oldCursor| |
|
1364 |
|
1365 mmH := self horizontalPixelPerMillimeter. |
|
1366 mmV := self verticalPixelPerMillimeter. |
|
1367 hires := self horizontalPixelPerInch > 120. |
|
1368 |
|
1369 (scaleMetric == #mm) ifTrue:[ |
|
1370 "dots every mm; lines every cm" |
|
1371 bigStepH := mmH * 10.0. |
|
1372 bigStepV := mmV * 10.0. |
|
1373 littleStepH := mmH. |
|
1374 littleStepV := mmV |
|
1375 ]. |
|
1376 (scaleMetric == #inch) ifTrue:[ |
|
1377 "dots every eights inch; lines every half inch" |
|
1378 bigStepH := mmH * (25.4 / 2). |
|
1379 bigStepV := mmV * (25.4 / 2). |
|
1380 littleStepH := mmH * (25.4 / 8). |
|
1381 littleStepV := mmV * (25.4 / 8) |
|
1382 ]. |
|
1383 bigStepH isNil ifTrue:[^ self]. |
|
1384 |
|
1385 oldCursor := cursor. |
|
1386 self cursor:Cursor wait. |
|
1387 |
|
1388 gridW := (self widthOfContentsInMM * mmH + 1) asInteger. |
|
1389 gridH := (self heightOfContentsInMM * mmV + 1) asInteger. |
|
1390 gridPixmap := Form width:gridW height:gridH depth:(device depth). |
|
1391 gridPixmap fill:viewBackground. |
|
1392 gridPixmap paint:paint. |
|
1393 |
|
1394 "draw first row point-by-point" |
|
1395 yp := 0.0. |
|
1396 xp := 0.0. |
|
1397 y := yp asInteger. |
|
1398 [xp <= gridW] whileTrue:[ |
|
1399 x := xp rounded. |
|
1400 hires ifTrue:[ |
|
1401 gridPixmap drawPointX:(x + 1) y:y. |
|
1402 gridPixmap drawPointX:(x + 2) y:y |
|
1403 ]. |
|
1404 gridPixmap drawPointX:x y:y. |
|
1405 xp := xp + littleStepH |
|
1406 ]. |
|
1407 |
|
1408 "copy rest from what has been drawn already" |
|
1409 yp := yp + bigStepV. |
|
1410 [yp <= gridH] whileTrue:[ |
|
1411 y := yp rounded. |
|
1412 hires ifTrue:[ |
|
1413 gridPixmap copyFrom:gridPixmap x:0 y:0 |
|
1414 toX:0 y:(y + 1) |
|
1415 width:gridW height:1. |
|
1416 gridPixmap copyFrom:gridPixmap x:0 y:0 |
|
1417 toX:0 y:(y + 2) |
|
1418 width:gridW height:1 |
|
1419 ]. |
|
1420 gridPixmap copyFrom:gridPixmap x:0 y:0 |
|
1421 toX:0 y:y |
|
1422 width:gridW height:1. |
|
1423 yp := yp + bigStepV |
|
1424 ]. |
|
1425 |
|
1426 "draw first col point-by-point" |
|
1427 xp := 0.0. |
|
1428 yp := 0.0. |
|
1429 x := xp asInteger. |
|
1430 [yp <= gridH] whileTrue:[ |
|
1431 y := yp rounded. |
|
1432 hires ifTrue:[ |
|
1433 gridPixmap drawPointX:x y:(y + 1). |
|
1434 gridPixmap drawPointX:x y:(y + 2) |
|
1435 ]. |
|
1436 gridPixmap drawPointX:x y:y. |
|
1437 yp := yp + littleStepV |
|
1438 ]. |
|
1439 |
|
1440 "copy rest from what has been drawn already" |
|
1441 xp := xp + bigStepH. |
|
1442 [xp <= gridW] whileTrue:[ |
|
1443 x := xp rounded. |
|
1444 hires ifTrue:[ |
|
1445 gridPixmap copyFrom:gridPixmap x:0 y:0 |
|
1446 toX:(x + 1) y:0 |
|
1447 width:1 height:gridH. |
|
1448 gridPixmap copyFrom:gridPixmap x:0 y:0 |
|
1449 toX:(x + 2) y:0 |
|
1450 width:1 height:gridH |
|
1451 ]. |
|
1452 gridPixmap copyFrom:gridPixmap x:0 y:0 |
|
1453 toX:x y:0 |
|
1454 width:1 height:gridH. |
|
1455 xp := xp + bigStepH |
|
1456 ]. |
|
1457 self cursor:oldCursor |
|
1458 ! |
|
1459 |
|
1460 showGrid |
|
1461 "show the grid" |
|
1462 |
|
1463 gridShown := true. |
|
1464 gridPixmap isNil ifTrue:[ |
|
1465 self defineGrid |
|
1466 ]. |
|
1467 self redraw |
|
1468 ! |
|
1469 |
|
1470 hideGrid |
|
1471 "hide the grid" |
|
1472 |
|
1473 gridShown := false. |
|
1474 self redraw |
|
1475 ! |
|
1476 |
|
1477 alignOn |
|
1478 "align points to grid" |
|
1479 |
|
1480 aligning := true |
|
1481 ! |
|
1482 |
|
1483 alignOff |
|
1484 "do no align point to grid" |
|
1485 |
|
1486 aligning := false |
|
1487 ! ! |
|
1488 |
|
1489 !ObjectView methodsFor:'user interface'! |
|
1490 |
|
1491 alignToGrid:aPoint |
|
1492 "round aPoint to the next nearest point on the grid" |
|
1493 |
|
1494 |mmH mmV aH aV| |
|
1495 |
|
1496 aligning ifFalse:[ |
|
1497 ^ aPoint |
|
1498 ]. |
|
1499 |
|
1500 mmH := self horizontalPixelPerMillimeter. |
|
1501 mmV := self verticalPixelPerMillimeter. |
|
1502 |
|
1503 (scaleMetric == #mm) ifTrue:[ |
|
1504 "align to mm" |
|
1505 aH := mmH. |
|
1506 aV := mmV |
|
1507 ]. |
|
1508 (scaleMetric == #inch) ifTrue:[ |
|
1509 "align to eights inch" |
|
1510 aH := mmH * (25.4 / 8). |
|
1511 aV := mmV * (25.4 / 8) |
|
1512 ]. |
|
1513 |
|
1514 ^ (aPoint grid:(aH @ aV)) grid:(1 @ 1) |
|
1515 ! |
|
1516 |
|
1517 startRectangleDrag:startPoint |
|
1518 "start a rectangle drag" |
|
1519 |
|
1520 self setRectangleDragActions. |
|
1521 groupRectangleFrame := Rectangle origin:startPoint corner:startPoint. |
|
1522 self xoring:[self drawRectangle:groupRectangleFrame]. |
|
1523 oldCursor := cursor. |
|
1524 self cursor:leftHandCursor |
|
1525 ! |
|
1526 |
|
1527 doRectangleDrag:aPoint |
|
1528 "do drag a rectangle" |
|
1529 |
|
1530 self xoring:[ |
|
1531 self drawRectangle:groupRectangleFrame. |
|
1532 groupRectangleFrame corner:aPoint. |
|
1533 self drawRectangle:groupRectangleFrame |
|
1534 ] |
|
1535 ! |
|
1536 |
|
1537 endRectangleDrag |
|
1538 "cleanup after rectangle drag; select them" |
|
1539 |
|
1540 self xoring:[self drawRectangle:groupRectangleFrame]. |
|
1541 self cursor:oldCursor. |
|
1542 self selectAllIn:(groupRectangleFrame + viewOrigin) |
|
1543 ! |
|
1544 |
|
1545 selectMore:aPoint |
|
1546 "add/remove an object from the selection" |
|
1547 |
|
1548 |anObject| |
|
1549 |
|
1550 anObject := self findObjectAtVisible:aPoint. |
|
1551 anObject notNil ifTrue:[ |
|
1552 (self isSelected:anObject) ifTrue:[ |
|
1553 "remove from selection" |
|
1554 self removeFromSelection:anObject |
|
1555 ] ifFalse:[ |
|
1556 "add to selection" |
|
1557 self addToSelection:anObject |
|
1558 ] |
|
1559 ]. |
|
1560 ^ self |
|
1561 ! |
|
1562 |
|
1563 startSelectOrMove:aPoint |
|
1564 "start a rectangleDrag or objectMove - if aPoint hits an object, |
|
1565 an object move is started, otherwise a rectangleDrag" |
|
1566 |
|
1567 |anObject| |
|
1568 |
|
1569 anObject := self findObjectAtVisible:aPoint. |
|
1570 anObject notNil ifTrue:[ |
|
1571 (self isSelected:anObject) ifFalse:[self unselect]. |
|
1572 self startObjectMove:anObject at:aPoint. |
|
1573 ^ self |
|
1574 ]. |
|
1575 "nothing was hit by this click - this starts a group select" |
|
1576 self unselect. |
|
1577 self startRectangleDrag:aPoint |
|
1578 ! |
|
1579 |
|
1580 startSelectMoreOrMove:aPoint |
|
1581 "add/remove object hit by aPoint, then start a rectangleDrag or move |
|
1582 - if aPoint hits an object, a move is started, otherwise a rectangleDrag" |
|
1583 |
|
1584 |anObject| |
|
1585 |
|
1586 anObject := self findObjectAtVisible:aPoint. |
|
1587 anObject notNil ifTrue:[ |
|
1588 (self isSelected:anObject) ifTrue:[ |
|
1589 "remove from selection" |
|
1590 self removeFromSelection:anObject |
|
1591 ] ifFalse:[ |
|
1592 "add to selection" |
|
1593 self addToSelection:anObject |
|
1594 ]. |
|
1595 self startObjectMove:selection at:aPoint. |
|
1596 ^ self |
|
1597 ]. |
|
1598 self unselect. |
|
1599 self startRectangleDrag:aPoint |
|
1600 ! |
|
1601 |
|
1602 startObjectMove:something at:aPoint |
|
1603 "start an object move" |
|
1604 |
|
1605 something notNil ifTrue:[ |
|
1606 self select:something. |
|
1607 (self canMove:something) ifTrue:[ |
|
1608 self setMoveActions. |
|
1609 moveStartPoint := aPoint. |
|
1610 rootMotion := canDragOutOfView "." |
|
1611 "self doObjectMove:aPoint " |
|
1612 ] ifFalse:[ |
|
1613 self setDefaultActions |
|
1614 ] |
|
1615 ] |
|
1616 ! |
|
1617 |
|
1618 doObjectMove:aPoint |
|
1619 "do an object move" |
|
1620 |
|
1621 |dragger offs2| |
|
1622 |
|
1623 canDragOutOfView ifTrue:[ |
|
1624 dragger := rootView. |
|
1625 offs2 := viewOrigin |
|
1626 ] ifFalse:[ |
|
1627 dragger := self. |
|
1628 offs2 := 0@0 |
|
1629 ]. |
|
1630 movedObject isNil ifTrue:[ |
|
1631 movedObject := selection. |
|
1632 movedObject notNil ifTrue:[ |
|
1633 moveDelta := 0@0. |
|
1634 dragger xoring:[ |
|
1635 self showDragging:movedObject |
|
1636 offset:(moveDelta - offs2) |
|
1637 ] |
|
1638 ] |
|
1639 ]. |
|
1640 movedObject notNil ifTrue:[ |
|
1641 dragger xoring:[ |
|
1642 self showDragging:movedObject offset:(moveDelta - offs2). |
|
1643 moveDelta := aPoint - moveStartPoint. |
|
1644 self showDragging:movedObject offset:(moveDelta - offs2) |
|
1645 ] |
|
1646 ] |
|
1647 ! |
|
1648 |
|
1649 endObjectMove |
|
1650 "cleanup after object move - physically move the object now" |
|
1651 |
|
1652 |dragger inMySelf offs2 rootPoint destinationPoint |
|
1653 viewId destinationView destinationId lastViewId| |
|
1654 |
|
1655 movedObject notNil ifTrue:[ |
|
1656 canDragOutOfView ifTrue:[ |
|
1657 dragger := rootView. |
|
1658 offs2 := viewOrigin |
|
1659 ] ifFalse:[ |
|
1660 dragger := self. |
|
1661 offs2 := 0@0 |
|
1662 ]. |
|
1663 dragger xoring:[self showDragging:movedObject |
|
1664 offset:(moveDelta - offs2)]. |
|
1665 dragger device synchronizeOutput. |
|
1666 |
|
1667 "check if object is to be put into another view" |
|
1668 rootMotion ifTrue:[ |
|
1669 rootPoint := device translatePoint:lastButt |
|
1670 from:(self id) |
|
1671 to:(rootView id). |
|
1672 "search view the drop is in" |
|
1673 viewId := rootView id. |
|
1674 [viewId notNil] whileTrue:[ |
|
1675 destinationId := device viewIdFromPoint:rootPoint in:viewId. |
|
1676 lastViewId := viewId. |
|
1677 viewId := destinationId |
|
1678 ]. |
|
1679 destinationView := device viewFromId:lastViewId. |
|
1680 destinationId := lastViewId. |
|
1681 inMySelf := (destinationView == self). |
|
1682 rootMotion := false |
|
1683 ] ifFalse:[ |
|
1684 inMySelf := true |
|
1685 ]. |
|
1686 inMySelf ifTrue:[ |
|
1687 "simple move" |
|
1688 self move:movedObject by:moveDelta |
|
1689 ] ifFalse:[ |
|
1690 destinationPoint := device translatePoint:rootPoint |
|
1691 from:(rootView id) |
|
1692 to:destinationId. |
|
1693 destinationView notNil ifTrue:[ |
|
1694 "move into another smalltalk view" |
|
1695 self move:movedObject to:destinationPoint |
|
1696 in:destinationView |
|
1697 ] ifFalse:[ |
|
1698 self move:movedObject to:destinationPoint |
|
1699 inAlienViewId:destinationId |
|
1700 ] |
|
1701 ]. |
|
1702 self setDefaultActions. |
|
1703 movedObject := nil |
|
1704 ] |
|
1705 ! ! |
|
1706 |
|
1707 !ObjectView methodsFor:'events'! |
|
1708 |
|
1709 buttonPress:button x:x y:y |
|
1710 "user pressed left button" |
|
1711 |
|
1712 (button == 1) ifTrue:[ |
|
1713 pressAction notNil ifTrue:[ |
|
1714 lastButt := x @ y. |
|
1715 pressAction value:lastButt |
|
1716 ] |
|
1717 ] ifFalse:[ |
|
1718 super buttonPress:button x:x y:y |
|
1719 ] |
|
1720 ! |
|
1721 |
|
1722 buttonShiftPress:button x:x y:y |
|
1723 "user pressed left button with shift" |
|
1724 |
|
1725 (button == 1) ifTrue:[ |
|
1726 shiftPressAction notNil ifTrue:[ |
|
1727 lastButt := x @ y. |
|
1728 shiftPressAction value:lastButt |
|
1729 ] |
|
1730 ] ifFalse:[ |
|
1731 super buttonShiftPress:button x:x y:y |
|
1732 ] |
|
1733 ! |
|
1734 |
|
1735 buttonMultiPress:button x:x y:y |
|
1736 "user pressed left button twice (or more)" |
|
1737 |
|
1738 (button == 1) ifTrue:[ |
|
1739 doublePressAction notNil ifTrue:[ |
|
1740 doublePressAction value:(x @ y) |
|
1741 ] |
|
1742 ] ifFalse:[ |
|
1743 super buttonMultiPress:button x:x y:y |
|
1744 ] |
|
1745 ! |
|
1746 |
|
1747 buttonMotion:button x:buttX y:buttY |
|
1748 "user moved mouse while button pressed" |
|
1749 |
|
1750 |xpos ypos movePoint| |
|
1751 |
|
1752 (lastButt == nil) ifFalse:[ |
|
1753 xpos := buttX. |
|
1754 ypos := buttY. |
|
1755 |
|
1756 "check against view limits if move outside is not allowed" |
|
1757 rootMotion ifFalse:[ |
|
1758 (xpos < 0) ifTrue:[ |
|
1759 xpos := 0 |
|
1760 ] ifFalse: [ |
|
1761 (xpos > width) ifTrue:[xpos := width] |
|
1762 ]. |
|
1763 (ypos < 0) ifTrue:[ |
|
1764 ypos := 0 |
|
1765 ] ifFalse: [ |
|
1766 (ypos > height) ifTrue:[ypos := height] |
|
1767 ] |
|
1768 ]. |
|
1769 movePoint := xpos @ ypos. |
|
1770 |
|
1771 (xpos == (lastButt x)) ifTrue:[ |
|
1772 (ypos == (lastButt y)) ifTrue:[ |
|
1773 ^ self "no move" |
|
1774 ] |
|
1775 ]. |
|
1776 |
|
1777 motionAction notNil ifTrue:[ |
|
1778 motionAction value:movePoint |
|
1779 ]. |
|
1780 lastButt := movePoint |
|
1781 ] |
|
1782 ! |
|
1783 |
|
1784 buttonRelease:button x:x y:y |
|
1785 (button == 1) ifTrue: [ |
|
1786 releaseAction notNil ifTrue:[releaseAction value] |
|
1787 ] ifFalse:[ |
|
1788 super buttonRelease:button x:x y:y |
|
1789 ] |
|
1790 ! |
|
1791 |
|
1792 keyPress:key x:x y:y |
|
1793 keyPressAction notNil ifTrue:[ |
|
1794 selection notNil ifTrue:[ |
|
1795 self selectionDo: [:obj | |
|
1796 obj keyInput:key |
|
1797 ] |
|
1798 ] |
|
1799 ] |
|
1800 ! |
|
1801 |
|
1802 redrawX:x y:y width:w height:h |
|
1803 |innerX innerY innerW innerH redrawFrame | |
|
1804 |
|
1805 innerX := x. |
|
1806 innerY := y. |
|
1807 innerW := w. |
|
1808 innerH := h. |
|
1809 scaleShown ifTrue:[ |
|
1810 (x < leftMarginForScale) ifTrue:[ |
|
1811 self redrawVerticalScale. |
|
1812 innerW := w - (leftMarginForScale - x). |
|
1813 innerX := leftMarginForScale |
|
1814 ]. |
|
1815 (y < topMarginForScale) ifTrue:[ |
|
1816 self redrawHorizontalScale. |
|
1817 innerH := h - (topMarginForScale - y). |
|
1818 innerY := topMarginForScale |
|
1819 ] |
|
1820 ]. |
|
1821 (contents size ~~ 0) ifTrue:[ |
|
1822 redrawFrame := Rectangle left:innerX top:innerY |
|
1823 width:innerW height:innerH. |
|
1824 self redrawObjectsInVisible:redrawFrame |
|
1825 ] |
|
1826 ! ! |
|
1827 |
|
1828 !ObjectView methodsFor:'saving / restoring'! |
|
1829 |
|
1830 storeContentsOn:aStream |
|
1831 |excla| |
|
1832 |
|
1833 excla := aStream class chunkSeparator. |
|
1834 self forEach:contents do:[:theObject | |
|
1835 theObject storeOn:aStream. |
|
1836 aStream nextPut:excla. |
|
1837 aStream cr |
|
1838 ]. |
|
1839 aStream nextPut:excla |
|
1840 ! |
|
1841 |
|
1842 initializeFileInObject:anObject |
|
1843 "each object may be processed here after its beeing filed-in |
|
1844 - subclasses may do whatever they want here ... |
|
1845 (see LogicView for example)" |
|
1846 |
|
1847 ^ self |
|
1848 ! |
|
1849 |
|
1850 withoutRedrawFileInContentsFrom:aStream |
|
1851 self fileInContentsFrom:aStream redraw:false |
|
1852 ! |
|
1853 |
|
1854 fileInContentsFrom:aStream |
|
1855 self fileInContentsFrom:aStream redraw:true |
|
1856 ! |
|
1857 |
|
1858 fileInContentsFrom:aStream redraw:redraw |
|
1859 |newObject chunk savCursor| |
|
1860 |
|
1861 savCursor := self cursor. |
|
1862 self cursor:readCursor. |
|
1863 self unselect. |
|
1864 self removeAll. |
|
1865 [aStream atEnd] whileFalse:[ |
|
1866 chunk := aStream nextChunk. |
|
1867 chunk notNil ifTrue:[ |
|
1868 chunk isEmpty ifFalse:[ |
|
1869 newObject := Compiler evaluate:chunk. |
|
1870 self initializeFileInObject:newObject. |
|
1871 redraw ifFalse:[ |
|
1872 self addObjectWithoutRedraw:newObject |
|
1873 ] ifTrue:[ |
|
1874 self addObject:newObject |
|
1875 ] |
|
1876 ] |
|
1877 ] |
|
1878 ]. |
|
1879 self cursor:savCursor |
|
1880 ! ! |