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 libraries 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;
139 // Allocate storage for labels, widgets, and data.
141 _text_labels = new char *[_num_text_fields];
142 _text_names = new char *[_num_text_fields];
143 _text_values = new char *[_num_text_fields];
144 _text_abstract_name = new char *[_num_text_fields];
145 _text_fields = new Widget[_num_text_fields];
146 _buttonData = new ActionAreaItem[_num_buttons];
147 _searchForward = TRUE;
150 // Initialize the buttons.
152 _buttonData[0].label = strdup(GETMSG(DT_catd, 1, 183, "Find"));
153 _buttonData[0].callback = findCallback;
154 _buttonData[0].data = (caddr_t) this;
159 * This is an obsolete message. Replaced by message 220 in set 1
161 _buttonData[1].label = strdup(GETMSG(DT_catd, 1, 184, "Find & Select All"));
165 * This message replaces message 184 in set 1
167 _buttonData[1].label = strdup(GETMSG(DT_catd, 1, 220, "Select All"));
168 _buttonData[1].callback = findSelectAllCallback;
169 _buttonData[1].data = (caddr_t) this;
171 _buttonData[2].label = strdup(GETMSG(DT_catd, 1, 185, "Clear"));
172 _buttonData[2].callback = clearCallback;
173 _buttonData[2].data = (caddr_t) this;
175 _buttonData[3].label = strdup(GETMSG(DT_catd, 1, 186, "Close"));
176 _buttonData[3].callback = closeCallback;
177 _buttonData[3].data = (caddr_t) this;
179 _buttonData[4].label = strdup(GETMSG(DT_catd, 1, 187, "Help"));
180 _buttonData[4].callback = HelpCB;
181 _buttonData[4].data = (caddr_t) DTMAILFINDDIALOG;
183 _text_labels[0] = strdup(GETMSG(DT_catd, 1, 188, "To:"));
184 _text_labels[1] = strdup(GETMSG(DT_catd, 1, 189, "From:"));
185 _text_labels[2] = strdup(GETMSG(DT_catd, 1, 190, "Subject:"));
186 _text_labels[3] = strdup(GETMSG(DT_catd, 1, 191, "Cc:"));
188 // These strings should not be translated. They are
189 // the Motif names for the widgets that will be created (they are
191 _text_names[0] = "To";
192 _text_names[1] = "From";
193 _text_names[2] = "Subject";
194 _text_names[3] = "Cc";
197 // Initialize the names of the fields to the abstract
198 // names used by libDtMail.
200 _text_abstract_name[0] = strdup(DtMailMessageTo);
201 _text_abstract_name[1] = strdup(DtMailMessageSender);
202 _text_abstract_name[2] = strdup(DtMailMessageSubject);
203 _text_abstract_name[3] = strdup(DtMailMessageCc);
207 // Print a string in the status line of the find dialog.
210 FindDialog::setStatus(const char * str)
212 char *tmpstr = strdup(str);
213 XmString label = XmStringCreateLocalized(tmpstr);
215 XtVaSetValues(_status_text,
216 XmNlabelString, label,
219 XmUpdateDisplay(baseWidget());
224 // Clear the status line of the find dialog.
227 FindDialog::clearStatus(void)
233 // Create the guts of the dialog
236 FindDialog::createWorkArea(Widget dialog)
238 // TODO - CHECK ERROR!!!
239 Widget *label = new Widget[_num_text_fields];
242 register unsigned int offset;
244 _name = GETMSG(DT_catd, 1, 192, "Mailer - Find");
248 // make this a modal dialog
250 XtVaSetValues (dialog,
251 XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
255 printHelpId("dialog", dialog);
257 /* add help callback */
258 // XtAddCallback(dialog, XmNhelpCallback, HelpCB, helpId);
260 Widget fd_pane = XtVaCreateWidget ("fd_pane",
261 xmPanedWindowWidgetClass,
267 printHelpId ("fd_pane", fd_pane);
269 // XtAddCallback (fd_pane, XmNhelpCallback, HelpCB, helpId);
271 Widget fd_form = XtVaCreateWidget ("fd_form",
274 XmNfractionBase, 100,
277 printHelpId ("fd_form", fd_form);
279 // XtAddCallback (fd_form, XmNhelpCallback, HelpCB, helpId);
282 Widget _fd_labelbox = XtVaCreateManagedWidget ("_fd_labelbox",
283 xmRowColumnWidgetClass,
285 XmNtopAttachment, XmATTACH_FORM,
286 XmNleftAttachment, XmATTACH_POSITION,
287 XmNrightAttachment, XmATTACH_POSITION,
289 XmNrightPosition, 95,
290 XmNpacking, XmPACK_COLUMN,
292 XmNorientation, XmVERTICAL,
294 XmNentryAlignment, XmALIGNMENT_END,
295 XmNentryVerticalAlignment, XmALIGNMENT_CENTER,
297 printHelpId ("_fd_labelbox", _fd_labelbox);
299 // XtAddCallback (_fd_labelbox, XmNhelpCallback, HelpCB, helpId);
302 Widget *_fd_labels = new Widget [_num_text_fields];
305 for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
307 _fd_labels [_fd_i] = XtVaCreateManagedWidget (
308 _text_labels [_fd_i],
313 printHelpId ("_fd_labels [%s]", _fd_labels [_fd_i]);
314 // naturally, this is bogus --must be fixed to return proper label
316 // XtAddCallback(_fd_labels [_fd_i], XmNhelpCallback, HelpCB, helpId);
319 for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
321 _text_fields [_fd_i] = XtVaCreateManagedWidget (
323 xmTextFieldWidgetClass,
326 printHelpId ("_text_fields [%s]", _text_fields [_fd_i]);
327 // naturally, this is bogus --must be fixed to return proper label
329 // XtAddCallback(_text_fields [_fd_i], XmNhelpCallback, HelpCB, helpId);
331 XtAddCallback(_text_fields [_fd_i], XmNactivateCallback,
332 (XtCallbackProc)textFieldCallback, (XtPointer)this);
336 XmString strForward = XmStringCreateLocalized(GETMSG(DT_catd, 1, 193, "Forward"));
337 XmString strBackward = XmStringCreateLocalized(GETMSG(DT_catd, 1, 194, "Backward"));
340 = XmVaCreateSimpleRadioBox(fd_form,
342 0, // Initial selection
345 XmVaRADIOBUTTON, strForward, NULL, NULL, NULL,
346 XmVaRADIOBUTTON, strBackward, NULL, NULL, NULL,
349 XmNtopAttachment, XmATTACH_WIDGET,
350 XmNtopWidget, _fd_labelbox,
351 XmNorientation, XmHORIZONTAL,
352 XmNleftAttachment, XmATTACH_POSITION,
355 printHelpId ("fd_direction", fd_direction);
357 //XtAddCallback (fd_direction, XmNhelpCallback, HelpCB, helpId);
359 XmStringFree(strForward);
360 XmStringFree(strBackward);
363 // Now create the Action Area.
367 register Widget widget;
369 Widget fd_action = XtVaCreateWidget("actionArea",
372 XmNleftAttachment, XmATTACH_FORM,
373 XmNrightAttachment, XmATTACH_FORM,
374 XmNfractionBase, TIGHTNESS * _num_buttons-1,
376 printHelpId ("actionArea", fd_action);
378 //XtAddCallback (fd_action, XmNhelpCallback, HelpCB, helpId);
380 for (offset = 0; offset < _num_buttons; offset++)
381 { widget = XtVaCreateManagedWidget(_buttonData[offset].label,
382 xmPushButtonWidgetClass, fd_action,
385 offset ? XmATTACH_POSITION:XmATTACH_FORM,
387 XmNleftPosition, TIGHTNESS * offset,
388 XmNtopAttachment, XmATTACH_FORM,
391 offset != _num_buttons - 1 ? XmATTACH_POSITION : XmATTACH_FORM,
394 TIGHTNESS * offset + (TIGHTNESS - 1),
396 XmNshowAsDefault, offset == 0,
399 // again, bogus -- doesn't each one need a unique tag?
400 printHelpId ("widget", widget);
402 //XtAddCallback (widget, XmNhelpCallback, HelpCB, helpId);
404 if (_buttonData[offset].callback != NULL) {
405 XtAddCallback(widget, XmNactivateCallback,
406 _buttonData[offset].callback,
407 _buttonData[offset].data);
415 XtVaGetValues(fd_action, XmNmarginHeight, &margin, NULL);
416 XtVaGetValues(widget, XmNheight, &height, NULL);
418 XtVaSetValues(fd_action,
419 XmNdefaultButton, widget,
420 XmNpaneMaximum, height,
421 XmNpaneMinimum, height,
427 _status_text = XtVaCreateManagedWidget("StatusLabel",
428 xmLabelWidgetClass, fd_pane,
429 XmNrightAttachment, XmATTACH_FORM,
430 XmNleftAttachment, XmATTACH_FORM,
431 XmNalignment, XmALIGNMENT_BEGINNING,
435 XtWidgetGeometry size;
437 size.request_mode = CWHeight;
438 XtQueryGeometry(_status_text, NULL, &size);
439 XtVaSetValues(_status_text,
440 XmNpaneMaximum, size.height,
441 XmNpaneMinimum, size.height,
446 XtManageChild (fd_form);
447 XtManageChild (fd_direction);
448 XtManageChild(fd_action);
449 XtManageChild(fd_pane);
451 XtManageChild(dialog);
453 // Make sure get the height of the dialog after it has been
455 XtVaGetValues(dialog, XmNheight, &height, NULL);
456 XtVaSetValues(dialog,
457 XmNmappedWhenManaged, True,
458 XmNminHeight, height,
460 XtRealizeWidget(dialog);
467 // Look for all matching messages.
470 FindDialog::findMatching(Boolean findAll)
472 // TODO - CHECK ERROR!!!
474 unsigned int matchCount = 0;
477 * This string is displayed on the find dialog status line
478 * when searching for a matching message.
481 setStatus(GETMSG(DT_catd, 1, 231, "Searching..."));
483 theRoamApp.busyAllWindows(NULL);
486 // Get the active list.
488 MsgScrollingList * displayList = _roamWindow->list();
491 // Find the max. number of messages that we are to find matching.
493 int numberMessages = displayList->get_num_messages();
496 // Are there any messages?
498 if (numberMessages > 0) {
501 // A pointer to the currently interesting message.
503 register DtMailMessageHandle currentHandle = NULL;
506 // The offset of the currentHandle in the MsgScrollingList.
508 register int handleOffset;
511 // Find the current message. We would always start from the
512 // currently selected message.
514 // Get the handle to the currently displaied message.
516 DtMailMessageHandle initialHandle = displayList->current_msg_handle();
519 // Get the list of DtMailMessageHandle's.
521 MsgHndArray * msgHandles = displayList->get_messages();
524 // Up to all of them can match, allocate and clear the list.
526 DtMailMessageHandle * matchList = NULL;
528 matchList = new DtMailMessageHandle[numberMessages+1];
530 unsigned int matchOffset = 0;
533 // Deselect all messages.
535 XmListDeselectAllItems(displayList->baseWidget());
538 // Start the search from the initially displaied message (+1).
540 handleOffset = displayList->position(initialHandle) - 1;
541 if (_searchForward) {
543 if (handleOffset >= numberMessages) {
548 if (handleOffset < 0) {
549 handleOffset = numberMessages - 1;
553 for (; handleOffset < numberMessages;) {
554 currentHandle = msgHandles->at(handleOffset)->message_handle;
557 // See if this message is a match, if it is...
559 if (compareMessage(currentHandle)) {
563 // If we are finding all, then add to the list.
564 // If not, then display this message and we are done.
567 matchList[matchOffset++] = currentHandle;
569 XmListDeselectAllItems(displayList->baseWidget());
570 //displayList->set_selected_item_position(handleOffset);
571 displayList->display_and_select_message(error, currentHandle);
577 // If we have looped back to the initial
578 // message (handle), then we are done.
580 if (currentHandle == initialHandle) {
585 // Get the next message.
587 // If we have reached the end, start over.
588 // (as if the list was a circular list)
590 // We loop forward (_searchForward == TRUE) else we loop backward.
592 if (_searchForward) {
594 if (handleOffset >= numberMessages) {
599 if (handleOffset < 0) {
600 handleOffset = numberMessages - 1;
603 currentHandle = msgHandles->at(handleOffset)->message_handle;
607 // Select all the messages that match, and display the last
612 displayList->select_all_and_display_last(error, matchList, matchCount);
613 if (matchCount > 0) {
614 char *line = new char[80];
616 * These strings are displayed on the find dialog status line
617 * when one or more matching messages are found. The first
618 * string is displayed when there is one matching message,
619 * and the second string is displayed when there is more than
620 * one. The %d is the number of messages that matched.
622 if (matchCount == 1) {
623 strcpy(line, GETMSG(DT_catd, 1, 232, "1 message selected"));
625 sprintf(line, GETMSG(DT_catd, 1, 233, "%d messages selected"),
639 theRoamApp.unbusyAllWindows();
640 if (error.isNotSet()) {
641 if (matchCount > 0) {
649 * This string is displayed on the find dialog status line when
650 * no matching messages were found.
652 setStatus(GETMSG(DT_catd, 1, 234, "No matches were found"));
657 FindDialog::compareMessage(DtMailMessageHandle handle)
659 Boolean found = False;
660 register unsigned int offset;
663 // Check for something to do.
665 for (offset = 0; offset < _num_text_fields; offset++) {
666 if (_text_values[offset] != NULL) {
671 // If all fields are empty then we match anything
672 if (offset >= _num_text_fields) {
676 if (offset < _num_text_fields && handle != NULL) {
678 // TODO - CHECK ERROR!!!
684 DtMail::MailBox * mbox = _roamWindow->mailbox();
687 // Get the DtMail::Message and Envelope for this handle.
689 DtMail::Message * message = mbox->getMessage(error, handle);
690 DtMail::Envelope * envelope = message->getEnvelope(error);
693 // Get the meassage header.
695 DtMailValueSeq header;
697 for (offset = 0; offset < _num_text_fields; offset++) {
698 if (_text_values[offset] != NULL) {
699 if (_text_abstract_name[offset] != NULL) {
700 envelope->getHeader(error, _text_abstract_name[offset],
704 envelope->getHeader(error, _text_names[offset],
708 if (!compareHeader(error, header, _text_values[offset])) {
713 // Problem: if we have multiple search fields ... and use
714 // the same "header" array ... "compareHeader" looks for
715 // each "find" field in each (available) header field.
716 // So, make sure only one is available for searching.
717 // Really "offset" should be passed to "compareHeader" ...
718 // That way, correct comparison can be done and the
719 // memory for this array can be released correctly via the
720 // destructor .... since "remove" fails to do so.
725 if (offset > _num_text_fields) {
732 #if !defined(CSRG_BASED) && !defined(linux) && !defined(sun)
734 // See if string 'toFind' is anyware in string 'str'.
735 // A case-insensitive version of strstr().
738 strcasestr(const char *str, const char *toFind)
740 const char *result = NULL; // Default to not found.
742 if (str && toFind) { // Sanity check
743 register int offset = 0;
744 register int lenToFind = strlen(toFind);
745 register int lenStr = strlen(str);
748 // If toFind == "", then return the entire string (like strstr()).
750 if (lenToFind == 0) {
754 // Start at each position in the string and look for
755 // toFind - ignore case.
757 for (offset = 0; offset + lenToFind <= lenStr; offset++) {
758 if (strncasecmp(&str[offset], toFind, lenToFind) == 0) {
759 result = &str[offset];
770 FindDialog::compareHeader(DtMailEnv & error,
771 DtMailValueSeq & header,
772 const char * cmpToString)
774 register int headerOffset = header.length() - 1;
778 while(headerOffset >= 0) {
779 if ((strcasestr(*(header[headerOffset]), cmpToString)) != NULL) {
788 // Pull all fields out of the dialog and store in the class.
791 FindDialog::getAllFields()
793 register unsigned int offset;
795 for (offset = 0; offset < _num_text_fields; offset++) {
796 if (_text_fields[offset] != NULL) {
797 _text_values[offset] = XmTextFieldGetString(_text_fields[offset]);
799 // Ignore zero length strings.
800 if (_text_values[offset] != NULL) {
801 if (strlen(_text_values[offset]) == 0) {
802 _text_values[offset] = NULL;
812 FindDialog::textFieldCallback(
818 FindDialog *findData = (FindDialog *)data;
820 if (*(s = XmTextGetString(field)) == '\0') {
821 // Empty field. Traverse
822 (void) XmProcessTraversal(field, XmTRAVERSE_NEXT_TAB_GROUP);
824 // Field not empty. Do search
825 findData->getAllFields();
826 if (!findData->findMatching(False)) {
827 XBell(XtDisplay(field), 0);
835 FindDialog::directionCallback(Widget widget,
839 int which = (int) ((long) closure); // closure contains button #
842 // Client data is actually on the @!$?@* parent, not the toggle item
843 XtVaGetValues(XtParent(widget), XmNuserData, &find, NULL);
846 find->setSearchForward(TRUE);
848 find->setSearchForward(False);
855 FindDialog::findCallback(Widget /*button*/,
857 XtPointer /*call_data*/)
859 FindDialog *findData = (FindDialog *)closure;
861 findData->getAllFields();
862 findData->findMatching(False);
868 FindDialog::findSelectAllCallback(Widget /*button*/,
870 XtPointer /*call_data*/)
872 FindDialog *findData = (FindDialog *)closure;
874 findData->getAllFields();
875 findData->findMatching(TRUE);
880 FindDialog::clearCallback(Widget /*button*/,
882 XtPointer /*call_data*/)
884 FindDialog *findData = (FindDialog *)closure;
885 register unsigned int offset;
887 for (offset = 0; offset < findData->_num_text_fields; offset++) {
888 if (findData->_text_fields[offset] != NULL) {
889 XmTextFieldSetString(findData->_text_fields[offset], "");
896 FindDialog::closeCallback(Widget /*button*/,
898 XtPointer /*call_data*/)
900 FindDialog *findData = (FindDialog *)closure;