2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
26 * $TOG: FindDialog.C /main/7 1998/07/23 17:59:06 mgreess $
28 * RESTRICTED CONFIDENTIAL INFORMATION:
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
38 * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
44 #include <X11/Intrinsic.h>
46 #include <Xm/RowColumn.h>
47 #include <Xm/MessageB.h>
51 #include <Xm/DialogS.h>
52 #include <Xm/PanedW.h>
53 #include <Xm/LabelG.h>
55 #include <Xm/SeparatoG.h>
56 #include <DtMail/DtMail.h>
57 #include <DtMail/DtMail.hh>
58 #include "FindDialog.h"
60 #include "RoamMenuWindow.h"
64 #include <EUSCompat.h>
65 #include "str_utils.h"
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().
76 register unsigned int offset;
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]);
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]);
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]);
105 delete _text_abstract_name;
110 if (_buttonData != NULL) {
111 for (offset = 0; offset < _num_buttons; offset++) {
112 if (_buttonData[offset].label != NULL) {
113 free(_buttonData[offset].label);
119 if (_text_fields != NULL) {
123 if (_text_names != NULL) {
129 // The only constructor.
131 FindDialog::FindDialog(RoamMenuWindow *parent) : Dialog("find", parent)
133 _roamWindow = parent;
134 _num_text_fields = 4;
138 // Allocate storage for labels, widgets, and data.
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;
149 // Initialize the buttons.
151 _buttonData[0].label = strdup(GETMSG(DT_catd, 1, 183, "Find"));
152 _buttonData[0].callback = findCallback;
153 _buttonData[0].data = (caddr_t) this;
158 * This is an obsolete message. Replaced by message 220 in set 1
160 _buttonData[1].label = strdup(GETMSG(DT_catd, 1, 184, "Find & Select All"));
164 * This message replaces message 184 in set 1
166 _buttonData[1].label = strdup(GETMSG(DT_catd, 1, 220, "Select All"));
167 _buttonData[1].callback = findSelectAllCallback;
168 _buttonData[1].data = (caddr_t) this;
170 _buttonData[2].label = strdup(GETMSG(DT_catd, 1, 185, "Clear"));
171 _buttonData[2].callback = clearCallback;
172 _buttonData[2].data = (caddr_t) this;
174 _buttonData[3].label = strdup(GETMSG(DT_catd, 1, 186, "Close"));
175 _buttonData[3].callback = closeCallback;
176 _buttonData[3].data = (caddr_t) this;
178 _buttonData[4].label = strdup(GETMSG(DT_catd, 1, 187, "Help"));
179 _buttonData[4].callback = HelpCB;
180 _buttonData[4].data = (caddr_t) DTMAILFINDDIALOG;
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:"));
187 // These strings should not be translated. They are
188 // the Motif names for the widgets that will be created (they are
190 _text_names[0] = "To";
191 _text_names[1] = "From";
192 _text_names[2] = "Subject";
193 _text_names[3] = "Cc";
196 // Initialize the names of the fields to the abstract
197 // names used by libDtMail.
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);
206 // Print a string in the status line of the find dialog.
209 FindDialog::setStatus(const char * str)
211 char *tmpstr = strdup(str);
212 XmString label = XmStringCreateLocalized(tmpstr);
214 XtVaSetValues(_status_text,
215 XmNlabelString, label,
218 XmUpdateDisplay(baseWidget());
223 // Clear the status line of the find dialog.
226 FindDialog::clearStatus(void)
232 // Create the guts of the dialog
235 FindDialog::createWorkArea(Widget dialog)
237 // TODO - CHECK ERROR!!!
238 Widget *label = new Widget[_num_text_fields];
241 register unsigned int offset;
243 _name = GETMSG(DT_catd, 1, 192, "Mailer - Find");
247 // make this a modal dialog
249 XtVaSetValues (dialog,
250 XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
254 printHelpId("dialog", dialog);
256 /* add help callback */
257 // XtAddCallback(dialog, XmNhelpCallback, HelpCB, helpId);
259 Widget fd_pane = XtVaCreateWidget ("fd_pane",
260 xmPanedWindowWidgetClass,
266 printHelpId ("fd_pane", fd_pane);
268 // XtAddCallback (fd_pane, XmNhelpCallback, HelpCB, helpId);
270 Widget fd_form = XtVaCreateWidget ("fd_form",
273 XmNfractionBase, 100,
276 printHelpId ("fd_form", fd_form);
278 // XtAddCallback (fd_form, XmNhelpCallback, HelpCB, helpId);
281 Widget _fd_labelbox = XtVaCreateManagedWidget ("_fd_labelbox",
282 xmRowColumnWidgetClass,
284 XmNtopAttachment, XmATTACH_FORM,
285 XmNleftAttachment, XmATTACH_POSITION,
286 XmNrightAttachment, XmATTACH_POSITION,
288 XmNrightPosition, 95,
289 XmNpacking, XmPACK_COLUMN,
291 XmNorientation, XmVERTICAL,
293 XmNentryAlignment, XmALIGNMENT_END,
294 XmNentryVerticalAlignment, XmALIGNMENT_CENTER,
296 printHelpId ("_fd_labelbox", _fd_labelbox);
298 // XtAddCallback (_fd_labelbox, XmNhelpCallback, HelpCB, helpId);
301 Widget *_fd_labels = new Widget [_num_text_fields];
304 for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
306 _fd_labels [_fd_i] = XtVaCreateManagedWidget (
307 _text_labels [_fd_i],
312 printHelpId ("_fd_labels [%s]", _fd_labels [_fd_i]);
313 // naturally, this is bogus --must be fixed to return proper label
315 // XtAddCallback(_fd_labels [_fd_i], XmNhelpCallback, HelpCB, helpId);
318 for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
320 _text_fields [_fd_i] = XtVaCreateManagedWidget (
322 xmTextFieldWidgetClass,
325 printHelpId ("_text_fields [%s]", _text_fields [_fd_i]);
326 // naturally, this is bogus --must be fixed to return proper label
328 // XtAddCallback(_text_fields [_fd_i], XmNhelpCallback, HelpCB, helpId);
330 XtAddCallback(_text_fields [_fd_i], XmNactivateCallback,
331 (XtCallbackProc)textFieldCallback, (XtPointer)this);
335 XmString strForward = XmStringCreateLocalized(GETMSG(DT_catd, 1, 193, "Forward"));
336 XmString strBackward = XmStringCreateLocalized(GETMSG(DT_catd, 1, 194, "Backward"));
339 = XmVaCreateSimpleRadioBox(fd_form,
341 0, // Initial selection
344 XmVaRADIOBUTTON, strForward, NULL, NULL, NULL,
345 XmVaRADIOBUTTON, strBackward, NULL, NULL, NULL,
348 XmNtopAttachment, XmATTACH_WIDGET,
349 XmNtopWidget, _fd_labelbox,
350 XmNorientation, XmHORIZONTAL,
351 XmNleftAttachment, XmATTACH_POSITION,
354 printHelpId ("fd_direction", fd_direction);
356 //XtAddCallback (fd_direction, XmNhelpCallback, HelpCB, helpId);
358 XmStringFree(strForward);
359 XmStringFree(strBackward);
362 // Now create the Action Area.
366 register Widget widget;
368 Widget fd_action = XtVaCreateWidget("actionArea",
371 XmNleftAttachment, XmATTACH_FORM,
372 XmNrightAttachment, XmATTACH_FORM,
373 XmNfractionBase, TIGHTNESS * _num_buttons-1,
375 printHelpId ("actionArea", fd_action);
377 //XtAddCallback (fd_action, XmNhelpCallback, HelpCB, helpId);
379 for (offset = 0; offset < _num_buttons; offset++)
380 { widget = XtVaCreateManagedWidget(_buttonData[offset].label,
381 xmPushButtonWidgetClass, fd_action,
384 offset ? XmATTACH_POSITION:XmATTACH_FORM,
386 XmNleftPosition, TIGHTNESS * offset,
387 XmNtopAttachment, XmATTACH_FORM,
390 offset != _num_buttons - 1 ? XmATTACH_POSITION : XmATTACH_FORM,
393 TIGHTNESS * offset + (TIGHTNESS - 1),
395 XmNshowAsDefault, offset == 0,
398 // again, bogus -- doesn't each one need a unique tag?
399 printHelpId ("widget", widget);
401 //XtAddCallback (widget, XmNhelpCallback, HelpCB, helpId);
403 if (_buttonData[offset].callback != NULL) {
404 XtAddCallback(widget, XmNactivateCallback,
405 _buttonData[offset].callback,
406 _buttonData[offset].data);
414 XtVaGetValues(fd_action, XmNmarginHeight, &margin, NULL);
415 XtVaGetValues(widget, XmNheight, &height, NULL);
417 XtVaSetValues(fd_action,
418 XmNdefaultButton, widget,
419 XmNpaneMaximum, height,
420 XmNpaneMinimum, height,
426 _status_text = XtVaCreateManagedWidget("StatusLabel",
427 xmLabelWidgetClass, fd_pane,
428 XmNrightAttachment, XmATTACH_FORM,
429 XmNleftAttachment, XmATTACH_FORM,
430 XmNalignment, XmALIGNMENT_BEGINNING,
434 XtWidgetGeometry size;
436 size.request_mode = CWHeight;
437 XtQueryGeometry(_status_text, NULL, &size);
438 XtVaSetValues(_status_text,
439 XmNpaneMaximum, size.height,
440 XmNpaneMinimum, size.height,
445 XtManageChild (fd_form);
446 XtManageChild (fd_direction);
447 XtManageChild(fd_action);
448 XtManageChild(fd_pane);
450 XtManageChild(dialog);
452 // Make sure get the height of the dialog after it has been
454 XtVaGetValues(dialog, XmNheight, &height, NULL);
455 XtVaSetValues(dialog,
456 XmNmappedWhenManaged, True,
457 XmNminHeight, height,
459 XtRealizeWidget(dialog);
466 // Look for all matching messages.
469 FindDialog::findMatching(Boolean findAll)
471 // TODO - CHECK ERROR!!!
473 unsigned int matchCount = 0;
476 * This string is displayed on the find dialog status line
477 * when searching for a matching message.
480 setStatus(GETMSG(DT_catd, 1, 231, "Searching..."));
482 theRoamApp.busyAllWindows(NULL);
485 // Get the active list.
487 MsgScrollingList * displayList = _roamWindow->list();
490 // Find the max. number of messages that we are to find matching.
492 int numberMessages = displayList->get_num_messages();
495 // Are there any messages?
497 if (numberMessages > 0) {
500 // A pointer to the currently interesting message.
502 register DtMailMessageHandle currentHandle = NULL;
505 // The offset of the currentHandle in the MsgScrollingList.
507 register int handleOffset;
510 // Find the current message. We would always start from the
511 // currently selected message.
513 // Get the handle to the currently displaied message.
515 DtMailMessageHandle initialHandle = displayList->current_msg_handle();
518 // Get the list of DtMailMessageHandle's.
520 MsgHndArray * msgHandles = displayList->get_messages();
523 // Up to all of them can match, allocate and clear the list.
525 DtMailMessageHandle * matchList = NULL;
527 matchList = new DtMailMessageHandle[numberMessages+1];
529 unsigned int matchOffset = 0;
532 // Deselect all messages.
534 XmListDeselectAllItems(displayList->baseWidget());
537 // Start the search from the initially displaied message (+1).
539 handleOffset = displayList->position(initialHandle) - 1;
540 if (_searchForward) {
542 if (handleOffset >= numberMessages) {
547 if (handleOffset < 0) {
548 handleOffset = numberMessages - 1;
552 for (; handleOffset < numberMessages;) {
553 currentHandle = msgHandles->at(handleOffset)->message_handle;
556 // See if this message is a match, if it is...
558 if (compareMessage(currentHandle)) {
562 // If we are finding all, then add to the list.
563 // If not, then display this message and we are done.
566 matchList[matchOffset++] = currentHandle;
568 XmListDeselectAllItems(displayList->baseWidget());
569 //displayList->set_selected_item_position(handleOffset);
570 displayList->display_and_select_message(error, currentHandle);
576 // If we have looped back to the initial
577 // message (handle), then we are done.
579 if (currentHandle == initialHandle) {
584 // Get the next message.
586 // If we have reached the end, start over.
587 // (as if the list was a circular list)
589 // We loop forward (_searchForward == TRUE) else we loop backward.
591 if (_searchForward) {
593 if (handleOffset >= numberMessages) {
598 if (handleOffset < 0) {
599 handleOffset = numberMessages - 1;
602 currentHandle = msgHandles->at(handleOffset)->message_handle;
606 // Select all the messages that match, and display the last
611 displayList->select_all_and_display_last(error, matchList, matchCount);
612 if (matchCount > 0) {
613 char *line = new char[80];
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.
621 if (matchCount == 1) {
622 strcpy(line, GETMSG(DT_catd, 1, 232, "1 message selected"));
624 sprintf(line, GETMSG(DT_catd, 1, 233, "%d messages selected"),
638 theRoamApp.unbusyAllWindows();
639 if (error.isNotSet()) {
640 if (matchCount > 0) {
648 * This string is displayed on the find dialog status line when
649 * no matching messages were found.
651 setStatus(GETMSG(DT_catd, 1, 234, "No matches were found"));
656 FindDialog::compareMessage(DtMailMessageHandle handle)
658 Boolean found = False;
659 register unsigned int offset;
662 // Check for something to do.
664 for (offset = 0; offset < _num_text_fields; offset++) {
665 if (_text_values[offset] != NULL) {
670 // If all fields are empty then we match anything
671 if (offset >= _num_text_fields) {
675 if (offset < _num_text_fields && handle != NULL) {
677 // TODO - CHECK ERROR!!!
683 DtMail::MailBox * mbox = _roamWindow->mailbox();
686 // Get the DtMail::Message and Envelope for this handle.
688 DtMail::Message * message = mbox->getMessage(error, handle);
689 DtMail::Envelope * envelope = message->getEnvelope(error);
692 // Get the meassage header.
694 DtMailValueSeq header;
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],
703 envelope->getHeader(error, _text_names[offset],
707 if (!compareHeader(error, header, _text_values[offset])) {
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.
724 if (offset > _num_text_fields) {
731 #if !defined(CSRG_BASED) && !defined(linux)
733 // See if string 'toFind' is anyware in string 'str'.
734 // A case-insensitive version of strstr().
737 strcasestr(const char *str, const char *toFind)
739 const char *result = NULL; // Default to not found.
741 if (str && toFind) { // Sanity check
742 register int offset = 0;
743 register int lenToFind = strlen(toFind);
744 register int lenStr = strlen(str);
747 // If toFind == "", then return the entire string (like strstr()).
749 if (lenToFind == 0) {
753 // Start at each position in the string and look for
754 // toFind - ignore case.
756 for (offset = 0; offset + lenToFind <= lenStr; offset++) {
757 if (strncasecmp(&str[offset], toFind, lenToFind) == 0) {
758 result = &str[offset];
769 FindDialog::compareHeader(DtMailEnv & error,
770 DtMailValueSeq & header,
771 const char * cmpToString)
773 register int headerOffset = header.length() - 1;
777 while(headerOffset >= 0) {
778 if ((strcasestr(*(header[headerOffset]), cmpToString)) != NULL) {
787 // Pull all fields out of the dialog and store in the class.
790 FindDialog::getAllFields()
792 register unsigned int offset;
794 for (offset = 0; offset < _num_text_fields; offset++) {
795 if (_text_fields[offset] != NULL) {
796 _text_values[offset] = XmTextFieldGetString(_text_fields[offset]);
798 // Ignore zero length strings.
799 if (_text_values[offset] != NULL) {
800 if (strlen(_text_values[offset]) == 0) {
801 _text_values[offset] = NULL;
811 FindDialog::textFieldCallback(
817 FindDialog *findData = (FindDialog *)data;
819 if (*(s = XmTextGetString(field)) == '\0') {
820 // Empty field. Traverse
821 (void) XmProcessTraversal(field, XmTRAVERSE_NEXT_TAB_GROUP);
823 // Field not empty. Do search
824 findData->getAllFields();
825 if (!findData->findMatching(False)) {
826 XBell(XtDisplay(field), 0);
834 FindDialog::directionCallback(Widget widget,
838 int which = (int) ((long) closure); // closure contains button #
841 // Client data is actually on the @!$?@* parent, not the toggle item
842 XtVaGetValues(XtParent(widget), XmNuserData, &find, NULL);
845 find->setSearchForward(TRUE);
847 find->setSearchForward(False);
854 FindDialog::findCallback(Widget /*button*/,
856 XtPointer /*call_data*/)
858 FindDialog *findData = (FindDialog *)closure;
860 findData->getAllFields();
861 findData->findMatching(False);
867 FindDialog::findSelectAllCallback(Widget /*button*/,
869 XtPointer /*call_data*/)
871 FindDialog *findData = (FindDialog *)closure;
873 findData->getAllFields();
874 findData->findMatching(TRUE);
879 FindDialog::clearCallback(Widget /*button*/,
881 XtPointer /*call_data*/)
883 FindDialog *findData = (FindDialog *)closure;
884 register unsigned int offset;
886 for (offset = 0; offset < findData->_num_text_fields; offset++) {
887 if (findData->_text_fields[offset] != NULL) {
888 XmTextFieldSetString(findData->_text_fields[offset], "");
895 FindDialog::closeCallback(Widget /*button*/,
897 XtPointer /*call_data*/)
899 FindDialog *findData = (FindDialog *)closure;