4 * $TOG: SendMsgDialog.C /main/43 1999/03/25 13:42:29 mgreess $
6 * RESTRICTED CONFIDENTIAL INFORMATION:
8 * The information in this document is subject to special
9 * restrictions in a confidential disclosure agreement between
10 * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
11 * document outside HP, IBM, Sun, USL, SCO, or Univel without
12 * Sun's specific written approval. This document and all copies
13 * and derivative works thereof must be returned or destroyed at
16 * Copyright 1993, 1994, 1995 Sun Microsystems, Inc. All rights reserved.
21 #include <EUSCompat.h>
22 #include "AttachArea.h"
23 #include "Attachment.h"
25 #include "SendMsgDialog.h"
28 #include "ComposeCmds.hh"
31 #include "ButtonInterface.h"
32 #include "MemUtils.hh"
34 #include "DtMailHelp.hh"
35 #include "SelectFileCmd.h"
36 #include "DtMailGenDialog.hh"
37 #include "Application.h"
38 #include <X11/Intrinsic.h>
39 #include <X11/IntrinsicP.h>
40 #include <Xm/RepType.h>
41 #include <Xm/ScrolledW.h>
46 #include <Xm/RowColumn.h>
48 #include <Xm/CascadeB.h>
49 #include <Xm/SeparatoG.h>
50 #include <DtMail/IO.hh>
51 #include <DtMail/DtMailP.hh>
54 #include <sys/param.h>
60 #include "str_utils.h"
62 #define OFFSET 10 // Number of spaces from margin
64 #ifdef DTMAIL_TOOLTALK
65 // Time to self destruct
66 #define DESTRUCT_TIMEOUT 60000 // 1 minutes
69 // Pipe used between RFCTransport::childHandler and XtAppAddInput
70 static int _transfds[2];
72 static const char *ComposeIcon = "IcMcomp";
74 struct DefaultHeaders {
80 SendMsgDialog::ShowState show;
83 static int isInitializedDefaultHeaderList = 0;
84 static DefaultHeaders DefaultHeaderList[] = {
85 { 1, 241, "To", NULL, DtMailMessageTo, SendMsgDialog::SMD_ALWAYS },
86 { 1, 242, "Subject", NULL, DtMailMessageSubject, SendMsgDialog::SMD_ALWAYS },
87 { 1, 243, "Cc", NULL, DtMailMessageCc, SendMsgDialog::SMD_ALWAYS },
88 { 1, 244, "Bcc", NULL, DtMailMessageBcc, SendMsgDialog::SMD_HIDDEN },
89 { 0, 0, NULL, NULL, NULL, SendMsgDialog::SMD_NEVER }
92 // These headers can never be controlled by the user. They are generated
93 // by dtmail and the user is not allowed to override the values generated
94 // by the software. Besides, most users would not have a clue as to what
95 // a correct value would be.
97 static const char * BlockedHeaders[] = {
101 "Content-Transfer-Encoding",
109 block(const char * header)
111 for (const char ** test = BlockedHeaders; *test; test++) {
112 if (strcasecmp(header, *test) == 0) {
122 Compose theCompose; // Manages all compose windows.
124 SendMsgDialog::HeaderList::HeaderList(void)
135 SendMsgDialog::HeaderList::HeaderList(const HeaderList & other)
140 form_widget = other.form_widget;
141 label_widget = other.label_widget;
142 field_widget = other.field_widget;
145 label = strdup(other.label);
149 header = strdup(other.header);
153 value = strdup(other.value);
157 SendMsgDialog::HeaderList::~HeaderList(void)
171 Boolean SendMsgDialog::reservedHeader(const char *label)
173 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++)
174 if (strcmp(label, hl->label) == 0)
180 SendMsgDialog::SendMsgDialog()
181 : MenuWindow ( "ComposeDialog", True ),
188 _show_attach_area = FALSE;
191 _already_sending = FALSE;
197 _bccPopupCmdlist = NULL;
198 _bccPopupMenu = NULL;
199 _bccPopupMenuBar = NULL;
200 _ccPopupCmdlist = NULL;
202 _ccPopupMenuBar = NULL;
203 _toPopupCmdlist = NULL;
205 _toPopupMenuBar = NULL;
208 _attachmentActionsList = NULL;
209 _attachmentMenu = NULL;
210 _attachmentMenuList = NULL;
211 _attachmentPopupMenuList = NULL;
212 _textPopupMenuList = NULL;
214 _att_show_pane = NULL;
215 _att_select_all = NULL;
219 _att_undelete = NULL;
221 _att_select_all = NULL;
222 _auto_save_interval = 0;
223 _auto_save_path = NULL;
224 _auto_save_file = NULL;
225 _dead_letter_buf = NULL;
227 _file_include = NULL;
228 _file_save_as = NULL;
233 _format_word_wrap = NULL;
234 _format_settings = NULL;
235 _format_find_change = NULL;
236 _format_spell = NULL;
239 _format_cascade = NULL;
240 _format_separator = NULL;
241 _templateList = NULL;
243 // Now we need to get the additional headers from the property system.
246 DtMail::Session * d_session = theRoamApp.session()->session();
247 DtMail::MailRc * mail_rc = d_session->mailRc(error);
249 const char * value = NULL;
250 mail_rc->getValue(error, "additionalfields", &value);
252 DtVirtArray<PropStringPair *> results(8);
253 if (error.isNotSet()) {
254 _additionalfields = strdup(value);
255 parsePropString(value, results);
261 // Load the header list with the predefined/fixed headers.
263 if (! isInitializedDefaultHeaderList)
264 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
266 GETMSG(DT_catd, hl->msg_set, hl->msg_number, hl->dflt_label);
268 isInitializedDefaultHeaderList = TRUE;
271 for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
272 HeaderList * copy_hl = new HeaderList;
273 copy_hl->label = strdup(hl->label);
274 copy_hl->value = getPropStringValue(results, hl->label);
275 copy_hl->header = strdup(hl->header);
276 copy_hl->show = hl->show;
277 _header_list.append(copy_hl);
280 if (error.isNotSet()) {
282 for (int mrc = 0; mrc < results.length(); mrc++) {
283 PropStringPair * psp = results[mrc];
285 if (!reservedHeader(psp->label)) {
286 HeaderList * copy_hl = new HeaderList;
287 copy_hl->label = strdup(psp->label);
288 copy_hl->header = strdup(psp->label);
290 copy_hl->value = strdup(psp->value);
291 copy_hl->show = SMD_HIDDEN;
292 _header_list.append(copy_hl);
296 while(results.length()) {
297 PropStringPair * psp = results[0];
303 _additionalfields = NULL;
307 SendMsgDialog::~SendMsgDialog()
315 delete _file_include;
316 delete _file_save_as;
329 delete _edit_select_all;
335 XtDestroyWidget(_ccPopupMenu);
336 delete _ccPopupMenuBar;
337 delete _ccPopupCmdlist;
339 XtDestroyWidget(_toPopupMenu);
340 delete _toPopupMenuBar;
341 delete _toPopupCmdlist;
343 XtDestroyWidget(_bccPopupMenu);
344 delete _bccPopupMenuBar;
345 delete _bccPopupCmdlist;
352 delete _att_undelete;
354 delete _att_select_all;
356 delete _attachmentActionsList;
360 delete _format_word_wrap;
361 delete _format_settings;
362 delete _format_find_change;
363 delete _format_spell;
364 delete _format_separator;
366 // Things created by createWorkArea()
369 delete _close_button;
372 // Allocated using 'malloc'.
373 // Purify requires us to free it using 'free'.
375 free(_auto_save_path);
376 if (_auto_save_file) {
377 delete _auto_save_file;
379 if (_dead_letter_buf) {
380 delete _dead_letter_buf;
383 delete _attachmentMenuList;
384 delete _attachmentPopupMenuList;
385 delete _textPopupMenuList;
389 // Callback for each header row
391 header_form_traverse(Widget w, XtPointer, XtPointer)
393 (void) XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
398 send_pushb_callback(Widget, XtPointer, XtPointer)
402 #endif /* DEAD_WOOD */
404 // Create Message Handle with empty first body part.
406 SendMsgDialog::makeMessage(void)
409 DtMail::Session * d_session = theRoamApp.session()->session();
415 DtMail::Message * msg = d_session->messageConstruct(error,
421 if (error.isSet() || !msg) {
425 DtMail::BodyPart * bp = msg->newBodyPart(error, NULL);
427 // For now, reserve the first body part for text.
428 setFirstBPHandled(TRUE);
433 // Before Submitting, call this routine.
434 // This routine does not save attachment(s).
435 // Check point routine should also call this routine.
437 SendMsgDialog::updateMsgHnd()
440 DtMail::Envelope * env;
443 env = _msgHandle->getEnvelope(error);
446 char * widget_text = this->text();
447 if (widget_text && *widget_text == '\0') {
453 textLen = strlen(widget_text);
455 // Even if textlen is 0 because user has cleared all previous text,
456 // need to setContents again to clear first BP. Otherwise, deleted
457 // text will show up.
459 // Get FirstBodyPart and fill it up.
460 DtMail::BodyPart *bp = _msgHandle->getFirstBodyPart(error);
461 bp->setContents(error, widget_text, textLen, NULL, NULL, 0, NULL);
462 setFirstBPHandled(TRUE);
464 if (NULL != widget_text)
468 // Before Submitting, also call this routine.
469 // This routine fills the message handle with attachment(s).
470 // The reason why we get attachment(s) from the Back End before submission
471 // is in case the attachment(s) are updated during the compose session.
472 // If changes are saved back, then the AttachArea class would register the
473 // update with Back End. So BE always has the latest attachments.
475 SendMsgDialog::updateMsgHndAtt()
477 if ( _inclMsgHandle == NULL )
478 return; // No attachments
481 DtMail::BodyPart *msgBP;
482 DtMail::BodyPart *inclBP = _inclMsgHandle->getFirstBodyPart(error);
483 const void *contents;
490 // If message handle to be copied, _inclMsgHandle, does not have its first
491 // body part as text, then get its first body part content and copy it.
492 // If it contains text, then skip it.
493 if ( !_inclMsgHasText ) {
494 inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
495 if ( _firstBPHandled ) {
496 msgBP = _msgHandle->newBodyPart(error, _lastAttBP);
498 msgBP = _msgHandle->getFirstBodyPart(error);
500 msgBP->setContents(error, contents, len, type, name, mode, desc);
506 // Continue to get the next body part and start copy.
507 while ((inclBP = _inclMsgHandle->getNextBodyPart(error, inclBP)) != NULL) {
508 inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
509 if ( _firstBPHandled ) {
510 msgBP = _msgHandle->newBodyPart(error, _lastAttBP);
512 msgBP = _msgHandle->getFirstBodyPart(error);
514 msgBP->setContents(error, contents, len, type, name, mode, desc);
518 } // end of while loop
521 // Update the _lastAttBP pointer so that subsequent newBodyPart() calls can
522 // return a message body part following the last body part.
524 SendMsgDialog::setLastAttBP()
527 _lastAttBP = _msgHandle->getNextBodyPart(error, _lastAttBP);
530 // This sister routine is needed when _lastAttBP has not been initialized.
532 SendMsgDialog::setLastAttBP(DtMail::BodyPart *bp)
537 // Initialize _msgHandle
539 SendMsgDialog::setMsgHnd()
541 _msgHandle = makeMessage();
542 _inclMsgHandle = NULL;
543 _inclMsgHasText = FALSE;
547 // Set timeout to ten minutes (as milliseconds)
550 SendMsgDialog::startAutoSave(void)
553 if(!_auto_save_path) return;
554 _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
555 getAutoSaveInterval(),
561 SendMsgDialog::stopAutoSave(void)
563 if (!_auto_save_interval) {
567 XtRemoveTimeOut(_auto_save_interval);
568 _auto_save_interval = 0;
570 unlink(_auto_save_file);
571 free(_auto_save_file);
572 _auto_save_file = NULL;
575 static const char * DEAD_LETTER_DIR = "~/dead_letter";
576 static const char * BACKUP_DEAD_LETTER = "./dead_letter";
577 static const char * PREFIX = "mail.dead.letter";
580 SendMsgDialog::mkAutoSavePath(void)
582 // First, see if we need to set up the path.
585 if (!_auto_save_path) {
586 DtMail::Session * d_session = theRoamApp.session()->session();
588 const char *save_path = NULL;
590 d_session->mailRc(error)->getValue(error, "deaddir", &save_path);
591 if (error.isNotSet() && save_path != NULL && *save_path != '\0')
592 _auto_save_path = d_session->expandPath(error, save_path);
595 _auto_save_path = d_session->expandPath(error, DEAD_LETTER_DIR);
596 if (!_auto_save_path)
597 _auto_save_path = d_session->expandPath(error, BACKUP_DEAD_LETTER);
600 if (NULL != save_path)
601 free((void*) save_path);
604 // If we still have a path, punt.
606 if (!_auto_save_path) {
610 if (SafeAccess(_auto_save_path, W_OK) != 0) {
611 if (errno != ENOENT) {
612 // Not an error we can overcome here.
614 free(_auto_save_path);
615 _auto_save_path = NULL;
619 if (mkdir(_auto_save_path, 0700) < 0) {
620 free(_auto_save_path);
621 _auto_save_path = NULL;
626 // Now we run through the possible file names until we hit pay dirt.
628 _auto_save_file = (char*) malloc((size_t) strlen(_auto_save_path) + 100);
629 for (int suffix = 1; ; suffix++) {
630 sprintf(_auto_save_file, "%s/%s.%d", _auto_save_path, PREFIX, suffix);
631 if (SafeAccess(_auto_save_file, F_OK) != 0) {
638 SendMsgDialog::loadDeadLetter(const char * path)
640 _auto_save_file = strdup(path);
643 _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
644 getAutoSaveInterval(),
650 SendMsgDialog::autoSaveCallback(XtPointer client_data, XtIntervalId * id)
652 SendMsgDialog * self = (SendMsgDialog *)client_data;
654 if (self->_auto_save_interval != *id) {
655 // Random noise. Ignore it.
661 self->_auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
662 self->getAutoSaveInterval(),
668 SendMsgDialog::doAutoSave(char *filename)
670 DtMail::Session * d_session = theRoamApp.session()->session();
674 setStatus(GETMSG(DT_catd, 3, 70, "Writing dead letter..."));
678 assert((NULL != filename));
679 RFCWriteMessage(error, d_session, filename, _msgHandle);
680 if((DTMailError_t) error == DTME_OutOfSpace )
682 RoamMenuWindow::ShowErrMsg((char *)error.getClient(),TRUE,this );
689 SendMsgDialog::doAutoSave(void)
691 doAutoSave(_auto_save_file);
695 SendMsgDialog::getAutoSaveInterval(void)
698 // Initialize the mail_error.
702 DtMail::Session * d_session = theRoamApp.session()->session();
703 DtMail::MailRc * mail_rc = d_session->mailRc(error);
706 const char * value = NULL;
707 mail_rc->getValue(error, "composeinterval", &value);
708 if (error.isSet() || value == NULL) {
709 save_interval = 10 * 60 * 1000; // 10 minutes
712 save_interval = (int) strtol(value, NULL, 10) * 60 * 1000;
713 save_interval = (save_interval <= 0) ? 10 * 60 * 1000 : save_interval;
718 return(save_interval);
722 SendMsgDialog::setInclMsgHnd(DtMail::Message *msg, Boolean text)
724 _inclMsgHandle = msg;
725 _inclMsgHasText = text;
729 SendMsgDialog::setFirstBPHandled(Boolean handle)
731 _firstBPHandled = handle;
735 SendMsgDialog::setHeader(const char * name, const char * value)
737 // See if this header is in the list. If so, set the widget for
740 int slot = lookupHeader(name);
745 HeaderList * hl = _header_list[slot];
746 if (hl->show == SMD_NEVER) {
747 // The user removed this header via props.
752 if (hl->field_widget) {
753 XtVaSetValues(hl->field_widget,
756 if (hl->show == SMD_HIDDEN) {
757 changeHeaderState(name);
763 SendMsgDialog::setHeader(const char * name, DtMailValueSeq & value)
765 if (value.length() == 0) {
769 if (value.length() == 1) {
770 setHeader(name, *(value[0]));
774 for (int slen = 0; slen < value.length(); slen++) {
775 max_len += strlen(*(value[slen]));
778 char * new_str = new char[max_len + (value.length() * 3)];
781 for (int copy = 0; copy < value.length(); copy++) {
783 strcat(new_str, " ");
786 strcat(new_str, *(value[copy]));
789 setHeader(name, new_str);
795 SendMsgDialog::loadHeaders(DtMail::Message * input,
796 DtMailBoolean load_all)
798 // We are going to go through every header in the message.
799 // If it is not one of the headers we block, then we will
800 // load it into the header pane, depending on the setting of
801 // load_all. If true, then we load every header we allow and
802 // create new dynamic headers as necessary. If load_all is false,
803 // then we only load headers that already are available in the
807 DtMail::Message * msg = (input ? input : _msgHandle);
808 DtMail::Envelope * env = msg->getEnvelope(error);
809 DtMailHeaderHandle hnd;
813 DtMailValueSeq value;
815 for (hnd = env->getFirstHeader(error, &name, value);
816 error.isNotSet() && hnd;
817 hnd = env->getNextHeader(error, hnd, &name, value)) {
819 // Always ignore the Unix from line.
822 strcmp(name, "From") == 0) {
831 // See if the name is one we always block.
839 int slot = lookupHeader(name);
841 // We dont have a place for this information. We may need
842 // to create a new header.
845 HeaderList *hl = new HeaderList;
846 hl->label = strdup(name);
847 hl->header = strdup(name);
848 hl->show = SMD_HIDDEN;
849 _header_list.append(hl);
850 createHeaders(_header_form);
851 doDynamicHeaderMenus();
860 setHeader(name, value);
867 SendMsgDialog::storeHeaders(DtMail::Message * input)
869 DtMail::Message * msg = (input ? input : _msgHandle);
871 DtMail::Envelope * env = msg->getEnvelope(error);
873 // Walk through the headers. Fetch the strings from the ones
874 // that are visible to the user and stuff them into the
877 for (int scan = 0; scan < _header_list.length(); scan++) {
878 HeaderList * hl = _header_list[scan];
879 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
884 XtVaGetValues(hl->field_widget,
887 // If the header has a value, we want to set the value
888 // in the back end. Otherwise, we don't want to send
889 // out a blank header, so we remove it.
890 if (strlen(value) > 0) {
891 env->setHeader(error, hl->header, DTM_TRUE, value);
894 env->removeHeader(error, hl->header);
903 SendMsgDialog::changeHeaderState(const char * name)
905 int slot = lookupHeader(name);
910 HeaderList * hl = _header_list[slot];
911 if (hl->show == SMD_ALWAYS || hl->show == SMD_NEVER) {
915 // If the user is trying to remove a header with a value other than
916 // the default, we should at least ask.
918 if (hl->show == SMD_SHOWN) {
920 XtVaGetValues(hl->field_widget,
923 if (strlen(value) > 0) {
924 if (!hl->value || strcmp(value, hl->value) != 0) {
925 char *buf = new char[256];
927 GETMSG(DT_catd, 2, 17,
928 "You have edited \"%s\". Delete anyway?"),
930 _genDialog->setToWarningDialog(GETMSG(DT_catd, 3, 71,
933 char * helpId = DTMAILHELPERROR;
934 int answer = _genDialog->post_and_return(
935 GETMSG(DT_catd, 3, 72, "OK"),
936 GETMSG(DT_catd, 3, 73, "Cancel"),
949 // Now we need to toggle the current state of the header.
951 char *label = new char[100];
952 if (hl->show == SMD_SHOWN) {
953 XtUnmanageChild(hl->form_widget);
954 hl->show = SMD_HIDDEN;
955 sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
959 XtManageChild(hl->form_widget);
960 hl->show = SMD_SHOWN;
961 sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
967 // Change the label on the menu item.
969 strcat(label, hl->label);
971 char *button_name = new char[100];
972 sprintf(button_name, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
973 strcat(button_name, hl->label);
974 strcat(button_name, ":");
976 _menuBar->changeLabel(_format_menu, button_name, label);
978 delete [] button_name;
982 SendMsgDialog::setStatus(const char * str)
984 char *tmpstr = strdup(str);
985 XmString label = XmStringCreateLocalized(tmpstr);
987 XtVaSetValues(_status_text,
988 XmNlabelString, label,
991 XmUpdateDisplay(baseWidget());
997 SendMsgDialog::clearStatus(void)
1003 SendMsgDialog::isMsgValid(void)
1012 // Sendmail is exed'd and this parent process returns immediately. When
1013 // the sendmail child exits, this function is called with the pid of the
1014 // child and its status.
1016 SendMsgDialog::sendmailErrorProc (int, int status, void *data)
1018 SendMsgDialog *smd = (SendMsgDialog *)data;
1019 char *helpId = NULL;
1020 char *buf = new char[2048];
1022 smd->_first_time = TRUE;
1023 smd->_takeDown = FALSE;
1025 // pid is the child process (sendmail) id
1026 // status is the exit status of the child process
1027 // data is any extra data associated with the child process
1031 // The mail was successfully sent so return the compose
1032 // window to the cache and then return.
1033 smd->_send_button->activate();
1034 smd->_close_button->activate();
1038 case DTME_BadMailAddress:
1040 * There was an error in one or more of the email addresses.
1041 * Ask the user to type in a valid address and try again.
1043 sprintf(buf, GETMSG(DT_catd, 5, 5,
1044 "Some of the addresses in the message are incorrect,\n\
1045 and do not refer to any known users in the system.\n\
1046 Please make sure all of the addresses are valid and try again."));
1047 helpId = DTMAILHELPBADADDRESS;
1053 * Mailer ran out of memory. Ask the user to quit some other
1054 * applications so there will be more memory available.
1057 sprintf(buf, GETMSG(DT_catd, 5, 6,
1058 "Mailer does not have enough memory\n\
1059 available to send this message.\n\
1060 Try quitting other applications and\n\
1061 resend this message."));
1062 helpId = DTMAILHELPNOMEMORY;
1066 case DTME_TransportFailed:
1069 * There was an error from the mail transport (sendmail).
1072 sprintf(buf, GETMSG(DT_catd, 5, 7,
1073 "An error occurred while trying to send your message.\n\
1074 Check to make sure the message was received. If not,\n\
1075 you may have to resend this message."));
1076 helpId = DTMAILHELPTRANSPORTFAILED;
1079 // popup the compose window
1081 smd->_send_button->activate();
1082 smd->_close_button->activate();
1084 // popup the error dialog
1085 smd->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1087 smd->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1093 SendMsgDialog::send_message(const char * trans_impl, int trans_type)
1095 DtMailEnv mail_exec_error;
1096 DtMailOperationId id;
1097 DtMail::Transport * mail_transport;
1098 DtMail::Session * d_session = theRoamApp.session()->session();
1099 DtMailEditor *editor = this->get_editor();
1100 AttachArea *attachArea = editor->attachArea();
1101 int numPendingActions, answer;
1102 char *helpId = NULL;
1103 char *buf = new char[2048];
1104 static int first_time = 1;
1107 // Check to see if we are already trying to send from this
1108 // SendMsgDialog. If we are, we don't want to do it again.
1109 if (_already_sending) {
1113 _already_sending = TRUE;
1116 // First remove (unmap) the window;
1117 // send the message.
1118 // If you do it in the reverse order, users get confused coz
1119 // the window remains behind for a couple seconds after hitting
1120 // "Send" and it ...
1122 _send_button->deactivate();
1123 _close_button->deactivate();
1125 mail_exec_error.clear();
1128 // Check if message has addressees. If it doesn't, what sense does
1129 // it make to Send it?
1131 if (!this->hasAddressee()) {
1132 // Message has no valid addressee. Pop up error dialog.
1134 sprintf(buf, GETMSG(DT_catd, 5, 8,
1135 "Try Send after specifying recipient(s) of the message in \nthe To:, Cc:, or Bcc: fields."));
1137 helpId = DTMAILHELPNEEDADDRESSEE;
1139 // Need help tag for above HelpID.
1141 // popup the compose window
1143 _send_button->activate();
1144 _close_button->activate();
1146 _genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1148 _genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1150 // Reset the flag before we return.
1151 _already_sending = FALSE;
1157 // Since we are Send-ing, the SMD can be taken down later without
1158 // checking for dirty...
1165 // Just get text from text widget; attachment BPs are filled
1166 // already when they are included/forwarded/added.
1170 // Check if there are any pending attachments (open, print....)
1171 // If there are, pop up the dialog.
1172 // If the user wants to Send the message as is, continue with the
1173 // submission process.
1174 // If the user opted to Cancel, then return.
1177 numPendingActions = attachArea->getNumPendingActions();
1178 sprintf(buf, GETMSG(
1182 "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." ));
1184 while (numPendingActions != 0) {
1185 // popup the compose window
1187 _send_button->activate();
1188 _close_button->activate();
1191 * The user tried to send a messages without saving changes in
1192 * some open attachments. This warning makes sure that is what
1193 * the user intended.
1196 _genDialog->setToQuestionDialog(
1197 GETMSG(DT_catd, 5, 1, "Mailer"),
1199 helpId = DTMAILHELPPENDINGACTIONS;
1201 answer = _genDialog->post_and_return(helpId);
1205 numPendingActions = 0;
1207 _send_button->deactivate();
1208 _close_button->deactivate();
1210 else if (answer == 2) {
1212 // Reset the flag before we return.
1213 _already_sending = FALSE;
1219 // Determine which transport mechanism will be used.
1220 if ( trans_type ) { // Default
1221 // Only register XtAppAddInput once
1224 // Create the pipe between the RFCTransport::childHandler
1225 // and XtAppAddInput
1226 if (pipe(_transfds) < 0) {
1227 // If this failed, make sure we try to initialize again later.
1228 mail_exec_error.setError(DTME_NoMemory);
1229 popupMemoryError (mail_exec_error);
1231 // Reset the flag before we return.
1232 _already_sending = FALSE;
1237 // Call ourproc when input is available on _transfds[0]
1238 XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1239 _transfds[0], (XtPointer)XtInputReadMask,
1240 (XtInputCallbackProc)
1241 (theRoamApp.default_transport()->getSendmailReturnProc()),
1246 // Tell the transport where the callback is
1247 theRoamApp.default_transport()->initTransportData( _transfds,
1248 &(SendMsgDialog::sendmailErrorProc), this);
1249 id = theRoamApp.default_transport()->submit(mail_exec_error,
1250 _msgHandle, _log_msg);
1253 // Construct transport
1254 mail_transport = d_session->transportConstruct(mail_exec_error,
1255 trans_impl, RoamApp::statusCallback, this);
1257 // Only register XtAppAddInput once
1260 // Create the pipe between the RFCTransport::childHandler
1261 // and XtAppAddInput
1262 if (pipe(_transfds) < 0) {
1263 // If this failed, make sure we try to initialize again later.
1264 mail_exec_error.setError(DTME_NoMemory);
1265 popupMemoryError (mail_exec_error);
1267 // Reset the flag before we return.
1268 _already_sending = FALSE;
1273 // Call ourproc when input is available on _transfds[0]
1274 XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1275 _transfds[0], (XtPointer)XtInputReadMask,
1276 (XtInputCallbackProc)(mail_transport->getSendmailReturnProc()),
1281 // Tell the transport where the callback is
1282 mail_transport->initTransportData(_transfds,
1283 &(SendMsgDialog::sendmailErrorProc), this);
1284 id = mail_transport->submit(mail_exec_error, _msgHandle, _log_msg);
1287 popupMemoryError (mail_exec_error);
1289 // Reset the flag before we return.
1290 _already_sending = FALSE;
1296 SendMsgDialog::popupMemoryError(DtMailEnv &error)
1298 char *helpId = NULL;
1299 char *buf = new char[2048];
1303 // Popup an error dialog if necessary.
1304 if (error.isSet()) {
1305 if ((DTMailError_t)error == DTME_NoMemory) {
1308 * Mailer ran out of memory. Ask the user to quit some other
1309 * applications so there will be more memory available.
1311 sprintf(buf, GETMSG(DT_catd, 5, 6,
1312 "Mailer does not have enough memory\n\
1313 available to send this message.\n\
1314 Try quitting other applications and\n\
1315 resend this message."));
1316 helpId = DTMAILHELPNOMEMORY;
1320 * An unidentifiable error happened during mail transport
1321 * Pop it up *as is* (need to update this function if so)
1323 sprintf(buf, "%s", (const char *)error);
1324 helpId = DTMAILHELPERROR;
1327 // popup the compose window
1329 _send_button->activate();
1330 _close_button->activate();
1332 // popup the error dialog
1333 this->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1335 this->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"),
1343 SendMsgDialog::createWorkArea ( Widget parent )
1345 FORCE_SEGV_DECL(CmdInterface, ci);
1348 // Create the parent form
1350 _main_form = XmCreateForm( parent, "Work_Area", NULL, 0 );
1351 XtVaSetValues(_main_form, XmNresizePolicy, XmRESIZE_NONE, NULL);
1353 printHelpId("form", _main_form);
1354 /* add help callback */
1355 XtAddCallback(_main_form, XmNhelpCallback, HelpCB, DTMAILCOMPOSEWINDOW);
1356 XtVaSetValues(_main_form, XmNallowResize, True, NULL);
1359 // Create the area for status messages.
1361 _status_form = XtVaCreateManagedWidget("StatusForm",
1362 xmFormWidgetClass, _main_form,
1363 XmNtopAttachment, XmATTACH_FORM,
1364 XmNrightAttachment, XmATTACH_FORM,
1366 XmNleftAttachment, XmATTACH_FORM,
1370 _status_text = XtVaCreateManagedWidget("StatusLabel",
1371 xmLabelWidgetClass, _status_form,
1372 XmNtopAttachment, XmATTACH_FORM,
1373 XmNbottomAttachment, XmATTACH_FORM,
1374 XmNrightAttachment, XmATTACH_FORM,
1375 XmNleftAttachment, XmATTACH_FORM,
1376 XmNalignment, XmALIGNMENT_BEGINNING,
1381 Widget s_sep = XtVaCreateManagedWidget("StatusSep",
1382 xmSeparatorGadgetClass,
1384 XmNtopAttachment, XmATTACH_WIDGET,
1385 XmNtopWidget, _status_form,
1386 XmNleftAttachment, XmATTACH_FORM,
1387 XmNrightAttachment, XmATTACH_FORM,
1390 _header_form = XtVaCreateManagedWidget("HeaderArea",
1391 xmFormWidgetClass, _main_form,
1392 XmNtopAttachment, XmATTACH_WIDGET,
1393 XmNtopWidget, s_sep,
1394 XmNleftAttachment, XmATTACH_FORM,
1395 XmNrightAttachment, XmATTACH_FORM,
1397 printHelpId("header_form", _header_form);
1399 createHeaders(_header_form);
1401 Widget sep1 = XtVaCreateManagedWidget("Sep1",
1402 xmSeparatorGadgetClass,
1404 XmNtopAttachment, XmATTACH_WIDGET,
1405 XmNtopWidget, _header_form,
1407 XmNrightAttachment, XmATTACH_FORM,
1408 XmNleftAttachment, XmATTACH_FORM,
1411 // Create the editor and attach it to the header_form
1413 _my_editor = new DtMailEditor(_main_form, this);
1415 _my_editor->initialize();
1416 _my_editor->attachArea()->setOwnerShell(this);
1417 _my_editor->setEditable(TRUE);
1418 _my_editor->manageAttachArea();
1420 // Create a RowCol widget that contains buttons
1422 send_form = XtCreateManagedWidget("SendForm",
1423 xmFormWidgetClass, _main_form, NULL, 0);
1426 // Create the Send and Close buttons as children of rowCol
1428 _send_button = new SendCmd ( "Send",
1429 GETMSG(DT_catd, 1, 230, "Send"),
1433 ci = new ButtonInterface (send_form, _send_button);
1435 XtVaSetValues(ci->baseWidget(),
1436 XmNleftAttachment, XmATTACH_FORM,
1437 XmNleftOffset, OFFSET,
1438 XmNbottomAttachment, XmATTACH_FORM,
1442 Widget send_bw = ci->baseWidget();
1443 XtManageChild(send_bw);
1446 _close_button = new CloseCmd (
1448 GETMSG(DT_catd, 1, 118, "Close"),
1452 ci = new ButtonInterface (send_form, _close_button);
1453 XtVaSetValues(ci->baseWidget(),
1455 XmNleftAttachment, XmATTACH_WIDGET,
1456 XmNleftWidget, send_bw,
1457 XmNbottomAttachment, XmATTACH_FORM,
1461 XtManageChild(ci->baseWidget());
1464 // Now attach the editor to the form and to the rowCol
1465 // And the rowCol to the bottom of the form.
1466 // We need this attachment ordering so that resizes always
1467 // get transferred to the editor.
1469 Widget wid = _my_editor->container();
1471 XmNleftAttachment, XmATTACH_FORM,
1472 XmNrightAttachment, XmATTACH_FORM,
1473 XmNtopAttachment, XmATTACH_WIDGET,
1476 XmNbottomAttachment, XmATTACH_WIDGET,
1477 XmNbottomWidget, send_form,
1480 XtVaSetValues(send_form,
1481 XmNbottomAttachment, XmATTACH_FORM,
1486 HeaderList * hl = _header_list[0];
1487 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
1489 // Set the title to be New Message
1490 //char *ttl = GETMSG(DT_catd, 1, 119, "New Message");
1491 //this->setTitle(ttl);
1492 //this->setIconTitle(ttl);
1494 XtManageChild(_main_form);
1500 SendMsgDialog::createHeaders(Widget header_form)
1502 Widget previous_form = NULL;
1503 char *field_name = new char[50];
1505 for (int header = 0; header < _header_list.length(); header++) {
1506 HeaderList * hl = _header_list[header];
1508 // We use SMD_NEVER to indicate the header has disappeared from
1511 if (hl->show == SMD_NEVER) {
1515 // If the widgets already exist, then simply manage them.
1516 if (hl->form_widget) {
1517 previous_form = hl->form_widget;
1519 XtVaSetValues(hl->field_widget,
1520 XmNvalue, hl->value,
1523 XtVaSetValues(hl->field_widget,
1529 if (previous_form == NULL) {
1530 // Create a form, attaching it to the top. This is a special
1531 // case. Other lines are created attached to the form above
1533 strcpy(field_name, "form_");
1534 strncat(field_name, hl->label, 45);
1535 field_name[strlen(hl->label) + 5] = 0;
1537 XtVaCreateWidget(field_name,
1540 XmNtopAttachment, XmATTACH_FORM,
1542 XmNleftAttachment, XmATTACH_FORM,
1544 XmNrightAttachment, XmATTACH_FORM,
1550 strcpy(field_name, "form_");
1551 strncat(field_name, hl->label, 45);
1552 field_name[strlen(hl->label) + 5] = 0;
1554 XtVaCreateWidget(field_name,
1557 XmNtopAttachment, XmATTACH_WIDGET,
1558 XmNtopWidget, previous_form,
1559 XmNleftAttachment, XmATTACH_FORM,
1561 XmNrightAttachment, XmATTACH_FORM,
1567 // The label will be to the left of the form.
1569 strcpy(field_name, hl->label);
1570 strcat(field_name, ":");
1571 XmString label = XmStringCreateLocalized(field_name);
1573 XtVaCreateManagedWidget(hl->label,
1576 XmNtopAttachment, XmATTACH_FORM,
1577 XmNbottomAttachment, XmATTACH_FORM,
1578 XmNleftAttachment, XmATTACH_FORM,
1579 XmNlabelString, label,
1581 XmStringFree(label);
1583 strcpy(field_name, "field_");
1584 strncat(field_name, hl->label, 43);
1585 field_name[strlen(hl->label) + 6] = 0;
1588 XtVaCreateManagedWidget(field_name,
1589 xmTextFieldWidgetClass,
1591 XmNtraversalOn, True,
1592 XmNtopAttachment, XmATTACH_FORM,
1593 XmNrightAttachment, XmATTACH_FORM,
1594 XmNleftAttachment, XmATTACH_WIDGET,
1595 XmNleftWidget, hl->label_widget,
1598 if (hl->show != SMD_HIDDEN) {
1599 XtManageChild(hl->form_widget);
1602 XtVaSetValues(hl->form_widget,
1603 XmNtopAttachment, XmATTACH_NONE,
1607 XtAddCallback(hl->field_widget,
1608 XmNactivateCallback,
1609 header_form_traverse,
1612 XtAddCallback(hl->field_widget,
1613 XmNvalueChangedCallback,
1618 XtVaSetValues(hl->field_widget,
1619 XmNvalue, hl->value,
1623 previous_form = hl->form_widget;
1627 delete [] field_name;
1631 SendMsgDialog::doDynamicHeaderMenus(void)
1633 // This is really a pain, but we have to blow away the list to
1634 // build another one. This could probably be done more efficiently,
1635 // but we wont try to figure out how right now.
1638 _menuBar->removeOnlyCommands(_format_menu, _format_cmds);
1641 _format_cmds = new CmdList("DynamicFormatCommands", "DynamicFormatCommands");
1643 // Only put on commands that are shown or hidden. The items that
1644 // are always are never should not be presented to the user as
1645 // an option to change.
1647 char *label = new char[100];
1649 for (int h = 0; h < _header_list.length(); h++) {
1650 HeaderList * hl = _header_list[h];
1654 sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
1658 sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
1665 strcat(label, hl->label);
1668 char * priv_label = strdup(label);
1670 Cmd * new_cmd = new HideShowCmd(priv_label, priv_label,
1671 1, this, hl->label);
1673 // Add the commands one at a time with addCommand() vs. all
1674 // at once with addCommands(). That way new commands will
1675 // be created instead of reusing old ones.
1676 _menuBar->addCommand(_format_menu, new_cmd);
1677 _format_cmds->add(new_cmd);
1685 // Should theInfoDialogManager be destroyed here ???
1690 SendMsgDialog::open_att_cb( void *clientData, char *selection )
1692 SendMsgDialog *obj = (SendMsgDialog *)clientData;
1694 obj->open_att(selection);
1698 SendMsgDialog::open_att( char *) // arg is char *selection
1701 #endif /* DEAD_WOOD */
1704 SendMsgDialog::include_file_cb( void *client_data, char *selection )
1706 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1707 obj->include_file(selection);
1708 if (NULL != selection)
1714 SendMsgDialog::include_file(
1719 char *buf = new char[MAXPATHLEN];
1721 // I don't need to open the file to see if it's readable if loadFile()
1722 // returns error status.
1723 if ( (fp = fopen(selection, "r")) == NULL ) {
1724 sprintf(buf, GETMSG(DT_catd, 2, 18, "Error: Cannot include file %s"),
1726 theInfoDialogManager->post(
1729 (void *)this->_file_include,
1733 this->_my_editor->textEditor()->append_to_contents("\n", 2);
1734 this->_my_editor->textEditor()->append_at_cursor(selection);
1735 this->_my_editor->textEditor()->append_to_contents("\n", 2);
1741 SendMsgDialog::get_confirm_attachment_threshold()
1744 DtMail::Session *m_session = theRoamApp.session()->session();
1745 const char *value = NULL;
1748 m_session->mailRc(error)->getValue(error, "confirmattachments", &value);
1749 if (error.isSet()) return 0;
1751 m_session->mailRc(error)->getValue(error, "confirmattachmentthreshold",
1753 if (error.isNotSet() && NULL!=value)
1754 threshold = 1024 * atoi(value);
1756 threshold = 1024 * 64;
1762 SendMsgDialog::confirm_add_attachment(char *file, int size)
1764 char *buf = new char[BUFSIZ];
1769 GETMSG(DT_catd, 1, 263,
1770 "The attachment '%s' is %d kilobytes.\nAdd as attachment?");
1771 sprintf(buf, format, file, size/1024);
1772 _genDialog->setToQuestionDialog(GETMSG(DT_catd, 5, 2, "Mailer"), buf);
1773 answer = _genDialog->post_and_return(NULL);
1778 SendMsgDialog::add_att_cb( void *client_data, char *selection )
1780 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1781 obj->add_att(selection);
1782 if (NULL != selection)
1787 SendMsgDialog::add_att(char *file)
1789 struct stat statbuf;
1791 if (-1 != stat(file, &statbuf) && _confirm_attachment_threshold &&
1792 _confirm_attachment_threshold < statbuf.st_size)
1794 if (! confirm_add_attachment(file, statbuf.st_size)) return;
1797 // Activate Attachment menu???
1798 this->get_editor()->attachArea()->
1799 addAttachment(_msgHandle, _lastAttBP, file, NULL);
1800 this->setLastAttBP();
1801 this->activate_default_attach_menu();
1803 // This will manage the attach pane too.
1804 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1808 SendMsgDialog::add_att(char *name, DtMailBuffer buf)
1810 if (_confirm_attachment_threshold &&
1811 _confirm_attachment_threshold < buf.size)
1813 if (! confirm_add_attachment("", buf.size)) return;
1816 this->get_editor()->attachArea()->
1817 addAttachment(_msgHandle, _lastAttBP, name, buf);
1818 this->setLastAttBP();
1819 this->activate_default_attach_menu();
1821 // This will manage the attach pane too.
1822 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1826 SendMsgDialog::add_att(DtMailBuffer buf)
1833 SendMsgDialog::save_att_cb( void *client_data, char *selection )
1835 SendMsgDialog *obj = (SendMsgDialog *)client_data;
1837 obj->save_selected_attachment(selection);
1842 SendMsgDialog::save_selected_attachment(
1846 DtMailEnv mail_error;
1850 AttachArea *attarea = this->get_editor()->attachArea();
1851 Attachment *attachment = attarea->getSelectedAttachment();
1853 // Get selected attachment, if none selected, then return.
1854 if ( attachment == NULL ) {
1855 // Let User know that no attachment has been selected???
1857 char *helpId = NULL;
1860 _genDialog->setToErrorDialog(
1861 GETMSG(DT_catd, 1, 120, "Mailer"),
1862 GETMSG(DT_catd, 2, 19, "An attachment needs to be selected before issuing the\n\"Save As\" command to save to a file.") );
1863 helpId = DTMAILHELPSELECTATTACH;
1864 answer = _genDialog->post_and_return(
1865 GETMSG(DT_catd, 3, 74, "OK"), helpId );
1869 // Save selected attachments.
1870 attachment->saveToFile(mail_error, selection);
1871 if ( mail_error.isSet() ) {
1872 // Let User know error condition???
1878 SendMsgDialog::propsChanged(void)
1880 DtMail::Session *m_session = theRoamApp.session()->session();
1881 const char * value = NULL;
1884 enableWorkAreaResize();
1886 m_session->mailRc(error)->getValue(error, "hideattachments", &value);
1888 if (_show_attach_area) {
1889 _show_attach_area = FALSE;
1890 this->hideAttachArea();
1894 if (!_show_attach_area) {
1895 _show_attach_area = TRUE;
1896 this->showAttachArea();
1899 free((void*) value);
1901 _confirm_attachment_threshold = get_confirm_attachment_threshold();
1904 const char * logfile = NULL;
1905 m_session->mailRc(error)->getValue(error, "record", &logfile);
1906 if (logfile == NULL)
1907 _file_log->deactivate();
1909 _file_log->activate();
1911 _my_editor->textEditor()->update_display_from_props();
1913 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
1914 if (logfile == NULL || error.isNotSet()) {
1915 // logfile is not specified or "dontlogmessages" is TRUE
1916 setLogState(DTM_FALSE);
1917 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
1919 // logfile is specified and "dontlogmessages" is FALSE
1920 setLogState(DTM_TRUE);
1921 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
1924 if (NULL != logfile)
1925 free((void*) logfile);
1927 free((void*) value);
1930 m_session->mailRc(error)->getValue(error, "templates", &value);
1931 if ( (value == NULL && _templateList != NULL) ||
1933 (_templateList == NULL || strcmp(value, _templateList)) != 0) ) {
1934 // Template list has changed
1935 if (_templateList != NULL)
1936 free (_templateList);
1937 if (value != NULL && *value != '\0')
1938 _templateList = strdup(value);
1940 _templateList = NULL;
1944 free((void*) value);
1946 // Alias Popup Menus
1947 DtVirtArray<PropStringPair*> *newAliases;
1948 Boolean aliasesChanged = FALSE;
1950 newAliases = new DtVirtArray<PropStringPair*> (10);
1951 createAliasList(newAliases);
1952 if (newAliases->length() == _aliasList->length())
1954 int length = newAliases->length();
1957 while (i<length && aliasesChanged==FALSE)
1959 PropStringPair *p1 = (*newAliases)[i];
1960 PropStringPair *p2 = (*_aliasList)[i];
1962 if ( strncmp(p1->label, p2->label, strlen(p2->label)) ||
1963 strncmp(p1->value, p2->value, strlen(p2->value)) )
1964 aliasesChanged = TRUE;
1970 aliasesChanged = TRUE;
1972 if (aliasesChanged == TRUE)
1974 destroyAliasPopupMenus();
1975 destroyAliasList(_aliasList);
1976 _aliasList = newAliases;
1977 createAliasPopupMenus();
1980 destroyAliasList(newAliases);
1982 disableWorkAreaResize();
1986 SendMsgDialog::createMenuPanes()
1991 const char * value = NULL;
1994 _separator = new SeparatorCmd( "Separator","Separator", TRUE );
1997 cmdList = new CmdList( "File", GETMSG(DT_catd, 1, 121, "File") );
1999 // Default directory is set below at the same time as the default
2000 // directory for att_add.
2001 _file_include = new UnifiedSelectFileCmd (
2003 GETMSG(DT_catd, 1, 122, "Include..."),
2004 GETMSG(DT_catd, 1, 123, "Mailer - Include"),
2005 GETMSG(DT_catd, 1, 124, "Include"),
2007 SendMsgDialog::include_file_cb,
2009 this->baseWidget());
2012 // Remap OK button to Include
2013 // XtVaSetValues(_file_include->fileBrowser,
2014 // XmNokLabelString, GETMSG(DT_catd,
2015 // 1, 77, "Include"), NULL);
2016 _file_save_as = new SaveAsTextCmd(
2018 GETMSG(DT_catd, 1, 125, "Save As Text..."),
2019 GETMSG(DT_catd, 1, 126, "Mailer - Save As Text"),
2021 get_editor()->textEditor(),
2025 _file_log = new LogMsgCmd (
2027 GETMSG(DT_catd, 1, 127, "Log Message"), TRUE, this);
2029 // 1 for default transport.
2031 _file_send = new SendCmd (
2033 GETMSG(DT_catd, 1, 117, "Send"),
2038 // Find out how many transports there are and build sub menu dynamically.
2039 DtMail::Session *d_session;
2041 if ( theRoamApp.session() == NULL ) {
2042 MailSession *new_session = new MailSession(
2044 theApplication->appContext());
2045 theRoamApp.setSession(new_session);
2048 CmdList *subcmdList1 = new CmdList (
2050 GETMSG(DT_catd, 1, 128, "Send As") );
2052 d_session = theRoamApp.session()->session();
2053 const char **impls = d_session->enumerateImpls(error);
2055 for ( int impl = 0; impls[impl]; impl++ ) {
2056 DtMailBoolean trans;
2057 d_session->queryImpl(error, impls[impl],
2058 DtMailCapabilityTransport, &trans);
2059 if (!error.isSet() && trans == DTM_TRUE ) {
2060 _file_sendAs[_num_sendAs] = new SendCmd( strdup(impls[impl]),
2061 (char *)impls[impl],
2065 subcmdList1->add( _file_sendAs[_num_sendAs] );
2068 // Assume an error means this query failed. But keep going and
2069 // get the next transport.
2072 _file_close = new CloseCmd (
2074 GETMSG(DT_catd, 1, 129, "Close"),
2076 _menuBar->baseWidget(),
2079 // Now build the menu
2081 cmdList->add( _file_include );
2082 cmdList->add( _file_save_as );
2083 cmdList->add( _file_log );
2084 cmdList->add( _separator );
2086 cmdList->add( _file_send );
2087 #if defined(USE_SEND_AS_MENU)
2088 cmdList->add( subcmdList1 );
2090 cmdList->add( _separator );
2092 cmdList->add( _file_close );
2094 _menuBar->addCommands ( cmdList );
2100 cmdList = new CmdList( "Edit", GETMSG(DT_catd, 1, 130, "Edit") );
2102 _edit_undo = new EditUndoCmd ( "Undo",
2103 GETMSG(DT_catd, 1, 131, "Undo"),
2105 _edit_cut = new EditCutCmd ( "Cut",
2106 GETMSG(DT_catd, 1, 132, "Cut"),
2108 _edit_copy = new EditCopyCmd ( "Copy",
2109 GETMSG(DT_catd, 1, 133, "Copy"),
2111 _edit_paste = new EditPasteCmd ( "Paste",
2112 GETMSG(DT_catd, 1, 134 , "Paste"),
2116 // Begin Paste Special submenu
2117 subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2118 _edit_paste_special[0] = new EditPasteSpecialCmd (
2120 GETMSG(DT_catd, 1, 136 , "Bracketed"),
2121 TRUE, this, Editor::IF_BRACKETED
2123 subcmdList1->add(_edit_paste_special[0]);
2124 _edit_paste_special[1] = new EditPasteSpecialCmd (
2126 GETMSG(DT_catd, 1, 137 , "Indented"),
2127 TRUE, this, Editor::IF_INDENTED );
2128 subcmdList1->add(_edit_paste_special[1]);
2129 // End Paste Special submenu
2131 _edit_clear = new EditClearCmd ( "Clear", GETMSG(DT_catd, 1, 138, "Clear"),
2134 _edit_delete = new EditDeleteCmd ( "Delete", GETMSG(DT_catd, 1, 139, "Delete"),
2137 _edit_select_all = new EditSelectAllCmd (
2139 GETMSG(DT_catd, 1, 140, "Select All"),
2142 _format_find_change = new FindChangeCmd (
2144 GETMSG(DT_catd, 1, 155, "Find/Change..."),
2147 _format_spell = new SpellCmd (
2148 "Check Spelling...",
2149 GETMSG(DT_catd, 1, 156, "Check Spelling..."),
2153 cmdList->add( _edit_undo );
2154 cmdList->add( _separator );
2155 cmdList->add( _edit_cut );
2156 cmdList->add( _edit_copy );
2157 cmdList->add( _edit_paste );
2158 cmdList->add( subcmdList1 ); // Add Paste Special submenu
2159 cmdList->add( _separator );
2160 cmdList->add( _edit_clear );
2161 cmdList->add( _edit_delete );
2162 cmdList->add( _separator );
2163 cmdList->add( _edit_select_all );
2164 cmdList->add( _separator );
2165 cmdList->add( _format_find_change );
2168 * SpellCheck is not supported by Base System for the multibyte language
2169 * currently. ( See dtpad's source ) So that this should be disabled.
2170 * See Defect 174873. (I should think this solution is not good one, but..)
2171 * What is the best way to check if I'm in MB or SB.....???
2174 if ( MB_CUR_MAX == 1 )
2175 cmdList->add( _format_spell );
2177 _menuBar->addCommands ( cmdList );
2181 // Alias Popup Menus
2182 if (NULL != _aliasList) delete _aliasList;
2183 _aliasList = new DtVirtArray<PropStringPair*> (10);
2184 createAliasList(_aliasList);
2185 createAliasPopupMenus();
2187 // Compose Popup CmdList
2188 construct_text_popup();
2192 cmdList = new CmdList(
2194 GETMSG(DT_catd, 1, 141, "Attachments"));
2196 _att_add = new UnifiedSelectFileCmd (
2198 GETMSG(DT_catd, 1, 142, "Add File..."),
2199 GETMSG(DT_catd, 1, 143, "Mailer - Add"),
2200 GETMSG(DT_catd, 1, 144, "Add"),
2202 SendMsgDialog::add_att_cb,
2204 this->baseWidget());
2206 _att_save = new SaveAttachCmd (
2208 GETMSG(DT_catd, 1, 145, "Save As..."),
2209 GETMSG(DT_catd, 1, 146,
2210 "Mailer - Attachments - Save As"),
2212 SendMsgDialog::save_att_cb,
2214 this->baseWidget());
2215 _att_delete = new DeleteAttachCmd (
2217 GETMSG(DT_catd, 1, 147, "Delete"),
2220 _att_undelete = new UndeleteAttachCmd (
2222 GETMSG(DT_catd, 1, 148, "Undelete"),
2225 _att_rename = new RenameAttachCmd(
2227 GETMSG(DT_catd, 1, 149, "Rename"),
2231 _att_select_all = new SelectAllAttachsCmd(
2233 GETMSG(DT_catd, 1, 150, "Select All"),
2237 * This is the label for a toggle item in a menu. When the item
2238 * is set to "Show List", the Attachment List is mapped in the
2239 * Compose Window. This message replaces message 151 in set 1.
2241 _att_show_pane = new ShowAttachPaneCmd(
2243 GETMSG(DT_catd, 1, 226, "Show List"),
2246 cmdList->add( _att_add );
2247 cmdList->add( _att_save );
2248 cmdList->add( _separator );
2250 // subcmdList1 = new CmdList ( "Create", "Create" );
2251 // // subcmdList1->add( att_audio );
2252 // // subcmdList1->add( att_appt );
2253 // cmdList->add( subcmdList1 );
2254 // cmdList->add( _separator );
2256 cmdList->add( _att_delete );
2257 cmdList->add( _att_undelete );
2258 cmdList->add( _att_rename );
2259 cmdList->add( _att_select_all );
2260 cmdList->add(_att_show_pane);
2262 // Create a pulldown from the items in the list. Retain a handle
2263 // to that pulldown since we need to dynamically add/delete entries
2264 // to this menu based on the selection of attachments.
2266 _attachmentMenu = _menuBar->addCommands ( cmdList );
2267 construct_attachment_popup();
2269 // delete subcmdList1;
2273 d_session->mailRc(error)->getValue(error, "templates", &value);
2274 if (value != NULL && *value != '\0')
2275 _templateList = strdup(value);
2277 free((void*) value);
2281 _overview = new OnAppCmd("Overview",
2282 GETMSG(DT_catd, 1, 71, "Overview"),
2284 _tasks = new TasksCmd("Tasks", GETMSG(DT_catd, 1, 72, "Tasks"),
2286 _reference = new ReferenceCmd("Reference",
2287 GETMSG(DT_catd, 1, 73, "Reference"),
2289 _on_item = new OnItemCmd("On Item", GETMSG(DT_catd, 1, 74, "On Item"),
2291 _using_help = new UsingHelpCmd("Using Help",
2292 GETMSG(DT_catd, 1, 75, "Using Help"),
2294 _about_mailer = new RelNoteCmd("About Mailer...",
2295 GETMSG(DT_catd, 1, 77, "About Mailer..."),
2297 cmdList = new CmdList("Help", GETMSG(DT_catd, 1, 76, "Help"));
2298 cmdList->add(_overview);
2299 cmdList->add(_separator);
2300 cmdList->add(_tasks);
2301 cmdList->add(_reference);
2302 cmdList->add(_separator);
2303 cmdList->add(_on_item);
2304 cmdList->add(_separator);
2305 cmdList->add(_using_help);
2306 cmdList->add(_separator);
2307 cmdList->add(_about_mailer);
2308 _menuBar->addCommands(cmdList, TRUE);
2313 SendMsgDialog::construct_attachment_popup(void)
2315 _attachmentPopupMenuList = new CmdList( "AttachmentsPopup", "AttachmentsPopup");
2317 LabelCmd *title = new LabelCmd (
2318 "Mailer - Attachments",
2319 GETMSG(DT_catd, 1, 158, "Mailer - Attachments"), TRUE);
2320 SeparatorCmd *separator = new SeparatorCmd( "Separator","Separator", TRUE );
2322 _attachmentPopupMenuList->add(title);
2323 _attachmentPopupMenuList->add(separator);
2324 _attachmentPopupMenuList->add( _att_add );
2325 _attachmentPopupMenuList->add( _att_save );
2326 _attachmentPopupMenuList->add( _att_delete );
2327 _attachmentPopupMenuList->add( _att_undelete );
2328 _attachmentPopupMenuList->add( _att_select_all );
2330 _menuPopupAtt = new MenuBar(_my_editor->attachArea()->getClipWindow(),
2331 "RoamAttachmentPopup", XmMENU_POPUP);
2332 _attachmentPopupMenu = _menuPopupAtt->addCommands(_attachmentPopupMenuList,
2333 FALSE, XmMENU_POPUP);
2337 SendMsgDialog::construct_text_popup(void)
2339 if (theApplication->bMenuButton() != Button3)
2342 _textPopupMenuList = new CmdList( "TextPopup", "TextPopup");
2344 LabelCmd *title = new LabelCmd (
2346 GETMSG(DT_catd, 1, 159, "Mailer - Compose"), TRUE);
2347 SeparatorCmd *separator = new SeparatorCmd("Separator", "Separator", TRUE );
2349 _textPopupMenuList->add(title);
2350 _textPopupMenuList->add(separator);
2351 _textPopupMenuList->add(_file_send);
2352 _textPopupMenuList->add( _edit_undo );
2353 _textPopupMenuList->add( _edit_cut );
2354 _textPopupMenuList->add( _edit_copy );
2355 _textPopupMenuList->add( _edit_paste );
2357 // Work in progress from Mike. This adds the Paste Special to the
2358 // third mouse button in the compose area of a compose window.
2359 // Begin Paste Special submenu
2360 CmdList * subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2361 subcmdList1->add(_edit_paste_special[0]);
2362 subcmdList1->add(_edit_paste_special[1]);
2363 // End Paste Special submenu
2364 _textPopupMenuList->add( subcmdList1 ); // Add Paste Special submenu
2365 // (Either way) _textPopupMenuList->add( separator );
2366 _textPopupMenuList->add( _edit_clear );
2368 _textPopupMenuList->add( _edit_delete );
2369 _textPopupMenuList->add( _edit_select_all );
2371 Widget parent = _my_editor->textEditor()->get_editor();
2372 _menuPopupText = new MenuBar(parent, "SendMsgTextPopup", XmMENU_POPUP);
2373 _textPopupMenu = _menuPopupText->addCommands(_textPopupMenuList,
2374 FALSE, XmMENU_POPUP);
2377 static int cmp_prop_pair(const void *v1, const void *v2)
2379 PropStringPair *p1 = *((PropStringPair **) v1);
2380 PropStringPair *p2 = *((PropStringPair **) v2);
2383 ret = strcmp((const char *) p1->label, (const char *) p2->label);
2387 static void alias_stuffing_func(char * key, void * data, void * client_data)
2389 DtVirtArray<PropStringPair *> *alias_list;
2390 PropStringPair *new_pair;
2392 alias_list = (DtVirtArray<PropStringPair*> *) client_data;
2393 new_pair = new PropStringPair;
2394 new_pair->label = strdup(key);
2395 new_pair->value = strdup((char *)data);
2396 alias_list->append(new_pair);
2400 SendMsgDialog::createAliasList(DtVirtArray<PropStringPair*> *aliases)
2403 DtMail::Session *d_session = theRoamApp.session()->session();
2404 DtMail::MailRc *mail_rc = d_session->mailRc(error);
2407 mail_rc->getAliasList(alias_stuffing_func, aliases);
2409 if (nalias = aliases->length())
2411 PropStringPair **prop_pairs = NULL;
2413 prop_pairs = (PropStringPair**) malloc(nalias*sizeof(PropStringPair*));
2415 for (i=0; i<nalias; i++)
2417 prop_pairs[i] = (*aliases)[0];
2420 qsort(prop_pairs, nalias, sizeof(PropStringPair*), cmp_prop_pair);
2421 for (i=0; i<nalias; i++)
2422 aliases->append(prop_pairs[i]);
2424 free((void*) prop_pairs);
2429 SendMsgDialog::destroyAliasList(DtVirtArray<PropStringPair*> *aliases)
2431 while (aliases->length() >= 0)
2433 PropStringPair *prop_pair = (*aliases)[0];
2439 // map_menu is used to figure out how many columns to split the menu
2440 // into. It is a callback that is called when the menu is mapped.
2441 // If the menu is over half the height of the screen, it figures out
2442 // how many columns to make the menu, and sets its XmNnumColumns
2443 // attribute to that value. It calculates the maximum number of columns
2444 // that would fit and never goes beyond that number.
2446 static void map_alias_menu(Widget menu, XtPointer, XtPointer)
2450 Dimension maxcols, newcols, columns;
2451 Dimension screenheight = (Dimension) HeightOfScreen(XtScreen(menu));
2452 Dimension fudgefact = 20; /* to allow for decorations on menu */
2459 XmNnumColumns, &columns,
2462 if ((h + fudgefact) > (screenheight / 2))
2464 // The menu is taller than half the screen.
2465 // We need to find out how many more columns
2466 // to specify for the menu to make it fit.
2468 newcols = (columns * ((h+fudgefact)/(screenheight/2))) + 1;
2469 maxcols = WidthOfScreen(XtScreen(menu))/(w/columns);
2471 if (newcols > maxcols)
2474 XtVaSetValues(menu, XmNnumColumns, newcols, NULL);
2479 SendMsgDialog::aliasMenuButtonHandler(
2481 XtPointer client_data,
2485 Widget menu = (Widget) client_data;
2486 XButtonEvent *be = (XButtonEvent *) event;
2488 if (event->xany.type != ButtonPress) return;
2489 if(be->button == theApplication->bMenuButton())
2491 XmMenuPosition(menu, (XButtonEvent *)event);
2492 XtManageChild(menu);
2497 SendMsgDialog::createAliasPopupMenu(
2501 DtVirtArray<PropStringPair*> *aliases)
2504 OtherAliasesCmd *otherAliases =
2505 new OtherAliasesCmd(
2507 GETMSG(DT_catd, 1, 247, "Other Aliases..."),
2509 #if defined(USE_TITLED_ALIAS_POPUPS)
2513 GETMSG(DT_catd, 1, 248, "Mailer - Aliases"),
2516 SeparatorCmd *separator =
2517 new SeparatorCmd("Separator","Separator", TRUE);
2519 (*cmdlist) = new CmdList("AliasCommands", "AliasCommands");
2520 #if defined(USE_TITLED_ALIAS_POPUPS)
2521 (*cmdlist)->add(title);
2522 (*cmdlist)->add(separator);
2524 for (int i=0, length=aliases->length(); i<length; i++)
2526 PropStringPair *prop_pair = (*aliases)[i];
2528 AliasCmd *alias = new AliasCmd(
2529 strdup(prop_pair->label),
2530 strdup(prop_pair->label),
2533 (*cmdlist)->add(alias);
2535 if (0 < aliases->length())
2536 (*cmdlist)->add(separator);
2537 (*cmdlist)->add(otherAliases);
2539 *menubar = new MenuBar(parent, "AliasesPopup", XmMENU_POPUP);
2540 menu = (*menubar)->addCommands((*cmdlist), FALSE, XmMENU_POPUP);
2546 aliasMenuButtonHandler,
2551 XmNpacking, XmPACK_COLUMN,
2552 XmNorientation, XmVERTICAL,
2557 XmNmapCallback, &map_alias_menu,
2564 SendMsgDialog::destroyAliasPopupMenu(
2570 XtRemoveEventHandler(
2574 aliasMenuButtonHandler,
2579 XmNmapCallback, &map_alias_menu,
2582 XtDestroyWidget(menu);
2589 SendMsgDialog::getHeaderWidget(const char *hdrname)
2591 for (int i=0, length=_header_list.length(); i<length; i++)
2593 HeaderList *hdritem = _header_list[i];
2595 if (0 == strncmp(hdrname, hdritem->header, strlen(hdrname)))
2596 return hdritem->field_widget;
2603 SendMsgDialog::createAliasPopupMenus(void)
2607 w = getHeaderWidget(DtMailMessageTo);
2609 _toPopupMenu = createAliasPopupMenu(
2615 w = getHeaderWidget(DtMailMessageCc);
2617 _ccPopupMenu = createAliasPopupMenu(
2623 w = getHeaderWidget(DtMailMessageBcc);
2625 _bccPopupMenu = createAliasPopupMenu(
2633 SendMsgDialog::destroyAliasPopupMenus(void)
2637 w = getHeaderWidget(DtMailMessageTo);
2638 destroyAliasPopupMenu(w, _toPopupMenuBar, _toPopupCmdlist, _toPopupMenu);
2639 _toPopupMenuBar = NULL;
2640 _toPopupCmdlist = NULL;
2641 _toPopupMenu = NULL;
2643 w = getHeaderWidget(DtMailMessageCc);
2644 destroyAliasPopupMenu(w, _ccPopupMenuBar, _ccPopupCmdlist, _ccPopupMenu);
2645 _ccPopupMenuBar = NULL;
2646 _ccPopupCmdlist = NULL;
2647 _ccPopupMenu = NULL;
2649 w = getHeaderWidget(DtMailMessageBcc);
2650 destroyAliasPopupMenu(w, _bccPopupMenuBar, _bccPopupCmdlist, _bccPopupMenu);
2651 _bccPopupMenuBar = NULL;
2652 _bccPopupCmdlist = NULL;
2653 _bccPopupMenu = NULL;
2657 SendMsgDialog::createFormatMenu()
2660 _format_separator = new SeparatorCmd( "Separator","Separator", TRUE );
2662 cmdList = new CmdList( "Format", GETMSG(DT_catd, 1, 152,"Format") );
2664 _format_word_wrap = new WordWrapCmd (
2666 GETMSG(DT_catd, 1, 153, "Word Wrap"),
2669 _format_settings = new FormatCmd ( "Settings...",
2670 GETMSG(DT_catd, 1, 154, "Settings..."),
2674 cmdList->add( _format_word_wrap );
2675 cmdList->add( _format_settings );
2676 cmdList->add( _format_separator);
2678 _templates = new CmdList ( "Templates", GETMSG(DT_catd, 1, 157, "Templates") );
2679 addTemplates(_templates);
2681 cmdList->add(_templates);
2683 cmdList->add( _format_separator );
2685 _format_menu = _menuBar->addCommands ( &_format_cascade, cmdList,
2690 doDynamicHeaderMenus();
2692 if (_template_count == 0 && _templates->getPaneWidget())
2694 XtSetSensitive(_templates->getPaneWidget(), FALSE);
2700 SendMsgDialog::addTemplates(CmdList * subCmd)
2704 _template_count = 0;
2706 if (_templateList == NULL)
2709 DtMail::Session *m_session = theRoamApp.session()->session();
2710 char * expanded_list = m_session->expandPath(error, _templateList);
2712 DtVirtArray<PropStringPair *> templates(8);
2713 parsePropString(expanded_list, templates);
2714 free(expanded_list);
2716 _template_count = templates.length();
2718 for (int tmp = 0; tmp < _template_count; tmp++) {
2719 PropStringPair * psp = templates[tmp];
2720 if (psp->label && psp->value) {
2721 Cmd * button = new TemplateCmd(strdup(psp->label),
2726 subCmd->add(button);
2730 while (templates.length()) {
2731 PropStringPair * psp = templates[0];
2733 templates.remove(0);
2738 SendMsgDialog::initialize()
2742 const char * hideAttachPane = NULL;
2745 // Without the TearOffModelConverter call, there will be warning messages:
2746 // Warning: No type converter registered for 'String' to 'TearOffModel'
2749 XmRepTypeInstallTearOffModelConverter();
2750 MenuWindow::initialize();
2752 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
2755 XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
2756 XtSetValues( _w, args, n);
2758 _genDialog = new DtMailGenDialog("Dialog", _main);
2760 // See if the .mailrc specifies if attachPane is to be shown or hid
2761 // at SMD startup time.
2763 DtMail::Session *m_session = theRoamApp.session()->session();
2764 m_session->mailRc(error)->getValue(error, "hideattachments",
2767 if (!hideAttachPane) {
2768 _show_attach_area = TRUE;
2771 _show_attach_area = FALSE;
2772 // The user wants to hide attachments
2774 this->hideAttachArea();
2776 if (NULL != hideAttachPane)
2777 free((void*) hideAttachPane);
2779 _confirm_attachment_threshold = get_confirm_attachment_threshold();
2781 // Log Message Toggle button. A LogMsgCmd is a ToggleButtonCmd....
2782 const char * logfile = NULL;
2783 const char * value = NULL;
2784 m_session->mailRc(error)->getValue(error, "record", &logfile);
2786 _file_log->deactivate();
2788 _file_log->activate();
2790 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2791 if (logfile == NULL || error.isNotSet()) {
2792 // logfile is not specified or "dontlogmessages" is TRUE
2793 setLogState(DTM_FALSE);
2794 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2796 // logfile is specified and "dontlogmessages" is FALSE
2797 setLogState(DTM_TRUE);
2798 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2801 if (NULL != logfile)
2802 free((void*) logfile);
2804 free((void*) value);
2806 // Word Wrap Toggle button. A WordWrapCmd is a ToggleButtonCmd...
2807 ((ToggleButtonCmd *)_format_word_wrap)->setButtonState(
2808 ((WordWrapCmd *)_format_word_wrap)->wordWrap(),
2812 // Initialize the Edit menu
2814 this->text_unselected();
2816 setIconName(ComposeIcon);
2820 Self_destruct(XtPointer, XtIntervalId *)
2823 fprintf(stderr, "DEBUG: Self_destruct(): invoked!\n");
2827 XtRemoveAllCallbacks(
2828 theApplication->baseWidget(),
2829 XmNdestroyCallback);
2830 delete theApplication;
2833 // Clears Compose window title, header fields, text, and attachment areas.
2835 SendMsgDialog::reset()
2837 _my_editor->textEditor()->clear_contents();
2838 _my_editor->attachArea()->resetPendingAction();
2841 // This will deselect any Attachment action, if any available now.
2842 // Also deselect text menu items....
2844 this->deactivate_default_attach_menu();
2845 this->text_unselected();
2846 this->all_attachments_deselected();
2847 _att_undelete->deactivate(); // This needs to be done in addition
2849 this->get_editor()->attachArea()->removeCurrentAttachments();
2851 // Unmanage the dialog
2854 if (_show_attach_area) { // .mailrc wants default attach area invisible
2856 // Unmanage the attach Area. Set the show_pane button.
2857 // This is done because if we are caching this window (after
2858 // unmanaging), we don't want the window to pop back up, on uncaching,
2859 // with the attachment pane visible, etc..
2861 this->showAttachArea();
2864 this->hideAttachArea();
2867 // Need to destroy current Message handle.
2868 delete _msgHandle; // All its body parts are deleted.
2870 _lastAttBP = NULL; // So just set this to NULL.
2871 // Delete or set to NULL ???
2872 _inclMsgHandle = NULL;
2873 _inclMsgHasText = NULL;
2875 for (int clear = 0; clear < _header_list.length(); clear++) {
2876 HeaderList * hl = _header_list[clear];
2878 // Bugfix: Old selection area remained selected, after text cleared
2879 // and parent widget unmanged, and then managed again for next
2880 // Compose. (So new text in old select area was still being selected).
2881 // Perhaps, this is a Motif bug ... but this fixes the problem.
2882 XmTextFieldClearSelection( hl->field_widget, CurrentTime );
2885 XtVaSetValues(hl->field_widget,
2886 XmNvalue, hl->value,
2890 XtVaSetValues(hl->field_widget,
2895 // Reset the Log state in case the user happened to change it.
2896 DtMail::Session *m_session = theRoamApp.session()->session();
2897 const char * logfile = NULL;
2898 const char * value = NULL;
2901 m_session->mailRc(error)->getValue(error, "record", &logfile);
2903 _file_log->deactivate();
2905 _file_log->activate();
2907 m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2908 if (logfile == NULL || error.isNotSet()) {
2909 // logfile is not specified or "dontlogmessages" is TRUE
2910 setLogState(DTM_FALSE);
2911 ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2913 // logfile is specified and "dontlogmessages" is FALSE
2914 setLogState(DTM_TRUE);
2915 ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2918 if (NULL != logfile)
2919 free((void*) logfile);
2921 free((void*) value);
2924 // Recycles Compose window.
2926 SendMsgDialog::quit(Boolean delete_win)
2929 // There are several ways we could have reached here.
2930 // 1) From the user choosing Send.
2931 // 2) From the user clicking on the Close button or Close menu item
2932 // 3) The user choosing Close from window manager menu.
2933 // For (1), we just forge ahead. For that, the _takeDown boolean
2934 // is set in send_message() method.
2935 // For (2), the boolean is set in goAway().
2936 // For (3), we call goAway() which sets the _takeDown depending on
2937 // a dialog negotiation if SMD has contents.
2939 if (_file_include->fileBrowser() != NULL)
2940 XtPopdown(XtParent(_file_include->fileBrowser()));
2941 if (_att_add->fileBrowser() != NULL)
2942 XtPopdown(XtParent(_att_add->fileBrowser()));
2944 if (_file_save_as->fileBrowser() != NULL)
2945 XtPopdown(XtParent(_file_save_as->fileBrowser()));
2946 if (_att_save->fileBrowser() != NULL)
2947 XtPopdown(XtParent(_att_save->fileBrowser()));
2950 // Check to see if it's the first time through the quit()
2951 // method. Set _first_time to FALSE so that we don't come
2952 // down this path again until we're done quitting or bad
2953 // things will happen.
2954 if (_first_time == TRUE) {
2955 _first_time = FALSE;
2957 // We're done quitting, so we can set _first_time to TRUE again.
2965 #ifdef DTMAIL_TOOLTALK
2966 // For explanation of dtmail_mapped, look at RoamApp.h.
2967 if ( started_by_tt && (0 == theCompose.getTimeOutId()) &&
2968 (theCompose.numUnusedWindows() == theCompose.numCreatedWindows()) &&
2972 id = XtAppAddTimeOut(
2973 theApplication->appContext(),
2974 (unsigned long)DESTRUCT_TIMEOUT,
2975 Self_destruct, NULL);
2976 theCompose.putTimeOutId(id);
2985 theCompose.putWin(this, FALSE);
2989 // If there are no composer timeouts, check if its time to shutdown.
2991 if (0 == theCompose.getTimeOutId()) theRoamApp.checkForShutdown();
2995 SendMsgDialog::panicQuit()
2998 // Need to make sure the message is still valid before proceeding.
2999 // ::reset may have been called so the message may no longer be valid.
3008 // Given a file name, include the file as attachment.
3010 SendMsgDialog::inclAsAttmt(char *file, char *name)
3012 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3014 this->setLastAttBP();
3018 // Given a buffer, include its content as an attachment.
3020 SendMsgDialog::inclAsAttmt(unsigned char *contents, int len, char *name)
3024 mbuf.buffer = (void *)contents;
3025 mbuf.size = (unsigned long)len;
3026 this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3027 (String)name, mbuf);
3028 this->setLastAttBP();
3031 // Given a RFC_822_Message formatted buffer, parse it and fill the Compose Window.
3033 SendMsgDialog::parseNplace(char *contents, int len)
3035 // 1. Create message handle for contents
3038 DtMail::Session * d_session = theRoamApp.session()->session();
3041 mbuf.buffer = (void *)contents;
3042 mbuf.size = (unsigned long)len;
3044 DtMail::Message * msg = d_session->messageConstruct(error,
3052 } else if ( error.isSet() ) {
3053 if ( (DTMailError_t) error == DTME_UnknownFormat ) {
3054 // The content does not have header info. Therefore, store
3055 // everything as text.
3056 _my_editor->textEditor()->set_contents((const char *)mbuf.buffer,
3062 char * status_string;
3063 DtMailBoolean first_bp_handled;
3064 first_bp_handled = _my_editor->textEditor()->set_message(
3070 int num_bodyParts = msg->getBodyCount(error);
3072 // Don't use setInclMsgHnd() because it causes the SMD's attachments
3073 // to get out of sink with the BE. Just assign the newly created message
3076 if ((num_bodyParts > 1) || (!first_bp_handled)) {
3078 if (first_bp_handled) {
3080 // setInclMsgHnd(msg, TRUE);
3084 // setInclMsgHnd(msg, FALSE);
3091 _my_editor->attachArea()->parseAttachments(error,
3096 // Need to call this after calling parseAttachments() so attachments
3097 // will be displayed in the attachment pane.
3098 _my_editor->manageAttachArea();
3100 // Need to update this compose window's internal message handle.
3102 // GL - calling updateMsgHndAtt is no longer necessary because we
3103 // just assigning the newly created msg to _msgHandle.
3104 // updateMsgHndAtt();
3107 loadHeaders(msg, DTM_TRUE);
3110 // Given a RFC_822_Message formatted file, parse it and fill the Compose Window.
3112 SendMsgDialog::parseNplace(const char * path)
3114 // 1. Get file content into buffer.
3115 int fd = SafeOpen(path, O_RDONLY);
3121 if (SafeFStat(fd, &buf) < 0) {
3126 _dead_letter_buf = new char[buf.st_size];
3127 if (!_dead_letter_buf) {
3132 if (SafeRead(fd, _dead_letter_buf,
3133 (unsigned int) buf.st_size) != buf.st_size) {
3134 delete _dead_letter_buf;
3139 parseNplace(_dead_letter_buf, (int) buf.st_size);
3143 SendMsgDialog::text( const char *text )
3145 _my_editor->textEditor()->set_contents( text, strlen(text) );
3149 SendMsgDialog::append( const char *text )
3151 _my_editor->textEditor()->append_to_contents( text, strlen(text) );
3155 SendMsgDialog::text()
3157 // Potential memory leak here. Because XmTextGetString returns
3158 // pointer to space containing all the text in the widget. Need
3159 // to call XtFree after we use this space
3160 // Also DtEditor widget requires application to free data.
3162 return (_my_editor->textEditor()->get_contents());
3168 SendMsgDialog::text_selected()
3170 // turn on sensitivity for Cut/Clear/Copy/Delete
3171 _edit_cut->activate();
3172 _edit_copy->activate();
3173 _edit_clear->activate();
3174 _edit_delete->activate();
3175 _edit_select_all->activate();
3179 SendMsgDialog::text_unselected()
3181 // turn off sensitivity for those items
3182 _edit_cut->deactivate();
3183 _edit_copy->deactivate();
3184 _edit_clear->deactivate();
3185 _edit_delete->deactivate();
3191 SendMsgDialog::attachment_selected()
3193 _att_save->activate();
3194 _att_delete->activate();
3195 _att_rename->activate();
3200 SendMsgDialog::all_attachments_selected()
3202 _att_delete->activate();
3203 _att_save->deactivate();
3204 _att_rename->deactivate();
3206 if (_attachmentActionsList != NULL) {
3207 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3208 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3209 _attachmentActionsList);
3210 delete _attachmentActionsList;
3211 _attachmentActionsList = NULL;
3218 SendMsgDialog::all_attachments_deselected()
3220 _att_save->deactivate();
3221 _att_delete->deactivate();
3222 _att_rename->deactivate();
3224 if (_attachmentActionsList != NULL) {
3225 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3226 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3227 _attachmentActionsList);
3228 delete _attachmentActionsList;
3229 _attachmentActionsList = NULL;
3236 SendMsgDialog::addAttachmentActions(
3243 AttachmentActionCmd *attachActionCmd;
3245 if (_attachmentActionsList == NULL) {
3246 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3249 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3250 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3251 _attachmentActionsList);
3252 delete _attachmentActionsList;
3253 _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3257 for (i = 0; i < indx; i++) {
3258 anAction = actions[i];
3259 actionLabel = DtActionLabel(anAction); // get the localized action label
3260 attachActionCmd = new AttachmentActionCmd(
3265 _attachmentActionsList->add(attachActionCmd);
3268 _attachmentMenu = _menuBar->addCommands(
3270 _attachmentActionsList
3272 _attachmentPopupMenu = _menuPopupAtt->addCommands(
3273 _attachmentPopupMenu,
3274 _attachmentActionsList
3279 SendMsgDialog::removeAttachmentActions()
3282 // Stubbed out for now
3286 SendMsgDialog::invokeAttachmentAction(
3290 DtMailEditor *editor = this->get_editor();
3291 AttachArea *attacharea = editor->attachArea();
3292 Attachment *attachment = attacharea->getSelectedAttachment();
3294 attachment->invokeAction(index);
3298 SendMsgDialog::selectAllAttachments()
3301 DtMailEditor *editor = this->get_editor();
3302 AttachArea *attachArea = editor->attachArea();
3304 attachArea->selectAllAttachments();
3310 SendMsgDialog::activate_default_attach_menu()
3312 _att_select_all->activate();
3316 SendMsgDialog::deactivate_default_attach_menu()
3318 _att_select_all->deactivate();
3323 SendMsgDialog::delete_selected_attachments()
3325 DtMailEnv mail_error;
3327 // Initialize the mail_error.
3331 AttachArea *attachArea = _my_editor->attachArea();
3332 attachArea->deleteSelectedAttachments(mail_error);
3334 if (mail_error.isSet()) {
3338 // Activate this button to permit the user to undelete.
3340 _att_undelete->activate();
3342 // Deactivate buttons that will be activated when another
3343 // selection applies.
3345 _att_save->deactivate();
3346 _att_delete->deactivate();
3347 _att_rename->deactivate();
3349 if (_attachmentActionsList) {
3350 _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3351 _menuPopupAtt->removeCommands(_attachmentPopupMenu,
3352 _attachmentActionsList);
3353 delete _attachmentActionsList;
3354 _attachmentActionsList = NULL;
3359 SendMsgDialog::undelete_last_deleted_attachment()
3361 DtMailEnv mail_error;
3363 // Initialize the mail_error.
3367 AttachArea *attachArea = _my_editor->attachArea();
3368 attachArea->undeleteLastDeletedAttachment(mail_error);
3370 if (mail_error.isSet()) {
3374 if(_my_editor->attachArea()->getIconSelectedCount())
3375 _att_delete->activate();
3377 if (attachArea->getDeleteCount() == 0) {
3378 _att_undelete->deactivate();
3383 SendMsgDialog::renameAttachmentOK()
3385 AttachArea *attachArea = _my_editor->attachArea();
3387 if (attachArea->getIconSelectedCount() > 1) {
3388 char *buf = new char[512];
3390 sprintf(buf, GETMSG(DT_catd, 5, 4, "Select only one attachment\n\
3391 and then choose rename"));
3393 _genDialog->setToQuestionDialog(
3394 GETMSG(DT_catd, 5, 2, "Mailer"),
3397 char * helpId = DTMAILHELPSELECTONEATTACH;
3399 int answer = _genDialog->post_and_return(helpId);
3410 SendMsgDialog::showAttachArea()
3412 DtMailEditor *editor = this->get_editor();
3413 editor->showAttachArea();
3414 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, FALSE);
3418 SendMsgDialog::hideAttachArea()
3420 DtMailEditor *editor = this->get_editor();
3421 editor->hideAttachArea();
3422 ((ToggleButtonCmd *)_att_show_pane)->setButtonState(FALSE, FALSE);
3426 SendMsgDialog::lookupHeader(const char * name)
3428 for (int h = 0; h < _header_list.length(); h++) {
3429 HeaderList * hl = _header_list[h];
3430 if (hl->show != SMD_NEVER &&
3431 strcmp(hl->label, name) == 0) {
3440 SendMsgDialog::headerValueChanged(Widget,
3441 XtPointer client_data,
3444 SendMsgDialog * self = (SendMsgDialog *)client_data;
3445 self->_headers_changed = DTM_TRUE;
3449 SendMsgDialog::reattachHeaders(void)
3451 // We have to walk through the entire list of headers, attaching
3452 // the shown headers to the ones above them.
3454 HeaderList * hl = _header_list[0];
3455 Widget previous_form = hl->form_widget;
3457 for (int h = 1; h < _header_list.length(); h++) {
3458 hl = _header_list[h];
3462 previous_form = hl->form_widget;
3466 XtVaSetValues(hl->form_widget,
3467 XmNtopAttachment, XmATTACH_WIDGET,
3468 XmNtopWidget, previous_form,
3470 previous_form = hl->form_widget;
3478 forceFormResize(_main_form);
3479 forceFormResize(_header_form);
3483 SendMsgDialog::justifyHeaders(void)
3485 // Find out which header label has the longest display width to right
3486 // justify all labels.
3488 Dimension longest = 0;
3491 for (int count = 0; count < _header_list.length(); count++) {
3492 HeaderList * hl = _header_list[count];
3493 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3497 XtVaGetValues(hl->label_widget,
3499 XmNmarginLeft, &margin,
3502 if ( w > longest ) {
3507 for (int adjust = 0; adjust < _header_list.length(); adjust++) {
3508 HeaderList * hl = _header_list[adjust];
3509 if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3513 XtVaGetValues(hl->label_widget,
3515 XmNmarginLeft, &margin,
3518 XtVaSetValues(hl->label_widget, XmNmarginLeft, (longest-w) > 0 ? longest-w : 1, NULL );
3523 SendMsgDialog::forceFormResize(Widget form)
3525 // The Motif Form widget is at least a little bit brain damaged.
3526 // We need to convince it to do the right thing after we make
3527 // minor adjustments in the children.
3529 Dimension width, height, border;
3533 XmNborderWidth, &border,
3537 XmNwidth, width + 1,
3538 XmNheight, height + 1,
3549 SendMsgDialog::unfilled_headers()
3551 // Walk through the headers. See if any of them have a value.
3553 for (int scan = 0; scan < _header_list.length(); scan++) {
3554 HeaderList * hl = _header_list[scan];
3555 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
3559 char * value = NULL;
3560 XtVaGetValues(hl->field_widget,
3564 if (strlen(value) > 0) {
3574 // Method checks if self has text in it.
3577 SendMsgDialog::checkDirty()
3579 if (this->unfilled_headers() &&
3580 (this->get_editor()->textEditor()->no_text()) &&
3581 (this->get_editor()->attachArea()->getIconCount() == 0) ) {
3583 // return FALSE so quit() can go ahead.
3594 SendMsgDialog::handleQuitDialog()
3598 DtMail::Session *m_session = theRoamApp.session()->session();
3599 const char * value = NULL;
3602 m_session->mailRc(error)->getValue(error, "expert", &value);
3603 if (error.isNotSet() && value != NULL)
3606 free((void*) value);
3611 DtMailGenDialog *dialog = this->genDialog();
3613 dialog->setToQuestionDialog(
3623 "The Compose window contains text or\n\
3624 attachments that will be lost if\n\
3625 the window is closed.\n\
3626 Close the Compose window?")
3628 helpId = DTMAILHELPCLOSECOMPOSEWINDOW;
3629 if ( dialog->post_and_return(
3640 helpId) == 1 ) { // Close selected
3645 return(FALSE); // Cancel selected
3650 SendMsgDialog::goAway(
3651 Boolean checkForDirty
3655 if (!checkForDirty) {
3660 // Check to see if self has contents (ie., is dirty)
3662 Boolean is_dirty = this->checkDirty();
3665 if (isIconified()) {
3666 MainWindow::manage();
3669 // Enquire if user really wants this window to go away
3671 Boolean really_quit = this->handleQuitDialog();
3682 SendMsgDialog::manage()
3684 MenuWindow::manage();
3686 HeaderList * hl = _header_list[0];
3687 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3692 SendMsgDialog::unmanage()
3694 MenuWindow::unmanage();
3695 XFlush(XtDisplay(this->_main_form));
3696 XSync(XtDisplay(this->_main_form), False);
3702 _compose_head = NULL;
3710 Compose::Compose_Win *a_node;
3711 Compose::Compose_Win *next_node;
3713 theRoamApp.registerPendingTask();
3715 a_node = _compose_head;
3718 next_node = a_node->next;
3719 if (!a_node->in_use)
3722 free((void*) a_node);
3727 theRoamApp.unregisterPendingTask();
3731 Compose::putWin(SendMsgDialog *smd, Boolean in_use)
3733 Compose::Compose_Win* a_node = NULL;
3734 Compose::Compose_Win *tmp = NULL;
3737 // Update the _not_in_use count.
3743 // Check to see if compose window is already in the list.
3745 for (a_node = _compose_head; a_node; a_node=a_node->next)
3747 if (a_node->win == smd)
3749 a_node->in_use = in_use;
3754 // Need new node with smd.
3755 tmp = (Compose::Compose_Win *)malloc(sizeof(Compose::Compose_Win));
3758 tmp->in_use = in_use;
3760 // If nothing is cached so far, add this Compose window to the head.
3761 if (NULL == _compose_head)
3763 _compose_head = tmp;
3767 // There exists a cache. Add this compose window to the tail.
3768 for (a_node=_compose_head; a_node; a_node=a_node->next)
3770 if (NULL == a_node->next)
3779 Compose::getUnusedWin()
3781 if (NULL == _compose_head) return NULL;
3783 Compose::Compose_Win* a_node = NULL;
3784 Compose::Compose_Win* the_node = NULL;
3786 // Find a node with unused smd. Return smd
3787 for (a_node=_compose_head; a_node; a_node=a_node->next)
3789 if (!a_node->in_use)
3791 a_node->in_use = TRUE;
3800 // Get a compose window either by creating a new SendMsgDialog or
3801 // from the recycle list.
3805 SendMsgDialog *newsend = NULL;
3807 #ifdef DTMAIL_TOOLTALK
3810 XtRemoveTimeOut(_timeout_id);
3815 newsend = getUnusedWin();
3819 // We have no unused SMDs around; therefore, create new window.
3820 theRoamApp.busyAllWindows();
3821 newsend = new SendMsgDialog();
3822 newsend->initialize();
3824 putWin(newsend, TRUE);
3825 theRoamApp.unbusyAllWindows();
3829 newsend->resetHeaders();
3830 newsend->displayInCurrentWorkspace();
3833 newsend->text_unselected();
3835 newsend->startAutoSave();
3837 // Get new Message Handle
3838 newsend->setMsgHnd();
3839 char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
3840 newsend->setTitle(ttl);
3841 newsend->setIconTitle(ttl);
3847 SendMsgDialog::setTitle(char *subject)
3849 char *format = "%s - %s";
3850 char *prefix = GETMSG(DT_catd, 1, 6, "Mailer");
3854 len = strlen(format) + strlen(prefix) + strlen(subject) + 1;
3855 new_title = new char[len];
3856 sprintf(new_title, format, prefix, subject);
3859 delete [] new_title;
3863 SendMsgDialog::resetHeaders(void)
3865 DtMail::Session *m_session = theRoamApp.session()->session();
3866 const char * value = NULL;
3870 m_session->mailRc(error)->getValue(error, "additionalfields", &value);
3872 // Return if no props were applied and headers did not change.
3873 if ((_additionalfields == NULL && value == NULL) ||
3874 ( _additionalfields != NULL && value != NULL &&
3875 strcmp (_additionalfields, value) == 0))
3878 free((void*) value);
3883 // User changed the Header list via props. Recreate list
3886 // First hide all shown headers
3887 for (i=0; i < _header_list.length(); i++) {
3888 HeaderList * hl = _header_list[i];
3889 if (hl->show == SMD_SHOWN) {
3890 hl->show = SMD_HIDDEN;
3891 XtUnmanageChild(hl->form_widget);
3895 // Now remove the old list.
3896 DtVirtArray<PropStringPair *> results(8);
3897 parsePropString(_additionalfields, results);
3898 for (i=0, j=results.length(); i < j; i++) {
3899 PropStringPair * psp = results[i];
3900 int slot = lookupHeader(psp->label);
3901 // dont allow removal of default headers.
3902 HeaderList * hl = _header_list[slot];
3903 if (!reservedHeader(hl->label)) {
3905 hl->show = SMD_NEVER;
3907 else if (hl->value != NULL) {
3912 while(results.length()) {
3913 PropStringPair * psp = results[0];
3918 if (_additionalfields != NULL)
3919 free(_additionalfields);
3920 if (value != NULL && *value != '\0')
3921 _additionalfields = strdup(value);
3923 _additionalfields = NULL;
3925 parsePropString(value, results);
3928 for (j=results.length(), i=0; i < j; i++) {
3929 PropStringPair * psp = results[i];
3930 int slot = lookupHeader(psp->label);
3933 HeaderList * hl = _header_list[slot];
3934 if (!reservedHeader(hl->label))
3935 hl->show = SMD_HIDDEN;
3936 if (hl->value != NULL) {
3940 if (psp->value != NULL)
3941 hl->value = strdup(psp->value);
3944 HeaderList * copy_hl = new HeaderList;
3945 copy_hl->label = strdup(psp->label);
3946 copy_hl->header = strdup(psp->label);
3948 copy_hl->value = strdup(psp->value);
3949 copy_hl->show = SMD_HIDDEN;
3950 _header_list.append(copy_hl);
3952 while(results.length()) {
3953 PropStringPair * psp = results[0];
3958 createHeaders(_header_form);
3959 doDynamicHeaderMenus();
3962 free((void*) value);
3966 SendMsgDialog::setInputFocus(const int mode)
3970 HeaderList * hl = _header_list[0];
3971 (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3973 else if (mode == 1) {
3974 Widget edWid = _my_editor->textEditor()->get_editor();
3975 (void) XmProcessTraversal(edWid, XmTRAVERSE_CURRENT);
3980 SendMsgDialog::attachmentFeedback(
3988 this->normalCursor();
3993 SendMsgDialog::hasAddressee()
3997 DtMail::Envelope * env = _msgHandle->getEnvelope(error);
3999 // Walk through the headers.
4000 // Return TRUE if the message has a value for either of the
4001 // following headers: To:, Cc:, or Bcc:.
4002 // Return FALSE if none of the three headers have any value.
4004 for (int scan = 0; scan < _header_list.length(); scan++) {
4005 HeaderList * hl = _header_list[scan];
4006 if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
4009 if ((strcmp(hl->label, "To") == 0) ||
4010 (strcmp(hl->label, "Cc") == 0) ||
4011 (strcmp(hl->label, "Bcc") == 0)) {
4012 char * value = NULL;
4013 XtVaGetValues(hl->field_widget,
4017 for (char *cv = value; *cv; cv++) {
4018 if (!isspace(*cv)) {
4020 return(TRUE); // text value contains contents
4023 XtFree(value); // text value is "content free" - try the next one
4027 return(FALSE); // no field has contents