af237935db6d419df64b618b7186f9bdbb08d468
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / Agents / OutlineListViewMotif.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*    Copyright (c) 1994 FUJITSU LIMITED      */
24 /*    All Rights Reserved                     */
25
26 /*
27  * $TOG: OutlineListViewMotif.C /main/15 1997/06/18 17:31:56 samborn $
28  *
29  * Copyright (c) 1992 HaL Computer Systems, Inc.  All rights reserved.
30  * UNPUBLISHED -- rights reserved under the Copyright Laws of the United
31  * States.  Use of a copyright notice is precautionary only and does not
32  * imply publication or disclosure.
33  * 
34  * This software contains confidential information and trade secrets of HaL
35  * Computer Systems, Inc.  Use, disclosure, or reproduction is prohibited
36  * withuot the prior express written permission of HaL Computer Systems, Inc.
37  * 
38  *                         RESTRICTED RIGHTS LEGEND
39  * Use, duplication, or disclosure by the Government is subject to
40  * restrictions as set forth in subparagraph (c)(l)(ii) of the Rights in
41  * Technical Data and Computer Software clause at DFARS 252.227-7013.
42  *                        HaL Computer Systems, Inc.
43  *                  1315 Dell Avenue, Campbell, CA  95008
44  * 
45  */
46
47 #include "UAS.hh"
48
49 #define C_WindowSystem
50 #define L_Other
51
52 #define C_OutlineList
53 #define C_OutlineElement
54 #define C_TOC_Element
55 #define L_Basic
56
57 #define C_OutlineListView
58 #define C_LibraryAgent
59 #define L_Agents
60
61 #define C_LibraryMgr
62 #define C_MessageMgr
63 #define L_Managers
64
65 #define USES_OLIAS_FONT
66
67 #include "Prelude.h"
68
69 #include "Managers/CatMgr.hh"
70 #include "Registration.hh"
71
72 #include <WWL/WComposite.h>
73 #include <WWL/WXmScrollBar.h>
74
75 #include <iostream>
76 using namespace std;
77 #include <unistd.h>
78 #include <sys/param.h>
79
80 #ifdef DEBUG
81 #define DEBUGF(X) printf X
82 #else
83 #define DEBUGF(X)
84 #endif
85
86 #if 0
87 #  define RCS_DEBUG(statement) cerr << statement << endl
88 #else
89 #  define RCS_DEBUG(statement) 
90 #endif
91
92 extern "C" { void _Xm_dump_external(XmString); }
93
94 static XmString
95 XmStringCreateComponent (XmStringComponentType tag, void *data, u_int length);
96 static void register_actions();
97
98 enum { XmSTRING_COMPONENT_POINTER = XmSTRING_COMPONENT_USER_BEGIN };
99
100
101 // /////////////////////////////////////////////////////////////////
102 // class constructor
103 // /////////////////////////////////////////////////////////////////
104
105 OutlineListView::OutlineListView (const WComposite &parent, const char *name,
106                                   bool automanage,
107                                   bool enable_activate)
108 : WXmList ((Widget) NULL), f_list (NULL), f_max_level (LEVEL_UNLIMITED),
109   f_current_selection (NULL), f_selected_item_count(0),
110   f_tracking_position (0),
111   f_tracking_element (NULL),
112   f_library_agent (NULL)
113 {
114   static int serial_number = 1;
115
116   // Assign a unique serial number to this outline list. 
117   f_serial_number = serial_number++;
118
119   // Non-scrolled list is useless so make it a scrolled list. 
120   widget = XmCreateScrolledList (parent, (char *) name, NULL, 0);
121
122
123   // if dtinfo_font is defined then get the fontlist for the widget 
124   // and append thte dtinfo_font
125
126   if (window_system().dtinfo_font()) {
127       XmFontList tempFontList, defaultList;
128
129       // get the current font list and make a copy
130       XtVaGetValues(widget, XmNfontList, &tempFontList, NULL);
131       defaultList = XmFontListCopy(tempFontList);
132
133       // append the dtinfo font. XmFontListAppendEntry deallocates the original font list after
134       // extracting the required information. 
135
136       defaultList = XmFontListAppendEntry(defaultList, window_system().dtinfo_font());
137
138       // set the new new font list back
139       XtVaSetValues(widget, XmNfontList, defaultList, NULL);
140
141   }
142
143   // Save this pointer in user data of widget so that actions can
144   // recover. 
145   UserData (this);
146
147 #if 0
148   printf (">>> *** shadow %d, margin %d, highlight %d\n",
149           ShadowThickness(), ListMarginWidth(), HighlightThickness());
150 #endif
151   f_margin = ShadowThickness() + ListMarginWidth() + HighlightThickness();
152   f_selection_policy = SelectionPolicy();
153
154   register_actions();
155
156   SetSingleSelectionCallback (this, (WWL_FUN) &OutlineListView::select);
157   SetBrowseSelectionCallback (this, (WWL_FUN) &OutlineListView::select);
158   SetExtendedSelectionCallback (this, (WWL_FUN) &OutlineListView::select);
159   SetMultipleSelectionCallback (this, (WWL_FUN) &OutlineListView::select);
160
161   SetConvertCallback (this, (WWL_FUN) &OutlineListView::printConvertCallback);
162
163   if (enable_activate)
164     {
165       SetDefaultActionCallback (this, (WWL_FUN) &OutlineListView::activate);
166       f_tracking_possible = 1;
167     }
168   else
169     {
170       f_tracking_possible = 0;
171     }
172   
173   if (automanage)
174     Manage();
175 }
176
177 static Dimension
178 icon_width(Widget w)
179 {
180   XmFontList defaultList;
181   XtVaGetValues(w, XmNfontList, &defaultList, NULL);
182
183   // Setup parameters for calculating click position.
184   char string[2];
185   if (window_system().nofonts())
186     string[0] = ' ';
187   else
188     string[0] = OLIAS_PLACEHOLDER_ICON;
189   string[1] = '\0';
190   XmString thing = XmStringCreate (string, (char*)OLIAS_FONT);
191
192   Dimension rval = XmStringWidth(defaultList, thing);
193   XmStringFree (thing);
194
195   return rval;
196 }
197
198 // /////////////////////////////////////////////////////////////////
199 // class destructor
200 // /////////////////////////////////////////////////////////////////
201
202 OutlineListView::~OutlineListView()
203 {
204 #ifdef BOGUS
205   if (f_list != NULL)
206     f_data_handle = library_mgr().library().get_data_handle();
207 #endif
208 }
209
210
211 // /////////////////////////////////////////////////////////////////
212 // xmstring - return the xm string given an outline element
213 // /////////////////////////////////////////////////////////////////
214
215 XmString
216 OutlineListView::xmstring (OutlineElement *oe, unsigned force, char icon)
217 {
218   if (force || (oe->string_creator() != f_serial_number))
219     {
220       if (oe->xm_string() != NULL)
221         XmStringFree ((XmString) oe->xm_string());
222       oe->set_xm_string ((void *) create_xm_string (oe, f_base_level,
223                                                     f_tracking_possible, icon));
224       oe->string_creator (f_serial_number);
225     }
226   return ((XmString) oe->xm_string());
227 }
228
229
230 // /////////////////////////////////////////////////////////////////
231 // set_list - set the list
232 // /////////////////////////////////////////////////////////////////
233
234 void
235 OutlineListView::set_list (OutlineList *list, BitHandle handle)
236 {
237   // NOTE: Eventually it should be possible to set the list to NULL.
238   if (f_list != NULL && f_list != list )
239     list->free_data_handle (f_data_handle);
240   f_list = list;
241   if (list != NULL)
242     if (handle != 0)
243       f_data_handle = handle ;
244     else
245       f_data_handle = list->get_data_handle();
246   // Determine the base level.
247   f_base_level = 0;
248   if (list->length() > 0) {
249       //  SWM -- need to change this so that when determining
250       //  the base level, we check all of the roots...
251       f_base_level = ((OutlineElement *)(*list)[0])->level();
252   }
253   ON_DEBUG(printf (">> OutlineList base level = %d\n", f_base_level));
254   regen_list();
255 }
256
257
258 // /////////////////////////////////////////////////////////////////
259 // regen_list - regenerate list based on internal list
260 // /////////////////////////////////////////////////////////////////
261
262 static u_int g_table_index;
263
264 void
265 OutlineListView::regen_list()
266 {
267   Xassert (f_list != NULL);
268   // if list is null we should just empty the list. 
269
270   u_int visible_count = f_list->count_expanded (f_data_handle);
271
272   XmStringTable table = new XmString[visible_count];
273   bool *selected_list = new bool[visible_count];
274
275   g_table_index = 0;
276   generate_table (f_list, table, selected_list, 0);
277
278   WArgList args;
279   Items (table, args);
280   ItemCount (visible_count, args);
281   Set (args);
282
283   delete [] table;
284   delete [] selected_list;
285
286
287   // reset tracking position to let dtinfo determine
288   // the new position in track_to()
289   f_tracking_element = NULL;
290   f_tracking_position = 0;
291 }
292
293 // /////////////////////////////////////////////////////////////////
294 // create_outline_string - create the XmString to display
295 // /////////////////////////////////////////////////////////////////
296
297 XmString
298 OutlineListView::create_xm_string (OutlineElement *oe, int base_level,
299                                    unsigned char track_on, char icon) 
300 {
301   XmString string, tmp, next;
302
303   unsigned char track = track_on ? 1 : 0; // normalize track_on into 1 or 0
304
305   // First component is pointer to original element.  Motif 1.2
306   // will ignore this component. 
307 #ifdef NotDefined
308   string = XmStringCreateComponent (XmSTRING_COMPONENT_POINTER,
309                                     &oe, sizeof (oe));
310 #endif
311   // Next component is expand/contract icon, if any.
312   // 1 for tracker, 1 for arrow, 3 for icon and spacing, 1 for terminator
313   char s[64];
314   char *p = s ;
315   assert ((oe->level() - base_level + 6) * sizeof(char) < 64);
316   // Spaces before and including arrow, if any
317   if (window_system().nofonts())
318   {
319     memset (s, ' ', oe->level() - base_level + 1 + track);
320   }
321   else
322   {
323     if (track)
324       {
325         if (icon)
326           {
327             s[0] = icon ;
328           }
329         else
330           {
331             s[0] = OLIAS_PLACEHOLDER_ICON ;
332           }
333         p++ ;
334       }
335     memset (p, OLIAS_PLACEHOLDER_ICON, oe->level() - base_level);
336
337     unsigned int pos = oe->level() - base_level + track;
338
339     // Only show the icon if this entry has children and
340     // the max level is unlimited or max level is higher than this level. 
341     
342     if (oe->has_children() &&
343         (f_max_level == LEVEL_UNLIMITED || f_max_level > oe->level())) {
344       if (oe->is_expanded (f_data_handle))
345         s[pos] = OLIAS_EXPANDED_ICON;
346       else
347         s[pos] = OLIAS_CONTRACTED_ICON;
348     }
349     else
350         s[pos] = OLIAS_NO_CHILDREN_ICON;
351   }
352   // Slot for icon -- plug in icon based on element type.
353   char entry_icon = '\0';
354   
355   if (oe->type() == TOC_Element::TOC_ElementClass)
356     {
357       UAS_ObjectType type = ((TOC_Element *) oe)->toc()->type();
358       switch (type)
359         {
360         case UAS_LIBRARY:
361           entry_icon = OLIAS_INFOLIB_ICON;
362           break;
363         case UAS_BOOKCASE:
364           entry_icon = OLIAS_INFOBASE_ICON;
365           break;
366         case UAS_BOOK:
367           entry_icon = OLIAS_BOOK_ICON;
368           break;
369         default:
370           entry_icon = '\0';
371         }
372     }
373   
374   if (entry_icon != '\0' && !window_system().nofonts())
375     {
376       s[oe->level() - base_level + 1 + track] = OLIAS_SPACE04;
377       s[oe->level() - base_level + 2 + track] = entry_icon;
378       s[oe->level() - base_level + 3 + track] = OLIAS_SPACE04;
379       s[oe->level() - base_level + 4 + track] = '\0';
380     }
381   else if (window_system().nofonts())
382     {
383       s[oe->level() - base_level + 1 + track] = ' ';
384       s[oe->level() - base_level + 2 + track] = '\0';
385     }
386   else
387     s[oe->level() - base_level + 1 + track] = '\0';
388   
389   next = XmStringCreate (s, (char*)OLIAS_FONT);
390   
391 #ifdef NotDefined
392   // Concat the parts 
393   tmp = string;
394   string = XmStringConcat (tmp, next);
395   XmStringFree (tmp);
396   XmStringFree (next);
397 #else
398   string = next ;
399 #endif
400   
401   // Final component is text of item
402   next = XmStringCreateLocalized ((String) oe->display_as());
403   tmp = string;
404   string = XmStringConcat (tmp, next);
405   XmStringFree (tmp);
406   XmStringFree (next);
407   //  _Xm_dump_external (string);
408   
409   return (string);
410 }
411
412
413 // /////////////////////////////////////////////////////////////////
414 // set_icon - set the icon in the xm string (the quick way)
415 // /////////////////////////////////////////////////////////////////
416
417 // Warning: This routine depends heavily upon the format of the XmString
418 // generated above.  Any changes in that string WILL break this routine.
419
420 void
421 OutlineListView::set_icon (OutlineElement *oe)
422 {
423   xmstring (oe, 1);             // force creation of string
424 }
425
426
427 // /////////////////////////////////////////////////////////////////
428 // generate_table - generate XmStringTable starting at list
429 // /////////////////////////////////////////////////////////////////
430
431 // NOTE: Can probably (and should) change g_element to f_element 
432 static OutlineElement *g_element;
433
434 void
435 OutlineListView::generate_table (OutlineList *list, XmStringTable &table,
436                                  bool *selected_list, u_int level, unsigned force)
437 {
438   u_int i;
439
440 #if 0
441   printf ("In generate_table, list = %p, level = %d\n", list, level);
442 #endif
443   for (i = 0; i < list->length(); i++)
444     {
445       g_element = ((OutlineElement *)(*list)[i]);
446       // NOTE: temporary for now - calc level in outline list code - DJB
447       // The level can be stored in the list, instead of the element.
448       // Store pointer to list in each element to back tracing. 
449       
450       // Level is initialized on first display.
451       // NOTE: This is a temporary hack.  15:59 01/13/93 DJB 
452       if (g_element->string_creator() == 0)
453         g_element->level (level);
454       
455       xmstring (g_element, force); // create the string
456
457       selected_list[g_table_index] = g_element->is_selected (f_data_handle);
458       table[g_table_index++] = (XmString) g_element->xm_string();
459       
460       //  g_element->print();
461       if ((g_element->has_children()) &&
462           g_element->is_expanded (f_data_handle))
463         generate_table (g_element->children(), table, selected_list, level+1, force);
464     }
465 }
466
467
468 // /////////////////////////////////////////////////////////////////
469 // XmStringCreateComponent
470 // /////////////////////////////////////////////////////////////////
471
472 static XmString
473 XmStringCreateComponent (XmStringComponentType tag,
474                          void *data, u_int length)
475 {
476   // NOTE: Possible improvement is to handle greater lengths, but this
477   // is easiest for now.  DJB 8-8-92
478   assert (length <= 124);
479   
480   // Length = ASN header + Component header + data 
481   unsigned char total_length = 2 + length;
482   
483   XmString string = (XmString) XtMalloc (total_length + 4);
484   unsigned char *p = (unsigned char*) string;
485   
486   // Write the ASN.1 header (values from XmString.c)
487   *p++ = 0xdf;
488   *p++ = 0x80;
489   *p++ = 0x06;
490   *p++ = total_length;
491   
492   // Write the component header
493   *p++ = tag;
494   *p++ = length;
495   
496   memcpy (p, data, length);
497 #if 0
498   printf ("data = %p\n", data);
499   
500   int i;
501   printf ("XmStringComponent = ");
502   for (i = 0; i < total_length + 4; i++)
503     printf ("%02x ", string[i]);
504   puts ("");
505 #endif
506   return (string);
507 }
508
509
510 // /////////////////////////////////////////////////////////////////
511 // y_to_outline_element
512 // /////////////////////////////////////////////////////////////////
513
514 OutlineElement *
515 OutlineListView::y_to_outline_element (Position y)
516 {
517   OutlineElement *oe;
518   // NOTE: if having problems with list expansion items, check here 
519   
520   // NOTE: Motif 1.2 bug alert: YToPos returns zero based, instead of 1 based
521   // as documented. 
522   
523   // NOTE: seems to be fixed returning 1 based 
524   
525   f_item_pos = YToPos (y) - 1;  
526   
527 #if 0
528   printf ("Click in item %d, item count = %d\n", f_item_pos, ItemCount());
529 #endif
530   // Another Motif 1.2 bug.  YToPos should return 0 for bogus position,
531   // but it doesn't check the range. 
532   if (f_item_pos < 0 || f_item_pos >= ItemCount())
533     return (NULL);
534   
535   oe = item_at (f_item_pos);
536   
537 #if 1
538   ON_DEBUG(printf ("Found element %p (%s) at pos %d, level %d\n",
539                    oe, oe->display_as(), f_item_pos, oe->level()));
540 #endif
541   
542   f_item_pos++;         // Adjust for Motif bug here. 
543   return (oe);
544 }
545
546
547 // /////////////////////////////////////////////////////////////////
548 // icon_extent
549 // /////////////////////////////////////////////////////////////////
550
551 inline Position
552 OutlineListView::icon_extent(OutlineElement *oe) const
553 {
554   ON_DEBUG (printf ("horiz sbar value = %d\n",
555                     WXmScrollBar(HorizontalScrollBar()).Value()));
556   ON_DEBUG (printf ("  extent = %d\n",
557                     f_margin - WXmScrollBar(HorizontalScrollBar()).Value() +
558                     icon_width(*this) * (oe->level() - f_base_level + 1 + f_tracking_possible)));
559   
560   return (f_margin - WXmScrollBar(HorizontalScrollBar()).Value() +
561           icon_width(*this) * (oe->level() - f_base_level + 1 + f_tracking_possible));
562 }
563
564
565 // /////////////////////////////////////////////////////////////////
566 // select_start - mouse button pressed (action proc)
567 // /////////////////////////////////////////////////////////////////
568
569 void
570 OutlineListView::_select_start (Widget w, XEvent *event,
571                                 String *params, Cardinal *num_params)
572 {
573   WXmPrimitive W (w);
574   OutlineListView *lv = (OutlineListView *) W.UserData();
575   if (lv == NULL)
576     return;
577   lv->select_start (w, event, params, num_params);
578 }
579
580 void
581 OutlineListView::select_start (Widget w, XEvent *event,
582                                String *params, Cardinal *num_params)
583 {
584   if (event->type != ButtonPress)
585     return;
586   
587   ON_DEBUG(printf ("Select start at (%d, %d)\n",
588                    event->xbutton.x, event->xbutton.y));
589   
590   f_outline_element = y_to_outline_element (event->xbutton.y);
591   
592 #ifdef DEBUG
593   if (f_outline_element)
594     {
595       printf (">>> *** shadow %d, margin %d, highlight %d\n",
596               ShadowThickness(), ListMarginWidth(), HighlightThickness());
597       
598       printf ("margin = %d, icon width = %d, level = %d\n",
599               f_margin, icon_width(*this), f_outline_element->level());
600     }
601   else
602     printf ("No element found at this location\n");
603 #endif
604   // See if item valid and icon was clicked in.
605   ON_DEBUG (printf ("X pos = %d\n", event->xbutton.x));
606   if (f_outline_element && event->xbutton.x < icon_extent (f_outline_element)
607       && f_outline_element->has_children())
608     {
609       ON_DEBUG (puts ("Clicked in an icon #1"));
610       return;
611     }
612   
613   // Set variable to signal that icon was not clicked on.  This is
614   // checked in the select_end method. 
615   f_outline_element = NULL;
616   
617   ON_DEBUG (puts ("Passing through start"));
618   XtCallActionProc (w, "ListBeginSelect", event, params, *num_params);
619 }
620
621
622 // /////////////////////////////////////////////////////////////////
623 // select_end - mouse button released (action_proc)
624 // /////////////////////////////////////////////////////////////////
625
626 void
627 OutlineListView::_select_end (Widget w, XEvent *event, String *params,
628                               Cardinal *num_params)
629 {
630   WXmPrimitive W (w);
631   OutlineListView *lv = (OutlineListView *) W.UserData();
632   if (lv == NULL)
633     return;
634   lv->select_end (w, event, params, num_params);
635 }
636
637 void
638 OutlineListView::select_end (Widget w, XEvent *event,
639                              String *params, Cardinal *num_params)
640 {
641   if (event->type != ButtonRelease)
642     return;
643   
644   ON_DEBUG (printf ("Select end at (%d, %d)\n",
645                     event->xbutton.x, event->xbutton.y));
646   
647   // See if selection ended on the same item and in range.
648   if (f_outline_element != NULL &&
649       (y_to_outline_element (event->xbutton.y) == f_outline_element) &&
650       f_outline_element && event->xbutton.x < icon_extent (f_outline_element)
651       && f_outline_element->has_children() &&
652       (f_max_level == LEVEL_UNLIMITED ||
653        f_max_level > f_outline_element->level()))
654     {
655       ON_DEBUG (puts ("Clicked in an icon #2"));
656       unsigned char current_policy = f_selection_policy;
657       u_int subcount;
658       
659       // Remove previous tracking, if any.
660       // Must happen before any expand/contract takes place
661       // or either the position will be wrong, or item hidden. 
662       if (f_tracking_position > 0)
663         {
664           if (window_system().nofonts())
665             set_track_icon (f_tracking_element, f_tracking_position, ' ');
666           else
667             set_track_icon (f_tracking_element, f_tracking_position,
668                             OLIAS_PLACEHOLDER_ICON);
669           f_tracking_position = 0;
670         }
671       
672       if (f_outline_element->is_expanded (f_data_handle))
673         {
674           /* -------- Toggle current state to contracted -------- */
675           f_outline_element->set_contracted (f_data_handle);
676           subcount =
677             f_outline_element->children()->count_expanded (f_data_handle);
678           DeleteItemsPos (subcount, f_item_pos + 1);
679         }
680       else
681         {
682           /* -------- Toggle current state to expanded -------- */
683           // Turn on the wait cursor if database access to occur.
684           bool wait_state = FALSE;
685           if (!f_outline_element->children_cached())
686             {
687               window_system().set_wait_state (WS_ON);
688               wait_state = TRUE;
689             }
690           
691           f_outline_element->set_expanded (f_data_handle);
692           subcount =
693             f_outline_element->children()->count_expanded (f_data_handle);
694           XmStringTable table = new XmString[subcount];
695           bool *selected_list = new bool[subcount];
696           g_table_index = 0;
697           generate_table (f_outline_element->children(),
698                           table, selected_list,
699                           f_outline_element->level() + 1);
700           AddItemsUnselected (table, subcount, f_item_pos + 1);
701           // Must be in multiple select mode to add selected items 
702           if (f_selection_policy != XmMULTIPLE_SELECT)
703             {
704               SelectionPolicy (XmMULTIPLE_SELECT);
705               current_policy = XmMULTIPLE_SELECT;
706             }
707           // Bogus Motif should have a routine to select multiple items. 
708           while (subcount > 0)
709             if (selected_list[--subcount])
710               SelectPos (f_item_pos + subcount + 1, False);
711           delete table;
712           delete selected_list;
713           
714           // Turn the wait cursor off if it was on. 
715           if (wait_state)
716             window_system().set_wait_state (WS_OFF);
717         }
718       
719       // Update the icon for new mode 
720       set_icon (f_outline_element);
721       
722       // Tell the list about the change
723       XmString item = (XmString) f_outline_element->xm_string();
724       bool selected = PosSelected (f_item_pos);
725       ReplaceItemsPosUnselected (&item, 1, f_item_pos);
726       
727       // YAMB (Yet Another Motif Bug): Cannot call ReplaceItemsPos
728       // because if the item matches another item in the list that
729       // happens to be selected, the item replaced will be selected. 
730       // So, do selection here, if needed. 
731       if (selected)
732         {
733           // Bugs Galore!!  Motif will automatically (gee thanks)
734           // deselect all other selections on a select call, even
735           // if this list is in extended select mode.  Work around
736           // by switching to multiple select mode which allows it.
737           if (current_policy != XmMULTIPLE_SELECT)
738             {
739               SelectionPolicy (XmMULTIPLE_SELECT);
740               current_policy = XmMULTIPLE_SELECT;
741             }
742           
743           // Select it, do not call the select callback. 
744           SelectPos (f_item_pos, False);
745         }
746       
747       // Update the tracking if activated.
748       // Must happen after the expand/contract so that the track
749       // location is correctly computed.
750       bool scroll = (f_tracking_element == f_outline_element);
751       if (f_tracking_possible && f_library_agent)
752         f_library_agent->track (scroll);
753       
754       // Switch the policy back, if it had been changed. 
755       if (f_selection_policy != current_policy)
756         SelectionPolicy (f_selection_policy);
757     }
758   // f_outline_element can only be NULL if the click down wasn't in the icon. 
759   else if (f_outline_element == NULL)
760     {
761       ON_DEBUG (("Passing through end"));
762       XtCallActionProc (w, "ListEndSelect", event, params, *num_params);
763     }
764 #if 0
765 else
766   ON_DEBUG(puts ("Click down in icon, but click up wasn't"));
767 #endif
768 }
769
770 void
771 OutlineListView::update_list(OutlineList *list, BitHandle handle)
772 {
773   u_int visible_count = list->count_expanded (handle);
774   
775   XmStringTable table = new XmString[visible_count];
776   bool *selected_list = new bool[visible_count];
777   g_table_index = 0;
778   generate_table (list, table, selected_list, 0, 1);
779
780   WArgList args;
781   Items (table, args);
782   ItemCount (visible_count, args);
783   Set (args);
784
785   delete table;
786   delete selected_list;
787 }
788
789 // /////////////////////////////////////////////////////////////////
790 // register_actions
791 // /////////////////////////////////////////////////////////////////
792
793 void
794 OutlineListView::register_actions()
795 {
796   static bool registered = FALSE;
797   if (registered)
798     return;
799   
800   static XtActionsRec actions_list[] =
801     {
802       {(char*)"OutlineListBeginSelect",   OutlineListView::_select_start},
803       {(char*)"OutlineListEndSelect",     OutlineListView::_select_end},
804     };
805   
806   
807   XtAppAddActions (AppContext(), actions_list, XtNumber (actions_list));
808 }
809
810 /**********************************************************************
811 OutputAnAtomName: Translates a target from its internal atom format into 
812 a human readable character string.
813 **********************************************************************/
814 static void
815 OutputAnAtomName(Widget w, Atom target)
816 {
817     char  *AtomName = (char *)malloc(sizeof(char *) * 34);
818     
819     AtomName = XGetAtomName(XtDisplay(w), target);
820     printf("\t%s\n", AtomName);
821 }
822
823 // /////////////////////////////////////////////////////////////////
824 // /////////////////////////////////////////////////////////////////
825
826 void
827 OutlineListView::printConvertCallback(WCallback *wcb)
828 {
829     XmConvertCallbackStruct *ccs = (XmConvertCallbackStruct *)wcb->CallData();
830     Widget         w = wcb->GetWidget();
831     unsigned int * selectedPositions;
832     int            selectedItemCount;
833     char           filepath[MAXPATHLEN];
834     OutlineElement *oe = NULL;
835     FILE *         fp;
836     int            n;
837     
838     Atom FILE_NAME = XInternAtom(XtDisplay(w), XmSFILE_NAME, False);
839     Atom TARGETS = XInternAtom(XtDisplay(w), XmSTARGETS, False);
840     Atom MOTIF_EXPORT_TARGETS = XInternAtom(XtDisplay(w), XmS_MOTIF_EXPORT_TARGETS, False);
841     
842     RCS_DEBUG("printConvertCallback: called.\n");
843     
844     if (ccs == NULL) {
845         return;
846     }
847     
848     RCS_DEBUG("\nNow in ConvertCallback.\n");
849     RCS_DEBUG("\tSelection: ");
850     OutputAnAtomName((Widget)w, ccs->selection);
851     RCS_DEBUG("\tTarget: ");
852     OutputAnAtomName((Widget)w, ccs->target); 
853     
854     /*
855      *  XmeDragSource is going to call ConvertCallback and ask
856      *  it to convert MOTIF_EXPORT_TARGETS. 
857      */ 
858     if ( (ccs->target == MOTIF_EXPORT_TARGETS) ||
859          (ccs->target == TARGETS))
860     {
861         
862         /* 
863          *  this callback must support the FILE_NAME transfer
864          *  as this is the mechanism the Printer Icon on the 
865          *  front panel uses to transfer data.
866          */
867         Atom *targs = (Atom *) XtMalloc(sizeof(Atom) * 1);
868         if (targs == NULL) {
869             ccs->status = XmCONVERT_REFUSE;
870             printf("Refuse.\n");
871             return;
872         }
873         n = 0;
874         targs[n] = FILE_NAME; n++;
875         
876         ccs->value = (XtPointer) targs;
877         ccs->type = XA_ATOM;
878         ccs->length = n;
879         ccs->format = 32;
880         ccs->status = XmCONVERT_DONE;  /* Yes, we converted the target. */
881     }
882     
883     /* 
884      *  As the drop site supports FILE_NAME as an import target, then
885      *  the drop site will ask ConvertCallback to convert the 
886      *  value to FILE_NAME format. 
887      */ 
888     else if (ccs->target == FILE_NAME) {
889         
890         /*
891          *   Get the text from the container
892          */
893         XtVaGetValues(w,
894                       XmNselectedPositions,    &selectedPositions,
895                       XmNselectedItemCount,   &selectedItemCount,
896                       NULL);
897
898         f_selected_item_count = selectedItemCount;
899         
900         // This is commented out for now until i go and make this function private      
901 #if 0
902         tmpfile = _DtActGenerateTmpFile(NULL, 
903                                         "%s.itp", 
904                                         ( S_IRUSR|S_IRGRP|S_IWUSR|S_IWGRP), 
905                                         &fp);
906 #else   
907         strcpy(filepath, getenv("HOME"));
908         strcat(filepath, "/.dt/tmp/file.itp");
909 #endif
910         
911         // print on on debug
912         fprintf(stderr, "printConvertCallback: temp file = %s.\n", filepath);
913         
914         // open the temporary file for writing
915
916         if ((fp = fopen(filepath, "w")) == NULL) {
917             fprintf(stderr, "Cannot open file %s.\n", filepath);
918         }
919
920         // write out each element in the list
921
922         for (int i = 0; i < f_selected_item_count; i++) {
923             oe = item_at(selectedPositions[i] - 1) ;
924             TOC_Element *te = (TOC_Element *)oe;
925
926             // write out the locator
927             if (fputs(((TOC_Element *)te)->toc()->locator(), fp) == EOF) {
928                 fprintf(stderr, "Cannot write file %s in current directory.\n", filepath);
929                 return;
930             }
931
932             // write out eol
933             if (fputs("\n", fp) == EOF) {
934                 fprintf(stderr, "Cannot write file %s in current directory.\n", filepath);
935                 return;
936             }
937             
938         }
939
940         // close the file
941         fclose(fp);
942         
943         AppPrintData* p = window_system().GetAppPrintData();
944         p->f_pshell_parent = w;
945          
946         // Assign file name to XmConvertCallbackStruct. 
947         
948         ccs->value  = (XtPointer)filepath;
949         ccs->type   = XA_STRING;
950         ccs->length = strlen(filepath);
951         ccs->format = 8;
952         ccs->status = XmCONVERT_DONE;
953     }
954     
955     else  {
956         /* Unexpected target. */
957         ccs->status = XmCONVERT_REFUSE;
958     } 
959 }
960
961 // /////////////////////////////////////////////////////////////////
962 // select - selection callback
963 // /////////////////////////////////////////////////////////////////
964
965 void
966 OutlineListView::select (WCallback *wcb)
967 {
968   XmListCallbackStruct *lcs = (XmListCallbackStruct *) wcb->CallData();
969   XmStringTable items = Items();
970   OutlineElement *oe = NULL;
971   
972   DEBUGF (("**** select: item count = %d, pos = %d, selected = %s\n",
973            lcs->selected_item_count, lcs->item_position,
974            (PosSelected (lcs->item_position) ? "true" : "false")));
975   
976   //  DEBUGF ((">>>> SELECTION TYPE <<< %d\n", lcs->selection_type));
977   
978   /* Fix for DTS #6303 -- Removed "if" statement.  Always deselect all. */
979   deselect (f_list, DESELECT_ALL);
980   
981   if (f_selection_policy == XmMULTIPLE_SELECT ||
982       f_selection_policy == XmEXTENDED_SELECT)
983     {
984       f_selected_item_count = lcs->selected_item_count;
985 #ifdef DEBUG
986       printf ("selected # = %d\n", f_selected_item_count);
987       printf ("         # = %d\n", SelectedItemCount());
988 #endif
989       for (int i = 0; i < f_selected_item_count; i++)
990         {
991           DEBUGF (("M-> extracting at %d\n", lcs->selected_item_positions[i]));
992           oe = item_at(lcs->selected_item_positions[i] - 1) ;
993           oe->set_selected (f_data_handle);
994         }
995       // Make the current selection the last item selected.  This is
996       // (unfortunately) used by the LibraryAgent for the detach feature.
997       // The reason is that the Document List didn't support these selection
998       // modes until printing was added and could only have one selection.
999       // If there's more than one selection it won't matter because the
1000       // Detach button will be insensitive. 
1001       f_current_selection = oe;
1002     }
1003   else
1004     {
1005       if (PosSelected (lcs->item_position))
1006         {
1007           DEBUGF (("--> extracting pointer @ %d\n", lcs->item_position));
1008           oe = item_at(lcs->item_position - 1) ;
1009           oe->set_selected (f_data_handle);
1010           // Save the selection:
1011           f_current_selection = oe;
1012           f_selected_item_count = 1;
1013         }
1014     }
1015   if (f_selected_item_count != 1) // 94/10/28 haya 
1016     f_item_pos = 0;               // modify to change sensitive of detach
1017   else                            // button by cursol key.
1018     f_item_pos = *lcs->selected_item_positions; //
1019   
1020   notify (ENTRY_SELECTED);
1021   
1022   // DISCOURSE:  If the list is in extended selection mode it is possible
1023   // for the user to toggle in and out of add mode (cf. Motif Style Guide
1024   // 1.2 pg. 4-6).  Unfortunately there's no way for us to know if he is
1025   // in add mode, but that's ok for now, since there's a bug in Motif 1.2
1026   // which deselects the previous selection, even when add mode is on and
1027   // the selection policy is extended select. 
1028 }
1029
1030
1031 // /////////////////////////////////////////////////////////////////
1032 // deselect - turn off select bit in in-memory elements in list
1033 // /////////////////////////////////////////////////////////////////
1034
1035 #define OUTLINE_ELEMENT(I) ((OutlineElement *)(*list)[I])
1036
1037 void
1038 OutlineListView::deselect (OutlineList *list, deselect_mode_t mode)
1039 {
1040   u_int i;
1041   
1042   for (i = 0; i < list->length(); i++)
1043     {
1044       OUTLINE_ELEMENT(i)->unset_selected (f_data_handle);
1045       if (OUTLINE_ELEMENT(i)->children_cached() &&
1046           OUTLINE_ELEMENT(i)->has_children() &&
1047           (mode == DESELECT_ALL ||
1048            OUTLINE_ELEMENT(i)->is_expanded (f_data_handle)))
1049         deselect (OUTLINE_ELEMENT(i)->children(), mode);
1050     }
1051 }
1052
1053
1054 // /////////////////////////////////////////////////////////////////
1055 // activate - activate callback
1056 //
1057 // Here's the deal: to get a consistent result for "activate" on any
1058 // entry in the booklist, such expert activation will always** do at
1059 // least an open of the object in the/a reading window. If the item
1060 // also is the parent of a sub-hierarchy of more nodes, the first
1061 // level beneath this object will be expanded [or contracted] in the
1062 // booklist. No expansion will occur, of course, if it is the lowest
1063 // leaf in the tree. A TOC (== a "Book" level) will display its TOC
1064 // page per its OutlineElement type; all other entries will display
1065 // the document or the top-most document in the sub-hierarchy. -jcb
1066 //
1067 // ** Only if an entry has children and is initially expanded,
1068 //  double-click contracts it, but does Not [re-]display its topmost
1069 //  document. The assumption is being made that user Contract of a
1070 //  hierarchical level is more often than not an expression of lesser
1071 //  interest in its content. In many cases, the topmost doc will have
1072 //  been previously displayed by a double-click on the entry while
1073 //  contracted, anyway.
1074 //
1075 // Also, the double-click action "selects" only the activated entry.
1076 // /////////////////////////////////////////////////////////////////
1077
1078 void
1079 OutlineListView::activate (WCallback *wcb)
1080 {
1081   XmListCallbackStruct *lcs = (XmListCallbackStruct *) wcb->CallData();
1082   XmStringTable items = Items();
1083   OutlineElement *oe;
1084   int   expanded = False;
1085   
1086   oe = item_at(lcs->item_position - 1) ;
1087
1088 #ifdef DEBUG
1089   if( oe == NULL ) printf( "OLV::activate: NULL outline element pointer\n" ) ;
1090 #endif
1091
1092   if ( oe->has_children() )
1093     {
1094       u_int subcount;
1095
1096       // Remove previous tracking, if any.
1097       // Must happen before any expand/contract takes place
1098       // or either the position will be wrong, or item hidden.
1099       if (f_tracking_position > 0)
1100         {
1101           if (window_system().nofonts())
1102             set_track_icon (f_tracking_element, f_tracking_position, ' ');
1103           else
1104             set_track_icon (f_tracking_element, f_tracking_position,
1105                             OLIAS_PLACEHOLDER_ICON);
1106           f_tracking_position = 0;
1107         }
1108
1109       if (oe->is_expanded (f_data_handle))
1110         {
1111           expanded = True;
1112 #ifdef DEBUG
1113           printf( "OLV::activate: entry is Expanded\n" ) ;
1114 #endif
1115           /* -------- Toggle current state to contracted -------- */
1116           oe->set_contracted (f_data_handle);
1117           subcount =
1118             oe->children()->count_expanded (f_data_handle);
1119           DeleteItemsPos (subcount, lcs->item_position + 1);
1120         }
1121       else
1122         {
1123 #ifdef DEBUG
1124           printf( "OLV::activate: entry is Not expanded\n" ) ;
1125 #endif
1126           /* -------- Toggle current state to expanded -------- */
1127           // Turn on the wait cursor if database access to occur.
1128           bool wait_state = FALSE;
1129           if (!oe->children_cached())
1130             {
1131               window_system().set_wait_state (WS_ON);
1132               wait_state = TRUE;
1133             }
1134
1135           oe->set_expanded (f_data_handle);
1136           subcount =
1137             oe->children()->count_expanded (f_data_handle);
1138           XmStringTable table = new XmString[subcount];
1139           bool *sel_list = new bool[subcount];
1140           g_table_index = 0;
1141           generate_table (oe->children(),
1142                           table, sel_list,
1143                           oe->level() + 1);
1144           AddItemsUnselected (table, subcount, lcs->item_position + 1);
1145           delete table;
1146           delete sel_list;
1147
1148           // Turn the wait cursor off if it was on.
1149           if (wait_state)
1150             window_system().set_wait_state (WS_OFF);
1151         }
1152
1153       // Update the icon for new mode
1154       set_icon (oe);
1155       // Tell the list about the change
1156       XmString item = (XmString) oe->xm_string();
1157       ReplaceItemsPosUnselected (&item, 1, lcs->item_position);
1158
1159       // Update the tracking if activated.
1160       // Must happen after the expand/contract so that the track
1161       // location is correctly computed.
1162       bool scroll = (f_tracking_element == oe);
1163       if (f_tracking_possible && f_library_agent)
1164         f_library_agent->track (scroll);
1165     }
1166   else
1167     {
1168       // seems to occur ok for has_children case, above...
1169       deselect (f_list, DESELECT_ALL);
1170     }
1171
1172   // Select it as the only selection; do not call the select callback.
1173   // Need to reset all class pointers to elements or item pos, regardless
1174   // of traversal method paths in which used by class.
1175   SelectPos (lcs->item_position, True);
1176   oe->set_selected (f_data_handle);
1177   f_current_selection = oe;
1178   f_selected_item_count = 1;
1179   f_item_pos = lcs->item_position;
1180
1181   f_outline_element = NULL;    // because it should be with this path
1182
1183
1184   ON_DEBUG(cout << ">>>>>> CALLING DISPLAY ON ELEMENT <<<<<<" << endl);
1185
1186   if ((oe->type() == TOC_Element::TOC_ElementClass) && !expanded)
1187     {
1188       mtry {
1189           UAS_ObjectType type = ((TOC_Element *) oe)->toc()->type();
1190           switch (type)
1191           {
1192             case UAS_LIBRARY:
1193               {
1194                 UAS_List<UAS_Common> kids =
1195                       ((TOC_Element *)oe)->toc()->children();
1196                 if (   (kids != (const int)NULL)
1197                     && (kids[0] != (const int)NULL)
1198                     && (kids[0]->type() == UAS_BOOKCASE))
1199                 {
1200                   UAS_List<UAS_Common> bckids = kids[0]->children();
1201                   if (! (bckids[0] == (const int)NULL))
1202                   {
1203                     bckids[0]->retrieve();
1204                   }
1205                 }
1206               }
1207               break;
1208
1209             case UAS_BOOKCASE:
1210               {
1211                 UAS_List<UAS_Common> kids =
1212                       ((TOC_Element *)oe)->toc()->children();
1213                 if (! (kids[0] == (const int)NULL))
1214                 {
1215                   kids[0]->retrieve();
1216                 }
1217               }
1218               break;
1219
1220             default:
1221             case UAS_BOOK:
1222               // let the OutlineElement "display" method handle it.
1223               oe->display();
1224               break;
1225           }
1226         }
1227       mcatch_any()
1228         {
1229           message_mgr().error_dialog( (char*)UAS_String(CATGETS(
1230                            Set_UrlAgent, 5, "Document not found." )) ) ;
1231         }
1232       end_try;
1233     }
1234   else if (oe->type() != TOC_Element::TOC_ElementClass)  oe->display();
1235 }
1236
1237
1238 // /////////////////////////////////////////////////////////////////
1239 // data_handle - return a new data handle
1240 // /////////////////////////////////////////////////////////////////
1241
1242
1243 BitHandle
1244 OutlineListView::data_handle (BitHandle new_handle)
1245 {
1246   f_data_handle = new_handle;
1247   // Need a variable since routine modifies it.
1248   u_int item_pos = 0;
1249   update_highlighting (f_list, item_pos);
1250   return new_handle;
1251 }
1252
1253
1254 // NOTE: is the  calling objects responsibility to make sure that expanded
1255 // flag is correct with the current visual representation 
1256
1257 // /////////////////////////////////////////////////////////////////
1258 // update_highlighting
1259 // /////////////////////////////////////////////////////////////////
1260
1261
1262 void
1263 OutlineListView::update_highlighting_recursive (OutlineList *list,
1264                                                 u_int &item_pos)
1265 {
1266   for (int i = 0; i < list->length(); i++)
1267     {
1268       // Select it, do not call the select callback.  
1269       if (OUTLINE_ELEMENT(i)->is_selected (f_data_handle))
1270         {
1271           if (!PosSelected(item_pos))
1272             {
1273               SelectPos (item_pos, False);
1274             }
1275         } else {
1276           DeselectPos(item_pos);
1277         }
1278       item_pos++;
1279       
1280       if (OUTLINE_ELEMENT(i)->is_expanded (f_data_handle) &&
1281           OUTLINE_ELEMENT(i)->has_children())
1282         update_highlighting (OUTLINE_ELEMENT(i)->children(), item_pos);
1283     }
1284 }
1285
1286
1287 void
1288 OutlineListView::update_highlighting (OutlineList *list, u_int &item_pos)
1289 {
1290   
1291   if (item_pos == 0)
1292     {
1293       DeselectAllItems();
1294       item_pos = 1 ;
1295     }
1296   
1297   // Motif bug - see above.
1298   unsigned char current_policy = f_selection_policy;
1299   
1300   if (f_selection_policy != XmMULTIPLE_SELECT)
1301     {
1302       current_policy = f_selection_policy;
1303       f_selection_policy = XmMULTIPLE_SELECT;
1304       SelectionPolicy (f_selection_policy);
1305     }
1306   
1307   update_highlighting_recursive (list, item_pos);
1308   
1309   // Switch the policy back, if it was changed. 
1310   if (f_selection_policy != current_policy)
1311     {
1312       f_selection_policy = current_policy;
1313       SelectionPolicy (f_selection_policy);
1314     }
1315 }
1316
1317
1318 // /////////////////////////////////////////////////////////////////
1319 // selected_item_list
1320 // /////////////////////////////////////////////////////////////////
1321
1322 List *
1323 OutlineListView::selected_item_list()
1324 {
1325   return (f_list->selected_items (f_data_handle));
1326 }
1327
1328
1329 // /////////////////////////////////////////////////////////////////
1330 // clear
1331 // /////////////////////////////////////////////////////////////////
1332
1333 void
1334 OutlineListView::clear()
1335 {
1336   DeselectAllItems();
1337   deselect(f_list, DESELECT_ALL);
1338   f_selected_item_count = 0;
1339 }
1340
1341 // /////////////////////////////////////////////////////////////////
1342 // track_to - display tracking icon at specified position
1343 // /////////////////////////////////////////////////////////////////
1344
1345 void
1346 OutlineListView::track_to (OutlineElement *oe, u_int position, char icon)
1347 {
1348   // Remove the old tracking icon, if any set. 
1349   if (f_tracking_position > 0)
1350     if (window_system().nofonts())
1351       set_track_icon (f_tracking_element, f_tracking_position, ' ');
1352     else
1353       set_track_icon (f_tracking_element, f_tracking_position,
1354                       OLIAS_PLACEHOLDER_ICON);
1355   
1356   // Set the new tracking icon. 
1357   set_track_icon (oe, position, icon);
1358   f_tracking_position = position;
1359   f_tracking_element = oe;
1360 }
1361
1362
1363 void
1364 OutlineListView::untrack()
1365 {
1366   if (f_tracking_position > 0)
1367     if (window_system().nofonts())
1368       set_track_icon (f_tracking_element, f_tracking_position, ' ');
1369     else
1370       set_track_icon (f_tracking_element, f_tracking_position,
1371                       OLIAS_PLACEHOLDER_ICON);
1372   
1373   f_tracking_position = 0;
1374 }
1375
1376
1377 // Warning: This routine depends heavily upon the format of the XmString
1378 // generated earlier.  Any changes in that string WILL break this routine.
1379
1380 void
1381 OutlineListView::set_track_icon (OutlineElement *oe, u_int position, char icon)
1382 {
1383   xmstring (oe, 1, icon);
1384
1385   XmStringTable items = Items();
1386
1387   // Tell the list about the change
1388   XmString item = (XmString) oe->xm_string();
1389   bool selected = PosSelected (position);
1390   ReplaceItemsPosUnselected (&item, 1, position);
1391   if (selected)
1392     {
1393       unsigned char current_policy = f_selection_policy;
1394       if (current_policy != XmMULTIPLE_SELECT)
1395         {
1396           SelectionPolicy (XmMULTIPLE_SELECT);
1397           current_policy = XmMULTIPLE_SELECT;
1398         }
1399       
1400       // Select it, do not call the select callback. 
1401       SelectPos (position, False);
1402       // Switch the policy back, if it had been changed. 
1403       if (f_selection_policy != current_policy)
1404         SelectionPolicy (f_selection_policy);
1405     }
1406 }
1407
1408 // OutlineListView::item_at(position)
1409 //
1410 // return the OutlineElement that is visible at 'position' in 
1411 // the list view 
1412 //
1413
1414 OutlineElement *
1415 OutlineListView::item_at(unsigned int position)
1416 {
1417   OutlineList *lptr = f_list ;
1418   unsigned int count  = 0 ;
1419   unsigned int offset = 0 ;
1420   
1421   ON_DEBUG(cerr << "item_at: " << position << endl);
1422   
1423   // start at the beginning and descend the nested lists until we
1424   // reach the desired position, and return the element there
1425   
1426   OutlineElement *element = (OutlineElement *) (*lptr)[offset] ;
1427   while (count < position)
1428     {
1429       if (element->is_expanded(f_data_handle) && element->has_children())
1430         {
1431           if (count + element->children()->count_expanded(f_data_handle) >= position)
1432             {
1433               ON_DEBUG(cerr << "descend: " << element->display_as() << endl);
1434               lptr = element->children();
1435               offset = 0 ;
1436             }
1437           else
1438             {
1439               count += element->children()->count_expanded(f_data_handle) ;
1440               offset++ ;
1441             }
1442         }
1443       else
1444         {
1445           offset++ ;                    // go to the next one
1446         }
1447       element = (OutlineElement*) (*lptr)[offset] ;
1448       count++ ;
1449     }
1450   ON_DEBUG(cerr << "item_at ==> " << element->display_as() << endl);
1451   return element ;
1452 }