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