1 /***********************************************************************/
2 /* Open Visualization Data Explorer                                    */
3 /* (C) Copyright IBM Corp. 1989,1999                                   */
4 /* ALL RIGHTS RESERVED                                                 */
5 /* This code licensed under the                                        */
6 /*    "IBM PUBLIC LICENSE - Open Visualization Data Explorer"          */
7 /***********************************************************************/
8 
9 #include <dxconfig.h>
10 #include "../base/defines.h"
11 
12 #ifdef OS2
13 #include <stdlib.h>
14 #include <types.h>
15 #endif
16 #include <stdio.h> /* for sprintf */
17 #include <ctype.h>              /*  define isupper()  */
18 #include <X11/Xlib.h>
19 #include <X11/StringDefs.h>
20 #include <X11/cursorfont.h>
21 #include <X11/IntrinsicP.h>
22 #include <X11/Intrinsic.h>
23 #include <X11/CoreP.h>
24 #include <X11/CompositeP.h>
25 #include <X11/Composite.h>
26 #include <X11/Constraint.h>
27 #include <Xm/XmP.h>
28 #include <Xm/Xm.h>
29 #include "WorkspaceW.h"
30 #include "WorkspaceP.h"
31 #include "WorkspaceCallback.h"
32 #include <string.h>
33 #include <X11/keysym.h>
34 
35 #if !defined(XK_MISCELLANY)
36 #define XK_MISCELLANY 1
37 #endif
38 
39 
40 /* External functions */
41 extern void ChangeWidgetInCollideList(); /* From Findroute.c */
42 
43 extern void _XmManagerEnter();
44 extern void _XmManagerFocusIn();
45 
46 #define STRCMP(a,b)  ((a) ? ((b) ? strcmp(a,b) : strcmp(a,"")) : \
47 			    ((b) ? strcmp("",b) : 0))
48 
49 /*
50  * Explanation of default Actions in response to mouse pressing and motion:
51  *
52  * Action started on background:
53  *
54  *  ~SHIFT Button1Down: Start rubberband
55  *                      Unhighlight previously selected children
56  *
57  *  SHIFT Button1Down:  Start rubberband
58  *
59  *  (~SHIFT)Button1Move:        Stretch rubberband
60  *                      (A) Highlight children as included
61  *                      (A) Unhighlight children as unincluded
62  *
63  *  (SHIFT)Button1Move: Stretch rubberband
64  *                      (A) Toggle highlight of children as included
65  *                      (A) Toggle highlight of children as unincluded
66  *
67  *  (~SHIFT)Button1Up:  Hide rubberband
68  *                      (B) Highlight new included children
69  *                      Callback w/ unselect new excluded children
70  *                      Callback w/ select new included children
71  *
72  *  (SHIFT)Button1Up:   Hide rubberband
73  *                      (B) Toggle highlight of all included children
74  *                      Callback w/ unselect new unhighlighted children
75  *                      Callback w/ select new highlighted children
76  *
77  * Action started in child:
78  *
79  *  If child was not already selected:
80  *    ~SHIFT Button1Down:         Unhighlight prior highlighted children
81  *                        Highlight child
82  *                        Callback w/ unselect to prior selected children
83  *                        Callback w/ select to child
84  *                        enable move
85  *
86  *    SHIFT Button1Down:          Highlight child
87  *                        Callback w/ select to child
88  *                        enable move
89  *
90  *  If child was already selected:
91  *    ~SHIFT Button1Down:   enable move
92  *
93  *    SHIFT Button1Down:    Unhighlight child
94  *                        Callback w/ unselect to child
95  *                        disable move
96  *
97  *  Button1Move:                if move enabled:
98  *                        (A) Move outline of each selected child
99  *                        (B) Move bounding box of selected children extremes
100  *                        (C) (B) plus outline of grabbed child
101  *
102  *  Button1Up:          if move enabled:
103  *                      Hide rubberband(s)
104  *                      Move selected children
105  *                      (?) Action to update graphics
106  *
107  */
108 
109 
110 /*
111  * Explanation of Automated widget movement applied when widget overlap is
112  * not allowed:
113  *
114  *  Space wars rules:
115  *
116  *    1. No moved widget may overlap any other.
117  *    2. Movement to clear overlaps is to the right only
118  *    3. Any widget moved, whether by user or by space wars, will be placed
119  *       according to all existing gridding rules.
120  *    4. All adjustment to avoid overlap is done by moving widgets to the right.
121  *
122  *  Option Rules:
123  *   (Option 2 is the default)
124  *   (Option 0 may be a bit more efficient if only 1 child can be moved at
125  *     a time).
126  *    0. Automated movement for space wars does not change horizontal widget
127  *       ordering (established at time selected children are dropped).
128  *    1. a. The left-most selected child maintains its horizontal order as
129  *          placed.
130  *       b. All other selected children maintain their position relative to
131  *          that left-most child
132  *    2. a. Selected widgets are placed (and snapped-to-grid if called for) with
133  *          no consideration of other widgets.
134  *       b. All other widgets move right to accomodate new placement.
135  *       c. Movement of non-selected widgets does not alter the horizontal
136  *       ordering among those non-selected widgets (e.g. a small child to
137  *          the right of a large one will NOT be moved ahead of it to fill a
138  *          convenient hole too small for the larger one).
139  *
140  *  Rules of Horizontal Ordering
141  *    1. Horizontal ordering is based on the x coordinate of each widget's
142  *       left edge, center, or right edge as per alignment_policy.
143  *    2. a. When two widgets have the same x coordinate for alignment, the
144  *          selected widget is ordered before one that is not selected.
145  *       b. When both widgets have the same x coordinate and selection status,
146  *          the ordering will be based on the y coordinates as per alignment.
147  */
148 
149 
150 /*  Define structure of link elements in a sort list of child widgets  */
151 struct SortRec {
152     struct SortRec* next;
153     Widget child;
154     XmWorkspaceConstraints constraints;
155     short x_left, x_right;
156     short y_upper, y_lower;
157     int x_index;
158     Boolean is_selected;
159     Boolean is_displaced;
160 };
161 
162 /*  Parameter to control size of arms of alignment hash marks: ray+1+ray  */
163 #define HASH_RAY 4
164 #define GRID_DASH_MIN 10
165 
166 /*  Codes and indexes for rubberbanding cursor  */
167 #define SZ_NONE 4
168 #define SZ_LEFT 0
169 #define SZ_RIGHT 1
170 #define SZ_UPPER 0
171 #define SZ_LOWER 2
172 #define SZ_UL 0
173 #define SZ_UR 1
174 #define SZ_LL 2
175 #define SZ_LR 3
176 
177 #define FC_LEFT    0
178 #define FC_RIGHT   1
179 #define FC_TOP     2
180 #define FC_BOTTOM  3
181 
182 #define superclass (&xmFormClassRec)
183 
184 LineElement *Manhattan(XmWorkspaceWidget ww, int srcx, int srcy,
185                               int dstx, int dsty,
186                               int level, int *cost,
187 			      Widget source, Widget destination,
188 			      XmWorkspaceLine new, int *failnum);
189 void CopyPointsToLine(XmWorkspaceWidget ww,LineElement *list,
190 						XmWorkspaceLine line);
191 extern void AddWidgetToCollideList(Widget child);
192 extern void HideWidgetInCollideList(XmWorkspaceWidget ww, Widget child);
193 extern void DeleteWidgetFromCollideList(Widget child);
194 extern void RemoveLineFromCollideList(XmWorkspaceWidget ww,
195 					XmWorkspaceLine line);
196 extern void AddLineToCollideList(XmWorkspaceWidget ww, XmWorkspaceLine line);
197 
198 static void     ClassInitialize         ();
199 static void     Initialize              (XmWorkspaceWidget request,
200 	                                 XmWorkspaceWidget new);
201 static void     Realize                 (XmWorkspaceWidget ww,
202 	                                 Mask* p_valueMask,
203 	                                 XSetWindowAttributes *attributes);
204 static void     Destroy                 (XmWorkspaceWidget ww);
205 static void     Redisplay               (XmWorkspaceWidget ww,
206 	                                 XExposeEvent* event,
207 	                                 Region region);
208 static void	Resize			(XmWorkspaceWidget ww);
209 static Boolean  RedisplayRectangle      (XmWorkspaceWidget ww,
210 	                                 XRectangle* rect,
211 	                                 Region region,
212 	                                 Boolean clip_region_set);
213 static Boolean  SetValues               (XmWorkspaceWidget current,
214 	                                 XmWorkspaceWidget request,
215 	                                 XmWorkspaceWidget new);
216 static void     ChangeManaged           (XmWorkspaceWidget ww);
217 static XtGeometryResult
218 	        _GeometryManager         (Widget w,
219 	                                 XtWidgetGeometry *request,
220 	                                 XtWidgetGeometry *reply,
221 	                                 Boolean adjust_constraints);
222 static XtGeometryResult
223 	        GeometryManager         (Widget w,
224 	                                 XtWidgetGeometry *request,
225 	                                 XtWidgetGeometry *reply);
226 static void     Arm                     (XmWorkspaceWidget ww,
227 	                                 XEvent* event);
228 static void     Drag                    (XmWorkspaceWidget ww,
229 	                                 XEvent* event);
230 static void     Disarm                  (XmWorkspaceWidget ww,
231 	                                 XEvent* event);
232 static void     KeyboardNavigation      (XmWorkspaceWidget ww,
233 	                                 XEvent* event);
234 static void     ConstraintInitialize    (Widget req,
235 	                                 Widget new);
236 static Boolean  ConstraintSetValues     (Widget current,
237 	                                 Widget request,
238 	                                 Widget new);
239 static void     ConstraintDestroy       (Widget w);
240 static void     GetRubberbandGC         (XmWorkspaceWidget ww);
241 static void     DrawRubberband          (XmWorkspaceWidget ww);
242 static void     GrabSelections          (Widget w,
243 	                                 XEvent* event,
244 	                                 String* params,
245 	                                 Cardinal* num_params);
246 static void     MoveResizeSelections     (Widget w,
247 	                                 XEvent* event,
248 	                                 String* params,
249 	                                 Cardinal* num_params);
250 static void     ChildNavigation         (Widget w,
251 	                                 XEvent* event,
252 	                                 String* params,
253 	                                 Cardinal* num_params);
254 #if RESIZE_HANDLES
255 static void     ResizeNE 	        (Widget w,
256 	                                 XEvent* event,
257 	                                 String* params,
258 	                                 Cardinal* num_params);
259 static void     ResizeNW 	        (Widget w,
260 	                                 XEvent* event,
261 	                                 String* params,
262 	                                 Cardinal* num_params);
263 static void     ResizeSE 	        (Widget w,
264 	                                 XEvent* event,
265 	                                 String* params,
266 	                                 Cardinal* num_params);
267 static void     ResizeSW 	        (Widget w,
268 	                                 XEvent* event,
269 	                                 String* params,
270 	                                 Cardinal* num_params);
271 static void     NewDrop 	        (Widget w,
272 	                                 XEvent* event,
273 	                                 String* params,
274 	                                 Cardinal* num_params);
275 #endif
276 static void     DropSelections          (Widget w,
277 	                                 XEvent* event,
278 	                                 String* params,
279 	                                 Cardinal* num_params);
280 static void     RaiseSelections         (Widget w,
281 	                                 XEvent* event,
282 	                                 String* params,
283 	                                 Cardinal* num_params);
284 static void     MoveChild               (XmWorkspaceWidget ww,
285 	                                 Widget child,
286 	                                 XmWorkspaceConstraints constraints);
287 static void     ResetChild              (XmWorkspaceWidget ww,
288 	                                 XmWorkspaceConstraints constraints);
289 static void     SnapToGrid              (XmWorkspaceWidget ww,
290 	                                 Widget child,
291 	                                 XmWorkspaceConstraints constraints,
292 	                                 Boolean increase);
293 static void     Align1D                 (int* x,
294 	                                 int width,
295 	                                 int grid,
296 	                                 unsigned char alignment,
297 	                                 Boolean increase_only);
298 static void     UpdateRubberbandSelections
299 	                                (XmWorkspaceWidget ww,
300 	                                 XEvent* event);
301 static void     UnselectAll             (XmWorkspaceWidget ww,
302 	                                 XEvent* event);
303 static void     SelectChild             (XmWorkspaceWidget ww,
304 	                                 Widget child,
305 	                                 XmWorkspaceConstraints constraints,
306 	                                 XEvent* event);
307 static void     UnselectChild           (XmWorkspaceWidget ww,
308 	                                 Widget child,
309 	                                 XmWorkspaceConstraints constraints,
310 	                                 XEvent* event);
311 static void     StartRubberband         (XmWorkspaceWidget ww,
312 	                                 XEvent* event);
313 static void     StretchRubberband       (XmWorkspaceWidget ww,
314 	                                 XEvent* event);
315 static void     EndRubberband           (XmWorkspaceWidget ww,
316 	                                 XEvent* event);
317 static void     AccentChild             (XmWorkspaceWidget ww,
318 	                                 Widget child,
319 	                                 XmWorkspaceConstraints constraints,
320 	                                 XEvent* event);
321 static void     UnaccentChild           (XmWorkspaceWidget ww,
322 	                                 Widget child,
323 	                                 XmWorkspaceConstraints constraints,
324 	                                 XEvent* event);
325 static void     UnaccentAll             (XmWorkspaceWidget ww,
326 	                                 XEvent* event);
327 static void     RestackSelectedChildren (XmWorkspaceWidget ww);
328 static Boolean  ChildWindowIsSelected   (XmWorkspaceWidget ww,
329 	                                 Window child);
330 static void     SetGridBackground       (XmWorkspaceWidget ww, Boolean change);
331 static void     DrawGridMarks           (XmWorkspaceWidget ww,
332 	                                 GC gc,
333 	                                 Pixmap pixmap);
334 static void     OutlineChild            (XRectangle* surrogate,
335 	                                 XmWorkspaceConstraints constraints);
336 static void     SaveOutline             (XmWorkspaceWidget ww,
337 	                                 Widget grab_child);
338 static void     SaveOutlines            (XmWorkspaceWidget ww);
339 static void     DiscardSurrogate        (XmWorkspaceWidget ww);
340 static void     CreateSurrogate         (XmWorkspaceWidget ww,
341 	                                 Widget grab_child);
342 static void     DrawSurrogate           (XmWorkspaceWidget ww);
343 static void     MoveSurrogate           (XmWorkspaceWidget ww,
344 	                                 int delta_x,
345 	                                 int delta_y);
346 static void     ResizeSurrogate         (XmWorkspaceWidget ww,
347 					 int width,
348 					 int height);
349 static Boolean  PerformSpaceWars        (XmWorkspaceWidget ww,
350 	                                 Boolean change_managed,
351 					 XEvent* event);
352 static Boolean ResolveOverlaps         (XmWorkspaceWidget ww,
353 	                                 struct SortRec* sortlist,
354 	                                 Boolean* first_selected,
355 					 XEvent* event);
356 static Boolean  MoveIfDisplaced         (XmWorkspaceWidget ww,
357 	                                 struct SortRec *list,
358 	                                 int max_x,
359 	                                 int max_y);
360 static int      MoveAToRightOfB         (XmWorkspaceWidget ww,
361 	                                 struct SortRec* movee,
362 	                                 struct SortRec *mover,
363 					 XEvent* event);
364 static void  RepositionSelectedChildren (struct SortRec* current,
365 	                                 short delta_x);
366 static struct SortRec* GetSortList      (XmWorkspaceWidget ww,
367 	                                 Boolean change_managed,
368 	                                 struct SortRec** sortmem);
369 void     RerouteLines            (XmWorkspaceWidget ww,
370 	                                 Boolean reroute_all);
371 static void     SetLineRoute            (XmWorkspaceWidget ww,
372 	                                 XmWorkspaceLine new);
373 static Boolean  DestroyLine             (XmWorkspaceWidget ww,
374 	                                 XmWorkspaceLine old);
375 void  AugmentExposureAreaForLine (XmWorkspaceWidget ww,
376 	                                 XmWorkspaceLine line);
377 static void     UnsetExposureArea       (XmWorkspaceWidget ww);
378 void     RefreshLines            (XmWorkspaceWidget ww);
379 static void     InitLineGC              (XmWorkspaceWidget ww,
380 	                                 int color);
381 static void     CvtStringToWorkspaceType(XrmValue* args,
382 	                                 Cardinal num_args,
383 	                                 XrmValue* from_val,
384 	                                 XrmValue* to_val);
385 static Boolean  StringsAreEqual         (register char * in_str,
386 	                                 register char * test_str);
387 static void 	childRelative 		(XmWorkspaceWidget ww,
388 					 XEvent *xev, int *x, int *y);
389 static Boolean Overlapping( XmWorkspaceWidget , int , int );
390 
391 void ReallocCollideLists(XmWorkspaceWidget ww);
392 static void MyInsertChild( Widget w);
393 static void MyDeleteChild( Widget w);
394 extern void MarkCommonLines(XmWorkspaceWidget ww);
395 static void dropResizedSurrogates (XmWorkspaceWidget ww, int delta_x, int delta_y, XEvent* event);
396 
397 #if (OLD_LESSTIF == 1)
398 #define GetFormConstraint(w) (&((XmFormConstraints) (w)->core.constraints)->form)
399 #else
400 #define GetFormConstraint(w) (&((XmFormConstraintPtr) (w)->core.constraints)->form)
401 #endif
402 
403 static XtResource defaultResources[] =
404 {
405     { XmNforceRoute, XmCForceRoute, XmRBoolean, sizeof(Boolean),
406       XtOffset(XmWorkspaceWidget, workspace.force_route),
407       XmRImmediate, (XtPointer) FALSE
408     },
409     {
410       XmNcollisionSpacing, XmCCollisionSpacing, XmRInt, sizeof(int),
411       XtOffset(XmWorkspaceWidget, workspace.collision_spacing),
412       XmRImmediate, (XtPointer) 20
413     },
414     {
415       XmNhaloThickness, XmCHaloThickness, XmRInt, sizeof(int),
416       XtOffset(XmWorkspaceWidget, workspace.halo_thickness),
417       XmRImmediate, (XtPointer) 2
418     },
419     {
420       XmNpositionChangeCallback, XmCPositionChangeCallback, XmRCallback, sizeof(XtPointer),
421       XtOffset(XmWorkspaceWidget, workspace.position_change_callback),
422       XmRCallback, NULL
423     },
424     {
425       XmNerrorCallback, XmCErrorCallback, XmRCallback, sizeof(XtPointer),
426       XtOffset(XmWorkspaceWidget, workspace.error_callback),
427       XmRCallback, NULL
428     },
429     {
430       XmNbackgroundCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList),
431       XtOffset(XmWorkspaceWidget, workspace.background_callback),
432       XmRPointer, (XtPointer) NULL
433     },
434     {
435       XmNdefaultActionCallback, XmCCallback, XmRCallback,
436       sizeof(XtCallbackList),
437       XtOffset(XmWorkspaceWidget, workspace.action_callback),
438       XmRPointer, (XtPointer) NULL
439     },
440     {
441       XmNselectionCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList),
442       XtOffset(XmWorkspaceWidget, workspace.selection_change_callback),
443       XmRPointer, (XtPointer) NULL
444     },
445     {
446       XmNresizeCallback, XmCCallback, XmRCallback, sizeof(XtCallbackList),
447       XtOffset(XmWorkspaceWidget, workspace.resize_callback),
448       XmRPointer, (XtPointer) NULL
449     },
450     { XmNaccentColor, XmCAccentColor, XmRPixel, sizeof(Pixel),
451       XtOffset(XmWorkspaceWidget, workspace.accent_color),
452       XmRImmediate, (XtPointer) -1
453     },
454     { XmNaccentPolicy, XmCAccentPolicy,
455       XmRWorkspaceType, sizeof(unsigned char),
456       XtOffset(XmWorkspaceWidget, workspace.accent_policy),
457       XmRImmediate, (XtPointer) XmACCENT_BACKGROUND
458     },
459     { XmNallowMovement, XmCAllowMovement, XmRBoolean, sizeof(Boolean),
460       XtOffset(XmWorkspaceWidget, workspace.movement_is_allowed),
461       XmRImmediate, (XtPointer) TRUE
462     },
463     { XmNmanhattanRoute, XmCManhattanRoute, XmRBoolean, sizeof(Boolean),
464       XtOffset(XmWorkspaceWidget, workspace.manhattan_route),
465       XmRImmediate, (XtPointer) FALSE
466     },
467     { XmNgridWidth, XmCGridWidth, XmRShort, sizeof(short),
468       XtOffset(XmWorkspaceWidget, workspace.grid_width),
469       XmRImmediate, (XtPointer) 1
470     },
471     { XmNgridHeight, XmCGridHeight, XmRShort, sizeof(short),
472       XtOffset(XmWorkspaceWidget, workspace.grid_height),
473       XmRImmediate, (XtPointer) 1
474     },
475     { XmNhorizontalAlignment, XmCAlignment, XmRAlignment, sizeof(unsigned char),
476       XtOffset(XmWorkspaceWidget, workspace.horizontal_alignment),
477       XmRImmediate, (XtPointer) XmALIGNMENT_CENTER
478     },
479     { XmNhorizontalDrawGrid, XmCDrawGrid,
480       XmRWorkspaceType, sizeof(unsigned char),
481       XtOffset(XmWorkspaceWidget, workspace.horizontal_draw_grid),
482       XmRImmediate, (XtPointer) XmDRAW_NONE
483     },
484     { XmNverticalAlignment, XmCAlignment, XmRAlignment, sizeof(unsigned char),
485       XtOffset(XmWorkspaceWidget, workspace.vertical_alignment),
486       XmRImmediate, (XtPointer) XmALIGNMENT_CENTER
487     },
488     { XmNverticalDrawGrid, XmCDrawGrid,
489       XmRWorkspaceType, sizeof(unsigned char),
490       XtOffset(XmWorkspaceWidget, workspace.vertical_draw_grid),
491       XmRImmediate, (XtPointer) XmDRAW_NONE
492     },
493     { XmNsortPolicy, XmCAlignment, XmRAlignment, sizeof(unsigned char),
494       XtOffset(XmWorkspaceWidget, workspace.sort_policy),
495       XmRImmediate, (XtPointer) XmALIGNMENT_CENTER
496     },
497     { XmNsnapToGrid, XmCSnapToGrid, XmRBoolean, sizeof(Boolean),
498       XtOffset(XmWorkspaceWidget, workspace.snap_to_grid),
499       XmRImmediate, (XtPointer) FALSE
500     },
501     { XmNlineThickness, XmCLineThickness, XmRShort, sizeof(short),
502       XtOffset(XmWorkspaceWidget, workspace.line_thickness),
503       XmRImmediate, (XtPointer) 0
504     },
505     { XmNdoubleClickInterval, XmCDoubleClickInterval, XmRInt, sizeof(int),
506       XtOffset(XmWorkspaceWidget, workspace.double_click_interval),
507       XmRImmediate, (XtPointer) 500
508     },
509     { XmNinclusionPolicy, XmCInclusionPolicy,
510       XmRWorkspaceType, sizeof(unsigned char),
511       XtOffset(XmWorkspaceWidget, workspace.inclusion_policy),
512       XmRImmediate, (XtPointer) XmINCLUDE_ALL
513     },
514     { XmNselectionPolicy, XmCSelectionPolicy,
515       XmRSelectionPolicy, sizeof(unsigned char),
516       XtOffset(XmWorkspaceWidget, workspace.selection_policy),
517       XmRImmediate, (XtPointer) XmEXTENDED_SELECT
518     },
519     { XmNoutlineType, XmCOutlineType,
520       XmRWorkspaceType, sizeof(unsigned char),
521       XtOffset(XmWorkspaceWidget, workspace.surrogate_type),
522       XmRImmediate, (XtPointer) XmOUTLINE_EACH
523     },
524     { XmNplacementPolicy, XmCPlacementPolicy, XmRChar, sizeof(unsigned char),
525       XtOffset(XmWorkspaceWidget, workspace.placement_policy),
526       XmRImmediate, (XtPointer) XmSPACE_WARS_SELECTED_STAYS
527     },
528     { XmNbutton1PressMode, XmCButton1PressMode, XmRBoolean, sizeof(Boolean),
529       XtOffset(XmWorkspaceWidget, workspace.button1_press_mode),
530       XmRImmediate, (XtPointer) True
531     },
532     { XmNlineTolerance, XmCLineTolerance, XmRInt, sizeof(int),
533       XtOffset(XmWorkspaceWidget, workspace.ltol),
534       XmRImmediate, (XtPointer) 2
535     },
536     { XmNwidgetTolerance, XmCWidgetTolerance, XmRInt, sizeof(int),
537       XtOffset(XmWorkspaceWidget, workspace.wtol),
538       XmRImmediate, (XtPointer) 1
539     },
540     { XmNlineDrawingEnabled, XmCLineDrawingEnabled, XmRBoolean, sizeof(Boolean),
541       XtOffset(XmWorkspaceWidget, workspace.line_drawing_enabled),
542       XmRImmediate, (XtPointer) True
543     },
544     { XmNselectable, XmCSelectable, XmRBoolean, sizeof(Boolean),
545       XtOffset(XmWorkspaceWidget, workspace.is_selectable),
546       XmRImmediate, (XtPointer) TRUE
547     },
548     { XmNpreventOverlap, XmCAllowOverlap, XmRBoolean, sizeof(Boolean),
549       XtOffset(XmWorkspaceWidget, workspace.check_overlap),
550       XmRImmediate, (XtPointer) FALSE
551     },
552     { XmNautoArrange, XmCAutoArrange, XmRBoolean, sizeof(Boolean),
553       XtOffset(XmWorkspaceWidget, workspace.auto_arrange),
554       XmRImmediate, (XtPointer) FALSE
555     }
556 };
557 
558 #define OFFSET_DEFAULT -1
559 static XtResource constraintResources[] = {
560     { XmNselectionCallback, XmCSelectionCallback,
561       XmRCallback, sizeof(WsCallbackList),
562       XtOffset(XmWorkspaceConstraints, workspace.select_callbacks),
563       XmRImmediate, (XtPointer) NULL
564     },
565     { XmNresizingCallback, XmCResizingCallback,
566       XmRCallback, sizeof(WsCallbackList),
567       XtOffset(XmWorkspaceConstraints, workspace.resizing_callbacks),
568       XmRImmediate, (XtPointer) NULL
569     },
570     { XmNaccentCallback, XmCAccentCallback, XmRCallback,sizeof(WsCallbackList),
571       XtOffset(XmWorkspaceConstraints, workspace.accent_callbacks),
572       XmRImmediate, (XtPointer) NULL
573     },
574     { XmNselectable, XmCSelectable, XmRBoolean, sizeof(Boolean),
575       XtOffset(XmWorkspaceConstraints, workspace.is_selectable),
576       XmRImmediate, (XtPointer) TRUE
577     },
578     { XmNselected, XmCSelected, XmRBoolean, sizeof(Boolean),
579       XtOffset(XmWorkspaceConstraints, workspace.is_selected),
580       XmRImmediate, (XtPointer) FALSE
581     },
582     { XmNid, XmCId, XmRChar, sizeof(char),
583       XtOffset(XmWorkspaceConstraints, workspace.id), XmRImmediate, (XtPointer) 0
584     },
585     { XmNallowVerticalResizing, XmCAllowResizing, XmRBoolean, sizeof(Boolean),
586       XtOffset(XmWorkspaceConstraints, workspace.is_v_resizable),
587       XmRImmediate, (XtPointer) FALSE
588     },
589     { XmNallowHorizontalResizing, XmCAllowResizing, XmRBoolean, sizeof(Boolean),
590       XtOffset(XmWorkspaceConstraints, workspace.is_h_resizable),
591       XmRImmediate, (XtPointer) FALSE
592     },
593     { XmNpinLeftRight, XmCPinSides, XmRBoolean, sizeof(Boolean),
594       XtOffset(XmWorkspaceConstraints, workspace.pin_to_sides_lr),
595       XmRImmediate, (XtPointer) FALSE
596     },
597     { XmNpinTopBottom, XmCPinSides, XmRBoolean, sizeof(Boolean),
598       XtOffset(XmWorkspaceConstraints, workspace.pin_to_sides_tb),
599       XmRImmediate, (XtPointer) FALSE
600     },
601     {
602       XmNwwTopAttachment, XmCAttachment, XmRAttachment, sizeof(unsigned char),
603       XtOffset(XmWorkspaceConstraints, workspace.att[FC_TOP].type),
604       XmRImmediate, (XtPointer) XmATTACH_NONE
605    },
606    {
607       XmNwwBottomAttachment, XmCAttachment, XmRAttachment, sizeof(unsigned char),
608       XtOffset(XmWorkspaceConstraints, workspace.att[FC_BOTTOM].type),
609       XmRImmediate, (XtPointer) XmATTACH_NONE
610    },
611    {
612       XmNwwLeftAttachment, XmCAttachment, XmRAttachment, sizeof(unsigned char),
613       XtOffset(XmWorkspaceConstraints, workspace.att[FC_LEFT].type),
614       XmRImmediate, (XtPointer) XmATTACH_NONE
615    },
616    {
617       XmNwwRightAttachment, XmCAttachment, XmRAttachment, sizeof(unsigned char),
618       XtOffset(XmWorkspaceConstraints, workspace.att[FC_RIGHT].type),
619       XmRImmediate, (XtPointer) XmATTACH_NONE
620    },
621    {
622       XmNwwTopWidget, XmCWidget, XmRWindow, sizeof(Widget),
623       XtOffset(XmWorkspaceConstraints, workspace.att[FC_TOP].w),
624       XmRWindow, (XtPointer) NULL
625    },
626    {
627       XmNwwBottomWidget, XmCWidget, XmRWindow, sizeof(Widget),
628       XtOffset(XmWorkspaceConstraints, workspace.att[FC_BOTTOM].w),
629       XmRWindow, (XtPointer) NULL
630    },
631    {
632       XmNwwLeftWidget, XmCWidget, XmRWindow, sizeof(Widget),
633       XtOffset(XmWorkspaceConstraints, workspace.att[FC_LEFT].w),
634       XmRWindow, (XtPointer) NULL
635    },
636    {
637       XmNwwRightWidget, XmCWidget, XmRWindow, sizeof(Widget),
638       XtOffset(XmWorkspaceConstraints, workspace.att[FC_RIGHT].w),
639       XmRWindow, (XtPointer) NULL
640    },
641    {
642       XmNwwTopPosition, XmCAttachment, XmRInt, sizeof(int),
643       XtOffset(XmWorkspaceConstraints, workspace.att[FC_TOP].percent),
644       XmRImmediate, (XtPointer) 0
645    },
646    {
647       XmNwwBottomPosition, XmCAttachment, XmRInt, sizeof(int),
648       XtOffset(XmWorkspaceConstraints, workspace.att[FC_BOTTOM].percent),
649       XmRImmediate, (XtPointer) 0
650    },
651    {
652       XmNwwLeftPosition, XmCAttachment, XmRInt, sizeof(int),
653       XtOffset(XmWorkspaceConstraints, workspace.att[FC_LEFT].percent),
654       XmRImmediate, (XtPointer) 0
655    },
656    {
657       XmNwwRightPosition, XmCAttachment, XmRInt, sizeof(int),
658       XtOffset(XmWorkspaceConstraints, workspace.att[FC_RIGHT].percent),
659       XmRImmediate, (XtPointer) 0
660    },
661    {
662       XmNwwTopOffset, XmCOffset, XmRInt, sizeof(int),
663       XtOffset(XmWorkspaceConstraints, workspace.att[FC_TOP].offset),
664       XmRImmediate, (XtPointer) (Dimension) OFFSET_DEFAULT
665    },
666    {
667       XmNwwBottomOffset, XmCOffset, XmRInt, sizeof(int),
668       XtOffset(XmWorkspaceConstraints, workspace.att[FC_BOTTOM].offset),
669       XmRImmediate, (XtPointer) (Dimension) OFFSET_DEFAULT
670    },
671    {
672       XmNwwLeftOffset, XmCOffset, XmRInt, sizeof(int),
673       XtOffset(XmWorkspaceConstraints, workspace.att[FC_LEFT].offset),
674       XmRImmediate, (XtPointer) (Dimension) OFFSET_DEFAULT
675    },
676 
677    {
678       XmNwwRightOffset, XmCOffset, XmRInt, sizeof(int),
679       XtOffset(XmWorkspaceConstraints, workspace.att[FC_RIGHT].offset),
680       XmRImmediate, (XtPointer) (Dimension) OFFSET_DEFAULT
681    },
682    {
683       XmNlineInvisibility, XmCLineInvisibility, XmRBoolean, sizeof(Boolean),
684       XtOffset(XmWorkspaceConstraints, workspace.line_invisibility),
685       XmRImmediate, (XtPointer) FALSE
686    },
687 };
688 
689 
690 
691 /*  This translation table maps to rubberbanding actions  */
692 static char defaultTranslations[] =
693 	"<Btn1Down>:     arm() \n\
694 	<Btn1Motion>:   drag()\n\
695 	<Key>osfHelp:   ManagerGadgetHelp()\n\
696 	<Key>:          kbd_nav()\n\
697 	<Btn1Up>:       disarm()";
698 
699 /*  Translation table to be installed on the children for selection actions  */
700 static char acceleratorTranslations[] = "#override\n\
701 	<Btn1Motion>:   move_w()\n\
702         <Btn1Down>:     select_w()\n\
703 	<Btn1Up>(2+):   select_w() release_w() select_w() release_w()\n\
704 	<Key>osfUp:     child_nav()\n\
705 	<Key>osfDown:   child_nav()\n\
706 	<Key>osfLeft:   child_nav()\n\
707 	<Key>osfRight:  child_nav()\n\
708         <Btn1Up>:       release_w()";
709 
710 static char traversalTranslations[] =
711 "<EnterWindow>:  ManagerEnter()\n\
712 <LeaveWindow>:  ManagerLeave()\n\
713 <FocusOut>:     ManagerFocusOut()\n\
714 <FocusIn>:      ManagerFocusIn()";
715 
716 
717 XtActionsRec defaultActions[] = {
718     {"arm",             (XtActionProc)Arm},
719     {"drag",            (XtActionProc)Drag},
720     {"disarm",          (XtActionProc)Disarm},
721     {"kbd_nav",         (XtActionProc)KeyboardNavigation},
722     {"select_w",        (XtActionProc)GrabSelections },
723     {"move_w",          (XtActionProc)MoveResizeSelections },
724     {"child_nav",       (XtActionProc)ChildNavigation},
725 #if RESIZE_HANDLES
726     {"grow_ne",         (XtActionProc)ResizeNE },
727     {"grow_nw",         (XtActionProc)ResizeNW },
728     {"grow_se",         (XtActionProc)ResizeSE },
729     {"grow_sw",         (XtActionProc)ResizeSW },
730     {"new_release",     (XtActionProc)NewDrop },
731 #endif
732     {"release_w",       (XtActionProc)DropSelections },
733     {"raise",		(XtActionProc)RaiseSelections },
734    { "Enter",    (XtActionProc) _XmManagerEnter },      /* Motif 1.0 */
735    { "FocusIn",  (XtActionProc) _XmManagerFocusIn },    /* Motif 1.0 */
736 };
737 
738 
739 /*
740  *  Workspace class record definition:
741  */
742 XmWorkspaceClassRec xmWorkspaceClassRec = {
743     /*
744      *  CoreClassPart:
745      */
746     {
747 	(WidgetClass)&xmFormClassRec,/* superclass                  */
748 	"XmWorkspace",                  /* class_name                   */
749 	sizeof(XmWorkspaceRec),         /* widget_size                  */
750 	ClassInitialize,                /* class_initialize             */
751 	(XtWidgetClassProc) NULL,       /* class_part_initialize        */
752 	FALSE,                          /* class_inited                 */
753 	(XtInitProc)Initialize,         /* initialize                   */
754 	(XtArgsProc) NULL,              /* initialize_hook              */
755 	(XtRealizeProc)Realize,         /* realize                      */
756 	defaultActions,                 /* actions                      */
757 	XtNumber(defaultActions),       /* num_actions                  */
758 	defaultResources,               /* resources                    */
759 	XtNumber(defaultResources),     /* num_resources                */
760 	NULLQUARK,                      /* xrm_class                    */
761 	TRUE,                           /* compress_motion              */
762 	TRUE,                           /* compress_exposure            */
763 	TRUE,                           /* compress_enterleave          */
764 	TRUE,                           /* visible_interest             */
765 	(XtWidgetProc)Destroy,          /* destroy                      */
766 	(XtWidgetProc) Resize,          /* resize                       */
767 	(XtExposeProc)Redisplay,        /* expose                       */
768 	(XtSetValuesFunc)SetValues,     /* set_values                   */
769 	(XtArgsFunc) NULL,              /* set_values_hook              */
770 	XtInheritSetValuesAlmost,       /* set_values_almost            */
771 	(XtArgsProc) NULL,              /* get_values_hook              */
772 	XtInheritAcceptFocus,           /* accept_focus                 */
773 	XtVersion,                      /* version                      */
774 	NULL,    			/* callback private             */
775 	defaultTranslations,            /* tm_table                     */
776 	XtInheritQueryGeometry,         /* query_geometry               */
777 	XtInheritDisplayAccelerator,    /* display_accelerator  */
778 	(void *) NULL,                 /* extension                    */
779     },
780     /*
781      *  CompositeClassPart:             composite_class
782      */
783     {
784 	GeometryManager,                /* geometry_manager             */
785 	(XtWidgetProc)ChangeManaged,    /* change_managed               */
786 	MyInsertChild,      		/* insert_child                 */
787 	MyDeleteChild,		        /* delete_child                 */
788 	(void *) NULL,                 /* extension                    */
789     },
790     /*
791      *  ConstraintClassPart:            constraint_class
792      */
793     {
794 	constraintResources,            /* resource list                */
795 	XtNumber(constraintResources),  /* num resources                */
796 	sizeof(XmWorkspaceConstraintRec), /* constraint size            */
797 	(XtInitProc)ConstraintInitialize, /* init proc                    */
798 	ConstraintDestroy,              /* destroy proc                 */
799 	(XtSetValuesFunc)ConstraintSetValues, /* set values proc              */
800 	(void *) NULL,                 /* extension                    */
801     },
802     /*
803      *  XmManagerClassPart:             manager_class
804      */
805     {
806 	traversalTranslations,		/* translations           */
807 	NULL,                           /* get resources                */
808 	0,                              /* num get_resources            */
809 	NULL,                           /* get_cont_resources           */
810 	0,                              /* num_get_cont_resources       */
811 	(XmParentProcessProc)NULL,      /* parent_process         */
812 	NULL,                           /* extension                    */
813     },
814    {                        /* bulletin_board_class fields */
815       FALSE,                                /* always_install_accelerators */
816       NULL,                                 /* geo_matrix_create  */
817       XmInheritFocusMovedProc,              /* focus_moved_proc   */
818       NULL,                                 /* extension          */
819    },
820 
821    {                        /* form_class fields  */
822       NULL,                 /* extension          */
823    },
824 
825     /*
826      *  XmWorkspaceClassPart:           workspace_class
827      */
828     {
829 	NULL,                           /* lineGC                       */
830 	NULL,                           /* lineGC2                      */
831 	None,                           /* hour_glass                   */
832 	None,                           /* move_cursor                  */
833 	{ None, None, None, None },     /* size_cursor[4]               */
834 	NULL,
835     }
836 };
837 
838 WidgetClass xmWorkspaceWidgetClass = (WidgetClass)&xmWorkspaceClassRec;
839 
840 /*  Subroutine: MyDeleteChild
841  *  Purpose:    Add a child and populate the collision array
842  */
MyDeleteChild(Widget w)843 static void MyDeleteChild(Widget w)
844 {
845 XmWorkspaceConstraints nc;
846     nc = WORKSPACE_CONSTRAINT(w);
847     if (!nc->workspace.line_invisibility)
848 	DeleteWidgetFromCollideList(w);
849     (*superclass->composite_class.delete_child) (w);
850 }
851 
852 /*  Subroutine: MyInsertChild
853  *  Purpose:    Add a child and populate the collision array
854  */
MyInsertChild(Widget w)855 static void MyInsertChild( Widget w)
856 {
857 Dimension new_width, new_height;
858 Dimension old_width, old_height;
859 XmWorkspaceWidget ww;
860 XtWidgetGeometry request, reply;
861 XtGeometryResult result;
862 XmWorkspaceConstraints nc;
863 
864     (*superclass->composite_class.insert_child) (w);
865     ww = (XmWorkspaceWidget)w->core.parent;
866     /* The folloing code will dynamically resize the workspace widget if a
867        a child is inserted beyond the current width and height.  If the
868        parent does not allow the resize, punt with an XtWarning.  The
869        size of the collision lists are then resized.
870      */
871     new_width = w->core.x + w->core.width;
872     old_width = XtWidth(ww);
873     new_width = MAX(new_width, old_width);
874 
875     new_height = w->core.y + w->core.height;
876     old_height = XtHeight(ww);
877     new_height = MAX(new_height, old_height);
878 
879     if ( (new_width > old_width) || (new_height > old_height) )
880         {
881 	request.width = new_width + 2*(ww->workspace.grid_width);
882 	request.height = new_height + 2*(ww->workspace.grid_width);
883 	request.request_mode = CWWidth | CWHeight;
884 	result = XtMakeGeometryRequest((Widget)ww, &request, &reply);
885 	if(result != XtGeometryYes)
886 	    {
887 	    XtWarning("Geometry Request failed in Workspace.InsertChild.");
888 	    return;
889 	    }
890 	ReallocCollideLists(ww);
891 	}
892     nc = WORKSPACE_CONSTRAINT(w);
893     if (!nc->workspace.line_invisibility)
894 	AddWidgetToCollideList(w);
895 }
896 
897 /*  Subroutine: ClassInitialize
898  *  Purpose:    Install non-standard type converters needed by this widget class
899  */
ClassInitialize()900 static void ClassInitialize( )
901 {
902     /*  Install resource converter to parse strings in Xdefaults file  */
903     XtAddConverter(XmRString, XmRWorkspaceType,
904 	           (XtConverter)CvtStringToWorkspaceType, NULL, 0);
905 }
906 
907 
908 /*  Subroutine: Initialize
909  *  Purpose:    Initialize the workspace widget instance
910  */
Initialize(XmWorkspaceWidget request,XmWorkspaceWidget new)911 static void Initialize( XmWorkspaceWidget request, XmWorkspaceWidget new )
912 {
913     XtTranslations trans_table;
914     Arg warg;
915 
916     /*  Contrary to what is promised, record is not initially cleared  */
917     new->workspace.suppress_callbacks = False;
918     new->workspace.num_selected = 0;
919     new->workspace.is_rubberbanding = FALSE;
920     new->workspace.is_moving = FALSE;
921     new->workspace.is_resizing = FALSE;
922     new->workspace.band_is_visible = FALSE;
923     new->workspace.surrogate_is_visible = FALSE;
924     new->workspace.bandGC = NULL;
925     new->workspace.badBandGC = NULL;
926     new->workspace.gridGC = NULL;
927     new->workspace.num_surrogates = 0;
928     new->workspace.lines = NULL;
929     new->workspace.num_lines = 0;
930     new->workspace.button_tracker = NULL;
931     new->workspace.time = 0;
932     new->workspace.move_cursor_installed = 0;
933     new->workspace.size_xx_installed = SZ_NONE;
934     new->workspace.auto_arrange = FALSE;
935 
936     GetRubberbandGC(new);
937     if( xmWorkspaceClassRec.workspace_class.move_cursor == None )
938 	xmWorkspaceClassRec.workspace_class.move_cursor =
939 	  XCreateFontCursor(XtDisplay(new), XC_fleur);
940     if( xmWorkspaceClassRec.workspace_class.hour_glass == None )
941 	xmWorkspaceClassRec.workspace_class.hour_glass =
942 	  XCreateFontCursor(XtDisplay(new), XC_watch);
943     if( xmWorkspaceClassRec.workspace_class.size_cursor[SZ_UL] == None )
944 	xmWorkspaceClassRec.workspace_class.size_cursor[SZ_UL] =
945 	  XCreateFontCursor(XtDisplay(new), XC_ul_angle);
946     if( xmWorkspaceClassRec.workspace_class.size_cursor[SZ_UR] == None )
947 	xmWorkspaceClassRec.workspace_class.size_cursor[SZ_UR] =
948 	  XCreateFontCursor(XtDisplay(new), XC_ur_angle);
949     if( xmWorkspaceClassRec.workspace_class.size_cursor[SZ_LL] == None )
950 	xmWorkspaceClassRec.workspace_class.size_cursor[SZ_LL] =
951 	  XCreateFontCursor(XtDisplay(new), XC_ll_angle);
952     if( xmWorkspaceClassRec.workspace_class.size_cursor[SZ_LR] == None )
953 	xmWorkspaceClassRec.workspace_class.size_cursor[SZ_LR] =
954 	  XCreateFontCursor(XtDisplay(new), XC_lr_angle);
955     new->workspace.collide_width  = new->core.width;
956     new->workspace.collide_height = new->core.height;
957     new->workspace.collide_list_x =
958 			(CollideList **)XtCalloc(new->core.width, sizeof(new->workspace.collide_list_x));
959     new->workspace.collide_list_y =
960 			(CollideList **)XtCalloc(new->core.height, sizeof(new->workspace.collide_list_y));
961     new->workspace.widget_list = NULL;
962 
963     /* Adjust wtol and ltol to be a multiple of the line thickness */
964     new->workspace.wtol = (new->workspace.wtol*new->workspace.line_thickness)/2;
965     new->workspace.ltol = new->workspace.ltol * new->workspace.line_thickness;
966 
967     UnsetExposureArea(new);
968 
969     /*
970      * calling XtSetValues inside the Initialize method is a bad
971      * thing since the SetValues method will then be called on a widget
972      * whose initialization process hasn't completed.
973      *
974      */
975 
976     trans_table = XtParseTranslationTable (defaultTranslations);
977     XtSetArg (warg, XmNtranslations, trans_table);
978 #if 0
979     // Don't check this in -MST
980     //XtSetValues ((Widget)new, &warg, 1);
981 #endif
982 }
983 
984 
985 /*  Subroutine: Realize
986  */
Realize(XmWorkspaceWidget ww,Mask * p_valueMask,XSetWindowAttributes * attributes)987 static void Realize( XmWorkspaceWidget ww, Mask* p_valueMask,
988 	             XSetWindowAttributes *attributes )
989 {
990     XmWorkspaceConstraints constraints;
991     Mask valueMask;
992     int i;
993     Widget child;
994     XMapEvent event;
995 
996     if( ww->workspace.accent_color == ((Pixel)(-1)) )
997     {
998 	Display *display = XtDisplay(ww);
999 	ww->workspace.accent_color =
1000 	  WhitePixel(display, XScreenNumberOfScreen(XtScreen(ww)));
1001     }
1002     valueMask = *p_valueMask | CWBitGravity | CWDontPropagate;
1003     attributes->bit_gravity = NorthWestGravity;
1004 
1005     attributes->do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask
1006       | KeyPressMask | KeyReleaseMask | PointerMotionMask;
1007     XtCreateWindow((Widget)ww, InputOutput, CopyFromParent, valueMask, attributes);
1008     /*  Create a background pixmap with grid lines, if warranted  */
1009     SetGridBackground(ww, FALSE);
1010     /*  Accents pre-selected children and then perform selection  */
1011     /*  Fake a map event to pass in the callback  */
1012     event.type = MapNotify;
1013     event.event = XtWindow(ww);
1014     event.send_event = TRUE;
1015     event.display = XtDisplay(ww);
1016     event.override_redirect = FALSE;
1017     for( i=0; i<ww->composite.num_children; i++ ) {
1018 	child = ww->composite.children[i];
1019 	constraints = WORKSPACE_CONSTRAINT(child);
1020 	if( constraints->workspace.is_selected )
1021 	{
1022 	    constraints->workspace.is_selected = FALSE;
1023 	    /*  SelectChild will include call to AccentChild  */
1024 	    event.window = XtWindow(child);
1025 	    SelectChild(ww, child, constraints, (XEvent *)&event);
1026 	}
1027     }
1028 }
1029 
1030 
1031 /*  Subroutine: Destroy
1032  *  Purpose:    Clean up allocated resources when the widget is destroyed.
1033  */
Destroy(XmWorkspaceWidget ww)1034 static void Destroy(  XmWorkspaceWidget ww )
1035 {
1036 CollideList **cl_ptr;
1037 CollideList *ce_ptr;
1038 CollideList *n_ce_ptr;
1039 int i;
1040 MyWidgetList *wl_ptr, *rove;
1041 
1042     if( ww->workspace.gridGC )
1043 	XtReleaseGC((Widget)ww, ww->workspace.gridGC);
1044     if( ww->workspace.bandGC )
1045 	XtReleaseGC((Widget)ww, ww->workspace.bandGC);
1046     if( ww->workspace.badBandGC )
1047 	XtReleaseGC((Widget)ww, ww->workspace.badBandGC);
1048     XtRemoveAllCallbacks((Widget)ww, XmNbackgroundCallback);
1049     XtRemoveAllCallbacks((Widget)ww, XmNdefaultActionCallback);
1050     XtRemoveAllCallbacks((Widget)ww, XmNerrorCallback);
1051     XtRemoveAllCallbacks((Widget)ww, XmNpositionChangeCallback);
1052 
1053 
1054     /* Free the collision lists */
1055     cl_ptr = ww->workspace.collide_list_x;
1056     for (i = 0; i < ww->workspace.collide_width; ++i)
1057     {
1058 	for (ce_ptr = cl_ptr[i]; ce_ptr; ce_ptr = n_ce_ptr)
1059 	{
1060 	    n_ce_ptr = ce_ptr->next;
1061 	    XtFree((char*)ce_ptr);
1062 	}
1063     }
1064     XtFree((char*)cl_ptr);
1065 
1066     cl_ptr = ww->workspace.collide_list_y;
1067     for (i = 0; i < ww->workspace.collide_height; ++i)
1068     {
1069 	for (ce_ptr = cl_ptr[i]; ce_ptr; ce_ptr = n_ce_ptr)
1070 	{
1071 	    n_ce_ptr = ce_ptr->next;
1072 	    XtFree((char*)ce_ptr);
1073 	}
1074     }
1075     XtFree((char*)cl_ptr);
1076 
1077     /*
1078      * Free the widget list
1079      */
1080     wl_ptr = ww->workspace.widget_list;
1081     while (wl_ptr) {
1082 	rove = wl_ptr->next;
1083 	XtFree((char*)wl_ptr);
1084 	wl_ptr = rove;
1085     }
1086 }
1087 
1088 #if STRETCH_SEPARATORS
1089 static void unobstructedHoriz (XmWorkspaceWidget , Widget , int *, int*);
1090 #endif
1091 
Resize(XmWorkspaceWidget ww)1092 static void Resize( XmWorkspaceWidget ww)
1093 {
1094 XmWorkspaceCallbackStruct cb;
1095 
1096     if (ww->workspace.auto_arrange)
1097 	(*superclass->core_class.resize) ((Widget)ww);
1098     if (!XtIsRealized((Widget)ww))
1099 	return;
1100 
1101 #if STRETCH_SEPARATORS
1102 {
1103 XtWidgetGeometry req;
1104 int i, bw;
1105 Widget child;
1106 XmWorkspaceConstraints cons;
1107 
1108     /*
1109      * if !auto_arrange_mode then see if there are widgets who want
1110      * want to be pinned top/bottom or left/right.  Separator widgets
1111      * in ControlPanels want this.
1112      */
1113     if (!ww->workspace.auto_arrange) {
1114 	int smallx, bigx, bigy;
1115 	XmWorkspaceGetMaxWidthHeight ((Widget)ww, &bigx, &bigy);
1116 	for (i=0; i<ww->composite.num_children; i++) {
1117 	    child = ww->composite.children[i];
1118 	    cons = WORKSPACE_CONSTRAINT(child);
1119 	    if ((cons->workspace.pin_to_sides_lr == False) &&
1120 		(cons->workspace.pin_to_sides_tb == False)) continue;
1121 	    req.x = child->core.x;
1122 	    req.y = child->core.y;
1123 	    req.width = child->core.width;
1124 	    req.height = child->core.height;
1125 	    bw = child->core.border_width;
1126 	    req.request_mode = 0;
1127 	    /* refuse to pin left,right and top.bottom */
1128 	    if (cons->workspace.pin_to_sides_lr) {
1129 		smallx = 0;
1130 		unobstructedHoriz (ww, child, &smallx, &bigx);
1131 		req.x = smallx;
1132 		req.width = bigx - smallx;
1133 		if (child->core.x > req.x) req.request_mode|= CWX;
1134 		if (child->core.width != req.width) req.request_mode|= CWWidth;
1135 	    } else if (cons->workspace.pin_to_sides_tb) {
1136 	    }
1137 	    XtConfigureWidget (child, req.x, req.y, req.width, req.height, bw);
1138 	    _GeometryManager (child, &req, NULL, False);
1139 	}
1140     }
1141 }
1142 #endif
1143 
1144     ReallocCollideLists(ww);
1145 
1146     cb.reason = XmCR_RESIZE;
1147     cb.event = NULL;
1148     cb.window = XtWindow(ww);
1149     cb.selected_widget = NULL;
1150     XtCallCallbackList ((Widget)ww, ww->workspace.resize_callback, &cb);
1151 }
1152 
1153 #if STRETCH_SEPARATORS
1154 /*
1155  * assumes the outputs are initialized by the caller.
1156  */
1157 static void
unobstructedHoriz(XmWorkspaceWidget ww,Widget cw,int * lx,int * rx)1158 unobstructedHoriz (XmWorkspaceWidget ww, Widget cw, int *lx, int *rx)
1159 {
1160 int i, minx, maxx, cw_bottom, cw_right, this_bottom, this_right;
1161 Boolean overlap;
1162 Widget child;
1163 XmWorkspaceConstraints cons;
1164 
1165     minx = *lx;
1166     maxx = *rx;
1167 
1168     cw_bottom = cw->core.y + cw->core.height;
1169     cw_right = maxx;
1170 
1171     for (i=0; i<ww->composite.num_children; i++) {
1172 	child = ww->composite.children[i];
1173 	if (child == cw) continue;
1174 	cons = WORKSPACE_CONSTRAINT(child);
1175 
1176 	this_bottom = child->core.y + child->core.height;
1177 	this_right = child->core.x + child->core.width;
1178 	if ((child->core.y > cw_bottom)||
1179 	    (cw->core.y    > this_bottom)) overlap = False;
1180 	else if (child->core.x > cw_right) overlap = False;
1181 	else overlap = True;
1182 
1183 	if (overlap) {
1184 	    minx = MAX(minx, this_right+1);
1185 	}
1186 
1187 	if ((child->core.y > cw_bottom)||
1188 	    (cw->core.y    > this_bottom)) overlap = False;
1189 	else if (child->core.x < cw->core.x) overlap = False;
1190 	else overlap = True;
1191 
1192 	if (overlap) {
1193 	    maxx = MIN(maxx, child->core.x-1);
1194 	}
1195 
1196     }
1197 
1198     *lx = minx;
1199     *rx = maxx;
1200 }
1201 #endif
1202 
1203 
1204 /*  Subroutine: Expose
1205  *  Effect:     Redraw portion of the window exposed by movement of other
1206  *              windows
1207  *  Note:       Redrawing of xor'd features must be precisely fit to
1208  *              regions newly exposed.
1209  */
1210 #define IF_MORE(a,b) if((a)>(b))(b)=(a)
1211 #define IF_LESS(a,b) if((a)<(b))(b)=(a)
Redisplay(XmWorkspaceWidget ww,XExposeEvent * event,Region region)1212 static void Redisplay( XmWorkspaceWidget ww, XExposeEvent* event,
1213 	               Region region )
1214 {
1215     int i;
1216     Boolean clip_region_set = FALSE;
1217     short line_excess;
1218 
1219     if (ww->workspace.auto_arrange)
1220 	(*superclass->core_class.expose) ((Widget)ww, (XEvent *)event, region);
1221 
1222     if( !XtIsRealized((Widget)ww) )
1223 	return;
1224     /* The user can enter events while the application is in the process of   *
1225      * rearranging its windows.  Since the server may be slow to make all of  *
1226      * the changes, events can be entered during meaningless intermediate     *
1227      * states.                                                                */
1228     /* Discard or suppress all events that represent user input.  Do not let  *
1229      * these events get queued up for later processing.  Expose events must   *
1230      * still be accepted as they are used to synchronize changes and          *
1231      * redrawing.                                                             */
1232     line_excess = ww->workspace.line_thickness / 2;
1233     /*  Augment exposure rectangle (in case it's already being augmented)  */
1234     IF_LESS(event->x - line_excess, ww->workspace.expose_left);
1235     IF_MORE(line_excess + event->width + event->x - 1,
1236 	    ww->workspace.expose_right);
1237     IF_LESS(event->y - line_excess, ww->workspace.expose_upper);
1238     IF_MORE(line_excess + event->y + event->height - 1,
1239 	    ww->workspace.expose_lower);
1240     /*  Redraw application lines if needed (uses exposure rectangle)  */
1241     if( ww->workspace.lines )
1242 	RefreshLines(ww);
1243     /*  Grid lines are handled by server as background pixmap so do nothing  */
1244     /*  Redraw rubberband bounding box, if visible  */
1245     if( ww->workspace.band_is_visible )
1246     {
1247 	if( RedisplayRectangle(ww, ww->workspace.rubberband, region,
1248 	                       clip_region_set) )
1249 	    XSetClipMask(XtDisplay(ww), ww->workspace.bandGC, None);
1250     }
1251     /*  Redraw surrogate outline(s), if visible  */
1252     else if( ww->workspace.surrogate_is_visible )
1253     {
1254 	for( i=0; i<ww->workspace.num_surrogates; i++ )
1255 	     clip_region_set =
1256 	       RedisplayRectangle(ww, &ww->workspace.surrogates[i], region,
1257 	                          clip_region_set);
1258 	/*  Restore Clipping mask to None  */
1259 	if( clip_region_set )
1260 	    XSetClipMask(XtDisplay(ww), ww->workspace.bandGC, None);
1261     }
1262     UnsetExposureArea(ww);
1263 }
1264 #undef IF_MORE
1265 #undef IF_LESS
1266 
1267 
RedisplayRectangle(XmWorkspaceWidget ww,XRectangle * rect,Region region,Boolean clip_region_set)1268 static Boolean RedisplayRectangle( XmWorkspaceWidget ww, XRectangle* rect,
1269 	                           Region region, Boolean clip_region_set )
1270 {
1271     if( XRectInRegion(region, rect->x, rect->y, rect->width, rect->height) )
1272     {
1273 	if( region && (clip_region_set == FALSE) )
1274 	    XSetRegion(XtDisplay(ww), ww->workspace.bandGC, region);
1275 	XDrawRectangles(XtDisplay(ww), XtWindow(ww), ww->workspace.bandGC,
1276 	                rect, 1);
1277 	if( region )
1278 	    return TRUE;
1279 	else
1280 	    return FALSE;
1281     }
1282     else
1283 	return FALSE;
1284 }
1285 
1286 typedef struct {
1287   char *type;
1288   char *w;
1289   char *offset;
1290   char *percent;
1291 } ResNamePlaceHolder;
1292 
1293 static ResNamePlaceHolder rname[] = {
1294  { XmNleftAttachment, XmNleftWidget, XmNleftOffset, XmNleftPosition },
1295  { XmNrightAttachment, XmNrightWidget, XmNrightOffset, XmNrightPosition },
1296  { XmNtopAttachment, XmNtopWidget, XmNtopOffset, XmNtopPosition },
1297  { XmNbottomAttachment, XmNbottomWidget, XmNbottomOffset, XmNbottomPosition }
1298 };
1299 
1300 
1301 /********************************************************************************/
1302 /* return True if this child widget can scoot over to the left edge of the      */
1303 /* Workspace without bumping into anyone. Used when switching into XmForm mode. */
1304 /* Had been assuming that coords within a few pixels of the edge guaranteed     */
1305 /* we could stretch, but then Rich S. started making vertical separators along  */
1306 /* the edges of his control panels.                                             */
1307 /********************************************************************************/
1308 static Boolean
safe2StretchLeft(XRectangle rangle[],int nkids,int kid)1309 safe2StretchLeft (XRectangle rangle[], int nkids, int kid)
1310 {
1311 int i;
1312 
1313     for (i=0; i<nkids; i++) {
1314 	if (i == kid) continue;
1315 	if (rangle[i].y > (rangle[kid].y + rangle[kid].height)) continue;
1316 	if ((rangle[i].y + rangle[i].height) < rangle[kid].y) continue;
1317 	if (rangle[i].x < rangle[kid].x) return False;
1318     }
1319     return True;
1320 }
1321 static Boolean
safe2StretchRight(XRectangle rangle[],int nkids,int kid)1322 safe2StretchRight (XRectangle rangle[], int nkids, int kid)
1323 {
1324 int i;
1325 
1326     for (i=0; i<nkids; i++) {
1327 	if (i == kid) continue;
1328 	if (rangle[i].y > (rangle[kid].y + rangle[kid].height)) continue;
1329 	if ((rangle[i].y + rangle[i].height) < rangle[kid].y) continue;
1330 	if (rangle[i].x > rangle[kid].x) return False;
1331     }
1332     return True;
1333 }
1334 
1335 
1336 
1337 /*  Subroutine: SetValues
1338  *  Purpose:    Check for changes that require other action to bring all things
1339  *              into consistency.
1340  */
SetValues(XmWorkspaceWidget current,XmWorkspaceWidget request,XmWorkspaceWidget new)1341 static Boolean SetValues( XmWorkspaceWidget current,
1342 	               XmWorkspaceWidget request,
1343 	               XmWorkspaceWidget new )
1344 {
1345     Boolean clear_window = FALSE;
1346     XmWorkspaceLine line;
1347     int i;
1348     XmWorkspaceConstraints nc;
1349     Widget child;
1350     XEvent event;
1351     Boolean reroute = False, refresh = False, redraw = False;
1352     Boolean *was_selected;
1353 
1354 #if 0
1355     /*
1356      * this is how you make lines disappear when the value of the resource changes
1357      */
1358     if ((new->workspace.line_drawing_enabled != current->workspace.line_drawing_enabled)&&
1359 	(new->workspace.line_drawing_enabled == False)) redraw = True;
1360 #endif
1361 
1362     if (new->workspace.button1_press_mode !=
1363 		current->workspace.button1_press_mode)
1364 	{
1365 	return FALSE;
1366 	}
1367 
1368     /*  If grid visibility changes or grid is visible and its form changes  */
1369     if(   (new->workspace.snap_to_grid != current->workspace.snap_to_grid)
1370        || (   new->workspace.horizontal_draw_grid
1371 	   != current->workspace.horizontal_draw_grid)
1372        || (   new->workspace.vertical_draw_grid
1373 	   != current->workspace.vertical_draw_grid)
1374        || (   (   current->workspace.snap_to_grid
1375 	       && (   (current->workspace.horizontal_draw_grid != XmDRAW_NONE)
1376 	           || (current->workspace.vertical_draw_grid != XmDRAW_NONE)))
1377 	   && (   (   new->workspace.horizontal_alignment
1378 	           != current->workspace.horizontal_alignment)
1379 	       || (   new->workspace.vertical_alignment
1380 	           != current->workspace.vertical_alignment)
1381 	       || (new->workspace.grid_width != current->workspace.grid_width)
1382 	       || (   new->workspace.grid_height
1383 	           != current->workspace.grid_height))
1384 	   )
1385        )
1386     {
1387 	/*  Create the new gridding situation  */
1388 	SetGridBackground(new, TRUE);
1389 	clear_window = TRUE;
1390     }
1391 
1392     /*  if line routing changed  */
1393     if (new->workspace.manhattan_route !=
1394 	 current->workspace.manhattan_route)
1395     {
1396 #if 11
1397 	reroute = True;
1398 #else
1399 	RerouteLines(new, TRUE);
1400 #endif
1401 	clear_window = TRUE;
1402     }
1403     if ((new->workspace.line_drawing_enabled == TRUE) &&
1404 	 (current->workspace.line_drawing_enabled == FALSE))
1405 	{
1406 	if (XtIsRealized ((Widget)new))
1407 	    XClearWindow(XtDisplay(new), XtWindow(new));
1408 #if 11
1409 	reroute = True;
1410 	refresh = True;
1411 #else
1412 	RerouteLines(new, TRUE);
1413 	RefreshLines(new);
1414 #endif
1415 	}
1416     if( (new->core.width >= current->core.width) ||
1417 	(new->core.height >= current->core.height) )
1418 	{
1419 	ReallocCollideLists(new);
1420 	}
1421 
1422     /*  Replace the old with the new  */
1423     if (clear_window)
1424 	{
1425 	if ( (new->workspace.snap_to_grid == TRUE) &&
1426 		(current->workspace.snap_to_grid == FALSE) )
1427 	    {
1428 	    new->workspace.suppress_callbacks = True;
1429 	    was_selected = (Boolean *)
1430 		XtMalloc(new->composite.num_children*sizeof(Boolean));
1431 	    for(i=0; i<new->composite.num_children; i++)
1432 		{
1433 		child = new->composite.children[i];
1434 	        nc = WORKSPACE_CONSTRAINT(child);
1435 		was_selected[i] = nc->workspace.is_selected;
1436 		if(!was_selected[i])
1437 		    SelectChild(new, child, nc, (XEvent *)NULL);
1438 		}
1439 	    if(new->composite.num_children > 0)
1440 		{
1441 		child = new->composite.children[0];
1442 		event.xbutton.x = child->core.x+1;
1443 		event.xbutton.y = child->core.y+1;
1444 		event.xbutton.window = new->composite.children[0]->core.window;
1445 		event.xbutton.time = new->workspace.time -
1446 				2*new->workspace.double_click_interval;
1447 		event.xbutton.state = 0;
1448 		GrabSelections((Widget)new, &event, NULL, NULL);
1449 		event.xbutton.x = child->core.x;
1450 		event.xbutton.y = child->core.y;
1451 		DropSelections((Widget)new, &event, NULL, NULL);
1452 		}
1453 	    for(i=0; i<new->composite.num_children; i++)
1454 		{
1455 		child = new->composite.children[i];
1456 	        nc = WORKSPACE_CONSTRAINT(child);
1457 		if(was_selected[i])
1458 		    SelectChild(new, child, nc, (XEvent *)NULL);
1459 		else
1460 		    UnselectChild(new, child, nc, (XEvent *)NULL);
1461 		}
1462 	    if(new->composite.num_children > 0)
1463 		XtFree(was_selected);
1464 	    new->workspace.suppress_callbacks = False;
1465 	    }
1466 	if (XtWindow(new))
1467 	    XClearWindow(XtDisplay(new), XtWindow(new));
1468 	if(new->workspace.lines)
1469 	    {
1470 	    line = new->workspace.lines;
1471 	    while(line)
1472 		{
1473 		line->is_to_be_drawn = TRUE;
1474 		line = line->next;
1475 		}
1476 	    }
1477 	redraw =  TRUE;
1478 	}
1479     if ((new->bulletin_board.allow_overlap !=
1480 	 current->bulletin_board.allow_overlap) &&
1481 	!new->bulletin_board.allow_overlap)
1482 	{
1483 	/*
1484 	 * Pretend like all are newly managed so space wars works
1485 	 */
1486 	Boolean moved;
1487 	for( i=0; i<new->composite.num_children; i++ )
1488 	    {
1489 	    child = new->composite.children[i];
1490 	    nc = WORKSPACE_CONSTRAINT(child);
1491 	    nc->workspace.is_newly_managed = True;
1492 	    }
1493 	moved = PerformSpaceWars(new, TRUE, NULL);
1494 	for( i=0; i<new->composite.num_children; i++ )
1495 	    {
1496 	    child = new->composite.children[i];
1497 	    nc = WORKSPACE_CONSTRAINT(child);
1498 	    nc->workspace.is_newly_managed = False;
1499 	    }
1500 	/*
1501 	 * If we move anything, be sure to reroute the lines
1502 	 */
1503 	if( moved && new->workspace.lines )
1504 	    {
1505 	    if (XtWindow(new))
1506 		XClearWindow(XtDisplay(new), XtWindow(new));
1507 #if 11
1508 	    reroute = True;
1509 	    refresh = True;
1510 #else
1511 	    RerouteLines(new, FALSE);
1512 	    RefreshLines(new);
1513 #endif
1514 	    }
1515 	}
1516 
1517     /*
1518      * form attachments will be set all at once when XmNautoArrange is set to
1519      * True.  Any settings which have been put into XmNwwTopAttachment etc will
1520      * be used first.  Any settings == XmATTACH_NONE will be calculated and set
1521      * to XmATTACH_POSITION.  It would be nice to have a real way to specify
1522      * the attachments.  One possibility would be sorting the edges.
1523      */
1524     if (current->workspace.auto_arrange != new->workspace.auto_arrange) {
1525 	int n, wwidth, wheight, bw;
1526 	int bottomp, rightp, leftp, topp, fbase;
1527 	XmFormAttachmentRec *att;
1528 	Arg args[20];
1529 
1530 	/*
1531 	 * We're switching into XmForm mode... do attachment calculations.
1532 	 */
1533 	if (new->workspace.auto_arrange) {
1534 
1535 	    int maxw, maxh;
1536 	    XRectangle *rangle;
1537 	    int nkids = current->composite.num_children;
1538 	    wwidth = new->core.width;
1539 	    wheight = new->core.height;
1540 	    rangle = (XRectangle *)malloc((1+nkids) * sizeof(XRectangle));
1541 
1542 	    /*
1543 	     * must hang on to dimensions before superclass get its hands on them
1544 	     * must also inform the form widget of the child's preferred dimensions.
1545 	     * Ordinarily a form widget would have this info, but the form side of
1546 	     * personality has been asleep and needs to be roused carefully.
1547 	     */
1548 	    for (i=0; i<current->composite.num_children; i++) {
1549 #if (OLD_LESSTIF == 1)
1550 		XmFormConstraintPart* formcons;
1551 #else
1552 		XmFormConstraint formcons;
1553 #endif
1554 		rangle[i].x = current->composite.children[i]->core.x;
1555 		rangle[i].y = current->composite.children[i]->core.y;
1556 		rangle[i].width = current->composite.children[i]->core.width;
1557 		rangle[i].height = current->composite.children[i]->core.height;
1558 
1559 		formcons = GetFormConstraint(current->composite.children[i]);
1560 		formcons->preferred_width = rangle[i].width;
1561 		formcons->preferred_height = rangle[i].height;
1562 	    }
1563 	    (*superclass->composite_class.change_managed) ((Widget)current);
1564 
1565 
1566 	    /*
1567 	     * From this point on, must not use core.{x,y,width,height} because
1568 	     * the superclass might have touched these values.  Can only use
1569 	     * values in rangle.
1570 	     */
1571 	    XtVaGetValues ((Widget)current, XmNfractionBase, &fbase, NULL);
1572 	    XmWorkspaceGetMaxWidthHeight  ((Widget)current, &maxw, &maxh);
1573 	    for (i=0; i<current->composite.num_children; i++) {
1574 		int j;
1575 		int x2use, y2use, w2use, h2use;
1576 		float posfl, wfactor, hfactor;
1577 		struct {
1578 		    int top,left,right,bottom;
1579 		} limits;
1580 
1581 		posfl = fbase * 0.05;
1582 		limits.top = limits.left = (int)posfl;
1583 		posfl = fbase * 0.95;
1584 		limits.right = limits.bottom = (int)posfl;
1585 
1586 		child = current->composite.children[i];
1587 		nc = WORKSPACE_CONSTRAINT(child);
1588 		att = nc->workspace.att;
1589 
1590 		x2use = rangle[i].x;
1591 		y2use = rangle[i].y;
1592 		w2use = rangle[i].width;
1593 		h2use = rangle[i].height;
1594 
1595 		nc->workspace.orig_base.x = x2use;
1596 		nc->workspace.orig_base.y = y2use;
1597 		nc->workspace.orig_base.width = w2use;
1598 		nc->workspace.orig_base.height = h2use;
1599 
1600 		/*
1601 		 * perform accurate rounding
1602 		 */
1603 		if (wwidth <= 0) wwidth = 1;
1604 		if (wheight <= 0) wheight = 1;
1605 		wfactor = (float)fbase/(float)wwidth;
1606 		hfactor = (float)fbase/(float)wheight;
1607 
1608 		posfl = (float)x2use * wfactor;
1609 		leftp = (int)posfl;
1610 		if ((posfl - (float)leftp) >= 0.5) leftp++;
1611 
1612 		posfl = (float)y2use * hfactor;
1613 		topp = (int)posfl;
1614 		if ((posfl - (float)topp) >= 0.5) topp++;
1615 
1616 		posfl = (float)(x2use+w2use) * wfactor;
1617 		rightp = (int)posfl;
1618 		if ((posfl - (float)rightp) >= 0.5) rightp++;
1619 
1620 		posfl = (float)(y2use+h2use) * hfactor;
1621 		bottomp = (int)posfl;
1622 		if ((posfl - (float)bottomp) >= 0.5) bottomp++;
1623 
1624 		/*
1625 		 * ASSERT (rightp >= (leftp+1))
1626 		 * ASSERT (bottomp >= (topp+1))
1627 		 * You would expect not to have to do this but because of
1628 		 * an old bug it might be possible to find a dimension set to
1629 		 * something like 0 or 1 in the .cfg file.  So this prevents
1630 		 * pages and pages of motif warnings from XmForm.
1631 		 */
1632 		if (rightp < (leftp+1)) rightp = leftp+1;
1633 		if (bottomp < (topp+1)) bottomp = topp+1;
1634 
1635 
1636 
1637 		n = 0;
1638 
1639 		/*
1640 		 * separator decorators have this resource set so that they will
1641 		 * be stretched on a window resize.
1642 		 */
1643 		if ((nc->workspace.pin_to_sides_lr) && (!nc->workspace.pin_to_sides_tb)) {
1644 
1645 		    /*
1646 		     * The separator is currently going all the way across
1647 		     */
1648 		    if ((leftp<=limits.left)&&(safe2StretchLeft(rangle, nkids, i)) &&
1649 			(rightp>=limits.right)&&(safe2StretchRight(rangle, nkids, i))) {
1650 			for (j=0; j<4; j++) {
1651 			    if (att[j].offset != OFFSET_DEFAULT) {
1652 				XtSetArg (args[n], rname[j].offset, att[j].offset); n++;
1653 			    } else {
1654 				XtSetArg (args[n], rname[j].offset, 0); n++;
1655 			    }
1656 			    if (att[j].type != XmATTACH_NONE) {
1657 				XtSetArg (args[n], rname[j].type, att[j].type); n++;
1658 				XtSetArg (args[n], rname[j].w, att[j].w); n++;
1659 				XtSetArg (args[n], rname[j].percent, att[j].percent); n++;
1660 			    } else {
1661 				switch (j) {
1662 				    case FC_LEFT:
1663 					XtSetArg (args[n],
1664 					  XmNleftAttachment, XmATTACH_FORM); n++;
1665 					break;
1666 				    case FC_RIGHT:
1667 					XtSetArg (args[n],
1668 					  XmNrightAttachment, XmATTACH_FORM); n++;
1669 					break;
1670 				    case FC_TOP:
1671 					XtSetArg (args[n],
1672 					  XmNtopAttachment, XmATTACH_POSITION); n++;
1673 					XtSetArg (args[n], XmNtopPosition, topp); n++;
1674 					break;
1675 				    case FC_BOTTOM:
1676 					break;
1677 				}
1678 			    }
1679 			}
1680 		    } else if ((leftp<=limits.left)&&(safe2StretchLeft(rangle, nkids, i))) {
1681 			/*
1682 			 * The separator goes from the left side, part way across.
1683 			 */
1684 			for (j=0; j<4; j++) {
1685 			    if (att[j].offset != OFFSET_DEFAULT) {
1686 				XtSetArg (args[n], rname[j].offset, att[j].offset); n++;
1687 			    } else {
1688 				XtSetArg (args[n], rname[j].offset, 0); n++;
1689 			    }
1690 			    if (att[j].type != XmATTACH_NONE) {
1691 				XtSetArg (args[n], rname[j].type, att[j].type); n++;
1692 				XtSetArg (args[n], rname[j].w, att[j].w); n++;
1693 				XtSetArg (args[n], rname[j].percent, att[j].percent); n++;
1694 			    } else {
1695 				switch (j) {
1696 				    case FC_LEFT:
1697 					XtSetArg (args[n],
1698 					    XmNleftAttachment, XmATTACH_FORM); n++;
1699 				        break;
1700 				    case FC_RIGHT:
1701 		    			XtSetArg (args[n],
1702 					    XmNrightAttachment, XmATTACH_POSITION); n++;
1703 					XtSetArg (args[n], XmNrightPosition, rightp); n++;
1704 				        break;
1705 				    case FC_TOP:
1706 					XtSetArg (args[n],
1707 					    XmNtopAttachment, XmATTACH_POSITION); n++;
1708 					XtSetArg (args[n], XmNtopPosition, topp); n++;
1709 				        break;
1710 				    case FC_BOTTOM:
1711 				        break;
1712 				}
1713 			    }
1714 			}
1715 		    } else if ((rightp>=limits.right)&&(safe2StretchRight(rangle, nkids, i))) {
1716 			/*
1717 			 * The separator goes from somewhere in the middle all the
1718 			 * way to the right side.
1719 			 */
1720 			for (j=0; j<4; j++) {
1721 			    if (att[j].offset != OFFSET_DEFAULT) {
1722 				XtSetArg (args[n], rname[j].offset, att[j].offset); n++;
1723 			    } else {
1724 				XtSetArg (args[n], rname[j].offset, 0); n++;
1725 			    }
1726 			    if (att[j].type != XmATTACH_NONE) {
1727 				XtSetArg (args[n], rname[j].type, att[j].type); n++;
1728 				XtSetArg (args[n], rname[j].w, att[j].w); n++;
1729 				XtSetArg (args[n], rname[j].percent, att[j].percent); n++;
1730 			    } else {
1731 				switch (j) {
1732 				    case FC_LEFT:
1733 		    			XtSetArg (args[n],
1734 					    XmNleftAttachment, XmATTACH_POSITION); n++;
1735 					XtSetArg (args[n], XmNleftPosition, leftp); n++;
1736 				        break;
1737 				    case FC_RIGHT:
1738 		    			XtSetArg (args[n],
1739 					    XmNrightAttachment, XmATTACH_FORM); n++;
1740 				        break;
1741 				    case FC_TOP:
1742 		    			XtSetArg (args[n],
1743 					    XmNtopAttachment, XmATTACH_POSITION); n++;
1744 					XtSetArg (args[n], XmNtopPosition, topp); n++;
1745 				        break;
1746 				    case FC_BOTTOM:
1747 				        break;
1748 				}
1749 			    }
1750 			}
1751 		    } else {
1752 			/*
1753 			 * The separator starts somewhere in the middle and ends
1754 			 * somewhere in the middle
1755 			 */
1756 			for (j=0; j<4; j++) {
1757 			    if (att[j].offset != OFFSET_DEFAULT) {
1758 				XtSetArg (args[n], rname[j].offset, att[j].offset); n++;
1759 			    } else {
1760 				XtSetArg (args[n], rname[j].offset, 0); n++;
1761 			    }
1762 			    if (att[j].type != XmATTACH_NONE) {
1763 				XtSetArg (args[n], rname[j].type, att[j].type); n++;
1764 				XtSetArg (args[n], rname[j].w, att[j].w); n++;
1765 				XtSetArg (args[n], rname[j].percent, att[j].percent); n++;
1766 			    } else {
1767 				switch (j) {
1768 				    case FC_LEFT:
1769 		    			XtSetArg (args[n],
1770 					    XmNleftAttachment, XmATTACH_POSITION); n++;
1771 					XtSetArg (args[n], XmNleftPosition, leftp); n++;
1772 				        break;
1773 				    case FC_RIGHT:
1774 		    			XtSetArg (args[n],
1775 					    XmNrightAttachment, XmATTACH_POSITION); n++;
1776 					XtSetArg (args[n], XmNrightPosition, rightp); n++;
1777 				        break;
1778 				    case FC_TOP:
1779 		    			XtSetArg (args[n],
1780 					    XmNtopAttachment, XmATTACH_POSITION); n++;
1781 					XtSetArg (args[n], XmNtopPosition, topp); n++;
1782 				        break;
1783 				    case FC_BOTTOM:
1784 				        break;
1785 				}
1786 			    }
1787 			}
1788 		    }
1789 		} else {
1790 		    /*
1791 		     * handle everything except separator decorators.
1792 		     */
1793 		    for (j=0; j<4; j++) {
1794 			    if (att[j].offset != OFFSET_DEFAULT) {
1795 				XtSetArg (args[n], rname[j].offset, att[j].offset); n++;
1796 			    } else {
1797 				XtSetArg (args[n], rname[j].offset, 0); n++;
1798 			    }
1799 			if (att[j].type != XmATTACH_NONE) {
1800 			    XtSetArg (args[n], rname[j].type, att[j].type); n++;
1801 			    XtSetArg (args[n], rname[j].w, att[j].w); n++;
1802 			    XtSetArg (args[n], rname[j].percent, att[j].percent); n++;
1803 			} else {
1804 			    switch (j) {
1805 				case FC_LEFT:
1806 				    XtSetArg (args[n],
1807 					XmNleftAttachment, XmATTACH_POSITION); n++;
1808 				    XtSetArg (args[n], XmNleftPosition, leftp); n++;
1809 				    break;
1810 				case FC_TOP:
1811 				    XtSetArg (args[n],
1812 					XmNtopAttachment, XmATTACH_POSITION); n++;
1813 				    XtSetArg (args[n], XmNtopPosition, topp); n++;
1814 				    break;
1815 				case FC_RIGHT:
1816 				    if (nc->workspace.pin_to_sides_lr) {
1817 					XtSetArg (args[n],
1818 					    XmNrightAttachment, XmATTACH_POSITION); n++;
1819 					XtSetArg (args[n],
1820 					    XmNrightPosition, rightp); n++;
1821 				    } else {
1822 					XtSetArg (args[n],
1823 					    XmNrightAttachment, XmATTACH_NONE); n++;
1824 				    }
1825 				    break;
1826 				case FC_BOTTOM:
1827 				    if (nc->workspace.pin_to_sides_tb) {
1828 					XtSetArg (args[n],
1829 					    XmNbottomAttachment, XmATTACH_POSITION); n++;
1830 					XtSetArg (args[n],
1831 					    XmNbottomPosition, bottomp); n++;
1832 				    } else {
1833 					XtSetArg (args[n],
1834 					    XmNbottomAttachment, XmATTACH_NONE); n++;
1835 				    }
1836 				    break;
1837 			    }
1838 			}
1839 		    }
1840 		}
1841 
1842 		XtSetValues (child, args, n);
1843 
1844 		bw = child->core.border_width;
1845 		XtResizeWidget (child, rangle[i].width, rangle[i].height, bw);
1846 		if (nc->workspace.is_selected) {
1847 		    UnselectChild(new, child, nc, (XEvent *)NULL);
1848 		}
1849 
1850 	    }
1851 	    free(rangle);
1852 	} else {
1853 	    /*
1854 	     * We're switching from XmForm mode to Workspace mode.
1855 	     */
1856 	    Boolean tmp;
1857 	    XtWidgetGeometry req;
1858 	    tmp = current->bulletin_board.allow_overlap;
1859 	    current->bulletin_board.allow_overlap = True;
1860 	    for (i=0; i<current->composite.num_children; i++) {
1861 		child = current->composite.children[i];
1862 		XtVaSetValues (child, XmNtopAttachment, XmATTACH_NONE,
1863 		  XmNleftAttachment, XmATTACH_NONE, XmNbottomAttachment,
1864 		  XmATTACH_NONE, XmNrightAttachment, XmATTACH_NONE, NULL);
1865 	    }
1866 	    for (i=0; i<current->composite.num_children; i++) {
1867 		child = current->composite.children[i];
1868 		nc = WORKSPACE_CONSTRAINT(child);
1869 		req.request_mode = CWX|CWY|CWWidth|CWHeight;
1870 		req.x = nc->workspace.orig_base.x;
1871 		req.y = nc->workspace.orig_base.y;
1872 		req.width = nc->workspace.orig_base.width;
1873 		req.height = nc->workspace.orig_base.height;
1874 		bw = child->core.border_width;
1875 		XtResizeWidget (child, req.width, req.height, bw);
1876 		XtMoveWidget (child, req.x, req.y);
1877 		/*XtConfigureWidget (child, req.x, req.y, req.width, req.height, bw);*/
1878             	_GeometryManager (child, &req, NULL, False);
1879 	    }
1880 	    current->workspace.orig_width = 0;
1881 	    current->workspace.orig_height = 0;
1882 	    current->bulletin_board.allow_overlap = tmp;
1883 	}
1884     }
1885 
1886 #if 11
1887     if (reroute)
1888 	RerouteLines(new, TRUE);
1889     if (refresh)
1890 	RefreshLines(new);
1891 #endif
1892 /*  Watch out for line management %%  */
1893     return redraw;
1894 }
1895 
1896 
1897 /*  Subroutine: ChangeManaged
1898  *  Effect:     Makes sure everyone is in their places before the curtain opens
1899  */
ChangeManaged(XmWorkspaceWidget ww)1900 static void ChangeManaged( XmWorkspaceWidget ww )
1901 {
1902     XmWorkspaceConstraints nc=0;
1903     Widget child=0;
1904     int i;
1905     Boolean resolve_overlap = FALSE;
1906     Boolean new_node = FALSE;
1907 #if (OLD_LESSTIF == 1)
1908     XmFormConstraintPart *formcon;
1909 #else
1910     XmFormConstraint formcon;
1911 #endif
1912 
1913     if (ww->workspace.auto_arrange)
1914 	(*superclass->composite_class.change_managed) ((Widget)ww);
1915     for( i=0; i<ww->composite.num_children; i++ )
1916     {
1917 	child = ww->composite.children[i];
1918 	nc = WORKSPACE_CONSTRAINT(child);
1919 	if (((child->core.managed) && (!nc->workspace.is_managed)) &&
1920 	     (!ww->workspace.auto_arrange))
1921 	{
1922 
1923 	    /*
1924 	     * Certainly don't want to do this if auto_arrange==True
1925 	     */
1926 	   formcon = GetFormConstraint(child);
1927 #if (OLD_LESSTIF == 1)
1928 	   formcon->atta[0].type = XmATTACH_NONE;
1929 	   formcon->atta[1].type = XmATTACH_NONE;
1930 	   formcon->atta[2].type = XmATTACH_NONE;
1931 	   formcon->atta[3].type = XmATTACH_NONE;
1932 #else
1933 	   formcon->att[0].type = XmATTACH_NONE;
1934 	   formcon->att[1].type = XmATTACH_NONE;
1935 	   formcon->att[2].type = XmATTACH_NONE;
1936 	   formcon->att[3].type = XmATTACH_NONE;
1937 #endif
1938 
1939 	    new_node = True;
1940 #if 1
1941 	    /*
1942 	     * grow the workspace if the child won't fit
1943 	     */
1944 	    {
1945 		XtWidgetGeometry compromise,req; int newdim;
1946 		XtGeometryResult result;
1947 		newdim = child->core.x + child->core.width;
1948 		req.request_mode = 0;
1949 		if (newdim > ww->core.width) {
1950 		    req.request_mode|= CWWidth;
1951 		    req.width = newdim + 100;
1952 		}
1953 		newdim = child->core.y + child->core.height;
1954 		if (newdim > ww->core.height) {
1955 		    req.request_mode = CWHeight;
1956 		    req.height = newdim + 100;
1957 		}
1958 		if (req.request_mode) {
1959 		    result = XtMakeGeometryRequest ((Widget)ww, &req, &compromise);
1960 		    if (result != XtGeometryNo) {
1961 			if (result == XtGeometryAlmost) {
1962 			    XtMakeGeometryRequest ((Widget)ww, &compromise, NULL);
1963 			}
1964 			ReallocCollideLists(ww);
1965 		    }
1966 		}
1967 	    }
1968 #endif
1969 	    ChangeWidgetInCollideList(ww, child);
1970 
1971 
1972 
1973 	    nc->workspace.x_left = child->core.x;
1974 	    nc->workspace.x_right = child->core.x + child->core.width - 1;
1975 	    nc->workspace.y_upper = child->core.y;
1976 	    nc->workspace.y_lower = child->core.y + child->core.height - 1;
1977 	    nc->workspace.x_delta = nc->workspace.y_delta = 0;
1978 	    nc->workspace.x_center = child->core.x + (child->core.width / 2);
1979 	    nc->workspace.y_center = child->core.y + (child->core.height / 2);
1980 	    /*  Register initial sort position (before any adjustment)  */
1981 	    if( ww->workspace.sort_policy == XmALIGNMENT_BEGINNING )
1982 	        nc->workspace.x_index = nc->workspace.x_left;
1983 	    else if( ww->workspace.sort_policy == XmALIGNMENT_CENTER )
1984 	        nc->workspace.x_index = nc->workspace.x_center;
1985 	    else
1986 	        nc->workspace.x_index = nc->workspace.x_right;
1987 	    /*  Adjust position to constraint-grid location  */
1988 	    if( ww->workspace.snap_to_grid )
1989 	    {
1990 	        /*  Move into closest grid position (any direction)  */
1991 	        SnapToGrid(ww, child, nc, FALSE);
1992 	        /*  If no other adjustments will be made, move it now  */
1993 	        if( ww->bulletin_board.allow_overlap )
1994 	            MoveChild(ww, child, nc);
1995 	    }
1996 	    if( ww->bulletin_board.allow_overlap )
1997 	        nc->workspace.is_newly_managed = FALSE;
1998 	    else
1999 	    {
2000 	        nc->workspace.is_newly_managed = TRUE;
2001 	        resolve_overlap = TRUE;
2002 	    }
2003 	}
2004 	nc->workspace.is_managed = child->core.managed;
2005     }
2006 
2007     /*  If a child has been added, make sure it doesn't add any overlaps  */
2008     if ((resolve_overlap) && (!ww->workspace.auto_arrange))
2009 	(void)PerformSpaceWars(ww, TRUE, NULL);
2010     /* %% Check window size and extents for need to resize */
2011     if( ww->workspace.lines )
2012 	{
2013 	if ( (!(nc->workspace.is_managed && child->core.managed)) ||
2014 		new_node )
2015 	    {
2016 	    /*  Establish new routes for lines that were moved  */
2017 	    RerouteLines(ww, FALSE);
2018 	    if( ww->workspace.expose_left < ww->workspace.expose_right )
2019 		{
2020 		if (XtWindow(ww))
2021 		    XClearArea(XtDisplay(ww), XtWindow(ww),
2022 			       ww->workspace.expose_left,
2023 			       ww->workspace.expose_upper,
2024 			       ww->workspace.expose_right -
2025 			       ww->workspace.expose_left,
2026 			       ww->workspace.expose_lower -
2027 			       ww->workspace.expose_upper, False);
2028 		}
2029 	    RefreshLines(ww);
2030 	    }
2031 	}
2032 }
2033 
2034 /*
2035  *  Subroutine: XtGeometryResult
2036  *  Purpose:    Get informed of a change in a child widget's geometry.
2037  */
2038 static XtGeometryResult
GeometryManager(Widget w,XtWidgetGeometry * request,XtWidgetGeometry * reply)2039 GeometryManager( Widget w, XtWidgetGeometry *request, XtWidgetGeometry *reply )
2040 {
2041     return _GeometryManager (w, request, reply, True);
2042 }
2043 
2044 /*
2045  * Expose the ability to move standins programatically.  Using
2046  * XtSetValues() doesn't work for this because our GeometryManager
2047  * is never called.  Using XtMakeGeometryRequest() almost does the job
2048  * but that function is doc'ed as being only for widget writers
2049  * to use and not for application programmers.  In addition to
2050  * changing x,y location of standins, we need to ensure that
2051  * line routing is handled at the same time.
2052  */
XmWorkspaceSetLocation(Widget w,Dimension x,Dimension y)2053 Boolean XmWorkspaceSetLocation (Widget w, Dimension x, Dimension y)
2054 {
2055     XmWorkspaceWidget ww;
2056     XtWidgetGeometry req;
2057     XtGeometryResult result;
2058 
2059     ww = (XmWorkspaceWidget)XtParent(w);
2060     req.request_mode = 0;
2061 
2062 
2063     /* Using 32767 is a hack.  I just happen to know that that it's
2064      * what we use elsewhere in dxui to indicate no-change.
2065      */
2066     if (x != 32767) {
2067 	req.request_mode|= CWX;
2068 	req.x = x;
2069     }
2070     if (y != 32767) {
2071 	req.request_mode|= CWY;
2072 	req.y = y;
2073     }
2074 
2075     if (req.request_mode == 0) return TRUE;
2076 	result = XtMakeGeometryRequest (w, &req, NULL);
2077     return (result == XtGeometryYes);
2078 }
2079 
2080 static XtGeometryResult
_GeometryManager(Widget w,XtWidgetGeometry * request,XtWidgetGeometry * reply,Boolean adjust_constraints)2081 _GeometryManager( Widget w, XtWidgetGeometry *request,
2082 	XtWidgetGeometry *reply, Boolean adjust_constraints )
2083 {
2084 XmWorkspaceWidget ww;
2085 XmWorkspaceConstraints constraints = WORKSPACE_CONSTRAINT(w);
2086 Boolean resolve_overlap = False;
2087 XtGeometryResult retval;
2088 
2089     ww = (XmWorkspaceWidget)XtParent(w);
2090 
2091     if ((adjust_constraints) && (ww->workspace.auto_arrange))
2092 	retval = (*superclass->composite_class.geometry_manager) (w, request, reply);
2093     else
2094 	retval = XtGeometryYes;
2095 
2096     if ((retval == XtGeometryYes)||(retval == XtGeometryDone)) {
2097 	if (request->request_mode & CWWidth)
2098 	    w->core.width = request->width;
2099 	if (request->request_mode & CWHeight)
2100 	    w->core.height = request->height;
2101 	if (request->request_mode & CWBorderWidth)
2102 	    w->core.border_width = request->border_width;
2103 	if (request->request_mode & CWX)
2104 	    w->core.x = request->x;
2105 	if (request->request_mode & CWY)
2106 	    w->core.y = request->y;
2107     }
2108 
2109     /*if ((retval == XtGeometryNo) || (retval == XtGeometryAlmost)) return retval;*/
2110 
2111 #if 1
2112     /*
2113      * grow the workspace if the child won't fit
2114      */
2115     {
2116 	XtWidgetGeometry compromise,req; int newdim;
2117 	XtGeometryResult result;
2118 	newdim = w->core.x + w->core.width;
2119 	req.request_mode = 0;
2120 	if (newdim > ww->core.width) {
2121 	    req.request_mode|= CWWidth;
2122 	    req.width = newdim + 10;
2123 	}
2124 	newdim = w->core.y + w->core.height;
2125 	if (newdim > ww->core.height) {
2126 	    req.request_mode = CWHeight;
2127 	    req.height = newdim + 10;
2128 	}
2129 	if (req.request_mode) {
2130 	    result = XtMakeGeometryRequest ((Widget)ww, &req, &compromise);
2131 	    if (result != XtGeometryNo) {
2132 		if (result == XtGeometryAlmost) {
2133 		    XtMakeGeometryRequest ((Widget)ww, &compromise, NULL);
2134 		}
2135 		ReallocCollideLists(ww);
2136 	    }
2137 	}
2138     }
2139 #endif
2140 
2141     constraints->workspace.x_left = w->core.x;
2142     constraints->workspace.x_right = w->core.x + w->core.width - 1;
2143     constraints->workspace.y_upper = w->core.y;
2144     constraints->workspace.y_lower = w->core.y + w->core.height - 1;
2145     constraints->workspace.x_center = w->core.x + (w->core.width/2);
2146     constraints->workspace.y_center = w->core.y + (w->core.height/2);
2147     constraints->workspace.x_delta = 0;
2148     constraints->workspace.y_delta = 0;
2149     constraints->workspace.x_center = w->core.x + (w->core.width / 2);
2150     constraints->workspace.y_center = w->core.y + (w->core.height / 2);
2151 
2152     /*  Register initial sort position (before any adjustment)  */
2153     if( ww->workspace.sort_policy == XmALIGNMENT_BEGINNING )
2154         constraints->workspace.x_index = constraints->workspace.x_left;
2155     else if( ww->workspace.sort_policy == XmALIGNMENT_CENTER )
2156         constraints->workspace.x_index = constraints->workspace.x_center;
2157     else
2158         constraints->workspace.x_index = constraints->workspace.x_right;
2159     /*  Adjust position to constraint-grid location  */
2160     if( ww->workspace.snap_to_grid )
2161         {
2162         /*  Move into closest grid position (any direction)  */
2163         SnapToGrid(ww, w, constraints, FALSE);
2164         /*  If no other adjustments will be made, move it now  */
2165         if( ww->bulletin_board.allow_overlap )
2166             MoveChild(ww, w, constraints);
2167         }
2168     if( ww->bulletin_board.allow_overlap )
2169         constraints->workspace.is_newly_managed = FALSE;
2170     else
2171         {
2172         constraints->workspace.is_newly_managed = TRUE;
2173         resolve_overlap = TRUE;
2174         }
2175     if ((resolve_overlap) && (!ww->workspace.auto_arrange))
2176 	{
2177 	(void)PerformSpaceWars(ww, TRUE, NULL);
2178 	}
2179 
2180     if (!constraints->workspace.line_invisibility)
2181 	ChangeWidgetInCollideList(ww, w);
2182 
2183     return retval;
2184 }
2185 
2186 
2187 /*  Subroutine: ConstraintInitialize
2188  *  Purpose:    Update parameters in a child widget's constraints (except
2189  *              those pertaining to size) and install the accerators.
2190  */
ConstraintInitialize(Widget req,Widget new)2191 static void ConstraintInitialize( Widget req, Widget new )
2192 {
2193     XtTranslations acc_table;
2194 
2195     /*  Unitialized fields are not automatically cleared!  */
2196     XmWorkspaceConstraints constraints = WORKSPACE_CONSTRAINT(new);
2197     constraints->workspace.source_lines = NULL;
2198     constraints->workspace.destination_lines = NULL;
2199     constraints->workspace.will_be_selected = FALSE;
2200     constraints->workspace.is_accented = FALSE;
2201     constraints->workspace.is_managed = FALSE;
2202 
2203     /*  Accelerators are per-instance and must be installed at runtime  */
2204     /*
2205      * on behalf of vpe pages... don't put accelerators on the child
2206      * if the child is another workspace.
2207      */
2208     if (XtClass(new) != xmWorkspaceWidgetClass) {
2209 	acc_table = XtParseTranslationTable(acceleratorTranslations);
2210 	XtOverrideTranslations (new, acc_table);
2211     } else {
2212 
2213 	/*
2214 	 * FIXME:  there are 2 resources with the same name: XmNselectable (of the
2215 	 * workspace itself) and XmNselectable (a constraint provided by the workspace
2216 	 * to its children).  That means there is no way to set the resource on
2217 	 * workspace whose parent is also a workspace.
2218 	 */
2219 	constraints->workspace.is_selectable = False;
2220     }
2221 }
2222 
2223 
2224 /*  Subroutine: ConstraintDestroy
2225  *  Purpose:    Free space allocated for use in the child's constraints
2226  */
ConstraintDestroy(Widget w)2227 static void ConstraintDestroy( Widget w )
2228 {
2229 XmWorkspaceWidget ww = (XmWorkspaceWidget)XtParent(w);
2230 XmWorkspaceConstraints constraints = WORKSPACE_CONSTRAINT(w);
2231 
2232     /*  Make sure child is no longer counted among the select  */
2233     if( constraints->workspace.is_selected )
2234 	ww->workspace.num_selected--;
2235     if( constraints->workspace.select_callbacks )
2236 	RemoveConstraintCallbacks(&constraints->workspace.select_callbacks);
2237     if( constraints->workspace.accent_callbacks )
2238 	RemoveConstraintCallbacks(&constraints->workspace.accent_callbacks);
2239     if( constraints->workspace.resizing_callbacks )
2240 	RemoveConstraintCallbacks(&constraints->workspace.resizing_callbacks);
2241 }
2242 
2243 
2244 /*  Subroutine: ConstraintSetValues
2245  *  Purpose:    Check for application changes to constraint parameters and
2246  *              take actions indicated.
2247  */
ConstraintSetValues(Widget current,Widget request,Widget new)2248 static Boolean ConstraintSetValues( Widget current, Widget request, Widget new )
2249 {
2250     Boolean retval;
2251     Cardinal zero = 0;
2252     XmWorkspaceConstraints new_constraints = WORKSPACE_CONSTRAINT(new);
2253     XmWorkspaceConstraints old_constraints = WORKSPACE_CONSTRAINT(current);
2254 
2255     retval = (*superclass->constraint_class.set_values)(current,request,new,NULL,&zero);
2256 
2257     new->core.x = current->core.x;
2258     new->core.y = current->core.y;
2259     if( new_constraints->workspace.is_selected !=
2260 	old_constraints->workspace.is_selected )
2261     {
2262 	if( new_constraints->workspace.is_selected )
2263 	{
2264 	    if(   new_constraints->workspace.is_selectable
2265 	       && new_constraints->workspace.is_managed ) {
2266 		new_constraints->workspace.is_selected = FALSE;
2267 	        SelectChild((XmWorkspaceWidget)XtParent(new), new, new_constraints,
2268 		    (XEvent *)NULL);
2269 	    }
2270 	}
2271 	else {
2272 	    new_constraints->workspace.is_selected = TRUE;
2273 	    UnselectChild((XmWorkspaceWidget)XtParent(new), new, new_constraints, NULL);
2274 	}
2275     }
2276     if( new_constraints->workspace.is_selectable !=
2277 	old_constraints->workspace.is_selectable )
2278     {
2279 	if( (new_constraints->workspace.is_selectable == FALSE)
2280 	   && new_constraints->workspace.is_selected )
2281 	    UnselectChild((XmWorkspaceWidget)XtParent(new), new, new_constraints, NULL);
2282     }
2283 
2284     if (new_constraints->workspace.line_invisibility !=
2285 	old_constraints->workspace.line_invisibility) {
2286 	if (new_constraints->workspace.line_invisibility) {
2287 	    DeleteWidgetFromCollideList(new);
2288 	} else {
2289 	    AddWidgetToCollideList(new);
2290 	}
2291 	RerouteLines ((XmWorkspaceWidget)XtParent(new), FALSE);
2292     }
2293 
2294     return retval;
2295 }
2296 
2297 
2298 /*  Subroutine: Arm
2299  *  Purpose:    Action for when button1 is pressed
2300  */
Arm(XmWorkspaceWidget ww,XEvent * event)2301 static void Arm( XmWorkspaceWidget ww, XEvent* event )
2302 {
2303 
2304     if (ww->workspace.auto_arrange) return ;
2305 
2306     /*  Check for the second click of a double click  */
2307     if (ww->workspace.button1_press_mode == False)
2308 	{
2309 	StartRubberband(ww, event);
2310 	}
2311     else
2312 	{
2313 	if( (event->xbutton.time - ww->workspace.time)	/* Double click */
2314        		<= ww->workspace.double_click_interval )
2315 	    {
2316 	    ; /* Nada */
2317 	    }
2318     	else	/* Single Click */
2319 	    {
2320 	    XmWorkspaceCallbackStruct call_value;
2321 
2322 	    call_value.reason = XmCR_BACKGROUND;
2323 	    call_value.event = event;
2324 	    call_value.window = event->xbutton.window;
2325 	    call_value.selected_widget = NULL;
2326 	    XtCallCallbacks((Widget)ww, XmNbackgroundCallback, &call_value);
2327 	    }
2328 	}
2329 	/*  Third click will not put us back here
2330 	ww->workspace.time = 0;
2331 	return; */
2332 
2333     ww->workspace.time = event->xbutton.time;
2334 }
2335 
2336 /*  Subroutine: Disarm
2337  *  Purpose:    Action for when button1 is released
2338  */
Disarm(XmWorkspaceWidget ww,XEvent * event)2339 static void Disarm( XmWorkspaceWidget ww, XEvent* event )
2340 {
2341 Boolean bool = True;
2342 
2343     if( ww->workspace.button_tracker != NULL )
2344     {
2345 	event->xbutton.x += ww->workspace.track_x;
2346 	event->xbutton.y += ww->workspace.track_y;
2347 	ww->workspace.button_tracker(ww->workspace.track_widget,
2348 	                             ww->workspace.track_client_data,
2349 	                             event
2350 				     ,&bool
2351 				     );
2352     }
2353     else if( ww->workspace.is_rubberbanding )
2354 	EndRubberband(ww, event);
2355 }
2356 
2357 /*
2358  * Action for using the keyboard in the empty canvas
2359  * Should move selected children if any
2360  * Handling keysyms should not be done here.  The proper way is to write a
2361  * translation table that ties specific key events to their operations.  This
2362  * isn't working on my computer however.  The keyboard's arrow keys never
2363  * percolate this far if the translation table specifies them.
2364  */
KeyboardNavigation(XmWorkspaceWidget ww,XEvent * event)2365 static void KeyboardNavigation( XmWorkspaceWidget ww, XEvent* event )
2366 {
2367     KeySym keysym;
2368     int delta_x, delta_y;
2369     int dx=1, dy=1;
2370     int i;
2371     int minx=0, miny=0;
2372     short first = True;
2373     XmWorkspaceConstraints constraints;
2374     switch (event->type) {
2375 	case KeyPress:
2376 	    keysym = XLookupKeysym (&event->xkey, 0);
2377 	    if ((keysym == XK_Left) || (keysym == XK_Right) ||
2378 		(keysym == XK_Up) || (keysym == XK_Down)) {
2379 		if( ww->workspace.movement_is_allowed && (!ww->workspace.is_moving) &&
2380 		    !(event->xbutton.state & ControlMask) )
2381 		{
2382 		    /*  Prepare to move  */
2383 		    ww->workspace.grab_x = 0;
2384 		    ww->workspace.grab_y = 0;
2385 		    ww->workspace.is_moving = TRUE;
2386 		    ww->workspace.is_resizing = FALSE;
2387 		    /*  Use base variables to keep track of move applied so far  */
2388 		    ww->workspace.base_x = 0;
2389 		    ww->workspace.base_y = 0;
2390 		    /* loop over all selected */
2391 		    /* set min_x, min_y so that we know when to stop sliding left,up */
2392 		    for(i=0; i<ww->composite.num_children; i++)
2393 		    {
2394 			Widget child = ww->composite.children[i];
2395 			constraints = WORKSPACE_CONSTRAINT(child);
2396 			if(constraints->workspace.is_selected)
2397 			{
2398 			    if (first) {
2399 				minx = child->core.x;
2400 				miny = child->core.y;
2401 				first = 0;
2402 			    } else {
2403 				minx = MIN(minx, child->core.x);
2404 				miny = MIN(miny, child->core.y);
2405 			    }
2406 			}
2407 		    }
2408 		}
2409 	    }
2410 	    if (first) ww->workspace.is_moving = FALSE;
2411 	    delta_x = delta_y = 0;
2412 	    if (ww->workspace.snap_to_grid) {
2413 		if (ww->workspace.horizontal_alignment != XmALIGNMENT_NONE)
2414 		    dx = ww->workspace.grid_width;
2415 		if (ww->workspace.vertical_alignment != XmALIGNMENT_NONE)
2416 		    dy = ww->workspace.grid_height;
2417 	    }
2418 	    switch (keysym) {
2419 		case XK_Left:
2420 		    if ((minx-dx)<0) ww->workspace.is_moving = FALSE;
2421 		    delta_x = -dx;
2422 		    break;
2423 		case XK_Right:
2424 		    delta_x = dx;
2425 		    break;
2426 		case XK_Up:
2427 		    if ((miny-dy)<0) ww->workspace.is_moving = FALSE;
2428 		    delta_y = -dy;
2429 		    break;
2430 		case XK_Down:
2431 		    delta_y = dy;
2432 		    break;
2433 		default:
2434 		    break;
2435 	    }
2436 	    /*
2437 	     * If check_overlap is turned on then we set up the surrogates
2438 	     * and run the check for overlapping before allow the move to
2439 	     * go ahead.
2440 	     */
2441 	    if ((ww->workspace.check_overlap) && (ww->workspace.is_moving)) {
2442 		SaveOutlines (ww);
2443 		if (Overlapping (ww, delta_x, delta_y ))
2444 		    ww->workspace.is_moving = FALSE;
2445 		DiscardSurrogate (ww);
2446 	    }
2447 	    if (ww->workspace.is_moving) {
2448 		dropResizedSurrogates (ww, delta_x, delta_y, event);
2449 	    }
2450 	    break;
2451 	default:
2452 	    break;
2453     }
2454 }
2455 /*  Subroutine: Drag
2456  *  Purpose:    Action for when the mouse is dragged with button1 down
2457  */
Drag(XmWorkspaceWidget ww,XEvent * event)2458 static void Drag( XmWorkspaceWidget ww, XEvent* event )
2459 {
2460 Boolean bool = True;
2461 
2462     if (ww->workspace.auto_arrange) return ;
2463 
2464     if( ww->workspace.button_tracker != NULL )
2465     {
2466 	event->xbutton.x += ww->workspace.track_x;
2467 	event->xbutton.y += ww->workspace.track_y;
2468 	ww->workspace.button_tracker(ww->workspace.track_widget,
2469 	                             ww->workspace.track_client_data,
2470 	                             event
2471 				     ,&bool
2472 				     );
2473     }
2474     else if( ww->workspace.is_rubberbanding )
2475 	StretchRubberband(ww, event);
2476 }
2477 
2478 
2479 /*  Subroutine: GetRubberbandGC
2480  *  Purpose:    Get a GC with the right resources for drawing the bounding box
2481  */
GetRubberbandGC(XmWorkspaceWidget ww)2482 static void GetRubberbandGC( XmWorkspaceWidget ww )
2483 {
2484     XGCValues values;
2485     XtGCMask  valueMask;
2486 
2487     values.graphics_exposures = False;
2488     values.foreground = ww->manager.foreground ^ ww->core.background_pixel;
2489     values.background = 0;
2490     values.function = GXxor;
2491     values.line_style = LineOnOffDash;
2492     values.subwindow_mode = IncludeInferiors;
2493     valueMask = GCForeground | GCBackground | GCGraphicsExposures
2494 	| GCFunction | GCSubwindowMode | GCLineStyle;
2495     ww->workspace.bandGC = XtGetGC((Widget)ww, valueMask, &values);
2496     values.line_style = LineSolid;
2497     /*values.foreground = WhitePixel(XtDisplay(ww), XScreenNumberOfScreen(XtScreen(ww)));*/
2498     ww->workspace.badBandGC = XtGetGC((Widget)ww, valueMask, &values);
2499 }
2500 
2501 
2502 /*  Subroutine: DrawRubberband
2503  *  Purpose:    Draw or undraw the rubberband bounding box
2504  */
DrawRubberband(XmWorkspaceWidget ww)2505 static void DrawRubberband( XmWorkspaceWidget ww )
2506 {
2507     XDrawRectangles(XtDisplay(ww), XtWindow(ww), ww->workspace.bandGC,
2508 	            ww->workspace.rubberband, 1);
2509     ww->workspace.band_is_visible = !ww->workspace.band_is_visible;
2510 }
2511 
2512 static Boolean
inHierarchy(Widget child,XmWorkspaceWidget ww,int i)2513 inHierarchy (Widget child, XmWorkspaceWidget ww, int i)
2514 {
2515 Widget parent;
2516 
2517     parent = child;
2518     while ((parent) && (parent!=ww->composite.children[i]) && (parent != (Widget)ww))
2519 	parent = XtParent(parent);
2520 
2521     if (parent == ww->composite.children[i]) return True;
2522     return False;
2523 }
2524 
2525 static XmWorkspaceWidget
WorkspaceOfWidget(Widget w)2526 WorkspaceOfWidget (Widget w)
2527 {
2528 XmWorkspaceWidget ww;
2529 
2530     /*
2531      * Make the routine less restrictive in that it can cope with being invoked
2532      * by any descendant, not just the immediate children.
2533      */
2534     ww = (XmWorkspaceWidget)w;
2535 
2536     while ((ww) && (!XtIsSubclass ((Widget)ww, xmWorkspaceWidgetClass)))
2537         ww = (XmWorkspaceWidget)XtParent((Widget)ww);
2538     if (!ww) return 0;
2539     if (!XtIsSubclass ((Widget)ww, xmWorkspaceWidgetClass)) return 0;
2540     return ww;
2541 }
2542 
2543 
ChildNavigation(Widget w,XEvent * event,String * ignore,Cardinal * ignore2)2544 static void ChildNavigation (Widget w, XEvent* event, String* ignore, Cardinal* ignore2)
2545 {
2546 XmWorkspaceWidget ww;
2547 
2548     if (!(ww = WorkspaceOfWidget(w))) return ;
2549 
2550     KeyboardNavigation (ww, event);
2551 }
2552 
2553 
2554 /*  Subroutine: GrabSelections
2555  *  Purpose:    Action for Button1Down in child widget (accelerator translation)
2556  */
2557 /* ARGSUSED */
GrabSelections(Widget w,XEvent * event,String * params,Cardinal * num_params)2558 static void GrabSelections( Widget w, XEvent* event,
2559 	                    String* params, Cardinal* num_params )
2560 {
2561     XmWorkspaceWidget ww;
2562     Widget grab_child;
2563     XmWorkspaceConstraints constraints;
2564     int i;
2565 
2566     if (!(ww = WorkspaceOfWidget(w))) return ;
2567     ww->workspace.overlaps = FALSE;
2568 
2569     if (ww->workspace.auto_arrange) return ;
2570 
2571     /*
2572      * Determine which child was grabbed, but use a less restricitve method.
2573      * Don't assume that the window of the event is the window of a child
2574      * widget.  Assume only the the window is descendant of a child widget.
2575      */
2576     for (i=0; i<ww->composite.num_children; i++)
2577 	if (inHierarchy(XtWindowToWidget(XtDisplay(ww), event->xany.window), ww, i))
2578 	    break;
2579     /*  This call comes from the accelerator so it should have a child  */
2580     if( i >= ww->composite.num_children )
2581 	return;
2582     grab_child = ww->composite.children[i];
2583     constraints = WORKSPACE_CONSTRAINT(grab_child);
2584     /*  Check if child is sensitive  */
2585     if( (constraints->workspace.is_selectable == FALSE ) ||
2586 	(ww->workspace.is_selectable == FALSE ) )
2587 	return;
2588     /*  Check for the second click of a double click  */
2589     if( (event->xbutton.time - ww->workspace.time)
2590        <= XtGetMultiClickTime(XtDisplay((Widget)ww)) )
2591     {
2592 	XmWorkspaceCallbackStruct call_value;
2593 
2594 	call_value.reason = XmCR_DEFAULT_ACTION;
2595 	call_value.event = event;
2596 	call_value.window = event->xbutton.window;
2597 	call_value.selected_widget = grab_child;
2598 	XtCallCallbacks((Widget)ww, XmNdefaultActionCallback, &call_value);
2599 
2600 	/*  Third click will not put us back here  */
2601 	ww->workspace.time = 0;
2602 	return;
2603     }
2604     ww->workspace.time = event->xbutton.time;
2605     /*  Handle change of selection status  */
2606     if( constraints->workspace.is_selected == FALSE )
2607     {
2608 	if(   (   ((event->xbutton.state & ShiftMask) == 0)
2609 	       || (ww->workspace.selection_policy == XmSINGLE_SELECT))
2610 	   && (ww->workspace.num_selected > 0) )
2611 	    UnselectAll(ww, event);
2612 	SelectChild(ww, grab_child, constraints, (XEvent *)event);
2613     }
2614     /*  If this is a deselect, there should be no move  */
2615     else if( event->xbutton.state & ShiftMask )
2616     {
2617 	UnselectChild(ww, grab_child, constraints, event);
2618 	return;
2619     }
2620     if( ww->workspace.movement_is_allowed &&
2621 	!(event->xbutton.state & ControlMask) )
2622     {
2623 	/*  Prepare to move  */
2624 	CreateSurrogate(ww, grab_child);
2625 	ww->workspace.grab_x = event->xbutton.x;
2626 	ww->workspace.grab_y = event->xbutton.y;
2627 	ww->workspace.is_moving = TRUE;
2628 	ww->workspace.is_resizing = FALSE;
2629 	/*  Use base variables to keep track of move applied so far  */
2630 	ww->workspace.base_x = 0;
2631 	ww->workspace.base_y = 0;
2632     }
2633     if( (constraints->workspace.is_v_resizable ||
2634 	 constraints->workspace.is_h_resizable) &&
2635 	(event->xbutton.state & ControlMask) )
2636     {
2637 	for(i=0; i<ww->composite.num_children; i++)
2638 	{
2639 	    Widget child = ww->composite.children[i];
2640 	    constraints = WORKSPACE_CONSTRAINT(child);
2641 	    if(!(constraints->workspace.is_v_resizable  ||
2642 		 constraints->workspace.is_h_resizable) &&
2643 		constraints->workspace.is_selected)
2644 		UnselectChild(ww, child, constraints, event);
2645 	}
2646 
2647 	/*  Prepare to resize  */
2648 	CreateSurrogate(ww, grab_child);
2649 	ww->workspace.grab_x = grab_child->core.width;
2650 	ww->workspace.grab_y = grab_child->core.height;
2651 	ww->workspace.is_resizing = TRUE;
2652 	ww->workspace.is_moving = FALSE;
2653 	/*  Use base variables to keep track of move applied so far  */
2654 	ww->workspace.base_x = grab_child->core.x;
2655 	ww->workspace.base_y = grab_child->core.y;
2656 	XWarpPointer(XtDisplay(ww), None, XtWindow(grab_child),
2657 	    0,0,0,0, grab_child->core.width-1, grab_child->core.height-1);
2658     }
2659 }
2660 
2661 
2662 /*  Subroutine: MoveResizeSelections
2663  *  Purpose:    Action during move of selected children
2664  */
2665 /* ARGSUSED */
MoveResizeSelections(Widget w,XEvent * event,String * params,Cardinal * num_params)2666 static void MoveResizeSelections( Widget w, XEvent* event,
2667 	                    String* params, Cardinal* num_params )
2668 {
2669 XmWorkspaceWidget ww;
2670 
2671     if (!(ww = WorkspaceOfWidget(w))) return ;
2672 
2673     if (ww->workspace.auto_arrange) return ;
2674 
2675     if( ww->workspace.is_moving )
2676     {
2677 	int delta_x, delta_y;
2678 	if( ww->workspace.move_cursor_installed == FALSE )
2679 	{
2680 	    XDefineCursor(XtDisplay(ww), XtWindow(ww),
2681 	                  xmWorkspaceClassRec.workspace_class.move_cursor);
2682 	    ww->workspace.move_cursor_installed = TRUE;
2683 	}
2684 	delta_x = event->xbutton.x - ww->workspace.grab_x;
2685 	delta_y = event->xbutton.y - ww->workspace.grab_y;
2686 	if( delta_x < ww->workspace.min_x )
2687 	    delta_x = ww->workspace.min_x;
2688 	if( delta_y < ww->workspace.min_y )
2689 	    delta_y = ww->workspace.min_y;
2690 	delta_x -= ww->workspace.base_x;
2691 	delta_y -= ww->workspace.base_y;
2692 	MoveSurrogate(ww, delta_x, delta_y);
2693     }
2694     else if( ww->workspace.is_resizing)
2695     {
2696 	int width, height, tx,ty;
2697 	tx = event->xbutton.x;
2698 	ty = event->xbutton.y;
2699         childRelative (ww, event, &tx, &ty);
2700 
2701 	width = MAX(10, tx);
2702 	height = MAX(10, ty);
2703 	ResizeSurrogate(ww, width, height);
2704     }
2705 }
2706 
2707 /*
2708  * The coords in event refer to the widget of the event which might
2709  * not be an immediate child of the workspace.  So, find the immediate
2710  * child, then translate coords in event to that widget.
2711  */
2712 static void
childRelative(XmWorkspaceWidget ww,XEvent * xev,int * x,int * y)2713 childRelative (XmWorkspaceWidget ww, XEvent *xev, int *x, int *y)
2714 {
2715 Widget parent;
2716 int tx, ty, i;
2717 Widget woe = XtWindowToWidget(XtDisplay(ww), xev->xany.window);
2718 
2719     *x = tx = xev->xbutton.x; *y = ty = xev->xbutton.y;
2720 
2721     for (i=0; i<ww->composite.num_children; i++)
2722 	if (inHierarchy(woe, ww, i)) break;
2723     if (i==ww->composite.num_children) return ;
2724 
2725     parent = woe;
2726     while (XtParent(parent) != (Widget)ww) {
2727 	tx+= parent->core.x;
2728 	ty+= parent->core.y;
2729 	parent = XtParent(parent);
2730     }
2731     *x = tx; *y = ty;
2732 }
2733 
2734 
2735 
2736 /*  Subroutine: DropSelections
2737  *  Purpose:    Action at end of move selected children
2738  */
2739 /* ARGSUSED */
DropSelections(Widget w,XEvent * event,String * params,Cardinal * num_params)2740 static void DropSelections( Widget w, XEvent* event,
2741 	                    String* params, Cardinal* num_params )
2742 {
2743     XmWorkspaceChildCallbackStruct cb;
2744     XmWorkspaceConstraints constraints;
2745     XmWorkspaceWidget ww;
2746     Widget child;
2747     int i;
2748     short delta_x, delta_y;
2749     short resize_x = 0;
2750     short resize_y = 0;
2751     Dimension width, height;
2752     XtWidgetGeometry request, reply;
2753     XtGeometryResult result;
2754     int n;
2755 
2756     if (!(ww = WorkspaceOfWidget(w))) return ;
2757 
2758     if (ww->workspace.auto_arrange) return ;
2759 
2760     if( ww->workspace.is_moving )
2761     {
2762 	/*  We're done, so get rid of surrogate box(es)  */
2763 	if( ww->workspace.surrogate_is_visible )
2764 	    DrawSurrogate(ww);
2765 	XSync(XtDisplay(ww), 0);
2766 	/*  Determine the move, and clip to limits  */
2767 #if RESIZE_HANDLES
2768 	if ((num_params) && (*num_params==1) && (!strcmp(params[0], "NE"))) delta_x = 0;
2769 	else
2770 #endif
2771 	    delta_x = (event->xbutton.x - ww->workspace.grab_x);
2772 	if( delta_x < ww->workspace.min_x )
2773 	    delta_x = ww->workspace.min_x;
2774         else if( delta_x > ww->workspace.max_x )
2775             resize_x = delta_x - ww->workspace.max_x;
2776 #if RESIZE_HANDLES
2777 	if ((num_params) && (*num_params==1) && (!strcmp(params[0], "SW"))) delta_y = 0;
2778 	else
2779 #endif
2780 	    delta_y = (event->xbutton.y - ww->workspace.grab_y);
2781 	if( delta_y < ww->workspace.min_y )
2782 	    delta_y = ww->workspace.min_y;
2783         else if( delta_y > ww->workspace.max_y )
2784             resize_y = delta_y - ww->workspace.max_y;
2785 	if(Overlapping (ww, delta_x-ww->workspace.base_x, delta_y-ww->workspace.base_y)) {
2786 	    delta_x = delta_y = 0;
2787 	    resize_x = resize_y = 0;
2788 	}
2789 	if( (resize_x > 0) || (resize_y > 0) )
2790 	{
2791 	    /*old_width = ww->core.width;*/
2792 	    /*old_height = ww->core.height;*/
2793 	    if(resize_x > 0)
2794 		request.width = ww->core.width + resize_x +
2795 			ww->workspace.grid_width;
2796 	    else
2797 		request.width = ww->core.width;
2798 	    if(resize_y > 0)
2799 		request.height = ww->core.height + resize_y +
2800 			ww->workspace.grid_height;
2801 	    else
2802 		request.height = ww->core.height;
2803 	    request.request_mode = CWWidth | CWHeight;
2804 	    result = XtMakeGeometryRequest((Widget)ww, &request, &reply);
2805 	    if( result == XtGeometryAlmost )
2806 	    {
2807 	        short x_diff, y_diff;
2808 	        x_diff = request.width - reply.width;
2809 	        y_diff = request.height - reply.height;
2810 	        result = XtMakeGeometryRequest((Widget)ww, &reply, &request);
2811 	        if( x_diff > 0 )
2812 	            delta_x -= x_diff;
2813 	        if( y_diff > 0 )
2814 	            delta_y -= y_diff;
2815 	    }
2816 	    else if( result == XtGeometryNo )
2817 	    {
2818 	        if( resize_x > 0 )
2819 	            delta_x -= resize_x;
2820 	        if( resize_y > 0 )
2821 	            delta_y -= resize_y;
2822 	    }
2823 	    /* If the size of the workspace changed, reallocate the collision
2824 	       lists */
2825 	    if ( (result == XtGeometryAlmost) || (result == XtGeometryYes) )
2826 		{
2827 		ReallocCollideLists(ww);
2828 		}
2829 	}
2830 	dropResizedSurrogates (ww, delta_x, delta_y, event);
2831     }
2832     if( ww->workspace.is_resizing)
2833     {
2834 	int tx, ty;
2835 	if( ww->workspace.surrogate_is_visible )
2836 	    DrawSurrogate(ww);
2837 	XSync(XtDisplay(ww), 0);
2838 
2839 	/*  Determine the size, and clip to limits  */
2840 	childRelative (ww, event, &tx, &ty);
2841 	width = MAX(10, tx);
2842 	height = MAX(10, ty);
2843 #if RESIZE_HANDLES
2844 	if ((num_params) && (*num_params == 1)) {
2845 	    int i;
2846 
2847 	    for (i=0; i<ww->composite.num_children; i++) {
2848 		child = ww->composite.children[i];
2849 		constraints = WORKSPACE_CONSTRAINT(child);
2850 		if (constraints->workspace.is_selected) {
2851 		    if (!strcmp (params[0], "NW")) {
2852 			width = child->core.width - event->xbutton.x;
2853 			height = child->core.height - event->xbutton.y;
2854 		    } else if (!strcmp (params[0], "NE")) {
2855 			height = child->core.height - event->xbutton.y;
2856 		    } else if (!strcmp (params[0], "SW")) {
2857 			width = child->core.width - event->xbutton.x;
2858 		    }
2859 		    break;
2860 		}
2861 	    }
2862 	}
2863 #endif
2864        	for( i=ww->composite.num_children-1; i>=0; i-- )
2865 	{
2866 	    child = ww->composite.children[i];
2867 	    constraints = WORKSPACE_CONSTRAINT(child);
2868 	    if(constraints->workspace.is_selected)
2869 	    {
2870 		n = 0;
2871 		cb.height = 0;
2872 		cb.width  = 0;
2873 		if(constraints->workspace.is_h_resizable)
2874 		{
2875 		    /*XtSetArg(wargs[n], XmNwidth, width);*/ n++;
2876 		    cb.width = width;
2877 		} else cb.width = child->core.width;
2878 		if(constraints->workspace.is_v_resizable)
2879 		{
2880 		    /*XtSetArg(wargs[n], XmNheight, height);*/ n++;
2881 		    cb.height = height;
2882 		} else cb.height = child->core.height;
2883 		if(n > 0)
2884 		{
2885 		    /*XtSetValues(child, wargs, n);*/
2886 		    XtResizeWidget (child, cb.width, cb.height, child->core.border_width);
2887 
2888 		    constraints->workspace.x_right = constraints->workspace.x_left +
2889 			cb.width;
2890 		    constraints->workspace.y_lower = constraints->workspace.y_upper +
2891 			cb.height;
2892 		    CallConstraintCallbacks(child,
2893 			constraints->workspace.resizing_callbacks,
2894 	                    (XmAnyCallbackStruct *)&cb);
2895 		}
2896 	    }
2897 	}
2898 	ww->workspace.is_resizing = FALSE;
2899 	/*  Deal with overlaps by movement or stacking  */
2900 	if( ww->bulletin_board.allow_overlap  )
2901 	    RestackSelectedChildren(ww);
2902 	else
2903 	    PerformSpaceWars(ww, FALSE, event);
2904     }
2905 }
2906 
2907 /*
2908  * Broken out of DropSelections so that the code can be called from a similar
2909  * operation performed using the keyboard instead of the mouse
2910  */
dropResizedSurrogates(XmWorkspaceWidget ww,int delta_x,int delta_y,XEvent * event)2911 static void dropResizedSurrogates (XmWorkspaceWidget ww, int delta_x, int delta_y, XEvent* event)
2912 {
2913     XmWorkspaceConstraints constraints;
2914     XmWorkspaceLine line;
2915     Widget child;
2916     int i;
2917     if( (delta_x != 0) || (delta_y != 0) ) {
2918 	/* Go through all the children and see if they were selected.  If
2919 	 * they were, "hide" them in the collide lists, so they do not
2920 	 * cause collisions with the ones that are moved first.  For the
2921 	 * lines, simply remove them from the lists.  They will be added
2922 	 * back in when they are re-reouted
2923 	 */
2924 	for( i=ww->composite.num_children-1; i>=0; i-- )
2925 	    {
2926 	    child = ww->composite.children[i];
2927 	    constraints = WORKSPACE_CONSTRAINT(child);
2928 	    if( constraints->workspace.is_selected == TRUE )
2929 		{
2930 		constraints->workspace.x_delta = delta_x;
2931 		constraints->workspace.y_delta = delta_y;
2932 		if( ww->workspace.snap_to_grid )
2933 		    SnapToGrid(ww, child, constraints, FALSE);
2934 		if ( (constraints->workspace.x_delta != 0 ) ||
2935 		     (constraints->workspace.y_delta != 0) )
2936 		    {
2937 		    if (!constraints->workspace.line_invisibility)
2938 			HideWidgetInCollideList(ww, child);
2939 		    for( line = constraints->workspace.source_lines;
2940 			 line;
2941 			 line = line->src_next )
2942 			{
2943 			if( line->is_to_be_moved == FALSE )
2944 			    {
2945 			    RemoveLineFromCollideList(ww, line);
2946 			    line->is_to_be_moved = TRUE;
2947 			    AugmentExposureAreaForLine(ww, line);
2948 			    }
2949 			}
2950 		    for( line = constraints->workspace.destination_lines;
2951 			line;
2952 			line = line->dst_next )
2953 			{
2954 			if( line->is_to_be_moved == FALSE )
2955 			    {
2956 			    RemoveLineFromCollideList(ww, line);
2957 			    line->is_to_be_moved = TRUE;
2958 			    AugmentExposureAreaForLine(ww, line);
2959 			    }
2960 			}
2961 		    }
2962 		}
2963 	    }
2964 	for( i=ww->composite.num_children-1; i>=0; i-- )
2965 	{
2966 	    child = ww->composite.children[i];
2967 	    constraints = WORKSPACE_CONSTRAINT(child);
2968 	    if( constraints->workspace.is_selected == TRUE )
2969 	    {
2970 		constraints->workspace.x_delta = delta_x;
2971 		constraints->workspace.y_delta = delta_y;
2972 		if( ww->workspace.snap_to_grid )
2973 		    SnapToGrid(ww, child, constraints, FALSE);
2974 		if ( ((constraints->workspace.x_delta != 0) ||
2975 		     (constraints->workspace.y_delta != 0)) &&
2976 		      !ww->workspace.suppress_callbacks )
2977 		    {
2978 		    XmWorkspacePositionChangeCallbackStruct call_data;
2979 
2980 		    call_data.child = child;
2981 		    call_data.x = constraints->workspace.x_left +
2982 			constraints->workspace.x_delta;
2983 		    call_data.y = constraints->workspace.y_upper +
2984 			constraints->workspace.y_delta;
2985 		    call_data.event = event;
2986 		    XtCallCallbacks((Widget)
2987 			ww, XmNpositionChangeCallback, &call_data);
2988 		    }
2989 		if( ww->bulletin_board.allow_overlap )
2990 		    MoveChild(ww, child, constraints);
2991 		else
2992 		{
2993 		    if ((POLICY(ww) == XmSPACE_WARS_LEFT_MOST_STAYS)||
2994 			(POLICY(ww) == XmSPACE_WARS_SELECTED_MIGRATE))
2995 			/*  Register position where widget was dropped  */
2996 			constraints->workspace.x_index += delta_x;
2997 		    else
2998 			/*  Register position post-snap_to_grid  */
2999 			constraints->workspace.x_index +=
3000 			  constraints->workspace.x_delta;
3001 		}
3002 	    }
3003 	}
3004     }
3005     /*  Deal with overlaps by movement or stacking  */
3006     if( ww->bulletin_board.allow_overlap  )
3007 	RestackSelectedChildren(ww);
3008     else
3009 	(void)PerformSpaceWars(ww, FALSE, event);
3010     ww->workspace.is_moving = FALSE;
3011     /*  Redraw any affected application lines  */
3012     if( ww->workspace.lines )
3013     {
3014 	/*  Establish new routes for lines that were moved  */
3015 	RerouteLines(ww, FALSE);
3016 	if ( (delta_x != 0) || (delta_y != 0) )
3017 	{
3018 	    if( ww->workspace.expose_left < ww->workspace.expose_right )
3019 	    {
3020 		if (XtWindow(ww))
3021 		    XClearArea(XtDisplay(ww), XtWindow(ww),
3022 		       ww->workspace.expose_left,
3023 		       ww->workspace.expose_upper,
3024 		       ww->workspace.expose_right -
3025 		       ww->workspace.expose_left,
3026 		       ww->workspace.expose_lower -
3027 		       ww->workspace.expose_upper, False);
3028 	    }
3029 	}
3030     }
3031     if( ww->workspace.move_cursor_installed )
3032     {
3033 	if (XtWindow(ww))
3034 	    XUndefineCursor(XtDisplay(ww), XtWindow(ww));
3035 	ww->workspace.move_cursor_installed = FALSE;
3036     }
3037 }
3038 
3039 /*  Subroutine: MoveChild
3040  *  Purpose:    Move child widget by given offsets and update constraints notes
3041  */
MoveChild(XmWorkspaceWidget ww,Widget child,XmWorkspaceConstraints constraints)3042 static void MoveChild( XmWorkspaceWidget ww, Widget child,
3043 	               XmWorkspaceConstraints constraints )
3044 {
3045     XmWorkspaceLine line;
3046     register short x_delta, y_delta;
3047 
3048     x_delta = constraints->workspace.x_delta;
3049     y_delta = constraints->workspace.y_delta;
3050     if( (x_delta == 0) && (y_delta == 0) )
3051 	return;
3052     constraints->workspace.x_left += x_delta;
3053     constraints->workspace.x_center += x_delta;
3054     constraints->workspace.x_right += x_delta;
3055     constraints->workspace.y_upper += y_delta;
3056     constraints->workspace.y_center += y_delta;
3057     constraints->workspace.y_lower += y_delta;
3058     XtMoveWidget(child, constraints->workspace.x_left,
3059 	         constraints->workspace.y_upper);
3060     if (!constraints->workspace.line_invisibility)
3061 	ChangeWidgetInCollideList(ww, child);
3062     ResetChild(ww, constraints);
3063     /*  Note movement for managed lines  */
3064     for( line = constraints->workspace.source_lines;
3065 	 line;
3066 	 line = line->src_next )
3067     {
3068 	line->src_x += x_delta;
3069 	line->src_y += y_delta;
3070 	if( line->is_to_be_moved == FALSE )
3071 	{
3072 	    line->is_to_be_moved = TRUE;
3073 	    AugmentExposureAreaForLine(ww, line);
3074 	}
3075     }
3076     for( line = constraints->workspace.destination_lines;
3077 	 line;
3078 	 line = line->dst_next )
3079     {
3080 	line->dst_x += x_delta;
3081 	line->dst_y += y_delta;
3082 	if( line->is_to_be_moved == FALSE )
3083 	{
3084 	    line->is_to_be_moved = TRUE;
3085 	    AugmentExposureAreaForLine(ww, line);
3086 	}
3087     }
3088 }
3089 
3090 
3091 /*  Subroutine: ResetChild
3092  *  Purpose:    Reset parameters changed during transitional state to
3093  *              their default or static state values.
3094  */
ResetChild(XmWorkspaceWidget ww,XmWorkspaceConstraints constraints)3095 static void ResetChild( XmWorkspaceWidget ww,
3096 	                XmWorkspaceConstraints constraints )
3097 {
3098     /*  Unset move request  */
3099     constraints->workspace.x_delta = 0;
3100     constraints->workspace.y_delta = 0;
3101     /*  Register current sort position  */
3102     if( ww->workspace.sort_policy == XmALIGNMENT_BEGINNING )
3103 	constraints->workspace.x_index = constraints->workspace.x_left;
3104     else if( ww->workspace.sort_policy == XmALIGNMENT_CENTER )
3105 	constraints->workspace.x_index = constraints->workspace.x_center;
3106     else
3107 	constraints->workspace.x_index = constraints->workspace.x_right;
3108 }
3109 
3110 
3111 /*  Subroutine: SnapToGrid
3112  *  Purpose:    Given a widget, choose the nearest grid position based
3113  *              on the current policy;
3114  */
SnapToGrid(XmWorkspaceWidget ww,Widget child,XmWorkspaceConstraints constraints,Boolean increase)3115 static void SnapToGrid( XmWorkspaceWidget ww, Widget child,
3116 	                XmWorkspaceConstraints constraints, Boolean increase )
3117 {
3118     int x, width;
3119     int y, height;
3120 
3121     if (XtClass(child) == xmWorkspaceWidgetClass) return ;
3122     x = constraints->workspace.x_left + constraints->workspace.x_delta;
3123     width = 1 + constraints->workspace.x_right - constraints->workspace.x_left;
3124     Align1D(&x, width, ww->workspace.grid_width,
3125 	    ww->workspace.horizontal_alignment, increase);
3126     constraints->workspace.x_delta = x - constraints->workspace.x_left;
3127     y = constraints->workspace.y_upper + constraints->workspace.y_delta;
3128     height =
3129       1 + constraints->workspace.y_lower - constraints->workspace.y_upper;
3130     Align1D(&y, height, ww->workspace.grid_height,
3131 	    ww->workspace.vertical_alignment, increase);
3132     constraints->workspace.y_delta = y - constraints->workspace.y_upper;
3133 }
3134 
3135 
3136 /*  Subroutine: Align1D
3137  *  Purpose:    Set position on one axis according to rules of alignment
3138  *  Note:       If increase is set, only positive adjustments will be made
3139  */
Align1D(int * x,int width,int grid,unsigned char alignment,Boolean increase_only)3140 static void Align1D( int* x, int width, int grid, unsigned char alignment,
3141 	             Boolean increase_only )
3142 {
3143     int index, x_off, center;
3144     Boolean  odd;            /* Is (width / grid) an odd number? */
3145 
3146     if( grid == 1 )
3147 	return;
3148 
3149     switch( alignment )
3150     {
3151       case XmALIGNMENT_NONE:
3152 	break;
3153       case XmALIGNMENT_BEGINNING:
3154 	if( increase_only )
3155 	    index = (*x + 1.5*grid - 1) / grid;
3156 	else
3157 	    index = (*x + .5*grid) / grid;
3158 	*x = index * grid;
3159 	break;
3160       case XmALIGNMENT_CENTER:
3161 	/*  x_off is center in widget for first grid cell if 1 or more used  */
3162 	/*  (-1 seems to work better, but I don't really know why)  */
3163 	x_off = (width % grid) / 2;
3164         odd   = (width / grid) % 2;
3165 	center = grid / 2;
3166 
3167 	if( increase_only )
3168 	    /* -1 prevents advancing if perfect fit */
3169             index = (*x + x_off + center - 1) / grid;
3170 	else
3171 	    index = (*x + x_off + odd * center) / grid;
3172 	*x = (grid * index) + !odd * center - x_off;
3173         while(*x < 0)
3174             *x += grid;
3175 	break;
3176 
3177       case XmALIGNMENT_END:
3178 	index = (*x + width + 0.5*grid - 1) / grid;
3179 	*x = (index * grid) - width;
3180 	break;
3181       default:
3182 	break;
3183     }
3184     if (*x < 0) *x = 0;
3185 }
3186 
3187 
3188 /*  Subroutine: StartRubberband
3189  *  Purpose:    Initialize rubberbanding action
3190  */
StartRubberband(XmWorkspaceWidget ww,XEvent * event)3191 static void StartRubberband( XmWorkspaceWidget ww, XEvent* event )
3192 {
3193     if( ww->workspace.selection_policy != XmEXTENDED_SELECT )
3194 	return;
3195     if( ww->workspace.bandGC == NULL )
3196 	GetRubberbandGC(ww);
3197     /*  Shift at this point means leave unboxed items alone  */
3198     if( (event->xbutton.state & ShiftMask) == 0 )
3199     {
3200 	UnaccentAll(ww, event);
3201 	ww->workspace.shift_is_applied = FALSE;
3202     }
3203     else
3204     {
3205 	register XmWorkspaceConstraints constraints;
3206 	int i;
3207 	/*  Update looks at prior inclusion status in this case, so clean up  */
3208 	/*  Clear all inclusion marks for a fresh start  */
3209 	for( i=0; i<ww->composite.num_children; i++ )
3210 	{
3211 	    constraints = WORKSPACE_CONSTRAINT(ww->composite.children[i]);
3212 	    constraints->workspace.is_within_band = FALSE;
3213 	}
3214 	ww->workspace.shift_is_applied = TRUE;
3215     }
3216     ww->workspace.is_rubberbanding = TRUE;
3217     ww->workspace.base_x = ww->workspace.rubberband[0].x = event->xbutton.x;
3218     ww->workspace.base_y = ww->workspace.rubberband[0].y = event->xbutton.y;
3219     ww->workspace.rubberband[0].width = ww->workspace.rubberband[0].height = 1;
3220 /*
3221     DrawRubberband(ww);
3222 */
3223 }
3224 
3225 
3226 /*  Subroutine: StretchRubberband
3227  *  Purpose:    Change the rubberband bounding box and do all that implies
3228  */
StretchRubberband(XmWorkspaceWidget ww,XEvent * event)3229 static void StretchRubberband( XmWorkspaceWidget ww, XEvent * event )
3230 {
3231     unsigned char direction;
3232 
3233     if( ww->workspace.is_rubberbanding )
3234     {
3235 	if( ww->workspace.band_is_visible )
3236 	    DrawRubberband(ww);
3237 	if( event->xbutton.x > ww->workspace.base_x )
3238 	{
3239 	    ww->workspace.rubberband[0].x = ww->workspace.base_x;
3240 	    ww->workspace.rubberband[0].width =
3241 	      1 + event->xbutton.x - ww->workspace.base_x;
3242 	    direction = SZ_RIGHT;
3243 	}
3244 	else
3245 	{
3246 	    ww->workspace.rubberband[0].x = event->xbutton.x;
3247 	    ww->workspace.rubberband[0].width =
3248 	      1 + ww->workspace.base_x - event->xbutton.x;
3249 	    direction = SZ_LEFT;
3250 	}
3251 	if( event->xbutton.y > ww->workspace.base_y )
3252 	{
3253 	    ww->workspace.rubberband[0].y = ww->workspace.base_y;
3254 	    ww->workspace.rubberband[0].height =
3255 	      1 + event->xbutton.y - ww->workspace.base_y;
3256 	    direction += SZ_LOWER;
3257 	}
3258 	else
3259 	{
3260 	    ww->workspace.rubberband[0].y = event->xbutton.y;
3261 	    ww->workspace.rubberband[0].height =
3262 	      1 + ww->workspace.base_y - event->xbutton.y;
3263 	    direction += SZ_UPPER;
3264 	}
3265 	/*  Make sure band is not present when/if widget changes appearance  */
3266 	XSync(XtDisplay(ww), False);
3267 	if( ww->workspace.size_xx_installed != direction )
3268 	{
3269 	    if (XtWindow(ww))
3270 		XDefineCursor(XtDisplay(ww), XtWindow(ww),
3271 	          xmWorkspaceClassRec.workspace_class.size_cursor[direction]);
3272 	    ww->workspace.size_xx_installed = direction;
3273 	}
3274 	/*  Check for selection changes  */
3275 	UpdateRubberbandSelections(ww, event);
3276 	/*  Make sure selection changes are done before drawing new band  */
3277 	XSync(XtDisplay(ww), False);
3278 	DrawRubberband(ww);
3279 	XSync(XtDisplay(ww), False);
3280     }
3281 }
3282 
3283 
3284 /*  Subroutine: EndRubberband
3285  *  Purpose:    Terminate rubberbanding action
3286  */
EndRubberband(XmWorkspaceWidget ww,XEvent * event)3287 static void EndRubberband( XmWorkspaceWidget ww, XEvent* event )
3288 {
3289     Widget child;
3290     XmWorkspaceConstraints constraints;
3291     int i;
3292 
3293     if( ww->workspace.is_rubberbanding )
3294     {
3295 	ww->workspace.is_rubberbanding = FALSE;
3296 	if( ww->workspace.band_is_visible )
3297 	{
3298 	    DrawRubberband(ww);
3299 	    XSync(XtDisplay(ww), 0);
3300 	}
3301 	if( ww->workspace.size_xx_installed != SZ_NONE )
3302 	{
3303 	    XUndefineCursor(XtDisplay(ww), XtWindow(ww));
3304 	    ww->workspace.size_xx_installed = SZ_NONE;
3305 	}
3306 	/*  Send notification to children whose selection status has changed  */
3307 	for( i=0; i<ww->composite.num_children; i++ )
3308 	{
3309 	    child = ww->composite.children[i];
3310 	    constraints = WORKSPACE_CONSTRAINT(child);
3311 	    if( constraints->workspace.will_be_selected !=
3312 	        constraints->workspace.is_selected )
3313 	    {
3314 	        if( constraints->workspace.will_be_selected )
3315 	            SelectChild(ww, ww->composite.children[i],
3316 	                        constraints, (XEvent *)event);
3317 	        else
3318 	            UnselectChild(ww, ww->composite.children[i],
3319 	                        constraints, event);
3320 	    }
3321 	}
3322 /*
3323 	RestackSelectedChildren(ww);
3324 */
3325     }
3326 }
3327 
3328 
3329 /*  Subroutine: UpdateRubberbandSelections
3330  *  Purpose:    Update selections to reflect current bounding box
3331  *  Note:       is_within_band shows whether widget was previously counted in
3332  *              will_be_selected shows whether has been marked for selection
3333  *              is_accented shows if widget has visible sign of being marked
3334  *              is_selected shows if last callback for widget was select true.
3335  */
UpdateRubberbandSelections(XmWorkspaceWidget ww,XEvent * event)3336 static void UpdateRubberbandSelections( XmWorkspaceWidget ww, XEvent* event )
3337 {
3338     XmWorkspaceConstraints constraints;
3339     register short x1, x2, y1, y2;
3340     int i;
3341     Boolean included;
3342 
3343     if(!ww->workspace.is_selectable) return;
3344 
3345     x1 = ww->workspace.rubberband[0].x;
3346     x2 = x1 + ww->workspace.rubberband[0].width - 1;
3347     y1 = ww->workspace.rubberband[0].y;
3348     y2 = y1 + ww->workspace.rubberband[0].height - 1;
3349     for( i=ww->composite.num_children-1; i>=0; i-- )
3350     {
3351 	constraints = WORKSPACE_CONSTRAINT(ww->composite.children[i]);
3352 	if( constraints->workspace.is_selectable )
3353 	{
3354 	    if( ww->workspace.inclusion_policy == XmINCLUDE_ANY )
3355 	    {
3356 	        /*  If any side beyond rubberband range, no part is included  */
3357 	        if(   (x2 < constraints->workspace.x_left)
3358 	           || (x1 > constraints->workspace.x_right)
3359 	           || (y2 < constraints->workspace.y_upper)
3360 	           || (y1 > constraints->workspace.y_lower) )
3361 	            included = FALSE;
3362 	        else
3363 	            included = TRUE;
3364 	    }
3365 	    else if( ww->workspace.inclusion_policy == XmINCLUDE_CENTER )
3366 	    {
3367 	        if(   (x1 < constraints->workspace.x_center)
3368 	           && (x2 > constraints->workspace.x_center)
3369 	           && (y1 < constraints->workspace.y_center)
3370 	           && (y2 > constraints->workspace.y_center) )
3371 	            included = TRUE;
3372 	        else
3373 	            included = FALSE;
3374 	    }
3375 	    else        /*  XmINCLUDE_ALL  */
3376 	    {
3377 	        if(   (x1 < constraints->workspace.x_left)
3378 	           && (x2 > constraints->workspace.x_right)
3379 	           && (y1 < constraints->workspace.y_upper)
3380 	           && (y2 > constraints->workspace.y_lower) )
3381 	            included = TRUE;
3382 	        else
3383 	            included = FALSE;
3384 	    }
3385 	    /*  With shift, we are toggling states  */
3386 	    if( ww->workspace.shift_is_applied )
3387 	    {
3388 	        /*  If inclusion status changed since last we checked  */
3389 	        if( constraints->workspace.is_within_band != included )
3390 	        {
3391 	            if( constraints->workspace.will_be_selected )
3392 	                UnaccentChild(ww, ww->composite.children[i],
3393 	                              constraints, event);
3394 	            else
3395 	                AccentChild(ww, ww->composite.children[i],
3396 	                            constraints, event);
3397 	            constraints->workspace.is_within_band = included;
3398 	        }
3399 	    }
3400 	    else
3401 	    {
3402 	        if( included )
3403 	        {
3404 	            if( constraints->workspace.will_be_selected == FALSE )
3405 	                AccentChild(ww, ww->composite.children[i],
3406 	                            constraints, event);
3407 	        }
3408 	        else
3409 	        {
3410 	            if( constraints->workspace.will_be_selected == TRUE )
3411 	                UnaccentChild(ww, ww->composite.children[i],
3412 	                              constraints, event);
3413 	        }
3414 	    }
3415 	}
3416     }
3417 }
3418 
3419 
3420 /*  Subroutine: UnselectAll
3421  *  Purpose:    Unselect all selected children widgets
3422  */
UnselectAll(XmWorkspaceWidget ww,XEvent * event)3423 static void UnselectAll( XmWorkspaceWidget ww, XEvent* event )
3424 {
3425     XmWorkspaceConstraints constraints;
3426     Widget child;
3427     int i;
3428 
3429     for( i=ww->composite.num_children-1; i>=0; i-- )
3430     {
3431 	child = ww->composite.children[i];
3432 	constraints = WORKSPACE_CONSTRAINT(child);
3433 	if( constraints->workspace.is_selected == TRUE )
3434 	    UnselectChild(ww, child, constraints, event);
3435     }
3436 }
3437 
3438 
3439 /*  Subroutine: SelectChild
3440  *  Purpose:    Perform selection on given child widget
3441  */
SelectChild(XmWorkspaceWidget ww,Widget child,XmWorkspaceConstraints constraints,XEvent * event)3442 static void SelectChild( XmWorkspaceWidget ww, Widget child,
3443 	                 XmWorkspaceConstraints constraints, XEvent* event )
3444 {
3445     XmWorkspaceChildCallbackStruct cb;
3446 
3447     /*  Make sure selection is visually accented  */
3448     if( constraints->workspace.will_be_selected == FALSE )
3449 	AccentChild(ww, child, constraints, event);
3450     /*  Select this widget  */
3451     if (constraints->workspace.is_selected == FALSE)
3452 	++ww->workspace.num_selected;
3453     constraints->workspace.is_selected = TRUE;
3454     /*  Tell the widget that it has been Selected  */
3455     cb.reason = XmCR_SELECT;
3456     cb.event = event;
3457     cb.status = TRUE;
3458     /*  Call the selection callback  */
3459     if(!ww->workspace.suppress_callbacks)
3460     {
3461 	CallConstraintCallbacks(child, constraints->workspace.select_callbacks,
3462 	                    (XmAnyCallbackStruct *)&cb);
3463     }
3464     XtCallCallbacks((Widget)ww, XmNselectionCallback, &cb);
3465 }
3466 
3467 
3468 /*  Subroutine: UnselectChild
3469  *  Purpose:    Unperform selection on given child widget
3470  */
UnselectChild(XmWorkspaceWidget ww,Widget child,XmWorkspaceConstraints constraints,XEvent * event)3471 static void UnselectChild( XmWorkspaceWidget ww, Widget child,
3472 	                   XmWorkspaceConstraints constraints, XEvent* event )
3473 {
3474     XmWorkspaceChildCallbackStruct cb;
3475 
3476     /*  Make sure selection is visually unaccented  */
3477     if( constraints->workspace.will_be_selected == TRUE )
3478 	UnaccentChild(ww, child, constraints, event);
3479     /*  Deselect this widget  */
3480     if (constraints->workspace.is_selected == TRUE)
3481 	--ww->workspace.num_selected;
3482     constraints->workspace.is_selected = FALSE;
3483 
3484     if(ww->workspace.suppress_callbacks)
3485 	return;
3486 
3487     /*  Tell the widget that it has been deselected  */
3488     cb.reason = XmCR_SELECT;
3489     cb.event = event;
3490     cb.status = FALSE;
3491     /*  Call the selection callback  */
3492     CallConstraintCallbacks(child, constraints->workspace.select_callbacks,
3493 	                    (XmAnyCallbackStruct *)&cb);
3494     XtCallCallbacks((Widget)ww, XmNselectionCallback, &cb);
3495 }
3496 
3497 
3498 /*  Subroutine: UnaccentAll
3499  *  Purpose:    Remove accents from all accented children
3500  */
UnaccentAll(XmWorkspaceWidget ww,XEvent * event)3501 static void UnaccentAll( XmWorkspaceWidget ww, XEvent* event )
3502 {
3503     XmWorkspaceConstraints constraints;
3504     Widget child;
3505     int i;
3506 
3507     for( i=ww->composite.num_children-1; i>=0; i-- )
3508     {
3509 	child = ww->composite.children[i];
3510 	constraints = WORKSPACE_CONSTRAINT(child);
3511 	if( constraints->workspace.will_be_selected == TRUE )
3512 	    UnaccentChild(ww, child, constraints, event);
3513     }
3514 }
3515 
3516 
3517 /*  Subroutine: AccentChild
3518  *  Purpose:    Apply any indicated visual accent and call accent callback
3519  */
AccentChild(XmWorkspaceWidget ww,Widget child,XmWorkspaceConstraints constraints,XEvent * event)3520 static void AccentChild( XmWorkspaceWidget ww, Widget child,
3521 	                 XmWorkspaceConstraints constraints, XEvent* event )
3522 {
3523     XmWorkspaceChildCallbackStruct cb;
3524     Arg warg;
3525 
3526     if( ww->workspace.accent_policy == XmACCENT_BACKGROUND )
3527     {
3528 	constraints->workspace.accent_uncolor = child->core.background_pixel;
3529 	if( XmIsPrimitive(child) || XmIsManager(child) )
3530 	{
3531 	    register int thickness;
3532 	    if( XmIsPrimitive(child) )
3533 	        thickness =
3534 	          ((XmPrimitiveWidget)child)->primitive.shadow_thickness +
3535 	          ((XmPrimitiveWidget)child)->primitive.highlight_thickness;
3536 	    else
3537 	        thickness = ((XmManagerWidget)child)->manager.shadow_thickness;
3538 	    child->core.background_pixel = ww->workspace.accent_color;
3539 	    if (XtWindow(child)) {
3540 		XSetWindowBackground(XtDisplay(child), XtWindow(child),
3541 				     child->core.background_pixel);
3542 		XClearArea(XtDisplay(child), XtWindow(child), thickness, thickness,
3543 			   child->core.width - (thickness + thickness),
3544 			   child->core.height - (thickness + thickness), True);
3545 	    }
3546 	}
3547 	else
3548 	{
3549 	    XtSetArg(warg, XmNbackground, ww->workspace.accent_color);
3550 	    XtSetValues((Widget)child, &warg, 1);
3551 	}
3552 	constraints->workspace.is_accented = TRUE;
3553     }
3554     else if( ww->workspace.accent_policy == XmACCENT_BORDER )
3555     {
3556 	constraints->workspace.accent_uncolor = child->core.border_pixel;
3557 	XtSetArg(warg, XmNborderColor, ww->workspace.accent_color);
3558 	XtSetValues((Widget)child, &warg, 1);
3559 	constraints->workspace.is_accented = TRUE;
3560     }
3561     cb.reason = XmCR_ACCENT;
3562     cb.event = event;
3563     cb.status = ON;
3564     CallConstraintCallbacks(child, constraints->workspace.accent_callbacks,
3565 	                    (XmAnyCallbackStruct *)&cb);
3566     constraints->workspace.will_be_selected = TRUE;
3567 }
3568 
3569 
3570 /*  Subroutine: UnaccentChild
3571  *  Purpose:    Remove any visual accents and call accent callback
3572  */
UnaccentChild(XmWorkspaceWidget ww,Widget child,XmWorkspaceConstraints constraints,XEvent * event)3573 static void UnaccentChild( XmWorkspaceWidget ww, Widget child,
3574 	                   XmWorkspaceConstraints constraints, XEvent* event )
3575 {
3576     XmWorkspaceChildCallbackStruct cb;
3577     Arg warg;
3578 
3579     if( constraints->workspace.is_accented == TRUE )
3580     {
3581 	if( ww->workspace.accent_policy == XmACCENT_BACKGROUND )
3582 	{
3583 	    if( XmIsPrimitive(child) || XmIsManager(child) )
3584 	    {
3585 	        register int thickness;
3586 	        if( XmIsPrimitive(child) )
3587 	            thickness =
3588 	              ((XmPrimitiveWidget)child)->primitive.shadow_thickness +
3589 	              ((XmPrimitiveWidget)child)->primitive.highlight_thickness;
3590 	        else
3591 	            thickness =
3592 	              ((XmManagerWidget)child)->manager.shadow_thickness;
3593 	        child->core.background_pixel =
3594 	          constraints->workspace.accent_uncolor;
3595 		if (XtWindow(child)) {
3596 		    XSetWindowBackground(XtDisplay(child), XtWindow(child),
3597 					 child->core.background_pixel);
3598 		    XClearArea(XtDisplay(child), XtWindow(child),
3599 			       thickness, thickness,
3600 			       child->core.width - (thickness + thickness),
3601 			       child->core.height - (thickness + thickness), True);
3602 		}
3603 	    }
3604 	    else
3605 	    {
3606 	        XtSetArg(warg, XmNbackground,
3607 	                 constraints->workspace.accent_uncolor);
3608 	        XtSetValues((Widget)child, &warg, 1);
3609 	    }
3610 	}
3611 	else if( ww->workspace.accent_policy == XmACCENT_BORDER )
3612 	{
3613 	    XtSetArg(warg, XmNborderColor,
3614 	             constraints->workspace.accent_uncolor);
3615 	    XtSetValues((Widget)child, &warg, 1);
3616 	}
3617 	constraints->workspace.is_accented = FALSE;
3618     }
3619     cb.reason = XmCR_ACCENT;
3620     cb.event = event;
3621     cb.status = OFF;
3622     CallConstraintCallbacks(child, constraints->workspace.accent_callbacks,
3623 	                    (XmAnyCallbackStruct *)&cb);
3624     constraints->workspace.will_be_selected = FALSE;
3625 }
3626 
RaiseSelections(Widget w,XEvent * event,String * params,Cardinal * num_params)3627 static void RaiseSelections( Widget w, XEvent* event,
3628 	                    String* params, Cardinal* num_params )
3629 {
3630 XmWorkspaceWidget ww;
3631 
3632     if (!(ww = WorkspaceOfWidget(w))) return ;
3633     if (ww->workspace.auto_arrange) return ;
3634     RestackSelectedChildren (ww);
3635 }
3636 
3637 /*  Subroutine: RestackSelectedChildren
3638  *  Purpose:    Change the stacking order such that all moved children appear
3639  *              in front of all unmoved children.
3640  */
RestackSelectedChildren(XmWorkspaceWidget ww)3641 static void RestackSelectedChildren( XmWorkspaceWidget ww )
3642 {
3643     int i;
3644     if( ww->workspace.num_selected == 1 )
3645     {
3646 	XmWorkspaceConstraints constraints;
3647 	Widget child;
3648 
3649 	for( i=0; i<ww->composite.num_children; i++ )
3650 	{
3651 	    child = ww->composite.children[i];
3652 	    constraints = WORKSPACE_CONSTRAINT(child);
3653 	    if( constraints->workspace.is_selected == TRUE )
3654 	    {
3655 		if (XtWindow(ww->composite.children[i]))
3656 		    XRaiseWindow(XtDisplay(ww), XtWindow(ww->composite.children[i]));
3657 	        return;
3658 	    }
3659 	}
3660     }
3661     else if (XtWindow(ww))
3662     {
3663 	Window root, parent, *children, *new_order;
3664 	int selCnt, under, over;
3665 	unsigned int num_children;
3666 
3667 	/*
3668 	 * Problem: the number of widgets in ww->composite.num_children may
3669 	 * not be the same a num_children returned from XQueryTree.
3670 	 * That makes this a garbage-in garbage-out situation.
3671 	 */
3672 
3673 	/*  Get current stacking order as bottom-first list  */
3674 	(void)XQueryTree(XtDisplay(ww), XtWindow(ww), &root, &parent,
3675 	                 &children, &num_children);
3676 	new_order = (Window *)XtCalloc(num_children, sizeof(Window));
3677 	under = num_children - 1;
3678 	selCnt = 0;
3679 	for( i=0; i<num_children; i++ )
3680 	    if( ChildWindowIsSelected(ww, children[i]) ) selCnt++;
3681 
3682 	over = selCnt - 1;
3683 	for( i=0; i<num_children; i++ )
3684 	{
3685 	    if( ChildWindowIsSelected(ww, children[i]) )
3686 	    {
3687 	        new_order[over] = children[i];
3688 	        over--;
3689 	    }
3690 	    else
3691 	    {
3692 	        new_order[under] = children[i];
3693 	        under--;
3694 	    }
3695 	}
3696 	/*  Stack according to the top-first list  */
3697 	XRestackWindows(XtDisplay(ww), new_order, num_children);
3698 	XtFree((char*)new_order);
3699 	XFree((void *)children);
3700     }
3701 }
3702 
3703 
3704 /*  Subroutine: ChildWindowIsSelected
3705  *  Purpose:    Determine if passed window belongs to a child which is selected
3706  */
ChildWindowIsSelected(XmWorkspaceWidget ww,Window child)3707 static Boolean ChildWindowIsSelected( XmWorkspaceWidget ww, Window child )
3708 {
3709     int i;
3710     for( i=0; i<ww->composite.num_children; i++ )
3711     {
3712 	if( XtWindow(ww->composite.children[i]) == child )
3713 	{
3714 	    XmWorkspaceConstraints constraints =
3715 	      WORKSPACE_CONSTRAINT(ww->composite.children[i]);
3716 	    return constraints->workspace.is_selected;
3717 	}
3718     }
3719     return FALSE;
3720 }
3721 
3722 
3723 /*  Subroutine: SetGridBackground
3724  *  Purpose:    Install a background pixmap with grid marks
3725  */
SetGridBackground(XmWorkspaceWidget ww,Boolean change)3726 static void SetGridBackground( XmWorkspaceWidget ww, Boolean change )
3727 {
3728     Pixmap pixmap;
3729     XGCValues values;
3730     XtGCMask valueMask;
3731     GC gc;
3732     int width, height;
3733     Boolean draw_grid_marks;
3734     Screen* scrptr = ww->core.screen;
3735     Window rootw = RootWindowOfScreen(scrptr);
3736 
3737     /*  Check if drawing the grid marks is requested  */
3738     if(   ww->workspace.snap_to_grid
3739        && (   (ww->workspace.horizontal_draw_grid != XmDRAW_NONE)
3740 	   || (ww->workspace.vertical_draw_grid != XmDRAW_NONE)) )
3741     {
3742 	width = ww->workspace.grid_width;
3743 	height = ww->workspace.grid_height;
3744 	draw_grid_marks = True;
3745     }
3746     else
3747     {
3748 #if DUPLICATE_XT_STUFF
3749 #else
3750 	XtVaSetValues ((Widget)ww, XmNbackgroundPixmap, XtUnspecifiedPixmap, NULL);
3751 	return ;
3752 #endif
3753 	/*  When window is created, it has a solid background by default  */
3754 	if( change == False )
3755 	    return;
3756 	/*  If pixmap is changed, we must manufacture a solid background  */
3757 	width = 16;
3758 	height = 16;
3759 	draw_grid_marks = False;
3760     }
3761 
3762     if (!XtWindow(ww)) return ;
3763 
3764     pixmap = XCreatePixmap(XtDisplay(ww), rootw,
3765 	width, height, ww->core.depth);
3766     values.graphics_exposures = False;
3767     values.foreground = ww->core.background_pixel;
3768     values.background = ww->core.background_pixel;
3769     values.function = GXcopy;
3770     values.line_style = LineOnOffDash;
3771     values.dashes = 1;
3772     values.plane_mask = AllPlanes;
3773     valueMask = GCForeground | GCBackground | GCGraphicsExposures
3774       | GCFunction | GCLineStyle | GCDashList | GCPlaneMask;
3775     gc = XtGetGC((Widget)ww, valueMask, &values);
3776     XFillRectangle(XtDisplay(ww), pixmap, gc, 0, 0, width, height);
3777     if( draw_grid_marks )
3778     {
3779   	XSetForeground(XtDisplay(ww), gc, ww->manager.foreground);
3780 	DrawGridMarks(ww, gc, pixmap);
3781     }
3782 #if DUPLICATE_XT_STUFF
3783     XSetWindowBackgroundPixmap(XtDisplay(ww), XtWindow(ww), pixmap);
3784 #else
3785     XtVaSetValues ((Widget)ww, XmNbackgroundPixmap, pixmap, NULL);
3786 #endif
3787     XtReleaseGC((Widget)ww, gc);
3788     XFreePixmap(XtDisplay(ww), pixmap);
3789 }
3790 
3791 
3792 /*  Subroutine: DrawGridMarks
3793  *  Purpose:    Draw the brid marks in the workspace background pixmap
3794  */
DrawGridMarks(XmWorkspaceWidget ww,GC gc,Pixmap pixmap)3795 static void DrawGridMarks( XmWorkspaceWidget ww, GC gc, Pixmap pixmap )
3796 {
3797     int x_h, y1_h, y2_h, x1_v, x2_v, y_v;
3798 
3799     if( ww->workspace.horizontal_alignment == XmALIGNMENT_END )
3800     {
3801 	x_h = ww->workspace.grid_width - 1;
3802 	x1_v = ww->workspace.grid_width - (HASH_RAY + HASH_RAY);
3803     }
3804     else if((ww->workspace.horizontal_alignment == XmALIGNMENT_CENTER) ||
3805             (ww->workspace.horizontal_alignment == XmALIGNMENT_NONE))
3806     {
3807 	x_h = ww->workspace.grid_width / 2;
3808 	x1_v = (ww->workspace.grid_width / 2) - HASH_RAY;
3809     }
3810     else
3811     {
3812 	x_h = 0;
3813 	x1_v = 0;
3814     }
3815     if(  ww->workspace.vertical_alignment == XmALIGNMENT_END )
3816     {
3817 	y1_h = ww->workspace.grid_height - (HASH_RAY + HASH_RAY);
3818 	y_v = ww->workspace.grid_height - 1;
3819     }
3820     else if((ww->workspace.vertical_alignment == XmALIGNMENT_CENTER) ||
3821             (ww->workspace.vertical_alignment == XmALIGNMENT_NONE))
3822     {
3823 	y1_h = (ww->workspace.grid_height / 2) - HASH_RAY;
3824 	y_v = ww->workspace.grid_height / 2;
3825     }
3826     else
3827     {
3828 	y1_h = 0;
3829 	y_v = 0;
3830     }
3831     /*  There is a dash problem in that some PS2 servers don't draw the on .. *
3832      *  part of a dash unless the off is also included!  So we stretch to ... *
3833      *  an even size.  Worse, some old PS2 servers don't do dashes: they ...  *
3834      *  will draw these lines too long.  Sigh!                                */
3835     if( ww->workspace.horizontal_draw_grid == XmDRAW_HASH )
3836 	/*  I don't know why -1 but OnOffDash comes out short without it  */
3837 	y2_h =  y1_h + HASH_RAY + HASH_RAY + 1;
3838     else
3839     {
3840 	if( ww->workspace.horizontal_draw_grid == XmDRAW_OUTLINE )
3841 	    x_h = 0;
3842 	y1_h = 0;
3843 	y2_h = ww->workspace.grid_height - 1;
3844     }
3845     if( ww->workspace.vertical_draw_grid == XmDRAW_HASH )
3846 	x2_v =  x1_v + HASH_RAY + HASH_RAY + 1;
3847     else
3848     {
3849 	if( ww->workspace.vertical_draw_grid == XmDRAW_OUTLINE )
3850 	    y_v = 0;
3851 	x1_v = 0;
3852 	x2_v = ww->workspace.grid_width - 1;
3853     }
3854     /*  If things are too tight in our direction, don't draw anything  */
3855     if( ww->workspace.horizontal_draw_grid != XmDRAW_NONE )
3856 	XDrawLine(XtDisplay(ww), pixmap, gc, x_h, y1_h, x_h, y2_h);
3857     if( ww->workspace.vertical_draw_grid != XmDRAW_NONE )
3858 	XDrawLine(XtDisplay(ww), pixmap, gc, x1_v, y_v, x2_v, y_v);
3859 }
3860 
3861 
3862 /*  Subroutine: OutlineChild
3863  *  Purpose:    Set a rectangle the outline of a single child widget
3864  */
OutlineChild(XRectangle * surrogate,XmWorkspaceConstraints constraints)3865 static void OutlineChild( XRectangle* surrogate,
3866 	                  XmWorkspaceConstraints constraints )
3867 {
3868     surrogate->x = constraints->workspace.x_left;
3869     surrogate->y = constraints->workspace.y_upper;
3870     /*  X draws box as x<->x+width instead of x<->x+width-1 as it should  */
3871     surrogate->width =
3872       constraints->workspace.x_right - constraints->workspace.x_left;
3873     surrogate->height =
3874       constraints->workspace.y_lower - constraints->workspace.y_upper;
3875 }
3876 
3877 
3878 /*  Subroutine: SaveOutline
3879  *  Purpose:    Make outline of grabbed widget and a box fit around all
3880  *              selected widgets (if there are more than the one).
3881  */
SaveOutline(XmWorkspaceWidget ww,Widget grab_child)3882 static void SaveOutline( XmWorkspaceWidget ww, Widget grab_child )
3883 {
3884     XmWorkspaceConstraints constraints;
3885 
3886     constraints = WORKSPACE_CONSTRAINT(grab_child);
3887     OutlineChild(ww->workspace.rubberband, constraints);
3888     if( constraints && (ww->workspace.num_selected > 1) )
3889     {
3890 	ww->workspace.rubberband[1].x = ww->workspace.min_x - 1;
3891 	ww->workspace.rubberband[1].width =
3892 	  3 + ww->workspace.max_x - ww->workspace.min_x;
3893 	ww->workspace.rubberband[1].y = ww->workspace.min_y - 1;
3894 	ww->workspace.rubberband[1].height =
3895 	  3 + ww->workspace.max_y - ww->workspace.min_y;
3896 	ww->workspace.num_surrogates = 2;
3897     }
3898     else
3899 	ww->workspace.num_surrogates = 1;
3900     ww->workspace.surrogates = ww->workspace.rubberband;
3901 }
3902 
3903 
3904 /*  Subroutine: SaveOutlines
3905  *  Purpose:    Make array of outlines of each of the selected widgets
3906  */
SaveOutlines(XmWorkspaceWidget ww)3907 static void SaveOutlines( XmWorkspaceWidget ww )
3908 {
3909     XmWorkspaceConstraints constraints;
3910     Widget child;
3911     int i, j;
3912 
3913     if ((ww->workspace.num_surrogates)&&(ww->workspace.surrogates))
3914 	DiscardSurrogate (ww);
3915 
3916     /*
3917      * Purify found an abw bug here.  It appears as though num_selected
3918      * is less than the number of constraint parts which say they
3919      * are selected.  I added the following loop and the MAX. Beware
3920      */
3921     j = 0;
3922     for (i=0; i<ww->composite.num_children; i++) {
3923 	child = ww->composite.children[i];
3924 	constraints = WORKSPACE_CONSTRAINT(child);
3925 	if( constraints->workspace.is_selected == TRUE ) j++;
3926     }
3927 #if 0
3928     if (j!=ww->workspace.num_selected) {
3929 	char msg[512];
3930  	sprintf (msg,
3931 "Selection cnt (%d) != number of children who think they are selected (%d). [%s:%d]",
3932 ww->workspace.num_selected, j, __FILE__, __LINE__);
3933 	XtWarning(msg);
3934     }
3935 #endif
3936 
3937     ww->workspace.surrogates =
3938       (XRectangle *)XtMalloc(MAX(j,ww->workspace.num_selected) * sizeof(XRectangle));
3939 
3940 
3941     for( j=0, i=0; i<ww->composite.num_children; i++ )
3942     {
3943 	child = ww->composite.children[i];
3944 	constraints = WORKSPACE_CONSTRAINT(child);
3945 	if( constraints->workspace.is_selected == TRUE )
3946 	{
3947 	    OutlineChild(&(ww->workspace.surrogates[j]), constraints);
3948 	    ++j;
3949 	}
3950     }
3951     ww->workspace.num_surrogates = j;
3952 }
3953 
3954 
3955 /*  Subroutine: DiscardSurrogate
3956  *  Purpose:    Free up space occupied by array of surrogate outlines
3957  */
DiscardSurrogate(XmWorkspaceWidget ww)3958 static void DiscardSurrogate( XmWorkspaceWidget ww )
3959 {
3960     if( ww->workspace.surrogates != ww->workspace.rubberband )
3961 	XtFree((char*)ww->workspace.surrogates);
3962     ww->workspace.surrogates = NULL;
3963     ww->workspace.num_surrogates = 0;
3964 }
3965 
3966 
3967 /*  Subroutine: CreateSurrogate
3968  *  Purpose:    Create drawable outlines of some type to aid in interactive
3969  *              placement of selected children.
3970  */
3971 #define IFMIN(a,b) if((b)<a)a=(b)
3972 #define IFMAX(a,b) if((b)>a)a=(b)
CreateSurrogate(XmWorkspaceWidget ww,Widget grab_child)3973 static void CreateSurrogate( XmWorkspaceWidget ww, Widget grab_child )
3974 {
3975     Widget child;
3976     XmWorkspaceConstraints constraints;
3977     int i, init;
3978 
3979     if( ww->workspace.bandGC == NULL )
3980 	GetRubberbandGC(ww);
3981     /*  Determine the limits (used to prevent moving anything off the edge)  */
3982     init = 1;
3983     for( i=0; i<ww->composite.num_children; i++ )
3984     {
3985 	child = ww->composite.children[i];
3986 	constraints = WORKSPACE_CONSTRAINT(child);
3987 	if( constraints->workspace.is_selected == TRUE )
3988 	{
3989 	    constraints->workspace.x_left = child->core.x;
3990 	    constraints->workspace.y_upper = child->core.y;
3991 	    constraints->workspace.x_right = child->core.x + child->core.width - 1;
3992 	    constraints->workspace.y_lower = child->core.y + child->core.height - 1;
3993 	    if( init )
3994 	    {
3995 	        ww->workspace.min_x = constraints->workspace.x_left;
3996 	        ww->workspace.max_x = constraints->workspace.x_right;
3997 	        ww->workspace.min_y = constraints->workspace.y_upper;
3998 	        ww->workspace.max_y = constraints->workspace.y_lower;
3999 	        init = 0;
4000 	    }
4001 	    else
4002 	    {
4003 	        IFMIN(ww->workspace.min_x,constraints->workspace.x_left);
4004 	        IFMAX(ww->workspace.max_x,constraints->workspace.x_right);
4005 	        IFMIN(ww->workspace.min_y,constraints->workspace.y_upper);
4006 	        IFMAX(ww->workspace.max_y,constraints->workspace.y_lower);
4007 	    }
4008 	}
4009     }
4010     /*  Draw the kind of visual feedback we will use  */
4011     switch( ww->workspace.surrogate_type )
4012     {
4013       case XmOUTLINE_EACH:
4014 	SaveOutlines(ww);
4015 	break;
4016       case XmOUTLINE_PLUS:
4017 	SaveOutline(ww, grab_child);
4018 	break;
4019       case XmOUTLINE_ALL:
4020       default:
4021 	SaveOutline(ww, NULL);
4022 	break;
4023     }
4024     /*  Reset limits to reflect limits of movement  */
4025     ww->workspace.min_x = -ww->workspace.min_x;
4026     ww->workspace.max_x = ww->core.width - ww->workspace.max_x;
4027     ww->workspace.min_y = -ww->workspace.min_y;
4028     ww->workspace.max_y = ww->core.height - ww->workspace.max_y;
4029 }
4030 #undef IFMIN
4031 #undef IFMAX
4032 
4033 
4034 /*  Subroutine: DrawSurrogate
4035  *  Purpose:    Put the surrogate outline(s) on the screen and note their state
4036  */
DrawSurrogate(XmWorkspaceWidget ww)4037 static void DrawSurrogate( XmWorkspaceWidget ww )
4038 {
4039     if (ww->workspace.overlaps)
4040 	XDrawRectangles(XtDisplay(ww), XtWindow(ww), ww->workspace.badBandGC,
4041 	    ww->workspace.surrogates, ww->workspace.num_surrogates);
4042     else
4043 	XDrawRectangles(XtDisplay(ww), XtWindow(ww), ww->workspace.bandGC,
4044 	    ww->workspace.surrogates, ww->workspace.num_surrogates);
4045     ww->workspace.surrogate_is_visible = !ww->workspace.surrogate_is_visible;
4046 }
4047 
4048 
4049 /*  Subroutine: MoveSurrogate
4050  *  Purpose:    Move the position of the surrogates (to track pointer)
4051  */
MoveSurrogate(XmWorkspaceWidget ww,int delta_x,int delta_y)4052 static void MoveSurrogate( XmWorkspaceWidget ww, int delta_x, int delta_y )
4053 {
4054     int i;
4055     if( ww->workspace.surrogate_is_visible )
4056 	DrawSurrogate(ww);
4057 
4058     Overlapping (ww, delta_x, delta_y);
4059     for( i=0; i<ww->workspace.num_surrogates; i++ )
4060     {
4061 	ww->workspace.surrogates[i].x += delta_x;
4062 	ww->workspace.surrogates[i].y += delta_y;
4063     }
4064     ww->workspace.base_x += delta_x;
4065     ww->workspace.base_y += delta_y;
4066     DrawSurrogate(ww);
4067 }
4068 
Overlapping(XmWorkspaceWidget ww,int delta_x,int delta_y)4069 static Boolean Overlapping( XmWorkspaceWidget ww, int delta_x, int delta_y )
4070 {
4071     int i;
4072     Boolean overlaps = False;
4073     Region r;
4074     XPoint p[4];
4075 
4076     if (ww->workspace.check_overlap == False) return False;
4077 
4078    /*
4079     * If the position of a surrogate overlaps the position of any
4080     * tool, then don't move any surrogates.
4081     */
4082     p[0].x = ww->workspace.surrogates[0].x + delta_x;
4083     p[0].y = ww->workspace.surrogates[0].y + delta_y;
4084     if (ww->workspace.snap_to_grid) {
4085 	int x = p[0].x;
4086 	int y = p[0].y;
4087 	Align1D(&x, ww->workspace.surrogates[0].width + 1, ww->workspace.grid_width,
4088 		ww->workspace.horizontal_alignment, False);
4089 	Align1D(&y, ww->workspace.surrogates[0].height + 1, ww->workspace.grid_height,
4090 		ww->workspace.vertical_alignment, False);
4091 	p[0].x = x;
4092 	p[0].y = y;
4093     }
4094     p[2].x = p[0].x + ww->workspace.surrogates[0].width + 1;
4095     p[2].y = p[0].y + ww->workspace.surrogates[0].height + 1;
4096 
4097 
4098     p[1].x = p[2].x;
4099     p[1].y = p[0].y;
4100     p[3].x = p[0].x;
4101     p[3].y = p[2].y;
4102     r = XPolygonRegion (p, 4, WindingRule);
4103 
4104     for( i=1; i<ww->workspace.num_surrogates; i++ )
4105     {
4106 	XRectangle rect;
4107 
4108 	/*
4109 	 * Use gridded postions
4110 	 */
4111 	rect.width = ww->workspace.surrogates[i].width + 1;
4112 	rect.height = ww->workspace.surrogates[i].height + 1;
4113 	if (ww->workspace.snap_to_grid) {
4114 	    int x = ww->workspace.surrogates[i].x + delta_x;
4115 	    int y = ww->workspace.surrogates[i].y + delta_y;
4116 
4117 	    Align1D(&x, rect.width, ww->workspace.grid_width,
4118 		    ww->workspace.horizontal_alignment, False);
4119 	    rect.x = (int)x;
4120 
4121 	    Align1D(&y, rect.height, ww->workspace.grid_height,
4122 		    ww->workspace.vertical_alignment, False);
4123 	    rect.y = (int)y;
4124 	} else {
4125 	    rect.x = ww->workspace.surrogates[i].x + delta_x;
4126 	    rect.y = ww->workspace.surrogates[i].y + delta_y;
4127 	}
4128 
4129 	XUnionRectWithRegion(&rect, r, r);
4130     }
4131     for (i=0; i<ww->composite.num_children; i++)
4132     {
4133 	Widget child = ww->composite.children[i];
4134 	XmWorkspaceConstraints constraints = WORKSPACE_CONSTRAINT(child);
4135 	int x = child->core.x;
4136 	int y = child->core.y;
4137 	int width = child->core.width;
4138 	int height = child->core.height;
4139 	if (!child->core.managed) continue;
4140 	if (!constraints->workspace.is_managed) continue;
4141 	if (!child->core.mapped_when_managed) continue;
4142         if( constraints->workspace.is_selected == TRUE ) continue;
4143 	if (XRectInRegion (r, x,y,width,height) != RectangleOut) {
4144 	    overlaps = True;
4145 	    break;
4146 	}
4147     }
4148     XDestroyRegion(r);
4149     ww->workspace.overlaps = overlaps;
4150     return overlaps;
4151 }
4152 
4153 /*  Subroutine: ResizeSurrogate
4154  *  Purpose:    Resize the surrogates (to track pointer)
4155  */
ResizeSurrogate(XmWorkspaceWidget ww,int width,int height)4156 static void ResizeSurrogate( XmWorkspaceWidget ww, int width, int height )
4157 {
4158     int i;
4159     int j;
4160     Widget child;
4161     XmWorkspaceConstraints constraints;
4162 
4163     if( ww->workspace.surrogate_is_visible )
4164 	DrawSurrogate(ww);
4165 
4166     for( j=0, i=0; i<ww->composite.num_children; i++ )
4167     {
4168         child = ww->composite.children[i];
4169         constraints = WORKSPACE_CONSTRAINT(child);
4170         if( constraints->workspace.is_selected == TRUE )
4171         {
4172 	    if(constraints->workspace.is_v_resizable)
4173 		ww->workspace.surrogates[j].height = height;
4174 	    if(constraints->workspace.is_h_resizable)
4175 		ww->workspace.surrogates[j].width  = width;
4176             ++j;
4177         }
4178     }
4179     DrawSurrogate(ww);
4180 }
4181 
4182 
4183 /*  Subroutine: PerformSpaceWars
4184  *  Purpose:    Prevent move from causing overlap by shifting children in
4185  *              conflict to the right.
4186  *  Note:       Rules for "placement_policy" code:
4187  *              0: widgets are adjusted separately, initial order is maintained
4188  *              >0: selected children move en-mass (keeping spacing among them)
4189  *               1: First selected child on left maintains ordered position
4190  *               2: Selected widgets stay put, all others move right as needed
4191  */
PerformSpaceWars(XmWorkspaceWidget ww,Boolean change_managed,XEvent * event)4192 static Boolean PerformSpaceWars( XmWorkspaceWidget ww, Boolean change_managed, XEvent* event)
4193 {
4194     struct SortRec* sortlist;
4195     struct SortRec* current;
4196     struct SortRec* next;
4197     struct SortRec* right_sort;
4198     struct SortRec* sortmem;
4199     Boolean moved = False, first_selected = TRUE;
4200 
4201     if (ww->workspace.auto_arrange) return False;
4202 
4203     sortlist = GetSortList(ww, change_managed, &sortmem);
4204     if (sortlist != NULL)
4205     {
4206 	/*  Call potentially recursive (but not) space resolution algorithm  */
4207 	moved = ResolveOverlaps(ww, sortlist, &first_selected, event);
4208 	/*  Perform insertion sort of linked list, by x_right  */
4209 	for( current = sortlist->next, sortlist->next = NULL;
4210 	     current != NULL;
4211 	     current = next )
4212 	{
4213 	    next = current->next;
4214 	    right_sort = sortlist;
4215 	    while(   (right_sort->next != NULL)
4216 		  && (right_sort->next->x_right < current->x_right) )
4217 		right_sort = right_sort->next;
4218 	    current->next = right_sort->next;
4219 	    right_sort->next = current;
4220 	}
4221 	/*  Now move the windows in right to left order to avoid expose races */
4222 	(void)MoveIfDisplaced(ww, sortlist, 0, 0);
4223     }
4224     if (sortmem)
4225 	XtFree((char*)sortmem);
4226     return moved;
4227 }
4228 
4229 
4230 /*  Subroutine: ResolveOverlaps
4231  *  Purpose:    Move widgets to clear any overlaps caused by the movement of
4232  *              widgets (children marked as selected or displaced)
4233  *  Method:     The basic idea is to go through the list in outer & inner loops,
4234  *              applying the rules at each overlap.  Either outer or inner
4235  *              progress through the list can be reset back as needed to repeat
4236  *              overlap checks on sublists of widgets.
4237  */
ResolveOverlaps(XmWorkspaceWidget ww,struct SortRec * sortlist,Boolean * first_selected,XEvent * event)4238 static Boolean ResolveOverlaps( XmWorkspaceWidget ww, struct SortRec* sortlist,
4239 	                     Boolean* first_selected, XEvent* event )
4240 {
4241     struct SortRec* current;
4242     struct SortRec* current_next;
4243     struct SortRec* other;
4244     struct SortRec* other_next;
4245     short delta_x = 0;
4246     Boolean moved = False;
4247 
4248     current = sortlist;
4249     while( current != NULL )
4250     {
4251 	/*  Set up next for normal progress (may be overwridden to back up)  */
4252 	current_next = current->next;
4253 
4254 	if( (current->is_selected || current->is_displaced ) &&
4255 	    ( !current->child->core.being_destroyed ) )
4256 	{
4257 	    /*  Look for overlap with widgets before this in the sort list  */
4258 	    other = sortlist;
4259 	    while((other != current) && (!other->child->core.being_destroyed))
4260 	    {
4261 	        other_next = other->next;
4262 	        if(   (other->y_upper <= current->y_lower)
4263 	           && (other->y_lower >= current->y_upper)
4264 	           && (other->x_left <= current->x_right)
4265 	           && (other->x_right >= current->x_left) )
4266 	        {
4267 		    int policy = ww->workspace.placement_policy;
4268 	            if(   (   (policy == XmSPACE_WARS_SELECTED_STAYS)
4269 	                   && (other->is_selected == FALSE)
4270 	                   && (   current->is_selected
4271 	                       || (other->x_index > current->x_index)))
4272 	               || (   (policy == XmSPACE_WARS_LEFT_MOST_STAYS)
4273 	                   && (*first_selected == FALSE)
4274 	                   && current->is_selected) )
4275 	            {
4276 	                /*  Push overlapping widgets farther to right  */
4277 	                (void)MoveAToRightOfB(ww, other, current, event);
4278 	                /*  Make resolution repeat from displaced widget  */
4279 	                if( current_next == current->next )
4280 	                    current_next = other;
4281 	            }
4282 	            else
4283 	            {
4284 	                /*  Slide to right of overlapping widgets on left  */
4285 	                delta_x += MoveAToRightOfB(ww, current, other, event);
4286 	                /*  Check list of widgets again for more conflict  */
4287 	                other_next = sortlist;
4288 	            }
4289 		    moved = True;
4290 	        }
4291 	        other = other_next;
4292 	    }
4293 	    /*  If we moved the first_selected, adjust the other selected  */
4294 	    if(   (ww->workspace.placement_policy == XmSPACE_WARS_LEFT_MOST_STAYS)
4295 	       && *first_selected
4296 	       && current->is_selected
4297 	       && delta_x > 0 )
4298 	    {
4299 	        /*  Move all other selected widgets by the same amount  */
4300 	        RepositionSelectedChildren(current, delta_x);
4301 	        /*  Indicate that selected widgets are now fixed  */
4302 	        *first_selected = FALSE;
4303 	    }
4304 	    /*  Look for overlap with widgets after this in the sort list  */
4305 	    other = current->next;
4306 	    while(other != NULL)
4307 	    {
4308 		if (other->child->core.being_destroyed)
4309 		{
4310 		    other = other->next;
4311 		    continue;
4312 		}
4313 	        if(   (other->y_upper <= current->y_lower)
4314 	           && (other->y_lower >= current->y_upper)
4315 	           && (other->x_left <= current->x_right)
4316 	           && (other->x_right >= current->x_left) )
4317 	        {
4318 	            /*  For policies 1 and 2, selected widgets stay put     */
4319 	            if(  (ww->workspace.placement_policy != XmSPACE_WARS_SELECTED_MIGRATE)
4320 	               && other->is_selected
4321 	               && (!current->is_selected) )
4322 	            {
4323 	                /*  Slide to right of overlapping widgets on right   */
4324 	                (void)MoveAToRightOfB(ww, current, other, event);
4325 	                /*  Make resolution repeat with this widget  */
4326 	                if( current_next == current->next )
4327 	                    current_next = current;
4328 	            }
4329 	            /*  For policy 0, the order is maintained (move >sort)   */
4330 	            else
4331 	            {
4332 	                /*  Push overlapping widgets on right farther right  */
4333 	                (void)MoveAToRightOfB(ww, other, current, event);
4334 	            }
4335 		    moved = True;
4336 	        }
4337 	        other = other->next;
4338 	    }
4339 	}
4340 	current = current_next;
4341     }
4342     return moved;
4343 }
4344 
4345 
4346 /*  Subroutine: MoveAToRightOfB
4347  *  Purpose:    Move the x position of movee to right of mover.
4348  */
MoveAToRightOfB(XmWorkspaceWidget ww,struct SortRec * movee,struct SortRec * mover,XEvent * event)4349 static int MoveAToRightOfB( XmWorkspaceWidget ww,
4350 	                    struct SortRec* movee, struct SortRec *mover, XEvent* event )
4351 {
4352     int delta_x;
4353     delta_x = 1 + mover->x_right - movee->x_left;
4354     if (ww->workspace.snap_to_grid)
4355 	{
4356 	delta_x += ww->workspace.grid_width;
4357 	}
4358     else
4359 	{
4360 	delta_x += ww->workspace.collision_spacing;
4361 	}
4362 
4363     movee->x_left += delta_x;
4364     movee->x_right += delta_x;
4365     movee->constraints->workspace.x_delta =
4366       movee->x_left - movee->constraints->workspace.x_left;
4367     if( ww->workspace.snap_to_grid )
4368     {
4369 	short delta;
4370 	/*  Move into closest grid position (left&down)  */
4371 	SnapToGrid(ww, movee->child, movee->constraints, TRUE);
4372 	if ( ((movee->constraints->workspace.x_delta != 0) ||
4373 	      (movee->constraints->workspace.y_delta != 0)) &&
4374 	      !ww->workspace.suppress_callbacks)
4375 	    {
4376 	    XmWorkspacePositionChangeCallbackStruct call_data;
4377 
4378 	    call_data.child = movee->child;
4379 	    call_data.x = movee->constraints->workspace.x_left +
4380 		    movee->constraints->workspace.x_delta;
4381 	    call_data.y = movee->constraints->workspace.y_upper +
4382 		    movee->constraints->workspace.y_delta;
4383 	    call_data.event = event;
4384 	    XtCallCallbacks((Widget)
4385 		    ww, XmNpositionChangeCallback, &call_data);
4386 	    }
4387 	/*  Update link-list record to new coords  */
4388 	delta = movee->constraints->workspace.x_delta;
4389 	movee->x_left = movee->constraints->workspace.x_left + delta;
4390 	movee->x_right = movee->constraints->workspace.x_right + delta;
4391 	delta = movee->constraints->workspace.y_delta;
4392 	movee->y_upper = movee->constraints->workspace.y_upper + delta;
4393 	movee->y_lower = movee->constraints->workspace.y_lower + delta;
4394     } else
4395     {
4396 	XmWorkspacePositionChangeCallbackStruct call_data;
4397 
4398 	call_data.child = movee->child;
4399 	call_data.x = movee->constraints->workspace.x_left +
4400 		movee->constraints->workspace.x_delta;
4401 	call_data.y = movee->constraints->workspace.y_upper +
4402 		movee->constraints->workspace.y_delta;
4403 	call_data.event = event;
4404 	XtCallCallbacks((Widget)
4405 		ww, XmNpositionChangeCallback, &call_data);
4406     }
4407     movee->is_displaced = TRUE;
4408     return delta_x;
4409 }
4410 
4411 
4412 /*  Subroutine: RepositionSelectedChildren
4413  *  Purpose:    Move the selected children to follow the leader, then
4414  *              change sort order so everybody isn't necessarily forced
4415  *              to the right of them
4416  */
RepositionSelectedChildren(struct SortRec * current,short delta_x)4417 static void RepositionSelectedChildren( struct SortRec* current, short delta_x )
4418 {
4419     struct SortRec* other;
4420 
4421     /*  Look for the other selected ones  */
4422     other = current->next;
4423     while( other != NULL )
4424     {
4425 	if( other->is_selected )
4426 	{
4427 	    /*  Reposition each other selected one  */
4428 	    other->x_left += delta_x;
4429 	    other->x_right += delta_x;
4430 	    other->is_displaced = TRUE;
4431 	}
4432 	/*  Keep track of connection point  */
4433 	current = other;
4434 	other = other->next;
4435     }
4436 }
4437 
4438 
4439 /*  Subroutine: MoveIfDisplaced
4440  *  Purpose:    Tail recursive call to move chidren being moved rightmost first.
4441  */
MoveIfDisplaced(XmWorkspaceWidget ww,struct SortRec * sortlist,int max_x,int max_y)4442 static Boolean MoveIfDisplaced( XmWorkspaceWidget ww, struct SortRec *sortlist,
4443 	                        int max_x, int max_y )
4444 {
4445     if( sortlist->is_selected || sortlist->is_displaced )
4446     {
4447 	if( sortlist->x_right > max_x )
4448 	    max_x = sortlist->x_right;
4449 	if( sortlist->y_lower > max_y )
4450 	    max_y = sortlist->y_lower;
4451     }
4452     if( sortlist->next != NULL )
4453     {
4454 	/*  Not the end of the sortlist, recurse  */
4455 	if( MoveIfDisplaced(ww, sortlist->next, max_x, max_y) == FALSE )
4456 	{
4457 	    ResetChild(ww, sortlist->constraints);
4458 	    return FALSE;
4459 	}
4460     }
4461     else if( (max_x >= ww->core.width) || (max_y >= ww->core.height) )
4462     {
4463 	/*  End of the sortlist, check necessity of resize  */
4464 	XtWidgetGeometry request, reply;
4465 	XtGeometryResult result;
4466 
4467 	request.width = MAX(max_x + 1, ww->core.width);
4468 	request.height = MAX(max_y + 1, ww->core.height);
4469 	request.request_mode = CWWidth | CWHeight;
4470 	result = XtMakeGeometryRequest((Widget)ww, &request, &reply);
4471 	ReallocCollideLists(ww);
4472 	if( (result == XtGeometryNo) || (result == XtGeometryAlmost) )
4473 	{
4474 	    ResetChild(ww, sortlist->constraints);
4475 	    return FALSE;
4476 	}
4477     }
4478     if( sortlist->is_selected || sortlist->is_displaced )
4479 	MoveChild(ww, sortlist->child, sortlist->constraints);
4480     return TRUE;
4481 }
4482 
4483 
4484 /*  Subroutine: GetSortList
4485  *  Purpose:    Create a list of chidren sorted by x position and priority
4486  *  Note:       Priority is x, then non-selected, then y where x and y can
4487  *              be left, center, or right based on policy variable.
4488  *  Note:       List is used to arbitrate who moves in cases of overlap
4489  */
4490 static struct SortRec *GetSortList( XmWorkspaceWidget ww,
4491 	                            Boolean change_managed,
4492 	                            struct SortRec** sortmem )
4493 #ifdef EXPLAIN
4494      XmWorkspaceWidget ww;      /* The widget                               */
4495      Boolean change_managed;    /* Need to mark children just managed       */
4496      struct SortRec** sortmem;  /* Xtalloc pointer to be XtFree'd when done */
4497 #endif
4498 {
4499     struct SortRec* sortlist;
4500     struct SortRec* heap;
4501 #ifdef DEBUG
4502     /*  Stupid debugger can't access variables not defined at top  */
4503     struct SortRec* current;
4504     struct SortRec* place_search;
4505     XmWorkspaceConstraints nc;
4506 #endif
4507     int i;
4508     int count;
4509 
4510     count = ww->composite.num_children;
4511     if (count > 0)
4512 	heap = (struct SortRec *) XtCalloc(count, sizeof(struct SortRec));
4513     else
4514 	heap = NULL;
4515 
4516     *sortmem = heap;
4517     sortlist = NULL;
4518     for( i = 0; i < count; i++ )
4519     {
4520 #ifndef DEBUG
4521 	/*  Debugger can't access variables not defined at top!  */
4522 	struct SortRec* current;
4523 	XmWorkspaceConstraints nc;
4524 #endif
4525 	current = &(heap[i]);
4526 	current->child = ww->composite.children[i];
4527 	nc = WORKSPACE_CONSTRAINT(current->child);
4528 	current->constraints = nc;
4529 	/*  Accomodate special case of children just placed by application  */
4530 	if( change_managed && nc->workspace.is_managed && nc->workspace.is_newly_managed )
4531 	{
4532 	    current->is_displaced = TRUE;
4533 	    nc->workspace.is_newly_managed = FALSE;
4534 	}
4535 	else
4536 	    current->is_displaced = FALSE;
4537 	/*  Only count those children that are managed (mapped to screen)  */
4538 	if( nc->workspace.is_managed )
4539 	{
4540 	    /*  Proposed position is offset by delta's  */
4541 	    current->x_left = nc->workspace.x_left + nc->workspace.x_delta;
4542 	    current->x_right = nc->workspace.x_right + nc->workspace.x_delta;
4543 	    current->y_upper = nc->workspace.y_upper + nc->workspace.y_delta;
4544 	    current->y_lower = nc->workspace.y_lower + nc->workspace.y_delta;
4545 	    current->is_selected = nc->workspace.is_selected;
4546 	    /*  Index is x position only and has been offset as appropriate  */
4547 	    /*  Make an index with x then selected then y absolute priority  */
4548 	    current->x_index = nc->workspace.x_index * 16384;
4549 	    if( current->is_selected == (POLICY(ww) != XmSPACE_WARS_SELECTED_STAYS) )
4550 	        /*  Selected children come first (policy > 1) or last (< 2)  */
4551 	        current->x_index += 8192;
4552 	    if( ww->workspace.sort_policy == XmALIGNMENT_BEGINNING )
4553 	        current->x_index += current->y_upper;
4554 	    else if( ww->workspace.sort_policy == XmALIGNMENT_CENTER )
4555 	        current->x_index +=
4556 	            (nc->workspace.y_center + nc->workspace.y_delta);
4557 	    else
4558 	        current->x_index += current->y_lower;
4559 	    if( (sortlist == NULL) || (sortlist->x_index > current->x_index) )
4560 	    {
4561 	        current->next = sortlist;
4562 	        sortlist = current;
4563 	    }
4564 	    else
4565 	    {
4566 	        /*  Perform insertion sort of linked list  */
4567 #ifndef DEBUG
4568 	        struct SortRec* place_search;
4569 #endif
4570 	        place_search = sortlist;
4571 	        while(   (place_search->next != NULL)
4572 	              && (place_search->next->x_index < current->x_index) )
4573 	            place_search = place_search->next;
4574 	        current->next = place_search->next;
4575 	        place_search->next = current;
4576 	    }
4577 	}
4578     }
4579     return sortlist;
4580 }
4581 
4582 /*  Subroutine: XmCreateWorkspaceLine
4583  *  Purpose:    Export routine to register an application line for management
4584  */
4585 XmWorkspaceLine
XmCreateWorkspaceLine(XmWorkspaceWidget ww,int color,Widget source,short src_x_offset,short src_y_offset,Widget dest,short dst_x_offset,short dst_y_offset)4586   XmCreateWorkspaceLine( XmWorkspaceWidget ww, int color,
4587 	                 Widget source, short src_x_offset, short src_y_offset,
4588 	                 Widget dest, short dst_x_offset, short dst_y_offset )
4589 {
4590     XmWorkspaceConstraints constraints;
4591     int i;
4592     XmWorkspaceLine line;
4593     Boolean source_not_found, destination_not_found;
4594 
4595     /*  Check to make sure this one is here  */
4596     source_not_found = TRUE;
4597     destination_not_found = TRUE;
4598     for( i=0; i<ww->composite.num_children; i++ )
4599     {
4600 	if( ww->composite.children[i] == source )
4601 	    source_not_found = FALSE;
4602 	if( ww->composite.children[i] == dest )
4603 	    destination_not_found = FALSE;
4604     }
4605     if( source_not_found || destination_not_found )
4606     {
4607 	XtWarning("Attempt to create line for non-child widget.");
4608 	return NULL;
4609     }
4610 
4611     line = (XmWorkspaceLine)XtMalloc(sizeof(XmWorkspaceLineRec));
4612     memset(line, 0, sizeof(XmWorkspaceLineRec));
4613 
4614     /*  Set the source end and install it in the source's list  */
4615     line->source = source;
4616     line->src_x = source->core.x + src_x_offset;
4617     line->src_y = source->core.y + src_y_offset;
4618     line->src_x_offset = src_x_offset;
4619     constraints = WORKSPACE_CONSTRAINT(source);
4620     line->src_next = constraints->workspace.source_lines;
4621     constraints->workspace.source_lines = line;
4622     /*  Set the destination end and install it in the destination's list  */
4623     line->destination = dest;
4624     line->dst_x = dest->core.x + dst_x_offset;
4625     line->dst_y = dest->core.y + dst_y_offset;
4626     line->dst_x_offset = dst_x_offset;
4627     constraints = WORKSPACE_CONSTRAINT(dest);
4628     line->dst_next = constraints->workspace.destination_lines;
4629     constraints->workspace.destination_lines = line;
4630     /*  Set the color or the default color  */
4631     if( color == -1 )
4632 	line->color = ww->manager.foreground;
4633     else
4634 	line->color = color;
4635     /*  Install it in the master line list  */
4636     line->next = ww->workspace.lines;
4637     ww->workspace.lines = line;
4638     ww->workspace.num_lines++;
4639     line->is_to_be_moved = TRUE;
4640 
4641     /*  Get the GC and set it to this color  */
4642     if( XtIsRealized((Widget)ww) )
4643     {
4644 	if( xmWorkspaceClassRec.workspace_class.lineGC == NULL )
4645 	    InitLineGC(ww, line->color);
4646 	else
4647 	    XSetForeground(XtDisplay(ww),
4648 	                   xmWorkspaceClassRec.workspace_class.lineGC,
4649 	                   line->color);
4650 	line->is_to_be_drawn = FALSE;
4651     }
4652     else
4653 	line->is_to_be_drawn = TRUE;
4654 
4655     if (ww->workspace.line_drawing_enabled)
4656 	{
4657 	RerouteLines(ww, FALSE);
4658 	/* Manually clear out the exposure area */
4659 	if (XtWindow(ww))
4660 	    XClearArea(XtDisplay(ww), XtWindow(ww), ww->workspace.expose_left,
4661 	        ww->workspace.expose_upper,
4662 	        ww->workspace.expose_right - ww->workspace.expose_left,
4663 	        ww->workspace.expose_lower - ww->workspace.expose_upper, False);
4664         RefreshLines(ww);
4665 	}
4666     UnsetExposureArea(ww);
4667     line->is_to_be_collapsed = TRUE;
4668     return line;
4669 }
4670 
4671 /*  Subroutine: SetLineRoute
4672  *  Purpose:    Set the points for drawing the line
4673  */
SetLineRoute(XmWorkspaceWidget ww,XmWorkspaceLine new)4674 static void SetLineRoute( XmWorkspaceWidget ww, XmWorkspaceLine new )
4675 {
4676 short i;
4677 LineElement *line_list;
4678 int cost, failnum;
4679 
4680     /* Point 1 */
4681     new->point[0].x = new->src_x;
4682     new->point[0].y = new->src_y;
4683 
4684     if( !ww->workspace.manhattan_route )
4685 	{
4686 	/* Point 2 */
4687 	new->point[1].x = new->dst_x;
4688 	new->point[1].y = new->dst_y;
4689 
4690 	new->num_points = 2;
4691 	}
4692 
4693     /*  If doing Manhattan routing...  */
4694     else
4695     {
4696         {
4697         line_list = Manhattan(ww, new->src_x, new->src_y, new->dst_x,
4698                                 new->dst_y, 0, &cost, new->source,
4699 				new->destination, new, &failnum);
4700 	if (cost < 999)
4701 	    {
4702             CopyPointsToLine(ww, line_list, new);
4703 	    AddLineToCollideList(ww, new);
4704 	    }
4705 	else
4706 	    {
4707 	    XmWorkspaceErrorCallbackStruct call_value;
4708 
4709 	    new->num_points = -1;
4710 
4711 	    call_value.reason = XmCR_ERROR;
4712 	    call_value.failnum = failnum;
4713 	    call_value.source = new->source;
4714 	    call_value.destination = new->destination;
4715 	    call_value.srcx = new->src_x;
4716 	    call_value.srcy = new->src_y;
4717 	    call_value.dstx = new->dst_x;
4718 	    call_value.dsty = new->dst_y;
4719 	    XtCallCallbacks((Widget)ww, XmNerrorCallback, &call_value);
4720 	    }
4721         }
4722         if (line_list)
4723             FreeLineElementList(line_list);
4724     }
4725     /*  Define the bounding box of this line  */
4726     new->x_left = new->x_right = new->point[0].x;
4727     new->y_upper = new->y_lower = new->point[0].y;
4728     for( i=1; i<new->num_points; i++ )
4729     {
4730 	if( new->point[i].x < new->x_left )
4731 	    new->x_left = new->point[i].x;
4732 	else if( new->point[i].x > new->x_right )
4733 	    new->x_right = new->point[i].x;
4734 	if( new->point[i].y < new->y_upper )
4735 	    new->y_upper = new->point[i].y;
4736 	else if( new->point[i].y > new->y_lower )
4737 	    new->y_lower = new->point[i].y;
4738     }
4739     AugmentExposureAreaForLine(ww, new);
4740 }
4741 
4742 /*  Subroutine: RerouteLines
4743  *  Purpose:    Find suitable routes for all lines waiting to be moved
4744  */
RerouteLines(XmWorkspaceWidget ww,Boolean reroute_all)4745 void RerouteLines( XmWorkspaceWidget ww, Boolean reroute_all )
4746 {
4747 XmWorkspaceLine line;
4748 Widget child;
4749 int i;
4750 
4751     if (!ww->workspace.line_drawing_enabled)
4752 	return;
4753 
4754     for( i=ww->composite.num_children-1; i>=0; i-- )
4755         {
4756         child = ww->composite.children[i];
4757 	if (child->core.being_destroyed)
4758 	    {
4759 	    XmWorkspaceConstraints nc;
4760 	    nc = WORKSPACE_CONSTRAINT(child);
4761 	    if (!nc->workspace.line_invisibility)
4762 		DeleteWidgetFromCollideList(child);
4763 	    }
4764 	}
4765 
4766     if(ww->workspace.lines)
4767     {
4768     	MarkCommonLines(ww);
4769 	for (line = ww->workspace.lines; line; line = line->next)
4770 	{
4771 	    if( line->is_to_be_moved || reroute_all )
4772 	    {
4773             	RemoveLineFromCollideList(ww, line);
4774 	    }
4775 	}
4776 	for (line = ww->workspace.lines; line; line = line->next)
4777 	{
4778 	    if( line->is_to_be_moved || reroute_all )
4779 	    {
4780 	        /*  Adjust exposure area to include all of old line's route  */
4781 	        SetLineRoute(ww, line);
4782 	        line->is_to_be_drawn = TRUE;
4783 	        line->is_to_be_moved = FALSE;
4784 	    }
4785 	}
4786     }
4787 }
4788 
4789 /*  Subroutine: XmDestroyWorkspaceLine
4790  *  Purpose:    Exported routine to remove an application line
4791  */
XmDestroyWorkspaceLine(XmWorkspaceWidget ww,XmWorkspaceLine line,Boolean middle_of_group)4792 void XmDestroyWorkspaceLine( XmWorkspaceWidget ww, XmWorkspaceLine line,
4793 	                     Boolean middle_of_group )
4794 {
4795     if( line != NULL )
4796     {
4797 	/* Before this line is removed, mark all lines that share its source
4798 	   or destination to be re-routed, since they may have been following
4799 	   this lines path */
4800 	line->is_to_be_moved = TRUE;
4801 	MarkCommonLines(ww);
4802 	AugmentExposureAreaForLine(ww, line);
4803 	if( DestroyLine(ww, line) )
4804 	{
4805             RemoveLineFromCollideList(ww, line);
4806             XSetForeground(XtDisplay(ww),
4807 	               xmWorkspaceClassRec.workspace_class.lineGC,
4808 	               ww->core.background_pixel);
4809 	if(ww->workspace.line_drawing_enabled)
4810 	{
4811 	    RerouteLines(ww, FALSE);
4812 	    /* Manually clear out the exposure area */
4813 	    if (XtWindow(ww))
4814 		XClearArea(XtDisplay(ww), XtWindow(ww), ww->workspace.expose_left,
4815 		    ww->workspace.expose_upper,
4816 		    ww->workspace.expose_right - ww->workspace.expose_left,
4817 		    ww->workspace.expose_lower - ww->workspace.expose_upper, True);
4818 	    if (!middle_of_group) RefreshLines(ww);
4819 	    UnsetExposureArea(ww);
4820 	}
4821 	XtFree((char*)line);
4822 	}
4823     }
4824     else
4825     {
4826 	if( ww->workspace.expose_left < ww->workspace.expose_right )
4827 	    middle_of_group = FALSE;
4828 	else
4829 	    middle_of_group = TRUE;
4830     }
4831     if( !middle_of_group && ww->workspace.line_drawing_enabled )
4832     {
4833 	RerouteLines(ww, FALSE);
4834 	RefreshLines(ww);
4835 	UnsetExposureArea(ww);
4836     }
4837 }
4838 
4839 /*  Subroutine: DestroyLine
4840  *  Purpose:    Remove a single line
4841  *  Returns:    False if line was not in list
4842  */
DestroyLine(XmWorkspaceWidget ww,XmWorkspaceLine old)4843 static Boolean DestroyLine( XmWorkspaceWidget ww, XmWorkspaceLine old )
4844 {
4845     XmWorkspaceLine line;
4846     XmWorkspaceConstraints constraints;
4847 
4848     /*  Remove link from main list  */
4849     if( ww->workspace.lines == old )
4850 	ww->workspace.lines = old->next;
4851     else
4852     {
4853 	for( line = ww->workspace.lines;
4854 	     (line->next && (line->next != old));
4855 	     line = line->next );
4856 	if( line->next == NULL )
4857 	{
4858 	    XtWarning("Attempt to destroy line not known to workspace.");
4859 	    return False;
4860 	}
4861 	line->next = old->next;
4862     }
4863     ww->workspace.num_lines--;
4864     /*  Remove line from source list  */
4865     constraints = WORKSPACE_CONSTRAINT(old->source);
4866     if( constraints->workspace.source_lines == old )
4867 	constraints->workspace.source_lines = old->src_next;
4868     else
4869     {
4870 	for( line = constraints->workspace.source_lines;
4871 	     (line->src_next && (line->src_next != old));
4872 	     line = line->src_next );
4873 	if( line->src_next )
4874 	    line->src_next = old->src_next;
4875     }
4876     /*  Remove line from destination list  */
4877     constraints = WORKSPACE_CONSTRAINT(old->destination);
4878     if( constraints->workspace.destination_lines == old )
4879 	constraints->workspace.destination_lines = old->dst_next;
4880     else
4881     {
4882 	for( line = constraints->workspace.destination_lines;
4883 	     (line->dst_next && (line->dst_next != old));
4884 	     line = line->dst_next );
4885 	if( line->dst_next )
4886 	    line->dst_next = old->dst_next;
4887     }
4888     return TRUE;
4889 }
4890 
4891 
4892 #define IF_MORE(a,b) if((a)>(b))(b)=(a)
4893 #define IF_LESS(a,b) if((a)<(b))(b)=(a)
4894 /*  Subroutine: AugmentExposeAreaForLine
4895  *  Purpose:    Extend expose area to include box of this line
4896  */
AugmentExposureAreaForLine(XmWorkspaceWidget ww,XmWorkspaceLine line)4897 void AugmentExposureAreaForLine( XmWorkspaceWidget ww,
4898 	                                XmWorkspaceLine line )
4899 {
4900     short line_excess = ww->workspace.line_thickness;
4901     IF_LESS(line->x_left - line_excess, ww->workspace.expose_left);
4902     IF_MORE(line->x_right + line_excess, ww->workspace.expose_right);
4903     IF_LESS(line->y_upper - line_excess, ww->workspace.expose_upper);
4904     IF_MORE(line->y_lower + line_excess, ww->workspace.expose_lower);
4905 }
4906 #undef IF_MORE
4907 #undef IF_LESS
4908 
4909 
4910 /*  Subroutine: UnsetExposureArea
4911  *  Purpose:    Set exposure area to show nothing and be ready for
4912  *              first augmentation
4913  */
UnsetExposureArea(XmWorkspaceWidget ww)4914 static void UnsetExposureArea( XmWorkspaceWidget ww )
4915 {
4916     ww->workspace.expose_left = ww->core.width;
4917     ww->workspace.expose_right = -1;
4918     ww->workspace.expose_upper = ww->core.height;
4919     ww->workspace.expose_lower = -1;
4920 }
4921 
4922 
4923 /*  Subroutine: RefreshLines
4924  *  Purpose:    Redraw all lines marked for movement, or within identified
4925  *              expose bounds.
4926  */
RefreshLines(XmWorkspaceWidget ww)4927 void RefreshLines( XmWorkspaceWidget ww )
4928 {
4929 XmWorkspaceLine line, tmp_line;
4930 int             color = -1;
4931 XRectangle      rect;
4932 
4933     if(!ww->workspace.line_drawing_enabled)
4934 	return;
4935     if (XtIsRealized((Widget)ww) == False)
4936 	return ;
4937 
4938     /*
4939      * Redraw all lines that are in the cleared region, but clip the drawing
4940      * to the cleared region.  This eliminate undesired side effects for
4941      * "copied" lines with haloing.  The alternative is to redraw all lines
4942      * every time (too slow).
4943      */
4944     if (ww->workspace.lines)
4945     {
4946 	if ( (ww->workspace.expose_right > ww->workspace.expose_left) &&
4947 	     (ww->workspace.expose_upper < ww->workspace.expose_lower) )
4948 	{
4949 	    rect.x      = ww->workspace.expose_left;
4950 	    rect.y      = ww->workspace.expose_upper;
4951 	    rect.width  = ww->workspace.expose_right -
4952 			  ww->workspace.expose_left +
4953 			  ww->workspace.line_thickness;
4954 	    rect.height = ww->workspace.expose_lower -
4955 			  ww->workspace.expose_upper +
4956 			  ww->workspace.line_thickness;
4957 	    XSetClipRectangles( XtDisplay(ww),
4958 			        xmWorkspaceClassRec.workspace_class.lineGC,
4959 			        0, 0,
4960 			        &rect, 1,
4961 			        YXBanded);
4962 	    XSetClipRectangles( XtDisplay(ww),
4963 			        xmWorkspaceClassRec.workspace_class.lineGC2,
4964 			        0, 0,
4965 			        &rect, 1,
4966 			        YXBanded);
4967 	}
4968 	else
4969 	{
4970 	    XSetClipMask( XtDisplay(ww),
4971 			  xmWorkspaceClassRec.workspace_class.lineGC,
4972 			  None);
4973 	    XSetClipMask( XtDisplay(ww),
4974 			  xmWorkspaceClassRec.workspace_class.lineGC2,
4975 			  None);
4976 	}
4977     }
4978     if( (line = ww->workspace.lines) )
4979     {
4980 	while( line )
4981 	{
4982 	    /*  If lines' region was cleared, redo it  */
4983 	    if( (line->x_right >= ww->workspace.expose_left)  &&
4984 		(line->x_left <= ww->workspace.expose_right)  &&
4985 		(line->y_lower >= ww->workspace.expose_upper) &&
4986 		(line->y_upper <= ww->workspace.expose_lower) )
4987 	    {
4988 		line->is_to_be_drawn = True;
4989 	    }
4990 	    line->two_phase_draw = False;
4991 	    line = line->next;
4992 	}
4993     }
4994 
4995     if( (line = ww->workspace.lines) )
4996     {
4997 	while( line )
4998 	{
4999 	    if( line->is_to_be_drawn )
5000 		/*
5001 		 * Look through the rest of the list and see if we have a
5002 		 * "sibling" line (same source x,y).  If we do, mark it for
5003 		 * 2 phase drawing and defer.
5004 		 */
5005 	    {
5006 		tmp_line = line->next;
5007 		while( tmp_line )
5008 		{
5009 		    if ( (line->point[0].x == tmp_line->point[0].x) &&
5010 		         (line->point[0].y == tmp_line->point[0].y) )
5011 		    {
5012 			line->two_phase_draw = True;
5013 			tmp_line->two_phase_draw = True;
5014 			break;
5015 		    }
5016 		    tmp_line = tmp_line->next;
5017 		}
5018 		if( XtIsRealized((Widget)ww) )
5019 		{
5020 		    color = line->color;
5021 		    /*  Get the GC and set it to this color  */
5022 		    if( xmWorkspaceClassRec.workspace_class.lineGC == NULL )
5023 			InitLineGC(ww, color);
5024 		    else
5025 			XSetForeground
5026 			  (XtDisplay(ww),
5027 			   xmWorkspaceClassRec.workspace_class.lineGC,
5028 			   color);
5029 		}
5030 		if( ( XtIsRealized((Widget)ww) ) && (!line->two_phase_draw) )
5031 		{
5032 		    if (line->num_points > 0)
5033 		    {
5034 			XDrawLines(XtDisplay(ww), XtWindow(ww),
5035 			       xmWorkspaceClassRec.workspace_class.lineGC2,
5036 			       line->point, line->num_points,
5037 			       CoordModeOrigin);
5038 
5039 			XDrawLines(XtDisplay(ww), XtWindow(ww),
5040 			       xmWorkspaceClassRec.workspace_class.lineGC,
5041 			       line->point, line->num_points,
5042 			       CoordModeOrigin);
5043 			line->is_to_be_drawn = FALSE;
5044 		    }
5045 	        }
5046 	    }
5047 	    line = line->next;
5048 	}
5049 	/*
5050 	 * Do the 2 phase draw
5051 	 */
5052 	line = ww->workspace.lines;
5053 	while( line )
5054 	{
5055 	    if( line->two_phase_draw )
5056 	    {
5057 		if( XtIsRealized((Widget)ww) )
5058 		{
5059 		    color = line->color;
5060 		    /*  Get the GC and set it to this color  */
5061 		    if( xmWorkspaceClassRec.workspace_class.lineGC == NULL )
5062 			InitLineGC(ww, color);
5063 		    else
5064 			XSetForeground
5065 			  (XtDisplay(ww),
5066 			   xmWorkspaceClassRec.workspace_class.lineGC,
5067 			   color);
5068 		}
5069 		if( XtIsRealized((Widget)ww) )
5070 		{
5071 		    if (line->num_points > 0)
5072 		    {
5073 			XDrawLines(XtDisplay(ww), XtWindow(ww),
5074 			       xmWorkspaceClassRec.workspace_class.lineGC2,
5075 			       line->point, line->num_points,
5076 			       CoordModeOrigin);
5077 
5078 		    }
5079 		}
5080 		tmp_line = line->next;
5081 		while( tmp_line )
5082 		{
5083 		    if ( (tmp_line->two_phase_draw) &&
5084 			 (tmp_line->point[0].x == line->point[0].x) &&
5085 			 (tmp_line->point[0].y == line->point[0].y) )
5086 		    {
5087 			if (tmp_line->num_points > 0)
5088 			{
5089 			    XDrawLines(XtDisplay(ww), XtWindow(ww),
5090 				   xmWorkspaceClassRec.workspace_class.lineGC2,
5091 				   tmp_line->point, tmp_line->num_points,
5092 				   CoordModeOrigin);
5093 
5094 			}
5095 		    }
5096 		    tmp_line = tmp_line->next;
5097 		}
5098 
5099 		if( XtIsRealized((Widget)ww) )
5100 		{
5101 		    if (line->num_points > 0)
5102 		    {
5103 			XDrawLines(XtDisplay(ww), XtWindow(ww),
5104 			       xmWorkspaceClassRec.workspace_class.lineGC,
5105 			       line->point, line->num_points,
5106 			       CoordModeOrigin);
5107 
5108 			line->two_phase_draw = False;
5109 			line->is_to_be_drawn = False;
5110 		    }
5111 		}
5112 		tmp_line = line->next;
5113 		while( tmp_line )
5114 		{
5115 		    if ( (tmp_line->two_phase_draw) &&
5116 			 (tmp_line->point[0].x == line->point[0].x) &&
5117 			 (tmp_line->point[0].y == line->point[0].y) )
5118 		    {
5119 			if (tmp_line->num_points > 0)
5120 			{
5121 			    XDrawLines(XtDisplay(ww), XtWindow(ww),
5122 				   xmWorkspaceClassRec.workspace_class.lineGC,
5123 				   tmp_line->point, tmp_line->num_points,
5124 				   CoordModeOrigin);
5125 
5126 			    tmp_line->two_phase_draw = False;
5127 			    tmp_line->is_to_be_drawn = False;
5128 			}
5129 		    }
5130 		    tmp_line = tmp_line->next;
5131 	        }
5132 	    }
5133 	    line = line->next;
5134 	}
5135     }
5136 }
5137 /*  Subroutine: InitLineGC
5138  *  Purpose:    Create the GC for drawing application lines in the workspace
5139  */
InitLineGC(XmWorkspaceWidget ww,int color)5140 static void InitLineGC( XmWorkspaceWidget ww, int color )
5141 {
5142     XGCValues values;
5143     unsigned long valuemask;
5144 
5145     values.foreground = (unsigned long)color;
5146     values.function = GXcopy;
5147     values.line_width = ww->workspace.line_thickness;
5148     if( values.line_width > 0 )
5149     {
5150 	valuemask = GCForeground | GCFunction | GCLineWidth ;
5151     }
5152     else
5153 	valuemask = GCForeground | GCFunction ;
5154     /*Use X call in place of Xt call since we will be changing gc's values  */
5155     xmWorkspaceClassRec.workspace_class.lineGC =
5156       XtGetGC((Widget)ww, valuemask, &values);
5157 
5158     /*
5159      * Create the line halo GC
5160      */
5161     values.foreground = ww->core.background_pixel;
5162     values.line_width = ww->workspace.line_thickness +
5163 			2*ww->workspace.halo_thickness;
5164     xmWorkspaceClassRec.workspace_class.lineGC2 =
5165       XtGetGC((Widget)ww, valuemask, &values);
5166 }
5167 
5168 
5169 /*  Subroutine: CvtStringToWorkspaceType
5170  *  Purpose:    Resource converter to be registered in class init to handle
5171  *              strings XXXX_XXXX which represent XmXXXX_XXXX from
5172  *              .Xdefaults file
5173  */
CvtStringToWorkspaceType(XrmValue * args,Cardinal num_args,XrmValue * from_val,XrmValue * to_val)5174 static void CvtStringToWorkspaceType( XrmValue* args, Cardinal num_args,
5175 	                             XrmValue* from_val, XrmValue* to_val )
5176 {
5177    char * in_str = (char *) (from_val->addr);
5178    static unsigned char i;
5179 
5180    to_val->size = sizeof (unsigned char);
5181    to_val->addr = (void *) &i;
5182 
5183    if( StringsAreEqual(in_str, "include_all") )
5184       i = XmINCLUDE_ALL;
5185    else if( StringsAreEqual(in_str, "include_any") )
5186       i = XmINCLUDE_ANY;
5187    else if( StringsAreEqual(in_str, "include_center") )
5188       i = XmINCLUDE_CENTER;
5189    else if( StringsAreEqual(in_str, "accent_background") )
5190       i = XmACCENT_BACKGROUND;
5191    else if( StringsAreEqual(in_str, "accent_border") )
5192       i = XmACCENT_BORDER;
5193    else if( StringsAreEqual(in_str, "accent_none") )
5194       i = XmACCENT_NONE;
5195    else if( StringsAreEqual(in_str, "outline_each") )
5196       i = XmOUTLINE_EACH;
5197    else if( StringsAreEqual(in_str, "outline_all") )
5198       i = XmOUTLINE_ALL;
5199    else if( StringsAreEqual(in_str, "outline_plus") )
5200       i = XmOUTLINE_PLUS;
5201    else if( StringsAreEqual(in_str, "draw_none") )
5202       i = XmDRAW_NONE;
5203    else if( StringsAreEqual(in_str, "draw_hash") )
5204       i = XmDRAW_HASH;
5205    else if( StringsAreEqual(in_str, "draw_line") )
5206       i = XmDRAW_LINE;
5207    else if( StringsAreEqual(in_str, "draw_outline") )
5208       i = XmDRAW_OUTLINE;
5209    else
5210    {
5211       to_val->size = 0;
5212       to_val->addr = NULL;
5213       XtStringConversionWarning ((char *)from_val->addr, XmRWorkspaceType);
5214    }
5215 }
5216 
StringsAreEqual(register char * in_str,register char * test_str)5217 static Boolean StringsAreEqual( register char * in_str,
5218 	                        register char * test_str )
5219 {
5220    register int i;
5221    register int j;
5222 
5223    for (;;)
5224    {
5225       i = *in_str;
5226       j = *test_str;
5227 
5228       if (isupper (i)) i = tolower (i);
5229       if (i != j) return (False);
5230       if (i == 0) return (True);
5231 
5232       in_str++;
5233       test_str++;
5234    }
5235 }
5236 
5237 
5238 /*  Subroutine: XmCreateWorkspace
5239  *  Purpose:    Externally accessable convenience function to create
5240  *              workspace widget
5241  */
XmCreateWorkspace(Widget parent,char * name,Arg arglist[],int argCount)5242 Widget XmCreateWorkspace( Widget parent, char* name,
5243 	                  Arg arglist[], int argCount )
5244 {
5245     return XtCreateWidget(name, xmWorkspaceWidgetClass, parent,
5246 	                  arglist, argCount);
5247 }
5248 
5249 
5250 /*  Subroutine: AddWorkspaceAddCallback
5251  *  Purpose:    Register a callback for selection changes due to workspace
5252  *              widget management events.
5253  *  Note:       This routine must be used in place of XtAddCallback since
5254  *              Xt code cannot handle callback lists in constraint fields.
5255  */
XmWorkspaceAddCallback(Widget child,String name,XtCallbackProc callback,XtPointer client_data)5256 void XmWorkspaceAddCallback( Widget child, String name,
5257 	                     XtCallbackProc callback, XtPointer client_data )
5258 {
5259     XmWorkspaceConstraints constraints;
5260 
5261     /*  Bypass Xt weakness of not handling callbacks in constraint resources  */
5262     if( callback )
5263     {
5264 	constraints = WORKSPACE_CONSTRAINT(child);
5265 	if( name && STRCMP(name, XmNaccentCallback) == 0 )
5266 	    AddConstraintCallback(child,
5267 	                          &(constraints->workspace.accent_callbacks),
5268 	                          callback, client_data);
5269 	else if( name && STRCMP(name, XmNselectionCallback) == 0 )
5270 	    AddConstraintCallback(child,
5271 	                          &(constraints->workspace.select_callbacks),
5272 	                          callback, client_data);
5273 	else if( name && STRCMP(name, XmNresizingCallback) == 0 )
5274 	    AddConstraintCallback(child,
5275 	                          &(constraints->workspace.resizing_callbacks),
5276 	                          callback, client_data);
5277     }
5278 }
5279 
5280 #if 0
5281 void XmWorkspaceGetMaxWidthHeight(Widget w, int *width, int *height)
5282 {
5283 XmWorkspaceWidget ww = (XmWorkspaceWidget)w;
5284 int i;
5285 Widget child;
5286 XmWorkspaceConstraints constraints;
5287 
5288     *width = *height = 0;
5289 
5290     for( i=0; i<ww->composite.num_children; i++ )
5291     {
5292         child = ww->composite.children[i];
5293 	if(!child->core.being_destroyed)
5294 	{
5295 	    constraints = WORKSPACE_CONSTRAINT(child);
5296 	    if(constraints->workspace.x_right > *width)
5297 		*width = constraints->workspace.x_right;
5298 	    if(constraints->workspace.y_lower > *height)
5299 		*height = constraints->workspace.y_lower;
5300 	}
5301     }
5302     (*width)++;
5303     (*height)++;
5304 }
5305 #else
XmWorkspaceGetMaxWidthHeight(Widget w,int * width,int * height)5306 void XmWorkspaceGetMaxWidthHeight(Widget w, int *width, int *height)
5307 {
5308 XmWorkspaceWidget ww = (XmWorkspaceWidget)w;
5309 int i,mw, mh,ext;
5310 Widget child;
5311 
5312     mw = mh = 0;
5313     for (i=0; i<ww->composite.num_children; i++) {
5314 	child = ww->composite.children[i];
5315 	if (!XtIsManaged(child)) continue;
5316 	if (XtClass(child) == xmWorkspaceWidgetClass) {
5317 	    int tmpw, tmph;
5318 	    XmWorkspaceGetMaxWidthHeight(child, &tmpw, &tmph);
5319 	    mw = MAX(mw, tmpw);
5320 	    mh = MAX(mh, tmph);
5321 	} else {
5322 	    ext = child->core.x + child->core.width;
5323 	    mw = MAX(mw, ext);
5324 	    ext = child->core.y + child->core.height;
5325 	    mh = MAX(mh, ext);
5326 	}
5327     }
5328 
5329     *width = mw;
5330     *height = mh;
5331 }
5332 #endif
5333 
5334 /*  Subroutine: XmAddWorkspaceEventHandler
5335  *  Pupose:     Export routine to add event-handler type callback for button
5336  *              motion and button-release in Workspace background.
5337  *  Note:       The event owner widget need not be a child of the workspace.
5338  */
XmAddWorkspaceEventHandler(XmWorkspaceWidget ww,Widget owner,XtEventHandler handler,XtPointer client_data)5339 void XmAddWorkspaceEventHandler( XmWorkspaceWidget ww, Widget owner,
5340 	                         XtEventHandler handler, XtPointer client_data )
5341 {
5342     Window child;
5343     int x_offset, y_offset;
5344 
5345     /*  If this is a void call, clear the handler  */
5346     if( handler == NULL )
5347 	ww->workspace.button_tracker = NULL;
5348     else
5349     {
5350 	/*  Install the pointers  */
5351 	ww->workspace.button_tracker = handler;
5352 	ww->workspace.track_widget = owner;
5353 	XTranslateCoordinates(XtDisplay(ww), XtWindow(ww), XtWindow(owner),
5354 	                      0, 0, &x_offset, &y_offset, &child);
5355 	ww->workspace.track_x = x_offset;
5356 	ww->workspace.track_y = y_offset;
5357 	ww->workspace.track_client_data = client_data;
5358     }
5359 }
5360 
5361 /*
5362  * Note on memory bug:
5363  * The code in this file and in Findroute.c assumes that all children are within
5364  * the bounds of this widget. (ww->core.{width,height} > bottom,right corner of
5365  * every child.)  If that becomes not true, then a core dump is imminent.  I added
5366  * the use of newWidth,newHeight here and a safety check in Findroute.c to protect
5367  * against this.
5368  */
ReallocCollideLists(XmWorkspaceWidget ww)5369 void ReallocCollideLists(XmWorkspaceWidget ww)
5370 {
5371 CollideList **cl_ptr;
5372 CollideList *ce_ptr;
5373 CollideList *n_ce_ptr;
5374 int         old_width  = ww->workspace.collide_width;
5375 int         old_height = ww->workspace.collide_height;
5376 int block_size;
5377 int i;
5378 int newWidth, newHeight, maxw, maxh;
5379 
5380     newWidth = ww->core.width;
5381     newHeight = ww->core.height;
5382     XmWorkspaceGetMaxWidthHeight ((Widget)ww, &maxw, &maxh);
5383     if ((maxw > ww->core.width) || (maxh > ww->core.height)) {
5384 	/*
5385 	 * Being here is dangerous becuase the code
5386 	 * assumes that the bottom right of the farthest
5387 	 * widget is inside the Workspace
5388 	 */
5389 	if (maxw > ww->core.width) newWidth = maxw;
5390 	if (maxh > ww->core.height) newHeight = maxh;
5391     }
5392 
5393     /* Do our own realloc since the other does not do the copy */
5394     if (newWidth > old_width) {
5395         cl_ptr = ww->workspace.collide_list_x;
5396         ww->workspace.collide_list_x =
5397             (CollideList **)XtMalloc(sizeof(CollideList*)*(newWidth));
5398         memset(ww->workspace.collide_list_x, 0,
5399 	      sizeof (CollideList*) * newWidth);
5400         block_size = sizeof(CollideList*) * MIN(old_width, newWidth);
5401         memcpy(ww->workspace.collide_list_x, cl_ptr, block_size);
5402         XtFree((char*)cl_ptr);
5403         ww->workspace.collide_width  = ww->core.width;
5404     } else {
5405         cl_ptr = ww->workspace.collide_list_x;
5406         for (i = newWidth; i < old_width; ++i)
5407         {
5408 	    for (ce_ptr = cl_ptr[i]; ce_ptr; ce_ptr = n_ce_ptr)
5409 	    {
5410 	        n_ce_ptr = ce_ptr->next;
5411 	        XtFree((char*)ce_ptr);
5412 	    }
5413 	    cl_ptr[i] = 0;
5414         }
5415     }
5416 
5417     if (newHeight > old_height) {
5418         cl_ptr = ww->workspace.collide_list_y;
5419         ww->workspace.collide_list_y =
5420             (CollideList **)XtMalloc(sizeof(CollideList*)*(newHeight));
5421         memset(ww->workspace.collide_list_y, 0,
5422 	      sizeof (CollideList*) * newHeight);
5423         block_size = sizeof(CollideList*) * MIN(old_height, newHeight);
5424         memcpy(ww->workspace.collide_list_y, cl_ptr, block_size);
5425         XtFree((char*)cl_ptr);
5426         ww->workspace.collide_height = newHeight;
5427     } else {
5428         cl_ptr = ww->workspace.collide_list_y;
5429         for (i = newHeight; i < old_height; ++i)
5430         {
5431 	    for (ce_ptr = cl_ptr[i]; ce_ptr; ce_ptr = n_ce_ptr)
5432 	    {
5433 	        n_ce_ptr = ce_ptr->next;
5434 	        XtFree((char*)ce_ptr);
5435 	    }
5436 	    cl_ptr[i] = 0;
5437 	}
5438     }
5439 
5440 }
5441 /*
5442  * Return the number of distinct points in the path as the return value.
5443  * Place in the x and y arrays, the list of connecting points.
5444  * The return arrays x and y, must be XtFree()'d by the caller.
5445  */
XmWorkspaceLineGetPath(XmWorkspaceLine wl,int ** x,int ** y)5446 int XmWorkspaceLineGetPath(XmWorkspaceLine wl, int **x, int **y)
5447 {
5448     int *xx, *yy, i, points;
5449 
5450     points = wl->num_points;
5451 
5452     if (points > 0) {
5453 	xx = (int*)XtMalloc(points * sizeof(int));
5454 	yy = (int*)XtMalloc(points * sizeof(int));
5455 	for (i=0; i<points ; i++) {
5456 	    xx[i] = wl->point[i].x;
5457 	    yy[i] = wl->point[i].y;
5458 	}
5459 	*x = xx;
5460 	*y = yy;
5461     } else {
5462 	*x = NULL;
5463 	*y = NULL;
5464     }
5465     return points;
5466 
5467 }
5468 
5469 
5470 /*
5471  * A quick-n-dirty way to check if a place is occupied by a standin.  This allows
5472  * StandIn not to inherit from DropSite.  It's a way to reject drops that would
5473  * land on top of an existing StandIn.   This was sought due to a theory that
5474  * said it was very expensive to put a StandIn on the screen if it had to be
5475  * registered as a drop site in addition to all its other responsibilities.
5476  * - Martin
5477  */
5478 Boolean
XmWorkspaceLocationEmpty(Widget w,int x,int y)5479 XmWorkspaceLocationEmpty (Widget w, int x, int y)
5480 {
5481 XmWorkspaceWidget ww = (XmWorkspaceWidget)w;
5482 Boolean unoccupied = True;
5483 Widget child;
5484 int i;
5485 
5486     for (i=0; ((i<ww->composite.num_children)&&(unoccupied)); i++) {
5487 	child = ww->composite.children[i];
5488 	if (!XtIsManaged(child)) continue;
5489 
5490 	if (x<child->core.x) continue;
5491 	if (y<child->core.y) continue;
5492 	if (x>(child->core.x + child->core.width)) continue;
5493 	if (y>(child->core.y + child->core.height)) continue;
5494 	unoccupied = False;
5495     }
5496     return unoccupied;
5497 }
5498 
5499 
5500 Boolean
XmWorkspaceRectangleEmpty(Widget w,int x,int y,int width,int height)5501 XmWorkspaceRectangleEmpty (Widget w, int x, int y, int width, int height)
5502 {
5503 XmWorkspaceWidget ww = (XmWorkspaceWidget)w;
5504 Boolean unoccupied = True;
5505 Widget child;
5506 int i;
5507 
5508     for (i=0; ((i<ww->composite.num_children)&&(unoccupied)); i++) {
5509 	child = ww->composite.children[i];
5510 	if (!XtIsManaged(child)) continue;
5511 
5512 	if ((x+width)<child->core.x) continue;
5513 	if ((y+height)<child->core.y) continue;
5514 	if (x>(child->core.x + child->core.width)) continue;
5515 	if (y>(child->core.y + child->core.height)) continue;
5516 	unoccupied = False;
5517     }
5518     return unoccupied;
5519 }
5520 
5521 
5522 
5523 #if RESIZE_HANDLES
status_check(Widget w,XEvent * event)5524 static void status_check (Widget w, XEvent *event)
5525 {
5526 XmWorkspaceWidget ww;
5527 Widget grab_child;
5528 int i;
5529 XmWorkspaceConstraints constraints;
5530 
5531     if (!ww->workspace.is_resizing) {
5532 	for (i=0; i<ww->composite.num_children; i++)
5533 	    if (inHierarchy(XtWindowToWidget(XtDisplay(ww), event->xany.window), ww, i))
5534 		break;
5535 	/*  This call comes from the accelerator so it should have a child  */
5536 	if( i >= ww->composite.num_children )
5537 	    return;
5538 	grab_child = ww->composite.children[i];
5539 	constraints = WORKSPACE_CONSTRAINT(grab_child);
5540 
5541 	/*  Prepare to resize  */
5542 	CreateSurrogate(ww, grab_child);
5543 	ww->workspace.is_resizing = TRUE;
5544 	/*  Use base variables to keep track of move applied so far  */
5545 	ww->workspace.start.x = 0;
5546 	ww->workspace.start.y = 0;
5547 	ww->workspace.corner.x = grab_child->core.width;
5548 	ww->workspace.corner.y = grab_child->core.height;
5549 	ww->workspace.grab_x = event->xbutton.x;
5550 	ww->workspace.grab_y = event->xbutton.y;
5551 	ww->workspace.base_x = 0;
5552 	ww->workspace.base_y = 0;
5553     }
5554 
5555     if( ww->workspace.move_cursor_installed == FALSE ) {
5556 	XDefineCursor(XtDisplay(ww), XtWindow(ww),
5557 		      xmWorkspaceClassRec.workspace_class.move_cursor);
5558 	ww->workspace.move_cursor_installed = TRUE;
5559     }
5560 }
5561 
ResizeNE(Widget w,XEvent * event,String * params,Cardinal * num_params)5562 static void ResizeNE( Widget w, XEvent* event, String* params, Cardinal* num_params )
5563 {
5564 XmWorkspaceWidget ww;
5565 XPoint loc, size;
5566 int tx,ty;
5567 Widget child;
5568 
5569     if (!(ww = WorkspaceOfWidget(w))) return ;
5570 
5571     if (ww->workspace.auto_arrange) return ;
5572 
5573     status_check (w, event);
5574     ww->workspace.is_moving = TRUE;
5575 
5576     loc.x = event->xbutton.x - ww->workspace.start.x;
5577     loc.y = event->xbutton.y - ww->workspace.start.y;
5578     size.x = ww->workspace.corner.x + event->xbutton.x;
5579     size.y = ww->workspace.corner.y - event->xbutton.y;
5580     MoveSurrogate(ww, 0, loc.y);
5581     childRelative (ww, event, &tx, &ty);
5582     size.x = MAX(10, tx);
5583     ResizeSurrogate (ww, size.x, size.y);
5584     ww->workspace.start.x = event->xbutton.x;
5585     ww->workspace.start.y = event->xbutton.y;
5586 }
ResizeNW(Widget w,XEvent * event,String * params,Cardinal * num_params)5587 static void ResizeNW( Widget w, XEvent* event, String* params, Cardinal* num_params )
5588 {
5589 XmWorkspaceWidget ww;
5590 XPoint loc, size;
5591 int tx,ty;
5592 Widget child;
5593 
5594     if (!(ww = WorkspaceOfWidget(w))) return ;
5595 
5596     if (ww->workspace.auto_arrange) return ;
5597 
5598     status_check (w, event);
5599     ww->workspace.is_moving = TRUE;
5600 
5601     loc.x = event->xbutton.x - ww->workspace.start.x;
5602     loc.y = event->xbutton.y - ww->workspace.start.y;
5603     size.x = ww->workspace.corner.x - event->xbutton.x;
5604     size.y = ww->workspace.corner.y - event->xbutton.y;
5605     MoveSurrogate(ww, loc.x, loc.y);
5606     ResizeSurrogate (ww, size.x, size.y);
5607     ww->workspace.start.x = event->xbutton.x;
5608     ww->workspace.start.y = event->xbutton.y;
5609 }
ResizeSE(Widget w,XEvent * event,String * params,Cardinal * num_params)5610 static void ResizeSE( Widget w, XEvent* event, String* params, Cardinal* num_params )
5611 {
5612 XmWorkspaceWidget ww;
5613 int width, height, tx,ty;
5614 
5615     if (!(ww = WorkspaceOfWidget(w))) return ;
5616 
5617     if (ww->workspace.auto_arrange) return ;
5618 
5619     status_check (w, event);
5620     ww->workspace.is_moving = FALSE;
5621     tx = event->xbutton.x; ty = event->xbutton.y;
5622     childRelative (ww, event, &tx, &ty);
5623     width = MAX(10, tx); height = MAX(10, ty);
5624     ResizeSurrogate(ww, width, height);
5625 }
ResizeSW(Widget w,XEvent * event,String * params,Cardinal * num_params)5626 static void ResizeSW( Widget w, XEvent* event, String* params, Cardinal* num_params )
5627 {
5628 XmWorkspaceWidget ww;
5629 XPoint loc, size;
5630 int tx,ty;
5631 Widget child;
5632 
5633     if (!(ww = WorkspaceOfWidget(w))) return ;
5634 
5635     if (ww->workspace.auto_arrange) return ;
5636 
5637     status_check (w, event);
5638     ww->workspace.is_moving = TRUE;
5639 
5640     loc.x = event->xbutton.x - ww->workspace.start.x;
5641     loc.y = event->xbutton.y - ww->workspace.start.y;
5642     size.x = ww->workspace.corner.x - event->xbutton.x;
5643     MoveSurrogate(ww, loc.x, 0);
5644     childRelative (ww, event, &tx, &ty);
5645     size.y = MAX(10, ty);
5646     ResizeSurrogate (ww, size.x, size.y);
5647     ww->workspace.start.x = event->xbutton.x;
5648     ww->workspace.start.y = event->xbutton.y;
5649 
5650 }
5651 
5652 
NewDrop(Widget w,XEvent * event,String * params,Cardinal * num_params)5653 static void NewDrop( Widget w, XEvent* event, String* params, Cardinal* num_params )
5654 {
5655 Widget child;
5656 XmWorkspaceWidget ww;
5657 
5658     if (!(ww = WorkspaceOfWidget(w))) return ;
5659 
5660     if (ww->workspace.auto_arrange) return ;
5661 
5662     child = w;
5663     while ((child) && (XtParent(child) != (Widget)ww))  child = XtParent(child);
5664     if ((child) && (XtParent(child) == (Widget)ww))
5665 	XtVaSetValues (child, XmNmappedWhenManaged, False, NULL);
5666 
5667     DropSelections (w, event, params, num_params);
5668 
5669     if ((child) && (XtParent(child) == (Widget)ww))  {
5670 	XSync (XtDisplay(child), False);
5671 	XtVaSetValues (child, XmNmappedWhenManaged, True, NULL);
5672     }
5673 }
5674 #endif
5675