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)
163 form_widget = other.form_widget;
164 label_widget = other.label_widget;
165 field_widget = other.field_widget;
168 label = strdup(other.label);
172 header = strdup(other.header);
176 value = strdup(other.value);
180 SendMsgDialog::HeaderList::~HeaderList(void)
194 Boolean SendMsgDialog::reservedHeader(const char *label)
196 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++)
197 if (strcmp(label, hl->label) == 0)
203 SendMsgDialog::SendMsgDialog()
204 : MenuWindow ( "ComposeDialog", True ),
211 _show_attach_area = FALSE;
214 _already_sending = FALSE;
220 _bccPopupCmdlist = NULL;
221 _bccPopupMenu = NULL;
222 _bccPopupMenuBar = NULL;
223 _ccPopupCmdlist = NULL;
225 _ccPopupMenuBar = NULL;
226 _toPopupCmdlist = NULL;
228 _toPopupMenuBar = NULL;
231 _attachmentActionsList = NULL;
232 _attachmentMenu = NULL;
233 _attachmentMenuList = NULL;
234 _attachmentPopupMenuList = NULL;
235 _textPopupMenuList = NULL;
237 _att_show_pane = NULL;
238 _att_select_all = NULL;
242 _att_undelete = NULL;
244 _att_select_all = NULL;
245 _auto_save_interval = 0;
246 _auto_save_path = NULL;
247 _auto_save_file = NULL;
248 _dead_letter_buf = NULL;
250 _file_include = NULL;
251 _file_save_as = NULL;
256 _format_word_wrap = NULL;
257 _format_settings = NULL;
258 _format_find_change = NULL;
259 _format_spell = NULL;
262 _format_cascade = NULL;
263 _format_separator = NULL;
264 _templateList = NULL;
266 // Now we need to get the additional headers from the property system.
269 DtMail::Session * d_session = theRoamApp.session()->session();
270 DtMail::MailRc * mail_rc = d_session->mailRc(error);
272 const char * value = NULL;
273 mail_rc->getValue(error, "additionalfields", &value);
275 DtVirtArray<PropStringPair *> results(8);
276 if (error.isNotSet()) {
277 _additionalfields = strdup(value);
278 parsePropString(value, results);
284 // Load the header list with the predefined/fixed headers.
286 if (! isInitializedDefaultHeaderList)
287 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
289 GETMSG(DT_catd, hl->msg_set, hl->msg_number, hl->dflt_label);
291 isInitializedDefaultHeaderList = TRUE;
294 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
295 HeaderList * copy_hl = new HeaderList;
296 copy_hl->label = strdup(hl->label);
297 copy_hl->value = getPropStringValue(results, hl->label);
298 copy_hl->header = strdup(hl->header);
299 copy_hl->show = hl->show;
300 _header_list.append(copy_hl);
303 if (error.isNotSet()) {
305 for (int mrc = 0; mrc < results.length(); mrc++) {
306 PropStringPair * psp = results[mrc];
308 if (!reservedHeader(psp->label)) {
309 HeaderList * copy_hl = new HeaderList;
310 copy_hl->label = strdup(psp->label);
311 copy_hl->header = strdup(psp->label);
313 copy_hl->value = strdup(psp->value);
314 copy_hl->show = SMD_HIDDEN;
315 _header_list.append(copy_hl);
319 while(results.length()) {
320 PropStringPair * psp = results[0];
326 _additionalfields = NULL;
330 SendMsgDialog::~SendMsgDialog()
338 delete _file_include;
339 delete _file_save_as;
352 delete _edit_select_all;
358 XtDestroyWidget(_ccPopupMenu);
359 delete _ccPopupMenuBar;
360 delete _ccPopupCmdlist;
362 XtDestroyWidget(_toPopupMenu);
363 delete _toPopupMenuBar;
364 delete _toPopupCmdlist;
366 XtDestroyWidget(_bccPopupMenu);
367 delete _bccPopupMenuBar;
368 delete _bccPopupCmdlist;
375 delete _att_undelete;
377 delete _att_select_all;
379 delete _attachmentActionsList;
383 delete _format_word_wrap;
384 delete _format_settings;
385 delete _format_find_change;
386 delete _format_spell;
387 delete _format_separator;
389 // Things created by createWorkArea()
392 delete _close_button;
395 // Allocated using 'malloc'.
396 // Purify requires us to free it using 'free'.
398 free(_auto_save_path);
399 if (_auto_save_file) {
400 delete _auto_save_file;
402 if (_dead_letter_buf) {
403 delete _dead_letter_buf;
406 delete _attachmentMenuList;
407 delete _attachmentPopupMenuList;
408 delete _textPopupMenuList;
412 // Callback for each header row
414 header_form_traverse(Widget w, XtPointer, XtPointer)
416 (void) XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
421 send_pushb_callback(Widget, XtPointer, XtPointer)
425 #endif /* DEAD_WOOD */
427 // Create Message Handle with empty first body part.
429 SendMsgDialog::makeMessage(void)
432 DtMail::Session * d_session = theRoamApp.session()->session();
438 DtMail::Message * msg = d_session->messageConstruct(error,
444 if (error.isSet() || !msg) {
448 DtMail::BodyPart * bp = msg->newBodyPart(error, NULL);
450 // For now, reserve the first body part for text.
451 setFirstBPHandled(TRUE);
456 // Before Submitting, call this routine.
457 // This routine does not save attachment(s).
458 // Check point routine should also call this routine.
460 SendMsgDialog::updateMsgHnd()
463 DtMail::Envelope * env;
466 env = _msgHandle->getEnvelope(error);
469 char * widget_text = this->text();
470 if (widget_text && *widget_text == '\0') {
475 else if(widget_text) {
476 textLen = strlen(widget_text);
478 // Even if textlen is 0 because user has cleared all previous text,
479 // need to setContents again to clear first BP. Otherwise, deleted
480 // text will show up.
482 // Get FirstBodyPart and fill it up.
483 DtMail::BodyPart *bp = _msgHandle->getFirstBodyPart(error);
484 bp->setContents(error, widget_text, textLen, NULL, NULL, 0, NULL);
485 setFirstBPHandled(TRUE);
487 if (NULL != widget_text)
491 // Before Submitting, also call this routine.
492 // This routine fills the message handle with attachment(s).
493 // The reason why we get attachment(s) from the Back End before submission
494 // is in case the attachment(s) are updated during the compose session.
495 // If changes are saved back, then the AttachArea class would register the
496 // update with Back End. So BE always has the latest attachments.
498 SendMsgDialog::updateMsgHndAtt()
500 if ( _inclMsgHandle == NULL )
501 return; // No attachments
504 DtMail::BodyPart *msgBP;
505 DtMail::BodyPart *inclBP = _inclMsgHandle->getFirstBodyPart(error);
506 const void *contents;
513 // If message handle to be copied, _inclMsgHandle, does not have its first
514 // body part as text, then get its first body part content and copy it.
515 // If it contains text, then skip it.
516 if ( !_inclMsgHasText ) {
517 inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
518 if ( _firstBPHandled ) {
519 msgBP = _msgHandle->newBodyPart(error, _lastAttBP);
521 msgBP = _msgHandle->getFirstBodyPart(error);
523 msgBP->setContents(error, contents, len, type, name, mode, desc);
529 // Continue to get the next body part and start copy.
530 while ((inclBP = _inclMsgHandle->getNextBodyPart(error, inclBP)) != NULL) {
531 inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
532 if ( _firstBPHandled ) {
533 msgBP = _msgHandle->newBodyPart(error, _lastAttBP);
535 msgBP = _msgHandle->getFirstBodyPart(error);
537 msgBP->setContents(error, contents, len, type, name, mode, desc);
541 } // end of while loop
544 // Update the _lastAttBP pointer so that subsequent newBodyPart() calls can
545 // return a message body part following the last body part.
547 SendMsgDialog::setLastAttBP()
550 _lastAttBP = _msgHandle->getNextBodyPart(error, _lastAttBP);
553 // This sister routine is needed when _lastAttBP has not been initialized.
555 SendMsgDialog::setLastAttBP(DtMail::BodyPart *bp)
560 // Initialize _msgHandle
562 SendMsgDialog::setMsgHnd()
564 _msgHandle = makeMessage();
565 _inclMsgHandle = NULL;
566 _inclMsgHasText = FALSE;
570 // Set timeout to ten minutes (as milliseconds)
573 SendMsgDialog::startAutoSave(void)
576 if(!_auto_save_path) return;
577 _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
578 getAutoSaveInterval(),
584 SendMsgDialog::stopAutoSave(void)
586 if (!_auto_save_interval) {
590 XtRemoveTimeOut(_auto_save_interval);
591 _auto_save_interval = 0;
593 unlink(_auto_save_file);
594 free(_auto_save_file);
595 _auto_save_file = NULL;
598 static const char * DEAD_LETTER_DIR = "~/dead_letter";
599 static const char * BACKUP_DEAD_LETTER = "./dead_letter";
600 static const char * PREFIX = "mail.dead.letter";
603 SendMsgDialog::mkAutoSavePath(void)
605 // First, see if we need to set up the path.
608 if (!_auto_save_path) {
609 DtMail::Session * d_session = theRoamApp.session()->session();
611 const char *save_path = NULL;
613 d_session->mailRc(error)->getValue(error, "deaddir", &save_path);
614 if (error.isNotSet() && save_path != NULL && *save_path != '\0')
615 _auto_save_path = d_session->expandPath(error, save_path);
618 _auto_save_path = d_session->expandPath(error, DEAD_LETTER_DIR);
619 if (!_auto_save_path)
620 _auto_save_path = d_session->expandPath(error, BACKUP_DEAD_LETTER);
623 if (NULL != save_path)
624 free((void*) save_path);
627 // If we still have a path, punt.
629 if (!_auto_save_path) {
633 if (SafeAccess(_auto_save_path, W_OK) != 0) {
634 if (errno != ENOENT) {
635 // Not an error we can overcome here.
637 free(_auto_save_path);
638 _auto_save_path = NULL;
642 if (mkdir(_auto_save_path, 0700) < 0) {
643 free(_auto_save_path);
644 _auto_save_path = NULL;
649 // Now we run through the possible file names until we hit pay dirt.
651 _auto_save_file = (char*) malloc((size_t) strlen(_auto_save_path) + 100);
652 for (int suffix = 1; ; suffix++) {
653 sprintf(_auto_save_file, "%s/%s.%d", _auto_save_path, PREFIX, suffix);
654 if (SafeAccess(_auto_save_file, F_OK) != 0) {
661 SendMsgDialog::loadDeadLetter(const char * path)
663 _auto_save_file = strdup(path);
666 _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
667 getAutoSaveInterval(),
673 SendMsgDialog::autoSaveCallback(XtPointer client_data, XtIntervalId * id)
675 SendMsgDialog * self = (SendMsgDialog *)client_data;
677 if (self->_auto_save_interval != *id) {
678 // Random noise. Ignore it.
684 self->_auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
685 self->getAutoSaveInterval(),
691 SendMsgDialog::doAutoSave(char *filename)
693 DtMail::Session * d_session = theRoamApp.session()->session();
697 setStatus(GETMSG(DT_catd, 3, 70, "Writing dead letter..."));
701 assert((NULL != filename));
702 RFCWriteMessage(error, d_session, filename, _msgHandle);
703 if((DTMailError_t) error == DTME_OutOfSpace )
705 RoamMenuWindow::ShowErrMsg((char *)error.getClient(),TRUE,this );
712 SendMsgDialog::doAutoSave(void)
714 doAutoSave(_auto_save_file);
718 SendMsgDialog::getAutoSaveInterval(void)
721 // Initialize the mail_error.
725 DtMail::Session * d_session = theRoamApp.session()->session();
726 DtMail::MailRc * mail_rc = d_session->mailRc(error);
729 const char * value = NULL;
730 mail_rc->getValue(error, "composeinterval", &value);
731 if (error.isSet() || value == NULL) {
732 save_interval = 10 * 60 * 1000; // 10 minutes
735 save_interval = (int) strtol(value, NULL, 10) * 60 * 1000;
736 save_interval = (save_interval <= 0) ? 10 * 60 * 1000 : save_interval;
741 return(save_interval);
745 SendMsgDialog::setInclMsgHnd(DtMail::Message *msg, Boolean text)
747 _inclMsgHandle = msg;
748 _inclMsgHasText = text;
752 SendMsgDialog::setFirstBPHandled(Boolean handle)
754 _firstBPHandled = handle;
758 SendMsgDialog::setHeader(const char * name, const char * value)
760 // See if this header is in the list. If so, set the widget for
763 int slot = lookupHeader(name);
768 HeaderList * hl = _header_list[slot];
769 if (hl->show == SMD_NEVER) {
770 // The user removed this header via props.
775 if (hl->field_widget) {
776 XtVaSetValues(hl->field_widget,
779 if (hl->show == SMD_HIDDEN) {
780 changeHeaderState(name);
786 SendMsgDialog::setHeader(const char * name, DtMailValueSeq & value)
788 if (value.length() == 0) {
792 if (value.length() == 1) {
793 setHeader(name, *(value[0]));
797 for (int slen = 0; slen < value.length(); slen++) {
798 max_len += strlen(*(value[slen]));
801 char * new_str = new char[max_len + (value.length() * 3)];
804 for (int copy = 0; copy < value.length(); copy++) {
806 strcat(new_str, " ");
809 strcat(new_str, *(value[copy]));
812 setHeader(name, new_str);
818 SendMsgDialog::loadHeaders(DtMail::Message * input,
819 DtMailBoolean load_all)
821 // We are going to go through every header in the message.
822 // If it is not one of the headers we block, then we will
823 // load it into the header pane, depending on the setting of
824 // load_all. If true, then we load every header we allow and
825 // create new dynamic headers as necessary. If load_all is false,
826 // then we only load headers that already are available in the
830 DtMail::Message * msg = (input ? input : _msgHandle);
831 DtMail::Envelope * env = msg->getEnvelope(error);
832 DtMailHeaderHandle hnd;
836 DtMailValueSeq value;
838 for (hnd = env->getFirstHeader(error, &name, value);
839 error.isNotSet() && hnd;
840 hnd = env->getNextHeader(error, hnd, &name, value)) {
842 // Always ignore the Unix from line.
845 strcmp(name, "From") == 0) {
854 // See if the name is one we always block.
862 int slot = lookupHeader(name);
864 // We dont have a place for this information. We may need
865 // to create a new header.
868 HeaderList *hl = new HeaderList;
869 hl->label = strdup(name);
870 hl->header = strdup(name);
871 hl->show = SMD_HIDDEN;
872 _header_list.append(hl);
873 createHeaders(_header_form);
874 doDynamicHeaderMenus();
883 setHeader(name, value);
890 SendMsgDialog::storeHeaders(DtMail::Message * input)
892 DtMail::Message * msg = (input ? input : _msgHandle);
894 DtMail::Envelope * env = msg->getEnvelope(error);
896 // Walk through the headers. Fetch the strings from the ones
897 // that are visible to the user and stuff them into the
900 for (int scan = 0; scan < _header_list.length(); scan++) {
901 HeaderList * hl = _header_list[scan];
902 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
907 XtVaGetValues(hl->field_widget,
910 // If the header has a value, we want to set the value
911 // in the back end. Otherwise, we don't want to send
912 // out a blank header, so we remove it.
913 if (strlen(value) > 0) {
914 env->setHeader(error, hl->header, DTM_TRUE, value);
917 env->removeHeader(error, hl->header);
924 SendMsgDialog::changeHeaderState(const char * name)
926 int slot = lookupHeader(name);
931 HeaderList * hl = _header_list[slot];
932 if (hl->show == SMD_ALWAYS || hl->show == SMD_NEVER) {
936 // If the user is trying to remove a header with a value other than
937 // the default, we should at least ask.
939 if (hl->show == SMD_SHOWN) {
941 XtVaGetValues(hl->field_widget,
944 if (strlen(value) > 0) {
945 if (!hl->value || strcmp(value, hl->value) != 0) {
946 char *buf = new char[256];
948 GETMSG(DT_catd, 2, 17,
949 "You have edited \"%s\". Delete anyway?"),
951 _genDialog->setToWarningDialog(GETMSG(DT_catd, 3, 71,
954 char * helpId = DTMAILHELPERROR;
955 int answer = _genDialog->post_and_return(
956 GETMSG(DT_catd, 3, 72, "OK"),
957 GETMSG(DT_catd, 3, 73, "Cancel"),
970 // Now we need to toggle the current state of the header.
972 char *label = new char[100];
973 if (hl->show == SMD_SHOWN) {
974 XtUnmanageChild(hl->form_widget);
975 hl->show = SMD_HIDDEN;
976 sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
980 XtManageChild(hl->form_widget);
981 hl->show = SMD_SHOWN;
982 sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
988 // Change the label on the menu item.
990 strcat(label, hl->label);
992 char *button_name = new char[100];
993 sprintf(button_name, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
994 strcat(button_name, hl->label);
995 strcat(button_name, ":");
997 _menuBar->changeLabel(_format_menu, button_name, label);
999 delete [] button_name;
1003 SendMsgDialog::setStatus(const char * str)
1005 char *tmpstr = strdup(str);
1006 XmString label = XmStringCreateLocalized(tmpstr);
1008 XtVaSetValues(_status_text,
1009 XmNlabelString, label,
1012 XmUpdateDisplay(baseWidget());
1013 XmStringFree(label);
1018 SendMsgDialog::clearStatus(void)
1024 SendMsgDialog::isMsgValid(void)
1033 // Sendmail is exed'd and this parent process returns immediately. When
1034 // the sendmail child exits, this function is called with the pid of the
1035 // child and its status.
1037 SendMsgDialog::sendmailErrorProc (int, int status, void *data)
1039 SendMsgDialog *smd = (SendMsgDialog *)data;
1040 char *helpId = NULL;
1041 char *buf = new char[2048];
1043 smd->_first_time = TRUE;
1044 smd->_takeDown = FALSE;
1046 // pid is the child process (sendmail) id
1047 // status is the exit status of the child process
1048 // data is any extra data associated with the child process
1052 // The mail was successfully sent so return the compose
1053 // window to the cache and then return.
1054 smd->_send_button->activate();
1055 smd->_close_button->activate();
1059 case DTME_BadMailAddress:
1061 * There was an error in one or more of the email addresses.
1062 * Ask the user to type in a valid address and try again.
1064 sprintf(buf, "%s", GETMSG(DT_catd, 5, 5,
1065 "Some of the addresses in the message are incorrect,\n\
1066 and do not refer to any known users in the system.\n\
1067 Please make sure all of the addresses are valid and try again."));
1068 helpId = DTMAILHELPBADADDRESS;
1074 * Mailer ran out of memory. Ask the user to quit some other
1075 * applications so there will be more memory available.
1078 sprintf(buf, "%s", GETMSG(DT_catd, 5, 6,
1079 "Mailer does not have enough memory\n\
1080 available to send this message.\n\
1081 Try quitting other applications and\n\
1082 resend this message."));
1083 helpId = DTMAILHELPNOMEMORY;
1087 case DTME_TransportFailed:
1090 * There was an error from the mail transport (sendmail).
1093 sprintf(buf, "%s", GETMSG(DT_catd, 5, 7,
1094 "An error occurred while trying to send your message.\n\
1095 Check to make sure the message was received. If not,\n\
1096 you may have to resend this message."));
1097 helpId = DTMAILHELPTRANSPORTFAILED;
1100 // popup the compose window
1102 smd->_send_button->activate();
1103 smd->_close_button->activate();
1105 // popup the error dialog
1106 smd->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1108 smd->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1114 SendMsgDialog::send_message(const char * trans_impl, int trans_type)
1116 DtMailEnv mail_exec_error;
1117 DtMailOperationId id;
1118 DtMail::Transport * mail_transport;
1119 DtMail::Session * d_session = theRoamApp.session()->session();
1120 DtMailEditor *editor = this->get_editor();
1121 AttachArea *attachArea = editor->attachArea();
1122 int numPendingActions, answer;
1123 char *helpId = NULL;
1124 char *buf = new char[2048];
1125 static int first_time = 1;
1128 // Check to see if we are already trying to send from this
1129 // SendMsgDialog. If we are, we don't want to do it again.
1130 if (_already_sending) {
1134 _already_sending = TRUE;
1137 // First remove (unmap) the window;
1138 // send the message.
1139 // If you do it in the reverse order, users get confused coz
1140 // the window remains behind for a couple seconds after hitting
1141 // "Send" and it ...
1143 _send_button->deactivate();
1144 _close_button->deactivate();
1146 mail_exec_error.clear();
1149 // Check if message has addressees. If it doesn't, what sense does
1150 // it make to Send it?
1152 if (!this->hasAddressee()) {
1153 // Message has no valid addressee. Pop up error dialog.
1155 sprintf(buf, "%s", GETMSG(DT_catd, 5, 8,
1156 "Try Send after specifying recipient(s) of the message in \nthe To:, Cc:, or Bcc: fields."));
1158 helpId = DTMAILHELPNEEDADDRESSEE;
1160 // Need help tag for above HelpID.
1162 // popup the compose window
1164 _send_button->activate();
1165 _close_button->activate();
1167 _genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1169 _genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1171 // Reset the flag before we return.
1172 _already_sending = FALSE;
1178 // Since we are Send-ing, the SMD can be taken down later without
1179 // checking for dirty...
1186 // Just get text from text widget; attachment BPs are filled
1187 // already when they are included/forwarded/added.
1191 // Check if there are any pending attachments (open, print....)
1192 // If there are, pop up the dialog.
1193 // If the user wants to Send the message as is, continue with the
1194 // submission process.
1195 // If the user opted to Cancel, then return.
1198 numPendingActions = attachArea->getNumPendingActions();
1199 sprintf(buf, "%s", GETMSG(
1203 "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." ));
1205 while (numPendingActions != 0) {
1206 // popup the compose window
1208 _send_button->activate();
1209 _close_button->activate();
1212 * The user tried to send a messages without saving changes in
1213 * some open attachments. This warning makes sure that is what
1214 * the user intended.
1217 _genDialog->setToQuestionDialog(
1218 GETMSG(DT_catd, 5, 1, "Mailer"),
1220 helpId = DTMAILHELPPENDINGACTIONS;
1222 answer = _genDialog->post_and_return(helpId);
1226 numPendingActions = 0;
1228 _send_button->deactivate();
1229 _close_button->deactivate();
1231 else if (answer == 2) {
1233 // Reset the flag before we return.
1234 _already_sending = FALSE;
1240 // Determine which transport mechanism will be used.
1241 if ( trans_type ) { // Default
1242 // Only register XtAppAddInput once
1245 // Create the pipe between the RFCTransport::childHandler
1246 // and XtAppAddInput
1247 if (pipe(_transfds) < 0) {
1248 // If this failed, make sure we try to initialize again later.
1249 mail_exec_error.setError(DTME_NoMemory);
1250 popupMemoryError (mail_exec_error);
1252 // Reset the flag before we return.
1253 _already_sending = FALSE;
1258 // Call ourproc when input is available on _transfds[0]
1259 XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1260 _transfds[0], (XtPointer)XtInputReadMask,
1261 (XtInputCallbackProc)
1262 (theRoamApp.default_transport()->getSendmailReturnProc()),
1267 // Tell the transport where the callback is
1268 theRoamApp.default_transport()->initTransportData( _transfds,
1269 &(SendMsgDialog::sendmailErrorProc), this);
1270 id = theRoamApp.default_transport()->submit(mail_exec_error,
1271 _msgHandle, _log_msg);
1274 // Construct transport
1275 mail_transport = d_session->transportConstruct(mail_exec_error,
1276 trans_impl, RoamApp::statusCallback, this);
1278 // Only register XtAppAddInput once
1281 // Create the pipe between the RFCTransport::childHandler
1282 // and XtAppAddInput
1283 if (pipe(_transfds) < 0) {
1284 // If this failed, make sure we try to initialize again later.
1285 mail_exec_error.setError(DTME_NoMemory);
1286 popupMemoryError (mail_exec_error);
1288 // Reset the flag before we return.
1289 _already_sending = FALSE;
1294 // Call ourproc when input is available on _transfds[0]
1295 XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1296 _transfds[0], (XtPointer)XtInputReadMask,
1297 (XtInputCallbackProc)(mail_transport->getSendmailReturnProc()),
1302 // Tell the transport where the callback is
1303 mail_transport->initTransportData(_transfds,
1304 &(SendMsgDialog::sendmailErrorProc), this);
1305 id = mail_transport->submit(mail_exec_error, _msgHandle, _log_msg);
1308 popupMemoryError (mail_exec_error);
1310 // Reset the flag before we return.
1311 _already_sending = FALSE;
1317 SendMsgDialog::popupMemoryError(DtMailEnv &error)
1319 char *helpId = NULL;
1320 char *buf = new char[2048];
1324 // Popup an error dialog if necessary.
1325 if (error.isSet()) {
1326 if ((DTMailError_t)error == DTME_NoMemory) {
1329 * Mailer ran out of memory. Ask the user to quit some other
1330 * applications so there will be more memory available.
1332 sprintf(buf, "%s", GETMSG(DT_catd, 5, 6,
1333 "Mailer does not have enough memory\n\
1334 available to send this message.\n\
1335 Try quitting other applications and\n\
1336 resend this message."));
1337 helpId = DTMAILHELPNOMEMORY;
1341 * An unidentifiable error happened during mail transport
1342 * Pop it up *as is* (need to update this function if so)
1344 sprintf(buf, "%s", (const char *)error);
1345 helpId = DTMAILHELPERROR;
1348 // popup the compose window
1350 _send_button->activate();
1351 _close_button->activate();
1353 // popup the error dialog
1354 this->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1356 this->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"),
1364 SendMsgDialog::createWorkArea ( Widget parent )
1366 FORCE_SEGV_DECL(CmdInterface, ci);
1369 // Create the parent form
1371 _main_form = XmCreateForm( parent, "Work_Area", NULL, 0 );
1372 XtVaSetValues(_main_form, XmNresizePolicy, XmRESIZE_NONE, NULL);
1374 printHelpId("form", _main_form);
1375 /* add help callback */
1376 XtAddCallback(_main_form, XmNhelpCallback, HelpCB, (void *)DTMAILCOMPOSEWINDOW);
1377 XtVaSetValues(_main_form, XmNallowResize, True, NULL);
1380 // Create the area for status messages.
1382 _status_form = XtVaCreateManagedWidget("StatusForm",
1383 xmFormWidgetClass, _main_form,
1384 XmNtopAttachment, XmATTACH_FORM,
1385 XmNrightAttachment, XmATTACH_FORM,
1387 XmNleftAttachment, XmATTACH_FORM,
1391 _status_text = XtVaCreateManagedWidget("StatusLabel",
1392 xmLabelWidgetClass, _status_form,
1393 XmNtopAttachment, XmATTACH_FORM,
1394 XmNbottomAttachment, XmATTACH_FORM,
1395 XmNrightAttachment, XmATTACH_FORM,
1396 XmNleftAttachment, XmATTACH_FORM,
1397 XmNalignment, XmALIGNMENT_BEGINNING,
1402 Widget s_sep = XtVaCreateManagedWidget("StatusSep",
1403 xmSeparatorGadgetClass,
1405 XmNtopAttachment, XmATTACH_WIDGET,
1406 XmNtopWidget, _status_form,
1407 XmNleftAttachment, XmATTACH_FORM,
1408 XmNrightAttachment, XmATTACH_FORM,
1411 _header_form = XtVaCreateManagedWidget("HeaderArea",
1412 xmFormWidgetClass, _main_form,
1413 XmNtopAttachment, XmATTACH_WIDGET,
1414 XmNtopWidget, s_sep,
1415 XmNleftAttachment, XmATTACH_FORM,
1416 XmNrightAttachment, XmATTACH_FORM,
1418 printHelpId("header_form", _header_form);
1420 createHeaders(_header_form);
1422 Widget sep1 = XtVaCreateManagedWidget("Sep1",
1423 xmSeparatorGadgetClass,
1425 XmNtopAttachment, XmATTACH_WIDGET,
1426 XmNtopWidget, _header_form,
1428 XmNrightAttachment, XmATTACH_FORM,
1429 XmNleftAttachment, XmATTACH_FORM,
1432 // Create the editor and attach it to the header_form
1434 _my_editor = new DtMailEditor(_main_form, this);
1436 _my_editor->initialize();
1437 _my_editor->attachArea()->setOwnerShell(this);
1438 _my_editor->setEditable(TRUE);
1439 _my_editor->manageAttachArea();
1441 // Create a RowCol widget that contains buttons
1443 send_form = XtCreateManagedWidget("SendForm",
1444 xmFormWidgetClass, _main_form, NULL, 0);
1447 // Create the Send and Close buttons as children of rowCol
1449 _send_button = new SendCmd ( "Send",
1450 GETMSG(DT_catd, 1, 230, "Send"),
1454 ci = new ButtonInterface (send_form, _send_button);
1456 XtVaSetValues(ci->baseWidget(),
1457 XmNleftAttachment, XmATTACH_FORM,
1458 XmNleftOffset, OFFSET,
1459 XmNbottomAttachment, XmATTACH_FORM,
1463 Widget send_bw = ci->baseWidget();
1464 XtManageChild(send_bw);
1467 _close_button = new CloseCmd (
1469 GETMSG(DT_catd, 1, 118, "Close"),
1473 ci = new ButtonInterface (send_form, _close_button);
1474 XtVaSetValues(ci->baseWidget(),
1476 XmNleftAttachment, XmATTACH_WIDGET,
1477 XmNleftWidget, send_bw,
1478 XmNbottomAttachment, XmATTACH_FORM,
1482 XtManageChild(ci->baseWidget());
1485 // Now attach the editor to the form and to the rowCol
1486 // And the rowCol to the bottom of the form.
1487 // We need this attachment ordering so that resizes always
1488 // get transferred to the editor.
1490 Widget wid = _my_editor->container();
1492 XmNleftAttachment, XmATTACH_FORM,
1493 XmNrightAttachment, XmATTACH_FORM,
1494 XmNtopAttachment, XmATTACH_WIDGET,
1497 XmNbottomAttachment, XmATTACH_WIDGET,
1498 XmNbottomWidget, send_form,
1501 XtVaSetValues(send_form,
1502 XmNbottomAttachment, XmATTACH_FORM,
1507 HeaderList * hl = _header_list[0];
1508 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
1510 // Set the title to be New Message
1511 //char *ttl = GETMSG(DT_catd, 1, 119, "New Message");
1512 //this->setTitle(ttl);
1513 //this->setIconTitle(ttl);
1515 XtManageChild(_main_form);
1521 SendMsgDialog::createHeaders(Widget header_form)
1523 Widget previous_form = NULL;
1524 char *field_name = new char[50];
1526 for (int header = 0; header < _header_list.length(); header++) {
1527 HeaderList * hl = _header_list[header];
1529 // We use SMD_NEVER to indicate the header has disappeared from
1532 if (hl->show == SMD_NEVER) {
1536 // If the widgets already exist, then simply manage them.
1537 if (hl->form_widget) {
1538 previous_form = hl->form_widget;
1540 XtVaSetValues(hl->field_widget,
1541 XmNvalue, hl->value,
1544 XtVaSetValues(hl->field_widget,
1550 if (previous_form == NULL) {
1551 // Create a form, attaching it to the top. This is a special
1552 // case. Other lines are created attached to the form above
1554 strcpy(field_name, "form_");
1555 strncat(field_name, hl->label, 45);
1556 field_name[strlen(hl->label) + 5] = 0;
1558 XtVaCreateWidget(field_name,
1561 XmNtopAttachment, XmATTACH_FORM,
1563 XmNleftAttachment, XmATTACH_FORM,
1565 XmNrightAttachment, XmATTACH_FORM,
1571 strcpy(field_name, "form_");
1572 strncat(field_name, hl->label, 45);
1573 field_name[strlen(hl->label) + 5] = 0;
1575 XtVaCreateWidget(field_name,
1578 XmNtopAttachment, XmATTACH_WIDGET,
1579 XmNtopWidget, previous_form,
1580 XmNleftAttachment, XmATTACH_FORM,
1582 XmNrightAttachment, XmATTACH_FORM,
1588 // The label will be to the left of the form.
1590 strcpy(field_name, hl->label);
1591 strcat(field_name, ":");
1592 XmString label = XmStringCreateLocalized(field_name);
1594 XtVaCreateManagedWidget(hl->label,
1597 XmNtopAttachment, XmATTACH_FORM,
1598 XmNbottomAttachment, XmATTACH_FORM,
1599 XmNleftAttachment, XmATTACH_FORM,
1600 XmNlabelString, label,
1602 XmStringFree(label);
1604 strcpy(field_name, "field_");
1605 strncat(field_name, hl->label, 43);
1606 field_name[strlen(hl->label) + 6] = 0;
1609 XtVaCreateManagedWidget(field_name,
1610 xmTextFieldWidgetClass,
1612 XmNtraversalOn, True,
1613 XmNtopAttachment, XmATTACH_FORM,
1614 XmNrightAttachment, XmATTACH_FORM,
1615 XmNleftAttachment, XmATTACH_WIDGET,
1616 XmNleftWidget, hl->label_widget,
1619 if (hl->show != SMD_HIDDEN) {
1620 XtManageChild(hl->form_widget);
1623 XtVaSetValues(hl->form_widget,
1624 XmNtopAttachment, XmATTACH_NONE,
1628 XtAddCallback(hl->field_widget,
1629 XmNactivateCallback,
1630 header_form_traverse,
1633 XtAddCallback(hl->field_widget,
1634 XmNvalueChangedCallback,
1639 XtVaSetValues(hl->field_widget,
1640 XmNvalue, hl->value,
1644 previous_form = hl->form_widget;
1648 delete [] field_name;
1652 SendMsgDialog::doDynamicHeaderMenus(void)
1654 // This is really a pain, but we have to blow away the list to
1655 // build another one. This could probably be done more efficiently,
1656 // but we wont try to figure out how right now.
1659 _menuBar->removeOnlyCommands(_format_menu, _format_cmds);
1662 _format_cmds = new CmdList("DynamicFormatCommands", "DynamicFormatCommands");
1664 // Only put on commands that are shown or hidden. The items that
1665 // are always are never should not be presented to the user as
1666 // an option to change.
1668 char *label = new char[100];
1670 for (int h = 0; h < _header_list.length(); h++) {
1671 HeaderList * hl = _header_list[h];
1675 sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
1679 sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
1686 strcat(label, hl->label);
1689 char * priv_label = strdup(label);
1691 Cmd * new_cmd = new HideShowCmd(priv_label, priv_label,
1692 1, this, hl->label);
1694 // Add the commands one at a time with addCommand() vs. all
1695 // at once with addCommands(). That way new commands will
1696 // be created instead of reusing old ones.
1697 _menuBar->addCommand(_format_menu, new_cmd);
1698 _format_cmds->add(new_cmd);
1706 // Should theInfoDialogManager be destroyed here ???
1711 SendMsgDialog::open_att_cb( void *clientData, char *selection )
1713 SendMsgDialog *obj = (SendMsgDialog *)clientData;
1715 obj->open_att(selection);
1719 SendMsgDialog::open_att( char *) // arg is char *selection
1722 #endif /* DEAD_WOOD */
1725 SendMsgDialog::include_file_cb( void *client_data, char *selection )
1727 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1728 obj->include_file(selection);
1733 SendMsgDialog::include_file(
1738 char *buf = new char[MAXPATHLEN];
1740 // I don't need to open the file to see if it's readable if loadFile()
1741 // returns error status.
1742 if ( (fp = fopen(selection, "r")) == NULL ) {
1743 sprintf(buf, GETMSG(DT_catd, 2, 18, "Error: Cannot include file %s"),
1745 theInfoDialogManager->post(
1748 (void *)this->_file_include,
1752 this->_my_editor->textEditor()->append_to_contents("\n", 2);
1753 this->_my_editor->textEditor()->append_at_cursor(selection);
1754 this->_my_editor->textEditor()->append_to_contents("\n", 2);
1760 SendMsgDialog::get_confirm_attachment_threshold()
1763 DtMail::Session *m_session = theRoamApp.session()->session();
1764 const char *value = NULL;
1767 m_session->mailRc(error)->getValue(error, "confirmattachments", &value);
1768 if (error.isSet()) return 0;
1770 m_session->mailRc(error)->getValue(error, "confirmattachmentthreshold",
1772 if (error.isNotSet() && NULL!=value)
1773 threshold = 1024 * atoi(value);
1775 threshold = 1024 * 64;
1781 SendMsgDialog::confirm_add_attachment(char *file, int size)
1783 char *buf = new char[BUFSIZ];
1788 GETMSG(DT_catd, 1, 263,
1789 "The attachment '%s' is %d kilobytes.\nAdd as attachment?");
1790 sprintf(buf, format, file, size/1024);
1791 _genDialog->setToQuestionDialog(GETMSG(DT_catd, 5, 2, "Mailer"), buf);
1792 answer = _genDialog->post_and_return(NULL);
1797 SendMsgDialog::add_att_cb( void *client_data, char *selection )
1799 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1800 obj->add_att(selection);
1801 if (NULL != selection)
1806 SendMsgDialog::add_att(char *file)
1808 struct stat statbuf;
1810 if (-1 != stat(file, &statbuf) && _confirm_attachment_threshold &&
1811 _confirm_attachment_threshold < statbuf.st_size)
1813 if (! confirm_add_attachment(file, statbuf.st_size)) return;
1816 // Activate Attachment menu???
1817 this->get_editor()->attachArea()->
1818 addAttachment(_msgHandle, _lastAttBP, file, NULL);
1819 this->setLastAttBP();
1820 this->activate_default_attach_menu();
1822 // This will manage the attach pane too.
1823 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1827 SendMsgDialog::add_att(char *name, DtMailBuffer buf)
1829 if (_confirm_attachment_threshold &&
1830 _confirm_attachment_threshold < buf.size)
1832 if (! confirm_add_attachment("", buf.size)) return;
1835 this->get_editor()->attachArea()->
1836 addAttachment(_msgHandle, _lastAttBP, name, buf);
1837 this->setLastAttBP();
1838 this->activate_default_attach_menu();
1840 // This will manage the attach pane too.
1841 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1845 SendMsgDialog::add_att(DtMailBuffer buf)
1852 SendMsgDialog::save_att_cb( void *client_data, char *selection )
1854 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1856 obj->save_selected_attachment(selection);
1861 SendMsgDialog::save_selected_attachment(
1865 DtMailEnv mail_error;
1869 AttachArea *attarea = this->get_editor()->attachArea();
1870 Attachment *attachment = attarea->getSelectedAttachment();
1872 // Get selected attachment, if none selected, then return.
1873 if ( attachment == NULL ) {
1874 // Let User know that no attachment has been selected???
1876 char *helpId = NULL;
1879 _genDialog->setToErrorDialog(
1880 GETMSG(DT_catd, 1, 120, "Mailer"),
1881 GETMSG(DT_catd, 2, 19, "An attachment needs to be selected before issuing the\n\"Save As\" command to save to a file.") );
1882 helpId = DTMAILHELPSELECTATTACH;
1883 answer = _genDialog->post_and_return(
1884 GETMSG(DT_catd, 3, 74, "OK"), helpId );
1888 // Save selected attachments.
1889 attachment->saveToFile(mail_error, selection);
1890 if ( mail_error.isSet() ) {
1891 // Let User know error condition???
1897 SendMsgDialog::propsChanged(void)
1899 DtMail::Session *m_session = theRoamApp.session()->session();
1900 const char * value = NULL;
1903 enableWorkAreaResize();
1905 m_session->mailRc(error)->getValue(error, "hideattachments", &value);
1907 if (_show_attach_area) {
1908 _show_attach_area = FALSE;
1909 this->hideAttachArea();
1913 if (!_show_attach_area) {
1914 _show_attach_area = TRUE;
1915 this->showAttachArea();
1918 free((void*) value);
1920 _confirm_attachment_threshold = get_confirm_attachment_threshold();
1923 const char * logfile = NULL;
1924 m_session->mailRc(error)->getValue(error, "record", &logfile);
1925 if (logfile == NULL)
1926 _file_log->deactivate();
1928 _file_log->activate();
1930 _my_editor->textEditor()->update_display_from_props();
1932 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
1933 if (logfile == NULL || error.isNotSet()) {
1934 // logfile is not specified or "dontlogmessages" is TRUE
1935 setLogState(DTM_FALSE);
1936 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
1938 // logfile is specified and "dontlogmessages" is FALSE
1939 setLogState(DTM_TRUE);
1940 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
1943 if (NULL != logfile)
1944 free((void*) logfile);
1946 free((void*) value);
1949 m_session->mailRc(error)->getValue(error, "templates", &value);
1950 if ( (value == NULL && _templateList != NULL) ||
1952 (_templateList == NULL || strcmp(value, _templateList)) != 0) ) {
1953 // Template list has changed
1954 if (_templateList != NULL)
1955 free (_templateList);
1956 if (value != NULL && *value != '\0')
1957 _templateList = strdup(value);
1959 _templateList = NULL;
1963 free((void*) value);
1965 // Alias Popup Menus
1966 DtVirtArray<PropStringPair*> *newAliases;
1967 Boolean aliasesChanged = FALSE;
1969 newAliases = new DtVirtArray<PropStringPair*> (10);
1970 createAliasList(newAliases);
1971 if (newAliases->length() == _aliasList->length())
1973 int length = newAliases->length();
1976 while (i<length && aliasesChanged==FALSE)
1978 PropStringPair *p1 = (*newAliases)[i];
1979 PropStringPair *p2 = (*_aliasList)[i];
1981 if ( strncmp(p1->label, p2->label, strlen(p2->label)) ||
1982 strncmp(p1->value, p2->value, strlen(p2->value)) )
1983 aliasesChanged = TRUE;
1989 aliasesChanged = TRUE;
1991 if (aliasesChanged == TRUE)
1993 destroyAliasPopupMenus();
1994 destroyAliasList(_aliasList);
1995 _aliasList = newAliases;
1996 createAliasPopupMenus();
1999 destroyAliasList(newAliases);
2001 disableWorkAreaResize();
2005 SendMsgDialog::createMenuPanes()
2010 const char * value = NULL;
2013 _separator = new SeparatorCmd( "Separator","Separator", TRUE );
2016 cmdList = new CmdList( "File", GETMSG(DT_catd, 1, 121, "File") );
2018 // Default directory is set below at the same time as the default
2019 // directory for att_add.
2020 _file_include = new UnifiedSelectFileCmd (
2022 GETMSG(DT_catd, 1, 122, "Include..."),
2023 GETMSG(DT_catd, 1, 123, "Mailer - Include"),
2024 GETMSG(DT_catd, 1, 124, "Include"),
2026 SendMsgDialog::include_file_cb,
2028 this->baseWidget());
2031 // Remap OK button to Include
2032 // XtVaSetValues(_file_include->fileBrowser,
2033 // XmNokLabelString, GETMSG(DT_catd,
2034 // 1, 77, "Include"), NULL);
2035 _file_save_as = new SaveAsTextCmd(
2037 GETMSG(DT_catd, 1, 125, "Save As Text..."),
2038 GETMSG(DT_catd, 1, 126, "Mailer - Save As Text"),
2040 get_editor()->textEditor(),
2044 _file_log = new LogMsgCmd (
2046 GETMSG(DT_catd, 1, 127, "Log Message"), TRUE, this);
2048 // 1 for default transport.
2050 _file_send = new SendCmd (
2052 GETMSG(DT_catd, 1, 117, "Send"),
2057 // Find out how many transports there are and build sub menu dynamically.
2058 DtMail::Session *d_session;
2060 if ( theRoamApp.session() == NULL ) {
2061 MailSession *new_session = new MailSession(
2063 theApplication->appContext());
2064 theRoamApp.setSession(new_session);
2067 CmdList *subcmdList1 = new CmdList (
2069 GETMSG(DT_catd, 1, 128, "Send As") );
2071 d_session = theRoamApp.session()->session();
2072 const char **impls = d_session->enumerateImpls(error);
2074 for ( int impl = 0; impls[impl]; impl++ ) {
2075 DtMailBoolean trans;
2076 d_session->queryImpl(error, impls[impl],
2077 DtMailCapabilityTransport, &trans);
2078 if (!error.isSet() && trans == DTM_TRUE ) {
2079 _file_sendAs[_num_sendAs] = new SendCmd( strdup(impls[impl]),
2080 (char *)impls[impl],
2084 subcmdList1->add( _file_sendAs[_num_sendAs] );
2087 // Assume an error means this query failed. But keep going and
2088 // get the next transport.
2091 _file_close = new CloseCmd (
2093 GETMSG(DT_catd, 1, 129, "Close"),
2095 _menuBar->baseWidget(),
2098 // Now build the menu
2100 cmdList->add( _file_include );
2101 cmdList->add( _file_save_as );
2102 cmdList->add( _file_log );
2103 cmdList->add( _separator );
2105 cmdList->add( _file_send );
2106 #if defined(USE_SEND_AS_MENU)
2107 cmdList->add( subcmdList1 );
2109 cmdList->add( _separator );
2111 cmdList->add( _file_close );
2113 _menuBar->addCommands ( cmdList );
2119 cmdList = new CmdList( "Edit", GETMSG(DT_catd, 1, 130, "Edit") );
2121 _edit_undo = new EditUndoCmd ( "Undo",
2122 GETMSG(DT_catd, 1, 131, "Undo"),
2124 _edit_cut = new EditCutCmd ( "Cut",
2125 GETMSG(DT_catd, 1, 132, "Cut"),
2127 _edit_copy = new EditCopyCmd ( "Copy",
2128 GETMSG(DT_catd, 1, 133, "Copy"),
2130 _edit_paste = new EditPasteCmd ( "Paste",
2131 GETMSG(DT_catd, 1, 134 , "Paste"),
2135 // Begin Paste Special submenu
2136 subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2137 _edit_paste_special[0] = new EditPasteSpecialCmd (
2139 GETMSG(DT_catd, 1, 136 , "Bracketed"),
2140 TRUE, this, Editor::IF_BRACKETED
2142 subcmdList1->add(_edit_paste_special[0]);
2143 _edit_paste_special[1] = new EditPasteSpecialCmd (
2145 GETMSG(DT_catd, 1, 137 , "Indented"),
2146 TRUE, this, Editor::IF_INDENTED );
2147 subcmdList1->add(_edit_paste_special[1]);
2148 // End Paste Special submenu
2150 _edit_clear = new EditClearCmd ( "Clear", GETMSG(DT_catd, 1, 138, "Clear"),
2153 _edit_delete = new EditDeleteCmd ( "Delete", GETMSG(DT_catd, 1, 139, "Delete"),
2156 _edit_select_all = new EditSelectAllCmd (
2158 GETMSG(DT_catd, 1, 140, "Select All"),
2161 _format_find_change = new FindChangeCmd (
2163 GETMSG(DT_catd, 1, 155, "Find/Change..."),
2166 _format_spell = new SpellCmd (
2167 "Check Spelling...",
2168 GETMSG(DT_catd, 1, 156, "Check Spelling..."),
2172 cmdList->add( _edit_undo );
2173 cmdList->add( _separator );
2174 cmdList->add( _edit_cut );
2175 cmdList->add( _edit_copy );
2176 cmdList->add( _edit_paste );
2177 cmdList->add( subcmdList1 ); // Add Paste Special submenu
2178 cmdList->add( _separator );
2179 cmdList->add( _edit_clear );
2180 cmdList->add( _edit_delete );
2181 cmdList->add( _separator );
2182 cmdList->add( _edit_select_all );
2183 cmdList->add( _separator );
2184 cmdList->add( _format_find_change );
2187 * SpellCheck is not supported by Base System for the multibyte language
2188 * currently. ( See dtpad's source ) So that this should be disabled.
2189 * See Defect 174873. (I should think this solution is not good one, but..)
2190 * What is the best way to check if I'm in MB or SB.....???
2193 if ( MB_CUR_MAX == 1 )
2194 cmdList->add( _format_spell );
2196 _menuBar->addCommands ( cmdList );
2200 // Alias Popup Menus
2201 if (NULL != _aliasList) delete _aliasList;
2202 _aliasList = new DtVirtArray<PropStringPair*> (10);
2203 createAliasList(_aliasList);
2204 createAliasPopupMenus();
2206 // Compose Popup CmdList
2207 construct_text_popup();
2211 cmdList = new CmdList(
2213 GETMSG(DT_catd, 1, 141, "Attachments"));
2215 _att_add = new UnifiedSelectFileCmd (
2217 GETMSG(DT_catd, 1, 142, "Add File..."),
2218 GETMSG(DT_catd, 1, 143, "Mailer - Add"),
2219 GETMSG(DT_catd, 1, 144, "Add"),
2221 SendMsgDialog::add_att_cb,
2223 this->baseWidget());
2225 _att_save = new SaveAttachCmd (
2227 GETMSG(DT_catd, 1, 145, "Save As..."),
2228 GETMSG(DT_catd, 1, 146,
2229 "Mailer - Attachments - Save As"),
2231 SendMsgDialog::save_att_cb,
2233 this->baseWidget());
2234 _att_delete = new DeleteAttachCmd (
2236 GETMSG(DT_catd, 1, 147, "Delete"),
2239 _att_undelete = new UndeleteAttachCmd (
2241 GETMSG(DT_catd, 1, 148, "Undelete"),
2244 _att_rename = new RenameAttachCmd(
2246 GETMSG(DT_catd, 1, 149, "Rename"),
2250 _att_select_all = new SelectAllAttachsCmd(
2252 GETMSG(DT_catd, 1, 150, "Select All"),
2256 * This is the label for a toggle item in a menu. When the item
2257 * is set to "Show List", the Attachment List is mapped in the
2258 * Compose Window. This message replaces message 151 in set 1.
2260 _att_show_pane = new ShowAttachPaneCmd(
2262 GETMSG(DT_catd, 1, 226, "Show List"),
2265 cmdList->add( _att_add );
2266 cmdList->add( _att_save );
2267 cmdList->add( _separator );
2269 // subcmdList1 = new CmdList ( "Create", "Create" );
2270 // // subcmdList1->add( att_audio );
2271 // // subcmdList1->add( att_appt );
2272 // cmdList->add( subcmdList1 );
2273 // cmdList->add( _separator );
2275 cmdList->add( _att_delete );
2276 cmdList->add( _att_undelete );
2277 cmdList->add( _att_rename );
2278 cmdList->add( _att_select_all );
2279 cmdList->add(_att_show_pane);
2281 // Create a pulldown from the items in the list. Retain a handle
2282 // to that pulldown since we need to dynamically add/delete entries
2283 // to this menu based on the selection of attachments.
2285 _attachmentMenu = _menuBar->addCommands ( cmdList );
2286 construct_attachment_popup();
2288 // delete subcmdList1;
2292 d_session->mailRc(error)->getValue(error, "templates", &value);
2293 if (value != NULL && *value != '\0')
2294 _templateList = strdup(value);
2296 free((void*) value);
2300 _overview = new OnAppCmd("Overview",
2301 GETMSG(DT_catd, 1, 71, "Overview"),
2303 _tasks = new TasksCmd("Tasks", GETMSG(DT_catd, 1, 72, "Tasks"),
2305 _reference = new ReferenceCmd("Reference",
2306 GETMSG(DT_catd, 1, 73, "Reference"),
2308 _on_item = new OnItemCmd("On Item", GETMSG(DT_catd, 1, 74, "On Item"),
2310 _using_help = new UsingHelpCmd("Using Help",
2311 GETMSG(DT_catd, 1, 75, "Using Help"),
2313 _about_mailer = new RelNoteCmd("About Mailer...",
2314 GETMSG(DT_catd, 1, 77, "About Mailer..."),
2316 cmdList = new CmdList("Help", GETMSG(DT_catd, 1, 76, "Help"));
2317 cmdList->add(_overview);
2318 cmdList->add(_separator);
2319 cmdList->add(_tasks);
2320 cmdList->add(_reference);
2321 cmdList->add(_separator);
2322 cmdList->add(_on_item);
2323 cmdList->add(_separator);
2324 cmdList->add(_using_help);
2325 cmdList->add(_separator);
2326 cmdList->add(_about_mailer);
2327 _menuBar->addCommands(cmdList, TRUE);
2332 SendMsgDialog::construct_attachment_popup(void)
2334 _attachmentPopupMenuList = new CmdList( "AttachmentsPopup", "AttachmentsPopup");
2336 LabelCmd *title = new LabelCmd (
2337 "Mailer - Attachments",
2338 GETMSG(DT_catd, 1, 158, "Mailer - Attachments"), TRUE);
2339 SeparatorCmd *separator = new SeparatorCmd( "Separator","Separator", TRUE );
2341 _attachmentPopupMenuList->add(title);
2342 _attachmentPopupMenuList->add(separator);
2343 _attachmentPopupMenuList->add( _att_add );
2344 _attachmentPopupMenuList->add( _att_save );
2345 _attachmentPopupMenuList->add( _att_delete );
2346 _attachmentPopupMenuList->add( _att_undelete );
2347 _attachmentPopupMenuList->add( _att_select_all );
2349 _menuPopupAtt = new MenuBar(_my_editor->attachArea()->getClipWindow(),
2350 "RoamAttachmentPopup", XmMENU_POPUP);
2351 _attachmentPopupMenu = _menuPopupAtt->addCommands(_attachmentPopupMenuList,
2352 FALSE, XmMENU_POPUP);
2356 SendMsgDialog::construct_text_popup(void)
2358 if (theApplication->bMenuButton() != Button3)
2361 _textPopupMenuList = new CmdList( "TextPopup", "TextPopup");
2363 LabelCmd *title = new LabelCmd (
2365 GETMSG(DT_catd, 1, 159, "Mailer - Compose"), TRUE);
2366 SeparatorCmd *separator = new SeparatorCmd("Separator", "Separator", TRUE );
2368 _textPopupMenuList->add(title);
2369 _textPopupMenuList->add(separator);
2370 _textPopupMenuList->add(_file_send);
2371 _textPopupMenuList->add( _edit_undo );
2372 _textPopupMenuList->add( _edit_cut );
2373 _textPopupMenuList->add( _edit_copy );
2374 _textPopupMenuList->add( _edit_paste );
2376 // Work in progress from Mike. This adds the Paste Special to the
2377 // third mouse button in the compose area of a compose window.
2378 // Begin Paste Special submenu
2379 CmdList * subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2380 subcmdList1->add(_edit_paste_special[0]);
2381 subcmdList1->add(_edit_paste_special[1]);
2382 // End Paste Special submenu
2383 _textPopupMenuList->add( subcmdList1 ); // Add Paste Special submenu
2384 // (Either way) _textPopupMenuList->add( separator );
2385 _textPopupMenuList->add( _edit_clear );
2387 _textPopupMenuList->add( _edit_delete );
2388 _textPopupMenuList->add( _edit_select_all );
2390 Widget parent = _my_editor->textEditor()->get_editor();
2391 _menuPopupText = new MenuBar(parent, "SendMsgTextPopup", XmMENU_POPUP);
2392 _textPopupMenu = _menuPopupText->addCommands(_textPopupMenuList,
2393 FALSE, XmMENU_POPUP);
2396 static int cmp_prop_pair(const void *v1, const void *v2)
2398 PropStringPair *p1 = *((PropStringPair **) v1);
2399 PropStringPair *p2 = *((PropStringPair **) v2);
2402 ret = strcmp((const char *) p1->label, (const char *) p2->label);
2406 static void alias_stuffing_func(char * key, void * data, void * client_data)
2408 DtVirtArray<PropStringPair *> *alias_list;
2409 PropStringPair *new_pair;
2411 alias_list = (DtVirtArray<PropStringPair*> *) client_data;
2412 new_pair = new PropStringPair;
2413 new_pair->label = strdup(key);
2414 new_pair->value = strdup((char *)data);
2415 alias_list->append(new_pair);
2419 SendMsgDialog::createAliasList(DtVirtArray<PropStringPair*> *aliases)
2422 DtMail::Session *d_session = theRoamApp.session()->session();
2423 DtMail::MailRc *mail_rc = d_session->mailRc(error);
2426 mail_rc->getAliasList(alias_stuffing_func, aliases);
2428 if (nalias = aliases->length())
2430 PropStringPair **prop_pairs = NULL;
2432 prop_pairs = (PropStringPair**) malloc(nalias*sizeof(PropStringPair*));
2434 for (i=0; i<nalias; i++)
2436 prop_pairs[i] = (*aliases)[0];
2439 qsort(prop_pairs, nalias, sizeof(PropStringPair*), cmp_prop_pair);
2440 for (i=0; i<nalias; i++)
2441 aliases->append(prop_pairs[i]);
2443 free((void*) prop_pairs);
2448 SendMsgDialog::destroyAliasList(DtVirtArray<PropStringPair*> *aliases)
2450 while (aliases->length() >= 0)
2452 PropStringPair *prop_pair = (*aliases)[0];
2458 // map_menu is used to figure out how many columns to split the menu
2459 // into. It is a callback that is called when the menu is mapped.
2460 // If the menu is over half the height of the screen, it figures out
2461 // how many columns to make the menu, and sets its XmNnumColumns
2462 // attribute to that value. It calculates the maximum number of columns
2463 // that would fit and never goes beyond that number.
2465 static void map_alias_menu(Widget menu, XtPointer, XtPointer)
2469 Dimension maxcols, newcols, columns;
2470 Dimension screenheight = (Dimension) HeightOfScreen(XtScreen(menu));
2471 Dimension fudgefact = 20; /* to allow for decorations on menu */
2478 XmNnumColumns, &columns,
2481 if ((h + fudgefact) > (screenheight / 2))
2483 // The menu is taller than half the screen.
2484 // We need to find out how many more columns
2485 // to specify for the menu to make it fit.
2487 newcols = (columns * ((h+fudgefact)/(screenheight/2))) + 1;
2488 maxcols = WidthOfScreen(XtScreen(menu))/(w/columns);
2490 if (newcols > maxcols)
2493 XtVaSetValues(menu, XmNnumColumns, newcols, NULL);
2498 SendMsgDialog::aliasMenuButtonHandler(
2500 XtPointer client_data,
2504 Widget menu = (Widget) client_data;
2505 XButtonEvent *be = (XButtonEvent *) event;
2507 if (event->xany.type != ButtonPress) return;
2508 if(be->button == theApplication->bMenuButton())
2510 XmMenuPosition(menu, (XButtonEvent *)event);
2511 XtManageChild(menu);
2516 SendMsgDialog::createAliasPopupMenu(
2520 DtVirtArray<PropStringPair*> *aliases)
2523 OtherAliasesCmd *otherAliases =
2524 new OtherAliasesCmd(
2526 GETMSG(DT_catd, 1, 247, "Other Aliases..."),
2528 #if defined(USE_TITLED_ALIAS_POPUPS)
2532 GETMSG(DT_catd, 1, 248, "Mailer - Aliases"),
2535 SeparatorCmd *separator =
2536 new SeparatorCmd("Separator","Separator", TRUE);
2538 (*cmdlist) = new CmdList("AliasCommands", "AliasCommands");
2539 #if defined(USE_TITLED_ALIAS_POPUPS)
2540 (*cmdlist)->add(title);
2541 (*cmdlist)->add(separator);
2543 for (int i=0, length=aliases->length(); i<length; i++)
2545 PropStringPair *prop_pair = (*aliases)[i];
2547 AliasCmd *alias = new AliasCmd(
2548 strdup(prop_pair->label),
2549 strdup(prop_pair->label),
2552 (*cmdlist)->add(alias);
2554 if (0 < aliases->length())
2555 (*cmdlist)->add(separator);
2556 (*cmdlist)->add(otherAliases);
2558 *menubar = new MenuBar(parent, "AliasesPopup", XmMENU_POPUP);
2559 menu = (*menubar)->addCommands((*cmdlist), FALSE, XmMENU_POPUP);
2565 aliasMenuButtonHandler,
2570 XmNpacking, XmPACK_COLUMN,
2571 XmNorientation, XmVERTICAL,
2576 XmNmapCallback, &map_alias_menu,
2583 SendMsgDialog::destroyAliasPopupMenu(
2589 XtRemoveEventHandler(
2593 aliasMenuButtonHandler,
2598 XmNmapCallback, &map_alias_menu,
2601 XtDestroyWidget(menu);
2608 SendMsgDialog::getHeaderWidget(const char *hdrname)
2610 for (int i=0, length=_header_list.length(); i<length; i++)
2612 HeaderList *hdritem = _header_list[i];
2614 if (0 == strncmp(hdrname, hdritem->header, strlen(hdrname)))
2615 return hdritem->field_widget;
2622 SendMsgDialog::createAliasPopupMenus(void)
2626 w = getHeaderWidget(DtMailMessageTo);
2628 _toPopupMenu = createAliasPopupMenu(
2634 w = getHeaderWidget(DtMailMessageCc);
2636 _ccPopupMenu = createAliasPopupMenu(
2642 w = getHeaderWidget(DtMailMessageBcc);
2644 _bccPopupMenu = createAliasPopupMenu(
2652 SendMsgDialog::destroyAliasPopupMenus(void)
2656 w = getHeaderWidget(DtMailMessageTo);
2657 destroyAliasPopupMenu(w, _toPopupMenuBar, _toPopupCmdlist, _toPopupMenu);
2658 _toPopupMenuBar = NULL;
2659 _toPopupCmdlist = NULL;
2660 _toPopupMenu = NULL;
2662 w = getHeaderWidget(DtMailMessageCc);
2663 destroyAliasPopupMenu(w, _ccPopupMenuBar, _ccPopupCmdlist, _ccPopupMenu);
2664 _ccPopupMenuBar = NULL;
2665 _ccPopupCmdlist = NULL;
2666 _ccPopupMenu = NULL;
2668 w = getHeaderWidget(DtMailMessageBcc);
2669 destroyAliasPopupMenu(w, _bccPopupMenuBar, _bccPopupCmdlist, _bccPopupMenu);
2670 _bccPopupMenuBar = NULL;
2671 _bccPopupCmdlist = NULL;
2672 _bccPopupMenu = NULL;
2676 SendMsgDialog::createFormatMenu()
2679 _format_separator = new SeparatorCmd( "Separator","Separator", TRUE );
2681 cmdList = new CmdList( "Format", GETMSG(DT_catd, 1, 152,"Format") );
2683 _format_word_wrap = new WordWrapCmd (
2685 GETMSG(DT_catd, 1, 153, "Word Wrap"),
2688 _format_settings = new FormatCmd ( "Settings...",
2689 GETMSG(DT_catd, 1, 154, "Settings..."),
2693 cmdList->add( _format_word_wrap );
2694 cmdList->add( _format_settings );
2695 cmdList->add( _format_separator);
2697 _templates = new CmdList ( "Templates", GETMSG(DT_catd, 1, 157, "Templates") );
2698 addTemplates(_templates);
2700 cmdList->add(_templates);
2702 cmdList->add( _format_separator );
2704 _format_menu = _menuBar->addCommands ( &_format_cascade, cmdList,
2709 doDynamicHeaderMenus();
2711 if (_template_count == 0 && _templates->getPaneWidget())
2713 XtSetSensitive(_templates->getPaneWidget(), FALSE);
2719 SendMsgDialog::addTemplates(CmdList * subCmd)
2723 _template_count = 0;
2725 if (_templateList == NULL)
2728 DtMail::Session *m_session = theRoamApp.session()->session();
2729 char * expanded_list = m_session->expandPath(error, _templateList);
2731 DtVirtArray<PropStringPair *> templates(8);
2732 parsePropString(expanded_list, templates);
2733 free(expanded_list);
2735 _template_count = templates.length();
2737 for (int tmp = 0; tmp < _template_count; tmp++) {
2738 PropStringPair * psp = templates[tmp];
2739 if (psp->label && psp->value) {
2740 Cmd * button = new TemplateCmd(strdup(psp->label),
2745 subCmd->add(button);
2749 while (templates.length()) {
2750 PropStringPair * psp = templates[0];
2752 templates.remove(0);
2757 SendMsgDialog::initialize()
2761 const char * hideAttachPane = NULL;
2764 // Without the TearOffModelConverter call, there will be warning messages:
2765 // Warning: No type converter registered for 'String' to 'TearOffModel'
2768 XmRepTypeInstallTearOffModelConverter();
2769 MenuWindow::initialize();
2771 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
2774 XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
2775 XtSetValues( _w, args, n);
2777 _genDialog = new DtMailGenDialog("Dialog", _main);
2779 // See if the .mailrc specifies if attachPane is to be shown or hid
2780 // at SMD startup time.
2782 DtMail::Session *m_session = theRoamApp.session()->session();
2783 m_session->mailRc(error)->getValue(error, "hideattachments",
2786 if (!hideAttachPane) {
2787 _show_attach_area = TRUE;
2790 _show_attach_area = FALSE;
2791 // The user wants to hide attachments
2793 this->hideAttachArea();
2795 if (NULL != hideAttachPane)
2796 free((void*) hideAttachPane);
2798 _confirm_attachment_threshold = get_confirm_attachment_threshold();
2800 // Log Message Toggle button. A LogMsgCmd is a ToggleButtonCmd....
2801 const char * logfile = NULL;
2802 const char * value = NULL;
2803 m_session->mailRc(error)->getValue(error, "record", &logfile);
2805 _file_log->deactivate();
2807 _file_log->activate();
2809 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2810 if (logfile == NULL || error.isNotSet()) {
2811 // logfile is not specified or "dontlogmessages" is TRUE
2812 setLogState(DTM_FALSE);
2813 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2815 // logfile is specified and "dontlogmessages" is FALSE
2816 setLogState(DTM_TRUE);
2817 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2820 if (NULL != logfile)
2821 free((void*) logfile);
2823 free((void*) value);
2825 // Word Wrap Toggle button. A WordWrapCmd is a ToggleButtonCmd...
2826 ((ToggleButtonCmd *)_format_word_wrap)->setButtonState(
2827 ((WordWrapCmd *)_format_word_wrap)->wordWrap(),
2831 // Initialize the Edit menu
2833 this->text_unselected();
2835 setIconName(ComposeIcon);
2839 Self_destruct(XtPointer, XtIntervalId *)
2842 fprintf(stderr, "DEBUG: Self_destruct(): invoked!\n");
2846 XtRemoveAllCallbacks(
2847 theApplication->baseWidget(),
2848 XmNdestroyCallback);
2849 delete theApplication;
2852 // Clears Compose window title, header fields, text, and attachment areas.
2854 SendMsgDialog::reset()
2856 _my_editor->textEditor()->clear_contents();
2857 _my_editor->attachArea()->resetPendingAction();
2860 // This will deselect any Attachment action, if any available now.
2861 // Also deselect text menu items....
2863 this->deactivate_default_attach_menu();
2864 this->text_unselected();
2865 this->all_attachments_deselected();
2866 _att_undelete->deactivate(); // This needs to be done in addition
2868 this->get_editor()->attachArea()->removeCurrentAttachments();
2870 // Unmanage the dialog
2873 if (_show_attach_area) { // .mailrc wants default attach area invisible
2875 // Unmanage the attach Area. Set the show_pane button.
2876 // This is done because if we are caching this window (after
2877 // unmanaging), we don't want the window to pop back up, on uncaching,
2878 // with the attachment pane visible, etc..
2880 this->showAttachArea();
2883 this->hideAttachArea();
2886 // Need to destroy current Message handle.
2887 delete _msgHandle; // All its body parts are deleted.
2889 _lastAttBP = NULL; // So just set this to NULL.
2890 // Delete or set to NULL ???
2891 _inclMsgHandle = NULL;
2892 _inclMsgHasText = 0;
2894 for (int clear = 0; clear < _header_list.length(); clear++) {
2895 HeaderList * hl = _header_list[clear];
2897 // Bugfix: Old selection area remained selected, after text cleared
2898 // and parent widget unmanged, and then managed again for next
2899 // Compose. (So new text in old select area was still being selected).
2900 // Perhaps, this is a Motif bug ... but this fixes the problem.
2901 XmTextFieldClearSelection( hl->field_widget, CurrentTime );
2904 XtVaSetValues(hl->field_widget,
2905 XmNvalue, hl->value,
2909 XtVaSetValues(hl->field_widget,
2914 // Reset the Log state in case the user happened to change it.
2915 DtMail::Session *m_session = theRoamApp.session()->session();
2916 const char * logfile = NULL;
2917 const char * value = NULL;
2920 m_session->mailRc(error)->getValue(error, "record", &logfile);
2922 _file_log->deactivate();
2924 _file_log->activate();
2926 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2927 if (logfile == NULL || error.isNotSet()) {
2928 // logfile is not specified or "dontlogmessages" is TRUE
2929 setLogState(DTM_FALSE);
2930 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2932 // logfile is specified and "dontlogmessages" is FALSE
2933 setLogState(DTM_TRUE);
2934 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2937 if (NULL != logfile)
2938 free((void*) logfile);
2940 free((void*) value);
2943 // Recycles Compose window.
2945 SendMsgDialog::quit(Boolean delete_win)
2948 // There are several ways we could have reached here.
2949 // 1) From the user choosing Send.
2950 // 2) From the user clicking on the Close button or Close menu item
2951 // 3) The user choosing Close from window manager menu.
2952 // For (1), we just forge ahead. For that, the _takeDown boolean
2953 // is set in send_message() method.
2954 // For (2), the boolean is set in goAway().
2955 // For (3), we call goAway() which sets the _takeDown depending on
2956 // a dialog negotiation if SMD has contents.
2958 if (_file_include->fileBrowser() != NULL)
2959 XtPopdown(XtParent(_file_include->fileBrowser()));
2960 if (_att_add->fileBrowser() != NULL)
2961 XtPopdown(XtParent(_att_add->fileBrowser()));
2963 if (_file_save_as->fileBrowser() != NULL)
2964 XtPopdown(XtParent(_file_save_as->fileBrowser()));
2965 if (_att_save->fileBrowser() != NULL)
2966 XtPopdown(XtParent(_att_save->fileBrowser()));
2969 // Check to see if it's the first time through the quit()
2970 // method. Set _first_time to FALSE so that we don't come
2971 // down this path again until we're done quitting or bad
2972 // things will happen.
2973 if (_first_time == TRUE) {
2974 _first_time = FALSE;
2976 // We're done quitting, so we can set _first_time to TRUE again.
2984 #ifdef DTMAIL_TOOLTALK
2985 // For explanation of dtmail_mapped, look at RoamApp.h.
2986 if ( started_by_tt && (0 == theCompose.getTimeOutId()) &&
2987 (theCompose.numUnusedWindows() == theCompose.numCreatedWindows()) &&
2991 id = XtAppAddTimeOut(
2992 theApplication->appContext(),
2993 (unsigned long)DESTRUCT_TIMEOUT,
2994 Self_destruct, NULL);
2995 theCompose.putTimeOutId(id);
3004 theCompose.putWin(this, FALSE);
3008 // If there are no composer timeouts, check if its time to shutdown.
3010 if (0 == theCompose.getTimeOutId()) theRoamApp.checkForShutdown();
3014 SendMsgDialog::panicQuit()
3017 // Need to make sure the message is still valid before proceeding.
3018 // ::reset may have been called so the message may no longer be valid.
3027 // Given a file name, include the file as attachment.
3029 SendMsgDialog::inclAsAttmt(char *file, char *name)
3031 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3033 this->setLastAttBP();
3037 // Given a buffer, include its content as an attachment.
3039 SendMsgDialog::inclAsAttmt(unsigned char *contents, int len, char *name)
3043 mbuf.buffer = (void *)contents;
3044 mbuf.size = (unsigned long)len;
3045 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3046 (String)name, mbuf);
3047 this->setLastAttBP();
3050 // Given a RFC_822_Message formatted buffer, parse it and fill the Compose Window.
3052 SendMsgDialog::parseNplace(char *contents, int len)
3054 // 1. Create message handle for contents
3057 DtMail::Session * d_session = theRoamApp.session()->session();
3060 mbuf.buffer = (void *)contents;
3061 mbuf.size = (unsigned long)len;
3063 DtMail::Message * msg = d_session->messageConstruct(error,
3071 } else if ( error.isSet() ) {
3072 if ( (DTMailError_t) error == DTME_UnknownFormat ) {
3073 // The content does not have header info. Therefore, store
3074 // everything as text.
3075 _my_editor->textEditor()->set_contents((const char *)mbuf.buffer,
3081 char * status_string;
3082 DtMailBoolean first_bp_handled;
3083 first_bp_handled = _my_editor->textEditor()->set_message(
3089 int num_bodyParts = msg->getBodyCount(error);
3091 // Don't use setInclMsgHnd() because it causes the SMD's attachments
3092 // to get out of sink with the BE. Just assign the newly created message
3095 if ((num_bodyParts > 1) || (!first_bp_handled)) {
3097 if (first_bp_handled) {
3099 // setInclMsgHnd(msg, TRUE);
3103 // setInclMsgHnd(msg, FALSE);
3110 _my_editor->attachArea()->parseAttachments(error,
3115 // Need to call this after calling parseAttachments() so attachments
3116 // will be displayed in the attachment pane.
3117 _my_editor->manageAttachArea();
3119 // Need to update this compose window's internal message handle.
3121 // GL - calling updateMsgHndAtt is no longer necessary because we
3122 // just assigning the newly created msg to _msgHandle.
3123 // updateMsgHndAtt();
3126 loadHeaders(msg, DTM_TRUE);
3129 // Given a RFC_822_Message formatted file, parse it and fill the Compose Window.
3131 SendMsgDialog::parseNplace(const char * path)
3133 // 1. Get file content into buffer.
3134 int fd = SafeOpen(path, O_RDONLY);
3140 if (SafeFStat(fd, &buf) < 0) {
3145 _dead_letter_buf = new char[buf.st_size];
3146 if (!_dead_letter_buf) {
3151 if (SafeRead(fd, _dead_letter_buf,
3152 (unsigned int) buf.st_size) != buf.st_size) {
3153 delete [] _dead_letter_buf;
3158 parseNplace(_dead_letter_buf, (int) buf.st_size);
3162 SendMsgDialog::text( const char *text )
3164 _my_editor->textEditor()->set_contents( text, strlen(text) );
3168 SendMsgDialog::append( const char *text )
3170 _my_editor->textEditor()->append_to_contents( text, strlen(text) );
3174 SendMsgDialog::text()
3176 // Potential memory leak here. Because XmTextGetString returns
3177 // pointer to space containing all the text in the widget. Need
3178 // to call XtFree after we use this space
3179 // Also DtEditor widget requires application to free data.
3181 return (_my_editor->textEditor()->get_contents());
3187 SendMsgDialog::text_selected()
3189 // turn on sensitivity for Cut/Clear/Copy/Delete
3190 _edit_cut->activate();
3191 _edit_copy->activate();
3192 _edit_clear->activate();
3193 _edit_delete->activate();
3194 _edit_select_all->activate();
3198 SendMsgDialog::text_unselected()
3200 // turn off sensitivity for those items
3201 _edit_cut->deactivate();
3202 _edit_copy->deactivate();
3203 _edit_clear->deactivate();
3204 _edit_delete->deactivate();
3210 SendMsgDialog::attachment_selected()
3212 _att_save->activate();
3213 _att_delete->activate();
3214 _att_rename->activate();
3219 SendMsgDialog::all_attachments_selected()
3221 _att_delete->activate();
3222 _att_save->deactivate();
3223 _att_rename->deactivate();
3225 if (_attachmentActionsList != NULL) {
3226 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3227 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3228 _attachmentActionsList);
3229 delete _attachmentActionsList;
3230 _attachmentActionsList = NULL;
3237 SendMsgDialog::all_attachments_deselected()
3239 _att_save->deactivate();
3240 _att_delete->deactivate();
3241 _att_rename->deactivate();
3243 if (_attachmentActionsList != NULL) {
3244 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3245 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3246 _attachmentActionsList);
3247 delete _attachmentActionsList;
3248 _attachmentActionsList = NULL;
3255 SendMsgDialog::addAttachmentActions(
3262 AttachmentActionCmd *attachActionCmd;
3264 if (_attachmentActionsList == NULL) {
3265 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3268 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3269 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3270 _attachmentActionsList);
3271 delete _attachmentActionsList;
3272 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3276 for (i = 0; i < indx; i++) {
3277 anAction = actions[i];
3278 actionLabel = DtActionLabel(anAction); // get the localized action label
3279 attachActionCmd = new AttachmentActionCmd(
3284 _attachmentActionsList->add(attachActionCmd);
3287 _attachmentMenu = _menuBar->addCommands(
3289 _attachmentActionsList
3291 _attachmentPopupMenu = _menuPopupAtt->addCommands(
3292 _attachmentPopupMenu,
3293 _attachmentActionsList
3298 SendMsgDialog::removeAttachmentActions()
3301 // Stubbed out for now
3305 SendMsgDialog::invokeAttachmentAction(
3309 DtMailEditor *editor = this->get_editor();
3310 AttachArea *attacharea = editor->attachArea();
3311 Attachment *attachment = attacharea->getSelectedAttachment();
3313 attachment->invokeAction(index);
3317 SendMsgDialog::selectAllAttachments()
3320 DtMailEditor *editor = this->get_editor();
3321 AttachArea *attachArea = editor->attachArea();
3323 attachArea->selectAllAttachments();
3329 SendMsgDialog::activate_default_attach_menu()
3331 _att_select_all->activate();
3335 SendMsgDialog::deactivate_default_attach_menu()
3337 _att_select_all->deactivate();
3342 SendMsgDialog::delete_selected_attachments()
3344 DtMailEnv mail_error;
3346 // Initialize the mail_error.
3350 AttachArea *attachArea = _my_editor->attachArea();
3351 attachArea->deleteSelectedAttachments(mail_error);
3353 if (mail_error.isSet()) {
3357 // Activate this button to permit the user to undelete.
3359 _att_undelete->activate();
3361 // Deactivate buttons that will be activated when another
3362 // selection applies.
3364 _att_save->deactivate();
3365 _att_delete->deactivate();
3366 _att_rename->deactivate();
3368 if (_attachmentActionsList) {
3369 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3370 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3371 _attachmentActionsList);
3372 delete _attachmentActionsList;
3373 _attachmentActionsList = NULL;
3378 SendMsgDialog::undelete_last_deleted_attachment()
3380 DtMailEnv mail_error;
3382 // Initialize the mail_error.
3386 AttachArea *attachArea = _my_editor->attachArea();
3387 attachArea->undeleteLastDeletedAttachment(mail_error);
3389 if (mail_error.isSet()) {
3393 if(_my_editor->attachArea()->getIconSelectedCount())
3394 _att_delete->activate();
3396 if (attachArea->getDeleteCount() == 0) {
3397 _att_undelete->deactivate();
3402 SendMsgDialog::renameAttachmentOK()
3404 AttachArea *attachArea = _my_editor->attachArea();
3406 if (attachArea->getIconSelectedCount() > 1) {
3407 char *buf = new char[512];
3409 sprintf(buf, "%s", GETMSG(DT_catd, 5, 4, "Select only one attachment\n\
3410 and then choose rename"));
3412 _genDialog->setToQuestionDialog(
3413 GETMSG(DT_catd, 5, 2, "Mailer"),
3416 char * helpId = DTMAILHELPSELECTONEATTACH;
3418 int answer = _genDialog->post_and_return(helpId);
3429 SendMsgDialog::showAttachArea()
3431 DtMailEditor *editor = this->get_editor();
3432 editor->showAttachArea();
3433 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, FALSE);
3437 SendMsgDialog::hideAttachArea()
3439 DtMailEditor *editor = this->get_editor();
3440 editor->hideAttachArea();
3441 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(FALSE, FALSE);
3445 SendMsgDialog::lookupHeader(const char * name)
3447 for (int h = 0; h < _header_list.length(); h++) {
3448 HeaderList * hl = _header_list[h];
3449 if (hl->show != SMD_NEVER &&
3450 strcmp(hl->label, name) == 0) {
3459 SendMsgDialog::headerValueChanged(Widget,
3460 XtPointer client_data,
3463 SendMsgDialog * self = (SendMsgDialog *)client_data;
3464 self->_headers_changed = DTM_TRUE;
3468 SendMsgDialog::reattachHeaders(void)
3470 // We have to walk through the entire list of headers, attaching
3471 // the shown headers to the ones above them.
3473 HeaderList * hl = _header_list[0];
3474 Widget previous_form = hl->form_widget;
3476 for (int h = 1; h < _header_list.length(); h++) {
3477 hl = _header_list[h];
3481 previous_form = hl->form_widget;
3485 XtVaSetValues(hl->form_widget,
3486 XmNtopAttachment, XmATTACH_WIDGET,
3487 XmNtopWidget, previous_form,
3489 previous_form = hl->form_widget;
3497 forceFormResize(_main_form);
3498 forceFormResize(_header_form);
3502 SendMsgDialog::justifyHeaders(void)
3504 // Find out which header label has the longest display width to right
3505 // justify all labels.
3507 Dimension longest = 0;
3510 for (int count = 0; count < _header_list.length(); count++) {
3511 HeaderList * hl = _header_list[count];
3512 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3516 XtVaGetValues(hl->label_widget,
3518 XmNmarginLeft, &margin,
3521 if ( w > longest ) {
3526 for (int adjust = 0; adjust < _header_list.length(); adjust++) {
3527 HeaderList * hl = _header_list[adjust];
3528 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3532 XtVaGetValues(hl->label_widget,
3534 XmNmarginLeft, &margin,
3537 XtVaSetValues(hl->label_widget, XmNmarginLeft, (longest-w) > 0 ? longest-w : 1, NULL );
3542 SendMsgDialog::forceFormResize(Widget form)
3544 // The Motif Form widget is at least a little bit brain damaged.
3545 // We need to convince it to do the right thing after we make
3546 // minor adjustments in the children.
3548 Dimension width, height, border;
3552 XmNborderWidth, &border,
3556 XmNwidth, width + 1,
3557 XmNheight, height + 1,
3568 SendMsgDialog::unfilled_headers()
3570 // Walk through the headers. See if any of them have a value.
3572 for (int scan = 0; scan < _header_list.length(); scan++) {
3573 HeaderList * hl = _header_list[scan];
3574 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
3578 char * value = NULL;
3579 XtVaGetValues(hl->field_widget,
3583 if (strlen(value) > 0) {
3593 // Method checks if self has text in it.
3596 SendMsgDialog::checkDirty()
3598 if (this->unfilled_headers() &&
3599 (this->get_editor()->textEditor()->no_text()) &&
3600 (this->get_editor()->attachArea()->getIconCount() == 0) ) {
3602 // return FALSE so quit() can go ahead.
3613 SendMsgDialog::handleQuitDialog()
3617 DtMail::Session *m_session = theRoamApp.session()->session();
3618 const char * value = NULL;
3621 m_session->mailRc(error)->getValue(error, "expert", &value);
3622 if (error.isNotSet() && value != NULL)
3625 free((void*) value);
3630 DtMailGenDialog *dialog = this->genDialog();
3632 dialog->setToQuestionDialog(
3642 "The Compose window contains text or\n\
3643 attachments that will be lost if\n\
3644 the window is closed.\n\
3645 Close the Compose window?")
3647 helpId = DTMAILHELPCLOSECOMPOSEWINDOW;
3648 if ( dialog->post_and_return(
3659 helpId) == 1 ) { // Close selected
3664 return(FALSE); // Cancel selected
3669 SendMsgDialog::goAway(
3670 Boolean checkForDirty
3674 if (!checkForDirty) {
3679 // Check to see if self has contents (ie., is dirty)
3681 Boolean is_dirty = this->checkDirty();
3684 if (isIconified()) {
3685 MainWindow::manage();
3688 // Enquire if user really wants this window to go away
3690 Boolean really_quit = this->handleQuitDialog();
3701 SendMsgDialog::manage()
3703 MenuWindow::manage();
3705 HeaderList * hl = _header_list[0];
3706 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3711 SendMsgDialog::unmanage()
3713 MenuWindow::unmanage();
3714 XFlush(XtDisplay(this->_main_form));
3715 XSync(XtDisplay(this->_main_form), False);
3721 _compose_head = NULL;
3729 Compose::Compose_Win *a_node;
3730 Compose::Compose_Win *next_node;
3732 theRoamApp.registerPendingTask();
3734 a_node = _compose_head;
3737 next_node = a_node->next;
3738 if (!a_node->in_use)
3741 free((void*) a_node);
3746 theRoamApp.unregisterPendingTask();
3750 Compose::putWin(SendMsgDialog *smd, Boolean in_use)
3752 Compose::Compose_Win* a_node = NULL;
3753 Compose::Compose_Win *tmp = NULL;
3756 // Update the _not_in_use count.
3762 // Check to see if compose window is already in the list.
3764 for (a_node = _compose_head; a_node; a_node=a_node->next)
3766 if (a_node->win == smd)
3768 a_node->in_use = in_use;
3773 // Need new node with smd.
3774 tmp = (Compose::Compose_Win *)malloc(sizeof(Compose::Compose_Win));
3777 tmp->in_use = in_use;
3779 // If nothing is cached so far, add this Compose window to the head.
3780 if (NULL == _compose_head)
3782 _compose_head = tmp;
3786 // There exists a cache. Add this compose window to the tail.
3787 for (a_node=_compose_head; a_node; a_node=a_node->next)
3789 if (NULL == a_node->next)
3798 Compose::getUnusedWin()
3800 if (NULL == _compose_head) return NULL;
3802 Compose::Compose_Win* a_node = NULL;
3803 Compose::Compose_Win* the_node = NULL;
3805 // Find a node with unused smd. Return smd
3806 for (a_node=_compose_head; a_node; a_node=a_node->next)
3808 if (!a_node->in_use)
3810 a_node->in_use = TRUE;
3819 // Get a compose window either by creating a new SendMsgDialog or
3820 // from the recycle list.
3824 SendMsgDialog *newsend = NULL;
3826 #ifdef DTMAIL_TOOLTALK
3829 XtRemoveTimeOut(_timeout_id);
3834 newsend = getUnusedWin();
3838 // We have no unused SMDs around; therefore, create new window.
3839 theRoamApp.busyAllWindows();
3840 newsend = new SendMsgDialog();
3841 newsend->initialize();
3843 putWin(newsend, TRUE);
3844 theRoamApp.unbusyAllWindows();
3848 newsend->resetHeaders();
3849 newsend->displayInCurrentWorkspace();
3852 newsend->text_unselected();
3854 newsend->startAutoSave();
3856 // Get new Message Handle
3857 newsend->setMsgHnd();
3858 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
3859 newsend->setTitle(ttl);
3860 newsend->setIconTitle(ttl);
3866 SendMsgDialog::setTitle(char *subject)
3868 char *format = "%s - %s";
3869 char *prefix = GETMSG(DT_catd, 1, 6, "Mailer");
3873 len = strlen(format) + strlen(prefix) + strlen(subject) + 1;
3874 new_title = new char[len];
3875 sprintf(new_title, format, prefix, subject);
3878 delete [] new_title;
3882 SendMsgDialog::resetHeaders(void)
3884 DtMail::Session *m_session = theRoamApp.session()->session();
3885 const char * value = NULL;
3889 m_session->mailRc(error)->getValue(error, "additionalfields", &value);
3891 // Return if no props were applied and headers did not change.
3892 if ((_additionalfields == NULL && value == NULL) ||
3893 ( _additionalfields != NULL && value != NULL &&
3894 strcmp (_additionalfields, value) == 0))
3897 free((void*) value);
3902 // User changed the Header list via props. Recreate list
3905 // First hide all shown headers
3906 for (i=0; i < _header_list.length(); i++) {
3907 HeaderList * hl = _header_list[i];
3908 if (hl->show == SMD_SHOWN) {
3909 hl->show = SMD_HIDDEN;
3910 XtUnmanageChild(hl->form_widget);
3914 // Now remove the old list.
3915 DtVirtArray<PropStringPair *> results(8);
3916 parsePropString(_additionalfields, results);
3917 for (i=0, j=results.length(); i < j; i++) {
3918 PropStringPair * psp = results[i];
3919 int slot = lookupHeader(psp->label);
3920 // dont allow removal of default headers.
3921 HeaderList * hl = _header_list[slot];
3922 if (!reservedHeader(hl->label)) {
3924 hl->show = SMD_NEVER;
3926 else if (hl->value != NULL) {
3931 while(results.length()) {
3932 PropStringPair * psp = results[0];
3937 if (_additionalfields != NULL)
3938 free(_additionalfields);
3939 if (value != NULL && *value != '\0')
3940 _additionalfields = strdup(value);
3942 _additionalfields = NULL;
3944 parsePropString(value, results);
3947 for (j=results.length(), i=0; i < j; i++) {
3948 PropStringPair * psp = results[i];
3949 int slot = lookupHeader(psp->label);
3952 HeaderList * hl = _header_list[slot];
3953 if (!reservedHeader(hl->label))
3954 hl->show = SMD_HIDDEN;
3955 if (hl->value != NULL) {
3959 if (psp->value != NULL)
3960 hl->value = strdup(psp->value);
3963 HeaderList * copy_hl = new HeaderList;
3964 copy_hl->label = strdup(psp->label);
3965 copy_hl->header = strdup(psp->label);
3967 copy_hl->value = strdup(psp->value);
3968 copy_hl->show = SMD_HIDDEN;
3969 _header_list.append(copy_hl);
3971 while(results.length()) {
3972 PropStringPair * psp = results[0];
3977 createHeaders(_header_form);
3978 doDynamicHeaderMenus();
3981 free((void*) value);
3985 SendMsgDialog::setInputFocus(const int mode)
3989 HeaderList * hl = _header_list[0];
3990 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3992 else if (mode == 1) {
3993 Widget edWid = _my_editor->textEditor()->get_editor();
3994 (void) XmProcessTraversal(edWid, XmTRAVERSE_CURRENT);
3999 SendMsgDialog::attachmentFeedback(
4007 this->normalCursor();
4012 SendMsgDialog::hasAddressee()
4016 DtMail::Envelope * env = _msgHandle->getEnvelope(error);
4018 // Walk through the headers.
4019 // Return TRUE if the message has a value for either of the
4020 // following headers: To:, Cc:, or Bcc:.
4021 // Return FALSE if none of the three headers have any value.
4023 for (int scan = 0; scan < _header_list.length(); scan++) {
4024 HeaderList * hl = _header_list[scan];
4025 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
4028 if ((strcmp(hl->label, "To") == 0) ||
4029 (strcmp(hl->label, "Cc") == 0) ||
4030 (strcmp(hl->label, "Bcc") == 0)) {
4031 char * value = NULL;
4032 XtVaGetValues(hl->field_widget,
4036 for (char *cv = value; *cv; cv++) {
4037 if (!isspace(*cv)) {
4039 return(TRUE); // text value contains contents
4042 XtFree(value); // text value is "content free" - try the next one
4046 return(FALSE); // no field has contents