cdesktopenv/cde/programs/dtmail/dtmail/FindDialog.C

905 lines
22 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*
*+SNOTICE
*
* $TOG: FindDialog.C /main/7 1998/07/23 17:59:06 mgreess $
*
* RESTRICTED CONFIDENTIAL INFORMATION:
*
* The information in this document is subject to special
* restrictions in a confidential disclosure agreement between
* HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
* document outside HP, IBM, Sun, USL, SCO, or Univel without
* Sun's specific written approval. This document and all copies
* and derivative works thereof must be returned or destroyed at
* Sun's request.
*
* Copyright 1993 Sun Microsystems, Inc. All rights reserved.
*
*+ENOTICE
*/
#include <assert.h>
#include <X11/Intrinsic.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/MessageB.h>
#include <Xm/TextF.h>
#include <Xm/Label.h>
#include <Xm/PushB.h>
#include <Xm/DialogS.h>
#include <Xm/PanedW.h>
#include <Xm/LabelG.h>
#include <Xm/Text.h>
#include <Xm/SeparatoG.h>
#include <DtMail/DtMail.h>
#include <DtMail/DtMail.hh>
#include "FindDialog.h"
#include "RoamApp.h"
#include "RoamMenuWindow.h"
#include "RoamCmds.h"
#include "Help.hh"
#include "MailMsg.h"
#include <EUSCompat.h>
#include "str_utils.h"
//
// Clear out the data. After this function is complete the
// data should look as if the constructor was just called and
// before initialize().
//
void
FindDialog::clear()
{
unsigned int offset;
//
if (_text_labels != NULL) {
for (offset = 0; offset < _num_text_fields; offset++) {
if (_text_labels[offset] != NULL) {
free(_text_labels[offset]);
}
}
delete _text_labels;
}
//
if (_text_values != NULL) {
for (offset = 0; offset < _num_text_fields; offset++) {
if (_text_values[offset] != NULL) {
free(_text_values[offset]);
}
}
delete _text_values;
}
//
if (_text_abstract_name != NULL) {
for (offset = 0; offset < _num_text_fields; offset++) {
if (_text_abstract_name[offset] != NULL) {
free(_text_abstract_name[offset]);
}
}
delete _text_abstract_name;
}
//
if (_buttonData != NULL) {
for (offset = 0; offset < _num_buttons; offset++) {
if (_buttonData[offset].label != NULL) {
free(_buttonData[offset].label);
}
}
delete _buttonData;
}
if (_text_fields != NULL) {
delete _text_fields;
}
if (_text_names != NULL) {
delete _text_names;
}
}
//
// The only constructor.
//
FindDialog::FindDialog(RoamMenuWindow *parent) : Dialog("find", parent)
{
_roamWindow = parent;
_num_text_fields = 4;
_num_buttons = 5;
_status_text = NULL;
//
// Allocate storage for labels, widgets, and data.
//
_text_labels = new char *[_num_text_fields];
_text_names = new char *[_num_text_fields];
_text_values = new char *[_num_text_fields];
_text_abstract_name = new char *[_num_text_fields];
_text_fields = new Widget[_num_text_fields];
_buttonData = new ActionAreaItem[_num_buttons];
_searchForward = TRUE;
//
// Initialize the buttons.
//
_buttonData[0].label = strdup(CATGETS(DT_catd, 1, 183, "Find"));
_buttonData[0].callback = findCallback;
_buttonData[0].data = (caddr_t) this;
#ifdef NL_OBSOLETE
/*
* NL_COMMENT
* This is an obsolete message. Replaced by message 220 in set 1
*/
_buttonData[1].label = strdup(CATGETS(DT_catd, 1, 184, "Find & Select All"));
#endif
/*
* NL_COMMENT
* This message replaces message 184 in set 1
*/
_buttonData[1].label = strdup(CATGETS(DT_catd, 1, 220, "Select All"));
_buttonData[1].callback = findSelectAllCallback;
_buttonData[1].data = (caddr_t) this;
_buttonData[2].label = strdup(CATGETS(DT_catd, 1, 185, "Clear"));
_buttonData[2].callback = clearCallback;
_buttonData[2].data = (caddr_t) this;
_buttonData[3].label = strdup(CATGETS(DT_catd, 1, 186, "Close"));
_buttonData[3].callback = closeCallback;
_buttonData[3].data = (caddr_t) this;
_buttonData[4].label = strdup(CATGETS(DT_catd, 1, 187, "Help"));
_buttonData[4].callback = HelpCB;
_buttonData[4].data = (caddr_t) DTMAILFINDDIALOG;
_text_labels[0] = strdup(CATGETS(DT_catd, 1, 188, "To:"));
_text_labels[1] = strdup(CATGETS(DT_catd, 1, 189, "From:"));
_text_labels[2] = strdup(CATGETS(DT_catd, 1, 190, "Subject:"));
_text_labels[3] = strdup(CATGETS(DT_catd, 1, 191, "Cc:"));
// These strings should not be translated. They are
// the Motif names for the widgets that will be created (they are
// not the labels).
_text_names[0] = "To";
_text_names[1] = "From";
_text_names[2] = "Subject";
_text_names[3] = "Cc";
//
// Initialize the names of the fields to the abstract
// names used by libDtMail.
//
_text_abstract_name[0] = strdup(DtMailMessageTo);
_text_abstract_name[1] = strdup(DtMailMessageSender);
_text_abstract_name[2] = strdup(DtMailMessageSubject);
_text_abstract_name[3] = strdup(DtMailMessageCc);
}
//
// Print a string in the status line of the find dialog.
//
void
FindDialog::setStatus(const char * str)
{
char *tmpstr = strdup(str);
XmString label = XmStringCreateLocalized(tmpstr);
XtVaSetValues(_status_text,
XmNlabelString, label,
NULL);
XmUpdateDisplay(baseWidget());
XmStringFree(label);
}
//
// Clear the status line of the find dialog.
//
void
FindDialog::clearStatus(void)
{
setStatus(" ");
}
//
// Create the guts of the dialog
//
Widget
FindDialog::createWorkArea(Widget dialog)
{
// TODO - CHECK ERROR!!!
Widget *label = new Widget[_num_text_fields];
unsigned int offset;
_name = CATGETS(DT_catd, 1, 192, "Mailer - Find");
title(_name);
// make this a modal dialog
/*
XtVaSetValues (dialog,
XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
NULL);
*/
printHelpId("dialog", dialog);
/* add help callback */
// XtAddCallback(dialog, XmNhelpCallback, HelpCB, helpId);
Widget fd_pane = XtVaCreateWidget ("fd_pane",
xmPanedWindowWidgetClass,
dialog,
XmNsashWidth, 1,
XmNsashHeight, 1,
NULL);
printHelpId ("fd_pane", fd_pane);
// add help callback
// XtAddCallback (fd_pane, XmNhelpCallback, HelpCB, helpId);
Widget fd_form = XtVaCreateWidget ("fd_form",
xmFormWidgetClass,
fd_pane,
XmNfractionBase, 100,
NULL);
printHelpId ("fd_form", fd_form);
// add help callback
// XtAddCallback (fd_form, XmNhelpCallback, HelpCB, helpId);
Widget _fd_labelbox = XtVaCreateManagedWidget ("_fd_labelbox",
xmRowColumnWidgetClass,
fd_form,
XmNtopAttachment, XmATTACH_FORM,
XmNleftAttachment, XmATTACH_POSITION,
XmNrightAttachment, XmATTACH_POSITION,
XmNleftPosition, 5,
XmNrightPosition, 95,
XmNpacking, XmPACK_COLUMN,
XmNnumColumns, 2,
XmNorientation, XmVERTICAL,
XmNisAligned, True,
XmNentryAlignment, XmALIGNMENT_END,
XmNentryVerticalAlignment, XmALIGNMENT_CENTER,
NULL);
printHelpId ("_fd_labelbox", _fd_labelbox);
// add help callback
// XtAddCallback (_fd_labelbox, XmNhelpCallback, HelpCB, helpId);
Widget *_fd_labels = new Widget [_num_text_fields];
int _fd_i = 0;
for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
{
_fd_labels [_fd_i] = XtVaCreateManagedWidget (
_text_labels [_fd_i],
xmLabelGadgetClass,
_fd_labelbox,
NULL);
printHelpId ("_fd_labels [%s]", _fd_labels [_fd_i]);
// naturally, this is bogus --must be fixed to return proper label
// add help callback
// XtAddCallback(_fd_labels [_fd_i], XmNhelpCallback, HelpCB, helpId);
}
for (_fd_i = 0; _fd_i < _num_text_fields; _fd_i++)
{
_text_fields [_fd_i] = XtVaCreateManagedWidget (
_text_names [_fd_i],
xmTextFieldWidgetClass,
_fd_labelbox,
NULL);
printHelpId ("_text_fields [%s]", _text_fields [_fd_i]);
// naturally, this is bogus --must be fixed to return proper label
// add help callback
// XtAddCallback(_text_fields [_fd_i], XmNhelpCallback, HelpCB, helpId);
XtAddCallback(_text_fields [_fd_i], XmNactivateCallback,
(XtCallbackProc)textFieldCallback, (XtPointer)this);
}
XmString strForward = XmStringCreateLocalized(CATGETS(DT_catd, 1, 193, "Forward"));
XmString strBackward = XmStringCreateLocalized(CATGETS(DT_catd, 1, 194, "Backward"));
Widget fd_direction
= XmVaCreateSimpleRadioBox(fd_form,
"Direction",
0, // Initial selection
directionCallback,
//NULL,
XmVaRADIOBUTTON, strForward, NULL, NULL, NULL,
XmVaRADIOBUTTON, strBackward, NULL, NULL, NULL,
XmNuserData, this,
XmNsensitive, True,
XmNtopAttachment, XmATTACH_WIDGET,
XmNtopWidget, _fd_labelbox,
XmNorientation, XmHORIZONTAL,
XmNleftAttachment, XmATTACH_POSITION,
XmNleftPosition, 33,
NULL);
printHelpId ("fd_direction", fd_direction);
// add help callback
//XtAddCallback (fd_direction, XmNhelpCallback, HelpCB, helpId);
XmStringFree(strForward);
XmStringFree(strBackward);
//
// Now create the Action Area.
//
#define TIGHTNESS 20
Widget widget;
Widget fd_action = XtVaCreateWidget("actionArea",
xmFormWidgetClass,
fd_pane,
XmNleftAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNfractionBase, TIGHTNESS * _num_buttons-1,
NULL);
printHelpId ("actionArea", fd_action);
// add help callback
//XtAddCallback (fd_action, XmNhelpCallback, HelpCB, helpId);
for (offset = 0; offset < _num_buttons; offset++)
{ widget = XtVaCreateManagedWidget(_buttonData[offset].label,
xmPushButtonWidgetClass, fd_action,
XmNleftAttachment,
offset ? XmATTACH_POSITION:XmATTACH_FORM,
XmNleftPosition, TIGHTNESS * offset,
XmNtopAttachment, XmATTACH_FORM,
XmNrightAttachment,
offset != _num_buttons - 1 ? XmATTACH_POSITION : XmATTACH_FORM,
XmNrightPosition,
TIGHTNESS * offset + (TIGHTNESS - 1),
XmNshowAsDefault, offset == 0,
NULL);
// again, bogus -- doesn't each one need a unique tag?
printHelpId ("widget", widget);
// add help callback
//XtAddCallback (widget, XmNhelpCallback, HelpCB, helpId);
if (_buttonData[offset].callback != NULL) {
XtAddCallback(widget, XmNactivateCallback,
_buttonData[offset].callback,
_buttonData[offset].data);
}
if (offset == 0) {
Dimension height;
Dimension margin;
XtVaGetValues(fd_action, XmNmarginHeight, &margin, NULL);
XtVaGetValues(widget, XmNheight, &height, NULL);
height +=2 * margin;
XtVaSetValues(fd_action,
XmNdefaultButton, widget,
XmNpaneMaximum, height,
XmNpaneMinimum, height,
NULL);
}
}
_status_text = XtVaCreateManagedWidget("StatusLabel",
xmLabelWidgetClass, fd_pane,
XmNrightAttachment, XmATTACH_FORM,
XmNleftAttachment, XmATTACH_FORM,
XmNalignment, XmALIGNMENT_BEGINNING,
NULL);
Dimension height;
XtWidgetGeometry size;
size.request_mode = CWHeight;
XtQueryGeometry(_status_text, NULL, &size);
XtVaSetValues(_status_text,
XmNpaneMaximum, size.height,
XmNpaneMinimum, size.height,
NULL);
clearStatus();
XtManageChild (fd_form);
XtManageChild (fd_direction);
XtManageChild(fd_action);
XtManageChild(fd_pane);
XtManageChild(dialog);
// Make sure get the height of the dialog after it has been
// managed.
XtVaGetValues(dialog, XmNheight, &height, NULL);
XtVaSetValues(dialog,
XmNmappedWhenManaged, True,
XmNminHeight, height,
NULL);
XtRealizeWidget(dialog);
return (fd_pane);
}
//
// Look for all matching messages.
//
Boolean
FindDialog::findMatching(Boolean findAll)
{
// TODO - CHECK ERROR!!!
DtMailEnv error;
unsigned int matchCount = 0;
/* NL_COMMENT
* This string is displayed on the find dialog status line
* when searching for a matching message.
*/
setStatus(CATGETS(DT_catd, 1, 231, "Searching..."));
busyCursor();
theRoamApp.busyAllWindows(NULL);
//
// Get the active list.
//
MsgScrollingList * displayList = _roamWindow->list();
//
// Find the max. number of messages that we are to find matching.
//
int numberMessages = displayList->get_num_messages();
//
// Are there any messages?
//
if (numberMessages > 0) {
//
// A pointer to the currently interesting message.
//
DtMailMessageHandle currentHandle = NULL;
//
// The offset of the currentHandle in the MsgScrollingList.
//
int handleOffset;
//
// Find the current message. We would always start from the
// currently selected message.
//
// Get the handle to the currently displaied message.
//
DtMailMessageHandle initialHandle = displayList->current_msg_handle();
//
// Get the list of DtMailMessageHandle's.
MsgHndArray * msgHandles = displayList->get_messages();
//
// Up to all of them can match, allocate and clear the list.
//
DtMailMessageHandle * matchList = NULL;
if (findAll) {
matchList = new DtMailMessageHandle[numberMessages+1];
}
unsigned int matchOffset = 0;
//
// Deselect all messages.
//
XmListDeselectAllItems(displayList->baseWidget());
//
// Start the search from the initially displaied message (+1).
//
handleOffset = displayList->position(initialHandle) - 1;
if (_searchForward) {
handleOffset++;
if (handleOffset >= numberMessages) {
handleOffset = 0;
}
} else {
handleOffset--;
if (handleOffset < 0) {
handleOffset = numberMessages - 1;
}
}
for (; handleOffset < numberMessages;) {
currentHandle = msgHandles->at(handleOffset)->message_handle;
//
// See if this message is a match, if it is...
//
if (compareMessage(currentHandle)) {
matchCount++;
//
// If we are finding all, then add to the list.
// If not, then display this message and we are done.
//
if (findAll) {
matchList[matchOffset++] = currentHandle;
} else {
XmListDeselectAllItems(displayList->baseWidget());
//displayList->set_selected_item_position(handleOffset);
displayList->display_and_select_message(error, currentHandle);
break; // Only one.
}
}
//
// If we have looped back to the initial
// message (handle), then we are done.
//
if (currentHandle == initialHandle) {
break;
}
//
// Get the next message.
//
// If we have reached the end, start over.
// (as if the list was a circular list)
//
// We loop forward (_searchForward == TRUE) else we loop backward.
//
if (_searchForward) {
handleOffset++;
if (handleOffset >= numberMessages) {
handleOffset = 0;
}
} else {
handleOffset--;
if (handleOffset < 0) {
handleOffset = numberMessages - 1;
}
}
currentHandle = msgHandles->at(handleOffset)->message_handle;
}
//
// Select all the messages that match, and display the last
// one in the list.
//
if (findAll) {
displayList->select_all_and_display_last(error, matchList, matchCount);
if (matchCount > 0) {
char *line = new char[80];
/* NL_COMMENT
* These strings are displayed on the find dialog status line
* when one or more matching messages are found. The first
* string is displayed when there is one matching message,
* and the second string is displayed when there is more than
* one. The %d is the number of messages that matched.
*/
if (matchCount == 1) {
strcpy(line, CATGETS(DT_catd, 1, 232, "1 message selected"));
} else {
sprintf(line, CATGETS(DT_catd, 1, 233, "%d messages selected"),
matchCount);
}
setStatus(line);
delete [] line;
}
// Clean up.
delete matchList;
matchList = NULL;
}
}
normalCursor();
theRoamApp.unbusyAllWindows();
if (error.isNotSet()) {
if (matchCount > 0) {
if (!findAll) {
clearStatus();
}
return(TRUE);
}
}
/* NL_COMMENT
* This string is displayed on the find dialog status line when
* no matching messages were found.
*/
setStatus(CATGETS(DT_catd, 1, 234, "No matches were found"));
return(False);
}
Boolean
FindDialog::compareMessage(DtMailMessageHandle handle)
{
Boolean found = False;
unsigned int offset;
//
// Check for something to do.
//
for (offset = 0; offset < _num_text_fields; offset++) {
if (_text_values[offset] != NULL) {
break;
}
}
// If all fields are empty then we match anything
if (offset >= _num_text_fields) {
return TRUE;
}
if (offset < _num_text_fields && handle != NULL) {
// TODO - CHECK ERROR!!!
DtMailEnv error;
//
// Get the mail box.
//
DtMail::MailBox * mbox = _roamWindow->mailbox();
//
// Get the DtMail::Message and Envelope for this handle.
//
DtMail::Message * message = mbox->getMessage(error, handle);
DtMail::Envelope * envelope = message->getEnvelope(error);
//
// Get the meassage header.
//
DtMailValueSeq header;
for (offset = 0; offset < _num_text_fields; offset++) {
if (_text_values[offset] != NULL) {
if (_text_abstract_name[offset] != NULL) {
envelope->getHeader(error, _text_abstract_name[offset],
DTM_TRUE, header);
found = TRUE;
} else {
envelope->getHeader(error, _text_names[offset],
DTM_FALSE, header);
found = TRUE;
}
if (!compareHeader(error, header, _text_values[offset])) {
found = False;
break;
}
else {
// Problem: if we have multiple search fields ... and use
// the same "header" array ... "compareHeader" looks for
// each "find" field in each (available) header field.
// So, make sure only one is available for searching.
// Really "offset" should be passed to "compareHeader" ...
// That way, correct comparison can be done and the
// memory for this array can be released correctly via the
// destructor .... since "remove" fails to do so.
header.remove(0);
}
}
}
if (offset > _num_text_fields) {
found = TRUE;
}
}
return(found);
}
#if !defined(CSRG_BASED) && !defined(__linux__)
//
// See if string 'toFind' is anyware in string 'str'.
// A case-insensitive version of strstr().
//
const char *
FindDialog::strcasestr(const char *str, const char *toFind)
{
const char *result = NULL; // Default to not found.
if (str && toFind) { // Sanity check
int offset = 0;
int lenToFind = strlen(toFind);
int lenStr = strlen(str);
//
// If toFind == "", then return the entire string (like strstr()).
//
if (lenToFind == 0) {
result = str;
} else {
//
// Start at each position in the string and look for
// toFind - ignore case.
//
for (offset = 0; offset + lenToFind <= lenStr; offset++) {
if (strncasecmp(&str[offset], toFind, lenToFind) == 0) {
result = &str[offset];
break;
}
}
}
}
return(result);
}
#endif
Boolean
FindDialog::compareHeader(DtMailEnv & error,
DtMailValueSeq & header,
const char * cmpToString)
{
int headerOffset = header.length() - 1;
error.clear();
while(headerOffset >= 0) {
if ((strcasestr(*(header[headerOffset]), cmpToString)) != NULL) {
return(TRUE);
}
headerOffset--;
}
return(False);
}
//
// Pull all fields out of the dialog and store in the class.
//
void
FindDialog::getAllFields()
{
unsigned int offset;
for (offset = 0; offset < _num_text_fields; offset++) {
if (_text_fields[offset] != NULL) {
_text_values[offset] = XmTextFieldGetString(_text_fields[offset]);
// Ignore zero length strings.
if (_text_values[offset] != NULL) {
if (strlen(_text_values[offset]) == 0) {
_text_values[offset] = NULL;
}
}
}
}
return;
}
void
FindDialog::textFieldCallback(
Widget field,
XtPointer data,
XtPointer)
{
char *s;
FindDialog *findData = (FindDialog *)data;
if (*(s = XmTextGetString(field)) == '\0') {
// Empty field. Traverse
(void) XmProcessTraversal(field, XmTRAVERSE_NEXT_TAB_GROUP);
} else {
// Field not empty. Do search
findData->getAllFields();
if (!findData->findMatching(False)) {
XBell(XtDisplay(field), 0);
}
}
return;
}
void
FindDialog::directionCallback(Widget widget,
XtPointer closure,
XtPointer)
{
int which = (int) ((long) closure); // closure contains button #
FindDialog *find;
// Client data is actually on the @!$?@* parent, not the toggle item
XtVaGetValues(XtParent(widget), XmNuserData, &find, NULL);
if (which == 0) {
find->setSearchForward(TRUE);
} else {
find->setSearchForward(False);
}
return;
}
void
FindDialog::findCallback(Widget /*button*/,
XtPointer closure,
XtPointer /*call_data*/)
{
FindDialog *findData = (FindDialog *)closure;
findData->getAllFields();
findData->findMatching(False);
return;
}
void
FindDialog::findSelectAllCallback(Widget /*button*/,
XtPointer closure,
XtPointer /*call_data*/)
{
FindDialog *findData = (FindDialog *)closure;
findData->getAllFields();
findData->findMatching(TRUE);
return;
}
void
FindDialog::clearCallback(Widget /*button*/,
XtPointer closure,
XtPointer /*call_data*/)
{
FindDialog *findData = (FindDialog *)closure;
unsigned int offset;
for (offset = 0; offset < findData->_num_text_fields; offset++) {
if (findData->_text_fields[offset] != NULL) {
XmTextFieldSetString(findData->_text_fields[offset], "");
}
}
return;
}
void
FindDialog::closeCallback(Widget /*button*/,
XtPointer closure,
XtPointer /*call_data*/)
{
FindDialog *findData = (FindDialog *)closure;
findData->popdown();
return;
}