02df2db9a334e2b0d001e9111670da709448d4d0
[oweals/cde.git] / cde / programs / dtinfo / dtinfo / src / Query / QueryEditor.C
1 /*      Copyright (c) 1994,1995,1996 FUJITSU LIMITED    */
2 /*      All Rights Reserved                             */
3
4 /*
5  * $XConsortium: QueryEditor.C /main/14 1996/10/11 17:11:51 cde-hal $
6  *
7  * Copyright (c) 1992 HAL Computer Systems International, Ltd.
8  * All rights reserved.  Unpublished -- rights reserved under
9  * the Copyright Laws of the United States.  USE OF A COPYRIGHT
10  * NOTICE IS PRECAUTIONARY ONLY AND DOES NOT IMPLY PUBLICATION
11  * OR DISCLOSURE.
12  * 
13  * THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION AND TRADE
14  * SECRETS OF HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.  USE,
15  * DISCLOSURE, OR REPRODUCTION IS PROHIBITED WITHOUT THE
16  * PRIOR EXPRESS WRITTEN PERMISSION OF HAL COMPUTER SYSTEMS
17  * INTERNATIONAL, LTD.
18  * 
19  *                         RESTRICTED RIGHTS LEGEND
20  * Use, duplication, or disclosure by the Government is subject
21  * to the restrictions as set forth in subparagraph (c)(l)(ii)
22  * of the Rights in Technical Data and Computer Software clause
23  * at DFARS 252.227-7013.
24  *
25  *          HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.
26  *                  1315 Dell Avenue
27  *                  Campbell, CA  95008
28  * 
29  */
30
31 #define C_TOC_Element
32 #define L_Basic
33
34 #define C_WindowSystem
35 #define L_Other
36
37 #define C_QueryEditor
38 #define C_QueryGroup
39 #define C_QueryTerm
40 #define C_QueryGroupView
41 #define L_Query
42
43 #define C_HelpAgent
44 #define L_Agents
45
46 #define C_SearchMgr
47 #define C_SearchScopeMgr
48 #define C_MessageMgr
49 #define L_Managers
50
51 #include "Other/XmStringLocalized.hh"
52 #include "Managers/CatMgr.hh"
53
54 #include "Prelude.h"
55
56 #include "Registration.hh"
57
58 #include <WWL/WXmMenu.h>
59 #include <WWL/WXmForm.h>
60 #include <WWL/WXmLabel.h>
61 #include <WWL/WXmCascadeButton.h>
62 #include <WWL/WXmSeparator.h>
63 #include <WWL/WXmPanedWindow.h>
64
65 #include <string.h>
66 #include <iostream.h>
67
68 #define CLASS QueryEditor
69 #include "../Agents/create_macros.hh"
70
71 //static bool g_allow_query_text_change;
72   
73 QueryEditor *QueryEditor::f_query_editor;
74
75 // /////////////////////////////////////////////////////////////////
76 // class constructor
77 // /////////////////////////////////////////////////////////////////
78
79 QueryEditor::QueryEditor(UAS_SearchEngine& search_engine)
80 : f_shell (NULL),
81   f_query (NULL),
82   f_query_view (NULL),
83   f_null_terms (0)
84 {
85   f_query_editor = this;
86
87   // NOTE: QueryTerm::f_caps is global. It needs to be term-wise
88   //       to get terms to have varied capabilities.
89   QueryTerm::avail_caps(search_engine.avail_caps());
90
91 #ifdef DEBUG
92   fprintf(stderr, "(DEBUG) search engine capabilities = 0x%x\n",
93                                                 QueryTerm::avail_caps());
94 #endif
95 }
96
97
98 // /////////////////////////////////////////////////////////////////
99 // display - display the query editor
100 // /////////////////////////////////////////////////////////////////
101
102 void
103 QueryEditor::display()
104 {
105   static int count = 0;
106   if (count++ == 0) {
107     if (f_shell == NULL)
108       create_ui();
109
110     if (f_query == NULL)
111       edit_query (NULL);
112
113     f_shell.Popup();
114     f_shell.DeIconify();
115   }
116   count--;
117 }
118
119
120 // /////////////////////////////////////////////////////////////////
121 // new_query - set up interface for a new query
122 // /////////////////////////////////////////////////////////////////
123
124 void
125 QueryEditor::edit_query (QueryGroup *query)
126 {
127   static Boolean first_time = True;
128
129 //#ifdef UseFJMTF
130 #if 1
131   // Fujitsu Motif seems to be buggy for HaL's Restraint widget.
132   // You must not destroy and re-create the Restraint widget.
133   // Instead keep the same Restraint widget forever. - 10/22/94 kamiya
134
135   // Create a group with a single term if none provided.
136   if (query == NULL)
137     {
138       query = new QueryGroup();
139       // This QueryTerm is pointed to by the QueryGroup.
140       new QueryTerm (query, NULL, NULL);
141     }
142
143   f_query = query;
144
145   // re-load the query terms
146   if (f_query_view != NULL)
147         f_query_view->re_load_terms(f_query);
148
149 #else
150   // Clean up old junk.
151   // The query isn't destroyed sometimes??
152   // Need to nuke only if it isn't saved on the history list.  DJB 
153   if (f_query_view != NULL)
154     {
155       QueryGroup *group = f_query_view->query_group();
156       f_query_view->destroy_widgets();
157       delete f_query_view;
158       delete f_query;
159     }
160
161   // NOTE: old query probably leaked!! 15:06 01/29/93 DJB
162   // This is going to need to be fixed up to edit pre-existing querys.
163   // NOTE: Another problem if this is called before the UI is created,
164   // because thar's widgets created below.  17:19 02/03/93 DJB
165   f_query = query;
166
167   // Create a group with a single term if none provided.
168   if (f_query == NULL)
169     {
170       f_null_terms = 0;
171       f_query = new QueryGroup();
172       // This QueryTerm is pointed to by the QueryGroup.
173       new QueryTerm (f_query, NULL, NULL);
174     }
175
176   // QueryGroupView holds the pointer to the QueryGroup. 
177   f_query_view =
178     new QueryGroupView (f_query, WXmForm ((WObject &) f_query_area));
179 #endif
180
181   if (first_time)
182     {
183 //#ifdef UseFJMTF
184 #if 1
185       f_null_terms = 0;
186
187       // QueryGroupView holds the pointer to the QueryGroup. 
188       f_query_view = new QueryGroupView (f_query, f_query_area);
189 #endif
190       // Only let pane get small enough to show two query entries.
191       // Start with the whole scrolled window, then take out the scrolling
192       // area to leave the borders, then add the term height * 2 back in.
193       // NOTE: This really needs to use the height of one row.  If this
194       // dialog comes up with an existing query at this point, the
195       // height will be wrong.  We need to get the height of a single
196       // line somehow.  11:59 02/04/93 DJB 
197       f_query_area.
198         PaneMinimum (f_query_area.Height() -
199                      WCore (XtParent(XtParent(*f_query_view))).Height() +
200                      (2 * f_query_view->Height()));
201
202       // Take the height of each pane - the min size of each pane.
203       // That is the excess height of the dialog.
204       // This needs to equal the current height - min height.
205       // So subtract it from the current height to get the new min height.
206       WXmForm qform (XtParent(f_query_text.Parent()));
207 #ifdef DEBUG
208       printf ("qform height = %d, scrolled height = %d\n",
209               qform.Height(), f_query_area.Height());
210       printf ("qform min = %d, scrolled min = %d\n",
211               qform.PaneMinimum(), f_query_area.PaneMinimum());
212 #endif
213       Dimension excess_height = qform.Height() + f_query_area.Height() -
214         qform.PaneMinimum() - f_query_area.PaneMinimum();
215
216       ON_DEBUG(printf ("Query Editor excess height = %d\n", excess_height));
217
218       f_shell.MinHeight (f_shell.Height() - excess_height);
219       
220       first_time = False;
221     }
222
223   //g_allow_query_text_change = TRUE;
224   f_query_text.Value ("");
225   //g_allow_query_text_change = FALSE;
226
227   // Move the input focus to the term view.
228   f_query_area.InitialFocus (*f_query_view);
229   //  f_query_view->traverse_here();
230
231   display();
232 }
233
234
235 // /////////////////////////////////////////////////////////////////
236 // create_ui - create the query editor user interface
237 // /////////////////////////////////////////////////////////////////
238
239 #define AM WAutoManage
240
241 void
242 QueryEditor::create_ui()
243 {
244   Wait_Cursor bob;
245
246   XmStringLocalized mtfstring;
247   String            string;
248   KeySym            mnemonic;
249
250   f_shell = WTopLevelShell(window_system().toplevel(), WPopup, "query_editor");
251   window_system().register_shell (&f_shell);
252
253   string = CATGETS(Set_QueryEditor, 1, "Dtinfo: Query Editor");
254   XtVaSetValues((Widget)f_shell, XmNtitle, string, NULL);
255
256   // Main form and menu bar. 
257   WXmForm form                      (f_shell,    "form"                );
258   WXmMenuBar menu_bar               (form,       "menu_bar"            );
259
260   WXmPulldownMenu scope_menu        (form,       "scope_menu");
261   Arg args[1];
262   int n = 0;
263   XtSetArg(args[n], XmNsubMenuId, (Widget) scope_menu); n++;
264   f_scope_option = WXmOptionMenu    (form,       "scope_option", AM, args, n);
265
266   mtfstring = CATGETS(Set_AgentLabel, 212, "Scope Name");
267   XtVaSetValues(f_scope_option, XmNlabelString, (XmString)mtfstring, NULL);
268
269   // Menu definitions.   how about using AddPushButton (name, obj, fun)??
270   WXmCascadeButton edit_cascade     (menu_bar,   "edit",             AM);
271   WXmPulldownMenu edit_menu         (menu_bar,   "edit_menu"           );
272   f_cut_btn = WXmPushButton         (edit_menu,  "cut",              AM);
273   f_copy_btn = WXmPushButton        (edit_menu,  "copy",             AM);
274   f_paste_btn = WXmPushButton       (edit_menu,  "paste",            AM);
275   f_paste_btn = WXmPushButton       (edit_menu,  "new_term",         AM);
276   WXmSeparator group_sep            (edit_menu,  "group_sep",        AM);
277   f_group_btn = WXmPushButton       (edit_menu,  "group",            AM);
278   f_ungroup_btn = WXmPushButton     (edit_menu,  "ungroup",          AM);
279   WXmSeparator undo_sep             (edit_menu,  "undo_sep",         AM);
280   f_undo_btn = WXmPushButton        (edit_menu,  "undo",             AM);
281   f_redo_btn = WXmPushButton        (edit_menu,  "redo",             AM);
282
283   mtfstring =  CATGETS(Set_AgentLabel, 16, "Edit");
284   mnemonic  = *CATGETS(Set_AgentLabel, 17, "E");
285   XtVaSetValues(edit_cascade, XmNlabelString, (XmString)mtfstring,
286                               XmNmnemonic, mnemonic, NULL);
287   mtfstring =  CATGETS(Set_AgentLabel, 224, "Cut");
288   XtVaSetValues(f_cut_btn, XmNlabelString, (XmString)mtfstring, NULL);
289   mtfstring =  CATGETS(Set_AgentLabel, 18, "Copy");
290   XtVaSetValues(f_copy_btn, XmNlabelString, (XmString)mtfstring, NULL);
291   mtfstring =  CATGETS(Set_AgentLabel, 225, "Paste");
292   XtVaSetValues(f_paste_btn, XmNlabelString, (XmString)mtfstring, NULL);
293   mtfstring =  CATGETS(Set_AgentLabel, 226, "Group");
294   XtVaSetValues(f_group_btn, XmNlabelString, (XmString)mtfstring, NULL);
295   mtfstring =  CATGETS(Set_AgentLabel, 227, "Ungroup");
296   XtVaSetValues(f_ungroup_btn, XmNlabelString, (XmString)mtfstring, NULL);
297   mtfstring =  CATGETS(Set_AgentLabel, 228, "Undo");
298   XtVaSetValues(f_undo_btn, XmNlabelString, (XmString)mtfstring, NULL);
299   mtfstring =  CATGETS(Set_AgentLabel, 229, "Redo");
300   XtVaSetValues(f_redo_btn, XmNlabelString, (XmString)mtfstring, NULL);
301
302   // Button area at the bottom 
303   WXmForm hform                     (form,       "hform"               );
304   f_hist_prev = WXmArrowButton      (hform,      "hist_prev",        AM);
305   f_hist_next = WXmArrowButton      (hform,      "hist_next",        AM);
306   WXmLabel history                  (hform,      "history",          AM);
307   f_search_btn = WXmPushButton      (form,       "search",           AM);
308   WXmPushButton cancel              (form,       "cancel",           AM);
309   WXmPushButton clear               (form,       "clear",            AM);
310   WXmPushButton scope               (form,       "scope",            AM);
311   WXmPushButton help                (form,       "help",             AM);
312   WXmSeparator separator            (form,       "separator",        AM);
313
314   mtfstring =  CATGETS(Set_AgentLabel, 92, "History");
315   XtVaSetValues(history, XmNlabelString, (XmString)mtfstring, NULL);
316   mtfstring =  CATGETS(Set_AgentLabel, 102, "Search");
317   XtVaSetValues(f_search_btn, XmNlabelString, (XmString)mtfstring, NULL);
318   mtfstring =  CATGETS(Set_AgentLabel, 231, "Clear All");
319   XtVaSetValues(clear, XmNlabelString, (XmString)mtfstring, NULL);
320   mtfstring =  CATGETS(Set_AgentLabel, 46, "Scope Editor");
321   XtVaSetValues(scope, XmNlabelString, (XmString)mtfstring, NULL);
322   mtfstring =  CATGETS(Set_AgentLabel, 12, "Close");
323   XtVaSetValues(cancel, XmNlabelString, (XmString)mtfstring, NULL);
324   mtfstring =  CATGETS(Set_AgentLabel, 48, "Help");
325   XtVaSetValues(help, XmNlabelString, (XmString)mtfstring, NULL);
326
327   help_agent().add_activate_help (help, "query_editor_help");
328
329   // Main "work" area 
330   WXmPanedWindow pane               (form,       "pane"                );
331   WXmForm qform                     (pane,       "qform"               );
332   WXmLabel qlabel                   (qform,      "qlabel",           AM);
333
334   mtfstring =  CATGETS(Set_AgentLabel, 230, "Query");
335   XtVaSetValues(qlabel, XmNlabelString, (XmString)mtfstring, NULL);
336   
337   f_query_text = WXmScrolledText    (qform,      "qtext",            AM);
338 //  f_query_text.SetEditable(False);
339   f_query_area = WXmScrolledWindow  (pane,       "query_area",       AM);
340   XtUnmanageChild (f_query_area.HorizontalScrollBar());
341
342   edit_cascade.SubMenuId (edit_menu);
343
344   // Callbacks
345   ON_ACTIVATE (f_search_btn, search_activate);
346   ON_ACTIVATE (cancel, cancel);
347   ON_ACTIVATE (clear, clear);
348   ON_ACTIVATE (scope, scope);
349
350 //  f_query_text.SetFocusCallback (this,
351 //                          (WWL_FUN) &QueryEditor::modify_verify);
352 //  f_query_text.SetModifyVerifyCallback (this,
353 //                          (WWL_FUN) &QueryEditor::modify_verify);
354
355   // Set minimum sizes.
356   qform.Manage();
357   pane.Manage();
358   //  hform.Manage();
359   form.DefaultButton (f_search_btn);
360   form.ShadowThickness (0);
361
362   form.InitialFocus (pane);
363   pane.InitialFocus (f_query_area);
364   qform.PaneMinimum (f_query_text.Height());
365   form.Manage();
366
367   f_shell.Realize();
368   f_shell.MinWidth (f_shell.Width());
369   f_shell.MinHeight (f_shell.Height());
370   ON_DEBUG(printf ("Query for height = %d\n", qform.Height()));
371   //cerr << "Query for height = " << qform.Height() << endl;
372
373 //#ifndef UseFJMTF
374 #if 1
375   // Swap the entries in the pane around.  Have to do this now
376   // to get initial sizes right.
377   qform.PositionIndex (1);
378 #endif
379
380   fill_menu();
381
382   UAS_SearchScope::request ((UAS_Receiver<ScopeCreated> *) this);
383   UAS_SearchScope::request ((UAS_Receiver<ScopeDeleted> *) this);
384   UAS_SearchScope::request ((UAS_Receiver<ScopeRenamed> *) this);
385
386   search_scope_mgr().option_menu(this);
387 }
388
389
390 // /////////////////////////////////////////////////////////////////
391 // search_activate
392 // /////////////////////////////////////////////////////////////////
393
394 void
395 QueryEditor::search_activate()
396 {
397   Wait_Cursor bob;
398
399   // Need to duplicate string returned, because it is in static data space. 
400   char *human_readable =
401     strdup (f_query_view->query_group()->
402             generate_query (QueryGroup::QUERY_TYPE_INFIX));
403
404 #ifdef DEBUG
405   fprintf(stderr, "(DEBUG) human_readable=\"%s\"\n", human_readable);
406 #endif
407
408   UAS_SearchScope *scope =
409     (UAS_SearchScope *) WXmPushButton (f_scope_option.MenuHistory()).UserData();
410
411   try {
412       // Search manager owns query from this point on.  Don't delete it here. 
413       search_mgr().parse_and_search (human_readable, scope);
414    }
415    catch (demoException &, demo) {
416       message_mgr().demo_failure(demo);
417    }
418    end_try;
419
420    free(human_readable);
421
422 }
423
424
425 // /////////////////////////////////////////////////////////////////
426 // cancel - close the window
427 // /////////////////////////////////////////////////////////////////
428
429 void
430 QueryEditor::cancel()
431 {
432   f_shell.Popdown();
433 }
434
435
436 // /////////////////////////////////////////////////////////////////
437 // empty term tracking routines
438 // /////////////////////////////////////////////////////////////////
439
440 void
441 QueryEditor::increment_null_terms()
442 {
443   if (f_null_terms == 0)
444     f_search_btn.SetSensitive (False);
445   f_null_terms++;
446   //  printf ("QueryEditor setting null_terms to %d\n", f_null_terms); 
447 }
448
449 void
450 QueryEditor::decrement_null_terms()
451 {
452   f_null_terms--;
453   //  printf ("QueryEditor setting null_terms to %d\n", f_null_terms); 
454   if (f_null_terms == 0)
455     f_search_btn.SetSensitive (True);
456 }
457
458
459 // /////////////////////////////////////////////////////////////////
460 // clear - clear the current query
461 // /////////////////////////////////////////////////////////////////
462
463 void
464 QueryEditor::clear()
465 {
466   edit_query (NULL);
467 }
468
469
470 // /////////////////////////////////////////////////////////////////////////
471 // scope - activate search scope dialog
472 // /////////////////////////////////////////////////////////////////////////
473 void
474 QueryEditor::scope()
475 {
476   search_scope_mgr().display();
477 }
478
479
480 // /////////////////////////////////////////////////////////////////
481 // query_changed - regenerate the text version
482 // /////////////////////////////////////////////////////////////////
483
484 void
485 QueryEditor::query_changed()
486 {
487   // Need to duplicate string returned, because it is in static data space. 
488 #ifdef UseQSearch
489   const char *human_readable =
490     f_query_view->query_group()->generate_readable();
491 #else
492   const char *human_readable =
493     f_query_view->query_group()->
494       generate_query (QueryGroup::QUERY_TYPE_INFIX);
495 #endif
496
497   //g_allow_query_text_change = TRUE;
498   f_query_text.Value ((char *) human_readable);
499   //g_allow_query_text_change = FALSE;
500 }
501
502
503 // /////////////////////////////////////////////////////////////////
504 // modify_verify
505 // /////////////////////////////////////////////////////////////////
506
507
508 void
509 QueryEditor::modify_verify (WCallback *wcb)
510 {
511 //  if (g_allow_query_text_change)
512 //    return;
513
514 //  XmTextVerifyPtr tvp = (XmTextVerifyPtr) wcb->CallData();
515
516   // Editing never allowed. 
517 //  tvp->doit = False;
518
519 //  message_mgr().warning_dialog (
520 //      (char*)UAS_String(CATGETS(Set_Messages, 47,
521 //                              "This is a display-only field.")));
522 }
523
524
525 // /////////////////////////////////////////////////////////////////
526 // fill_menu - initially file in the scope option menu
527 // /////////////////////////////////////////////////////////////////
528
529 void
530 QueryEditor::fill_menu()
531 {
532   int position = 0;
533   // Create push buttons for each scope.
534   xList<UAS_SearchScope *> &scope_list = search_scope_mgr().scope_list();
535   List_Iterator<UAS_SearchScope *> s (scope_list);
536   bool old_read_only = TRUE;
537
538   for (; s != NULL; s++)
539     {
540       // Add a separator when they change from read only to changable. 
541       if (old_read_only != s.item()->read_only())
542         {
543           DECLM (WXmSeparator, sep1, f_scope_option.SubMenuId(), "separator");
544           sep1.PositionIndex (position++);
545           old_read_only = FALSE;
546         }
547       DECLM (WXmPushButton, scope,f_scope_option.SubMenuId(),s.item()->name());
548       scope.UserData (s.item());
549       scope.PositionIndex (position++);
550     }
551
552   // Do not want first item (Current Section)
553
554 #ifdef __osf__
555 #if 1
556   Widget w = f_scope_option.SubMenuId();
557   WComposite temp_composite(w);
558   WXmPushButton(temp_composite.Children()[0]).Unmanage();
559 #else
560   int i = 0;
561   Widget w = f_scope_option.SubMenuId();;
562   Arg args[1];
563   WidgetList wl;
564   
565   XtSetArg(args[i], XmNchildren, &wl); i++;
566   XtGetValues(w, args, i);
567   XtUnmanageChild(wl[0]);
568 #endif
569 #else
570   WXmPushButton(WComposite(f_scope_option.SubMenuId()).Children()[0]).Unmanage();
571 #endif
572
573   f_scope_option.
574       MenuHistory (WComposite(f_scope_option.SubMenuId()).Children()[1]);
575   
576   //UAS_SearchScope::request ((UAS_Receiver<ScopeCreated> *) this);
577   //UAS_SearchScope::request ((UAS_Receiver<ScopeDeleted> *) this);
578   //UAS_SearchScope::request ((UAS_Receiver<ScopeRenamed> *) this);
579 //
580   //search_scope_mgr().option_menu(this);
581 }
582
583
584 // /////////////////////////////////////////////////////////////////
585 // receive* - handle updates to the list of scopes
586 // /////////////////////////////////////////////////////////////////
587
588 void
589 QueryEditor::receive (ScopeCreated &msg, void* /*client_data*/)
590 {
591   int position = 0;
592   const char *scope_name = msg.f_search_scope->name();
593   xList<UAS_SearchScope *> &scope_list = search_scope_mgr().scope_list();
594   List_Iterator<UAS_SearchScope *> s (scope_list);
595   bool need_sep = TRUE;
596
597   if (msg.f_search_scope->read_only())
598   {
599     // insert read-only scopes at the start; reserve position 0
600     // for "Current Section" scope and position 1 for
601     // the "All Libraries" scope
602     position = 2;
603     need_sep = FALSE;
604   }
605   else
606   {
607     // Scan the current menu to find the correct insertion position. 
608     UAS_SearchScope *scope;
609     for (; s != NULL; s++, position++)
610     {
611       scope = s.item();
612       if (scope->read_only() != need_sep)
613       {
614         position++;  // skip separator
615         need_sep = FALSE;
616       }
617       if (scope->read_only())
618         continue;
619       // Find the first item that the new entry belongs after.
620       ON_DEBUG (printf ("Scope Edit strcmp to <%s>\n", s.item()->name()));
621       if (strcmp (scope_name, scope->name()) < 0)
622         break;
623     }
624   }
625
626   ON_DEBUG (printf ("Final SE position = %d\n", position));
627
628   // Add a separator if this is the first user-defined entry. 
629   if (need_sep == TRUE)
630   {
631     DECLM (WXmSeparator, separator, f_scope_option.SubMenuId(), "separator");
632     separator.PositionIndex (position);
633     position++;
634   }
635
636   // Create the new toggle button. 
637   DECLM (WXmPushButton, scope, f_scope_option.SubMenuId(), scope_name);
638   scope.PositionIndex (position);
639   scope.UserData (msg.f_search_scope);
640 }
641
642
643 void
644 QueryEditor::receive (ScopeDeleted &msg, void* /*client_data*/)
645 {
646   // find the associated button and nuke it
647   ON_DEBUG (puts ("QueryEdit: handling delete message"));
648   // First find renamed button in our list.
649   WidgetList kids = WComposite(f_scope_option.SubMenuId()).Children();
650   int num_kids = WComposite(f_scope_option.SubMenuId()).NumChildren();
651   int separator_pos = -1;
652   for (int i = 0; i < num_kids; i++)
653     {
654       if (XmIsSeparator (kids[i]))
655         separator_pos = i + 1;
656       if (msg.f_search_scope ==
657           ((UAS_SearchScope *) WXmPushButton (kids[i]).UserData()))
658         break;
659     }
660
661   // It had better be in the list! 
662   Xassert (i != num_kids);
663   ON_DEBUG (printf ("  widget #%d is the button\n", i));
664
665   // if it is selected, select first w/ callback called
666   if (kids[i] == f_scope_option.MenuHistory())
667     f_scope_option.MenuHistory (kids[1]);
668   XtDestroyWidget (kids[i]);
669
670   ON_DEBUG (printf ("QueryEditor: sep pos = %d, kids = %d (%d)\n",
671                     separator_pos, num_kids, num_kids - 1));
672   if (separator_pos == num_kids - 1)
673     {
674       ON_DEBUG (puts ("   destroying separator"));
675       XtDestroyWidget (kids[separator_pos-1]);
676     }
677 }
678
679
680 void
681 QueryEditor::receive (ScopeRenamed &msg, void* /*client_data*/)
682 {
683   ON_DEBUG (puts ("QueryEdit: handling rename message"));
684   // First find renamed button in our list.
685   WidgetList kids = WComposite(f_scope_option.SubMenuId()).Children();
686   int num_kids = WComposite(f_scope_option.SubMenuId()).NumChildren();
687   for (int i = 0; i < num_kids; i++)
688     {
689       if (msg.f_search_scope ==
690           ((UAS_SearchScope *) WXmPushButton (kids[i]).UserData()))
691         break;
692     }
693
694   // It had better be in the list! 
695   Xassert (i != num_kids);
696   ON_DEBUG (printf ("QueryEdit: widget #%d is the button\n", i));
697
698   // Now find the new insertion position in the list.
699   int position = 0;
700   xList<UAS_SearchScope *> &scope_list = search_scope_mgr().scope_list();
701   List_Iterator<UAS_SearchScope *> s (scope_list);
702
703   // find the new position in the list
704   for (; s != NULL; s++)
705     {
706       if (s.item() == msg.f_search_scope)
707         continue;
708       position++;
709       if (s.item()->read_only())
710         continue;
711       ON_DEBUG (printf ("QueryEdit: strcmp <%s>\n", s.item()->name()));
712       if (strcmp (msg.f_search_scope->name(), s.item()->name()) < 0)
713         break;
714     }
715
716   ON_DEBUG (printf ("QueryEdit: Rename position = %d\n", position));
717   WXmPushButton scope_btn (kids[i]);
718   scope_btn.LabelString (msg.f_search_scope->name());
719   scope_btn.PositionIndex (position);
720 }
721
722 void
723 QueryEditor::update_option_menu()
724 {
725   WidgetList kids = WComposite(f_scope_option.SubMenuId()).Children();
726   int num_kids = WComposite(f_scope_option.SubMenuId()).NumChildren();
727
728   // destroy all toggle buttons in menu
729   for (int i = 0; i < num_kids; i++)
730   {
731     XtUnmanageChild (kids[i]);
732     XtDestroyWidget (kids[i]);
733   }
734
735   fill_menu();
736 }