bug fix to navigate menu
[dyninst.git] / paradyn / src / UIthread / whereAxis.C
1 /*
2  * Copyright (c) 1996 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * This license is for research uses.  For such uses, there is no
12  * charge. We define "research use" to mean you may freely use it
13  * inside your organization for whatever purposes you see fit. But you
14  * may not re-distribute Paradyn or parts of Paradyn, in any form
15  * source or binary (including derivatives), electronic or otherwise,
16  * to any other organization or entity without our permission.
17  * 
18  * (for other uses, please contact us at paradyn@cs.wisc.edu)
19  * 
20  * All warranties, including without limitation, any warranty of
21  * merchantability or fitness for a particular purpose, are hereby
22  * excluded.
23  * 
24  * By your use of Paradyn, you understand and agree that we (or any
25  * other person or entity with proprietary rights in Paradyn) are
26  * under no obligation to provide either maintenance services,
27  * update services, notices of latent defects, or correction of
28  * defects for Paradyn.
29  * 
30  * Even if advised of the possibility of such damages, under no
31  * circumstances shall we (or any other person or entity with
32  * proprietary rights in the software licensed hereunder) be liable
33  * to you or any third party for direct, indirect, or consequential
34  * damages of any character regardless of type of action, including,
35  * without limitation, loss of profits, loss of use, loss of good
36  * will, or computer failure or malfunction.  You agree to indemnify
37  * us (and any other person or entity with proprietary rights in the
38  * software licensed hereunder) for any and all liability it may
39  * incur to third parties resulting from your use of Paradyn.
40  */
41
42 // whereAxis.C
43 // Ariel Tamches
44
45 // A where axis corresponds to _exactly_ one Paradyn abstraction.
46
47 /* $Log: whereAxis.C,v $
48 /* Revision 1.18  1997/12/04 18:28:59  tamches
49 /* bug fix to navigate menu
50 /*
51  * Revision 1.17  1997/10/28 20:37:01  tamches
52  * dictionary_lite --> dictionary_hash
53  *
54  * Revision 1.16  1997/09/24 19:29:05  tamches
55  * XFontStruct --> Tk_Font; use of Tk_GetFontMetrics
56  * for tcl 8.0
57  *
58  * Revision 1.15  1996/11/26 16:07:08  naim
59  * Fixing asserts - naim
60  *
61  * Revision 1.14  1996/08/16 21:07:44  tamches
62  * updated copyright for release 1.1
63  *
64  * Revision 1.13  1996/02/15 23:14:56  tamches
65  * added code relating to the new line-GC indirection feature of where4tree
66  *
67  * Revision 1.12  1996/02/11 18:27:47  tamches
68  * Added a check for null rootPtr before drawing
69  *
70  * Revision 1.11  1996/02/07 19:15:06  tamches
71  * removed include of whereAxisMisc.h, except for the test program
72  *
73  * Revision 1.10  1996/01/11 04:45:37  tamches
74  * getSelections replaced - now returns Whole Program selection separately,
75  * runs faster, and alone with the new parseSelections of uimpd.tcl.C,
76  * avoids adding spurious metric/focus pairs when Whole Program is chosen
77  *
78  * Revision 1.9  1995/12/09 04:08:39  tamches
79  * shift-dbl-click now togggles highlight, like ctrl-dbl-click
80  *
81  * Revision 1.8  1995/11/20 03:27:50  tamches
82  * overallWindowBorderPix is no longer a global variable.
83  * double-click does a toggle_highlight of the parent node; same
84  * for ctrl-double-click.
85  * changed vector<whereAxisRootNode *> to vector<const whereAxisRootNode *>
86  *
87  * Revision 1.7  1995/10/17 22:20:15  tamches
88  * where axis is no longer a templated type; it uses where4tree with
89  * a template of whereAxisRootNode.
90  * Added many static vrbles and methods which had to be temporarily
91  * moved to whereAxisMisc for compiler-bug reasons in the past.
92  * Expanding a node now calls XWarpPointer more accurately.
93  * Same for un-expansion, finding, and navigating to a node.
94  *
95  */
96
97 #include <stdlib.h> // exit()
98
99 #include "minmax.h"
100
101 #ifndef PARADYN
102 #include "DMinclude.h"
103 #else
104 #include "paradyn/src/DMthread/DMinclude.h"
105 #endif
106
107 #include "whereAxis.h"
108
109 #ifndef PARADYN
110 // only the where axis test program needs this
111 #include "whereAxisMisc.h"
112 #endif
113
114 #include "tkTools.h"
115 #include "tk.h"
116
117 Tk_Font whereAxis::theRootItemFontStruct = NULL;
118 Tk_Font whereAxis::theListboxItemFontStruct = NULL;
119 Tk_3DBorder  whereAxis::rootItemTk3DBorder = NULL;
120 GC           whereAxis::rootItemTextGC = NULL;
121 Tk_3DBorder  whereAxis::listboxItem3DBorder = NULL;
122 GC           whereAxis::listboxItemGC = NULL;
123 GC           whereAxis::listboxRayGC = NULL;
124 GC           whereAxis::nonListboxRayGC = NULL;
125 int          whereAxis::listboxBorderPix = 3;
126 int          whereAxis::listboxScrollBarWidth = 16;
127
128
129 void whereAxis::initializeStaticsIfNeeded() {
130    if (theRootItemFontStruct == NULL)
131       // somewhat kludgy
132       theRootItemFontStruct = consts.rootTextFontStruct;
133
134    if (theListboxItemFontStruct == NULL)
135       // somewhat kludgy
136       theListboxItemFontStruct = consts.listboxFontStruct;
137
138    if (rootItemTk3DBorder == NULL)
139       // somewhat kludgy
140       rootItemTk3DBorder = consts.rootNodeBorder;
141
142    if (rootItemTextGC == NULL)
143       // somewhat kludgy
144       rootItemTextGC = consts.rootItemTextGC;
145
146    if (listboxItem3DBorder == NULL)
147       // somewhat kludgy
148       listboxItem3DBorder = consts.listboxBorder; // ???
149
150    if (listboxItemGC == NULL)
151       listboxItemGC = consts.listboxTextGC;
152
153    if (listboxRayGC == NULL)
154       listboxRayGC = consts.listboxRayGC;
155
156    if (nonListboxRayGC == NULL)
157       nonListboxRayGC = consts.subchildRayGC;
158 }
159
160 whereAxis::whereAxis(Tcl_Interp *in_interp, Tk_Window theTkWindow,
161                      const string &root_str,
162                      const string &iHorizSBName,
163                      const string &iVertSBName,
164                      const string &iNavigateMenuName) :
165              consts(in_interp, theTkWindow),
166              hash(&whereAxis::hashFunc),
167              horizSBName(iHorizSBName),
168              vertSBName(iVertSBName),
169              navigateMenuName(iNavigateMenuName) {
170    initializeStaticsIfNeeded();
171
172    const resourceHandle rootResHandle = 0;
173
174    whereAxisRootNode tempRootNode(rootResHandle, root_str);
175    rootPtr = new where4tree<whereAxisRootNode>(tempRootNode);
176    assert(rootPtr);
177
178    hash[rootResHandle] = rootPtr;
179
180    beginSearchFromPtr = NULL;
181
182    interp = in_interp;
183       
184    horizScrollBarOffset = vertScrollBarOffset = 0;
185       
186    rethink_nominal_centerx();
187
188    nonSliderButtonCurrentlyPressed = false;
189    nonSliderCurrentSubtree = NULL;
190    slider_currently_dragging_subtree = NULL;
191 }
192
193 void whereAxis::rethink_nominal_centerx() {
194    // using Tk_Width(theTkWindow) as the available screen width, and
195    // using root->myEntireWidthAsDrawn(consts) as the amount of screen real
196    // estate used, this routine rethinks this->nominal_centerx.
197
198    const int horizSpaceUsedByTree = rootPtr->entire_width(consts);
199
200    // If the entire tree fits, then set center-x to window-width / 2.
201    // Otherwise, set center-x to used-width / 2;
202    if (horizSpaceUsedByTree <= Tk_Width(consts.theTkWindow))
203       nominal_centerx = Tk_Width(consts.theTkWindow) / 2;
204    else
205       nominal_centerx = horizSpaceUsedByTree / 2;
206 }
207
208 void whereAxis::resizeScrollbars() {
209    string commandStr = string("resize1Scrollbar ") + horizSBName + " " +
210                        string(rootPtr->entire_width(consts)) + " " +
211                        string(Tk_Width(consts.theTkWindow));
212    myTclEval(interp, commandStr);
213
214    commandStr = string("resize1Scrollbar ") + vertSBName + " " +
215                 string(rootPtr->entire_height(consts)) + " " +
216                 string(Tk_Height(consts.theTkWindow));
217    myTclEval(interp, commandStr);
218 }
219
220 bool whereAxis::set_scrollbars(int absolute_x, int relative_x,
221                                int absolute_y, int relative_y,
222                                bool warpPointer) {
223    // Sets the scrollbars s.t. (absolute_x, absolute_y) will appear
224    // at window (relative) location (relative_x, relative_y).
225    // May need to take into account the current scrollbar setting...
226
227    bool anyChanges = true;
228
229    horizScrollBarOffset = -set_scrollbar(interp, horizSBName,
230                                          rootPtr->entire_width(consts),
231                                          absolute_x, relative_x);
232       // relative_x will be updated
233    
234    vertScrollBarOffset = -set_scrollbar(interp, vertSBName,
235                                         rootPtr->entire_height(consts),
236                                         absolute_y, relative_y);
237
238    if (warpPointer)
239       XWarpPointer(Tk_Display(consts.theTkWindow),
240                    Tk_WindowId(consts.theTkWindow), // src win
241                    Tk_WindowId(consts.theTkWindow), // dest win
242                    0, 0, // src x,y
243                    0, 0, // src height, width
244                    relative_x, relative_y
245                    );
246
247    return anyChanges;
248 }
249
250 whereNodeGraphicalPath<whereAxisRootNode> whereAxis::point2path(int x, int y) const {
251    const int overallWindowBorderPix = 0;
252    const int root_centerx = nominal_centerx + horizScrollBarOffset;
253       // relative (not absolute) coord.  note: horizScrollBarOffset <= 0
254    const int root_topy = overallWindowBorderPix + vertScrollBarOffset;
255       // relative (not absolute) coord.  note: vertScrollBarOffset <= 0
256
257    return whereNodeGraphicalPath<whereAxisRootNode>(x, y, consts, rootPtr,
258                                                     root_centerx, root_topy);
259 }
260
261 void whereAxis::nonSliderButtonRelease(ClientData cd, XEvent *) {
262    whereAxis *pthis = (whereAxis *)cd;
263
264    pthis->nonSliderButtonCurrentlyPressed = false;
265    Tk_DeleteTimerHandler(pthis->buttonAutoRepeatToken);
266 }
267
268 void whereAxis::nonSliderButtonAutoRepeatCallback(ClientData cd) {
269    // If the mouse button has been released, do NOT re-invoke the timer.
270    whereAxis *pthis = (whereAxis *)cd;
271    if (!pthis->nonSliderButtonCurrentlyPressed)
272       return;
273
274    const int listboxLeft = pthis->nonSliderSubtreeCenter -
275                            pthis->nonSliderCurrentSubtree->
276                              horiz_pix_everything_below_root(pthis->consts) / 2;
277    const int listboxTop = pthis->nonSliderSubtreeTop +
278                           pthis->nonSliderCurrentSubtree->getNodeData().
279                                getHeightAsRoot() +
280                           pthis->consts.vertPixParent2ChildTop;
281
282    const int listboxActualDataPix = pthis->nonSliderCurrentSubtree->getListboxActualPixHeight() - 2 * listboxBorderPix;
283    int deltaYpix = 0;
284    int repeatIntervalMillisecs = 100;
285
286    switch (pthis->nonSliderButtonPressRegion) {
287       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarUpArrow:
288          deltaYpix = -4;
289          repeatIntervalMillisecs = 10;
290          break;
291       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarDownArrow:
292          deltaYpix = 4;
293          repeatIntervalMillisecs = 10;
294          break;
295       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPageup:
296          deltaYpix = -listboxActualDataPix;
297          repeatIntervalMillisecs = 250; // not so fast
298          break;
299       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPagedown:
300          deltaYpix = listboxActualDataPix;
301          repeatIntervalMillisecs = 250; // not so fast
302          break;
303       default:
304          assert(false);
305    }
306
307    (void)pthis->nonSliderCurrentSubtree->scroll_listbox(pthis->consts,
308                                                         listboxLeft, listboxTop,
309                                                         deltaYpix);
310    pthis->buttonAutoRepeatToken = Tk_CreateTimerHandler(repeatIntervalMillisecs,
311                                                         nonSliderButtonAutoRepeatCallback,
312                                                         pthis);
313 }
314
315 void whereAxis::
316 processNonSliderButtonPress(whereNodeGraphicalPath<whereAxisRootNode> &thePath) {
317    nonSliderButtonCurrentlyPressed = true;
318    nonSliderButtonPressRegion = thePath.whatDoesPathEndIn();
319    nonSliderCurrentSubtree = thePath.getLastPathNode(rootPtr);
320    nonSliderSubtreeCenter = thePath.get_endpath_centerx();
321    nonSliderSubtreeTop = thePath.get_endpath_topy();
322
323    Tk_CreateEventHandler(consts.theTkWindow, ButtonReleaseMask,
324                          nonSliderButtonRelease, this);
325
326    nonSliderButtonAutoRepeatCallback(this);
327 }
328
329 void whereAxis::sliderMouseMotion(ClientData cd, XEvent *eventPtr) {
330    assert(eventPtr->type == MotionNotify);
331    whereAxis *pthis = (whereAxis *)cd;
332
333    const int y = eventPtr->xmotion.y;
334    const int amount_moved = y - pthis->slider_initial_yclick;
335       // may be negative, of course.
336    const int newScrollBarSliderTopPix = pthis->slider_initial_scrollbar_slider_top +
337                                         amount_moved;
338
339    assert(pthis->slider_currently_dragging_subtree != NULL);
340    (void)pthis->slider_currently_dragging_subtree->
341            rigListboxScrollbarSliderTopPix(pthis->consts, pthis->slider_scrollbar_left,
342                                            pthis->slider_scrollbar_top,
343                                            pthis->slider_scrollbar_bottom,
344                                            newScrollBarSliderTopPix,
345                                            true // redraw now
346                                            );
347 }
348
349 void whereAxis::sliderButtonRelease(ClientData cd, XEvent *eventPtr) {
350    assert(eventPtr->type == ButtonRelease);
351    whereAxis *pthis = (whereAxis *)cd;
352       
353    Tk_DeleteEventHandler(pthis->consts.theTkWindow,
354                          PointerMotionMask,
355                          sliderMouseMotion,
356                          pthis);
357    Tk_DeleteEventHandler(pthis->consts.theTkWindow,
358                          ButtonReleaseMask,
359                          sliderButtonRelease,
360                          pthis);
361    pthis->slider_currently_dragging_subtree = NULL;
362 }
363
364 void whereAxis::addItem(const string &newName,
365                         resourceHandle parentUniqueId,
366                         resourceHandle newNodeUniqueId,
367                         bool rethinkGraphicsNow,
368                         bool resortNow) {
369    whereAxisRootNode tempRootNode(newNodeUniqueId, newName);
370    where4tree<whereAxisRootNode> *newNode = new where4tree<whereAxisRootNode>(tempRootNode);
371    assert(newNode);
372
373    assert(hash.defines(parentUniqueId));
374    where4tree<whereAxisRootNode> *parentPtr = hash[parentUniqueId];
375    assert(parentPtr != NULL);
376
377    parentPtr->addChild(newNode, false, // not explicitly expanded
378                        consts,
379                        rethinkGraphicsNow,
380                        resortNow);
381
382    assert(!hash.defines(newNodeUniqueId));
383    hash[newNodeUniqueId] = newNode;
384    assert(hash.defines(newNodeUniqueId));
385 }
386
387 #ifndef PARADYN
388 // only the where axis test program uses this stuff:
389 int whereAxis::readTree(ifstream &is,
390                         resourceHandle parentUniqueId,
391                         resourceHandle nextUniqueIdToUse) {
392    // returns the number of new nodes added, 0 if nothing could be read
393    char c;
394    is >> c;
395    if (!is) return false;
396    if (c == ')') {
397       is.putback(c);
398       return 0;
399    }
400
401    const bool oneItemTree = (c != '(');
402    if (oneItemTree)
403       is.putback(c);
404
405    string rootString = readItem(is);
406    if (rootString.length() == 0)
407       return 0; // could not read root name
408
409    const bool thisIsWhereAxisRoot = (nextUniqueIdToUse==parentUniqueId);
410    if (thisIsWhereAxisRoot) {
411       // a little hack for the where axis root node, which can't be added
412       // using addItem().  Not to mention we have to set "whereAxis::rootPtr"
413
414       whereAxisRootNode tempRootNode(nextUniqueIdToUse, rootString);
415       where4tree<whereAxisRootNode> *newParentNode = new where4tree<whereAxisRootNode> (tempRootNode);
416
417       assert(newParentNode);
418
419       assert(nextUniqueIdToUse==0);
420       assert(!hash.defines(nextUniqueIdToUse));
421       hash[nextUniqueIdToUse] = newParentNode;
422       assert(hash.defines(nextUniqueIdToUse));
423
424       rootPtr = newParentNode;
425    }
426    else {
427       this->addItem(rootString, parentUniqueId, nextUniqueIdToUse, false, false);
428       // don't redraw; don't resort
429    }
430    
431
432    if (oneItemTree)
433       return 1;
434
435    parentUniqueId = nextUniqueIdToUse++;
436
437    // now the children (if any)
438    int result = 1; // number of nodes read in so far...
439    while (true) {
440       int localResult = readTree(is, parentUniqueId, nextUniqueIdToUse);
441       if (localResult == 0)
442          break;
443
444       result += localResult;
445       nextUniqueIdToUse += localResult;
446    }
447
448    // now, do some graphical rethinking w.r.t. "result" that we had suppressed
449    // up until now in the name of improved performance...
450    assert(hash.defines(parentUniqueId));
451    where4tree<whereAxisRootNode> *theParentNode = hash[parentUniqueId];
452
453    theParentNode->doneAddingChildren(consts, true); // true --> resort
454
455    // eat the closing )
456    is >> c;
457    if (c != ')') {
458       cerr << "expected ) to close tree, but found " << c << endl;
459       return 0;
460    }
461
462    return result;
463 }
464
465 // only the where axis test program gets this routine
466 whereAxis::whereAxis(ifstream &infile, Tcl_Interp *in_interp,
467                      Tk_Window theTkWindow,
468                      const char *iHorizSBName, const char *iVertSBName,
469                      const char *iNavigateMenuName) :
470                               consts(in_interp, theTkWindow),
471                               hash(hashFunc),
472                               horizSBName(iHorizSBName),
473                               vertSBName(iVertSBName),
474                               navigateMenuName(iNavigateMenuName) {
475    initializeStaticsIfNeeded();
476
477    interp = in_interp;
478    horizScrollBarOffset = vertScrollBarOffset = 0;
479
480    resourceHandle rootNodeUniqueId = 0;
481    const int result = readTree(infile, rootNodeUniqueId, rootNodeUniqueId);
482    assert(result > 0);
483
484    beginSearchFromPtr = NULL;  
485
486    rethink_nominal_centerx();
487 }
488 #endif
489
490 void whereAxis::draw(bool doubleBuffer,
491                      bool xsynchronize // are we debugging?
492                      ) const {
493    Drawable theDrawable = (doubleBuffer && !xsynchronize) ? consts.offscreenPixmap :
494                                                             Tk_WindowId(consts.theTkWindow);
495
496    if (doubleBuffer || xsynchronize)
497       // clear the offscreen pixmap before drawing onto it
498       XFillRectangle(consts.display,
499                      theDrawable,
500                      consts.erasingGC,
501                      0, // x-offset relative to drawable
502                      0, // y-offset relative to drawable
503                      Tk_Width(consts.theTkWindow),
504                      Tk_Height(consts.theTkWindow)
505                      );
506
507    const int overallWindowBorderPix = 0;
508
509    if (rootPtr)
510       rootPtr->draw(consts.theTkWindow, consts, theDrawable,
511                     nominal_centerx + horizScrollBarOffset,
512                        // relative (not absolute) coord
513                     overallWindowBorderPix + vertScrollBarOffset,
514                        // relative (not absolute) coord
515                     false, // not root only
516                     false // not listbox only
517                     );
518
519    if (doubleBuffer && !xsynchronize)
520       // copy from offscreen pixmap onto the 'real' window
521       XCopyArea(consts.display,
522                 theDrawable, // source pixmap
523                 Tk_WindowId(consts.theTkWindow), // dest pixmap
524                 consts.erasingGC, // ?????
525                 0, 0, // source x,y pix
526                 Tk_Width(consts.theTkWindow),
527                 Tk_Height(consts.theTkWindow),
528                 0, 0 // dest x,y offset pix
529                 );
530 }
531
532 void whereAxis::resize(bool currentlyDisplayedAbstraction) {
533    const int newWindowWidth = Tk_Width(consts.theTkWindow);
534 //   const int newWindowHeight = Tk_Height(consts.theTkWindow); [not used]
535
536    consts.resize(); // reallocates offscreen pixmap; rethinks
537                     // 'listboxHeightWhereSBappears'
538
539    // Loop through EVERY listbox (gasp) and rethink whether or not it needs
540    // a scrollbar and (possibly) change listBoxActualHeight...
541    // _Must_ go _after_ the consts.resize()
542    (void)rootPtr->rethinkListboxAfterResize(consts);
543
544    // Now the more conventional resize code:
545    rootPtr->rethinkAfterResize(consts, newWindowWidth);
546    rethink_nominal_centerx(); // must go _after_ the rootPtr->rethinkAfterResize()
547
548    if (currentlyDisplayedAbstraction) {
549       // We have changed axis width and/or height.  Let's inform tcl, so it may
550       // rethink the scrollbar ranges.
551       resizeScrollbars();
552
553       // Now, let's update our own stored horiz & vert scrollbar offset values
554       adjustHorizSBOffset(); // obtain FirstPix from the actual tk scrollbar
555       adjustVertSBOffset (); // obtain FirstPix from the actual tk scrollbar
556    }
557 }
558
559 void whereAxis::processSingleClick(int x, int y) {
560    whereNodeGraphicalPath<whereAxisRootNode> thePath=point2path(x, y);
561
562    switch (thePath.whatDoesPathEndIn()) {
563       case whereNodeGraphicalPath<whereAxisRootNode>::Nothing:
564 //         cout << "single-click in nothing at (" << x << "," << y << ")" << endl;
565          return;
566       case whereNodeGraphicalPath<whereAxisRootNode>::ExpandedNode: {
567 //         cout << "click on an non-listbox item; adjusting NAVIGATE menu..." << endl;
568          lastClickPath = thePath.getPath();
569          rethinkNavigateMenu();
570
571          // Now redraw the node in question...(its highlightedness changed)
572          where4tree<whereAxisRootNode> *ptr = thePath.getLastPathNode(rootPtr);
573          ptr->toggle_highlight();
574          ptr->getNodeData().drawAsRoot(consts.theTkWindow,
575                                        Tk_WindowId(consts.theTkWindow),
576                                        thePath.get_endpath_centerx(),
577                                        thePath.get_endpath_topy());
578          return;
579       }
580       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxItem:
581 //         cout << "click on a listbox item; adjusting NAVIGATE menu..." << endl;
582          lastClickPath = thePath.getPath();
583          rethinkNavigateMenu();
584
585          // Now we have to redraw the item in question...(its highlightedness changed)
586          thePath.getLastPathNode(rootPtr)->toggle_highlight();
587
588          thePath.getParentOfLastPathNode(rootPtr)->draw(consts.theTkWindow,
589                                                         consts, Tk_WindowId(consts.theTkWindow),
590                                                         thePath.get_endpath_centerx(),
591                                                         thePath.get_endpath_topy(),
592                                                         false, // not root only
593                                                         true // listbox only
594                                                         );
595          break;
596       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarUpArrow:
597       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarDownArrow:
598       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPageup:
599       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPagedown:
600          processNonSliderButtonPress(thePath);
601          return;
602       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarSlider: {
603 //         cout << "looks like a click in a listbox scrollbar slider" << endl;
604
605          where4tree<whereAxisRootNode> *parentPtr = thePath.getLastPathNode(rootPtr);
606
607          slider_initial_yclick = y;
608          slider_currently_dragging_subtree = parentPtr;
609
610          const int lbTop = thePath.get_endpath_topy() +
611                            parentPtr->getNodeData().getHeightAsRoot() +
612                            consts.vertPixParent2ChildTop;
613
614          int dummyint;
615          parentPtr->getScrollbar().getSliderCoords(lbTop,
616                      lbTop + parentPtr->getListboxActualPixHeight() - 1,
617                      parentPtr->getListboxActualPixHeight() - 2*listboxBorderPix,
618                      parentPtr->getListboxFullPixHeight() - 2*listboxBorderPix,
619                      slider_initial_scrollbar_slider_top, // filled in
620                      dummyint);
621
622          slider_scrollbar_left = thePath.get_endpath_centerx() -
623                                  parentPtr->horiz_pix_everything_below_root(consts) / 2;
624          slider_scrollbar_top = thePath.get_endpath_topy() +
625                                 parentPtr->getNodeData().getHeightAsRoot() +
626                                 consts.vertPixParent2ChildTop;
627          slider_scrollbar_bottom = slider_scrollbar_top +
628                                    parentPtr->getListboxActualPixHeight() - 1;
629
630 //         cout << "slider click was on subtree whose root name is "
631 //              << parentPtr->getRootName() << endl;
632
633          Tk_CreateEventHandler(consts.theTkWindow,
634                                ButtonReleaseMask,
635                                sliderButtonRelease,
636                                this);
637          Tk_CreateEventHandler(consts.theTkWindow,
638                                PointerMotionMask,
639                                sliderMouseMotion,
640                                this);
641          break;
642       }
643       default:
644          assert(false);
645    }
646 }
647
648 /* ***************************************************************** */
649
650 bool whereAxis::processShiftDoubleClick(int x, int y) {
651    // returns true iff a complete redraw is called for
652
653    whereNodeGraphicalPath<whereAxisRootNode> thePath = point2path(x, y);
654
655    switch (thePath.whatDoesPathEndIn()) {
656       case whereNodeGraphicalPath<whereAxisRootNode>::Nothing:
657 //         cout << "shift-double-click in nothing" << endl;
658          return false;
659       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxItem:
660 //         cout << "shift-double-click in lb item; ignoring" << endl;
661          return false;
662       case whereNodeGraphicalPath<whereAxisRootNode>::ExpandedNode:
663          break; // some breathing room for lots of code to follow...
664       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarUpArrow:
665       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarDownArrow:
666       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPageup:
667       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPagedown:
668          // in this case, do the same as a single-click
669          processNonSliderButtonPress(thePath);
670          return false; // no need to redraw further
671       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarSlider:
672 //         cout << "shift-double-click in a listbox scrollbar slider...doing nothing" << endl;
673          return false;
674       default:
675          assert(false);
676    }
677
678    // How do we know whether to expand-all or un-expand all?
679    // Well, if there is no listbox up, then we should _definitely_ unexpand.
680    // And, if there is a listbox up containing all items (no explicitly expanded items),
681    //    then we should _definitely_ expand.
682    // But, if there is a listbox up with _some_ explicitly expanded items, then
683    //    are we supposed to expand out the remaining ones; or un-expand the expanded
684    //    ones?  It could go either way.  For now, we'll say that we should un-expand.
685
686    bool anyChanges = false; // so far...
687    where4tree<whereAxisRootNode> *ptr = thePath.getLastPathNode(rootPtr);
688    ptr->toggle_highlight(); // doesn't redraw
689
690    if (ptr->getListboxPixWidth() > 0) {
691       bool noExplicitlyExpandedChildren = true; // so far...
692       for (unsigned childlcv=0; childlcv < ptr->getNumChildren(); childlcv++)
693          if (ptr->getChildIsExpandedFlag(childlcv)) {
694             noExplicitlyExpandedChildren = false; 
695             break;
696          }
697
698       if (noExplicitlyExpandedChildren)
699          // There is a listbox up, and it contains ALL children.
700          // So, obviously, we want to expand them all...
701          anyChanges = rootPtr->path2ExpandAllChildren(consts, thePath.getPath(), 0);
702       else
703          // There is a listbox up, but there are also some expanded children.
704          // This call (whether to expand the remaining listbox items, or whether
705          // to un-expand the expanded items) could go either way; we choose the
706          // latter (for now, at least)
707          anyChanges = rootPtr->path2UnExpandAllChildren(consts, thePath.getPath(), 0);
708    }
709    else
710       // No listbox is up; hence, all children are expanded.
711       // So obviously, we wish to un-expand all the children.
712       anyChanges = rootPtr->path2UnExpandAllChildren(consts, thePath.getPath(), 0);
713
714    if (!anyChanges)
715       return false;
716
717    rethink_nominal_centerx();
718
719    // We have changed axis width and/or height.  Let's inform tcl, so it may
720    // rethink the scrollbar ranges.
721    resizeScrollbars();
722
723    // Now, let's update our own stored horiz & vert scrollbar offset values
724    adjustHorizSBOffset(); // obtain FirstPix from the actual tk scrollbar
725    adjustVertSBOffset (); // obtain FirstPix from the actual tk scrollbar
726
727    return true;
728 }
729
730 bool whereAxis::processCtrlDoubleClick(int x, int y) {
731    // returns true iff changes were made
732
733    whereNodeGraphicalPath<whereAxisRootNode> thePath = point2path(x, y);
734
735    if (thePath.whatDoesPathEndIn() != whereNodeGraphicalPath<whereAxisRootNode>::ExpandedNode)
736       return false;
737
738 //   cout << "ctrl-double-click:" << endl;
739
740    // How do we know whether to select-all or unselect-all?
741    // Well, if noone is selected, then we should _definitely_ select all.
742    // And, if everyone is selected, then we should _definitely_ unselect all.
743    // But, if _some_ items are selected, what should we do?  It could go either way.
744    // For now, we'll say unselect.
745
746    where4tree<whereAxisRootNode> *ptr = thePath.getLastPathNode(rootPtr);
747
748    // change highlightedness of the root node (i.e. the double-click should undo
749    // the effects of the earlier single-click, now that we know that a double-click
750    // was the user's intention all along)
751    ptr->toggle_highlight(); // doesn't redraw
752
753    if (ptr->getNumChildren()==0)
754       return true; // changes were made
755
756    bool allChildrenSelected = true;
757    bool noChildrenSelected = true;
758    for (unsigned childlcv=0; childlcv < ptr->getNumChildren(); childlcv++)
759       if (ptr->getChildTree(childlcv)->isHighlighted()) {
760          noChildrenSelected = false;
761          if (!allChildrenSelected)
762             break;
763       }
764       else {
765          allChildrenSelected = false;
766          if (!noChildrenSelected)
767             break;
768       }
769
770    assert(!(allChildrenSelected && noChildrenSelected));
771
772    if (allChildrenSelected || !noChildrenSelected) {
773       // unselect all children
774       for (unsigned childlcv=0; childlcv < ptr->getNumChildren(); childlcv++)
775          ptr->getChildTree(childlcv)->unhighlight();
776       return true; // changes were made
777    }
778    else {
779       assert(noChildrenSelected);
780       for (unsigned childlcv=0; childlcv < ptr->getNumChildren(); childlcv++)
781          ptr->getChildTree(childlcv)->highlight();
782       return true; // changes were made
783    }
784 }
785
786 bool whereAxis::processDoubleClick(int x, int y) {
787    // returns true iff a complete redraw is called for
788
789    bool scrollToWhenDone = false; // for now...
790
791    whereNodeGraphicalPath<whereAxisRootNode> thePath = point2path(x, y);
792
793    switch (thePath.whatDoesPathEndIn()) {
794       case whereNodeGraphicalPath<whereAxisRootNode>::Nothing:
795 //         cout << "looks like a double-click in nothing" << endl;
796          return false;
797       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarUpArrow:
798       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarDownArrow:
799       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPageup:
800       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarPagedown:
801          // in this case, do the same as a single-click
802          processNonSliderButtonPress(thePath);
803          return false; // no need to redraw further
804       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxScrollbarSlider:
805 //         cout << "double-click in a listbox scrollbar slider...doing nothing" << endl;
806          return false;
807       case whereNodeGraphicalPath<whereAxisRootNode>::ExpandedNode: {
808          // double-click in a "regular" node (not in listbox): un-expand
809 //         cout << "double-click on a non-listbox item" << endl;
810
811          if (thePath.getSize() == 0) {
812 //            cout << "double-click un-expansion on the root..ignoring" << endl;
813             return false;
814          }
815
816          if (!rootPtr->path2lbItemUnexpand(consts, thePath.getPath(), 0)) {
817             // probably an attempt to expand a leaf item (w/o a triangle next to it)
818 //            cout << "could not un-expand this subtree (a leaf?)...continuing" << endl;
819             return false;
820          }
821
822          // Now let's scroll to the un-expanded item.
823          rethink_nominal_centerx();
824          resizeScrollbars();
825          adjustHorizSBOffset();
826          adjustVertSBOffset();
827          softScrollToEndOfPath(thePath.getPath());
828          return true;
829       }
830       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxItem: {
831          // double-click in a listbox item
832 //         cout << "double-click on a listbox item" << endl;
833          // first thing's first: now that we know the user intended to do a double-click
834          // all along, we should undo the effects of the single-click which came earlier.
835          thePath.getLastPathNode(rootPtr)->toggle_highlight(); // doesn't redraw
836
837          const bool anyChanges = rootPtr->path2lbItemExpand(consts,
838                                                             thePath.getPath(), 0);
839
840          if (!anyChanges) {
841             // The only real change we made was the toggle_highlight().  This is
842             // a case where we can do the redrawing ourselves (and fast).
843             thePath.getParentOfLastPathNode(rootPtr)->
844                  draw(consts.theTkWindow, consts, Tk_WindowId(consts.theTkWindow),
845                       thePath.get_endpath_centerx(),
846                       thePath.get_endpath_topy(),
847                       false, // not root only
848                       true // listbox only
849                       );
850             return false;
851          }
852          else {
853             // expansion was successful...later, we'll scroll to the expanded item.
854             // NOTE: rootPtr->path2lbItemExpand will have modified "thePath"
855             //       for us (by changing the last item from a listbox item to
856             //       an expanded one, which is the proper thing to do).
857
858             scrollToWhenDone = true;
859          }
860          break;
861       }
862       default:
863          assert(false);
864    }
865
866    // expansion or un-expansion successful
867
868    rethink_nominal_centerx();
869
870    // We have changed axis width and/or height.  Let's inform tcl, so it may
871    // rethink the scrollbar ranges.
872    resizeScrollbars();
873
874    // Now, let's update our own stored horiz & vert scrollbar offset values
875    adjustHorizSBOffset(); // obtain FirstPix from the actual tk scrollbar
876    adjustVertSBOffset (); // obtain FirstPix from the actual tk scrollbar
877
878    if (scrollToWhenDone) {
879       const int overallWindowBorderPix = 0;
880
881       whereNodeGraphicalPath<whereAxisRootNode>
882            path_to_scroll_to(thePath.getPath(),
883                         consts, rootPtr,
884                         nominal_centerx,
885                            // root centerx (abs. pos., regardless of sb [intentional])
886                         overallWindowBorderPix
887                            // topy of root (abs. pos., regardless of sb [intentional])
888                         );
889
890       int newlyExpandedElemCenterX = path_to_scroll_to.get_endpath_centerx();
891       int newlyExpandedElemTopY    = path_to_scroll_to.get_endpath_topy();
892       int newlyExpandedElemMiddleY = newlyExpandedElemTopY +
893                                      path_to_scroll_to.getLastPathNode(rootPtr)->getNodeData().getHeightAsRoot() / 2;
894
895       (void)set_scrollbars(newlyExpandedElemCenterX, x,
896                            newlyExpandedElemMiddleY, y,
897                            true);
898    }
899
900    return true;
901 }
902
903 bool whereAxis::softScrollToPathItem(const whereNodePosRawPath &thePath,
904                                      unsigned index) {
905    // scrolls s.t. the (centerx, topy) of path index is in middle of screen.
906
907    whereNodePosRawPath newPath = thePath;
908      // make a copy, because we're gonna hack it up a bit.
909    newPath.rigSize(index);
910    return softScrollToEndOfPath(newPath);
911 }
912
913 bool whereAxis::forciblyScrollToPathItem(const whereNodePosRawPath &thePath,
914                                          unsigned pathLen) {
915    // Simply a stub which generates a new path and calls forciblyScrollToEndOfPath()   
916    // "index" indicates the length of the new path; in particular, 0 will give
917    // and empty path (the root node).
918
919    // make a copy of "thePath", truncate it to "pathLen", and forciblyScrollToEndOfPath
920    whereNodePosRawPath newPath = thePath;
921    newPath.rigSize(pathLen);
922    return forciblyScrollToEndOfPath(newPath);
923 }
924
925 bool whereAxis::softScrollToEndOfPath(const whereNodePosRawPath &thePath) {
926    const int overallWindowBorderPix = 0;
927    whereNodeGraphicalPath<whereAxisRootNode>
928          scrollToPath(thePath, consts, rootPtr,
929                       nominal_centerx,
930                          // ignores scrollbar settings (an absolute coord),
931                       overallWindowBorderPix
932                          // absolute coord of root topy
933                       );
934    const int last_item_centerx = scrollToPath.get_endpath_centerx();
935    const int last_item_topy    = scrollToPath.get_endpath_topy();
936       // note: if the path ends in an lb item, these coords refer to the PARENT
937       //       node, which is expanded.
938
939    switch (scrollToPath.whatDoesPathEndIn()) {
940       case whereNodeGraphicalPath<whereAxisRootNode>::ExpandedNode: {
941 //         cout << "soft scrolling to expanded node" << endl;
942          const int last_item_middley =
943             last_item_topy +
944             scrollToPath.getLastPathNode(rootPtr)->getNodeData().getHeightAsRoot() / 2;
945          return set_scrollbars(last_item_centerx,
946                                Tk_Width(consts.theTkWindow) / 2,
947                                last_item_middley,
948                                Tk_Height(consts.theTkWindow) / 2,
949                                true);
950       }
951       case whereNodeGraphicalPath<whereAxisRootNode>::ListboxItem: {
952 //         cout << "soft scrolling to lb item" << endl;
953
954          // First, let's scroll within the listbox (no redrawing yet)
955          where4tree<whereAxisRootNode> *parent = scrollToPath.getParentOfLastPathNode(rootPtr);
956
957          Tk_FontMetrics lbFontMetrics; // filled in by Tk_GetFontMetrics()
958          Tk_GetFontMetrics(consts.listboxFontStruct, &lbFontMetrics);
959          const unsigned itemHeight = consts.listboxVertPadAboveItem +
960                                      lbFontMetrics.ascent +
961                                      consts.listboxVertPadAfterItemBaseline;
962    
963          int scrollToVertPix = 0; // relative to listbox top
964          const unsigned childnum = scrollToPath.getPath().getLastItem();
965          for (unsigned childlcv=0; childlcv < childnum; childlcv++)
966             if (!parent->getChildIsExpandedFlag(childlcv))
967                scrollToVertPix += itemHeight;
968
969          int destItemRelToListboxTop = scrollToVertPix;   
970          if (parent->getScrollbar().isValid()) {
971             (void)parent->scroll_listbox(consts, scrollToVertPix -
972                                          parent->getScrollbar().getPixFirst());
973
974             destItemRelToListboxTop -= parent->getScrollbar().getPixFirst();
975          }
976
977          if (destItemRelToListboxTop < 0) {
978             cout << "note: softScrollToEndOfPath() failed to scroll properly to item w/in listbox" << endl;
979          }
980
981          return set_scrollbars(scrollToPath.get_endpath_centerx() -
982                                   parent->horiz_pix_everything_below_root(consts)/2 +
983                                   parent->getListboxPixWidth() / 2,
984                                   // listbox centerx
985                                Tk_Width(consts.theTkWindow) / 2,
986                                scrollToPath.get_endpath_topy() +
987                                   parent->getNodeData().getHeightAsRoot() +
988                                   consts.vertPixParent2ChildTop +
989                                   destItemRelToListboxTop + itemHeight / 2,
990                                   // should be the middley of the lb item
991                                Tk_Height(consts.theTkWindow) / 2,
992                                true);
993       }
994       default: assert(false);
995    }
996
997    assert(false);
998    return false; // placate compiler
999 }
1000
1001 bool whereAxis::forciblyScrollToEndOfPath(const whereNodePosRawPath &thePath) {
1002    // Forcibly expands path items as needed, then calls softScrollToEndOfPath()
1003
1004    const bool anyChanges = rootPtr->expandEntirePath(consts, thePath, 0);
1005    if (anyChanges) {
1006       rethink_nominal_centerx();
1007       resizeScrollbars();
1008       adjustHorizSBOffset();
1009       adjustVertSBOffset();
1010    }
1011
1012    softScrollToEndOfPath(thePath);
1013
1014    return anyChanges;
1015 }
1016
1017 void whereAxis::navigateTo(unsigned pathLen) {
1018    (void)forciblyScrollToPathItem(lastClickPath, pathLen);
1019 }
1020
1021 int whereAxis::find(const string &str) {
1022    // does a blind search for the given string.  Expands things along the
1023    // way if needed.  Returns 0 if not found, 1 if found & no expansions
1024    // were needed, 2 if found & expansion(s) _were_ needed
1025
1026    // Our strategy: (1) make a path out of the string.
1027    //               (2) call a routine that "forcefully" scrolls to the end of that
1028    //                   path.  We say "forcefully" beceause this routine will
1029    //                   expand items along the way, if necessary.
1030
1031    // Uses and alters "beginSearchFromPtr"
1032
1033    whereNodePosRawPath thePath;
1034    int result = rootPtr->string2Path(thePath, consts, str, beginSearchFromPtr, true);
1035
1036    if (result == 0)
1037       if (beginSearchFromPtr == NULL)
1038          return 0; // not found
1039       else {
1040          // try again with beginSearchFromPtr of NULL (wrap-around search)
1041 //         cout << "search wrap-around" << endl;
1042          beginSearchFromPtr = NULL;
1043          return find(str);
1044       }
1045
1046    // found.  Update beginSearchFromPtr.
1047    where4tree<whereAxisRootNode> *beginSearchFromPtr = rootPtr;
1048    for (unsigned i=0; i < thePath.getSize(); i++)
1049       beginSearchFromPtr = beginSearchFromPtr->getChildTree(thePath[i]);
1050
1051    if (result==1)
1052       (void)softScrollToEndOfPath(thePath);
1053    else {
1054       assert(result==2);
1055       bool aflag;
1056       aflag = (forciblyScrollToEndOfPath(thePath));
1057       // rethinks nominal centerx, resizes scrollbars, etc.
1058       assert(aflag);
1059    }
1060
1061    return result;
1062 }
1063
1064 bool whereAxis::adjustHorizSBOffset(float newFirst) {
1065    // does not redraw.  Returns true iff any changes.
1066
1067    // First, we need to make the change to the tk scrollbar
1068    newFirst = moveScrollBar(interp, horizSBName, newFirst);
1069
1070    // Then, we update our C++ variables
1071    int widthOfEverything = rootPtr->entire_width(consts);
1072    int oldHorizScrollBarOffset = horizScrollBarOffset;
1073    horizScrollBarOffset = -(int)(newFirst * widthOfEverything);
1074       // yes, horizScrollBarOffset is always negative (unless it's zero)
1075    return (horizScrollBarOffset != oldHorizScrollBarOffset);
1076 }
1077
1078 bool whereAxis::adjustHorizSBOffsetFromDeltaPix(int deltapix) {
1079    // does not redraw.  Returns true iff any changes.
1080
1081    const int widthOfEverything = rootPtr->entire_width(consts);
1082    float newFirst = (float)(-horizScrollBarOffset + deltapix) / widthOfEverything;
1083    return adjustHorizSBOffset(newFirst);
1084 }
1085
1086 //template <class NODEDATA>
1087 //bool whereAxis<NODEDATA>::adjustHorizSBOffsetFromDeltaPages(int deltapages) {
1088 //   // does not redraw.  Returns true iff any changes.
1089 //
1090 //   // First, update the tk scrollbar
1091 //   const int widthOfEverything = rootPtr->entire_width(consts);
1092 //   const int widthOfVisible = Tk_Width(consts.theTkWindow);
1093 //   float newFirst = (float)(-horizScrollBarOffset + widthOfVisible*deltapages) /
1094 //                     widthOfEverything;
1095 //   return adjustHorizSBOffset(newFirst);
1096 //}
1097
1098 bool whereAxis::adjustHorizSBOffset() {
1099    // Does not redraw.  Obtains PixFirst from actual tk scrollbar.
1100    float first, last; // fractions (0.0 to 1.0)
1101    getScrollBarValues(interp, horizSBName, first, last);
1102
1103    return adjustHorizSBOffset(first);
1104 }
1105
1106 bool whereAxis::adjustVertSBOffset(float newFirst) {
1107    // does not redraw.  Returns true iff any changes.
1108    // First, we need to make the change to the tk scrollbar
1109    newFirst = moveScrollBar(interp, vertSBName, newFirst);
1110
1111    // Then, we update our C++ variables
1112    int heightOfEverything = rootPtr->entire_height(consts);
1113    int oldVertScrollBarOffset = vertScrollBarOffset;
1114    vertScrollBarOffset = -(int)(newFirst * heightOfEverything);
1115       // yes, vertScrollBarOffset is always negative (unless it's zero)
1116    return (vertScrollBarOffset != oldVertScrollBarOffset);
1117 }
1118
1119 bool whereAxis::adjustVertSBOffsetFromDeltaPix(int deltapix) {
1120    // does not redraw.  Returns true iff any changes were made.
1121
1122    const int heightOfEverything = rootPtr->entire_height(consts);
1123    float newFirst = (float)(-vertScrollBarOffset + deltapix) / heightOfEverything;
1124    return adjustVertSBOffset(newFirst);
1125 }
1126
1127 //template <class NODEDATA>
1128 //bool whereAxis<NODEDATA>::adjustVertSBOffsetFromDeltaPages(int deltapages) {
1129 //   // does not redraw
1130 //
1131 //   // First, update the tk scrollbar
1132 //   const int heightOfEverything = rootPtr->entire_height(consts);
1133 //   const int heightOfVisible = Tk_Height(consts.theTkWindow);
1134 //   float newFirst = (float)(-vertScrollBarOffset + heightOfVisible*deltapages) /
1135 //                    heightOfEverything;
1136 //   return adjustVertSBOffset(newFirst);
1137 //}
1138
1139 bool whereAxis::adjustVertSBOffset() {
1140    // Does not redraw.  Obtains PixFirst from actual tk scrollbar.
1141    float first, last; // fractions (0.0 to 1.0)
1142    getScrollBarValues(interp, vertSBName, first, last);
1143
1144    return adjustVertSBOffset(first);
1145 }
1146
1147 /* ************************************************************************ */
1148
1149 void whereAxis::rethinkNavigateMenu() {
1150    // We loop through the items of "lastClickPath".  For each item,
1151    // we get the name of the root node, and append the full-path entry
1152    // to the navigate menu.
1153
1154    where4tree<whereAxisRootNode> *currTree = rootPtr;   
1155
1156    // Note: in tk4.0, menu indices start at 1, not 0 (0 is reserved for tearoff)
1157    string commandStr = navigateMenuName + " delete 1 100";
1158    myTclEval(interp, commandStr);
1159
1160    string theString;
1161
1162    unsigned itemlcv = 0;
1163    while (true) {
1164       if (itemlcv >= 1)
1165          theString += "/";
1166       theString += currTree->getNodeData().getName();
1167
1168 //      cout << "adding " << theString << " to the navigate menu" << endl;
1169
1170       string commandStr = navigateMenuName + " add command -label {" + theString + "} " +
1171                           "-command {whereAxisNavigateTo " + string(itemlcv) + "}";
1172       myTclEval(interp, commandStr);
1173
1174       if (itemlcv >= lastClickPath.getSize())
1175          break;
1176
1177       unsigned theItem = lastClickPath[itemlcv++];
1178       currTree = currTree->getChildTree(theItem);
1179    }
1180 }
1181
1182 bool whereAxis::selectUnSelectFromFullPathName(const string &name, bool selectFlag) {
1183    // returns true iff found
1184    const char *str = name.string_of();
1185    if (str == NULL)
1186       return false;
1187
1188    return rootPtr->selectUnSelectFromFullPathName(str, selectFlag);
1189 }
1190
1191 vector< vector<resourceHandle> >
1192 whereAxis::getSelections(bool &wholeProgram,
1193                          vector<unsigned> &wholeProgramFocus) const {
1194    // returns a vector[num-hierarchies] of vector of selections.
1195    // The number of hierarchies is defined as the number of children of the
1196    // root node.  If "Whole Program" was selection, it isn't returned with
1197    // the main result; it's returned by modifying the 2 params
1198    const unsigned numHierarchies = rootPtr->getNumChildren();
1199
1200    vector < vector<resourceHandle> > result(numHierarchies);
1201
1202    bool wholeProgramImplicit = true; // so far...
1203
1204    for (unsigned i=0; i < numHierarchies; i++) {
1205       where4tree<whereAxisRootNode> *hierarchyRoot = rootPtr->getChildTree(i);
1206       vector <const whereAxisRootNode *> thisHierarchySelections = hierarchyRoot->getSelections();
1207
1208       if (thisHierarchySelections.size()==0)
1209          // add hierarchy's root item
1210          thisHierarchySelections += &hierarchyRoot->getNodeData();
1211       else
1212          // since the hierarchy selection was not empty, we do _not_
1213          // want to implicitly select whole-program
1214          wholeProgramImplicit = false;
1215
1216       result[i].resize(thisHierarchySelections.size());
1217       for (unsigned j=0; j < thisHierarchySelections.size(); j++)
1218          result[i][j] = thisHierarchySelections[j]->getUniqueId();
1219    }
1220
1221    wholeProgram = wholeProgramImplicit || rootPtr->isHighlighted();
1222    if (wholeProgram) {
1223       // write to wholeProgramFocus:
1224       wholeProgramFocus.resize(numHierarchies);
1225       for (unsigned i=0; i < numHierarchies; i++) {
1226          where4tree<whereAxisRootNode> *hierarchyRoot = rootPtr->getChildTree(i);
1227          const whereAxisRootNode &hierarchyRootData = hierarchyRoot->getNodeData();
1228          unsigned hierarchyRootUniqueId = hierarchyRootData.getUniqueId();
1229          wholeProgramFocus[i] = hierarchyRootUniqueId;
1230       }
1231    }
1232
1233    return result;
1234 }
1235
1236 void whereAxis::clearSelections() {
1237    rootPtr->recursiveClearSelections();
1238 }