2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
26 * $TOG: 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 don't 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 );
2375 _textPopupMenuList->add( _edit_delete );
2376 _textPopupMenuList->add( _edit_select_all );
2378 Widget parent = _my_editor->textEditor()->get_editor();
2379 _menuPopupText = new MenuBar(parent, "SendMsgTextPopup", XmMENU_POPUP);
2380 _textPopupMenu = _menuPopupText->addCommands(_textPopupMenuList,
2381 FALSE, XmMENU_POPUP);
2384 static int cmp_prop_pair(const void *v1, const void *v2)
2386 PropStringPair *p1 = *((PropStringPair **) v1);
2387 PropStringPair *p2 = *((PropStringPair **) v2);
2390 ret = strcmp((const char *) p1->label, (const char *) p2->label);
2394 static void alias_stuffing_func(char * key, void * data, void * client_data)
2396 DtVirtArray<PropStringPair *> *alias_list;
2397 PropStringPair *new_pair;
2399 alias_list = (DtVirtArray<PropStringPair*> *) client_data;
2400 new_pair = new PropStringPair;
2401 new_pair->label = strdup(key);
2402 new_pair->value = strdup((char *)data);
2403 alias_list->append(new_pair);
2407 SendMsgDialog::createAliasList(DtVirtArray<PropStringPair*> *aliases)
2410 DtMail::Session *d_session = theRoamApp.session()->session();
2411 DtMail::MailRc *mail_rc = d_session->mailRc(error);
2414 mail_rc->getAliasList(alias_stuffing_func, aliases);
2416 if (nalias = aliases->length())
2418 PropStringPair **prop_pairs = NULL;
2420 prop_pairs = (PropStringPair**) malloc(nalias*sizeof(PropStringPair*));
2422 for (i=0; i<nalias; i++)
2424 prop_pairs[i] = (*aliases)[0];
2427 qsort(prop_pairs, nalias, sizeof(PropStringPair*), cmp_prop_pair);
2428 for (i=0; i<nalias; i++)
2429 aliases->append(prop_pairs[i]);
2431 free((void*) prop_pairs);
2436 SendMsgDialog::destroyAliasList(DtVirtArray<PropStringPair*> *aliases)
2438 while (aliases->length() >= 0)
2440 PropStringPair *prop_pair = (*aliases)[0];
2446 // map_menu is used to figure out how many columns to split the menu
2447 // into. It is a callback that is called when the menu is mapped.
2448 // If the menu is over half the height of the screen, it figures out
2449 // how many columns to make the menu, and sets its XmNnumColumns
2450 // attribute to that value. It calculates the maximum number of columns
2451 // that would fit and never goes beyond that number.
2453 static void map_alias_menu(Widget menu, XtPointer, XtPointer)
2457 Dimension maxcols, newcols, columns;
2458 Dimension screenheight = (Dimension) HeightOfScreen(XtScreen(menu));
2459 Dimension fudgefact = 20; /* to allow for decorations on menu */
2466 XmNnumColumns, &columns,
2469 if ((h + fudgefact) > (screenheight / 2))
2471 // The menu is taller than half the screen.
2472 // We need to find out how many more columns
2473 // to specify for the menu to make it fit.
2475 newcols = (columns * ((h+fudgefact)/(screenheight/2))) + 1;
2476 maxcols = WidthOfScreen(XtScreen(menu))/(w/columns);
2478 if (newcols > maxcols)
2481 XtVaSetValues(menu, XmNnumColumns, newcols, NULL);
2486 SendMsgDialog::aliasMenuButtonHandler(
2488 XtPointer client_data,
2492 Widget menu = (Widget) client_data;
2493 XButtonEvent *be = (XButtonEvent *) event;
2495 if (event->xany.type != ButtonPress) return;
2496 if(be->button == theApplication->bMenuButton())
2498 XmMenuPosition(menu, (XButtonEvent *)event);
2499 XtManageChild(menu);
2504 SendMsgDialog::createAliasPopupMenu(
2508 DtVirtArray<PropStringPair*> *aliases)
2511 OtherAliasesCmd *otherAliases =
2512 new OtherAliasesCmd(
2514 GETMSG(DT_catd, 1, 247, "Other Aliases..."),
2516 #if defined(USE_TITLED_ALIAS_POPUPS)
2520 GETMSG(DT_catd, 1, 248, "Mailer - Aliases"),
2523 SeparatorCmd *separator =
2524 new SeparatorCmd("Separator","Separator", TRUE);
2526 (*cmdlist) = new CmdList("AliasCommands", "AliasCommands");
2527 #if defined(USE_TITLED_ALIAS_POPUPS)
2528 (*cmdlist)->add(title);
2529 (*cmdlist)->add(separator);
2531 for (int i=0, length=aliases->length(); i<length; i++)
2533 PropStringPair *prop_pair = (*aliases)[i];
2535 AliasCmd *alias = new AliasCmd(
2536 strdup(prop_pair->label),
2537 strdup(prop_pair->label),
2540 (*cmdlist)->add(alias);
2542 if (0 < aliases->length())
2543 (*cmdlist)->add(separator);
2544 (*cmdlist)->add(otherAliases);
2546 *menubar = new MenuBar(parent, "AliasesPopup", XmMENU_POPUP);
2547 menu = (*menubar)->addCommands((*cmdlist), FALSE, XmMENU_POPUP);
2553 aliasMenuButtonHandler,
2558 XmNpacking, XmPACK_COLUMN,
2559 XmNorientation, XmVERTICAL,
2564 XmNmapCallback, &map_alias_menu,
2571 SendMsgDialog::destroyAliasPopupMenu(
2577 XtRemoveEventHandler(
2581 aliasMenuButtonHandler,
2586 XmNmapCallback, &map_alias_menu,
2589 XtDestroyWidget(menu);
2596 SendMsgDialog::getHeaderWidget(const char *hdrname)
2598 for (int i=0, length=_header_list.length(); i<length; i++)
2600 HeaderList *hdritem = _header_list[i];
2602 if (0 == strncmp(hdrname, hdritem->header, strlen(hdrname)))
2603 return hdritem->field_widget;
2610 SendMsgDialog::createAliasPopupMenus(void)
2614 w = getHeaderWidget(DtMailMessageTo);
2616 _toPopupMenu = createAliasPopupMenu(
2622 w = getHeaderWidget(DtMailMessageCc);
2624 _ccPopupMenu = createAliasPopupMenu(
2630 w = getHeaderWidget(DtMailMessageBcc);
2632 _bccPopupMenu = createAliasPopupMenu(
2640 SendMsgDialog::destroyAliasPopupMenus(void)
2644 w = getHeaderWidget(DtMailMessageTo);
2645 destroyAliasPopupMenu(w, _toPopupMenuBar, _toPopupCmdlist, _toPopupMenu);
2646 _toPopupMenuBar = NULL;
2647 _toPopupCmdlist = NULL;
2648 _toPopupMenu = NULL;
2650 w = getHeaderWidget(DtMailMessageCc);
2651 destroyAliasPopupMenu(w, _ccPopupMenuBar, _ccPopupCmdlist, _ccPopupMenu);
2652 _ccPopupMenuBar = NULL;
2653 _ccPopupCmdlist = NULL;
2654 _ccPopupMenu = NULL;
2656 w = getHeaderWidget(DtMailMessageBcc);
2657 destroyAliasPopupMenu(w, _bccPopupMenuBar, _bccPopupCmdlist, _bccPopupMenu);
2658 _bccPopupMenuBar = NULL;
2659 _bccPopupCmdlist = NULL;
2660 _bccPopupMenu = NULL;
2664 SendMsgDialog::createFormatMenu()
2667 _format_separator = new SeparatorCmd( "Separator","Separator", TRUE );
2669 cmdList = new CmdList( "Format", GETMSG(DT_catd, 1, 152,"Format") );
2671 _format_word_wrap = new WordWrapCmd (
2673 GETMSG(DT_catd, 1, 153, "Word Wrap"),
2676 _format_settings = new FormatCmd ( "Settings...",
2677 GETMSG(DT_catd, 1, 154, "Settings..."),
2681 cmdList->add( _format_word_wrap );
2682 cmdList->add( _format_settings );
2683 cmdList->add( _format_separator);
2685 _templates = new CmdList ( "Templates", GETMSG(DT_catd, 1, 157, "Templates") );
2686 addTemplates(_templates);
2688 cmdList->add(_templates);
2690 cmdList->add( _format_separator );
2692 _format_menu = _menuBar->addCommands ( &_format_cascade, cmdList,
2697 doDynamicHeaderMenus();
2699 if (_template_count == 0 && _templates->getPaneWidget())
2701 XtSetSensitive(_templates->getPaneWidget(), FALSE);
2707 SendMsgDialog::addTemplates(CmdList * subCmd)
2711 _template_count = 0;
2713 if (_templateList == NULL)
2716 DtMail::Session *m_session = theRoamApp.session()->session();
2717 char * expanded_list = m_session->expandPath(error, _templateList);
2719 DtVirtArray<PropStringPair *> templates(8);
2720 parsePropString(expanded_list, templates);
2721 free(expanded_list);
2723 _template_count = templates.length();
2725 for (int tmp = 0; tmp < _template_count; tmp++) {
2726 PropStringPair * psp = templates[tmp];
2727 if (psp->label && psp->value) {
2728 Cmd * button = new TemplateCmd(strdup(psp->label),
2733 subCmd->add(button);
2737 while (templates.length()) {
2738 PropStringPair * psp = templates[0];
2740 templates.remove(0);
2745 SendMsgDialog::initialize()
2749 const char * hideAttachPane = NULL;
2752 // Without the TearOffModelConverter call, there will be warning messages:
2753 // Warning: No type converter registered for 'String' to 'TearOffModel'
2756 XmRepTypeInstallTearOffModelConverter();
2757 MenuWindow::initialize();
2759 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
2762 XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
2763 XtSetValues( _w, args, n);
2765 _genDialog = new DtMailGenDialog("Dialog", _main);
2767 // See if the .mailrc specifies if attachPane is to be shown or hid
2768 // at SMD startup time.
2770 DtMail::Session *m_session = theRoamApp.session()->session();
2771 m_session->mailRc(error)->getValue(error, "hideattachments",
2774 if (!hideAttachPane) {
2775 _show_attach_area = TRUE;
2778 _show_attach_area = FALSE;
2779 // The user wants to hide attachments
2781 this->hideAttachArea();
2783 if (NULL != hideAttachPane)
2784 free((void*) hideAttachPane);
2786 _confirm_attachment_threshold = get_confirm_attachment_threshold();
2788 // Log Message Toggle button. A LogMsgCmd is a ToggleButtonCmd....
2789 const char * logfile = NULL;
2790 const char * value = NULL;
2791 m_session->mailRc(error)->getValue(error, "record", &logfile);
2793 _file_log->deactivate();
2795 _file_log->activate();
2797 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2798 if (logfile == NULL || error.isNotSet()) {
2799 // logfile is not specified or "dontlogmessages" is TRUE
2800 setLogState(DTM_FALSE);
2801 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2803 // logfile is specified and "dontlogmessages" is FALSE
2804 setLogState(DTM_TRUE);
2805 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2808 if (NULL != logfile)
2809 free((void*) logfile);
2811 free((void*) value);
2813 // Word Wrap Toggle button. A WordWrapCmd is a ToggleButtonCmd...
2814 ((ToggleButtonCmd *)_format_word_wrap)->setButtonState(
2815 ((WordWrapCmd *)_format_word_wrap)->wordWrap(),
2819 // Initialize the Edit menu
2821 this->text_unselected();
2823 setIconName(ComposeIcon);
2827 Self_destruct(XtPointer, XtIntervalId *)
2830 fprintf(stderr, "DEBUG: Self_destruct(): invoked!\n");
2834 XtRemoveAllCallbacks(
2835 theApplication->baseWidget(),
2836 XmNdestroyCallback);
2837 delete theApplication;
2840 // Clears Compose window title, header fields, text, and attachment areas.
2842 SendMsgDialog::reset()
2844 _my_editor->textEditor()->clear_contents();
2845 _my_editor->attachArea()->resetPendingAction();
2848 // This will deselect any Attachment action, if any available now.
2849 // Also deselect text menu items....
2851 this->deactivate_default_attach_menu();
2852 this->text_unselected();
2853 this->all_attachments_deselected();
2854 _att_undelete->deactivate(); // This needs to be done in addition
2856 this->get_editor()->attachArea()->removeCurrentAttachments();
2858 // Unmanage the dialog
2861 if (_show_attach_area) { // .mailrc wants default attach area invisible
2863 // Unmanage the attach Area. Set the show_pane button.
2864 // This is done because if we are caching this window (after
2865 // unmanaging), we don't want the window to pop back up, on uncaching,
2866 // with the attachment pane visible, etc..
2868 this->showAttachArea();
2871 this->hideAttachArea();
2874 // Need to destroy current Message handle.
2875 delete _msgHandle; // All its body parts are deleted.
2877 _lastAttBP = NULL; // So just set this to NULL.
2878 // Delete or set to NULL ???
2879 _inclMsgHandle = NULL;
2880 _inclMsgHasText = 0;
2882 for (int clear = 0; clear < _header_list.length(); clear++) {
2883 HeaderList * hl = _header_list[clear];
2885 // Bugfix: Old selection area remained selected, after text cleared
2886 // and parent widget unmanged, and then managed again for next
2887 // Compose. (So new text in old select area was still being selected).
2888 // Perhaps, this is a Motif bug ... but this fixes the problem.
2889 XmTextFieldClearSelection( hl->field_widget, CurrentTime );
2892 XtVaSetValues(hl->field_widget,
2893 XmNvalue, hl->value,
2897 XtVaSetValues(hl->field_widget,
2902 // Reset the Log state in case the user happened to change it.
2903 DtMail::Session *m_session = theRoamApp.session()->session();
2904 const char * logfile = NULL;
2905 const char * value = NULL;
2908 m_session->mailRc(error)->getValue(error, "record", &logfile);
2910 _file_log->deactivate();
2912 _file_log->activate();
2914 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2915 if (logfile == NULL || error.isNotSet()) {
2916 // logfile is not specified or "dontlogmessages" is TRUE
2917 setLogState(DTM_FALSE);
2918 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2920 // logfile is specified and "dontlogmessages" is FALSE
2921 setLogState(DTM_TRUE);
2922 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2925 if (NULL != logfile)
2926 free((void*) logfile);
2928 free((void*) value);
2931 // Recycles Compose window.
2933 SendMsgDialog::quit(Boolean delete_win)
2936 // There are several ways we could have reached here.
2937 // 1) From the user choosing Send.
2938 // 2) From the user clicking on the Close button or Close menu item
2939 // 3) The user choosing Close from window manager menu.
2940 // For (1), we just forge ahead. For that, the _takeDown boolean
2941 // is set in send_message() method.
2942 // For (2), the boolean is set in goAway().
2943 // For (3), we call goAway() which sets the _takeDown depending on
2944 // a dialog negotiation if SMD has contents.
2946 if (_file_include->fileBrowser() != NULL)
2947 XtPopdown(XtParent(_file_include->fileBrowser()));
2948 if (_att_add->fileBrowser() != NULL)
2949 XtPopdown(XtParent(_att_add->fileBrowser()));
2951 if (_file_save_as->fileBrowser() != NULL)
2952 XtPopdown(XtParent(_file_save_as->fileBrowser()));
2953 if (_att_save->fileBrowser() != NULL)
2954 XtPopdown(XtParent(_att_save->fileBrowser()));
2957 // Check to see if it's the first time through the quit()
2958 // method. Set _first_time to FALSE so that we don't come
2959 // down this path again until we're done quitting or bad
2960 // things will happen.
2961 if (_first_time == TRUE) {
2962 _first_time = FALSE;
2964 // We're done quitting, so we can set _first_time to TRUE again.
2972 #ifdef DTMAIL_TOOLTALK
2973 // For explanation of dtmail_mapped, look at RoamApp.h.
2974 if ( started_by_tt && (0 == theCompose.getTimeOutId()) &&
2975 (theCompose.numUnusedWindows() == theCompose.numCreatedWindows()) &&
2979 id = XtAppAddTimeOut(
2980 theApplication->appContext(),
2981 (unsigned long)DESTRUCT_TIMEOUT,
2982 Self_destruct, NULL);
2983 theCompose.putTimeOutId(id);
2992 theCompose.putWin(this, FALSE);
2996 // If there are no composer timeouts, check if its time to shutdown.
2998 if (0 == theCompose.getTimeOutId()) theRoamApp.checkForShutdown();
3002 SendMsgDialog::panicQuit()
3005 // Need to make sure the message is still valid before proceeding.
3006 // ::reset may have been called so the message may no longer be valid.
3015 // Given a file name, include the file as attachment.
3017 SendMsgDialog::inclAsAttmt(char *file, char *name)
3019 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3021 this->setLastAttBP();
3025 // Given a buffer, include its content as an attachment.
3027 SendMsgDialog::inclAsAttmt(unsigned char *contents, int len, char *name)
3031 mbuf.buffer = (void *)contents;
3032 mbuf.size = (unsigned long)len;
3033 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3034 (String)name, mbuf);
3035 this->setLastAttBP();
3038 // Given a RFC_822_Message formatted buffer, parse it and fill the Compose Window.
3040 SendMsgDialog::parseNplace(char *contents, int len)
3042 // 1. Create message handle for contents
3045 DtMail::Session * d_session = theRoamApp.session()->session();
3048 mbuf.buffer = (void *)contents;
3049 mbuf.size = (unsigned long)len;
3051 DtMail::Message * msg = d_session->messageConstruct(error,
3059 } else if ( error.isSet() ) {
3060 if ( (DTMailError_t) error == DTME_UnknownFormat ) {
3061 // The content does not have header info. Therefore, store
3062 // everything as text.
3063 _my_editor->textEditor()->set_contents((const char *)mbuf.buffer,
3069 char * status_string;
3070 DtMailBoolean first_bp_handled;
3071 first_bp_handled = _my_editor->textEditor()->set_message(
3077 int num_bodyParts = msg->getBodyCount(error);
3079 // Don't use setInclMsgHnd() because it causes the SMD's attachments
3080 // to get out of sink with the BE. Just assign the newly created message
3083 if ((num_bodyParts > 1) || (!first_bp_handled)) {
3085 if (first_bp_handled) {
3087 // setInclMsgHnd(msg, TRUE);
3091 // setInclMsgHnd(msg, FALSE);
3098 _my_editor->attachArea()->parseAttachments(error,
3103 // Need to call this after calling parseAttachments() so attachments
3104 // will be displayed in the attachment pane.
3105 _my_editor->manageAttachArea();
3107 // Need to update this compose window's internal message handle.
3109 // GL - calling updateMsgHndAtt is no longer necessary because we
3110 // just assigning the newly created msg to _msgHandle.
3111 // updateMsgHndAtt();
3114 loadHeaders(msg, DTM_TRUE);
3117 // Given a RFC_822_Message formatted file, parse it and fill the Compose Window.
3119 SendMsgDialog::parseNplace(const char * path)
3121 // 1. Get file content into buffer.
3122 int fd = SafeOpen(path, O_RDONLY);
3128 if (SafeFStat(fd, &buf) < 0) {
3133 _dead_letter_buf = new char[buf.st_size];
3134 if (!_dead_letter_buf) {
3139 if (SafeRead(fd, _dead_letter_buf,
3140 (unsigned int) buf.st_size) != buf.st_size) {
3141 delete [] _dead_letter_buf;
3146 parseNplace(_dead_letter_buf, (int) buf.st_size);
3150 SendMsgDialog::text( const char *text )
3152 _my_editor->textEditor()->set_contents( text, strlen(text) );
3156 SendMsgDialog::append( const char *text )
3158 _my_editor->textEditor()->append_to_contents( text, strlen(text) );
3162 SendMsgDialog::text()
3164 // Potential memory leak here. Because XmTextGetString returns
3165 // pointer to space containing all the text in the widget. Need
3166 // to call XtFree after we use this space
3167 // Also DtEditor widget requires application to free data.
3169 return (_my_editor->textEditor()->get_contents());
3175 SendMsgDialog::text_selected()
3177 // turn on sensitivity for Cut/Clear/Copy/Delete
3178 _edit_cut->activate();
3179 _edit_copy->activate();
3180 _edit_clear->activate();
3181 _edit_delete->activate();
3182 _edit_select_all->activate();
3186 SendMsgDialog::text_unselected()
3188 // turn off sensitivity for those items
3189 _edit_cut->deactivate();
3190 _edit_copy->deactivate();
3191 _edit_clear->deactivate();
3192 _edit_delete->deactivate();
3198 SendMsgDialog::attachment_selected()
3200 _att_save->activate();
3201 _att_delete->activate();
3202 _att_rename->activate();
3207 SendMsgDialog::all_attachments_selected()
3209 _att_delete->activate();
3210 _att_save->deactivate();
3211 _att_rename->deactivate();
3213 if (_attachmentActionsList != NULL) {
3214 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3215 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3216 _attachmentActionsList);
3217 delete _attachmentActionsList;
3218 _attachmentActionsList = NULL;
3225 SendMsgDialog::all_attachments_deselected()
3227 _att_save->deactivate();
3228 _att_delete->deactivate();
3229 _att_rename->deactivate();
3231 if (_attachmentActionsList != NULL) {
3232 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3233 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3234 _attachmentActionsList);
3235 delete _attachmentActionsList;
3236 _attachmentActionsList = NULL;
3243 SendMsgDialog::addAttachmentActions(
3250 AttachmentActionCmd *attachActionCmd;
3252 if (_attachmentActionsList == NULL) {
3253 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3256 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3257 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3258 _attachmentActionsList);
3259 delete _attachmentActionsList;
3260 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3264 for (i = 0; i < indx; i++) {
3265 anAction = actions[i];
3266 actionLabel = DtActionLabel(anAction); // get the localized action label
3267 attachActionCmd = new AttachmentActionCmd(
3272 _attachmentActionsList->add(attachActionCmd);
3275 _attachmentMenu = _menuBar->addCommands(
3277 _attachmentActionsList
3279 _attachmentPopupMenu = _menuPopupAtt->addCommands(
3280 _attachmentPopupMenu,
3281 _attachmentActionsList
3286 SendMsgDialog::removeAttachmentActions()
3289 // Stubbed out for now
3293 SendMsgDialog::invokeAttachmentAction(
3297 DtMailEditor *editor = this->get_editor();
3298 AttachArea *attacharea = editor->attachArea();
3299 Attachment *attachment = attacharea->getSelectedAttachment();
3301 attachment->invokeAction(index);
3305 SendMsgDialog::selectAllAttachments()
3308 DtMailEditor *editor = this->get_editor();
3309 AttachArea *attachArea = editor->attachArea();
3311 attachArea->selectAllAttachments();
3317 SendMsgDialog::activate_default_attach_menu()
3319 _att_select_all->activate();
3323 SendMsgDialog::deactivate_default_attach_menu()
3325 _att_select_all->deactivate();
3330 SendMsgDialog::delete_selected_attachments()
3332 DtMailEnv mail_error;
3334 // Initialize the mail_error.
3338 AttachArea *attachArea = _my_editor->attachArea();
3339 attachArea->deleteSelectedAttachments(mail_error);
3341 if (mail_error.isSet()) {
3345 // Activate this button to permit the user to undelete.
3347 _att_undelete->activate();
3349 // Deactivate buttons that will be activated when another
3350 // selection applies.
3352 _att_save->deactivate();
3353 _att_delete->deactivate();
3354 _att_rename->deactivate();
3356 if (_attachmentActionsList) {
3357 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3358 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3359 _attachmentActionsList);
3360 delete _attachmentActionsList;
3361 _attachmentActionsList = NULL;
3366 SendMsgDialog::undelete_last_deleted_attachment()
3368 DtMailEnv mail_error;
3370 // Initialize the mail_error.
3374 AttachArea *attachArea = _my_editor->attachArea();
3375 attachArea->undeleteLastDeletedAttachment(mail_error);
3377 if (mail_error.isSet()) {
3381 if(_my_editor->attachArea()->getIconSelectedCount())
3382 _att_delete->activate();
3384 if (attachArea->getDeleteCount() == 0) {
3385 _att_undelete->deactivate();
3390 SendMsgDialog::renameAttachmentOK()
3392 AttachArea *attachArea = _my_editor->attachArea();
3394 if (attachArea->getIconSelectedCount() > 1) {
3395 char *buf = new char[512];
3397 sprintf(buf, "%s", GETMSG(DT_catd, 5, 4, "Select only one attachment\n\
3398 and then choose rename"));
3400 _genDialog->setToQuestionDialog(
3401 GETMSG(DT_catd, 5, 2, "Mailer"),
3404 char * helpId = DTMAILHELPSELECTONEATTACH;
3406 int answer = _genDialog->post_and_return(helpId);
3417 SendMsgDialog::showAttachArea()
3419 DtMailEditor *editor = this->get_editor();
3420 editor->showAttachArea();
3421 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, FALSE);
3425 SendMsgDialog::hideAttachArea()
3427 DtMailEditor *editor = this->get_editor();
3428 editor->hideAttachArea();
3429 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(FALSE, FALSE);
3433 SendMsgDialog::lookupHeader(const char * name)
3435 for (int h = 0; h < _header_list.length(); h++) {
3436 HeaderList * hl = _header_list[h];
3437 if (hl->show != SMD_NEVER &&
3438 strcmp(hl->label, name) == 0) {
3447 SendMsgDialog::headerValueChanged(Widget,
3448 XtPointer client_data,
3451 SendMsgDialog * self = (SendMsgDialog *)client_data;
3452 self->_headers_changed = DTM_TRUE;
3456 SendMsgDialog::reattachHeaders(void)
3458 // We have to walk through the entire list of headers, attaching
3459 // the shown headers to the ones above them.
3461 HeaderList * hl = _header_list[0];
3462 Widget previous_form = hl->form_widget;
3464 for (int h = 1; h < _header_list.length(); h++) {
3465 hl = _header_list[h];
3469 previous_form = hl->form_widget;
3473 XtVaSetValues(hl->form_widget,
3474 XmNtopAttachment, XmATTACH_WIDGET,
3475 XmNtopWidget, previous_form,
3477 previous_form = hl->form_widget;
3485 forceFormResize(_main_form);
3486 forceFormResize(_header_form);
3490 SendMsgDialog::justifyHeaders(void)
3492 // Find out which header label has the longest display width to right
3493 // justify all labels.
3495 Dimension longest = 0;
3498 for (int count = 0; count < _header_list.length(); count++) {
3499 HeaderList * hl = _header_list[count];
3500 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3504 XtVaGetValues(hl->label_widget,
3506 XmNmarginLeft, &margin,
3509 if ( w > longest ) {
3514 for (int adjust = 0; adjust < _header_list.length(); adjust++) {
3515 HeaderList * hl = _header_list[adjust];
3516 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3520 XtVaGetValues(hl->label_widget,
3522 XmNmarginLeft, &margin,
3525 XtVaSetValues(hl->label_widget, XmNmarginLeft, (longest-w) > 0 ? longest-w : 1, NULL );
3530 SendMsgDialog::forceFormResize(Widget form)
3532 // The Motif Form widget is at least a little bit brain damaged.
3533 // We need to convince it to do the right thing after we make
3534 // minor adjustments in the children.
3536 Dimension width, height, border;
3540 XmNborderWidth, &border,
3544 XmNwidth, width + 1,
3545 XmNheight, height + 1,
3556 SendMsgDialog::unfilled_headers()
3558 // Walk through the headers. See if any of them have a value.
3560 for (int scan = 0; scan < _header_list.length(); scan++) {
3561 HeaderList * hl = _header_list[scan];
3562 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
3566 char * value = NULL;
3567 XtVaGetValues(hl->field_widget,
3571 if (strlen(value) > 0) {
3581 // Method checks if self has text in it.
3584 SendMsgDialog::checkDirty()
3586 if (this->unfilled_headers() &&
3587 (this->get_editor()->textEditor()->no_text()) &&
3588 (this->get_editor()->attachArea()->getIconCount() == 0) ) {
3590 // return FALSE so quit() can go ahead.
3601 SendMsgDialog::handleQuitDialog()
3605 DtMail::Session *m_session = theRoamApp.session()->session();
3606 const char * value = NULL;
3609 m_session->mailRc(error)->getValue(error, "expert", &value);
3610 if (error.isNotSet() && value != NULL)
3613 free((void*) value);
3618 DtMailGenDialog *dialog = this->genDialog();
3620 dialog->setToQuestionDialog(
3630 "The Compose window contains text or\n\
3631 attachments that will be lost if\n\
3632 the window is closed.\n\
3633 Close the Compose window?")
3635 helpId = DTMAILHELPCLOSECOMPOSEWINDOW;
3636 if ( dialog->post_and_return(
3647 helpId) == 1 ) { // Close selected
3652 return(FALSE); // Cancel selected
3657 SendMsgDialog::goAway(
3658 Boolean checkForDirty
3662 if (!checkForDirty) {
3667 // Check to see if self has contents (ie., is dirty)
3669 Boolean is_dirty = this->checkDirty();
3672 if (isIconified()) {
3673 MainWindow::manage();
3676 // Enquire if user really wants this window to go away
3678 Boolean really_quit = this->handleQuitDialog();
3689 SendMsgDialog::manage()
3691 MenuWindow::manage();
3693 HeaderList * hl = _header_list[0];
3694 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3699 SendMsgDialog::unmanage()
3701 MenuWindow::unmanage();
3702 XFlush(XtDisplay(this->_main_form));
3703 XSync(XtDisplay(this->_main_form), False);
3709 _compose_head = NULL;
3717 Compose::Compose_Win *a_node;
3718 Compose::Compose_Win *next_node;
3720 theRoamApp.registerPendingTask();
3722 a_node = _compose_head;
3725 next_node = a_node->next;
3726 if (!a_node->in_use)
3729 free((void*) a_node);
3734 theRoamApp.unregisterPendingTask();
3738 Compose::putWin(SendMsgDialog *smd, Boolean in_use)
3740 Compose::Compose_Win* a_node = NULL;
3741 Compose::Compose_Win *tmp = NULL;
3744 // Update the _not_in_use count.
3750 // Check to see if compose window is already in the list.
3752 for (a_node = _compose_head; a_node; a_node=a_node->next)
3754 if (a_node->win == smd)
3756 a_node->in_use = in_use;
3761 // Need new node with smd.
3762 tmp = (Compose::Compose_Win *)malloc(sizeof(Compose::Compose_Win));
3765 tmp->in_use = in_use;
3767 // If nothing is cached so far, add this Compose window to the head.
3768 if (NULL == _compose_head)
3770 _compose_head = tmp;
3774 // There exists a cache. Add this compose window to the tail.
3775 for (a_node=_compose_head; a_node; a_node=a_node->next)
3777 if (NULL == a_node->next)
3786 Compose::getUnusedWin()
3788 if (NULL == _compose_head) return NULL;
3790 Compose::Compose_Win* a_node = NULL;
3791 Compose::Compose_Win* the_node = NULL;
3793 // Find a node with unused smd. Return smd
3794 for (a_node=_compose_head; a_node; a_node=a_node->next)
3796 if (!a_node->in_use)
3798 a_node->in_use = TRUE;
3807 // Get a compose window either by creating a new SendMsgDialog or
3808 // from the recycle list.
3812 SendMsgDialog *newsend = NULL;
3814 #ifdef DTMAIL_TOOLTALK
3817 XtRemoveTimeOut(_timeout_id);
3822 newsend = getUnusedWin();
3826 // We have no unused SMDs around; therefore, create new window.
3827 theRoamApp.busyAllWindows();
3828 newsend = new SendMsgDialog();
3829 newsend->initialize();
3831 putWin(newsend, TRUE);
3832 theRoamApp.unbusyAllWindows();
3836 newsend->resetHeaders();
3837 newsend->displayInCurrentWorkspace();
3840 newsend->text_unselected();
3842 newsend->startAutoSave();
3844 // Get new Message Handle
3845 newsend->setMsgHnd();
3846 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
3847 newsend->setTitle(ttl);
3848 newsend->setIconTitle(ttl);
3854 SendMsgDialog::setTitle(char *subject)
3856 char *format = "%s - %s";
3857 char *prefix = GETMSG(DT_catd, 1, 6, "Mailer");
3861 len = strlen(format) + strlen(prefix) + strlen(subject) + 1;
3862 new_title = new char[len];
3863 sprintf(new_title, format, prefix, subject);
3866 delete [] new_title;
3870 SendMsgDialog::resetHeaders(void)
3872 DtMail::Session *m_session = theRoamApp.session()->session();
3873 const char * value = NULL;
3877 m_session->mailRc(error)->getValue(error, "additionalfields", &value);
3879 // Return if no props were applied and headers did not change.
3880 if ((_additionalfields == NULL && value == NULL) ||
3881 ( _additionalfields != NULL && value != NULL &&
3882 strcmp (_additionalfields, value) == 0))
3885 free((void*) value);
3890 // User changed the Header list via props. Recreate list
3893 // First hide all shown headers
3894 for (i=0; i < _header_list.length(); i++) {
3895 HeaderList * hl = _header_list[i];
3896 if (hl->show == SMD_SHOWN) {
3897 hl->show = SMD_HIDDEN;
3898 XtUnmanageChild(hl->form_widget);
3902 // Now remove the old list.
3903 DtVirtArray<PropStringPair *> results(8);
3904 parsePropString(_additionalfields, results);
3905 for (i=0, j=results.length(); i < j; i++) {
3906 PropStringPair * psp = results[i];
3907 int slot = lookupHeader(psp->label);
3908 // don't allow removal of default headers.
3909 HeaderList * hl = _header_list[slot];
3910 if (!reservedHeader(hl->label)) {
3912 hl->show = SMD_NEVER;
3914 else if (hl->value != NULL) {
3919 while(results.length()) {
3920 PropStringPair * psp = results[0];
3925 if (_additionalfields != NULL)
3926 free(_additionalfields);
3927 if (value != NULL && *value != '\0')
3928 _additionalfields = strdup(value);
3930 _additionalfields = NULL;
3932 parsePropString(value, results);
3935 for (j=results.length(), i=0; i < j; i++) {
3936 PropStringPair * psp = results[i];
3937 int slot = lookupHeader(psp->label);
3940 HeaderList * hl = _header_list[slot];
3941 if (!reservedHeader(hl->label))
3942 hl->show = SMD_HIDDEN;
3943 if (hl->value != NULL) {
3947 if (psp->value != NULL)
3948 hl->value = strdup(psp->value);
3951 HeaderList * copy_hl = new HeaderList;
3952 copy_hl->label = strdup(psp->label);
3953 copy_hl->header = strdup(psp->label);
3955 copy_hl->value = strdup(psp->value);
3956 copy_hl->show = SMD_HIDDEN;
3957 _header_list.append(copy_hl);
3959 while(results.length()) {
3960 PropStringPair * psp = results[0];
3965 createHeaders(_header_form);
3966 doDynamicHeaderMenus();
3969 free((void*) value);
3973 SendMsgDialog::setInputFocus(const int mode)
3977 HeaderList * hl = _header_list[0];
3978 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3980 else if (mode == 1) {
3981 Widget edWid = _my_editor->textEditor()->get_editor();
3982 (void) XmProcessTraversal(edWid, XmTRAVERSE_CURRENT);
3987 SendMsgDialog::attachmentFeedback(
3995 this->normalCursor();
4000 SendMsgDialog::hasAddressee()
4004 DtMail::Envelope * env = _msgHandle->getEnvelope(error);
4006 // Walk through the headers.
4007 // Return TRUE if the message has a value for either of the
4008 // following headers: To:, Cc:, or Bcc:.
4009 // Return FALSE if none of the three headers have any value.
4011 for (int scan = 0; scan < _header_list.length(); scan++) {
4012 HeaderList * hl = _header_list[scan];
4013 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
4016 if ((strcmp(hl->label, "To") == 0) ||
4017 (strcmp(hl->label, "Cc") == 0) ||
4018 (strcmp(hl->label, "Bcc") == 0)) {
4019 char * value = NULL;
4020 XtVaGetValues(hl->field_widget,
4024 for (char *cv = value; *cv; cv++) {
4025 if (!isspace(*cv)) {
4027 return(TRUE); // text value contains contents
4030 XtFree(value); // text value is "content free" - try the next one
4034 return(FALSE); // no field has contents