-fpermissive to allow GCC to compile old C++
[oweals/cde.git] / cde / programs / dtmail / dtmail / FindDialog.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  *+SNOTICE
25  *
26  *      $TOG: FindDialog.C /main/7 1998/07/23 17:59:06 mgreess $
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *      
30  *      The information in this document is subject to special
31  *      restrictions in a confidential disclosure agreement between
32  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
33  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
34  *      Sun's specific written approval.  This document and all copies
35  *      and derivative works thereof must be returned or destroyed at
36  *      Sun's request.
37  *
38  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42
43 #include <assert.h>
44 #include <X11/Intrinsic.h>
45 #include <Xm/Form.h>
46 #include <Xm/RowColumn.h>
47 #include <Xm/MessageB.h>
48 #include <Xm/TextF.h>
49 #include <Xm/Label.h>
50 #include <Xm/PushB.h>
51 #include <Xm/DialogS.h>
52 #include <Xm/PanedW.h>
53 #include <Xm/LabelG.h>
54 #include <Xm/Text.h>
55 #include <Xm/SeparatoG.h>
56 #include <DtMail/DtMail.h>
57 #include <DtMail/DtMail.hh>
58 #include "FindDialog.h"
59 #include "RoamApp.h"
60 #include "RoamMenuWindow.h"
61 #include "RoamCmds.h"
62 #include "Help.hh"
63 #include "MailMsg.h"
64 #include <EUSCompat.h>
65 #include "str_utils.h"
66
67
68 //
69 // Clear out the data. After this function is complete the
70 // data should look as if the constructor was just called and
71 // before initialize().
72 //
73 void
74 FindDialog::clear()
75 {
76   register unsigned int         offset;
77
78   //
79   if (_text_labels != NULL) {
80     for (offset = 0; offset < _num_text_fields; offset++) {
81       if (_text_labels[offset] != NULL) {
82         free(_text_labels[offset]);
83       }
84     }
85     delete _text_labels;
86   }
87
88   //
89   if (_text_values != NULL) {
90     for (offset = 0; offset < _num_text_fields; offset++) {
91       if (_text_values[offset] != NULL) {
92         free(_text_values[offset]);
93       }
94     }
95     delete _text_values;
96   }
97
98   //
99   if (_text_abstract_name != NULL) {
100     for (offset = 0; offset < _num_text_fields; offset++) {
101       if (_text_abstract_name[offset] != NULL) {
102         free(_text_abstract_name[offset]);
103       }
104     }
105     delete _text_abstract_name;
106   }
107
108
109   //
110   if (_buttonData != NULL) {
111     for (offset = 0; offset < _num_buttons; offset++) {
112       if (_buttonData[offset].label != NULL) {
113         free(_buttonData[offset].label);
114       }
115     }
116     delete _buttonData;
117   }
118
119   if (_text_fields != NULL) {
120     delete _text_fields;
121   }
122
123   if (_text_names != NULL) {
124     delete _text_names;
125   }
126 }
127
128 //
129 // The only constructor.
130 //
131 FindDialog::FindDialog(RoamMenuWindow *parent) : Dialog("find", parent)
132 {
133   _roamWindow = parent;
134   _num_text_fields = 4;
135   _num_buttons = 5;
136
137   //
138   // Allocate storage for labels, widgets, and data.
139   //
140   _text_labels = new char *[_num_text_fields];
141   _text_names = new char *[_num_text_fields];
142   _text_values = new char *[_num_text_fields];
143   _text_abstract_name = new char *[_num_text_fields];
144   _text_fields = new Widget[_num_text_fields];
145   _buttonData = new ActionAreaItem[_num_buttons];
146   _searchForward = TRUE;
147
148   //
149   // Initialize the buttons.
150   //
151   _buttonData[0].label = strdup(GETMSG(DT_catd, 1, 183, "Find"));
152   _buttonData[0].callback = findCallback;
153   _buttonData[0].data = (caddr_t) this;
154
155 #ifdef NL_OBSOLETE
156   /*
157    * NL_COMMENT
158    * This is an obsolete message.  Replaced by message 220 in set 1
159    */
160   _buttonData[1].label = strdup(GETMSG(DT_catd, 1, 184, "Find & Select All"));
161 #endif
162    /*
163     * NL_COMMENT
164     * This message replaces message 184 in set 1
165     */
166   _buttonData[1].label = strdup(GETMSG(DT_catd, 1, 220, "Select All"));
167   _buttonData[1].callback = findSelectAllCallback;
168   _buttonData[1].data = (caddr_t) this;
169
170   _buttonData[2].label = strdup(GETMSG(DT_catd, 1, 185, "Clear"));
171   _buttonData[2].callback = clearCallback;
172   _buttonData[2].data = (caddr_t) this;
173
174   _buttonData[3].label = strdup(GETMSG(DT_catd, 1, 186, "Close"));
175   _buttonData[3].callback = closeCallback;
176   _buttonData[3].data = (caddr_t) this;
177
178   _buttonData[4].label = strdup(GETMSG(DT_catd, 1, 187, "Help"));
179   _buttonData[4].callback = HelpCB;
180   _buttonData[4].data = (caddr_t) DTMAILFINDDIALOG;
181
182   _text_labels[0] = strdup(GETMSG(DT_catd, 1, 188, "To:"));
183   _text_labels[1] = strdup(GETMSG(DT_catd, 1, 189, "From:"));
184   _text_labels[2] = strdup(GETMSG(DT_catd, 1, 190, "Subject:"));
185   _text_labels[3] = strdup(GETMSG(DT_catd, 1, 191, "Cc:"));
186
187   // These strings should not be translated.  They are
188   // the Motif names for the widgets that will be created (they are
189   // not the labels).
190   _text_names[0] = "To";
191   _text_names[1] = "From";
192   _text_names[2] = "Subject";
193   _text_names[3] = "Cc";
194
195   //
196   // Initialize the names of the fields to the abstract
197   // names used by libDtMail.
198   //
199   _text_abstract_name[0] = strdup(DtMailMessageTo);
200   _text_abstract_name[1] = strdup(DtMailMessageSender);
201   _text_abstract_name[2] = strdup(DtMailMessageSubject);
202   _text_abstract_name[3] = strdup(DtMailMessageCc);
203 }
204
205 //
206 // Print a string in the status line of the find dialog.
207 //
208 void
209 FindDialog::setStatus(const char * str)
210 {
211     char *tmpstr = strdup(str);
212     XmString label = XmStringCreateLocalized(tmpstr);
213  
214     XtVaSetValues(_status_text,
215                   XmNlabelString, label,
216                   NULL);
217  
218     XmUpdateDisplay(baseWidget());
219     XmStringFree(label);
220 }
221
222 //
223 // Clear the status line of the find dialog.
224 //
225 void
226 FindDialog::clearStatus(void)
227 {
228     setStatus(" ");
229 }
230
231 //
232 // Create the guts of the dialog
233 //
234 Widget
235 FindDialog::createWorkArea(Widget dialog)
236 {
237   // TODO - CHECK ERROR!!!
238   Widget *label = new Widget[_num_text_fields]; 
239
240
241   register unsigned int         offset;
242
243   _name = GETMSG(DT_catd, 1, 192, "Mailer - Find");
244
245   title(_name);
246
247         // make this a modal dialog
248         /*
249         XtVaSetValues (dialog,
250                         XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
251                         NULL);
252         */
253
254         printHelpId("dialog", dialog);
255
256   /* add help callback */
257   // XtAddCallback(dialog, XmNhelpCallback, HelpCB, helpId);
258
259         Widget fd_pane = XtVaCreateWidget ("fd_pane",
260                                 xmPanedWindowWidgetClass,
261                                 dialog,
262                                 XmNsashWidth,   1,
263                                 XmNsashHeight,  1,
264                                 NULL);
265
266         printHelpId ("fd_pane", fd_pane);
267         // add help callback
268         // XtAddCallback (fd_pane, XmNhelpCallback, HelpCB, helpId);
269
270         Widget  fd_form = XtVaCreateWidget ("fd_form",
271                                 xmFormWidgetClass,
272                                 fd_pane,
273                                 XmNfractionBase,        100,
274                                 NULL);
275
276         printHelpId ("fd_form", fd_form);
277         // add help callback
278         // XtAddCallback (fd_form, XmNhelpCallback, HelpCB, helpId);
279
280
281         Widget _fd_labelbox = XtVaCreateManagedWidget ("_fd_labelbox",
282                                 xmRowColumnWidgetClass,
283                                 fd_form,
284                                 XmNtopAttachment,       XmATTACH_FORM,
285                                 XmNleftAttachment,      XmATTACH_POSITION,
286                                 XmNrightAttachment,     XmATTACH_POSITION,
287                                 XmNleftPosition,        5,
288                                 XmNrightPosition,       95,
289                                 XmNpacking,             XmPACK_COLUMN,
290                                 XmNnumColumns,          2,
291                                 XmNorientation,         XmVERTICAL,
292                                 XmNisAligned,           True,
293                                 XmNentryAlignment,      XmALIGNMENT_END,
294                                 XmNentryVerticalAlignment,      XmALIGNMENT_CENTER,
295                                 NULL); 
296         printHelpId ("_fd_labelbox", _fd_labelbox);
297         // add help callback
298         // XtAddCallback (_fd_labelbox, XmNhelpCallback, HelpCB, helpId);
299
300
301         Widget  *_fd_labels = new Widget [_num_text_fields];
302
303         int     _fd_i = 0;
304         for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
305         {
306                 _fd_labels [_fd_i] = XtVaCreateManagedWidget (
307                                         _text_labels [_fd_i],
308                                         xmLabelGadgetClass,
309                                         _fd_labelbox,
310                                         NULL);
311
312                 printHelpId ("_fd_labels [%s]", _fd_labels [_fd_i]);
313                 // naturally, this is bogus --must be fixed to return proper label
314                 // add help callback
315                 // XtAddCallback(_fd_labels [_fd_i], XmNhelpCallback, HelpCB, helpId);
316         }
317
318         for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
319         {
320                 _text_fields [_fd_i] = XtVaCreateManagedWidget (
321                                         _text_names [_fd_i],
322                                         xmTextFieldWidgetClass,
323                                         _fd_labelbox,
324                                         NULL);
325                 printHelpId ("_text_fields [%s]", _text_fields [_fd_i]);
326                 // naturally, this is bogus --must be fixed to return proper label
327                 // add help callback
328                 // XtAddCallback(_text_fields [_fd_i], XmNhelpCallback, HelpCB, helpId);
329
330                 XtAddCallback(_text_fields [_fd_i], XmNactivateCallback,
331                         (XtCallbackProc)textFieldCallback, (XtPointer)this);
332         }
333
334
335   XmString      strForward = XmStringCreateLocalized(GETMSG(DT_catd, 1, 193, "Forward"));
336   XmString      strBackward = XmStringCreateLocalized(GETMSG(DT_catd, 1, 194, "Backward"));
337
338   Widget fd_direction
339         = XmVaCreateSimpleRadioBox(fd_form,
340                                 "Direction",
341                                0,               // Initial selection
342                                directionCallback,
343                                 //NULL,
344                                XmVaRADIOBUTTON, strForward, NULL, NULL, NULL,
345                                XmVaRADIOBUTTON, strBackward, NULL, NULL, NULL,
346                                XmNuserData,     this,
347                                 XmNsensitive,   True,
348                                 XmNtopAttachment,       XmATTACH_WIDGET,
349                                 XmNtopWidget,           _fd_labelbox,
350                                 XmNorientation, XmHORIZONTAL,
351                                 XmNleftAttachment,      XmATTACH_POSITION,
352                                 XmNleftPosition,        33,
353                                NULL);
354          printHelpId ("fd_direction", fd_direction);
355         // add help callback
356         //XtAddCallback (fd_direction, XmNhelpCallback, HelpCB, helpId);
357
358   XmStringFree(strForward);
359   XmStringFree(strBackward);
360
361   //
362   // Now create the Action Area.
363   //
364 #define TIGHTNESS       20
365
366   register Widget               widget;
367
368   Widget fd_action = XtVaCreateWidget("actionArea",
369                                  xmFormWidgetClass,
370                                  fd_pane,
371                                  XmNleftAttachment,     XmATTACH_FORM,
372                                  XmNrightAttachment,    XmATTACH_FORM,
373                                  XmNfractionBase, TIGHTNESS * _num_buttons-1,
374                                  NULL);
375          printHelpId ("actionArea", fd_action);
376         // add help callback
377         //XtAddCallback (fd_action, XmNhelpCallback, HelpCB, helpId);
378
379   for (offset = 0; offset < _num_buttons; offset++) 
380   {  widget = XtVaCreateManagedWidget(_buttonData[offset].label,
381                                      xmPushButtonWidgetClass,   fd_action,
382
383                                      XmNleftAttachment,
384                                      offset ? XmATTACH_POSITION:XmATTACH_FORM,
385
386                                      XmNleftPosition,   TIGHTNESS * offset,
387                                      XmNtopAttachment,  XmATTACH_FORM,
388
389                                      XmNrightAttachment,
390                                      offset != _num_buttons - 1 ? XmATTACH_POSITION : XmATTACH_FORM,
391
392                                      XmNrightPosition,
393                                      TIGHTNESS * offset + (TIGHTNESS - 1),
394
395                                      XmNshowAsDefault,  offset == 0,
396                                      NULL);
397
398         // again, bogus -- doesn't each one need a unique tag?
399          printHelpId ("widget", widget);
400         // add help callback
401         //XtAddCallback (widget, XmNhelpCallback, HelpCB, helpId);
402
403     if (_buttonData[offset].callback != NULL) {
404       XtAddCallback(widget, XmNactivateCallback,
405                     _buttonData[offset].callback,
406                     _buttonData[offset].data);
407     }
408
409
410     if (offset == 0) {
411       Dimension         height;
412       Dimension         margin;
413
414       XtVaGetValues(fd_action, XmNmarginHeight, &margin, NULL);
415       XtVaGetValues(widget, XmNheight, &height, NULL);
416       height +=2 * margin;
417       XtVaSetValues(fd_action,
418                     XmNdefaultButton,   widget,
419                     XmNpaneMaximum,     height,
420                     XmNpaneMinimum,     height,
421                     NULL);
422
423     }
424   }
425
426   _status_text = XtVaCreateManagedWidget("StatusLabel",
427                                            xmLabelWidgetClass, fd_pane,
428                                            XmNrightAttachment, XmATTACH_FORM,
429                                            XmNleftAttachment, XmATTACH_FORM,
430                                            XmNalignment, XmALIGNMENT_BEGINNING,
431                                            NULL);
432             
433   Dimension height;
434   XtWidgetGeometry size;
435
436   size.request_mode = CWHeight;
437   XtQueryGeometry(_status_text, NULL, &size);
438   XtVaSetValues(_status_text,
439                 XmNpaneMaximum, size.height,
440                 XmNpaneMinimum, size.height,
441                 NULL);
442  
443   clearStatus();
444
445   XtManageChild (fd_form);
446   XtManageChild (fd_direction);
447   XtManageChild(fd_action);
448   XtManageChild(fd_pane);
449
450   XtManageChild(dialog);
451
452   // Make sure get the height of the dialog after it has been
453   // managed.
454   XtVaGetValues(dialog, XmNheight, &height, NULL);
455   XtVaSetValues(dialog, 
456                 XmNmappedWhenManaged, True,
457                 XmNminHeight, height,
458                 NULL);
459   XtRealizeWidget(dialog);
460
461   return (fd_pane);
462 }
463
464
465 //
466 // Look for all matching messages.
467 //
468 Boolean
469 FindDialog::findMatching(Boolean findAll)
470 {
471   // TODO - CHECK ERROR!!!
472   DtMailEnv             error;
473   unsigned int          matchCount = 0;
474
475   /* NL_COMMENT
476    * This string is displayed on the find dialog status line
477    * when searching for a matching message.
478    */
479
480   setStatus(GETMSG(DT_catd, 1, 231, "Searching..."));
481   busyCursor();
482   theRoamApp.busyAllWindows(NULL);
483
484   //
485   // Get the active list.
486   //
487   MsgScrollingList      * displayList = _roamWindow->list();
488
489   //
490   // Find  the max. number of messages that we are to find matching.
491   //
492   int                     numberMessages = displayList->get_num_messages();
493
494   //
495   // Are there any messages?
496   //
497   if (numberMessages > 0) {
498
499     //
500     // A pointer to the currently interesting message.
501     //
502     register DtMailMessageHandle          currentHandle = NULL;
503
504     //
505     // The offset of the currentHandle in the MsgScrollingList.
506     //
507     register int                  handleOffset;
508
509     //
510     // Find the current message. We would always start from the
511     // currently selected message.
512     //
513     // Get the handle to the currently displaied message.
514     //
515     DtMailMessageHandle   initialHandle = displayList->current_msg_handle();
516
517     //
518     // Get the list of DtMailMessageHandle's.
519     
520     MsgHndArray         * msgHandles = displayList->get_messages();
521
522     //
523     // Up to all of them can match, allocate and clear the list.
524     //
525     DtMailMessageHandle * matchList = NULL;
526     if (findAll) {
527       matchList = new DtMailMessageHandle[numberMessages+1];
528     }
529     unsigned int         matchOffset = 0;
530
531     //
532     // Deselect all messages.
533     //
534     XmListDeselectAllItems(displayList->baseWidget());
535
536     //
537     // Start the search from the initially displaied message (+1).
538     //
539     handleOffset = displayList->position(initialHandle) - 1;
540     if (_searchForward) {
541       handleOffset++;
542       if (handleOffset >= numberMessages) {
543         handleOffset = 0;
544       }
545     } else {
546       handleOffset--;
547       if (handleOffset < 0) {
548         handleOffset = numberMessages - 1;
549       }
550     }
551
552     for (; handleOffset < numberMessages;) {
553       currentHandle = msgHandles->at(handleOffset)->message_handle;
554     
555       //
556       // See if this message is a match, if it is...
557       //
558       if (compareMessage(currentHandle)) {
559         matchCount++;
560
561         //
562         // If we are finding all, then add to the list.
563         // If not, then display this message and we are done.
564         //
565         if (findAll) {
566           matchList[matchOffset++] = currentHandle;
567         } else {
568           XmListDeselectAllItems(displayList->baseWidget());
569           //displayList->set_selected_item_position(handleOffset);
570           displayList->display_and_select_message(error, currentHandle);
571           break;                        // Only one.
572         }
573       }
574
575       //
576       // If we have looped back to the initial
577       // message (handle), then we are done.
578       //
579       if (currentHandle == initialHandle) {
580         break;
581       }
582
583       //
584       // Get the next message.
585       //
586       // If we have reached the end, start over.
587       // (as if the list was a circular list)
588       //
589       // We loop forward (_searchForward == TRUE) else we loop backward.
590       //
591       if (_searchForward) {
592         handleOffset++;
593         if (handleOffset >= numberMessages) {
594           handleOffset = 0;
595         }
596       } else {
597         handleOffset--;
598         if (handleOffset < 0) {
599           handleOffset = numberMessages - 1;
600         }
601       }
602       currentHandle = msgHandles->at(handleOffset)->message_handle;
603     }
604
605     //
606     // Select all the messages that match, and display the last
607     // one in the list.
608     //
609     if (findAll) {
610       
611       displayList->select_all_and_display_last(error, matchList, matchCount);
612       if (matchCount > 0) {
613         char *line = new char[80];
614         /* NL_COMMENT
615          * These strings are displayed on the find dialog status line
616          * when one or more matching messages are found.  The first
617          * string is displayed when there is one matching message,
618          * and the second string is displayed when there is more than
619          * one.  The %d is the number of messages that matched.
620          */
621         if (matchCount == 1) {
622             strcpy(line, GETMSG(DT_catd, 1, 232, "1 message selected"));
623         } else {
624             sprintf(line, GETMSG(DT_catd, 1, 233, "%d messages selected"), 
625                             matchCount);
626         }
627         setStatus(line);
628         delete [] line;
629       }
630
631       // Clean up.
632       delete matchList;
633       matchList = NULL;
634     }
635   }
636
637   normalCursor();
638   theRoamApp.unbusyAllWindows();
639   if (error.isNotSet()) {
640     if (matchCount > 0) {
641         if (!findAll) {
642             clearStatus();
643         }
644         return(TRUE);
645     }
646   }
647   /* NL_COMMENT
648    * This string is displayed on the find dialog status line when
649    * no matching messages were found.
650    */
651   setStatus(GETMSG(DT_catd, 1, 234, "No matches were found"));
652   return(False);
653 }
654
655 Boolean
656 FindDialog::compareMessage(DtMailMessageHandle    handle)
657 {
658   Boolean                               found = False;
659   register unsigned int         offset;
660
661   //
662   // Check for something to do.
663   //
664   for (offset = 0; offset < _num_text_fields; offset++) {
665     if (_text_values[offset] != NULL) {
666       break;
667     }
668   }
669
670   // If all fields are empty then we match anything
671   if (offset >= _num_text_fields) {
672         return TRUE;
673   }
674
675   if (offset < _num_text_fields && handle != NULL) {
676
677     // TODO - CHECK ERROR!!!
678     DtMailEnv           error;
679
680     //
681     // Get the mail box.
682     //
683     DtMail::MailBox     * mbox = _roamWindow->mailbox();
684
685     //
686     // Get the DtMail::Message and Envelope for this handle.
687     //
688     DtMail::Message     * message = mbox->getMessage(error, handle);
689     DtMail::Envelope    * envelope = message->getEnvelope(error);
690
691     //
692     // Get the meassage header.
693     //
694     DtMailValueSeq        header;
695
696     for (offset = 0; offset < _num_text_fields; offset++) {
697       if (_text_values[offset] != NULL) {
698         if (_text_abstract_name[offset] != NULL) {
699           envelope->getHeader(error, _text_abstract_name[offset],
700                               DTM_TRUE, header);
701           found = TRUE;
702         } else {
703           envelope->getHeader(error, _text_names[offset],
704                               DTM_FALSE, header);
705           found = TRUE;
706         }
707         if (!compareHeader(error, header, _text_values[offset])) {
708           found = False;
709           break;
710         }
711         else {
712           // Problem: if we have multiple search fields ... and use
713           // the same "header" array ... "compareHeader" looks for
714           // each "find" field in each (available) header field.
715           // So, make sure only one is available for searching.
716           // Really "offset" should be passed to "compareHeader" ...
717           // That way, correct comparison can be done and the
718           // memory for this array can be released correctly via the
719           // destructor .... since "remove" fails to do so.
720           header.remove(0);
721         }
722       }
723     }
724     if (offset > _num_text_fields) {
725       found = TRUE;
726     }
727   }
728   return(found);
729 }
730
731 //
732 // See if string 'toFind' is anyware in string 'str'.
733 // A case-insensitive version of strstr().
734 //
735 static const char       *
736 strcasestr(const char *str, const char *toFind)
737 {
738   const char    *result = NULL;         // Default to not found.
739
740   if (str && toFind) {          // Sanity check
741     register int        offset = 0;
742     register int        lenToFind = strlen(toFind);
743     register int        lenStr = strlen(str);
744
745     //
746     // If toFind == "", then return the entire string (like strstr()).
747     //
748     if (lenToFind == 0) {
749       result = str;
750     } else {
751       //
752       // Start at each position in the string and look for
753       // toFind - ignore case.
754       //
755       for (offset = 0; offset + lenToFind <= lenStr; offset++) {
756         if (strncasecmp(&str[offset], toFind, lenToFind) == 0) {
757           result = &str[offset];
758           break;
759         }
760       }
761     }
762   }
763   return(result);
764 }
765
766 Boolean
767 FindDialog::compareHeader(DtMailEnv             & error,
768                           DtMailValueSeq        & header,
769                           const char            * cmpToString)
770 {
771   register int          headerOffset = header.length() - 1;
772
773   error.clear();
774
775   while(headerOffset >= 0) {
776     if ((strcasestr(*(header[headerOffset]), cmpToString)) != NULL) {
777       return(TRUE);
778     }
779     headerOffset--;
780   }
781   return(False);
782 }
783
784 //
785 // Pull all fields out of the dialog and store in the class.
786 //
787 void
788 FindDialog::getAllFields()
789 {
790   register unsigned int         offset;
791
792   for (offset = 0; offset < _num_text_fields; offset++) {
793     if (_text_fields[offset] != NULL) {
794       _text_values[offset] = XmTextFieldGetString(_text_fields[offset]);
795
796       // Ignore zero length strings.
797       if (_text_values[offset] != NULL) {
798         if (strlen(_text_values[offset]) == 0) {
799           _text_values[offset] = NULL;
800         }
801       }
802     }
803   }
804
805   return;
806 }
807
808 void
809 FindDialog::textFieldCallback(
810         Widget          field,
811         XtPointer       data,
812         XtPointer)
813 {
814         char *s;
815         FindDialog    *findData = (FindDialog *)data;
816
817         if (*(s = XmTextGetString(field)) == '\0') {
818                 // Empty field.  Traverse
819                 (void) XmProcessTraversal(field, XmTRAVERSE_NEXT_TAB_GROUP);
820         } else {
821                 // Field not empty. Do search
822                 findData->getAllFields();
823                 if (!findData->findMatching(False)) {
824                         XBell(XtDisplay(field), 0);
825                 }
826         }
827
828         return;
829 }       
830
831 void
832 FindDialog::directionCallback(Widget    widget,
833                               XtPointer closure,
834                               XtPointer)
835 {
836   int          which = (int) ((long) closure);  // closure contains button #
837   FindDialog    *find; 
838   
839   // Client data is actually on the @!$?@* parent, not the toggle item
840   XtVaGetValues(XtParent(widget), XmNuserData, &find, NULL);
841
842   if (which == 0) {
843     find->setSearchForward(TRUE);
844   } else {
845     find->setSearchForward(False);
846   }
847
848   return;
849 }
850
851 void
852 FindDialog::findCallback(Widget         /*button*/,
853                          XtPointer      closure,
854                          XtPointer      /*call_data*/)
855 {
856   FindDialog    *findData = (FindDialog *)closure;
857
858   findData->getAllFields();
859   findData->findMatching(False);
860   return;
861 }
862
863
864 void
865 FindDialog::findSelectAllCallback(Widget        /*button*/,
866                                   XtPointer     closure,
867                                   XtPointer     /*call_data*/)
868 {
869   FindDialog    *findData = (FindDialog *)closure;
870
871   findData->getAllFields();
872   findData->findMatching(TRUE);
873   return;
874 }
875
876 void
877 FindDialog::clearCallback(Widget        /*button*/,
878                           XtPointer     closure,
879                           XtPointer     /*call_data*/)
880 {
881   FindDialog    *findData = (FindDialog *)closure;
882   register unsigned int         offset;
883
884   for (offset = 0; offset < findData->_num_text_fields; offset++) {
885     if (findData->_text_fields[offset] != NULL) {
886       XmTextFieldSetString(findData->_text_fields[offset], "");
887     }
888   }
889   return;
890 }
891
892 void
893 FindDialog::closeCallback(Widget        /*button*/,
894                           XtPointer     closure,
895                           XtPointer     /*call_data*/)
896 {
897   FindDialog    *findData = (FindDialog *)closure;
898   
899   findData->popdown();
900   return;
901 }