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