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