Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtappbuilder / src / ab / brws_utils.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23
24 /*
25  *      $XConsortium: brws_utils.c /main/4 1996/10/02 10:59:00 drk $
26  *
27  *      @(#)brws_utils.c        1.54 29 Mar 1995
28  *
29  *      RESTRICTED CONFIDENTIAL INFORMATION:
30  *      
31  *      The information in this document is subject to special
32  *      restrictions in a confidential disclosure agreement between
33  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
34  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
35  *      Sun's specific written approval.  This document and all copies
36  *      and derivative works thereof must be returned or destroyed at
37  *      Sun's request.
38  *
39  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
40  *
41  */
42
43
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <X11/Xlib.h>
47 #include <Xm/Xm.h>
48 #include <Xm/DrawingA.h>
49 #include <ab_private/util.h>
50 #include <ab_private/obj.h>
51 #include <ab_private/trav.h>
52 #include <ab_private/brwsP.h>
53 #include <ab_private/proj.h>
54 #include <ab_private/x_util.h>
55 #include <ab_private/istr.h>
56
57 static ABBrowser        get_browser_for_win(
58                             AB_OBJ *project, 
59                             Window      w
60                         );
61
62 static void             select_node(
63                             VNode       selected_node
64                         );
65
66 static void             deselect_node(
67                             VNode       selected_node
68                         );
69
70 static void             toggle_select_node(
71                             VNode       selected_node
72                         );
73
74 static int              select_fn(
75                             VNode       node
76                         );
77
78 static int              collapsed_fn(
79                             VNode       module
80                         );
81
82 static void             r_deselect_all_nodes(
83                             VNode       tree,
84                             int         flag
85                         );
86
87 static void             draw_area_snap (
88                             Vwr         v
89                         );
90
91 static Boolean          clipWindowWorkProc(
92                             XtPointer   client_data
93                         );
94
95 static void             clipwindowEventHandler(
96                             Widget      widget,
97                             XtPointer   client_data,
98                             XEvent      *event,
99                             Boolean     *cont_dispatch
100                         );
101
102 static void             clipwindowResizeCB(
103                             Widget      w, 
104                             XtPointer   client_data, 
105                             XtPointer   call_data
106                         );
107
108 static char             *ab_browser_project_name = "*module_name";
109
110 static int
111 select_fn(
112     VNode       vnode
113 )
114 {
115     if (BRWS_NODE_STATE_IS_SET(vnode, BRWS_NODE_SELECTED))
116         return (1);
117                  
118     return (0);
119 }
120
121 static int
122 collapsed_fn(
123     VNode       module
124 )
125 {
126     if (!BRWS_NODE_STATE_IS_SET(module, BRWS_NODE_EXPANDED))
127         return (1);
128
129     return (0);
130 }
131
132
133 static ABBrowser
134 get_browser_for_win(
135     AB_OBJ *project, 
136     Window      w
137 )
138 {
139     ABBrowser   cur_browser;
140     Widget      draw_area;
141     
142     if (!project || !w)
143         return (NULL);
144     
145     cur_browser = (ABBrowser)project->info.project.browsers;
146
147     while (cur_browser)
148     {
149         draw_area = brws_draw_area(cur_browser->module);
150
151         if ( draw_area && (w == XtWindow(draw_area)) )
152             return(cur_browser);
153
154         cur_browser = cur_browser->next;
155     }
156
157     return ((ABBrowser)NULL);
158 }
159
160 BOOL
161 aob_is_browser_win(
162     AB_OBJ *project,
163     Window      w
164 )
165 {
166     return(get_browser_for_win(project, w) != NULL);
167 }
168
169 AB_OBJ  *
170 aob_get_object_from_xy(
171     AB_OBJ *project, 
172     Window      w,
173     int x, 
174     int y
175 )
176 {
177     ABBrowser   cur_browser;
178     VNode       found_node;
179     
180     if (!project || !w)
181         return (NULL);
182     
183     cur_browser = get_browser_for_win(project, w);
184     if (cur_browser != NULL)
185     {
186         found_node = vwr_locate_node(cur_browser->module, x, y);
187
188         if (found_node)
189         {
190             return((AB_OBJ *)found_node->obj_data);
191         }
192     }
193     return (NULL);
194 }
195
196 /*
197  * aob_find_bnode
198  * Searches for the ViewerNode on the AB_OBJ corresponding to the
199  * passed Viewer
200  */
201 ViewerNode      *
202 aob_find_bnode
203 (
204     AB_OBJ      *obj,
205     Viewer      *v
206 )
207 {
208     ViewerNode          *bnodes, 
209                         *found = NULL;
210     ViewerMethods       *m;
211
212     if (!v || !obj)  
213         return (NULL);
214
215     m = v->methods;
216
217     if (!m)
218         return (NULL);
219
220     bnodes = (*m->get_viewer_data)(obj);
221
222     for (found = bnodes; found && (found->browser != v); found = found->next);
223
224     return (found);
225 }
226
227 /*
228  * Returns project from browser object
229  */
230 AB_OBJ  *
231 aob_project_from_browser(
232     Viewer      *b
233 )
234 {
235     return (b ? (AB_OBJ *)b->obj_data : NULL);
236 }
237
238 /*
239  * Returns browser properties
240  */
241 BrowserProperties *
242 aob_browser_properties(
243     Viewer      *b
244 )
245 {
246     return (b ? (BrowserProperties *)b->properties : NULL);
247 }
248
249 /*
250  * Copies the properties of one browser to another
251  */
252 void
253 aob_copy_props(
254     Viewer     *from,
255     Viewer     *to
256 )
257 {
258     BrowserProperties   *from_prop,
259                                 *to_prop;
260     if (!from || !to)
261         return;
262                                                               
263     from_prop = aob_browser_properties(from);
264     to_prop = aob_browser_properties(to);
265     /*
266      * These are the only ones that matter now
267      */
268     to_prop->orientation = from_prop->orientation;
269     to_prop->elements_shown = from_prop->elements_shown;
270 }
271
272 /*
273  * Get the UI handle from a browser
274  */
275 BrowserUiObjects *
276 aob_ui_from_browser
277 (
278     Viewer      *b
279 )
280 {
281     return (b ? (BrowserUiObjects *)b->ui_handle : NULL);
282 }
283
284 /*
285  * Get the shell widget for a browser
286  */
287 Widget
288 aob_ui_shell
289 (
290     Viewer      *b
291 )
292 {
293     BrowserUiObjects    *ui;
294     
295     if (!b)
296         return (NULL);
297
298     ui = aob_ui_from_browser(b);
299
300     return(ui ? ui->shell : NULL);
301 }
302
303 /*
304  * Get the draw area widget for a browser
305  */
306 Widget
307 brws_draw_area
308 (
309     Viewer      *b
310 )
311 {
312     VMethods            m;
313     
314     if (!b)
315         return (NULL);
316
317     m = b->methods;
318
319     return ((Widget)(*m->get_drawarea)(b));
320 }
321
322 /*
323  * Get the elements of a browser node
324  */
325 ViewerNodeElm *
326 aob_bnode_elements(
327     ViewerNode  *bnode
328 )
329 {
330     if (!bnode)
331         return (NULL);
332
333     return ((ViewerNodeElm *)bnode->elements);
334 }
335
336 /*
337  * Get the bit vector which contains info on
338  * what node elements of the bnodes are to be shown
339  */
340 unsigned long
341 browser_get_elm_shown
342 (
343     Viewer      *b
344 )
345 {
346     BrowserProperties   *props;
347
348     props = aob_browser_properties(b);
349
350     return(props ? props->elements_shown : (unsigned long)NULL);
351 }
352
353 /*
354  * Return the count of node elements that are
355  * shown for the browser
356  */
357 int
358 browser_num_elm_shown
359 (
360     Viewer      *b
361 )
362 {
363     int                 i, count = 0;
364     unsigned long       elm_shown;
365
366     elm_shown = browser_get_elm_shown(b);
367
368     for (i = 0; i < BRWS_NUM_ELM; ++i)
369     {
370         count += (elm_shown & 1);
371
372         elm_shown = elm_shown >> 1;
373     }
374
375     return (count);
376 }
377
378
379 /*
380  * Browser node selection
381  */
382 void
383 brws_select
384 (
385     AB_OBJ      *obj
386 )
387 {
388     AB_OBJ      *project;
389     ABBrowser   b_list;
390     AB_OBJ      *root_obj = obj;
391     VNode       selected_nodes;
392     Widget      draw_area;
393     ViewerMethods       *m;
394
395     if (!obj)
396         return;
397
398     /*
399      * Is this necessary ?
400      */
401     root_obj = obj_get_root(obj);
402
403     if (!root_obj)
404         return;
405
406     selected_nodes = (VNode)root_obj->browser_data;
407
408     if (!selected_nodes)
409         return;
410
411     /*
412      * Get project/browser list
413     project = obj_get_project(obj);
414     b_list = (ABBrowser)project->info.project.browsers;
415      */
416
417     while (selected_nodes)
418     {
419         BrowserProps    prop = 
420                 aob_browser_properties(selected_nodes->browser);
421
422         if (prop->active)
423             select_node(selected_nodes);
424
425         selected_nodes = selected_nodes->next;
426     }
427 }
428
429 /*
430  * Browser node de-selection
431  */
432 void
433 brws_deselect
434 (
435     AB_OBJ      *obj
436 )
437 {
438     AB_OBJ      *project;
439     ABBrowser   b_list;
440     AB_OBJ      *root_obj = obj;
441     VNode       selected_nodes;
442     Widget      draw_area;
443     ViewerMethods       *m;
444
445     if (!obj)
446         return;
447
448     /*
449      * Is this necessary ?
450      */
451     root_obj = obj_get_root(obj);
452
453     if (!root_obj)
454         return;
455
456     selected_nodes = (VNode)root_obj->browser_data;
457
458     if (!selected_nodes)
459         return;
460
461     /*
462      * Get project/browser list
463     project = obj_get_project(obj);
464     b_list = (ABBrowser)project->info.project.browsers;
465      */
466
467     while (selected_nodes)
468     {
469         BrowserProps    prop = 
470                 aob_browser_properties(selected_nodes->browser);
471
472         if (prop->active)
473             deselect_node(selected_nodes);
474
475         selected_nodes = selected_nodes->next;
476     }
477 }
478
479 void
480 brws_toggle_select
481 (
482     AB_OBJ *obj
483 )
484 {
485     AB_OBJ      *root_obj = obj;
486     VNode       selected_nodes;
487     Widget      draw_area;
488
489     if (!obj)
490         return;
491
492     root_obj = obj_get_root(obj);
493
494     if (!root_obj)
495         return;
496
497     selected_nodes = (VNode)root_obj->browser_data;
498
499     if (!selected_nodes)
500         return;
501
502     while(selected_nodes)
503     {
504         BrowserProps    prop = 
505                 aob_browser_properties(selected_nodes->browser);
506
507         if (prop->active)
508             toggle_select_node(selected_nodes);
509
510         selected_nodes = selected_nodes->next;
511     }
512 }
513
514 void
515 aob_deselect_all_objects(
516     AB_OBJ      *project
517 )
518 {
519     ABBrowser   browsers;
520
521     if (!project)
522         return;
523
524     browsers = (ABBrowser)project->info.project.browsers;
525
526     while (browsers)
527     {
528         aob_deselect_all_nodes(browsers->module, TRUE);
529         browsers = browsers->next;
530     }
531 }
532
533 static void
534 select_node
535 (
536     VNode selected_node
537 )
538 {
539     ViewerMethods       *m;
540     Viewer              *v;
541     ABObj               obj;
542
543     if (!selected_node)
544         return;
545
546     v = selected_node->browser;
547     m = v->methods;
548
549     BRWS_NODE_SET_STATE(selected_node, BRWS_NODE_SELECTED);
550
551     obj = (AB_OBJ *)selected_node->obj_data;
552
553     if (!brwsP_node_is_collapsed(selected_node))
554         (*m->render_node)(selected_node, TRUE);
555 }
556
557 static void
558 deselect_node
559 (
560     VNode selected_node
561 )
562 {
563     ViewerMethods       *m;
564     Viewer              *v;
565     ABObj               obj;
566
567     if (!selected_node)
568         return;
569
570     v = selected_node->browser;
571     m = v->methods;
572
573     BRWS_NODE_UNSET_STATE(selected_node, BRWS_NODE_SELECTED);
574
575     obj = (AB_OBJ *)selected_node->obj_data;
576
577     if (!brwsP_node_is_collapsed(selected_node))
578         (*m->render_node)(selected_node, FALSE);
579 }
580
581
582 /*
583  * Deselect all the nodes in the tree.
584  * The flag argument is to determine whether immediate refreshing
585  * in needed or not.
586  */
587 void
588 aob_deselect_all_nodes
589 (
590     Viewer      *b,
591     int         flag
592 )
593 {
594     r_deselect_all_nodes(b->current_tree, flag);
595 }
596
597 static void
598 toggle_select_node
599 (
600     VNode selected_node
601 )
602 {
603     AB_OBJ              *obj;
604     Viewer              *b;
605     ViewerMethods       *m;
606     Widget              draw_area;
607
608     if (!selected_node)  
609         return;
610
611     b = selected_node->browser;
612     m = b->methods;
613     obj = (AB_OBJ *)selected_node->obj_data;
614     draw_area = brws_draw_area(b);
615
616     if (BRWS_NODE_STATE_IS_SET(selected_node, BRWS_NODE_SELECTED))
617     {
618         BRWS_NODE_UNSET_STATE(selected_node, BRWS_NODE_SELECTED);
619         (*m->render_node)(selected_node, FALSE);
620     }
621     else
622     {
623         BRWS_NODE_SET_STATE(selected_node, BRWS_NODE_SELECTED);
624         (*m->render_node)(selected_node, TRUE);
625     }
626 }
627
628 /*
629  * Recusively deselect all the nodes in the tree.
630  */
631 static void
632 r_deselect_all_nodes
633 (
634     ViewerNode  *tree,
635     int                 flag
636 )
637 {
638     VMethods    m;
639     VNode       child;
640     VNode       *selected_nodes = NULL;
641     int         i, num_child, num_selected = 0;
642
643     if (!tree)
644         return;
645
646     vwr_get_cond(tree, &selected_nodes, 
647                         &num_selected, select_fn);
648
649     m = BNODE_METHODS(tree);
650
651     for (i=0; i < num_selected; ++i)
652     {
653         /*
654          * Make node state unselected
655          */
656         BRWS_NODE_UNSET_STATE(selected_nodes[i], BRWS_NODE_SELECTED);
657
658         /*
659          * If flag is set, and node is expanded (== not collapsed),
660          * re-render node in unselected state
661          */
662         if (flag && !brwsP_node_is_collapsed(selected_nodes[i]))
663             (*m->render_node)(selected_nodes[i], FALSE);
664     }
665
666     if (selected_nodes)
667         util_free(selected_nodes);
668 }
669
670 void
671 brws_set_module_name
672 (
673     Vwr         b
674 )
675 {
676     BrowserUiObjects *ui;
677     DtbBrwsMainwindowInfo       instance;
678     AB_OBJ      *module_obj = NULL;
679     Widget      shell,
680                 project_label;
681     XmString    xmlabel;
682     char        *module_name = NULL;
683     char        title[300];
684
685     if (!b)
686         return;
687     
688     shell = aob_ui_shell(b);
689
690     if (b->tree)
691         module_obj = (ABObj)b->tree->obj_data;
692
693     if (module_obj)
694     {
695         if (obj_get_file(module_obj) != NULL)
696         {
697             char        *fullpath, *filename;
698
699             fullpath = obj_get_file(module_obj);
700
701             /*
702              * Check return value of strrchr before adding 1 to it
703              */
704             if (filename = strrchr(fullpath, '/'))
705                 module_name = (STRING)strdup(filename + 1);
706             else
707                 module_name = (STRING)strdup(fullpath);
708
709         }
710         else
711         {
712             module_name = util_strsafe(obj_get_name(module_obj));
713         }
714     }
715
716     if (module_name)
717     {
718         xmlabel = XmStringCreateLocalized(module_name);
719
720         sprintf(title, "Module Browser - %s", module_name);
721     }
722     else
723     {
724         xmlabel = XmStringCreateLocalized(" ");
725         sprintf(title, "Module Browser");
726     }
727
728     XtVaSetValues(shell, XtNtitle, title, NULL);
729
730     ui = aob_ui_from_browser(b);
731     instance = (DtbBrwsMainwindowInfo)ui->ip;
732     project_label = instance->module_name;
733
734     XtVaSetValues(project_label, XmNlabelString, xmlabel, NULL);
735
736     XmStringFree(xmlabel);
737
738 }
739
740 /*
741  * aob_preview
742  * Parameters:
743  *      obj             - which object to preview in the browser
744  *      browser_window  - which browser window to do the priviewing in
745  *      
746  * Assumes:
747  *      that x_conn_fullscreen_init() was called before this function 
748  *      is called and x_conn_fullscreen_cleanup() will be called after
749  *      this function is called.
750  */
751 extern void
752 aob_preview(
753     ABObj       obj,
754     Window      browser_window
755 )
756 {
757 #define         AB_BROWSER_PREVIEW_MARGIN       2
758     Vwr         b;
759     VNode       bnode;
760     Display     *dpy;
761     Widget      draw_area;
762     Window      root, child_win;
763     int         root_x, root_y;
764
765     if (!obj || !browser_window)
766         return;
767
768     bnode = (VNode)obj->browser_data;
769     b = bnode->browser;
770     draw_area = brws_draw_area(b);
771
772     /*
773      * Search for the relevant bnode
774      * - there may be more than one browser visible
775      */
776     while(bnode && (browser_window != XtWindow(draw_area)))
777     {
778         bnode = bnode->next;
779         if (bnode)
780         {
781             b = bnode->browser;
782             draw_area = brws_draw_area(b);
783         }
784     }
785
786     if (!bnode)
787         return;
788
789     dpy = XtDisplay(draw_area);
790     root = RootWindow(dpy, DefaultScreen(dpy));
791
792     XTranslateCoordinates(dpy, browser_window, root,
793                     bnode->x, bnode->y, &root_x, &root_y, &child_win);
794     
795     root_x -= AB_BROWSER_PREVIEW_MARGIN;
796     root_y -= AB_BROWSER_PREVIEW_MARGIN;
797
798     x_fullscreen_preview_box(draw_area, root, 
799                 root_x, 
800                 root_y,
801                 root_x + bnode->width + 
802                     AB_BROWSER_PREVIEW_MARGIN + 
803                     AB_BROWSER_PREVIEW_MARGIN,
804                 root_y + bnode->height + 
805                     AB_BROWSER_PREVIEW_MARGIN + 
806                     AB_BROWSER_PREVIEW_MARGIN);
807     
808 #undef AB_BROWSER_PREVIEW_MARGIN
809 }
810
811 void
812 brws_update_node(
813     ABObj       obj
814 )
815 {
816     Vwr         v;
817     VNode       update_nodes = NULL;
818     VMethods    *m;
819
820     if (!obj)
821         return;
822     
823
824     update_nodes = (VNode)obj->browser_data;
825
826     while (update_nodes)
827     {
828         vwr_init_elements(update_nodes);
829
830         v = update_nodes->browser;
831
832         if (obj_is_module(obj))
833         {
834             brws_set_module_name(v);
835         }
836
837         erase_viewer(v);
838         draw_viewer(v);
839
840         update_nodes = update_nodes->next;
841     }
842 }
843
844 /*
845  * brws_get_browser_for_obj()
846  * Returns the browser for the passed obj.
847  * The browser list is searched for the module that contains the passed
848  * object. If none is found, a new browser is created, populated with
849  * the module and it is returned.
850  *
851  * If obj is NULL, an empty browser is searched for.
852  */
853 ABBrowser
854 brws_get_browser_for_obj(
855     ABObj       obj
856 )
857 {
858     ABObj       module = NULL,
859                 proj = NULL;
860     ABBrowser   cur_b = NULL,
861                 b_list = NULL;
862     ABObj       project = proj_get_project();
863
864     if (obj)
865     {
866         module = obj_get_module(obj);
867         proj = obj_get_project(obj);
868         b_list = proj ? (ABBrowser)proj->info.project.browsers : NULL;
869     }
870     else
871     {
872         b_list = project ? (ABBrowser)project->info.project.browsers : NULL;
873     }
874
875     for (cur_b = b_list; cur_b; cur_b = cur_b->next)
876     {
877         if (cur_b->project->tree &&
878                 ((AB_OBJ *)cur_b->project->tree->obj_data == module))
879             break;
880     }
881
882     if (!cur_b)
883     {
884         cur_b = brws_create();
885         cur_b->next = b_list;
886         if (b_list)
887             b_list->previous = cur_b;
888
889         if (project)
890             project->info.project.browsers = cur_b;
891
892         if (module)
893             brws_add_objects_to_browser(cur_b, module);
894     }
895
896     return(cur_b);
897 }
898
899 /*
900  * brws_get_browser_shell_for_obj()
901  * Returns the browser shell for the passed obj.
902  * The browser list is searched for the module that contains the passed
903  * object. If found, its shell is returned.
904  */
905 Widget
906 brws_get_browser_shell_for_obj(
907     ABObj       obj
908 )
909 {
910     ABObj       module = NULL,
911                 proj = NULL;
912     ABBrowser   cur_b = NULL,
913                 b_list = NULL;
914     ABObj       project = proj_get_project();
915
916     if (!obj)
917         return NULL;
918     
919     module = obj_get_module(obj);
920     proj = obj_get_project(obj);
921     b_list = proj ? (ABBrowser)proj->info.project.browsers : NULL;
922
923     for (cur_b = b_list; cur_b; cur_b = cur_b->next)
924     {
925         if (cur_b->project->tree &&
926                 ((AB_OBJ *)cur_b->project->tree->obj_data == module))
927             break;
928     }
929
930     if (!cur_b)
931         return NULL;
932     
933     return(aob_ui_shell(cur_b->module));
934 }
935
936 int
937 brwsP_select_fn(
938     VNode       vnode
939 )
940 {
941     if (BRWS_NODE_STATE_IS_SET(vnode, BRWS_NODE_SELECTED))
942         return (1);
943                  
944     return (0);
945 }
946
947 /*
948  * Synch up the toplevel and detailed view of browser
949  * The browser consists of the toplevel view and the
950  * detailed view. The toplevel view is used to control
951  * what is displayed in the detailed view. Selecting a node
952  * in the toplevel view (a basewindow for example), will
953  * show the node and it's children in the detailed view.
954  *
955  * If the 'select_at_least_one' flag is TRUE, and
956  * there are no nodes selected in the toplevel view, 
957  * the first node will be selected, making the corresponding
958  * node in the detailed view visible.
959  * If the 'select_at_least_one' is FALSE, no special action 
960  * is taken.
961  */
962 void
963 brwsP_sync_views(
964     ABBrowser   ab,
965     short       select_at_least_one
966 )
967 {
968     Vwr         toplevel,
969                 detailed;
970     VNode       toplevel_child,
971                 detailed_child;
972     VMethods    m;
973     int         num_child,
974                 num_selected = 0,
975                 i;
976
977     if (!ab)
978         return;
979
980     /*
981      * Get toplevel/detailed view
982      */
983     toplevel = ab->project;
984     detailed = ab->module;
985
986     if (!toplevel || !detailed)
987         return;
988
989     m = toplevel->methods;
990
991     /*
992      * Any nodes currently selected in toplevel view ?
993      */
994     vwr_num_cond(toplevel->current_tree, &num_selected, brwsP_select_fn);
995
996     /*
997      * Get child count of toplevel view
998      */
999     num_child = (*m->get_num_children)(toplevel->current_tree);
1000
1001     /*
1002      * For every node in toplevel view
1003      */
1004     for (i=0, toplevel_child = (*m->get_child)(toplevel->current_tree, 0); 
1005             (i < num_child); 
1006             toplevel_child = (*m->get_child)(toplevel->current_tree, ++i))
1007     {
1008         ABObj   cur_obj;
1009
1010         if (!toplevel_child)
1011             continue;
1012
1013         /*
1014          * Get ABObj of current toplevel child node
1015          */
1016         cur_obj = (ABObj)toplevel_child->obj_data;
1017
1018         /*
1019          * From the ABObj, get the corresponding node in detailed 
1020          * view
1021          */
1022         detailed_child = aob_find_bnode(cur_obj, detailed);
1023
1024         if (!detailed_child)
1025             continue;
1026
1027         if (BRWS_NODE_STATE_IS_SET(toplevel_child, BRWS_NODE_SELECTED))
1028         {
1029             /*
1030             * Node in toplevel view is selected.
1031             * Make node in detailed view visible.
1032             */
1033             BRWS_NODE_SET_STATE(detailed_child, BRWS_NODE_VISIBLE);
1034         }
1035         else
1036         {
1037             /*
1038              * Node in toplevel view is not selected.
1039              *
1040              * If 'select_at_least_one' flag set...
1041              * Check first if there are no nodes selected.
1042              * If there are none, make the first non selected node
1043              * in the toplevel view selected and make the corresponding 
1044              * node in the detailed view visible.
1045              */
1046             if (select_at_least_one && !num_selected)
1047             {
1048                 BRWS_NODE_SET_STATE(toplevel_child, BRWS_NODE_SELECTED);
1049                 BRWS_NODE_SET_STATE(detailed_child, BRWS_NODE_VISIBLE);
1050                 ++num_selected;
1051             }
1052             else
1053             {
1054                 /*
1055                  * Make node in detailed view not visible
1056                  */
1057                 BRWS_NODE_UNSET_STATE(detailed_child, BRWS_NODE_VISIBLE);
1058             }
1059         }
1060     }
1061
1062 }
1063
1064
1065 /*
1066  * brwsP_node_is_collapsed()
1067  * Returns TRUE if node is collapsed, FALSE otherwise.
1068  * Note: on error conditions, TRUE is returned.
1069  */
1070 int
1071 brwsP_node_is_collapsed
1072 (
1073     VNode       node
1074 )
1075 {
1076     VNode       parent;
1077     VMethods    m;
1078     Vwr         v;
1079             ABObj       obj;
1080
1081     if (!node)
1082         return (TRUE);
1083     
1084     obj = (AB_OBJ *)node->obj_data;
1085     /*
1086     fprintf(stderr, "brwsP_node_is_collapsed(%s), viewer = %p\n", 
1087         obj_get_name(obj),
1088         node->browser);
1089     */
1090
1091     if (!(v = node->browser))
1092         return (TRUE);
1093
1094     if (!(m = v->methods))
1095         return (TRUE);
1096
1097     while (parent = (*m->get_parent)(node))
1098     {
1099         if (!BRWS_NODE_STATE_IS_SET(parent, BRWS_NODE_EXPANDED))
1100         {
1101
1102             obj = (AB_OBJ *)parent->obj_data;
1103
1104             /*
1105             fprintf(stderr, "parent node %s is collapsed\n", obj_get_name(obj));
1106             */
1107             return (TRUE);
1108         }
1109
1110         node = parent;
1111     }
1112
1113     return (FALSE);
1114 }
1115
1116 /*
1117  * brwsP_node_is_visible()
1118  * Returns TRUE if node is visible, FALSE otherwise.
1119  * Note: on error conditions, TRUE is returned.
1120  */
1121 int
1122 brwsP_node_is_visible
1123 (
1124     VNode       node
1125 )
1126 {
1127     VNode       parent;
1128     VMethods    m;
1129     Vwr         v;
1130             ABObj       obj;
1131
1132     if (!node)
1133         return (FALSE);
1134     
1135     if (!BRWS_NODE_STATE_IS_SET(node, BRWS_NODE_VISIBLE))
1136         return (FALSE);
1137
1138     obj = (AB_OBJ *)node->obj_data;
1139     /*
1140     fprintf(stderr, "brwsP_node_is_visible(%s), viewer = %p\n", 
1141         obj_get_name(obj),
1142         node->browser);
1143     */
1144
1145     if (!(v = node->browser))
1146         return (TRUE);
1147
1148     if (!(m = v->methods))
1149         return (TRUE);
1150
1151     while (parent = (*m->get_parent)(node))
1152     {
1153         if (!BRWS_NODE_STATE_IS_SET(parent, BRWS_NODE_VISIBLE))
1154         {
1155
1156             obj = (AB_OBJ *)parent->obj_data;
1157
1158             /*
1159             fprintf(stderr, "parent node %s is not visible\n", obj_get_name(obj));
1160             */
1161             return (FALSE);
1162         }
1163
1164         node = parent;
1165     }
1166
1167     return (TRUE);
1168 }
1169
1170 /*
1171  * brwsP_make_drawarea_snap()
1172  * Make the passed draw area 'snap' to be the size of it's parent
1173  * whenever it's parent is resized.
1174  * This function tries to take advantage of the fact that the parent 
1175  * in most cases is the clipwindow of a scrolled window which is 
1176  * also a draw area widget. In this case, a resize callback is 
1177  * registered. In other cases, an event handler is registered.
1178  */
1179 void
1180 brwsP_make_drawarea_snap(
1181     Vwr         v,
1182     Widget      draw_area
1183 )
1184 {
1185     Widget      clipwindow;
1186     
1187     if (!v || !draw_area)
1188         return;
1189     
1190     clipwindow = XtParent(draw_area);
1191
1192     if (XmIsDrawingArea(clipwindow))  {
1193         XtAddCallback(clipwindow, XmNresizeCallback, 
1194                         clipwindowResizeCB, (XtPointer)v);
1195     }
1196     else
1197     {
1198         XtAddEventHandler(clipwindow,
1199                 StructureNotifyMask, False,
1200                 clipwindowEventHandler, (XtPointer)v);
1201     }
1202 }
1203
1204 /*
1205  * Resize callback for parent of draw area
1206  */
1207 static void
1208 clipwindowResizeCB(
1209     Widget      w, 
1210     XtPointer   client_data, 
1211     XtPointer   call_data
1212 )
1213 {
1214     /* 
1215      * Defer munging until we're out of the ScrolledWindow Resize callback 
1216      */
1217     XtAppAddWorkProc(XtWidgetToApplicationContext(w), clipWindowWorkProc, client_data);
1218 }
1219
1220 /*
1221  * Event handler for parent of draw area
1222  */
1223 static void
1224 clipwindowEventHandler(
1225     Widget      widget,
1226     XtPointer   client_data,
1227     XEvent      *event,
1228     Boolean     *cont_dispatch
1229 )
1230 {
1231     if (event->type != ConfigureNotify)
1232         return;
1233
1234     /* 
1235      * Defer munging until we're out of the ScrolledWindow Event Handler
1236      */
1237     XtAppAddWorkProc(XtWidgetToApplicationContext(widget), 
1238                 clipWindowWorkProc, client_data);
1239 }
1240
1241
1242 /*
1243  * Work proc for clip window
1244  * - make draw area snap to clip window's size.
1245  */
1246 static Boolean
1247 clipWindowWorkProc(
1248     XtPointer client_data
1249 )
1250 {
1251     draw_area_snap((Vwr)client_data);
1252     return(True);
1253 }
1254
1255
1256 /*
1257  * draw_area_snap()
1258  * Make size of draw area snap to size of clipwindow.
1259  * The clipwindow is the direct parent of the draw area.
1260  * The preferred/minimum size of the draw area is stored in
1261  * the viewer properties. This is the size needed to render
1262  * whatever graphics is needed.
1263  *
1264  * Logic used:
1265  *      if clipwindow_width > min_width
1266  *          make draw area width == clip window width
1267  *      else
1268  *          if draw area width > minimum width (i.e. preferred width)
1269  *              make draw area width == minimum width
1270  *
1271  * Same logic used for height
1272  */
1273 static void
1274 draw_area_snap (
1275     Vwr         v
1276 )
1277 {
1278     Widget              clipwindow = NULL,
1279                         draw_area = NULL;
1280     Dimension           cw_width = 0,
1281                         cw_height = 0,
1282                         da_borderWidth = 0,
1283                         da_width = 0,
1284                         da_height = 0;
1285     BrowserProps        props = NULL;
1286     Arg                 arg[4];
1287     int                 num_args = 0;
1288
1289     if (!v)
1290         return;
1291
1292     /*
1293      * Get viewer properties
1294      */
1295     if (!(props = aob_browser_properties(v)))
1296         return;
1297
1298     /*
1299      * Get draw area and clip window
1300      */
1301     if (!(draw_area = brws_draw_area(v)))
1302         return;
1303
1304     if (!(clipwindow = XtParent(draw_area)))
1305         return;
1306
1307     /*
1308      * Get clipwindow width/height, draw area
1309      * width/height/border width
1310      */
1311     XtVaGetValues(clipwindow, 
1312                 XmNwidth, &cw_width, 
1313                 XmNheight, &cw_height, 
1314                 NULL);
1315     XtVaGetValues(draw_area, 
1316                 XmNborderWidth, &da_borderWidth, 
1317                 XmNwidth, &da_width, 
1318                 XmNheight, &da_height, 
1319                 NULL);
1320
1321     if ((Dimension)cw_width > props->min_width)
1322     {
1323         /*
1324          * Clip window width is more than minimum width
1325          * -> expand draw area
1326          * The dimension that we actually use must be offset by the
1327          * border width to ensure a perfect fit in the clip window. 
1328          * Otherwise, this will trigger the scrollbar in the clipwindow.
1329          */
1330         if (cw_width > 2u * da_borderWidth) {
1331             cw_width -= 2u * da_borderWidth;
1332
1333             if (cw_width > 2) 
1334             {
1335                 XtSetArg(arg[num_args], XmNwidth, (cw_width - 2));
1336                 num_args++;
1337             }
1338         }
1339     }
1340     else
1341     {
1342         /*
1343          * Clip window width is less than minimum width
1344          * -> check if draw area width is more than it's minimum.
1345          * If yes, shrink it back down to the minimum width.
1346          */
1347         if ((Dimension)da_width > props->min_width)
1348         {
1349             XtSetArg(arg[num_args], XmNwidth, props->min_width);
1350             num_args++;
1351         }
1352     }
1353
1354
1355     if ((Dimension)cw_height > props->min_height)
1356     {
1357         /*
1358          * Clip window height is more than minimum height
1359          * -> expand draw area
1360          * The dimension that we actually use must be offset by the
1361          * border width to ensure a perfect fit in the clip window. 
1362          * Otherwise, this will trigger the scrollbar in the clipwindow.
1363          */
1364         if (cw_height > 2u * da_borderWidth) {
1365             cw_height -= 2u * da_borderWidth;
1366
1367             if (cw_height > 2) 
1368             {
1369                 XtSetArg(arg[num_args], XmNheight, (cw_height - 2));
1370                 num_args++;
1371             }
1372         }
1373     }
1374     else
1375     {
1376         /*
1377          * Clip window height is less than minimum height
1378          * -> check if draw area height is more than it's minimum.
1379          * If yes, shrink it back down to the minimum height.
1380          */
1381         if ((Dimension)da_height > props->min_height)
1382         {
1383             XtSetArg(arg[num_args], XmNheight, props->min_height);
1384             num_args++;
1385         }
1386     }
1387     
1388     if (num_args > 0)
1389         XtSetValues(draw_area, arg, num_args);
1390 }
1391
1392 void 
1393 brwsP_collapse_selected(
1394     ABBrowser   ab
1395 )
1396 {
1397     Vwr         v = NULL;
1398     VNode       *selected_nodes = NULL;
1399     VMethods    m;
1400     int         num_selected = 0,
1401                 i;
1402     short       redraw = FALSE;
1403
1404
1405     if (!ab)
1406         return;
1407     
1408     if (!(v = ab->module))
1409         return;
1410
1411     if (!(m = v->methods))
1412         return;
1413     
1414     /*
1415      * Get nodes that are selected
1416      */
1417     vwr_get_cond(v->current_tree, &selected_nodes, 
1418                         &num_selected, select_fn);
1419     
1420     /*
1421      * Return if no selected nodes
1422      */
1423     if (num_selected == 0)
1424         return;
1425
1426     for (i=0; i < num_selected; ++i)
1427     {
1428         /*
1429          * For each selected node, check if they are expanded.
1430          * If they are, mark them as collapsed.
1431          */
1432         if (BRWS_NODE_STATE_IS_SET(selected_nodes[i], BRWS_NODE_EXPANDED))
1433         {
1434             BRWS_NODE_UNSET_STATE(selected_nodes[i], BRWS_NODE_EXPANDED);
1435             redraw = TRUE;
1436         }
1437     }
1438
1439     if (redraw)
1440     {
1441         erase_viewer(v);
1442         draw_viewer(v);
1443     }
1444
1445     /*
1446      * Free up node list if it contained anything
1447      */
1448     if (selected_nodes)
1449         util_free(selected_nodes);
1450 }
1451
1452
1453 void 
1454 brwsP_expand_selected(
1455     ABBrowser   ab
1456 )
1457 {
1458     Vwr         v = NULL;
1459     VNode       *selected_nodes = NULL;
1460     VMethods    m;
1461     int         num_selected = 0,
1462                 i;
1463     short       redraw = FALSE;
1464
1465     if (!ab)
1466         return;
1467     
1468     if (!(v = ab->module))
1469         return;
1470
1471     if (!(m = v->methods))
1472         return;
1473     
1474     /*
1475      * Get nodes that are selected
1476      */
1477     vwr_get_cond(v->current_tree, &selected_nodes, 
1478                         &num_selected, select_fn);
1479     
1480     /*
1481      * Return if no selected nodes
1482      */
1483     if (num_selected == 0)
1484         return;
1485
1486     for (i=0; i < num_selected; ++i)
1487     {
1488         /*
1489          * For each selected node, check if they are collapsed.
1490          * If they are, mark them as expanded.
1491          */
1492         if (!BRWS_NODE_STATE_IS_SET(selected_nodes[i], BRWS_NODE_EXPANDED))
1493         {
1494             BRWS_NODE_SET_STATE(selected_nodes[i], BRWS_NODE_EXPANDED);
1495             redraw = TRUE;
1496         }
1497     }
1498
1499     if (redraw)
1500     {
1501         erase_viewer(v);
1502         draw_viewer(v);
1503     }
1504
1505     /*
1506      * Free up node list if it contained anything
1507      */
1508     if (selected_nodes)
1509         util_free(selected_nodes);
1510 }
1511
1512
1513 void 
1514 brwsP_expand_collapsed(
1515     ABBrowser   ab
1516 )
1517 {
1518     Vwr         v = NULL;
1519     VNode       *collapsed_nodes = NULL;
1520     VMethods    m;
1521     int         num_collapsed = 0,
1522                 i;
1523     short       redraw = FALSE;
1524
1525     if (!ab)
1526         return;
1527     
1528     if (!(v = ab->module))
1529         return;
1530
1531     if (!(m = v->methods))
1532         return;
1533     
1534     /*
1535      * Get nodes that are collapsed
1536      */
1537     vwr_get_cond(v->current_tree, &collapsed_nodes, 
1538                         &num_collapsed, collapsed_fn);
1539     
1540     /*
1541      * Return if no collapsed nodes
1542      */
1543     if (num_collapsed == 0)
1544         return;
1545
1546     for (i=0; i < num_collapsed; ++i)
1547     {
1548         /*
1549          * For each collapsed node, mark them as expanded only if
1550          * they are visible.
1551          */
1552         if (brwsP_node_is_visible(collapsed_nodes[i]))
1553         {
1554             BRWS_NODE_SET_STATE(collapsed_nodes[i], BRWS_NODE_EXPANDED);
1555             redraw = TRUE;
1556         }
1557     }
1558
1559     if (redraw)
1560     {
1561         erase_viewer(v);
1562         draw_viewer(v);
1563     }
1564
1565     /*
1566      * Free up node list if it contained anything
1567      */
1568     if (collapsed_nodes)
1569         util_free(collapsed_nodes);
1570 }
1571
1572
1573 void 
1574 brwsP_tear_off_selected(
1575     ABBrowser           b
1576 )
1577 {
1578     ABBrowser           new_browser, b_list;
1579     BrowserProps        props, new_props;
1580     AB_OBJ              *project, *sel_obj;
1581     VNode               selected;
1582     ViewerMethods       *m;
1583
1584     if (!b || !b->module)
1585         return;
1586
1587     project = (AB_OBJ *)b->module->obj_data;
1588
1589     if (!project)
1590         return;
1591
1592     new_browser = brws_create();
1593
1594
1595     /*
1596      * Link this new browser with the rest
1597      */
1598     b_list = (ABBrowser)project->info.project.browsers;
1599     new_browser->next = b_list;
1600     if (b_list)
1601         b_list->previous = new_browser;
1602
1603     /*
1604      * Make this new browser the first one on the list
1605      */
1606     project->info.project.browsers = new_browser;
1607
1608     /*
1609      * Add VNodes to the viewers of this browser
1610      */
1611     m = new_browser->project->methods;
1612     (*m->insert_tree)(new_browser->project, b->project->tree->obj_data);
1613
1614     m = new_browser->module->methods;
1615     (*m->insert_tree)(new_browser->module, b->module->tree->obj_data);
1616
1617     brwsP_sync_views(new_browser, TRUE);
1618
1619     /*
1620      * Code to make the root of the new browser the
1621      * current selected node in the current browser
1622     selected = node_selected(b->current_tree);
1623     sel_obj = (AB_OBJ *)selected->obj_data;
1624
1625     new_browser->current_tree = aob_find_bnode(sel_obj, new_browser);
1626     */
1627
1628     aob_copy_props(b->project, new_browser->project);
1629     aob_copy_props(b->module, new_browser->module);
1630
1631     recompute_viewer(new_browser->project);
1632     recompute_viewer(new_browser->module);
1633
1634     brws_popup(new_browser);
1635 }
1636
1637 void
1638 brws_center_on_obj(
1639     ABBrowser   ab,
1640     ABObj       obj
1641 )
1642 {
1643     Vwr                 top_level_view = NULL;
1644     Vwr                 detailed_view = NULL;
1645
1646     if (!ab || !obj)
1647         return;
1648
1649     /*
1650      * Get top level and detailed tree view
1651      */
1652     top_level_view = ab->project;
1653     detailed_view = ab->module;
1654
1655     if (top_level_view && detailed_view)
1656     {
1657         BrowserUiObj            ui;
1658         DtbBrwsMainwindowInfo   instance;
1659         VNode           detailed_node;
1660         Widget          vert_sb = (Widget)NULL;
1661         Widget          horiz_sb = (Widget)NULL;
1662         int             x, y, value, size, increment, page, 
1663                         vert_max, horiz_max,
1664                         vert_min, horiz_min;
1665         Dimension       scroll_width, scroll_height;
1666         BOOL            redraw_needed = FALSE;
1667
1668
1669         /*
1670          * Get node in detailed view to center on
1671          */
1672         detailed_node = aob_find_bnode(obj, detailed_view);
1673
1674         if (!detailed_node)
1675             return;
1676
1677         /*
1678          * If the node in the detailed view is not visible,
1679          * make it visible first before proceeding.
1680          * This is done by selecting the relevant node in the 
1681          * top level view and calling brwsP_sync_views().
1682          */
1683         if (!brwsP_node_is_visible(detailed_node))
1684         {
1685             AB_TRAVERSAL        trav;
1686             VNode               top_level_node;
1687             ABObj               module,
1688                                 top_level_obj;
1689             ViewerMethods       *m;
1690
1691             module = obj_get_module(obj);
1692
1693             /*
1694              * Look for direct child of module which is the
1695              * ancestor of the centered object/node
1696              */
1697             for (trav_open(&trav, module, AB_TRAV_CHILDREN); 
1698                 (top_level_obj = trav_next(&trav)) != NULL; )  {
1699                 
1700                 if (obj_is_descendant_of(obj, top_level_obj) ||
1701                    (obj == top_level_obj))
1702                     break;
1703             }
1704             trav_close(&trav);
1705
1706             if (!top_level_obj)
1707                 return;
1708
1709             /*
1710              * Found top level ABObj - direct child of module which is
1711              * the ancestor of 'obj'.
1712              *
1713              * Get the corresponding node in the top level view
1714              */
1715             top_level_node = aob_find_bnode(top_level_obj, top_level_view);
1716
1717             if (!top_level_node)
1718                 return;
1719
1720             /*
1721              * Select the top level node 
1722              */
1723             BRWS_NODE_SET_STATE(top_level_node, BRWS_NODE_SELECTED);
1724
1725             m = top_level_view->methods;
1726
1727             if (!m)
1728                 return;
1729
1730             /*
1731              * Render it selected
1732              */
1733             (*m->render_node)(top_level_node, TRUE);
1734
1735             /*
1736              * Sync up the top level and detailed tree views
1737              * After this the centered node will be 'visible'
1738              */
1739             brwsP_sync_views(ab, FALSE);
1740
1741             /*
1742              * Erase and recompute views
1743              */
1744             erase_viewer(detailed_view);
1745             recompute_viewer(detailed_view);
1746             redraw_needed = TRUE;
1747         }
1748
1749         ui = aob_ui_from_browser(detailed_view);
1750         instance = (DtbBrwsMainwindowInfo)ui->ip;
1751
1752         if (!instance->detailed_drawarea)
1753             return;
1754
1755         /*
1756          * Get vertical/horizontal scrollbars of scrolled window
1757          */
1758         XtVaGetValues(instance->detailed_drawarea_scrolledwin,
1759                         XmNverticalScrollBar, &vert_sb,
1760                         XmNhorizontalScrollBar, &horiz_sb,
1761                         XmNwidth, &scroll_width,
1762                         XmNheight, &scroll_height,
1763                         NULL);
1764
1765         /*
1766          * If the scrolled area is smaller than the node, use the
1767          * node's size as the scroll area - to prevent getting
1768          * negative numbers in our calculations later on.
1769          * BUG - am I casting properly here?
1770          */
1771         if (scroll_width < (Dimension)detailed_node->width)
1772             scroll_width = (Dimension)detailed_node->width;
1773         if (scroll_height < (Dimension)detailed_node->height)
1774             scroll_height = (Dimension)detailed_node->height;
1775
1776         /*
1777          **********************************************************
1778          * Center browser node to the middle of the scrolled window
1779          **********************************************************
1780          */
1781
1782         /*
1783          * Adjust vertical scrollbar
1784          */
1785
1786
1787         if (vert_sb)
1788             {
1789             /*
1790              * Get current scrollbar values
1791              */
1792             XtVaGetValues(vert_sb, 
1793                 XmNminimum, &vert_min, 
1794                 XmNmaximum, &vert_max, 
1795                 NULL);
1796             XmScrollBarGetValues(vert_sb, &value, &size, &increment, &page);
1797
1798             /*
1799              * Don't adjust if for some reason if the node is off the browser
1800              */
1801             if (detailed_node->y < vert_max)
1802             {
1803                 /*
1804                  * This adjusts the y position of the scrollbar so that
1805                  * the node is centered
1806                  */
1807                 y = detailed_node->y - ((int)scroll_height - detailed_node->height)/2;
1808
1809                 /*
1810                  * Check for illegal y value:
1811                  * Must make sure the y position does not exceed 
1812                  * (XmNmaximum - XmNsliderSize)
1813                  */
1814                 if (vert_max - y < size)
1815                     y = vert_max - size;
1816
1817                 if (y < vert_min)
1818                     y = vert_min;
1819
1820                 /*
1821                  * Set the scrollbar with new 'centered' y position
1822                  */
1823                 XmScrollBarSetValues(vert_sb, (int)y, size, increment, page, True);
1824             }
1825         }
1826
1827         /*
1828          * Adjust horizontal scrollbar
1829          */
1830
1831         if (horiz_sb)
1832         {
1833             /*
1834              * Get current scrollbar values
1835              */
1836             XtVaGetValues(horiz_sb, 
1837                 XmNminimum, &horiz_min, 
1838                 XmNmaximum, &horiz_max, 
1839                 NULL);
1840             XmScrollBarGetValues(horiz_sb, &value, &size, &increment, &page);
1841
1842             /*
1843              * Don't adjust if for some reason if the node is off the browser
1844              */
1845             if (detailed_node->x < horiz_max)
1846             {
1847                 /*
1848                  * This adjusts the y position of the scrollbar so that
1849                  * the node is centered
1850                  */
1851                 x = detailed_node->x - ((int)scroll_width - detailed_node->width)/2;
1852
1853                 /*
1854                  * Check for illegal y value:
1855                  * Must make sure the y position does not exceed 
1856                  * (XmNmaximum - XmNsliderSize)
1857                  */
1858                 if (horiz_max - x < size)
1859                     x = horiz_max - size;
1860
1861                 if (x < horiz_min)
1862                     x = horiz_min;
1863
1864                 /*
1865                  * Set the scrollbar with new 'centered' y position
1866                  */
1867                 XmScrollBarSetValues(horiz_sb, (int)x, size, increment, page, True);
1868             }
1869         }
1870
1871         /*
1872          * Redraw if needed
1873          */
1874         if (redraw_needed)
1875             draw_viewer(detailed_view);
1876     }
1877 }