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: SendMsgDialog.C /main/43 1999/03/25 13:42:29 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, 1994, 1995 Sun Microsystems, Inc. All rights reserved.
43 #include <EUSCompat.h>
44 #include "AttachArea.h"
45 #include "Attachment.h"
47 #include "SendMsgDialog.h"
50 #include "ComposeCmds.hh"
53 #include "ButtonInterface.h"
54 #include "MemUtils.hh"
56 #include "DtMailHelp.hh"
57 #include "SelectFileCmd.h"
58 #include "DtMailGenDialog.hh"
59 #include "Application.h"
60 #include <X11/Intrinsic.h>
61 #include <X11/IntrinsicP.h>
62 #include <Xm/RepType.h>
63 #include <Xm/ScrolledW.h>
68 #include <Xm/RowColumn.h>
70 #include <Xm/CascadeB.h>
71 #include <Xm/SeparatoG.h>
72 #include <DtMail/IO.hh>
73 #include <DtMail/DtMailP.hh>
76 #include <sys/param.h>
82 #include "str_utils.h"
84 #define OFFSET 10 // Number of spaces from margin
86 #ifdef DTMAIL_TOOLTALK
87 // Time to self destruct
88 #define DESTRUCT_TIMEOUT 60000 // 1 minutes
91 // Pipe used between RFCTransport::childHandler and XtAppAddInput
92 static int _transfds[2];
94 static const char *ComposeIcon = "IcMcomp";
96 struct DefaultHeaders {
102 SendMsgDialog::ShowState show;
105 static int isInitializedDefaultHeaderList = 0;
106 static DefaultHeaders DefaultHeaderList[] = {
107 { 1, 241, "To", NULL, DtMailMessageTo, SendMsgDialog::SMD_ALWAYS },
108 { 1, 242, "Subject", NULL, DtMailMessageSubject, SendMsgDialog::SMD_ALWAYS },
109 { 1, 243, "Cc", NULL, DtMailMessageCc, SendMsgDialog::SMD_ALWAYS },
110 { 1, 244, "Bcc", NULL, DtMailMessageBcc, SendMsgDialog::SMD_HIDDEN },
111 { 0, 0, NULL, NULL, NULL, SendMsgDialog::SMD_NEVER }
114 // These headers can never be controlled by the user. They are generated
115 // by dtmail and the user is not allowed to override the values generated
116 // by the software. Besides, most users would not have a clue as to what
117 // a correct value would be.
119 static const char * BlockedHeaders[] = {
123 "Content-Transfer-Encoding",
131 block(const char * header)
133 for (const char ** test = BlockedHeaders; *test; test++) {
134 if (strcasecmp(header, *test) == 0) {
144 Compose theCompose; // Manages all compose windows.
146 SendMsgDialog::HeaderList::HeaderList(void)
157 SendMsgDialog::HeaderList::HeaderList(const HeaderList & other)
162 form_widget = other.form_widget;
163 label_widget = other.label_widget;
164 field_widget = other.field_widget;
167 label = strdup(other.label);
171 header = strdup(other.header);
175 value = strdup(other.value);
179 SendMsgDialog::HeaderList::~HeaderList(void)
193 Boolean SendMsgDialog::reservedHeader(const char *label)
195 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++)
196 if (strcmp(label, hl->label) == 0)
202 SendMsgDialog::SendMsgDialog()
203 : MenuWindow ( "ComposeDialog", True ),
210 _show_attach_area = FALSE;
213 _already_sending = FALSE;
219 _bccPopupCmdlist = NULL;
220 _bccPopupMenu = NULL;
221 _bccPopupMenuBar = NULL;
222 _ccPopupCmdlist = NULL;
224 _ccPopupMenuBar = NULL;
225 _toPopupCmdlist = NULL;
227 _toPopupMenuBar = NULL;
230 _attachmentActionsList = NULL;
231 _attachmentMenu = NULL;
232 _attachmentMenuList = NULL;
233 _attachmentPopupMenuList = NULL;
234 _textPopupMenuList = NULL;
236 _att_show_pane = NULL;
237 _att_select_all = NULL;
241 _att_undelete = NULL;
243 _att_select_all = NULL;
244 _auto_save_interval = 0;
245 _auto_save_path = NULL;
246 _auto_save_file = NULL;
247 _dead_letter_buf = NULL;
249 _file_include = NULL;
250 _file_save_as = NULL;
255 _format_word_wrap = NULL;
256 _format_settings = NULL;
257 _format_find_change = NULL;
258 _format_spell = NULL;
261 _format_cascade = NULL;
262 _format_separator = NULL;
263 _templateList = NULL;
265 // Now we need to get the additional headers from the property system.
268 DtMail::Session * d_session = theRoamApp.session()->session();
269 DtMail::MailRc * mail_rc = d_session->mailRc(error);
271 const char * value = NULL;
272 mail_rc->getValue(error, "additionalfields", &value);
274 DtVirtArray<PropStringPair *> results(8);
275 if (error.isNotSet()) {
276 _additionalfields = strdup(value);
277 parsePropString(value, results);
283 // Load the header list with the predefined/fixed headers.
285 if (! isInitializedDefaultHeaderList)
286 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
288 GETMSG(DT_catd, hl->msg_set, hl->msg_number, hl->dflt_label);
290 isInitializedDefaultHeaderList = TRUE;
293 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
294 HeaderList * copy_hl = new HeaderList;
295 copy_hl->label = strdup(hl->label);
296 copy_hl->value = getPropStringValue(results, hl->label);
297 copy_hl->header = strdup(hl->header);
298 copy_hl->show = hl->show;
299 _header_list.append(copy_hl);
302 if (error.isNotSet()) {
304 for (int mrc = 0; mrc < results.length(); mrc++) {
305 PropStringPair * psp = results[mrc];
307 if (!reservedHeader(psp->label)) {
308 HeaderList * copy_hl = new HeaderList;
309 copy_hl->label = strdup(psp->label);
310 copy_hl->header = strdup(psp->label);
312 copy_hl->value = strdup(psp->value);
313 copy_hl->show = SMD_HIDDEN;
314 _header_list.append(copy_hl);
318 while(results.length()) {
319 PropStringPair * psp = results[0];
325 _additionalfields = NULL;
329 SendMsgDialog::~SendMsgDialog()
337 delete _file_include;
338 delete _file_save_as;
351 delete _edit_select_all;
357 XtDestroyWidget(_ccPopupMenu);
358 delete _ccPopupMenuBar;
359 delete _ccPopupCmdlist;
361 XtDestroyWidget(_toPopupMenu);
362 delete _toPopupMenuBar;
363 delete _toPopupCmdlist;
365 XtDestroyWidget(_bccPopupMenu);
366 delete _bccPopupMenuBar;
367 delete _bccPopupCmdlist;
374 delete _att_undelete;
376 delete _att_select_all;
378 delete _attachmentActionsList;
382 delete _format_word_wrap;
383 delete _format_settings;
384 delete _format_find_change;
385 delete _format_spell;
386 delete _format_separator;
388 // Things created by createWorkArea()
391 delete _close_button;
394 // Allocated using 'malloc'.
395 // Purify requires us to free it using 'free'.
397 free(_auto_save_path);
398 if (_auto_save_file) {
399 delete _auto_save_file;
401 if (_dead_letter_buf) {
402 delete _dead_letter_buf;
405 delete _attachmentMenuList;
406 delete _attachmentPopupMenuList;
407 delete _textPopupMenuList;
411 // Callback for each header row
413 header_form_traverse(Widget w, XtPointer, XtPointer)
415 (void) XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
420 send_pushb_callback(Widget, XtPointer, XtPointer)
424 #endif /* DEAD_WOOD */
426 // Create Message Handle with empty first body part.
428 SendMsgDialog::makeMessage(void)
431 DtMail::Session * d_session = theRoamApp.session()->session();
437 DtMail::Message * msg = d_session->messageConstruct(error,
443 if (error.isSet() || !msg) {
447 DtMail::BodyPart * bp = msg->newBodyPart(error, NULL);
449 // For now, reserve the first body part for text.
450 setFirstBPHandled(TRUE);
455 // Before Submitting, call this routine.
456 // This routine does not save attachment(s).
457 // Check point routine should also call this routine.
459 SendMsgDialog::updateMsgHnd()
462 DtMail::Envelope * env;
465 env = _msgHandle->getEnvelope(error);
468 char * widget_text = this->text();
469 if (widget_text && *widget_text == '\0') {
475 textLen = strlen(widget_text);
477 // Even if textlen is 0 because user has cleared all previous text,
478 // need to setContents again to clear first BP. Otherwise, deleted
479 // text will show up.
481 // Get FirstBodyPart and fill it up.
482 DtMail::BodyPart *bp = _msgHandle->getFirstBodyPart(error);
483 bp->setContents(error, widget_text, textLen, NULL, NULL, 0, NULL);
484 setFirstBPHandled(TRUE);
486 if (NULL != widget_text)
490 // Before Submitting, also call this routine.
491 // This routine fills the message handle with attachment(s).
492 // The reason why we get attachment(s) from the Back End before submission
493 // is in case the attachment(s) are updated during the compose session.
494 // If changes are saved back, then the AttachArea class would register the
495 // update with Back End. So BE always has the latest attachments.
497 SendMsgDialog::updateMsgHndAtt()
499 if ( _inclMsgHandle == NULL )
500 return; // No attachments
503 DtMail::BodyPart *msgBP;
504 DtMail::BodyPart *inclBP = _inclMsgHandle->getFirstBodyPart(error);
505 const void *contents;
512 // If message handle to be copied, _inclMsgHandle, does not have its first
513 // body part as text, then get its first body part content and copy it.
514 // If it contains text, then skip it.
515 if ( !_inclMsgHasText ) {
516 inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
517 if ( _firstBPHandled ) {
518 msgBP = _msgHandle->newBodyPart(error, _lastAttBP);
520 msgBP = _msgHandle->getFirstBodyPart(error);
522 msgBP->setContents(error, contents, len, type, name, mode, desc);
528 // Continue to get the next body part and start copy.
529 while ((inclBP = _inclMsgHandle->getNextBodyPart(error, inclBP)) != NULL) {
530 inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
531 if ( _firstBPHandled ) {
532 msgBP = _msgHandle->newBodyPart(error, _lastAttBP);
534 msgBP = _msgHandle->getFirstBodyPart(error);
536 msgBP->setContents(error, contents, len, type, name, mode, desc);
540 } // end of while loop
543 // Update the _lastAttBP pointer so that subsequent newBodyPart() calls can
544 // return a message body part following the last body part.
546 SendMsgDialog::setLastAttBP()
549 _lastAttBP = _msgHandle->getNextBodyPart(error, _lastAttBP);
552 // This sister routine is needed when _lastAttBP has not been initialized.
554 SendMsgDialog::setLastAttBP(DtMail::BodyPart *bp)
559 // Initialize _msgHandle
561 SendMsgDialog::setMsgHnd()
563 _msgHandle = makeMessage();
564 _inclMsgHandle = NULL;
565 _inclMsgHasText = FALSE;
569 // Set timeout to ten minutes (as milliseconds)
572 SendMsgDialog::startAutoSave(void)
575 if(!_auto_save_path) return;
576 _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
577 getAutoSaveInterval(),
583 SendMsgDialog::stopAutoSave(void)
585 if (!_auto_save_interval) {
589 XtRemoveTimeOut(_auto_save_interval);
590 _auto_save_interval = 0;
592 unlink(_auto_save_file);
593 free(_auto_save_file);
594 _auto_save_file = NULL;
597 static const char * DEAD_LETTER_DIR = "~/dead_letter";
598 static const char * BACKUP_DEAD_LETTER = "./dead_letter";
599 static const char * PREFIX = "mail.dead.letter";
602 SendMsgDialog::mkAutoSavePath(void)
604 // First, see if we need to set up the path.
607 if (!_auto_save_path) {
608 DtMail::Session * d_session = theRoamApp.session()->session();
610 const char *save_path = NULL;
612 d_session->mailRc(error)->getValue(error, "deaddir", &save_path);
613 if (error.isNotSet() && save_path != NULL && *save_path != '\0')
614 _auto_save_path = d_session->expandPath(error, save_path);
617 _auto_save_path = d_session->expandPath(error, DEAD_LETTER_DIR);
618 if (!_auto_save_path)
619 _auto_save_path = d_session->expandPath(error, BACKUP_DEAD_LETTER);
622 if (NULL != save_path)
623 free((void*) save_path);
626 // If we still have a path, punt.
628 if (!_auto_save_path) {
632 if (SafeAccess(_auto_save_path, W_OK) != 0) {
633 if (errno != ENOENT) {
634 // Not an error we can overcome here.
636 free(_auto_save_path);
637 _auto_save_path = NULL;
641 if (mkdir(_auto_save_path, 0700) < 0) {
642 free(_auto_save_path);
643 _auto_save_path = NULL;
648 // Now we run through the possible file names until we hit pay dirt.
650 _auto_save_file = (char*) malloc((size_t) strlen(_auto_save_path) + 100);
651 for (int suffix = 1; ; suffix++) {
652 sprintf(_auto_save_file, "%s/%s.%d", _auto_save_path, PREFIX, suffix);
653 if (SafeAccess(_auto_save_file, F_OK) != 0) {
660 SendMsgDialog::loadDeadLetter(const char * path)
662 _auto_save_file = strdup(path);
665 _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
666 getAutoSaveInterval(),
672 SendMsgDialog::autoSaveCallback(XtPointer client_data, XtIntervalId * id)
674 SendMsgDialog * self = (SendMsgDialog *)client_data;
676 if (self->_auto_save_interval != *id) {
677 // Random noise. Ignore it.
683 self->_auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
684 self->getAutoSaveInterval(),
690 SendMsgDialog::doAutoSave(char *filename)
692 DtMail::Session * d_session = theRoamApp.session()->session();
696 setStatus(GETMSG(DT_catd, 3, 70, "Writing dead letter..."));
700 assert((NULL != filename));
701 RFCWriteMessage(error, d_session, filename, _msgHandle);
702 if((DTMailError_t) error == DTME_OutOfSpace )
704 RoamMenuWindow::ShowErrMsg((char *)error.getClient(),TRUE,this );
711 SendMsgDialog::doAutoSave(void)
713 doAutoSave(_auto_save_file);
717 SendMsgDialog::getAutoSaveInterval(void)
720 // Initialize the mail_error.
724 DtMail::Session * d_session = theRoamApp.session()->session();
725 DtMail::MailRc * mail_rc = d_session->mailRc(error);
728 const char * value = NULL;
729 mail_rc->getValue(error, "composeinterval", &value);
730 if (error.isSet() || value == NULL) {
731 save_interval = 10 * 60 * 1000; // 10 minutes
734 save_interval = (int) strtol(value, NULL, 10) * 60 * 1000;
735 save_interval = (save_interval <= 0) ? 10 * 60 * 1000 : save_interval;
740 return(save_interval);
744 SendMsgDialog::setInclMsgHnd(DtMail::Message *msg, Boolean text)
746 _inclMsgHandle = msg;
747 _inclMsgHasText = text;
751 SendMsgDialog::setFirstBPHandled(Boolean handle)
753 _firstBPHandled = handle;
757 SendMsgDialog::setHeader(const char * name, const char * value)
759 // See if this header is in the list. If so, set the widget for
762 int slot = lookupHeader(name);
767 HeaderList * hl = _header_list[slot];
768 if (hl->show == SMD_NEVER) {
769 // The user removed this header via props.
774 if (hl->field_widget) {
775 XtVaSetValues(hl->field_widget,
778 if (hl->show == SMD_HIDDEN) {
779 changeHeaderState(name);
785 SendMsgDialog::setHeader(const char * name, DtMailValueSeq & value)
787 if (value.length() == 0) {
791 if (value.length() == 1) {
792 setHeader(name, *(value[0]));
796 for (int slen = 0; slen < value.length(); slen++) {
797 max_len += strlen(*(value[slen]));
800 char * new_str = new char[max_len + (value.length() * 3)];
803 for (int copy = 0; copy < value.length(); copy++) {
805 strcat(new_str, " ");
808 strcat(new_str, *(value[copy]));
811 setHeader(name, new_str);
817 SendMsgDialog::loadHeaders(DtMail::Message * input,
818 DtMailBoolean load_all)
820 // We are going to go through every header in the message.
821 // If it is not one of the headers we block, then we will
822 // load it into the header pane, depending on the setting of
823 // load_all. If true, then we load every header we allow and
824 // create new dynamic headers as necessary. If load_all is false,
825 // then we only load headers that already are available in the
829 DtMail::Message * msg = (input ? input : _msgHandle);
830 DtMail::Envelope * env = msg->getEnvelope(error);
831 DtMailHeaderHandle hnd;
835 DtMailValueSeq value;
837 for (hnd = env->getFirstHeader(error, &name, value);
838 error.isNotSet() && hnd;
839 hnd = env->getNextHeader(error, hnd, &name, value)) {
841 // Always ignore the Unix from line.
844 strcmp(name, "From") == 0) {
853 // See if the name is one we always block.
861 int slot = lookupHeader(name);
863 // We dont have a place for this information. We may need
864 // to create a new header.
867 HeaderList *hl = new HeaderList;
868 hl->label = strdup(name);
869 hl->header = strdup(name);
870 hl->show = SMD_HIDDEN;
871 _header_list.append(hl);
872 createHeaders(_header_form);
873 doDynamicHeaderMenus();
882 setHeader(name, value);
889 SendMsgDialog::storeHeaders(DtMail::Message * input)
891 DtMail::Message * msg = (input ? input : _msgHandle);
893 DtMail::Envelope * env = msg->getEnvelope(error);
895 // Walk through the headers. Fetch the strings from the ones
896 // that are visible to the user and stuff them into the
899 for (int scan = 0; scan < _header_list.length(); scan++) {
900 HeaderList * hl = _header_list[scan];
901 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
906 XtVaGetValues(hl->field_widget,
909 // If the header has a value, we want to set the value
910 // in the back end. Otherwise, we don't want to send
911 // out a blank header, so we remove it.
912 if (strlen(value) > 0) {
913 env->setHeader(error, hl->header, DTM_TRUE, value);
916 env->removeHeader(error, hl->header);
925 SendMsgDialog::changeHeaderState(const char * name)
927 int slot = lookupHeader(name);
932 HeaderList * hl = _header_list[slot];
933 if (hl->show == SMD_ALWAYS || hl->show == SMD_NEVER) {
937 // If the user is trying to remove a header with a value other than
938 // the default, we should at least ask.
940 if (hl->show == SMD_SHOWN) {
942 XtVaGetValues(hl->field_widget,
945 if (strlen(value) > 0) {
946 if (!hl->value || strcmp(value, hl->value) != 0) {
947 char *buf = new char[256];
949 GETMSG(DT_catd, 2, 17,
950 "You have edited \"%s\". Delete anyway?"),
952 _genDialog->setToWarningDialog(GETMSG(DT_catd, 3, 71,
955 char * helpId = DTMAILHELPERROR;
956 int answer = _genDialog->post_and_return(
957 GETMSG(DT_catd, 3, 72, "OK"),
958 GETMSG(DT_catd, 3, 73, "Cancel"),
971 // Now we need to toggle the current state of the header.
973 char *label = new char[100];
974 if (hl->show == SMD_SHOWN) {
975 XtUnmanageChild(hl->form_widget);
976 hl->show = SMD_HIDDEN;
977 sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
981 XtManageChild(hl->form_widget);
982 hl->show = SMD_SHOWN;
983 sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
989 // Change the label on the menu item.
991 strcat(label, hl->label);
993 char *button_name = new char[100];
994 sprintf(button_name, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
995 strcat(button_name, hl->label);
996 strcat(button_name, ":");
998 _menuBar->changeLabel(_format_menu, button_name, label);
1000 delete [] button_name;
1004 SendMsgDialog::setStatus(const char * str)
1006 char *tmpstr = strdup(str);
1007 XmString label = XmStringCreateLocalized(tmpstr);
1009 XtVaSetValues(_status_text,
1010 XmNlabelString, label,
1013 XmUpdateDisplay(baseWidget());
1014 XmStringFree(label);
1019 SendMsgDialog::clearStatus(void)
1025 SendMsgDialog::isMsgValid(void)
1034 // Sendmail is exed'd and this parent process returns immediately. When
1035 // the sendmail child exits, this function is called with the pid of the
1036 // child and its status.
1038 SendMsgDialog::sendmailErrorProc (int, int status, void *data)
1040 SendMsgDialog *smd = (SendMsgDialog *)data;
1041 char *helpId = NULL;
1042 char *buf = new char[2048];
1044 smd->_first_time = TRUE;
1045 smd->_takeDown = FALSE;
1047 // pid is the child process (sendmail) id
1048 // status is the exit status of the child process
1049 // data is any extra data associated with the child process
1053 // The mail was successfully sent so return the compose
1054 // window to the cache and then return.
1055 smd->_send_button->activate();
1056 smd->_close_button->activate();
1060 case DTME_BadMailAddress:
1062 * There was an error in one or more of the email addresses.
1063 * Ask the user to type in a valid address and try again.
1065 sprintf(buf, GETMSG(DT_catd, 5, 5,
1066 "Some of the addresses in the message are incorrect,\n\
1067 and do not refer to any known users in the system.\n\
1068 Please make sure all of the addresses are valid and try again."));
1069 helpId = DTMAILHELPBADADDRESS;
1075 * Mailer ran out of memory. Ask the user to quit some other
1076 * applications so there will be more memory available.
1079 sprintf(buf, GETMSG(DT_catd, 5, 6,
1080 "Mailer does not have enough memory\n\
1081 available to send this message.\n\
1082 Try quitting other applications and\n\
1083 resend this message."));
1084 helpId = DTMAILHELPNOMEMORY;
1088 case DTME_TransportFailed:
1091 * There was an error from the mail transport (sendmail).
1094 sprintf(buf, GETMSG(DT_catd, 5, 7,
1095 "An error occurred while trying to send your message.\n\
1096 Check to make sure the message was received. If not,\n\
1097 you may have to resend this message."));
1098 helpId = DTMAILHELPTRANSPORTFAILED;
1101 // popup the compose window
1103 smd->_send_button->activate();
1104 smd->_close_button->activate();
1106 // popup the error dialog
1107 smd->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1109 smd->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1115 SendMsgDialog::send_message(const char * trans_impl, int trans_type)
1117 DtMailEnv mail_exec_error;
1118 DtMailOperationId id;
1119 DtMail::Transport * mail_transport;
1120 DtMail::Session * d_session = theRoamApp.session()->session();
1121 DtMailEditor *editor = this->get_editor();
1122 AttachArea *attachArea = editor->attachArea();
1123 int numPendingActions, answer;
1124 char *helpId = NULL;
1125 char *buf = new char[2048];
1126 static int first_time = 1;
1129 // Check to see if we are already trying to send from this
1130 // SendMsgDialog. If we are, we don't want to do it again.
1131 if (_already_sending) {
1135 _already_sending = TRUE;
1138 // First remove (unmap) the window;
1139 // send the message.
1140 // If you do it in the reverse order, users get confused coz
1141 // the window remains behind for a couple seconds after hitting
1142 // "Send" and it ...
1144 _send_button->deactivate();
1145 _close_button->deactivate();
1147 mail_exec_error.clear();
1150 // Check if message has addressees. If it doesn't, what sense does
1151 // it make to Send it?
1153 if (!this->hasAddressee()) {
1154 // Message has no valid addressee. Pop up error dialog.
1156 sprintf(buf, GETMSG(DT_catd, 5, 8,
1157 "Try Send after specifying recipient(s) of the message in \nthe To:, Cc:, or Bcc: fields."));
1159 helpId = DTMAILHELPNEEDADDRESSEE;
1161 // Need help tag for above HelpID.
1163 // popup the compose window
1165 _send_button->activate();
1166 _close_button->activate();
1168 _genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1170 _genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1172 // Reset the flag before we return.
1173 _already_sending = FALSE;
1179 // Since we are Send-ing, the SMD can be taken down later without
1180 // checking for dirty...
1187 // Just get text from text widget; attachment BPs are filled
1188 // already when they are included/forwarded/added.
1192 // Check if there are any pending attachments (open, print....)
1193 // If there are, pop up the dialog.
1194 // If the user wants to Send the message as is, continue with the
1195 // submission process.
1196 // If the user opted to Cancel, then return.
1199 numPendingActions = attachArea->getNumPendingActions();
1200 sprintf(buf, GETMSG(
1204 "You have an attachment open that may have unsaved changes.\nSending this message will break the connection to the open\n attachment. Any unsaved changes will not be part of the\n message. You can use Save As to save Changes after the\n connection is broken, but the changes will not be part of\n the attachment." ));
1206 while (numPendingActions != 0) {
1207 // popup the compose window
1209 _send_button->activate();
1210 _close_button->activate();
1213 * The user tried to send a messages without saving changes in
1214 * some open attachments. This warning makes sure that is what
1215 * the user intended.
1218 _genDialog->setToQuestionDialog(
1219 GETMSG(DT_catd, 5, 1, "Mailer"),
1221 helpId = DTMAILHELPPENDINGACTIONS;
1223 answer = _genDialog->post_and_return(helpId);
1227 numPendingActions = 0;
1229 _send_button->deactivate();
1230 _close_button->deactivate();
1232 else if (answer == 2) {
1234 // Reset the flag before we return.
1235 _already_sending = FALSE;
1241 // Determine which transport mechanism will be used.
1242 if ( trans_type ) { // Default
1243 // Only register XtAppAddInput once
1246 // Create the pipe between the RFCTransport::childHandler
1247 // and XtAppAddInput
1248 if (pipe(_transfds) < 0) {
1249 // If this failed, make sure we try to initialize again later.
1250 mail_exec_error.setError(DTME_NoMemory);
1251 popupMemoryError (mail_exec_error);
1253 // Reset the flag before we return.
1254 _already_sending = FALSE;
1259 // Call ourproc when input is available on _transfds[0]
1260 XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1261 _transfds[0], (XtPointer)XtInputReadMask,
1262 (XtInputCallbackProc)
1263 (theRoamApp.default_transport()->getSendmailReturnProc()),
1268 // Tell the transport where the callback is
1269 theRoamApp.default_transport()->initTransportData( _transfds,
1270 &(SendMsgDialog::sendmailErrorProc), this);
1271 id = theRoamApp.default_transport()->submit(mail_exec_error,
1272 _msgHandle, _log_msg);
1275 // Construct transport
1276 mail_transport = d_session->transportConstruct(mail_exec_error,
1277 trans_impl, RoamApp::statusCallback, this);
1279 // Only register XtAppAddInput once
1282 // Create the pipe between the RFCTransport::childHandler
1283 // and XtAppAddInput
1284 if (pipe(_transfds) < 0) {
1285 // If this failed, make sure we try to initialize again later.
1286 mail_exec_error.setError(DTME_NoMemory);
1287 popupMemoryError (mail_exec_error);
1289 // Reset the flag before we return.
1290 _already_sending = FALSE;
1295 // Call ourproc when input is available on _transfds[0]
1296 XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1297 _transfds[0], (XtPointer)XtInputReadMask,
1298 (XtInputCallbackProc)(mail_transport->getSendmailReturnProc()),
1303 // Tell the transport where the callback is
1304 mail_transport->initTransportData(_transfds,
1305 &(SendMsgDialog::sendmailErrorProc), this);
1306 id = mail_transport->submit(mail_exec_error, _msgHandle, _log_msg);
1309 popupMemoryError (mail_exec_error);
1311 // Reset the flag before we return.
1312 _already_sending = FALSE;
1318 SendMsgDialog::popupMemoryError(DtMailEnv &error)
1320 char *helpId = NULL;
1321 char *buf = new char[2048];
1325 // Popup an error dialog if necessary.
1326 if (error.isSet()) {
1327 if ((DTMailError_t)error == DTME_NoMemory) {
1330 * Mailer ran out of memory. Ask the user to quit some other
1331 * applications so there will be more memory available.
1333 sprintf(buf, GETMSG(DT_catd, 5, 6,
1334 "Mailer does not have enough memory\n\
1335 available to send this message.\n\
1336 Try quitting other applications and\n\
1337 resend this message."));
1338 helpId = DTMAILHELPNOMEMORY;
1342 * An unidentifiable error happened during mail transport
1343 * Pop it up *as is* (need to update this function if so)
1345 sprintf(buf, "%s", (const char *)error);
1346 helpId = DTMAILHELPERROR;
1349 // popup the compose window
1351 _send_button->activate();
1352 _close_button->activate();
1354 // popup the error dialog
1355 this->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1357 this->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"),
1365 SendMsgDialog::createWorkArea ( Widget parent )
1367 FORCE_SEGV_DECL(CmdInterface, ci);
1370 // Create the parent form
1372 _main_form = XmCreateForm( parent, "Work_Area", NULL, 0 );
1373 XtVaSetValues(_main_form, XmNresizePolicy, XmRESIZE_NONE, NULL);
1375 printHelpId("form", _main_form);
1376 /* add help callback */
1377 XtAddCallback(_main_form, XmNhelpCallback, HelpCB, DTMAILCOMPOSEWINDOW);
1378 XtVaSetValues(_main_form, XmNallowResize, True, NULL);
1381 // Create the area for status messages.
1383 _status_form = XtVaCreateManagedWidget("StatusForm",
1384 xmFormWidgetClass, _main_form,
1385 XmNtopAttachment, XmATTACH_FORM,
1386 XmNrightAttachment, XmATTACH_FORM,
1388 XmNleftAttachment, XmATTACH_FORM,
1392 _status_text = XtVaCreateManagedWidget("StatusLabel",
1393 xmLabelWidgetClass, _status_form,
1394 XmNtopAttachment, XmATTACH_FORM,
1395 XmNbottomAttachment, XmATTACH_FORM,
1396 XmNrightAttachment, XmATTACH_FORM,
1397 XmNleftAttachment, XmATTACH_FORM,
1398 XmNalignment, XmALIGNMENT_BEGINNING,
1403 Widget s_sep = XtVaCreateManagedWidget("StatusSep",
1404 xmSeparatorGadgetClass,
1406 XmNtopAttachment, XmATTACH_WIDGET,
1407 XmNtopWidget, _status_form,
1408 XmNleftAttachment, XmATTACH_FORM,
1409 XmNrightAttachment, XmATTACH_FORM,
1412 _header_form = XtVaCreateManagedWidget("HeaderArea",
1413 xmFormWidgetClass, _main_form,
1414 XmNtopAttachment, XmATTACH_WIDGET,
1415 XmNtopWidget, s_sep,
1416 XmNleftAttachment, XmATTACH_FORM,
1417 XmNrightAttachment, XmATTACH_FORM,
1419 printHelpId("header_form", _header_form);
1421 createHeaders(_header_form);
1423 Widget sep1 = XtVaCreateManagedWidget("Sep1",
1424 xmSeparatorGadgetClass,
1426 XmNtopAttachment, XmATTACH_WIDGET,
1427 XmNtopWidget, _header_form,
1429 XmNrightAttachment, XmATTACH_FORM,
1430 XmNleftAttachment, XmATTACH_FORM,
1433 // Create the editor and attach it to the header_form
1435 _my_editor = new DtMailEditor(_main_form, this);
1437 _my_editor->initialize();
1438 _my_editor->attachArea()->setOwnerShell(this);
1439 _my_editor->setEditable(TRUE);
1440 _my_editor->manageAttachArea();
1442 // Create a RowCol widget that contains buttons
1444 send_form = XtCreateManagedWidget("SendForm",
1445 xmFormWidgetClass, _main_form, NULL, 0);
1448 // Create the Send and Close buttons as children of rowCol
1450 _send_button = new SendCmd ( "Send",
1451 GETMSG(DT_catd, 1, 230, "Send"),
1455 ci = new ButtonInterface (send_form, _send_button);
1457 XtVaSetValues(ci->baseWidget(),
1458 XmNleftAttachment, XmATTACH_FORM,
1459 XmNleftOffset, OFFSET,
1460 XmNbottomAttachment, XmATTACH_FORM,
1464 Widget send_bw = ci->baseWidget();
1465 XtManageChild(send_bw);
1468 _close_button = new CloseCmd (
1470 GETMSG(DT_catd, 1, 118, "Close"),
1474 ci = new ButtonInterface (send_form, _close_button);
1475 XtVaSetValues(ci->baseWidget(),
1477 XmNleftAttachment, XmATTACH_WIDGET,
1478 XmNleftWidget, send_bw,
1479 XmNbottomAttachment, XmATTACH_FORM,
1483 XtManageChild(ci->baseWidget());
1486 // Now attach the editor to the form and to the rowCol
1487 // And the rowCol to the bottom of the form.
1488 // We need this attachment ordering so that resizes always
1489 // get transferred to the editor.
1491 Widget wid = _my_editor->container();
1493 XmNleftAttachment, XmATTACH_FORM,
1494 XmNrightAttachment, XmATTACH_FORM,
1495 XmNtopAttachment, XmATTACH_WIDGET,
1498 XmNbottomAttachment, XmATTACH_WIDGET,
1499 XmNbottomWidget, send_form,
1502 XtVaSetValues(send_form,
1503 XmNbottomAttachment, XmATTACH_FORM,
1508 HeaderList * hl = _header_list[0];
1509 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
1511 // Set the title to be New Message
1512 //char *ttl = GETMSG(DT_catd, 1, 119, "New Message");
1513 //this->setTitle(ttl);
1514 //this->setIconTitle(ttl);
1516 XtManageChild(_main_form);
1522 SendMsgDialog::createHeaders(Widget header_form)
1524 Widget previous_form = NULL;
1525 char *field_name = new char[50];
1527 for (int header = 0; header < _header_list.length(); header++) {
1528 HeaderList * hl = _header_list[header];
1530 // We use SMD_NEVER to indicate the header has disappeared from
1533 if (hl->show == SMD_NEVER) {
1537 // If the widgets already exist, then simply manage them.
1538 if (hl->form_widget) {
1539 previous_form = hl->form_widget;
1541 XtVaSetValues(hl->field_widget,
1542 XmNvalue, hl->value,
1545 XtVaSetValues(hl->field_widget,
1551 if (previous_form == NULL) {
1552 // Create a form, attaching it to the top. This is a special
1553 // case. Other lines are created attached to the form above
1555 strcpy(field_name, "form_");
1556 strncat(field_name, hl->label, 45);
1557 field_name[strlen(hl->label) + 5] = 0;
1559 XtVaCreateWidget(field_name,
1562 XmNtopAttachment, XmATTACH_FORM,
1564 XmNleftAttachment, XmATTACH_FORM,
1566 XmNrightAttachment, XmATTACH_FORM,
1572 strcpy(field_name, "form_");
1573 strncat(field_name, hl->label, 45);
1574 field_name[strlen(hl->label) + 5] = 0;
1576 XtVaCreateWidget(field_name,
1579 XmNtopAttachment, XmATTACH_WIDGET,
1580 XmNtopWidget, previous_form,
1581 XmNleftAttachment, XmATTACH_FORM,
1583 XmNrightAttachment, XmATTACH_FORM,
1589 // The label will be to the left of the form.
1591 strcpy(field_name, hl->label);
1592 strcat(field_name, ":");
1593 XmString label = XmStringCreateLocalized(field_name);
1595 XtVaCreateManagedWidget(hl->label,
1598 XmNtopAttachment, XmATTACH_FORM,
1599 XmNbottomAttachment, XmATTACH_FORM,
1600 XmNleftAttachment, XmATTACH_FORM,
1601 XmNlabelString, label,
1603 XmStringFree(label);
1605 strcpy(field_name, "field_");
1606 strncat(field_name, hl->label, 43);
1607 field_name[strlen(hl->label) + 6] = 0;
1610 XtVaCreateManagedWidget(field_name,
1611 xmTextFieldWidgetClass,
1613 XmNtraversalOn, True,
1614 XmNtopAttachment, XmATTACH_FORM,
1615 XmNrightAttachment, XmATTACH_FORM,
1616 XmNleftAttachment, XmATTACH_WIDGET,
1617 XmNleftWidget, hl->label_widget,
1620 if (hl->show != SMD_HIDDEN) {
1621 XtManageChild(hl->form_widget);
1624 XtVaSetValues(hl->form_widget,
1625 XmNtopAttachment, XmATTACH_NONE,
1629 XtAddCallback(hl->field_widget,
1630 XmNactivateCallback,
1631 header_form_traverse,
1634 XtAddCallback(hl->field_widget,
1635 XmNvalueChangedCallback,
1640 XtVaSetValues(hl->field_widget,
1641 XmNvalue, hl->value,
1645 previous_form = hl->form_widget;
1649 delete [] field_name;
1653 SendMsgDialog::doDynamicHeaderMenus(void)
1655 // This is really a pain, but we have to blow away the list to
1656 // build another one. This could probably be done more efficiently,
1657 // but we wont try to figure out how right now.
1660 _menuBar->removeOnlyCommands(_format_menu, _format_cmds);
1663 _format_cmds = new CmdList("DynamicFormatCommands", "DynamicFormatCommands");
1665 // Only put on commands that are shown or hidden. The items that
1666 // are always are never should not be presented to the user as
1667 // an option to change.
1669 char *label = new char[100];
1671 for (int h = 0; h < _header_list.length(); h++) {
1672 HeaderList * hl = _header_list[h];
1676 sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
1680 sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
1687 strcat(label, hl->label);
1690 char * priv_label = strdup(label);
1692 Cmd * new_cmd = new HideShowCmd(priv_label, priv_label,
1693 1, this, hl->label);
1695 // Add the commands one at a time with addCommand() vs. all
1696 // at once with addCommands(). That way new commands will
1697 // be created instead of reusing old ones.
1698 _menuBar->addCommand(_format_menu, new_cmd);
1699 _format_cmds->add(new_cmd);
1707 // Should theInfoDialogManager be destroyed here ???
1712 SendMsgDialog::open_att_cb( void *clientData, char *selection )
1714 SendMsgDialog *obj = (SendMsgDialog *)clientData;
1716 obj->open_att(selection);
1720 SendMsgDialog::open_att( char *) // arg is char *selection
1723 #endif /* DEAD_WOOD */
1726 SendMsgDialog::include_file_cb( void *client_data, char *selection )
1728 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1729 obj->include_file(selection);
1730 if (NULL != selection)
1736 SendMsgDialog::include_file(
1741 char *buf = new char[MAXPATHLEN];
1743 // I don't need to open the file to see if it's readable if loadFile()
1744 // returns error status.
1745 if ( (fp = fopen(selection, "r")) == NULL ) {
1746 sprintf(buf, GETMSG(DT_catd, 2, 18, "Error: Cannot include file %s"),
1748 theInfoDialogManager->post(
1751 (void *)this->_file_include,
1755 this->_my_editor->textEditor()->append_to_contents("\n", 2);
1756 this->_my_editor->textEditor()->append_at_cursor(selection);
1757 this->_my_editor->textEditor()->append_to_contents("\n", 2);
1763 SendMsgDialog::get_confirm_attachment_threshold()
1766 DtMail::Session *m_session = theRoamApp.session()->session();
1767 const char *value = NULL;
1770 m_session->mailRc(error)->getValue(error, "confirmattachments", &value);
1771 if (error.isSet()) return 0;
1773 m_session->mailRc(error)->getValue(error, "confirmattachmentthreshold",
1775 if (error.isNotSet() && NULL!=value)
1776 threshold = 1024 * atoi(value);
1778 threshold = 1024 * 64;
1784 SendMsgDialog::confirm_add_attachment(char *file, int size)
1786 char *buf = new char[BUFSIZ];
1791 GETMSG(DT_catd, 1, 263,
1792 "The attachment '%s' is %d kilobytes.\nAdd as attachment?");
1793 sprintf(buf, format, file, size/1024);
1794 _genDialog->setToQuestionDialog(GETMSG(DT_catd, 5, 2, "Mailer"), buf);
1795 answer = _genDialog->post_and_return(NULL);
1800 SendMsgDialog::add_att_cb( void *client_data, char *selection )
1802 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1803 obj->add_att(selection);
1804 if (NULL != selection)
1809 SendMsgDialog::add_att(char *file)
1811 struct stat statbuf;
1813 if (-1 != stat(file, &statbuf) && _confirm_attachment_threshold &&
1814 _confirm_attachment_threshold < statbuf.st_size)
1816 if (! confirm_add_attachment(file, statbuf.st_size)) return;
1819 // Activate Attachment menu???
1820 this->get_editor()->attachArea()->
1821 addAttachment(_msgHandle, _lastAttBP, file, NULL);
1822 this->setLastAttBP();
1823 this->activate_default_attach_menu();
1825 // This will manage the attach pane too.
1826 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1830 SendMsgDialog::add_att(char *name, DtMailBuffer buf)
1832 if (_confirm_attachment_threshold &&
1833 _confirm_attachment_threshold < buf.size)
1835 if (! confirm_add_attachment("", buf.size)) return;
1838 this->get_editor()->attachArea()->
1839 addAttachment(_msgHandle, _lastAttBP, name, buf);
1840 this->setLastAttBP();
1841 this->activate_default_attach_menu();
1843 // This will manage the attach pane too.
1844 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1848 SendMsgDialog::add_att(DtMailBuffer buf)
1855 SendMsgDialog::save_att_cb( void *client_data, char *selection )
1857 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1859 obj->save_selected_attachment(selection);
1864 SendMsgDialog::save_selected_attachment(
1868 DtMailEnv mail_error;
1872 AttachArea *attarea = this->get_editor()->attachArea();
1873 Attachment *attachment = attarea->getSelectedAttachment();
1875 // Get selected attachment, if none selected, then return.
1876 if ( attachment == NULL ) {
1877 // Let User know that no attachment has been selected???
1879 char *helpId = NULL;
1882 _genDialog->setToErrorDialog(
1883 GETMSG(DT_catd, 1, 120, "Mailer"),
1884 GETMSG(DT_catd, 2, 19, "An attachment needs to be selected before issuing the\n\"Save As\" command to save to a file.") );
1885 helpId = DTMAILHELPSELECTATTACH;
1886 answer = _genDialog->post_and_return(
1887 GETMSG(DT_catd, 3, 74, "OK"), helpId );
1891 // Save selected attachments.
1892 attachment->saveToFile(mail_error, selection);
1893 if ( mail_error.isSet() ) {
1894 // Let User know error condition???
1900 SendMsgDialog::propsChanged(void)
1902 DtMail::Session *m_session = theRoamApp.session()->session();
1903 const char * value = NULL;
1906 enableWorkAreaResize();
1908 m_session->mailRc(error)->getValue(error, "hideattachments", &value);
1910 if (_show_attach_area) {
1911 _show_attach_area = FALSE;
1912 this->hideAttachArea();
1916 if (!_show_attach_area) {
1917 _show_attach_area = TRUE;
1918 this->showAttachArea();
1921 free((void*) value);
1923 _confirm_attachment_threshold = get_confirm_attachment_threshold();
1926 const char * logfile = NULL;
1927 m_session->mailRc(error)->getValue(error, "record", &logfile);
1928 if (logfile == NULL)
1929 _file_log->deactivate();
1931 _file_log->activate();
1933 _my_editor->textEditor()->update_display_from_props();
1935 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
1936 if (logfile == NULL || error.isNotSet()) {
1937 // logfile is not specified or "dontlogmessages" is TRUE
1938 setLogState(DTM_FALSE);
1939 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
1941 // logfile is specified and "dontlogmessages" is FALSE
1942 setLogState(DTM_TRUE);
1943 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
1946 if (NULL != logfile)
1947 free((void*) logfile);
1949 free((void*) value);
1952 m_session->mailRc(error)->getValue(error, "templates", &value);
1953 if ( (value == NULL && _templateList != NULL) ||
1955 (_templateList == NULL || strcmp(value, _templateList)) != 0) ) {
1956 // Template list has changed
1957 if (_templateList != NULL)
1958 free (_templateList);
1959 if (value != NULL && *value != '\0')
1960 _templateList = strdup(value);
1962 _templateList = NULL;
1966 free((void*) value);
1968 // Alias Popup Menus
1969 DtVirtArray<PropStringPair*> *newAliases;
1970 Boolean aliasesChanged = FALSE;
1972 newAliases = new DtVirtArray<PropStringPair*> (10);
1973 createAliasList(newAliases);
1974 if (newAliases->length() == _aliasList->length())
1976 int length = newAliases->length();
1979 while (i<length && aliasesChanged==FALSE)
1981 PropStringPair *p1 = (*newAliases)[i];
1982 PropStringPair *p2 = (*_aliasList)[i];
1984 if ( strncmp(p1->label, p2->label, strlen(p2->label)) ||
1985 strncmp(p1->value, p2->value, strlen(p2->value)) )
1986 aliasesChanged = TRUE;
1992 aliasesChanged = TRUE;
1994 if (aliasesChanged == TRUE)
1996 destroyAliasPopupMenus();
1997 destroyAliasList(_aliasList);
1998 _aliasList = newAliases;
1999 createAliasPopupMenus();
2002 destroyAliasList(newAliases);
2004 disableWorkAreaResize();
2008 SendMsgDialog::createMenuPanes()
2013 const char * value = NULL;
2016 _separator = new SeparatorCmd( "Separator","Separator", TRUE );
2019 cmdList = new CmdList( "File", GETMSG(DT_catd, 1, 121, "File") );
2021 // Default directory is set below at the same time as the default
2022 // directory for att_add.
2023 _file_include = new UnifiedSelectFileCmd (
2025 GETMSG(DT_catd, 1, 122, "Include..."),
2026 GETMSG(DT_catd, 1, 123, "Mailer - Include"),
2027 GETMSG(DT_catd, 1, 124, "Include"),
2029 SendMsgDialog::include_file_cb,
2031 this->baseWidget());
2034 // Remap OK button to Include
2035 // XtVaSetValues(_file_include->fileBrowser,
2036 // XmNokLabelString, GETMSG(DT_catd,
2037 // 1, 77, "Include"), NULL);
2038 _file_save_as = new SaveAsTextCmd(
2040 GETMSG(DT_catd, 1, 125, "Save As Text..."),
2041 GETMSG(DT_catd, 1, 126, "Mailer - Save As Text"),
2043 get_editor()->textEditor(),
2047 _file_log = new LogMsgCmd (
2049 GETMSG(DT_catd, 1, 127, "Log Message"), TRUE, this);
2051 // 1 for default transport.
2053 _file_send = new SendCmd (
2055 GETMSG(DT_catd, 1, 117, "Send"),
2060 // Find out how many transports there are and build sub menu dynamically.
2061 DtMail::Session *d_session;
2063 if ( theRoamApp.session() == NULL ) {
2064 MailSession *new_session = new MailSession(
2066 theApplication->appContext());
2067 theRoamApp.setSession(new_session);
2070 CmdList *subcmdList1 = new CmdList (
2072 GETMSG(DT_catd, 1, 128, "Send As") );
2074 d_session = theRoamApp.session()->session();
2075 const char **impls = d_session->enumerateImpls(error);
2077 for ( int impl = 0; impls[impl]; impl++ ) {
2078 DtMailBoolean trans;
2079 d_session->queryImpl(error, impls[impl],
2080 DtMailCapabilityTransport, &trans);
2081 if (!error.isSet() && trans == DTM_TRUE ) {
2082 _file_sendAs[_num_sendAs] = new SendCmd( strdup(impls[impl]),
2083 (char *)impls[impl],
2087 subcmdList1->add( _file_sendAs[_num_sendAs] );
2090 // Assume an error means this query failed. But keep going and
2091 // get the next transport.
2094 _file_close = new CloseCmd (
2096 GETMSG(DT_catd, 1, 129, "Close"),
2098 _menuBar->baseWidget(),
2101 // Now build the menu
2103 cmdList->add( _file_include );
2104 cmdList->add( _file_save_as );
2105 cmdList->add( _file_log );
2106 cmdList->add( _separator );
2108 cmdList->add( _file_send );
2109 #if defined(USE_SEND_AS_MENU)
2110 cmdList->add( subcmdList1 );
2112 cmdList->add( _separator );
2114 cmdList->add( _file_close );
2116 _menuBar->addCommands ( cmdList );
2122 cmdList = new CmdList( "Edit", GETMSG(DT_catd, 1, 130, "Edit") );
2124 _edit_undo = new EditUndoCmd ( "Undo",
2125 GETMSG(DT_catd, 1, 131, "Undo"),
2127 _edit_cut = new EditCutCmd ( "Cut",
2128 GETMSG(DT_catd, 1, 132, "Cut"),
2130 _edit_copy = new EditCopyCmd ( "Copy",
2131 GETMSG(DT_catd, 1, 133, "Copy"),
2133 _edit_paste = new EditPasteCmd ( "Paste",
2134 GETMSG(DT_catd, 1, 134 , "Paste"),
2138 // Begin Paste Special submenu
2139 subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2140 _edit_paste_special[0] = new EditPasteSpecialCmd (
2142 GETMSG(DT_catd, 1, 136 , "Bracketed"),
2143 TRUE, this, Editor::IF_BRACKETED
2145 subcmdList1->add(_edit_paste_special[0]);
2146 _edit_paste_special[1] = new EditPasteSpecialCmd (
2148 GETMSG(DT_catd, 1, 137 , "Indented"),
2149 TRUE, this, Editor::IF_INDENTED );
2150 subcmdList1->add(_edit_paste_special[1]);
2151 // End Paste Special submenu
2153 _edit_clear = new EditClearCmd ( "Clear", GETMSG(DT_catd, 1, 138, "Clear"),
2156 _edit_delete = new EditDeleteCmd ( "Delete", GETMSG(DT_catd, 1, 139, "Delete"),
2159 _edit_select_all = new EditSelectAllCmd (
2161 GETMSG(DT_catd, 1, 140, "Select All"),
2164 _format_find_change = new FindChangeCmd (
2166 GETMSG(DT_catd, 1, 155, "Find/Change..."),
2169 _format_spell = new SpellCmd (
2170 "Check Spelling...",
2171 GETMSG(DT_catd, 1, 156, "Check Spelling..."),
2175 cmdList->add( _edit_undo );
2176 cmdList->add( _separator );
2177 cmdList->add( _edit_cut );
2178 cmdList->add( _edit_copy );
2179 cmdList->add( _edit_paste );
2180 cmdList->add( subcmdList1 ); // Add Paste Special submenu
2181 cmdList->add( _separator );
2182 cmdList->add( _edit_clear );
2183 cmdList->add( _edit_delete );
2184 cmdList->add( _separator );
2185 cmdList->add( _edit_select_all );
2186 cmdList->add( _separator );
2187 cmdList->add( _format_find_change );
2190 * SpellCheck is not supported by Base System for the multibyte language
2191 * currently. ( See dtpad's source ) So that this should be disabled.
2192 * See Defect 174873. (I should think this solution is not good one, but..)
2193 * What is the best way to check if I'm in MB or SB.....???
2196 if ( MB_CUR_MAX == 1 )
2197 cmdList->add( _format_spell );
2199 _menuBar->addCommands ( cmdList );
2203 // Alias Popup Menus
2204 if (NULL != _aliasList) delete _aliasList;
2205 _aliasList = new DtVirtArray<PropStringPair*> (10);
2206 createAliasList(_aliasList);
2207 createAliasPopupMenus();
2209 // Compose Popup CmdList
2210 construct_text_popup();
2214 cmdList = new CmdList(
2216 GETMSG(DT_catd, 1, 141, "Attachments"));
2218 _att_add = new UnifiedSelectFileCmd (
2220 GETMSG(DT_catd, 1, 142, "Add File..."),
2221 GETMSG(DT_catd, 1, 143, "Mailer - Add"),
2222 GETMSG(DT_catd, 1, 144, "Add"),
2224 SendMsgDialog::add_att_cb,
2226 this->baseWidget());
2228 _att_save = new SaveAttachCmd (
2230 GETMSG(DT_catd, 1, 145, "Save As..."),
2231 GETMSG(DT_catd, 1, 146,
2232 "Mailer - Attachments - Save As"),
2234 SendMsgDialog::save_att_cb,
2236 this->baseWidget());
2237 _att_delete = new DeleteAttachCmd (
2239 GETMSG(DT_catd, 1, 147, "Delete"),
2242 _att_undelete = new UndeleteAttachCmd (
2244 GETMSG(DT_catd, 1, 148, "Undelete"),
2247 _att_rename = new RenameAttachCmd(
2249 GETMSG(DT_catd, 1, 149, "Rename"),
2253 _att_select_all = new SelectAllAttachsCmd(
2255 GETMSG(DT_catd, 1, 150, "Select All"),
2259 * This is the label for a toggle item in a menu. When the item
2260 * is set to "Show List", the Attachment List is mapped in the
2261 * Compose Window. This message replaces message 151 in set 1.
2263 _att_show_pane = new ShowAttachPaneCmd(
2265 GETMSG(DT_catd, 1, 226, "Show List"),
2268 cmdList->add( _att_add );
2269 cmdList->add( _att_save );
2270 cmdList->add( _separator );
2272 // subcmdList1 = new CmdList ( "Create", "Create" );
2273 // // subcmdList1->add( att_audio );
2274 // // subcmdList1->add( att_appt );
2275 // cmdList->add( subcmdList1 );
2276 // cmdList->add( _separator );
2278 cmdList->add( _att_delete );
2279 cmdList->add( _att_undelete );
2280 cmdList->add( _att_rename );
2281 cmdList->add( _att_select_all );
2282 cmdList->add(_att_show_pane);
2284 // Create a pulldown from the items in the list. Retain a handle
2285 // to that pulldown since we need to dynamically add/delete entries
2286 // to this menu based on the selection of attachments.
2288 _attachmentMenu = _menuBar->addCommands ( cmdList );
2289 construct_attachment_popup();
2291 // delete subcmdList1;
2295 d_session->mailRc(error)->getValue(error, "templates", &value);
2296 if (value != NULL && *value != '\0')
2297 _templateList = strdup(value);
2299 free((void*) value);
2303 _overview = new OnAppCmd("Overview",
2304 GETMSG(DT_catd, 1, 71, "Overview"),
2306 _tasks = new TasksCmd("Tasks", GETMSG(DT_catd, 1, 72, "Tasks"),
2308 _reference = new ReferenceCmd("Reference",
2309 GETMSG(DT_catd, 1, 73, "Reference"),
2311 _on_item = new OnItemCmd("On Item", GETMSG(DT_catd, 1, 74, "On Item"),
2313 _using_help = new UsingHelpCmd("Using Help",
2314 GETMSG(DT_catd, 1, 75, "Using Help"),
2316 _about_mailer = new RelNoteCmd("About Mailer...",
2317 GETMSG(DT_catd, 1, 77, "About Mailer..."),
2319 cmdList = new CmdList("Help", GETMSG(DT_catd, 1, 76, "Help"));
2320 cmdList->add(_overview);
2321 cmdList->add(_separator);
2322 cmdList->add(_tasks);
2323 cmdList->add(_reference);
2324 cmdList->add(_separator);
2325 cmdList->add(_on_item);
2326 cmdList->add(_separator);
2327 cmdList->add(_using_help);
2328 cmdList->add(_separator);
2329 cmdList->add(_about_mailer);
2330 _menuBar->addCommands(cmdList, TRUE);
2335 SendMsgDialog::construct_attachment_popup(void)
2337 _attachmentPopupMenuList = new CmdList( "AttachmentsPopup", "AttachmentsPopup");
2339 LabelCmd *title = new LabelCmd (
2340 "Mailer - Attachments",
2341 GETMSG(DT_catd, 1, 158, "Mailer - Attachments"), TRUE);
2342 SeparatorCmd *separator = new SeparatorCmd( "Separator","Separator", TRUE );
2344 _attachmentPopupMenuList->add(title);
2345 _attachmentPopupMenuList->add(separator);
2346 _attachmentPopupMenuList->add( _att_add );
2347 _attachmentPopupMenuList->add( _att_save );
2348 _attachmentPopupMenuList->add( _att_delete );
2349 _attachmentPopupMenuList->add( _att_undelete );
2350 _attachmentPopupMenuList->add( _att_select_all );
2352 _menuPopupAtt = new MenuBar(_my_editor->attachArea()->getClipWindow(),
2353 "RoamAttachmentPopup", XmMENU_POPUP);
2354 _attachmentPopupMenu = _menuPopupAtt->addCommands(_attachmentPopupMenuList,
2355 FALSE, XmMENU_POPUP);
2359 SendMsgDialog::construct_text_popup(void)
2361 if (theApplication->bMenuButton() != Button3)
2364 _textPopupMenuList = new CmdList( "TextPopup", "TextPopup");
2366 LabelCmd *title = new LabelCmd (
2368 GETMSG(DT_catd, 1, 159, "Mailer - Compose"), TRUE);
2369 SeparatorCmd *separator = new SeparatorCmd("Separator", "Separator", TRUE );
2371 _textPopupMenuList->add(title);
2372 _textPopupMenuList->add(separator);
2373 _textPopupMenuList->add(_file_send);
2374 _textPopupMenuList->add( _edit_undo );
2375 _textPopupMenuList->add( _edit_cut );
2376 _textPopupMenuList->add( _edit_copy );
2377 _textPopupMenuList->add( _edit_paste );
2379 // Work in progress from Mike. This adds the Paste Special to the
2380 // third mouse button in the compose area of a compose window.
2381 // Begin Paste Special submenu
2382 CmdList * subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2383 subcmdList1->add(_edit_paste_special[0]);
2384 subcmdList1->add(_edit_paste_special[1]);
2385 // End Paste Special submenu
2386 _textPopupMenuList->add( subcmdList1 ); // Add Paste Special submenu
2387 // (Either way) _textPopupMenuList->add( separator );
2388 _textPopupMenuList->add( _edit_clear );
2390 _textPopupMenuList->add( _edit_delete );
2391 _textPopupMenuList->add( _edit_select_all );
2393 Widget parent = _my_editor->textEditor()->get_editor();
2394 _menuPopupText = new MenuBar(parent, "SendMsgTextPopup", XmMENU_POPUP);
2395 _textPopupMenu = _menuPopupText->addCommands(_textPopupMenuList,
2396 FALSE, XmMENU_POPUP);
2399 static int cmp_prop_pair(const void *v1, const void *v2)
2401 PropStringPair *p1 = *((PropStringPair **) v1);
2402 PropStringPair *p2 = *((PropStringPair **) v2);
2405 ret = strcmp((const char *) p1->label, (const char *) p2->label);
2409 static void alias_stuffing_func(char * key, void * data, void * client_data)
2411 DtVirtArray<PropStringPair *> *alias_list;
2412 PropStringPair *new_pair;
2414 alias_list = (DtVirtArray<PropStringPair*> *) client_data;
2415 new_pair = new PropStringPair;
2416 new_pair->label = strdup(key);
2417 new_pair->value = strdup((char *)data);
2418 alias_list->append(new_pair);
2422 SendMsgDialog::createAliasList(DtVirtArray<PropStringPair*> *aliases)
2425 DtMail::Session *d_session = theRoamApp.session()->session();
2426 DtMail::MailRc *mail_rc = d_session->mailRc(error);
2429 mail_rc->getAliasList(alias_stuffing_func, aliases);
2431 if (nalias = aliases->length())
2433 PropStringPair **prop_pairs = NULL;
2435 prop_pairs = (PropStringPair**) malloc(nalias*sizeof(PropStringPair*));
2437 for (i=0; i<nalias; i++)
2439 prop_pairs[i] = (*aliases)[0];
2442 qsort(prop_pairs, nalias, sizeof(PropStringPair*), cmp_prop_pair);
2443 for (i=0; i<nalias; i++)
2444 aliases->append(prop_pairs[i]);
2446 free((void*) prop_pairs);
2451 SendMsgDialog::destroyAliasList(DtVirtArray<PropStringPair*> *aliases)
2453 while (aliases->length() >= 0)
2455 PropStringPair *prop_pair = (*aliases)[0];
2461 // map_menu is used to figure out how many columns to split the menu
2462 // into. It is a callback that is called when the menu is mapped.
2463 // If the menu is over half the height of the screen, it figures out
2464 // how many columns to make the menu, and sets its XmNnumColumns
2465 // attribute to that value. It calculates the maximum number of columns
2466 // that would fit and never goes beyond that number.
2468 static void map_alias_menu(Widget menu, XtPointer, XtPointer)
2472 Dimension maxcols, newcols, columns;
2473 Dimension screenheight = (Dimension) HeightOfScreen(XtScreen(menu));
2474 Dimension fudgefact = 20; /* to allow for decorations on menu */
2481 XmNnumColumns, &columns,
2484 if ((h + fudgefact) > (screenheight / 2))
2486 // The menu is taller than half the screen.
2487 // We need to find out how many more columns
2488 // to specify for the menu to make it fit.
2490 newcols = (columns * ((h+fudgefact)/(screenheight/2))) + 1;
2491 maxcols = WidthOfScreen(XtScreen(menu))/(w/columns);
2493 if (newcols > maxcols)
2496 XtVaSetValues(menu, XmNnumColumns, newcols, NULL);
2501 SendMsgDialog::aliasMenuButtonHandler(
2503 XtPointer client_data,
2507 Widget menu = (Widget) client_data;
2508 XButtonEvent *be = (XButtonEvent *) event;
2510 if (event->xany.type != ButtonPress) return;
2511 if(be->button == theApplication->bMenuButton())
2513 XmMenuPosition(menu, (XButtonEvent *)event);
2514 XtManageChild(menu);
2519 SendMsgDialog::createAliasPopupMenu(
2523 DtVirtArray<PropStringPair*> *aliases)
2526 OtherAliasesCmd *otherAliases =
2527 new OtherAliasesCmd(
2529 GETMSG(DT_catd, 1, 247, "Other Aliases..."),
2531 #if defined(USE_TITLED_ALIAS_POPUPS)
2535 GETMSG(DT_catd, 1, 248, "Mailer - Aliases"),
2538 SeparatorCmd *separator =
2539 new SeparatorCmd("Separator","Separator", TRUE);
2541 (*cmdlist) = new CmdList("AliasCommands", "AliasCommands");
2542 #if defined(USE_TITLED_ALIAS_POPUPS)
2543 (*cmdlist)->add(title);
2544 (*cmdlist)->add(separator);
2546 for (int i=0, length=aliases->length(); i<length; i++)
2548 PropStringPair *prop_pair = (*aliases)[i];
2550 AliasCmd *alias = new AliasCmd(
2551 strdup(prop_pair->label),
2552 strdup(prop_pair->label),
2555 (*cmdlist)->add(alias);
2557 if (0 < aliases->length())
2558 (*cmdlist)->add(separator);
2559 (*cmdlist)->add(otherAliases);
2561 *menubar = new MenuBar(parent, "AliasesPopup", XmMENU_POPUP);
2562 menu = (*menubar)->addCommands((*cmdlist), FALSE, XmMENU_POPUP);
2568 aliasMenuButtonHandler,
2573 XmNpacking, XmPACK_COLUMN,
2574 XmNorientation, XmVERTICAL,
2579 XmNmapCallback, &map_alias_menu,
2586 SendMsgDialog::destroyAliasPopupMenu(
2592 XtRemoveEventHandler(
2596 aliasMenuButtonHandler,
2601 XmNmapCallback, &map_alias_menu,
2604 XtDestroyWidget(menu);
2611 SendMsgDialog::getHeaderWidget(const char *hdrname)
2613 for (int i=0, length=_header_list.length(); i<length; i++)
2615 HeaderList *hdritem = _header_list[i];
2617 if (0 == strncmp(hdrname, hdritem->header, strlen(hdrname)))
2618 return hdritem->field_widget;
2625 SendMsgDialog::createAliasPopupMenus(void)
2629 w = getHeaderWidget(DtMailMessageTo);
2631 _toPopupMenu = createAliasPopupMenu(
2637 w = getHeaderWidget(DtMailMessageCc);
2639 _ccPopupMenu = createAliasPopupMenu(
2645 w = getHeaderWidget(DtMailMessageBcc);
2647 _bccPopupMenu = createAliasPopupMenu(
2655 SendMsgDialog::destroyAliasPopupMenus(void)
2659 w = getHeaderWidget(DtMailMessageTo);
2660 destroyAliasPopupMenu(w, _toPopupMenuBar, _toPopupCmdlist, _toPopupMenu);
2661 _toPopupMenuBar = NULL;
2662 _toPopupCmdlist = NULL;
2663 _toPopupMenu = NULL;
2665 w = getHeaderWidget(DtMailMessageCc);
2666 destroyAliasPopupMenu(w, _ccPopupMenuBar, _ccPopupCmdlist, _ccPopupMenu);
2667 _ccPopupMenuBar = NULL;
2668 _ccPopupCmdlist = NULL;
2669 _ccPopupMenu = NULL;
2671 w = getHeaderWidget(DtMailMessageBcc);
2672 destroyAliasPopupMenu(w, _bccPopupMenuBar, _bccPopupCmdlist, _bccPopupMenu);
2673 _bccPopupMenuBar = NULL;
2674 _bccPopupCmdlist = NULL;
2675 _bccPopupMenu = NULL;
2679 SendMsgDialog::createFormatMenu()
2682 _format_separator = new SeparatorCmd( "Separator","Separator", TRUE );
2684 cmdList = new CmdList( "Format", GETMSG(DT_catd, 1, 152,"Format") );
2686 _format_word_wrap = new WordWrapCmd (
2688 GETMSG(DT_catd, 1, 153, "Word Wrap"),
2691 _format_settings = new FormatCmd ( "Settings...",
2692 GETMSG(DT_catd, 1, 154, "Settings..."),
2696 cmdList->add( _format_word_wrap );
2697 cmdList->add( _format_settings );
2698 cmdList->add( _format_separator);
2700 _templates = new CmdList ( "Templates", GETMSG(DT_catd, 1, 157, "Templates") );
2701 addTemplates(_templates);
2703 cmdList->add(_templates);
2705 cmdList->add( _format_separator );
2707 _format_menu = _menuBar->addCommands ( &_format_cascade, cmdList,
2712 doDynamicHeaderMenus();
2714 if (_template_count == 0 && _templates->getPaneWidget())
2716 XtSetSensitive(_templates->getPaneWidget(), FALSE);
2722 SendMsgDialog::addTemplates(CmdList * subCmd)
2726 _template_count = 0;
2728 if (_templateList == NULL)
2731 DtMail::Session *m_session = theRoamApp.session()->session();
2732 char * expanded_list = m_session->expandPath(error, _templateList);
2734 DtVirtArray<PropStringPair *> templates(8);
2735 parsePropString(expanded_list, templates);
2736 free(expanded_list);
2738 _template_count = templates.length();
2740 for (int tmp = 0; tmp < _template_count; tmp++) {
2741 PropStringPair * psp = templates[tmp];
2742 if (psp->label && psp->value) {
2743 Cmd * button = new TemplateCmd(strdup(psp->label),
2748 subCmd->add(button);
2752 while (templates.length()) {
2753 PropStringPair * psp = templates[0];
2755 templates.remove(0);
2760 SendMsgDialog::initialize()
2764 const char * hideAttachPane = NULL;
2767 // Without the TearOffModelConverter call, there will be warning messages:
2768 // Warning: No type converter registered for 'String' to 'TearOffModel'
2771 XmRepTypeInstallTearOffModelConverter();
2772 MenuWindow::initialize();
2774 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
2777 XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
2778 XtSetValues( _w, args, n);
2780 _genDialog = new DtMailGenDialog("Dialog", _main);
2782 // See if the .mailrc specifies if attachPane is to be shown or hid
2783 // at SMD startup time.
2785 DtMail::Session *m_session = theRoamApp.session()->session();
2786 m_session->mailRc(error)->getValue(error, "hideattachments",
2789 if (!hideAttachPane) {
2790 _show_attach_area = TRUE;
2793 _show_attach_area = FALSE;
2794 // The user wants to hide attachments
2796 this->hideAttachArea();
2798 if (NULL != hideAttachPane)
2799 free((void*) hideAttachPane);
2801 _confirm_attachment_threshold = get_confirm_attachment_threshold();
2803 // Log Message Toggle button. A LogMsgCmd is a ToggleButtonCmd....
2804 const char * logfile = NULL;
2805 const char * value = NULL;
2806 m_session->mailRc(error)->getValue(error, "record", &logfile);
2808 _file_log->deactivate();
2810 _file_log->activate();
2812 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2813 if (logfile == NULL || error.isNotSet()) {
2814 // logfile is not specified or "dontlogmessages" is TRUE
2815 setLogState(DTM_FALSE);
2816 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2818 // logfile is specified and "dontlogmessages" is FALSE
2819 setLogState(DTM_TRUE);
2820 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2823 if (NULL != logfile)
2824 free((void*) logfile);
2826 free((void*) value);
2828 // Word Wrap Toggle button. A WordWrapCmd is a ToggleButtonCmd...
2829 ((ToggleButtonCmd *)_format_word_wrap)->setButtonState(
2830 ((WordWrapCmd *)_format_word_wrap)->wordWrap(),
2834 // Initialize the Edit menu
2836 this->text_unselected();
2838 setIconName(ComposeIcon);
2842 Self_destruct(XtPointer, XtIntervalId *)
2845 fprintf(stderr, "DEBUG: Self_destruct(): invoked!\n");
2849 XtRemoveAllCallbacks(
2850 theApplication->baseWidget(),
2851 XmNdestroyCallback);
2852 delete theApplication;
2855 // Clears Compose window title, header fields, text, and attachment areas.
2857 SendMsgDialog::reset()
2859 _my_editor->textEditor()->clear_contents();
2860 _my_editor->attachArea()->resetPendingAction();
2863 // This will deselect any Attachment action, if any available now.
2864 // Also deselect text menu items....
2866 this->deactivate_default_attach_menu();
2867 this->text_unselected();
2868 this->all_attachments_deselected();
2869 _att_undelete->deactivate(); // This needs to be done in addition
2871 this->get_editor()->attachArea()->removeCurrentAttachments();
2873 // Unmanage the dialog
2876 if (_show_attach_area) { // .mailrc wants default attach area invisible
2878 // Unmanage the attach Area. Set the show_pane button.
2879 // This is done because if we are caching this window (after
2880 // unmanaging), we don't want the window to pop back up, on uncaching,
2881 // with the attachment pane visible, etc..
2883 this->showAttachArea();
2886 this->hideAttachArea();
2889 // Need to destroy current Message handle.
2890 delete _msgHandle; // All its body parts are deleted.
2892 _lastAttBP = NULL; // So just set this to NULL.
2893 // Delete or set to NULL ???
2894 _inclMsgHandle = NULL;
2895 _inclMsgHasText = NULL;
2897 for (int clear = 0; clear < _header_list.length(); clear++) {
2898 HeaderList * hl = _header_list[clear];
2900 // Bugfix: Old selection area remained selected, after text cleared
2901 // and parent widget unmanged, and then managed again for next
2902 // Compose. (So new text in old select area was still being selected).
2903 // Perhaps, this is a Motif bug ... but this fixes the problem.
2904 XmTextFieldClearSelection( hl->field_widget, CurrentTime );
2907 XtVaSetValues(hl->field_widget,
2908 XmNvalue, hl->value,
2912 XtVaSetValues(hl->field_widget,
2917 // Reset the Log state in case the user happened to change it.
2918 DtMail::Session *m_session = theRoamApp.session()->session();
2919 const char * logfile = NULL;
2920 const char * value = NULL;
2923 m_session->mailRc(error)->getValue(error, "record", &logfile);
2925 _file_log->deactivate();
2927 _file_log->activate();
2929 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2930 if (logfile == NULL || error.isNotSet()) {
2931 // logfile is not specified or "dontlogmessages" is TRUE
2932 setLogState(DTM_FALSE);
2933 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2935 // logfile is specified and "dontlogmessages" is FALSE
2936 setLogState(DTM_TRUE);
2937 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2940 if (NULL != logfile)
2941 free((void*) logfile);
2943 free((void*) value);
2946 // Recycles Compose window.
2948 SendMsgDialog::quit(Boolean delete_win)
2951 // There are several ways we could have reached here.
2952 // 1) From the user choosing Send.
2953 // 2) From the user clicking on the Close button or Close menu item
2954 // 3) The user choosing Close from window manager menu.
2955 // For (1), we just forge ahead. For that, the _takeDown boolean
2956 // is set in send_message() method.
2957 // For (2), the boolean is set in goAway().
2958 // For (3), we call goAway() which sets the _takeDown depending on
2959 // a dialog negotiation if SMD has contents.
2961 if (_file_include->fileBrowser() != NULL)
2962 XtPopdown(XtParent(_file_include->fileBrowser()));
2963 if (_att_add->fileBrowser() != NULL)
2964 XtPopdown(XtParent(_att_add->fileBrowser()));
2966 if (_file_save_as->fileBrowser() != NULL)
2967 XtPopdown(XtParent(_file_save_as->fileBrowser()));
2968 if (_att_save->fileBrowser() != NULL)
2969 XtPopdown(XtParent(_att_save->fileBrowser()));
2972 // Check to see if it's the first time through the quit()
2973 // method. Set _first_time to FALSE so that we don't come
2974 // down this path again until we're done quitting or bad
2975 // things will happen.
2976 if (_first_time == TRUE) {
2977 _first_time = FALSE;
2979 // We're done quitting, so we can set _first_time to TRUE again.
2987 #ifdef DTMAIL_TOOLTALK
2988 // For explanation of dtmail_mapped, look at RoamApp.h.
2989 if ( started_by_tt && (0 == theCompose.getTimeOutId()) &&
2990 (theCompose.numUnusedWindows() == theCompose.numCreatedWindows()) &&
2994 id = XtAppAddTimeOut(
2995 theApplication->appContext(),
2996 (unsigned long)DESTRUCT_TIMEOUT,
2997 Self_destruct, NULL);
2998 theCompose.putTimeOutId(id);
3007 theCompose.putWin(this, FALSE);
3011 // If there are no composer timeouts, check if its time to shutdown.
3013 if (0 == theCompose.getTimeOutId()) theRoamApp.checkForShutdown();
3017 SendMsgDialog::panicQuit()
3020 // Need to make sure the message is still valid before proceeding.
3021 // ::reset may have been called so the message may no longer be valid.
3030 // Given a file name, include the file as attachment.
3032 SendMsgDialog::inclAsAttmt(char *file, char *name)
3034 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3036 this->setLastAttBP();
3040 // Given a buffer, include its content as an attachment.
3042 SendMsgDialog::inclAsAttmt(unsigned char *contents, int len, char *name)
3046 mbuf.buffer = (void *)contents;
3047 mbuf.size = (unsigned long)len;
3048 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3049 (String)name, mbuf);
3050 this->setLastAttBP();
3053 // Given a RFC_822_Message formatted buffer, parse it and fill the Compose Window.
3055 SendMsgDialog::parseNplace(char *contents, int len)
3057 // 1. Create message handle for contents
3060 DtMail::Session * d_session = theRoamApp.session()->session();
3063 mbuf.buffer = (void *)contents;
3064 mbuf.size = (unsigned long)len;
3066 DtMail::Message * msg = d_session->messageConstruct(error,
3074 } else if ( error.isSet() ) {
3075 if ( (DTMailError_t) error == DTME_UnknownFormat ) {
3076 // The content does not have header info. Therefore, store
3077 // everything as text.
3078 _my_editor->textEditor()->set_contents((const char *)mbuf.buffer,
3084 char * status_string;
3085 DtMailBoolean first_bp_handled;
3086 first_bp_handled = _my_editor->textEditor()->set_message(
3092 int num_bodyParts = msg->getBodyCount(error);
3094 // Don't use setInclMsgHnd() because it causes the SMD's attachments
3095 // to get out of sink with the BE. Just assign the newly created message
3098 if ((num_bodyParts > 1) || (!first_bp_handled)) {
3100 if (first_bp_handled) {
3102 // setInclMsgHnd(msg, TRUE);
3106 // setInclMsgHnd(msg, FALSE);
3113 _my_editor->attachArea()->parseAttachments(error,
3118 // Need to call this after calling parseAttachments() so attachments
3119 // will be displayed in the attachment pane.
3120 _my_editor->manageAttachArea();
3122 // Need to update this compose window's internal message handle.
3124 // GL - calling updateMsgHndAtt is no longer necessary because we
3125 // just assigning the newly created msg to _msgHandle.
3126 // updateMsgHndAtt();
3129 loadHeaders(msg, DTM_TRUE);
3132 // Given a RFC_822_Message formatted file, parse it and fill the Compose Window.
3134 SendMsgDialog::parseNplace(const char * path)
3136 // 1. Get file content into buffer.
3137 int fd = SafeOpen(path, O_RDONLY);
3143 if (SafeFStat(fd, &buf) < 0) {
3148 _dead_letter_buf = new char[buf.st_size];
3149 if (!_dead_letter_buf) {
3154 if (SafeRead(fd, _dead_letter_buf,
3155 (unsigned int) buf.st_size) != buf.st_size) {
3156 delete _dead_letter_buf;
3161 parseNplace(_dead_letter_buf, (int) buf.st_size);
3165 SendMsgDialog::text( const char *text )
3167 _my_editor->textEditor()->set_contents( text, strlen(text) );
3171 SendMsgDialog::append( const char *text )
3173 _my_editor->textEditor()->append_to_contents( text, strlen(text) );
3177 SendMsgDialog::text()
3179 // Potential memory leak here. Because XmTextGetString returns
3180 // pointer to space containing all the text in the widget. Need
3181 // to call XtFree after we use this space
3182 // Also DtEditor widget requires application to free data.
3184 return (_my_editor->textEditor()->get_contents());
3190 SendMsgDialog::text_selected()
3192 // turn on sensitivity for Cut/Clear/Copy/Delete
3193 _edit_cut->activate();
3194 _edit_copy->activate();
3195 _edit_clear->activate();
3196 _edit_delete->activate();
3197 _edit_select_all->activate();
3201 SendMsgDialog::text_unselected()
3203 // turn off sensitivity for those items
3204 _edit_cut->deactivate();
3205 _edit_copy->deactivate();
3206 _edit_clear->deactivate();
3207 _edit_delete->deactivate();
3213 SendMsgDialog::attachment_selected()
3215 _att_save->activate();
3216 _att_delete->activate();
3217 _att_rename->activate();
3222 SendMsgDialog::all_attachments_selected()
3224 _att_delete->activate();
3225 _att_save->deactivate();
3226 _att_rename->deactivate();
3228 if (_attachmentActionsList != NULL) {
3229 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3230 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3231 _attachmentActionsList);
3232 delete _attachmentActionsList;
3233 _attachmentActionsList = NULL;
3240 SendMsgDialog::all_attachments_deselected()
3242 _att_save->deactivate();
3243 _att_delete->deactivate();
3244 _att_rename->deactivate();
3246 if (_attachmentActionsList != NULL) {
3247 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3248 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3249 _attachmentActionsList);
3250 delete _attachmentActionsList;
3251 _attachmentActionsList = NULL;
3258 SendMsgDialog::addAttachmentActions(
3265 AttachmentActionCmd *attachActionCmd;
3267 if (_attachmentActionsList == NULL) {
3268 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3271 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3272 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3273 _attachmentActionsList);
3274 delete _attachmentActionsList;
3275 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3279 for (i = 0; i < indx; i++) {
3280 anAction = actions[i];
3281 actionLabel = DtActionLabel(anAction); // get the localized action label
3282 attachActionCmd = new AttachmentActionCmd(
3287 _attachmentActionsList->add(attachActionCmd);
3290 _attachmentMenu = _menuBar->addCommands(
3292 _attachmentActionsList
3294 _attachmentPopupMenu = _menuPopupAtt->addCommands(
3295 _attachmentPopupMenu,
3296 _attachmentActionsList
3301 SendMsgDialog::removeAttachmentActions()
3304 // Stubbed out for now
3308 SendMsgDialog::invokeAttachmentAction(
3312 DtMailEditor *editor = this->get_editor();
3313 AttachArea *attacharea = editor->attachArea();
3314 Attachment *attachment = attacharea->getSelectedAttachment();
3316 attachment->invokeAction(index);
3320 SendMsgDialog::selectAllAttachments()
3323 DtMailEditor *editor = this->get_editor();
3324 AttachArea *attachArea = editor->attachArea();
3326 attachArea->selectAllAttachments();
3332 SendMsgDialog::activate_default_attach_menu()
3334 _att_select_all->activate();
3338 SendMsgDialog::deactivate_default_attach_menu()
3340 _att_select_all->deactivate();
3345 SendMsgDialog::delete_selected_attachments()
3347 DtMailEnv mail_error;
3349 // Initialize the mail_error.
3353 AttachArea *attachArea = _my_editor->attachArea();
3354 attachArea->deleteSelectedAttachments(mail_error);
3356 if (mail_error.isSet()) {
3360 // Activate this button to permit the user to undelete.
3362 _att_undelete->activate();
3364 // Deactivate buttons that will be activated when another
3365 // selection applies.
3367 _att_save->deactivate();
3368 _att_delete->deactivate();
3369 _att_rename->deactivate();
3371 if (_attachmentActionsList) {
3372 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3373 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3374 _attachmentActionsList);
3375 delete _attachmentActionsList;
3376 _attachmentActionsList = NULL;
3381 SendMsgDialog::undelete_last_deleted_attachment()
3383 DtMailEnv mail_error;
3385 // Initialize the mail_error.
3389 AttachArea *attachArea = _my_editor->attachArea();
3390 attachArea->undeleteLastDeletedAttachment(mail_error);
3392 if (mail_error.isSet()) {
3396 if(_my_editor->attachArea()->getIconSelectedCount())
3397 _att_delete->activate();
3399 if (attachArea->getDeleteCount() == 0) {
3400 _att_undelete->deactivate();
3405 SendMsgDialog::renameAttachmentOK()
3407 AttachArea *attachArea = _my_editor->attachArea();
3409 if (attachArea->getIconSelectedCount() > 1) {
3410 char *buf = new char[512];
3412 sprintf(buf, GETMSG(DT_catd, 5, 4, "Select only one attachment\n\
3413 and then choose rename"));
3415 _genDialog->setToQuestionDialog(
3416 GETMSG(DT_catd, 5, 2, "Mailer"),
3419 char * helpId = DTMAILHELPSELECTONEATTACH;
3421 int answer = _genDialog->post_and_return(helpId);
3432 SendMsgDialog::showAttachArea()
3434 DtMailEditor *editor = this->get_editor();
3435 editor->showAttachArea();
3436 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, FALSE);
3440 SendMsgDialog::hideAttachArea()
3442 DtMailEditor *editor = this->get_editor();
3443 editor->hideAttachArea();
3444 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(FALSE, FALSE);
3448 SendMsgDialog::lookupHeader(const char * name)
3450 for (int h = 0; h < _header_list.length(); h++) {
3451 HeaderList * hl = _header_list[h];
3452 if (hl->show != SMD_NEVER &&
3453 strcmp(hl->label, name) == 0) {
3462 SendMsgDialog::headerValueChanged(Widget,
3463 XtPointer client_data,
3466 SendMsgDialog * self = (SendMsgDialog *)client_data;
3467 self->_headers_changed = DTM_TRUE;
3471 SendMsgDialog::reattachHeaders(void)
3473 // We have to walk through the entire list of headers, attaching
3474 // the shown headers to the ones above them.
3476 HeaderList * hl = _header_list[0];
3477 Widget previous_form = hl->form_widget;
3479 for (int h = 1; h < _header_list.length(); h++) {
3480 hl = _header_list[h];
3484 previous_form = hl->form_widget;
3488 XtVaSetValues(hl->form_widget,
3489 XmNtopAttachment, XmATTACH_WIDGET,
3490 XmNtopWidget, previous_form,
3492 previous_form = hl->form_widget;
3500 forceFormResize(_main_form);
3501 forceFormResize(_header_form);
3505 SendMsgDialog::justifyHeaders(void)
3507 // Find out which header label has the longest display width to right
3508 // justify all labels.
3510 Dimension longest = 0;
3513 for (int count = 0; count < _header_list.length(); count++) {
3514 HeaderList * hl = _header_list[count];
3515 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3519 XtVaGetValues(hl->label_widget,
3521 XmNmarginLeft, &margin,
3524 if ( w > longest ) {
3529 for (int adjust = 0; adjust < _header_list.length(); adjust++) {
3530 HeaderList * hl = _header_list[adjust];
3531 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3535 XtVaGetValues(hl->label_widget,
3537 XmNmarginLeft, &margin,
3540 XtVaSetValues(hl->label_widget, XmNmarginLeft, (longest-w) > 0 ? longest-w : 1, NULL );
3545 SendMsgDialog::forceFormResize(Widget form)
3547 // The Motif Form widget is at least a little bit brain damaged.
3548 // We need to convince it to do the right thing after we make
3549 // minor adjustments in the children.
3551 Dimension width, height, border;
3555 XmNborderWidth, &border,
3559 XmNwidth, width + 1,
3560 XmNheight, height + 1,
3571 SendMsgDialog::unfilled_headers()
3573 // Walk through the headers. See if any of them have a value.
3575 for (int scan = 0; scan < _header_list.length(); scan++) {
3576 HeaderList * hl = _header_list[scan];
3577 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
3581 char * value = NULL;
3582 XtVaGetValues(hl->field_widget,
3586 if (strlen(value) > 0) {
3596 // Method checks if self has text in it.
3599 SendMsgDialog::checkDirty()
3601 if (this->unfilled_headers() &&
3602 (this->get_editor()->textEditor()->no_text()) &&
3603 (this->get_editor()->attachArea()->getIconCount() == 0) ) {
3605 // return FALSE so quit() can go ahead.
3616 SendMsgDialog::handleQuitDialog()
3620 DtMail::Session *m_session = theRoamApp.session()->session();
3621 const char * value = NULL;
3624 m_session->mailRc(error)->getValue(error, "expert", &value);
3625 if (error.isNotSet() && value != NULL)
3628 free((void*) value);
3633 DtMailGenDialog *dialog = this->genDialog();
3635 dialog->setToQuestionDialog(
3645 "The Compose window contains text or\n\
3646 attachments that will be lost if\n\
3647 the window is closed.\n\
3648 Close the Compose window?")
3650 helpId = DTMAILHELPCLOSECOMPOSEWINDOW;
3651 if ( dialog->post_and_return(
3662 helpId) == 1 ) { // Close selected
3667 return(FALSE); // Cancel selected
3672 SendMsgDialog::goAway(
3673 Boolean checkForDirty
3677 if (!checkForDirty) {
3682 // Check to see if self has contents (ie., is dirty)
3684 Boolean is_dirty = this->checkDirty();
3687 if (isIconified()) {
3688 MainWindow::manage();
3691 // Enquire if user really wants this window to go away
3693 Boolean really_quit = this->handleQuitDialog();
3704 SendMsgDialog::manage()
3706 MenuWindow::manage();
3708 HeaderList * hl = _header_list[0];
3709 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3714 SendMsgDialog::unmanage()
3716 MenuWindow::unmanage();
3717 XFlush(XtDisplay(this->_main_form));
3718 XSync(XtDisplay(this->_main_form), False);
3724 _compose_head = NULL;
3732 Compose::Compose_Win *a_node;
3733 Compose::Compose_Win *next_node;
3735 theRoamApp.registerPendingTask();
3737 a_node = _compose_head;
3740 next_node = a_node->next;
3741 if (!a_node->in_use)
3744 free((void*) a_node);
3749 theRoamApp.unregisterPendingTask();
3753 Compose::putWin(SendMsgDialog *smd, Boolean in_use)
3755 Compose::Compose_Win* a_node = NULL;
3756 Compose::Compose_Win *tmp = NULL;
3759 // Update the _not_in_use count.
3765 // Check to see if compose window is already in the list.
3767 for (a_node = _compose_head; a_node; a_node=a_node->next)
3769 if (a_node->win == smd)
3771 a_node->in_use = in_use;
3776 // Need new node with smd.
3777 tmp = (Compose::Compose_Win *)malloc(sizeof(Compose::Compose_Win));
3780 tmp->in_use = in_use;
3782 // If nothing is cached so far, add this Compose window to the head.
3783 if (NULL == _compose_head)
3785 _compose_head = tmp;
3789 // There exists a cache. Add this compose window to the tail.
3790 for (a_node=_compose_head; a_node; a_node=a_node->next)
3792 if (NULL == a_node->next)
3801 Compose::getUnusedWin()
3803 if (NULL == _compose_head) return NULL;
3805 Compose::Compose_Win* a_node = NULL;
3806 Compose::Compose_Win* the_node = NULL;
3808 // Find a node with unused smd. Return smd
3809 for (a_node=_compose_head; a_node; a_node=a_node->next)
3811 if (!a_node->in_use)
3813 a_node->in_use = TRUE;
3822 // Get a compose window either by creating a new SendMsgDialog or
3823 // from the recycle list.
3827 SendMsgDialog *newsend = NULL;
3829 #ifdef DTMAIL_TOOLTALK
3832 XtRemoveTimeOut(_timeout_id);
3837 newsend = getUnusedWin();
3841 // We have no unused SMDs around; therefore, create new window.
3842 theRoamApp.busyAllWindows();
3843 newsend = new SendMsgDialog();
3844 newsend->initialize();
3846 putWin(newsend, TRUE);
3847 theRoamApp.unbusyAllWindows();
3851 newsend->resetHeaders();
3852 newsend->displayInCurrentWorkspace();
3855 newsend->text_unselected();
3857 newsend->startAutoSave();
3859 // Get new Message Handle
3860 newsend->setMsgHnd();
3861 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
3862 newsend->setTitle(ttl);
3863 newsend->setIconTitle(ttl);
3869 SendMsgDialog::setTitle(char *subject)
3871 char *format = "%s - %s";
3872 char *prefix = GETMSG(DT_catd, 1, 6, "Mailer");
3876 len = strlen(format) + strlen(prefix) + strlen(subject) + 1;
3877 new_title = new char[len];
3878 sprintf(new_title, format, prefix, subject);
3881 delete [] new_title;
3885 SendMsgDialog::resetHeaders(void)
3887 DtMail::Session *m_session = theRoamApp.session()->session();
3888 const char * value = NULL;
3892 m_session->mailRc(error)->getValue(error, "additionalfields", &value);
3894 // Return if no props were applied and headers did not change.
3895 if ((_additionalfields == NULL && value == NULL) ||
3896 ( _additionalfields != NULL && value != NULL &&
3897 strcmp (_additionalfields, value) == 0))
3900 free((void*) value);
3905 // User changed the Header list via props. Recreate list
3908 // First hide all shown headers
3909 for (i=0; i < _header_list.length(); i++) {
3910 HeaderList * hl = _header_list[i];
3911 if (hl->show == SMD_SHOWN) {
3912 hl->show = SMD_HIDDEN;
3913 XtUnmanageChild(hl->form_widget);
3917 // Now remove the old list.
3918 DtVirtArray<PropStringPair *> results(8);
3919 parsePropString(_additionalfields, results);
3920 for (i=0, j=results.length(); i < j; i++) {
3921 PropStringPair * psp = results[i];
3922 int slot = lookupHeader(psp->label);
3923 // dont allow removal of default headers.
3924 HeaderList * hl = _header_list[slot];
3925 if (!reservedHeader(hl->label)) {
3927 hl->show = SMD_NEVER;
3929 else if (hl->value != NULL) {
3934 while(results.length()) {
3935 PropStringPair * psp = results[0];
3940 if (_additionalfields != NULL)
3941 free(_additionalfields);
3942 if (value != NULL && *value != '\0')
3943 _additionalfields = strdup(value);
3945 _additionalfields = NULL;
3947 parsePropString(value, results);
3950 for (j=results.length(), i=0; i < j; i++) {
3951 PropStringPair * psp = results[i];
3952 int slot = lookupHeader(psp->label);
3955 HeaderList * hl = _header_list[slot];
3956 if (!reservedHeader(hl->label))
3957 hl->show = SMD_HIDDEN;
3958 if (hl->value != NULL) {
3962 if (psp->value != NULL)
3963 hl->value = strdup(psp->value);
3966 HeaderList * copy_hl = new HeaderList;
3967 copy_hl->label = strdup(psp->label);
3968 copy_hl->header = strdup(psp->label);
3970 copy_hl->value = strdup(psp->value);
3971 copy_hl->show = SMD_HIDDEN;
3972 _header_list.append(copy_hl);
3974 while(results.length()) {
3975 PropStringPair * psp = results[0];
3980 createHeaders(_header_form);
3981 doDynamicHeaderMenus();
3984 free((void*) value);
3988 SendMsgDialog::setInputFocus(const int mode)
3992 HeaderList * hl = _header_list[0];
3993 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3995 else if (mode == 1) {
3996 Widget edWid = _my_editor->textEditor()->get_editor();
3997 (void) XmProcessTraversal(edWid, XmTRAVERSE_CURRENT);
4002 SendMsgDialog::attachmentFeedback(
4010 this->normalCursor();
4015 SendMsgDialog::hasAddressee()
4019 DtMail::Envelope * env = _msgHandle->getEnvelope(error);
4021 // Walk through the headers.
4022 // Return TRUE if the message has a value for either of the
4023 // following headers: To:, Cc:, or Bcc:.
4024 // Return FALSE if none of the three headers have any value.
4026 for (int scan = 0; scan < _header_list.length(); scan++) {
4027 HeaderList * hl = _header_list[scan];
4028 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
4031 if ((strcmp(hl->label, "To") == 0) ||
4032 (strcmp(hl->label, "Cc") == 0) ||
4033 (strcmp(hl->label, "Bcc") == 0)) {
4034 char * value = NULL;
4035 XtVaGetValues(hl->field_widget,
4039 for (char *cv = value; *cv; cv++) {
4040 if (!isspace(*cv)) {
4042 return(TRUE); // text value contains contents
4045 XtFree(value); // text value is "content free" - try the next one
4049 return(FALSE); // no field has contents