dtmail: resolve 'deference before null check' errors related to if(!NULL) checks...
[oweals/cde.git] / cde / programs / dtmail / dtmail / SendMsgDialog.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  * $TOG: SendMsgDialog.C /main/43 1999/03/25 13:42:29 mgreess $
27  *
28  * RESTRICTED CONFIDENTIAL INFORMATION:
29  * 
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
36  * Sun's request.
37  *
38  * Copyright 1993, 1994, 1995 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42
43 #include <EUSCompat.h>
44 #include "AttachArea.h"
45 #include "Attachment.h"
46 #include "RoamApp.h"
47 #include "SendMsgDialog.h"
48 #include "NoOpCmd.h"
49 #include "RoamCmds.h"
50 #include "ComposeCmds.hh"
51 #include "CmdList.h"
52 #include "MenuBar.h"
53 #include "ButtonInterface.h"
54 #include "MemUtils.hh"
55 #include "Help.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>
64 #include <Xm/Text.h>
65 #include <Xm/TextF.h>
66 #include <Xm/Form.h>
67 #include <Xm/Label.h>
68 #include <Xm/RowColumn.h>
69 #include <Xm/PushB.h>
70 #include <Xm/CascadeB.h>
71 #include <Xm/SeparatoG.h>
72 #include <DtMail/IO.hh>
73 #include <DtMail/DtMailP.hh>
74 #include <stdio.h>
75 #include <errno.h>
76 #include <sys/param.h>
77 #include <stdlib.h>
78 #include <ctype.h>
79 #include <unistd.h>
80
81 #include "MailMsg.h"
82 #include "str_utils.h"
83
84 #define OFFSET 10    // Number of spaces from margin
85
86 #ifdef DTMAIL_TOOLTALK
87 // Time to self destruct
88 #define DESTRUCT_TIMEOUT   60000    // 1 minutes
89 #endif
90
91 // Pipe used between RFCTransport::childHandler and XtAppAddInput
92 static int _transfds[2];
93
94 static const char *ComposeIcon = "IcMcomp";
95
96 struct DefaultHeaders {
97     int                         msg_set;
98     int                         msg_number;
99     char *                      dflt_label;
100     char *                      label;
101     const char *                header;
102     SendMsgDialog::ShowState    show;
103 };
104
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  }
112 };
113
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.
118 //
119 static const char * BlockedHeaders[] = {
120     "Content-Length",
121     "Content-Type",
122     "Content-MD5",
123     "Content-Transfer-Encoding",
124     "Mime-Version",
125     "X-Sun-Charset",
126     NULL
127 };
128
129
130 static DtMailBoolean
131 block(const char * header)
132 {
133     for (const char ** test = BlockedHeaders; *test; test++) {
134         if (strcasecmp(header, *test) == 0) {
135             return(DTM_TRUE);
136         }
137     }
138
139     return(DTM_FALSE);
140 }
141
142 class CmdList;
143
144 Compose theCompose;   // Manages all compose windows.
145
146 SendMsgDialog::HeaderList::HeaderList(void)
147 {
148     label = NULL;
149     header = NULL;
150     value = NULL;
151     show = SMD_NEVER;
152     form_widget = NULL;
153     label_widget = NULL;
154     field_widget = NULL;
155 }
156
157 SendMsgDialog::HeaderList::HeaderList(const HeaderList & other)
158 {
159     label = NULL;
160     header = NULL;
161     show = other.show;
162     form_widget = other.form_widget;
163     label_widget = other.label_widget;
164     field_widget = other.field_widget;
165
166     if (other.label) {
167         label = strdup(other.label);
168     }
169
170     if (other.header) {
171         header = strdup(other.header);
172     }
173
174     if (other.value) {
175         value = strdup(other.value);
176     }
177 }
178
179 SendMsgDialog::HeaderList::~HeaderList(void)
180 {
181     if (label) {
182         free(label);
183     }
184
185     if (header) {
186         free(header);
187     }
188
189     if (value) {
190         free(value);
191     }
192 }
193 Boolean SendMsgDialog::reservedHeader(const char *label)
194 {
195     for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++)
196       if (strcmp(label, hl->label) == 0)
197         return TRUE;
198     return FALSE;
199 }
200
201
202 SendMsgDialog::SendMsgDialog() 
203 : MenuWindow ( "ComposeDialog", True ),
204 _header_list(16)
205 {
206     _main_form = NULL;
207     _num_sendAs = 0;
208     _genDialog = NULL;
209
210     _show_attach_area = FALSE;
211     _takeDown = FALSE;
212     _first_time = TRUE;
213     _already_sending = FALSE;
214
215     _separator = NULL;
216
217     // Alias Popup menus
218     _aliasList = NULL;
219     _bccPopupCmdlist = NULL;
220     _bccPopupMenu = NULL;
221     _bccPopupMenuBar = NULL;
222     _ccPopupCmdlist = NULL;
223     _ccPopupMenu = NULL;
224     _ccPopupMenuBar = NULL;
225     _toPopupCmdlist = NULL;
226     _toPopupMenu = NULL;
227     _toPopupMenuBar = NULL;
228
229     // Attachment menu
230     _attachmentActionsList = NULL;
231     _attachmentMenu = NULL;
232     _attachmentMenuList = NULL;
233     _attachmentPopupMenuList = NULL;
234     _textPopupMenuList = NULL;
235     
236     _att_show_pane = NULL;
237     _att_select_all = NULL;
238     _att_save = NULL;
239     _att_add = NULL;
240     _att_delete = NULL;
241     _att_undelete = NULL;
242     _att_rename = NULL;
243     _att_select_all = NULL;
244     _auto_save_interval = 0;
245     _auto_save_path = NULL;
246     _auto_save_file = NULL;
247     _dead_letter_buf = NULL;
248     
249     _file_include = NULL;
250     _file_save_as = NULL;
251     _file_send = NULL;
252     _file_log = NULL;
253     _file_close = NULL;
254
255     _format_word_wrap = NULL;
256     _format_settings = NULL;
257     _format_find_change = NULL;
258     _format_spell = NULL;
259     _format_menu = NULL;
260     _format_cmds = NULL;
261     _format_cascade = NULL;
262     _format_separator = NULL;
263     _templateList = NULL;
264
265     // Now we need to get the additional headers from the property system.
266     //   
267     DtMailEnv error;
268     DtMail::Session * d_session = theRoamApp.session()->session();
269     DtMail::MailRc * mail_rc = d_session->mailRc(error);
270  
271     const char * value = NULL;  
272     mail_rc->getValue(error, "additionalfields", &value);
273  
274     DtVirtArray<PropStringPair *> results(8);
275     if (error.isNotSet()) {
276         _additionalfields = strdup(value);
277         parsePropString(value, results);
278     }
279  
280     if (NULL != value)
281       free((void*) value);
282
283     // Load the header list with the predefined/fixed headers.
284     //
285     if (! isInitializedDefaultHeaderList)
286       for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
287           hl->label =
288             GETMSG(DT_catd, hl->msg_set, hl->msg_number, hl->dflt_label);
289
290           isInitializedDefaultHeaderList = TRUE;
291       }
292
293     for (DefaultHeaders * hl = DefaultHeaderList; hl->dflt_label; hl++) {
294         HeaderList * copy_hl = new HeaderList;
295         copy_hl->label = strdup(hl->label);
296         copy_hl->value = getPropStringValue(results, hl->label);
297         copy_hl->header = strdup(hl->header);
298         copy_hl->show = hl->show;
299         _header_list.append(copy_hl);
300     }
301
302     if (error.isNotSet()) {
303
304         for (int mrc = 0; mrc < results.length(); mrc++) {
305             PropStringPair * psp = results[mrc];
306
307             if (!reservedHeader(psp->label)) {
308                 HeaderList * copy_hl = new HeaderList;
309                 copy_hl->label = strdup(psp->label);
310                 copy_hl->header = strdup(psp->label);
311                 if (psp->value) 
312                         copy_hl->value = strdup(psp->value);
313                 copy_hl->show = SMD_HIDDEN;
314                 _header_list.append(copy_hl);
315             }
316         }
317
318         while(results.length()) {
319             PropStringPair * psp = results[0];
320             delete psp;
321             results.remove(0);
322         }
323     }
324     else 
325         _additionalfields = NULL;
326
327 }
328
329 SendMsgDialog::~SendMsgDialog()
330 {
331     
332     delete _genDialog;
333     delete _separator;
334     
335     // File
336     
337     delete _file_include;
338     delete _file_save_as;
339     delete _file_send;
340     delete _file_log;
341     delete _file_close;
342     
343     // Edit
344     
345     delete _edit_undo;
346     delete _edit_cut;
347     delete _edit_copy;
348     delete _edit_paste;
349     delete _edit_clear;
350     delete _edit_delete;
351     delete _edit_select_all;
352     
353     // Alias Popup menus
354
355     delete _aliasList;
356
357     XtDestroyWidget(_ccPopupMenu);
358     delete _ccPopupMenuBar;
359     delete _ccPopupCmdlist;
360
361     XtDestroyWidget(_toPopupMenu);
362     delete _toPopupMenuBar;
363     delete _toPopupCmdlist;
364
365     XtDestroyWidget(_bccPopupMenu);
366     delete _bccPopupMenuBar;
367     delete _bccPopupCmdlist;
368
369     // Attachment
370     
371     delete _att_save;
372     delete _att_add;
373     delete _att_delete;
374     delete _att_undelete;
375     delete _att_rename;
376     delete _att_select_all;
377     
378     delete _attachmentActionsList;
379     
380     // Format
381     
382     delete _format_word_wrap;
383     delete _format_settings;
384     delete _format_find_change;
385     delete _format_spell;
386     delete _format_separator;
387     
388     // Things created by createWorkArea()
389     delete _my_editor;
390     delete _send_button;
391     delete _close_button;
392     
393     //
394     // Allocated using 'malloc'.
395     // Purify requires us to free it using 'free'.
396     //
397     free(_auto_save_path);
398     if (_auto_save_file) {
399         delete _auto_save_file;
400     }
401     if (_dead_letter_buf) {
402         delete _dead_letter_buf;
403     }
404
405     delete _attachmentMenuList;
406     delete _attachmentPopupMenuList;
407     delete _textPopupMenuList;
408
409 }
410
411 // Callback for each header row
412 void
413 header_form_traverse(Widget w, XtPointer, XtPointer)
414 {
415     (void) XmProcessTraversal(w, XmTRAVERSE_NEXT_TAB_GROUP);
416 }
417
418 #ifdef DEAD_WOOD
419 void
420 send_pushb_callback(Widget, XtPointer, XtPointer)
421 {
422     
423 }
424 #endif /* DEAD_WOOD */
425
426 // Create Message Handle with empty first body part.
427 DtMail::Message *
428 SendMsgDialog::makeMessage(void)
429 {
430     DtMailEnv error;
431     DtMail::Session * d_session = theRoamApp.session()->session();
432     DtMailBuffer mbuf;
433     
434     mbuf.buffer = NULL;
435     mbuf.size = 0;
436     
437     DtMail::Message * msg = d_session->messageConstruct(error,
438                                                         DtMailBufferObject,
439                                                         &mbuf,
440                                                         NULL,
441                                                         NULL,
442                                                         NULL);
443     if (error.isSet() || !msg) {
444         return(NULL);
445     }
446     
447     DtMail::BodyPart * bp = msg->newBodyPart(error, NULL);
448     
449     // For now, reserve the first body part for text.
450     setFirstBPHandled(TRUE);
451     
452     return(msg);
453 }
454
455 // Before Submitting, call this routine.
456 // This routine does not save attachment(s).
457 // Check point routine should also call this routine.
458 void
459 SendMsgDialog::updateMsgHnd()
460 {
461     DtMailEnv error;
462     DtMail::Envelope * env;
463     int textLen;
464
465     env = _msgHandle->getEnvelope(error);
466     storeHeaders();
467
468     char * widget_text = this->text();
469     if (widget_text && *widget_text == '\0') {
470         textLen = 0;
471         XtFree(widget_text);
472         widget_text = NULL;
473     }
474     else
475         textLen = strlen(widget_text);
476     
477     // Even if textlen is 0 because user has cleared all previous text,
478     // need to setContents again to clear first BP.  Otherwise, deleted
479     // text will show up. 
480
481     // Get FirstBodyPart and fill it up.
482     DtMail::BodyPart *bp = _msgHandle->getFirstBodyPart(error);
483     bp->setContents(error, widget_text, textLen, NULL, NULL, 0, NULL);
484     setFirstBPHandled(TRUE);
485     
486     if (NULL != widget_text)
487       XtFree(widget_text);
488 }
489
490 // Before Submitting, also call this routine.
491 // This routine fills the message handle with attachment(s).
492 // The reason why we get attachment(s) from the Back End before submission
493 // is in case the attachment(s) are updated during the compose session.
494 // If changes are saved back, then the AttachArea class would register the
495 // update with Back End.  So BE always has the latest attachments.
496 void
497 SendMsgDialog::updateMsgHndAtt()
498 {
499     if ( _inclMsgHandle == NULL )
500         return;    // No attachments
501     
502     DtMailEnv error;
503     DtMail::BodyPart *msgBP;
504     DtMail::BodyPart *inclBP = _inclMsgHandle->getFirstBodyPart(error);
505     const void *contents;
506     unsigned long len;
507     char *type;
508     char *name;
509     int mode;
510     char *desc;
511     
512     // If message handle to be copied, _inclMsgHandle, does not have its first
513     // body part as text, then get its first body part content and copy it.
514     // If it contains text, then skip it.
515     if ( !_inclMsgHasText ) {
516         inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
517         if ( _firstBPHandled ) {
518             msgBP = _msgHandle->newBodyPart(error, _lastAttBP); 
519         } else {
520             msgBP = _msgHandle->getFirstBodyPart(error);
521         }
522         msgBP->setContents(error, contents, len, type, name, mode, desc);
523         setLastAttBP(msgBP);
524         free(name);
525         free(type);
526     }
527     
528     // Continue to get the next body part and start copy.
529     while ((inclBP = _inclMsgHandle->getNextBodyPart(error, inclBP)) != NULL) {
530         inclBP->getContents(error, &contents, &len, &type, &name, &mode, &desc);
531         if ( _firstBPHandled ) {
532             msgBP = _msgHandle->newBodyPart(error, _lastAttBP); 
533         } else {
534             msgBP = _msgHandle->getFirstBodyPart(error);
535         }
536         msgBP->setContents(error, contents, len, type, name, mode, desc);
537         setLastAttBP(msgBP);
538         free(name);
539         free(type);
540     }  // end of while loop
541 }
542
543 // Update the _lastAttBP pointer so that subsequent newBodyPart() calls can
544 // return a message body part following the last body part.
545 void
546 SendMsgDialog::setLastAttBP()
547 {
548     DtMailEnv error;
549     _lastAttBP = _msgHandle->getNextBodyPart(error, _lastAttBP);
550 }
551
552 // This sister routine is needed when _lastAttBP has not been initialized.
553 void
554 SendMsgDialog::setLastAttBP(DtMail::BodyPart *bp)
555 {
556     _lastAttBP = bp;
557 }
558
559 // Initialize _msgHandle
560 void
561 SendMsgDialog::setMsgHnd()
562 {
563     _msgHandle = makeMessage();
564     _inclMsgHandle = NULL;
565     _inclMsgHasText = FALSE;
566     _lastAttBP = NULL;
567 }
568
569 // Set timeout to ten minutes (as milliseconds)
570 //
571 void
572 SendMsgDialog::startAutoSave(void)
573 {
574     mkAutoSavePath();
575     if(!_auto_save_path) return;
576     _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
577                                           getAutoSaveInterval(),
578                                           autoSaveCallback,
579                                           this);
580 }
581
582 void
583 SendMsgDialog::stopAutoSave(void)
584 {
585     if (!_auto_save_interval) {
586         return;
587     }
588     
589     XtRemoveTimeOut(_auto_save_interval);
590     _auto_save_interval = 0;
591     
592     unlink(_auto_save_file);
593     free(_auto_save_file);
594     _auto_save_file = NULL;
595 }
596
597 static const char * DEAD_LETTER_DIR = "~/dead_letter";
598 static const char * BACKUP_DEAD_LETTER = "./dead_letter";
599 static const char * PREFIX = "mail.dead.letter";
600
601 void
602 SendMsgDialog::mkAutoSavePath(void)
603 {
604     // First, see if we need to set up the path.
605     //
606
607     if (!_auto_save_path) {
608         DtMail::Session * d_session = theRoamApp.session()->session();
609         DtMailEnv error;
610         const char *save_path = NULL;
611
612         d_session->mailRc(error)->getValue(error, "deaddir", &save_path);
613         if (error.isNotSet() && save_path != NULL && *save_path != '\0')
614             _auto_save_path = d_session->expandPath(error, save_path);
615         else {
616         
617                 _auto_save_path = d_session->expandPath(error, DEAD_LETTER_DIR);
618                 if (!_auto_save_path)
619                 _auto_save_path = d_session->expandPath(error, BACKUP_DEAD_LETTER);
620         }
621
622         if (NULL != save_path)
623           free((void*) save_path);
624     }
625     
626     // If we still have a path, punt.
627     // 
628     if (!_auto_save_path) {
629         return;
630     }
631     
632     if (SafeAccess(_auto_save_path, W_OK) != 0) {
633         if (errno != ENOENT) {
634             // Not an error we can overcome here.
635             //
636             free(_auto_save_path);
637             _auto_save_path = NULL;
638             return;
639         }
640         
641         if (mkdir(_auto_save_path, 0700) < 0) {
642             free(_auto_save_path);
643             _auto_save_path = NULL;
644             return;
645         }
646     }
647     
648     // Now we run through the possible file names until we hit pay dirt.
649     //
650     _auto_save_file = (char*) malloc((size_t) strlen(_auto_save_path) + 100);
651     for (int suffix = 1; ; suffix++) {
652         sprintf(_auto_save_file, "%s/%s.%d", _auto_save_path, PREFIX, suffix);
653         if (SafeAccess(_auto_save_file, F_OK) != 0) {
654             break;
655         }
656     }
657 }
658
659 void
660 SendMsgDialog::loadDeadLetter(const char * path)
661 {
662     _auto_save_file = strdup(path);
663     parseNplace(path);
664     
665     _auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
666                                           getAutoSaveInterval(),
667                                           autoSaveCallback,
668                                           this);
669 }
670
671 void
672 SendMsgDialog::autoSaveCallback(XtPointer client_data, XtIntervalId * id)
673 {
674     SendMsgDialog * self = (SendMsgDialog *)client_data;
675     
676     if (self->_auto_save_interval != *id) {
677         // Random noise. Ignore it.
678         return;
679     }
680     
681     self->doAutoSave();
682     
683     self->_auto_save_interval = XtAppAddTimeOut(theApplication->appContext(),
684                                                 self->getAutoSaveInterval(),
685                                                 autoSaveCallback,
686                                                 self);
687 }
688
689 void
690 SendMsgDialog::doAutoSave(char *filename)
691 {
692     DtMail::Session * d_session = theRoamApp.session()->session();
693     DtMailEnv error;
694
695     busyCursor();
696     setStatus(GETMSG(DT_catd, 3, 70, "Writing dead letter..."));
697
698     updateMsgHnd();
699     
700     assert((NULL != filename));
701     RFCWriteMessage(error, d_session, filename, _msgHandle);
702     if((DTMailError_t) error == DTME_OutOfSpace )
703     {
704        RoamMenuWindow::ShowErrMsg((char *)error.getClient(),TRUE,this );
705     }
706     normalCursor();
707     clearStatus();
708 }
709
710 void
711 SendMsgDialog::doAutoSave(void)
712 {
713     doAutoSave(_auto_save_file);
714 }
715
716 int
717 SendMsgDialog::getAutoSaveInterval(void)
718 {
719     DtMailEnv error;
720     // Initialize the mail_error.
721     
722     error.clear();
723     
724     DtMail::Session * d_session = theRoamApp.session()->session();
725     DtMail::MailRc * mail_rc = d_session->mailRc(error);
726     
727     int save_interval;
728     const char * value = NULL;
729     mail_rc->getValue(error, "composeinterval", &value);
730     if (error.isSet() || value == NULL) {
731         save_interval = 10 * 60 * 1000; // 10 minutes
732     }
733     else {
734         save_interval = (int) strtol(value, NULL, 10) * 60 * 1000;
735         save_interval = (save_interval <= 0) ? 10 * 60 * 1000 : save_interval;
736     }
737     if (NULL != value)
738       free((void*) value);
739     
740     return(save_interval);
741 }
742
743 void
744 SendMsgDialog::setInclMsgHnd(DtMail::Message *msg, Boolean text)
745 {
746     _inclMsgHandle = msg;
747     _inclMsgHasText = text;
748 }
749
750 void
751 SendMsgDialog::setFirstBPHandled(Boolean handle)
752 {
753     _firstBPHandled = handle;
754 }
755
756 void
757 SendMsgDialog::setHeader(const char * name, const char * value)
758 {
759     // See if this header is in the list. If so, set the widget for
760     // it.
761     //
762     int slot = lookupHeader(name);
763     if (slot < 0) {
764         return;
765     }
766
767     HeaderList * hl = _header_list[slot];
768     if (hl->show == SMD_NEVER) {
769         // The user removed this header via props.
770         //
771         return;
772     }
773
774     if (hl->field_widget) {
775         XtVaSetValues(hl->field_widget,
776                       XmNvalue, value,
777                       NULL);
778         if (hl->show == SMD_HIDDEN) {
779             changeHeaderState(name);
780         }
781     }
782 }
783
784 void
785 SendMsgDialog::setHeader(const char * name, DtMailValueSeq & value)
786 {
787     if (value.length() == 0) {
788         return;
789     }
790
791     if (value.length() == 1) {
792         setHeader(name, *(value[0]));
793     }
794     else {
795         int max_len = 0;
796         for (int slen = 0; slen < value.length(); slen++) {
797             max_len += strlen(*(value[slen]));
798         }
799
800         char * new_str = new char[max_len + (value.length() * 3)];
801
802         strcpy(new_str, "");
803         for (int copy = 0; copy < value.length(); copy++) {
804             if (copy != 0) {
805                 strcat(new_str, " ");
806             }
807
808             strcat(new_str, *(value[copy]));
809         }
810
811         setHeader(name, new_str);
812         delete [] new_str;
813     }
814 }
815
816 void
817 SendMsgDialog::loadHeaders(DtMail::Message * input,
818                            DtMailBoolean load_all)
819 {
820     // We are going to go through every header in the message.
821     // If it is not one of the headers we block, then we will
822     // load it into the header pane, depending on the setting of
823     // load_all. If true, then we load every header we allow and
824     // create new dynamic headers as necessary. If load_all is false,
825     // then we only load headers that already are available in the
826     // memory pane.
827     //
828     DtMailEnv error;
829     DtMail::Message * msg = (input ? input : _msgHandle);
830     DtMail::Envelope * env = msg->getEnvelope(error);
831     DtMailHeaderHandle hnd;
832
833     int hcount = 0;
834     char * name;
835     DtMailValueSeq value;
836
837     for (hnd = env->getFirstHeader(error, &name, value);
838          error.isNotSet() && hnd;
839          hnd = env->getNextHeader(error, hnd, &name, value)) {
840
841         // Always ignore the Unix from line.
842         //
843         if (hcount == 0 &&
844             strcmp(name, "From") == 0) {
845             free(name);
846             value.clear();
847             hcount += 1;
848             continue;
849         }
850
851         hcount += 1;
852
853         // See if the name is one we always block.
854         //
855         if (block(name)) {
856             free(name);
857             value.clear();
858             continue;
859         }
860
861         int slot = lookupHeader(name);
862         if (slot < 0) {
863             // We dont have a place for this information. We may need
864             // to create a new header.
865             //
866             if (load_all) {
867                 HeaderList *hl = new HeaderList;
868                 hl->label = strdup(name);
869                 hl->header = strdup(name);
870                 hl->show = SMD_HIDDEN;
871                 _header_list.append(hl);
872                 createHeaders(_header_form);
873                 doDynamicHeaderMenus();
874             }
875             else {
876                 free(name);
877                 value.clear();
878                 continue;
879             }
880         }
881
882         setHeader(name, value);
883         free(name);
884         value.clear();
885     }
886 }
887
888 void
889 SendMsgDialog::storeHeaders(DtMail::Message * input)
890 {
891     DtMail::Message * msg = (input ? input : _msgHandle);
892     DtMailEnv error;
893     DtMail::Envelope * env = msg->getEnvelope(error);
894
895     // Walk through the headers. Fetch the strings from the ones
896     // that are visible to the user and stuff them into the
897     // message.
898     //
899     for (int scan = 0; scan < _header_list.length(); scan++) {
900         HeaderList * hl = _header_list[scan];
901         if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
902             continue;
903         }
904
905         char * value;
906         XtVaGetValues(hl->field_widget,
907                       XmNvalue, &value,
908                       NULL);
909         // If the header has a value, we want to set the value
910         // in the back end.  Otherwise, we don't want to send
911         // out a blank header, so we remove it.
912         if (strlen(value) > 0) {
913             env->setHeader(error, hl->header, DTM_TRUE, value);
914         }
915         else {
916             env->removeHeader(error, hl->header);
917         }
918         XtFree(value);
919     }
920 }
921
922 void
923 SendMsgDialog::changeHeaderState(const char * name)
924 {
925     int slot = lookupHeader(name);
926     if (slot < 0) {
927         return;
928     }
929
930     HeaderList * hl = _header_list[slot];
931     if (hl->show == SMD_ALWAYS || hl->show == SMD_NEVER) {
932         return;
933     }
934
935     // If the user is trying to remove a header with a value other than
936     // the default, we should at least ask.
937     //
938     if (hl->show == SMD_SHOWN) {
939         char * value;
940         XtVaGetValues(hl->field_widget,
941                       XmNvalue, &value,
942                       NULL);
943         if (strlen(value) > 0) {
944             if (!hl->value || strcmp(value, hl->value) != 0) {
945                 char *buf = new char[256];
946                 sprintf(buf, 
947                         GETMSG(DT_catd, 2, 17, 
948                                 "You have edited \"%s\". Delete anyway?"), 
949                         hl->label);
950                 _genDialog->setToWarningDialog(GETMSG(DT_catd, 3, 71,
951                                                        "Mailer"),
952                                                buf);
953                 char * helpId = DTMAILHELPERROR;
954                 int answer = _genDialog->post_and_return(
955                                             GETMSG(DT_catd, 3, 72, "OK"), 
956                                             GETMSG(DT_catd, 3, 73, "Cancel"), 
957                                             helpId);
958                 delete [] buf;
959                 if (answer == 2) {
960                     free(value);
961                     return;
962                 }
963             }
964         }
965
966         free(value);
967     }
968
969     // Now we need to toggle the current state of the header.
970     //
971     char *label = new char[100];
972     if (hl->show == SMD_SHOWN) {
973         XtUnmanageChild(hl->form_widget);
974         hl->show = SMD_HIDDEN;
975         sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
976
977     }
978     else {
979         XtManageChild(hl->form_widget);
980         hl->show = SMD_SHOWN;
981         sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
982     }
983
984     justifyHeaders();
985     reattachHeaders();
986
987     // Change the label on the menu item.
988     //
989     strcat(label, hl->label);
990     strcat(label, ":");
991     char *button_name = new char[100];
992     sprintf(button_name, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
993     strcat(button_name, hl->label);
994     strcat(button_name, ":");
995
996     _menuBar->changeLabel(_format_menu, button_name, label);
997     delete [] label;
998     delete [] button_name;
999 }
1000
1001 void
1002 SendMsgDialog::setStatus(const char * str)
1003 {
1004     char *tmpstr = strdup(str);
1005     XmString label = XmStringCreateLocalized(tmpstr);
1006
1007     XtVaSetValues(_status_text,
1008                   XmNlabelString, label,
1009                   NULL);
1010
1011     XmUpdateDisplay(baseWidget());
1012     XmStringFree(label);
1013     free(tmpstr);
1014 }
1015
1016 void
1017 SendMsgDialog::clearStatus(void)
1018 {
1019     setStatus(" ");
1020 }
1021
1022 Boolean
1023 SendMsgDialog::isMsgValid(void)
1024 {
1025     if (!_msgHandle)
1026         return FALSE;
1027     else
1028         return TRUE;
1029 }
1030
1031
1032 // Sendmail is exed'd and this parent process returns immediately.  When
1033 // the sendmail child exits, this function is called with the pid of the 
1034 // child and its status.
1035 void
1036 SendMsgDialog::sendmailErrorProc (int, int status, void *data)
1037 {
1038     SendMsgDialog *smd = (SendMsgDialog *)data;
1039     char *helpId = NULL;
1040     char *buf = new char[2048];
1041
1042     smd->_first_time = TRUE;
1043     smd->_takeDown = FALSE;
1044
1045     // pid is the child process (sendmail) id
1046     // status is the exit status of the child process
1047     // data is any extra data associated with the child process
1048  
1049     switch (status) {
1050         case DTME_NoError:
1051             // The mail was successfully sent so return the compose
1052             // window to the cache and then return.
1053             smd->_send_button->activate();
1054             smd->_close_button->activate();
1055             smd->goAway(FALSE);
1056             return;
1057
1058         case DTME_BadMailAddress:
1059             /* NL_COMMENT
1060              * There was an error in one or more of the email addresses.  
1061              * Ask the user to type in a valid address and try again.
1062              */
1063             sprintf(buf, "%s", GETMSG(DT_catd, 5, 5, 
1064 "Some of the addresses in the message are incorrect,\n\
1065 and do not refer to any known users in the system.\n\
1066 Please make sure all of the addresses are valid and try again."));
1067             helpId = DTMAILHELPBADADDRESS;
1068
1069             break;
1070  
1071         case DTME_NoMemory:
1072             /* NL_COMMENT
1073              * Mailer ran out of memory.  Ask the user to quit some other
1074              * applications so there will be more memory available.
1075              */
1076  
1077             sprintf(buf, "%s", GETMSG(DT_catd, 5, 6,
1078 "Mailer does not have enough memory\n\
1079 available to send this message.\n\
1080 Try quitting other applications and\n\
1081 resend this message."));
1082             helpId = DTMAILHELPNOMEMORY;
1083                 
1084             break;
1085
1086         case DTME_TransportFailed:
1087         default:
1088             /* NL_COMMENT
1089              * There was an error from the mail transport (sendmail).
1090              */
1091
1092             sprintf(buf, "%s", GETMSG(DT_catd, 5, 7,
1093 "An error occurred while trying to send your message.\n\
1094 Check to make sure the message was received.  If not,\n\
1095 you may have to resend this message."));
1096             helpId = DTMAILHELPTRANSPORTFAILED;
1097     }
1098
1099     // popup the compose window
1100     smd->manage();
1101     smd->_send_button->activate();
1102     smd->_close_button->activate();
1103
1104     // popup the error dialog
1105     smd->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1106                              (char *) buf);
1107     smd->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1108     delete [] buf;
1109 }
1110
1111
1112 void
1113 SendMsgDialog::send_message(const char * trans_impl, int trans_type)
1114 {
1115     DtMailEnv mail_exec_error;
1116     DtMailOperationId id;
1117     DtMail::Transport * mail_transport;
1118     DtMail::Session * d_session = theRoamApp.session()->session();
1119     DtMailEditor *editor = this->get_editor();
1120     AttachArea *attachArea = editor->attachArea();
1121     int numPendingActions, answer;
1122     char *helpId = NULL;
1123     char *buf = new char[2048];
1124     static int first_time = 1;
1125
1126     //
1127     // Check to see if we are already trying to send from this
1128     // SendMsgDialog.  If we are, we don't want to do it again.
1129     if (_already_sending) {
1130         delete [] buf;
1131         return;
1132     } else {
1133         _already_sending = TRUE;
1134     }
1135
1136     // First remove (unmap) the window;
1137     // send the message.
1138     // If you do it in the reverse order, users get confused coz
1139     // the window remains behind for a couple seconds after hitting 
1140     // "Send" and it ...
1141     this->unmanage();
1142     _send_button->deactivate();
1143     _close_button->deactivate();
1144
1145     mail_exec_error.clear();
1146     
1147
1148     // Check if message has addressees.  If it doesn't, what sense does
1149     // it make to Send it? 
1150
1151     if (!this->hasAddressee()) {
1152         // Message has no valid addressee.  Pop up error dialog.
1153
1154         sprintf(buf, "%s", GETMSG(DT_catd, 5, 8,
1155                  "Try Send after specifying  recipient(s) of the message in \nthe To:, Cc:, or Bcc: fields."));
1156
1157         helpId = DTMAILHELPNEEDADDRESSEE;
1158
1159         // Need help tag for above HelpID.
1160         
1161         // popup the compose window
1162         this->manage();
1163         _send_button->activate();
1164         _close_button->activate();
1165
1166         _genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1167                                      (char *) buf);
1168         _genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"), helpId);
1169
1170         // Reset the flag before we return.
1171         _already_sending = FALSE;
1172
1173         delete [] buf;
1174         return;
1175     }
1176
1177     // Since we are Send-ing, the SMD can be taken down later without
1178     // checking for dirty...
1179
1180     _first_time = TRUE;
1181     _takeDown = TRUE;
1182
1183     stopAutoSave();
1184     
1185     // Just get text from text widget; attachment BPs are filled
1186     // already when they are included/forwarded/added.
1187     
1188     updateMsgHnd();
1189     
1190     // Check if there are any pending attachments (open, print....)
1191     // If there are, pop up the dialog.
1192     // If the user wants to Send the message as is, continue with  the
1193     // submission process. 
1194     // If the user opted to Cancel, then return.
1195     
1196     
1197     numPendingActions = attachArea->getNumPendingActions();
1198     sprintf(buf, "%s", GETMSG(
1199                         DT_catd, 
1200                         3, 
1201                         77, 
1202                         "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." ));
1203     
1204     while (numPendingActions != 0) {
1205         // popup the compose window
1206         this->manage();
1207         _send_button->activate();
1208         _close_button->activate();
1209
1210         /* NL_COMMENT
1211          * The user tried to send a messages without saving changes in
1212          * some open attachments.  This warning makes sure that is what
1213          * the user intended.
1214          */
1215         
1216         _genDialog->setToQuestionDialog(
1217                         GETMSG(DT_catd, 5, 1, "Mailer"),
1218                         buf);
1219         helpId = DTMAILHELPPENDINGACTIONS;
1220         
1221         answer = _genDialog->post_and_return(helpId);
1222         
1223         if (answer == 1) {
1224             // OK selected
1225             numPendingActions = 0;
1226             this->unmanage();
1227             _send_button->deactivate();
1228             _close_button->deactivate();
1229         }
1230         else if (answer == 2) {
1231             // Cancel selected
1232             // Reset the flag before we return.
1233             _already_sending = FALSE;
1234             delete [] buf;
1235             return;
1236         }
1237     }
1238    
1239     // Determine which transport mechanism will be used.
1240     if ( trans_type ) {   // Default
1241         // Only register XtAppAddInput once
1242         if (first_time)
1243         {
1244             // Create the pipe between the RFCTransport::childHandler
1245             // and XtAppAddInput
1246             if (pipe(_transfds) < 0) {
1247                 // If this failed, make sure we try to initialize again later.
1248                 mail_exec_error.setError(DTME_NoMemory);        
1249                 popupMemoryError (mail_exec_error);
1250
1251                 // Reset the flag before we return.
1252                 _already_sending = FALSE;
1253                 delete [] buf;
1254                 return;
1255             }
1256
1257             // Call ourproc when input is available on _transfds[0]
1258             XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1259                 _transfds[0], (XtPointer)XtInputReadMask,
1260                 (XtInputCallbackProc)
1261                     (theRoamApp.default_transport()->getSendmailReturnProc()),
1262                 NULL);
1263             first_time = 0;
1264         }
1265
1266         // Tell the transport where the callback is
1267         theRoamApp.default_transport()->initTransportData( _transfds,
1268                 &(SendMsgDialog::sendmailErrorProc), this);
1269         id = theRoamApp.default_transport()->submit(mail_exec_error,
1270                 _msgHandle, _log_msg);
1271
1272     } else {
1273         // Construct transport
1274         mail_transport = d_session->transportConstruct(mail_exec_error,
1275                 trans_impl, RoamApp::statusCallback, this);
1276
1277         // Only register XtAppAddInput once
1278         if (first_time)
1279         {
1280             // Create the pipe between the RFCTransport::childHandler
1281             // and XtAppAddInput
1282             if (pipe(_transfds) < 0) {
1283                 // If this failed, make sure we try to initialize again later.
1284                 mail_exec_error.setError(DTME_NoMemory);        
1285                 popupMemoryError (mail_exec_error);
1286
1287                 // Reset the flag before we return.
1288                 _already_sending = FALSE;
1289                 delete [] buf;
1290                 return;
1291             }
1292
1293             // Call ourproc when input is available on _transfds[0]
1294             XtAppAddInput(XtWidgetToApplicationContext(this->_main_form),
1295                 _transfds[0], (XtPointer)XtInputReadMask,
1296                 (XtInputCallbackProc)(mail_transport->getSendmailReturnProc()),
1297                 NULL);
1298             first_time = 0;
1299         }
1300
1301         // Tell the transport where the callback is
1302         mail_transport->initTransportData(_transfds,
1303                 &(SendMsgDialog::sendmailErrorProc), this);
1304         id = mail_transport->submit(mail_exec_error, _msgHandle, _log_msg);
1305     }
1306   
1307     popupMemoryError (mail_exec_error);
1308
1309     // Reset the flag before we return.
1310     _already_sending = FALSE;
1311
1312     delete [] buf;
1313 }
1314
1315 void
1316 SendMsgDialog::popupMemoryError(DtMailEnv &error)
1317 {
1318     char *helpId = NULL;
1319     char *buf = new char[2048];
1320
1321     _takeDown = FALSE;
1322
1323     // Popup an error dialog if necessary. 
1324     if (error.isSet()) {
1325         if ((DTMailError_t)error == DTME_NoMemory) {
1326
1327             /* NL_COMMENT
1328              * Mailer ran out of memory.  Ask the user to quit some other
1329              * applications so there will be more memory available.
1330              */
1331             sprintf(buf, "%s", GETMSG(DT_catd, 5, 6,
1332 "Mailer does not have enough memory\n\
1333 available to send this message.\n\
1334 Try quitting other applications and\n\
1335 resend this message."));
1336             helpId = DTMAILHELPNOMEMORY;
1337          }
1338          else {
1339                 /* NL_COMMENT
1340              * An unidentifiable error happened during mail transport
1341              * Pop it up *as is* (need to update this function if so)
1342              */
1343             sprintf(buf, "%s", (const char *)error);
1344             helpId = DTMAILHELPERROR;
1345         }
1346
1347         // popup the compose window
1348         this->manage();
1349         _send_button->activate();
1350         _close_button->activate();
1351
1352         // popup the error dialog
1353         this->_genDialog->setToErrorDialog(GETMSG(DT_catd, 2, 21, "Mailer"),
1354                 (char *) buf);
1355         this->_genDialog->post_and_return(GETMSG(DT_catd, 3, 76, "OK"),
1356                 helpId);
1357     }   
1358     delete [] buf;
1359 }
1360
1361
1362 Widget 
1363 SendMsgDialog::createWorkArea ( Widget parent )
1364 {
1365     FORCE_SEGV_DECL(CmdInterface, ci);
1366     Widget send_form;
1367     
1368     // Create the parent form
1369     
1370     _main_form = XmCreateForm( parent, "Work_Area", NULL, 0 );
1371     XtVaSetValues(_main_form, XmNresizePolicy, XmRESIZE_NONE, NULL);
1372     
1373     printHelpId("form", _main_form);
1374     /* add help callback */
1375     XtAddCallback(_main_form, XmNhelpCallback, HelpCB, (void *)DTMAILCOMPOSEWINDOW);
1376     XtVaSetValues(_main_form, XmNallowResize, True, NULL);
1377     
1378     
1379     // Create the area for status messages.
1380     //
1381     _status_form = XtVaCreateManagedWidget("StatusForm",
1382                                            xmFormWidgetClass, _main_form,
1383                                            XmNtopAttachment, XmATTACH_FORM,
1384                                            XmNrightAttachment, XmATTACH_FORM,
1385                                            XmNrightOffset, 2,
1386                                            XmNleftAttachment, XmATTACH_FORM,
1387                                            XmNleftOffset, 2,
1388                                            NULL);
1389
1390     _status_text = XtVaCreateManagedWidget("StatusLabel",
1391                                            xmLabelWidgetClass, _status_form,
1392                                            XmNtopAttachment, XmATTACH_FORM,
1393                                            XmNbottomAttachment, XmATTACH_FORM,
1394                                            XmNrightAttachment, XmATTACH_FORM,
1395                                            XmNleftAttachment, XmATTACH_FORM,
1396                                            XmNalignment, XmALIGNMENT_BEGINNING,
1397                                            NULL);
1398
1399     clearStatus();
1400
1401     Widget s_sep = XtVaCreateManagedWidget("StatusSep",
1402                                            xmSeparatorGadgetClass,
1403                                            _main_form,
1404                                            XmNtopAttachment, XmATTACH_WIDGET,
1405                                            XmNtopWidget, _status_form,
1406                                            XmNleftAttachment, XmATTACH_FORM,
1407                                            XmNrightAttachment, XmATTACH_FORM,
1408                                            NULL);
1409     
1410     _header_form = XtVaCreateManagedWidget("HeaderArea",
1411                                            xmFormWidgetClass, _main_form,
1412                                            XmNtopAttachment, XmATTACH_WIDGET,
1413                                            XmNtopWidget, s_sep,
1414                                            XmNleftAttachment, XmATTACH_FORM,
1415                                            XmNrightAttachment, XmATTACH_FORM,
1416                                            NULL);
1417     printHelpId("header_form", _header_form);
1418
1419     createHeaders(_header_form);
1420     
1421     Widget sep1 = XtVaCreateManagedWidget("Sep1",
1422                                           xmSeparatorGadgetClass,
1423                                           _main_form,
1424                                           XmNtopAttachment, XmATTACH_WIDGET,
1425                                           XmNtopWidget, _header_form,
1426                                           XmNtopOffset, 1,
1427                                           XmNrightAttachment, XmATTACH_FORM,
1428                                           XmNleftAttachment, XmATTACH_FORM,
1429                                           NULL);
1430
1431     // Create the editor and attach it to the header_form
1432     
1433     _my_editor = new DtMailEditor(_main_form, this);
1434     
1435     _my_editor->initialize();
1436     _my_editor->attachArea()->setOwnerShell(this);
1437     _my_editor->setEditable(TRUE);
1438     _my_editor->manageAttachArea();
1439     
1440     // Create a RowCol widget that contains buttons
1441     
1442     send_form =  XtCreateManagedWidget("SendForm", 
1443                                        xmFormWidgetClass, _main_form, NULL, 0);
1444     
1445     
1446     // Create the Send and Close buttons as children of rowCol
1447     
1448     _send_button = new SendCmd ( "Send", 
1449                                  GETMSG(DT_catd, 1, 230, "Send"), 
1450                                  TRUE, 
1451                                  this, 
1452                                  1);
1453     ci  = new ButtonInterface (send_form, _send_button);
1454     
1455     XtVaSetValues(ci->baseWidget(),
1456                   XmNleftAttachment, XmATTACH_FORM,
1457                   XmNleftOffset, OFFSET,
1458                   XmNbottomAttachment, XmATTACH_FORM,
1459                   XmNbottomOffset, 5,
1460                   NULL);
1461     
1462     Widget send_bw = ci->baseWidget();
1463     XtManageChild(send_bw);
1464     ci->manage();
1465     
1466     _close_button = new CloseCmd (
1467                                   "Close",
1468                                   GETMSG(DT_catd, 1, 118, "Close"),
1469                                   TRUE, 
1470                                   this->baseWidget(), 
1471                                   this );
1472     ci = new ButtonInterface (send_form, _close_button);
1473     XtVaSetValues(ci->baseWidget(),
1474                   XmNleftOffset, 20,
1475                   XmNleftAttachment, XmATTACH_WIDGET,
1476                   XmNleftWidget, send_bw,
1477                   XmNbottomAttachment, XmATTACH_FORM,
1478                   XmNbottomOffset, 5,
1479                   NULL);
1480     
1481     XtManageChild(ci->baseWidget());
1482     ci->manage();
1483     
1484     // Now attach the editor to the form and to the rowCol
1485     // And the rowCol to the bottom of the form.
1486     // We need this attachment ordering so that resizes always
1487     // get transferred to the editor.
1488     
1489     Widget wid = _my_editor->container();
1490     XtVaSetValues(wid,
1491                   XmNleftAttachment, XmATTACH_FORM,
1492                   XmNrightAttachment, XmATTACH_FORM,
1493                   XmNtopAttachment, XmATTACH_WIDGET,
1494                   XmNtopWidget, sep1,
1495                   XmNtopOffset, 1,
1496                   XmNbottomAttachment, XmATTACH_WIDGET,
1497                   XmNbottomWidget, send_form,
1498                   NULL);
1499     
1500     XtVaSetValues(send_form,
1501                   XmNbottomAttachment, XmATTACH_FORM,
1502                   NULL);
1503     
1504     
1505     // Set focus 
1506     HeaderList * hl = _header_list[0];
1507     (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
1508     
1509     // Set the title to be New Message
1510     //char *ttl = GETMSG(DT_catd, 1, 119, "New Message");
1511     //this->setTitle(ttl);
1512     //this->setIconTitle(ttl);
1513     
1514     XtManageChild(_main_form);
1515     return _main_form;
1516 }
1517
1518
1519 void
1520 SendMsgDialog::createHeaders(Widget header_form)
1521 {
1522     Widget      previous_form = NULL;
1523     char        *field_name = new char[50];
1524
1525     for (int header = 0; header < _header_list.length(); header++) {
1526         HeaderList * hl = _header_list[header];
1527
1528         // We use SMD_NEVER to indicate the header has disappeared from
1529         // the list.
1530         //
1531         if (hl->show == SMD_NEVER) {
1532             continue;
1533         }
1534
1535         // If the widgets already exist, then simply manage them.
1536         if (hl->form_widget) {
1537             previous_form = hl->form_widget;
1538             if (hl->value)
1539                 XtVaSetValues(hl->field_widget,
1540                           XmNvalue, hl->value,
1541                           NULL);
1542             else
1543                 XtVaSetValues(hl->field_widget,
1544                           XmNvalue, "",
1545                           NULL);
1546             continue;
1547         }
1548
1549         if (previous_form == NULL) {
1550             // Create a form, attaching it to the top. This is a special
1551             // case. Other lines are created attached to the form above
1552             // them.
1553             strcpy(field_name, "form_");
1554             strncat(field_name, hl->label, 45);
1555             field_name[strlen(hl->label) + 5] = 0;
1556             hl->form_widget =
1557                 XtVaCreateWidget(field_name,
1558                                  xmFormWidgetClass,
1559                                  header_form,
1560                                  XmNtopAttachment, XmATTACH_FORM,
1561                                  XmNtopOffset, 2,
1562                                  XmNleftAttachment, XmATTACH_FORM,
1563                                  XmNleftOffset, 3,
1564                                  XmNrightAttachment, XmATTACH_FORM,
1565                                  XmNrightOffset, 3,
1566                                  NULL);
1567
1568         }
1569         else {
1570             strcpy(field_name, "form_");
1571             strncat(field_name, hl->label, 45);
1572             field_name[strlen(hl->label) + 5] = 0;
1573             hl->form_widget =
1574                 XtVaCreateWidget(field_name,
1575                                  xmFormWidgetClass,
1576                                  header_form,
1577                                  XmNtopAttachment, XmATTACH_WIDGET,
1578                                  XmNtopWidget, previous_form,
1579                                  XmNleftAttachment, XmATTACH_FORM,
1580                                  XmNleftOffset, 3,
1581                                  XmNrightAttachment, XmATTACH_FORM,
1582                                  XmNrightOffset, 3,
1583                                  NULL);
1584
1585         }
1586
1587         // The label will be to the left of the form.
1588         //
1589         strcpy(field_name, hl->label);
1590         strcat(field_name, ":");
1591         XmString label = XmStringCreateLocalized(field_name);
1592         hl->label_widget =
1593             XtVaCreateManagedWidget(hl->label,
1594                                     xmLabelWidgetClass,
1595                                     hl->form_widget,
1596                                     XmNtopAttachment, XmATTACH_FORM,
1597                                     XmNbottomAttachment, XmATTACH_FORM,
1598                                     XmNleftAttachment, XmATTACH_FORM,
1599                                     XmNlabelString, label,
1600                                     NULL);
1601         XmStringFree(label);
1602         
1603         strcpy(field_name, "field_");
1604         strncat(field_name, hl->label, 43);
1605         field_name[strlen(hl->label) + 6] = 0;
1606         
1607         hl->field_widget =
1608             XtVaCreateManagedWidget(field_name,
1609                                     xmTextFieldWidgetClass,
1610                                     hl->form_widget,
1611                                     XmNtraversalOn,     True,
1612                                     XmNtopAttachment, XmATTACH_FORM,
1613                                     XmNrightAttachment, XmATTACH_FORM,
1614                                     XmNleftAttachment, XmATTACH_WIDGET,
1615                                     XmNleftWidget, hl->label_widget,
1616                                     NULL);
1617
1618         if (hl->show != SMD_HIDDEN) {
1619             XtManageChild(hl->form_widget);
1620         }
1621         else {
1622             XtVaSetValues(hl->form_widget,
1623                           XmNtopAttachment, XmATTACH_NONE,
1624                           NULL);
1625         }
1626
1627         XtAddCallback(hl->field_widget,
1628                       XmNactivateCallback,
1629                       header_form_traverse,
1630                       NULL);
1631
1632         XtAddCallback(hl->field_widget,
1633                       XmNvalueChangedCallback,
1634                       headerValueChanged,
1635                       this);
1636
1637         if (hl->value) {
1638             XtVaSetValues(hl->field_widget,
1639                           XmNvalue, hl->value,
1640                           NULL);
1641         }
1642
1643         previous_form = hl->form_widget;
1644     }
1645
1646     justifyHeaders();
1647     delete [] field_name;
1648 }
1649
1650 void
1651 SendMsgDialog::doDynamicHeaderMenus(void)
1652 {
1653     // This is really a pain, but we have to blow away the list to
1654     // build another one. This could probably be done more efficiently,
1655     // but we wont try to figure out how right now.
1656     //
1657     if (_format_cmds) {
1658         _menuBar->removeOnlyCommands(_format_menu, _format_cmds);
1659     }
1660
1661     _format_cmds = new CmdList("DynamicFormatCommands", "DynamicFormatCommands");
1662
1663     // Only put on commands that are shown or hidden. The items that
1664     // are always are never should not be presented to the user as
1665     // an option to change.
1666     //
1667     char *label = new char[100];
1668
1669     for (int h = 0; h < _header_list.length(); h++) {
1670         HeaderList * hl = _header_list[h];
1671
1672         switch(hl->show) {
1673           case SMD_SHOWN:
1674             sprintf(label, "%s ", GETMSG(DT_catd, 1, 229, "Delete"));
1675             break;
1676
1677           case SMD_HIDDEN:
1678             sprintf(label, "%s ", GETMSG(DT_catd, 1, 228, "Add"));
1679             break;
1680
1681           default:
1682             continue;
1683         }
1684
1685         strcat(label, hl->label);
1686         strcat(label, ":");
1687
1688         char * priv_label = strdup(label);
1689
1690         Cmd * new_cmd = new HideShowCmd(priv_label, priv_label,
1691                                         1, this, hl->label);
1692
1693         // Add the commands one at a time with addCommand() vs. all
1694         // at once with addCommands(). That way new commands will
1695         // be created instead of reusing old ones.
1696         _menuBar->addCommand(_format_menu, new_cmd);
1697         _format_cmds->add(new_cmd);
1698     }
1699     delete [] label;
1700 }
1701
1702 void
1703 bogus_cb(void *)
1704 {
1705 // Should theInfoDialogManager be destroyed here ???
1706 }
1707
1708 #ifdef DEAD_WOOD
1709 void
1710 SendMsgDialog::open_att_cb( void *clientData, char *selection )
1711 {
1712     SendMsgDialog *obj = (SendMsgDialog *)clientData;
1713     
1714     obj->open_att(selection);
1715 }
1716
1717 void
1718 SendMsgDialog::open_att( char *)  // arg is char *selection
1719 {
1720 }
1721 #endif /* DEAD_WOOD */
1722
1723 void
1724 SendMsgDialog::include_file_cb( void *client_data, char *selection )
1725 {
1726     SendMsgDialog *obj = (SendMsgDialog *)client_data;
1727     obj->include_file(selection);
1728     XtFree(selection);
1729 }
1730
1731 void
1732 SendMsgDialog::include_file(
1733                             char *selection
1734                             )
1735 {
1736     FILE *fp;
1737     char *buf = new char[MAXPATHLEN];
1738     
1739     // I don't need to open the file to see if it's readable if loadFile()
1740     // returns error status.
1741     if ( (fp = fopen(selection, "r")) == NULL ) {
1742         sprintf(buf, GETMSG(DT_catd, 2, 18, "Error: Cannot include file %s"), 
1743                 selection);
1744         theInfoDialogManager->post(
1745                                    "Mailer", 
1746                                    buf, 
1747                                    (void *)this->_file_include, 
1748                                    bogus_cb);
1749     } else {
1750         fclose(fp);
1751         this->_my_editor->textEditor()->append_to_contents("\n", 2);
1752         this->_my_editor->textEditor()->append_at_cursor(selection);
1753         this->_my_editor->textEditor()->append_to_contents("\n", 2);
1754     }
1755     delete [] buf;
1756 }
1757
1758 int
1759 SendMsgDialog::get_confirm_attachment_threshold()
1760 {
1761     DtMailEnv        error;
1762     DtMail::Session *m_session = theRoamApp.session()->session();
1763     const char      *value = NULL;
1764     int              threshold;
1765
1766     m_session->mailRc(error)->getValue(error, "confirmattachments", &value);
1767     if (error.isSet()) return 0;
1768
1769     m_session->mailRc(error)->getValue(error, "confirmattachmentthreshold",
1770                                         &value);
1771     if (error.isNotSet() && NULL!=value)
1772       threshold = 1024 * atoi(value);
1773     else
1774       threshold = 1024 * 64;
1775     
1776     return threshold;
1777 }
1778
1779 int
1780 SendMsgDialog::confirm_add_attachment(char *file, int size)
1781 {
1782     char *buf = new char[BUFSIZ];
1783     char *format;
1784     int   answer;
1785         
1786     format =
1787       GETMSG(DT_catd, 1, 263,
1788              "The attachment '%s' is %d kilobytes.\nAdd as attachment?");
1789     sprintf(buf, format, file, size/1024);
1790     _genDialog->setToQuestionDialog(GETMSG(DT_catd, 5, 2, "Mailer"), buf);
1791     answer = _genDialog->post_and_return(NULL);
1792     return (answer==1);
1793 }
1794
1795 void
1796 SendMsgDialog::add_att_cb( void *client_data, char *selection )
1797 {
1798     SendMsgDialog *obj = (SendMsgDialog *)client_data;
1799     obj->add_att(selection);
1800     if (NULL != selection)
1801       free(selection);
1802 }
1803
1804 void
1805 SendMsgDialog::add_att(char *file)
1806 {
1807     struct stat statbuf;
1808
1809     if (-1 != stat(file, &statbuf) && _confirm_attachment_threshold &&
1810         _confirm_attachment_threshold < statbuf.st_size)
1811     {
1812         if (! confirm_add_attachment(file, statbuf.st_size)) return;
1813     }
1814
1815     // Activate Attachment menu???
1816     this->get_editor()->attachArea()->
1817       addAttachment(_msgHandle, _lastAttBP, file, NULL);
1818     this->setLastAttBP();
1819     this->activate_default_attach_menu();
1820
1821     // This will manage the attach pane too.
1822     ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1823 }
1824
1825 void
1826 SendMsgDialog::add_att(char *name, DtMailBuffer buf)
1827 {
1828     if (_confirm_attachment_threshold &&
1829         _confirm_attachment_threshold < buf.size)
1830     {
1831         if (! confirm_add_attachment("", buf.size)) return;
1832     }
1833
1834     this->get_editor()->attachArea()->
1835       addAttachment(_msgHandle, _lastAttBP, name, buf);
1836     this->setLastAttBP();
1837     this->activate_default_attach_menu();
1838     
1839     // This will manage the attach pane too.
1840     ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, TRUE);
1841 }
1842
1843 void
1844 SendMsgDialog::add_att(DtMailBuffer buf)
1845 {
1846     char *name = NULL;
1847     add_att(name, buf);
1848 }
1849
1850 void
1851 SendMsgDialog::save_att_cb( void *client_data, char *selection )
1852 {
1853     SendMsgDialog *obj = (SendMsgDialog *)client_data;
1854     
1855     obj->save_selected_attachment(selection);
1856     
1857 }
1858
1859 void
1860 SendMsgDialog::save_selected_attachment(
1861                                         char *selection
1862                                         )
1863 {
1864     DtMailEnv mail_error;
1865     
1866     mail_error.clear();
1867     
1868     AttachArea *attarea = this->get_editor()->attachArea();
1869     Attachment *attachment = attarea->getSelectedAttachment(); 
1870     
1871     // Get selected attachment, if none selected, then return.
1872     if ( attachment == NULL ) {
1873         // Let User know that no attachment has been selected???
1874         int answer = 0;
1875         char *helpId = NULL;
1876         
1877         
1878         _genDialog->setToErrorDialog(
1879                                      GETMSG(DT_catd, 1, 120, "Mailer"),    
1880                                      GETMSG(DT_catd, 2, 19, "An attachment needs to be selected before issuing the\n\"Save As\" command to save to a file.") );
1881         helpId = DTMAILHELPSELECTATTACH;
1882         answer = _genDialog->post_and_return(
1883                                              GETMSG(DT_catd, 3, 74, "OK"), helpId );
1884         return;
1885     }
1886     
1887     // Save selected attachments.
1888     attachment->saveToFile(mail_error, selection);
1889     if ( mail_error.isSet() ) {
1890         // Let User know error condition???
1891         return;
1892     }
1893 }
1894
1895 void
1896 SendMsgDialog::propsChanged(void)
1897 {
1898     DtMail::Session *m_session = theRoamApp.session()->session();
1899     const char * value = NULL;
1900     DtMailEnv error;
1901
1902     enableWorkAreaResize();
1903
1904     m_session->mailRc(error)->getValue(error, "hideattachments", &value);
1905     if (value) {
1906         if (_show_attach_area) {
1907                 _show_attach_area = FALSE;
1908                 this->hideAttachArea();
1909         }
1910     }
1911     else 
1912         if (!_show_attach_area) {
1913                 _show_attach_area = TRUE;
1914                 this->showAttachArea();
1915         }
1916     if (NULL != value)
1917       free((void*) value);
1918
1919     _confirm_attachment_threshold = get_confirm_attachment_threshold();
1920
1921     // Reset Log State
1922     const char * logfile = NULL;
1923     m_session->mailRc(error)->getValue(error, "record", &logfile);
1924     if (logfile == NULL)
1925         _file_log->deactivate();
1926     else
1927         _file_log->activate();
1928
1929     _my_editor->textEditor()->update_display_from_props();
1930
1931     m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
1932     if (logfile == NULL || error.isNotSet()) {
1933         // logfile is not specified or "dontlogmessages" is TRUE
1934         setLogState(DTM_FALSE);
1935         ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
1936     } else {
1937         // logfile is specified and "dontlogmessages" is FALSE
1938         setLogState(DTM_TRUE);
1939         ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
1940     }
1941
1942     if (NULL != logfile)
1943       free((void*) logfile);
1944     if (NULL != value)
1945       free((void*) value);
1946
1947     value = NULL;
1948     m_session->mailRc(error)->getValue(error, "templates", &value);
1949     if  ( (value == NULL && _templateList != NULL) ||
1950           (value != NULL &&
1951           (_templateList == NULL || strcmp(value, _templateList)) != 0) ) {
1952         // Template list has changed
1953         if (_templateList != NULL)
1954                 free (_templateList);
1955         if (value != NULL && *value != '\0')
1956                 _templateList = strdup(value);
1957         else
1958                 _templateList = NULL;
1959         createFormatMenu();
1960     }
1961     if (NULL != value)
1962       free((void*) value);
1963
1964     // Alias Popup Menus
1965     DtVirtArray<PropStringPair*>        *newAliases;
1966     Boolean                             aliasesChanged = FALSE;
1967     
1968     newAliases = new DtVirtArray<PropStringPair*> (10);
1969     createAliasList(newAliases);
1970     if (newAliases->length() == _aliasList->length())
1971     {
1972         int     length = newAliases->length();
1973         int     i = 0;
1974
1975         while (i<length && aliasesChanged==FALSE)
1976         {
1977             PropStringPair      *p1 = (*newAliases)[i];
1978             PropStringPair      *p2 = (*_aliasList)[i];
1979
1980             if ( strncmp(p1->label, p2->label, strlen(p2->label)) ||
1981                  strncmp(p1->value, p2->value, strlen(p2->value)) )
1982               aliasesChanged = TRUE;
1983
1984             i++;
1985         }
1986     }
1987     else
1988       aliasesChanged = TRUE;
1989     
1990     if (aliasesChanged == TRUE)
1991     {
1992         destroyAliasPopupMenus();
1993         destroyAliasList(_aliasList);
1994         _aliasList = newAliases;
1995         createAliasPopupMenus();
1996     }
1997     else
1998       destroyAliasList(newAliases);
1999
2000     disableWorkAreaResize();
2001 }
2002
2003 void
2004 SendMsgDialog::createMenuPanes()
2005 {
2006     CmdList *cmdList;
2007     Cardinal n = 0;
2008     DtMailEnv error;
2009     const char * value = NULL;
2010
2011     
2012     _separator = new SeparatorCmd( "Separator","Separator", TRUE );
2013     
2014     // File
2015     cmdList = new CmdList( "File", GETMSG(DT_catd, 1, 121, "File") );
2016     
2017     // Default directory is set below at the same time as the default
2018     // directory for att_add.
2019     _file_include   = new UnifiedSelectFileCmd (
2020                                  "Include...",
2021                                  GETMSG(DT_catd, 1, 122, "Include..."),
2022                                  GETMSG(DT_catd, 1, 123, "Mailer - Include"),
2023                                  GETMSG(DT_catd, 1, 124, "Include"),
2024                                  TRUE, 
2025                                  SendMsgDialog::include_file_cb, 
2026                                  this,
2027                                  this->baseWidget());
2028     
2029
2030     // Remap OK button to Include
2031     // XtVaSetValues(_file_include->fileBrowser, 
2032     //    XmNokLabelString, GETMSG(DT_catd,
2033     //    1, 77, "Include"), NULL);
2034     _file_save_as = new SaveAsTextCmd(
2035                               "Save As Text...",
2036                               GETMSG(DT_catd, 1, 125, "Save As Text..."),
2037                               GETMSG(DT_catd, 1, 126, "Mailer - Save As Text"),
2038                               TRUE,
2039                               get_editor()->textEditor(),
2040                               this,
2041                               baseWidget());
2042     
2043     _file_log = new LogMsgCmd ( 
2044                         "Log Message",
2045                         GETMSG(DT_catd, 1, 127, "Log Message"), TRUE, this);
2046     
2047     // 1 for default transport.
2048     
2049     _file_send = new SendCmd (
2050                  "Send",
2051                  GETMSG(DT_catd, 1, 117, "Send"), 
2052                  TRUE, 
2053                  this, 
2054                  1 );
2055     
2056     // Find out how many transports there are and build sub menu dynamically.
2057     DtMail::Session *d_session;
2058
2059     if ( theRoamApp.session() == NULL ) {
2060          MailSession *new_session = new MailSession(
2061                                                 error, 
2062                                                 theApplication->appContext());
2063          theRoamApp.setSession(new_session);
2064      }
2065
2066     CmdList *subcmdList1 = new CmdList (
2067                                         "Send As",
2068                                         GETMSG(DT_catd, 1, 128, "Send As") );
2069     
2070     d_session = theRoamApp.session()->session();
2071     const char **impls = d_session->enumerateImpls(error);
2072     
2073     for ( int impl = 0;  impls[impl];  impl++ ) {
2074         DtMailBoolean trans;
2075         d_session->queryImpl(error, impls[impl],
2076                              DtMailCapabilityTransport, &trans);
2077         if (!error.isSet() && trans == DTM_TRUE ) {
2078             _file_sendAs[_num_sendAs] = new SendCmd( strdup(impls[impl]), 
2079                                                      (char *)impls[impl],
2080                                                      TRUE, 
2081                                                      this, 
2082                                                      0 );
2083             subcmdList1->add( _file_sendAs[_num_sendAs] );
2084             _num_sendAs++;
2085         }
2086         // Assume an error means this query failed.  But keep going and
2087         // get the next transport.
2088     }
2089     
2090     _file_close = new CloseCmd ( 
2091                                  "Close",
2092                                  GETMSG(DT_catd, 1, 129, "Close"), 
2093                                  TRUE, 
2094                                  _menuBar->baseWidget(), 
2095                                  this );
2096     
2097     // Now build the menu
2098     
2099     cmdList->add( _file_include );
2100     cmdList->add( _file_save_as );
2101     cmdList->add( _file_log );
2102     cmdList->add( _separator );
2103     
2104     cmdList->add( _file_send );
2105 #if defined(USE_SEND_AS_MENU)
2106     cmdList->add( subcmdList1 );
2107 #endif
2108     cmdList->add( _separator );
2109     
2110     cmdList->add( _file_close );
2111     
2112     _menuBar->addCommands ( cmdList );
2113     delete subcmdList1;
2114     delete cmdList;
2115     
2116     // Edit
2117     
2118     cmdList = new CmdList( "Edit", GETMSG(DT_catd, 1, 130, "Edit") );
2119     
2120     _edit_undo = new EditUndoCmd ( "Undo",
2121                                         GETMSG(DT_catd, 1, 131, "Undo"),
2122                                         TRUE, this );
2123     _edit_cut = new EditCutCmd ( "Cut",
2124                                         GETMSG(DT_catd, 1, 132, "Cut"),
2125                                         TRUE, this );
2126     _edit_copy = new EditCopyCmd ( "Copy",
2127                                         GETMSG(DT_catd, 1, 133, "Copy"),
2128                                         TRUE, this );
2129     _edit_paste = new EditPasteCmd ( "Paste",
2130                                         GETMSG(DT_catd, 1, 134 , "Paste"), 
2131                                         TRUE, 
2132                                         this );
2133     
2134     // Begin Paste Special submenu
2135     subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2136     _edit_paste_special[0] = new EditPasteSpecialCmd (
2137                                                       "Bracketed",
2138                                                       GETMSG(DT_catd, 1, 136 , "Bracketed"),
2139                                                       TRUE, this, Editor::IF_BRACKETED
2140                                                       );
2141     subcmdList1->add(_edit_paste_special[0]);
2142     _edit_paste_special[1] = new EditPasteSpecialCmd (
2143                                                       "Indented",
2144                                                       GETMSG(DT_catd, 1, 137 , "Indented"), 
2145                                                       TRUE, this, Editor::IF_INDENTED );
2146     subcmdList1->add(_edit_paste_special[1]);
2147     // End Paste Special submenu
2148     
2149     _edit_clear = new EditClearCmd ( "Clear", GETMSG(DT_catd, 1, 138, "Clear"), 
2150                                      TRUE, 
2151                                      this );
2152     _edit_delete = new EditDeleteCmd ( "Delete", GETMSG(DT_catd, 1, 139, "Delete"), 
2153                                        TRUE, 
2154                                        this );
2155     _edit_select_all = new EditSelectAllCmd ( 
2156                                               "Select All",
2157                                               GETMSG(DT_catd, 1, 140, "Select All"), 
2158                                               TRUE, 
2159                                               this );
2160     _format_find_change = new FindChangeCmd ( 
2161                                               "Find/Change...",
2162                                               GETMSG(DT_catd, 1, 155, "Find/Change..."), 
2163                                               TRUE, 
2164                                               this );
2165     _format_spell = new SpellCmd (
2166                                   "Check Spelling...",
2167                                   GETMSG(DT_catd, 1, 156, "Check Spelling..."), 
2168                                   TRUE, 
2169                                   this );
2170
2171     cmdList->add( _edit_undo );
2172     cmdList->add( _separator );
2173     cmdList->add( _edit_cut );
2174     cmdList->add( _edit_copy );
2175     cmdList->add( _edit_paste );
2176     cmdList->add( subcmdList1 ); // Add Paste Special submenu
2177     cmdList->add( _separator );
2178     cmdList->add( _edit_clear );
2179     cmdList->add( _edit_delete );
2180     cmdList->add( _separator );
2181     cmdList->add( _edit_select_all );
2182     cmdList->add( _separator );
2183     cmdList->add( _format_find_change );
2184     /*
2185      *
2186      * SpellCheck is not supported by Base System for the multibyte language
2187      * currently. ( See dtpad's source ) So that this should be disabled.
2188      * See Defect 174873. (I should think this solution is not good one, but..)
2189      * What is the best way to check if I'm in MB or SB.....???
2190      *
2191      */
2192     if ( MB_CUR_MAX == 1 )
2193       cmdList->add( _format_spell );
2194
2195     _menuBar->addCommands ( cmdList );
2196     delete subcmdList1;
2197     delete cmdList;
2198
2199     // Alias Popup Menus
2200     if (NULL != _aliasList) delete _aliasList;
2201     _aliasList = new DtVirtArray<PropStringPair*> (10);
2202     createAliasList(_aliasList);
2203     createAliasPopupMenus();
2204
2205     // Compose Popup CmdList
2206     construct_text_popup();
2207     
2208     // Attachment
2209     
2210     cmdList = new CmdList(
2211                         "Attachments",
2212                         GETMSG(DT_catd, 1, 141, "Attachments"));
2213     
2214     _att_add   = new UnifiedSelectFileCmd (
2215                                 "Add File...",
2216                                 GETMSG(DT_catd, 1, 142, "Add File..."),
2217                                 GETMSG(DT_catd, 1, 143, "Mailer - Add"), 
2218                                 GETMSG(DT_catd, 1, 144, "Add"),
2219                                 TRUE, 
2220                                 SendMsgDialog::add_att_cb,
2221                                 this,
2222                                 this->baseWidget());
2223
2224     _att_save  = new SaveAttachCmd (
2225                                 "Save As...",
2226                                 GETMSG(DT_catd, 1, 145, "Save As..."),
2227                                 GETMSG(DT_catd, 1, 146,
2228                                         "Mailer - Attachments - Save As"),
2229                                 FALSE, 
2230                                 SendMsgDialog::save_att_cb,
2231                                 this,
2232                                 this->baseWidget());
2233     _att_delete = new DeleteAttachCmd (
2234                                 "Delete",
2235                                 GETMSG(DT_catd, 1, 147, "Delete"),
2236                                 FALSE, 
2237                                 this);
2238     _att_undelete = new UndeleteAttachCmd (
2239                                 "Undelete",
2240                                 GETMSG(DT_catd, 1, 148, "Undelete"),
2241                                 FALSE, 
2242                                 this);
2243     _att_rename = new RenameAttachCmd(
2244                                 "Rename",
2245                                 GETMSG(DT_catd, 1, 149, "Rename"),
2246                                 FALSE,
2247                                 this);
2248     
2249     _att_select_all = new SelectAllAttachsCmd( 
2250                                 "Select All",
2251                                 GETMSG(DT_catd, 1, 150, "Select All"), 
2252                                 this);
2253     
2254         /* NL_COMMENT
2255          * This is the label for a toggle item in a menu.  When the item
2256          * is set to "Show List", the Attachment List is mapped in the
2257          * Compose Window.  This message replaces message 151 in set 1.
2258          */
2259     _att_show_pane = new ShowAttachPaneCmd(
2260                                            "Show List",
2261                                            GETMSG(DT_catd, 1, 226, "Show List"),
2262                                            this
2263                                            );
2264     cmdList->add( _att_add );
2265     cmdList->add( _att_save );
2266     cmdList->add( _separator );
2267     
2268 //   subcmdList1 = new CmdList ( "Create", "Create" );
2269 //   // subcmdList1->add( att_audio );
2270 //   // subcmdList1->add( att_appt );
2271 //   cmdList->add( subcmdList1 );
2272 //   cmdList->add( _separator );
2273     
2274     cmdList->add( _att_delete );
2275     cmdList->add( _att_undelete );
2276     cmdList->add( _att_rename );
2277     cmdList->add( _att_select_all );
2278     cmdList->add(_att_show_pane);
2279     
2280     // Create a pulldown from the items in the list.  Retain a handle
2281     // to that pulldown since we need to dynamically add/delete entries 
2282     // to this menu based on the selection of attachments.
2283     
2284     _attachmentMenu = _menuBar->addCommands ( cmdList );
2285     construct_attachment_popup();
2286  
2287 //  delete subcmdList1;
2288     delete cmdList;
2289     
2290     value = NULL;
2291     d_session->mailRc(error)->getValue(error, "templates", &value);
2292     if (value != NULL && *value != '\0')
2293         _templateList = strdup(value);
2294     if (NULL != value)
2295       free((void*) value);
2296
2297     createFormatMenu();
2298     
2299     _overview = new OnAppCmd("Overview",
2300                                 GETMSG(DT_catd, 1, 71, "Overview"),
2301                                 TRUE, this);
2302     _tasks = new TasksCmd("Tasks", GETMSG(DT_catd, 1, 72, "Tasks"), 
2303                                 TRUE, this);
2304     _reference = new ReferenceCmd("Reference",
2305                                 GETMSG(DT_catd, 1, 73, "Reference"), 
2306                                 TRUE, this);
2307     _on_item = new OnItemCmd("On Item", GETMSG(DT_catd, 1, 74, "On Item"),
2308                                 TRUE, this);
2309     _using_help = new UsingHelpCmd("Using Help",
2310                                 GETMSG(DT_catd, 1, 75, "Using Help"), 
2311                                 TRUE, this);
2312     _about_mailer = new RelNoteCmd("About Mailer...",
2313                                 GETMSG(DT_catd, 1, 77, "About Mailer..."),
2314                                 TRUE, this);
2315     cmdList = new CmdList("Help", GETMSG(DT_catd, 1, 76, "Help"));
2316     cmdList->add(_overview);
2317     cmdList->add(_separator);
2318     cmdList->add(_tasks);
2319     cmdList->add(_reference);
2320     cmdList->add(_separator);
2321     cmdList->add(_on_item);
2322     cmdList->add(_separator);
2323     cmdList->add(_using_help);
2324     cmdList->add(_separator);
2325     cmdList->add(_about_mailer);
2326     _menuBar->addCommands(cmdList, TRUE);
2327     delete cmdList;
2328 }
2329
2330 void
2331 SendMsgDialog::construct_attachment_popup(void)
2332 {
2333    _attachmentPopupMenuList = new CmdList( "AttachmentsPopup", "AttachmentsPopup");
2334
2335     LabelCmd *title     = new LabelCmd (
2336                         "Mailer - Attachments",
2337                         GETMSG(DT_catd, 1, 158, "Mailer - Attachments"), TRUE);
2338     SeparatorCmd *separator = new SeparatorCmd( "Separator","Separator", TRUE );
2339
2340     _attachmentPopupMenuList->add(title);
2341     _attachmentPopupMenuList->add(separator);
2342     _attachmentPopupMenuList->add( _att_add );
2343     _attachmentPopupMenuList->add( _att_save );
2344     _attachmentPopupMenuList->add( _att_delete );
2345     _attachmentPopupMenuList->add( _att_undelete );
2346     _attachmentPopupMenuList->add( _att_select_all );
2347
2348     _menuPopupAtt = new MenuBar(_my_editor->attachArea()->getClipWindow(), 
2349                                         "RoamAttachmentPopup", XmMENU_POPUP);
2350     _attachmentPopupMenu = _menuPopupAtt->addCommands(_attachmentPopupMenuList, 
2351                                 FALSE, XmMENU_POPUP);
2352 }
2353
2354 void
2355 SendMsgDialog::construct_text_popup(void)
2356 {
2357    if (theApplication->bMenuButton() != Button3)
2358         return;
2359
2360    _textPopupMenuList = new CmdList( "TextPopup", "TextPopup");
2361
2362     LabelCmd *title     = new LabelCmd (
2363                         "Mailer - Compose",
2364                         GETMSG(DT_catd, 1, 159, "Mailer - Compose"), TRUE);
2365     SeparatorCmd *separator = new SeparatorCmd("Separator", "Separator", TRUE );
2366
2367     _textPopupMenuList->add(title);
2368     _textPopupMenuList->add(separator);
2369     _textPopupMenuList->add(_file_send);
2370     _textPopupMenuList->add( _edit_undo );
2371     _textPopupMenuList->add( _edit_cut );
2372     _textPopupMenuList->add( _edit_copy );
2373     _textPopupMenuList->add( _edit_paste );
2374 #ifdef __osf__
2375     // Work in progress from Mike. This adds the Paste Special to the
2376     // third mouse button in the compose area of a compose window.
2377     // Begin Paste Special submenu
2378     CmdList * subcmdList1 = new CmdList ( "Paste Special", GETMSG(DT_catd, 1, 135 , "Paste Special") );
2379     subcmdList1->add(_edit_paste_special[0]);
2380     subcmdList1->add(_edit_paste_special[1]);
2381     // End Paste Special submenu
2382     _textPopupMenuList->add( subcmdList1 ); // Add Paste Special submenu
2383     // (Either way) _textPopupMenuList->add( separator );
2384     _textPopupMenuList->add( _edit_clear );
2385 #endif
2386     _textPopupMenuList->add( _edit_delete );
2387     _textPopupMenuList->add( _edit_select_all );
2388
2389     Widget parent = _my_editor->textEditor()->get_editor();
2390     _menuPopupText = new MenuBar(parent, "SendMsgTextPopup", XmMENU_POPUP);
2391     _textPopupMenu = _menuPopupText->addCommands(_textPopupMenuList, 
2392                                 FALSE, XmMENU_POPUP);
2393 }
2394
2395 static int cmp_prop_pair(const void *v1, const void *v2)
2396 {
2397     PropStringPair      *p1 = *((PropStringPair **) v1);
2398     PropStringPair      *p2 = *((PropStringPair **) v2);
2399     int                 ret;
2400
2401     ret =  strcmp((const char *) p1->label, (const char *) p2->label);
2402     return ret;
2403 }
2404
2405 static void alias_stuffing_func(char * key, void * data, void * client_data)
2406 {
2407     DtVirtArray<PropStringPair *>       *alias_list;
2408     PropStringPair                      *new_pair;
2409
2410     alias_list = (DtVirtArray<PropStringPair*> *) client_data;
2411     new_pair = new PropStringPair;
2412     new_pair->label = strdup(key);
2413     new_pair->value = strdup((char *)data);
2414     alias_list->append(new_pair);
2415 }
2416
2417 void
2418 SendMsgDialog::createAliasList(DtVirtArray<PropStringPair*> *aliases)
2419 {
2420     DtMailEnv           error;
2421     DtMail::Session     *d_session = theRoamApp.session()->session();
2422     DtMail::MailRc      *mail_rc = d_session->mailRc(error);
2423     int                 nalias = 0;
2424
2425     mail_rc->getAliasList(alias_stuffing_func, aliases);
2426
2427     if (nalias = aliases->length())
2428     {
2429         PropStringPair  **prop_pairs = NULL;
2430
2431         prop_pairs = (PropStringPair**) malloc(nalias*sizeof(PropStringPair*));
2432         int i;
2433         for (i=0; i<nalias; i++)
2434         {
2435             prop_pairs[i] = (*aliases)[0];
2436             aliases->remove(0);
2437         }
2438         qsort(prop_pairs, nalias, sizeof(PropStringPair*), cmp_prop_pair);
2439         for (i=0; i<nalias; i++)
2440           aliases->append(prop_pairs[i]);
2441         
2442         free((void*) prop_pairs);
2443     }
2444 }
2445
2446 void
2447 SendMsgDialog::destroyAliasList(DtVirtArray<PropStringPair*> *aliases)
2448 {
2449     while (aliases->length() >= 0)
2450     {
2451         PropStringPair  *prop_pair = (*aliases)[0];
2452         aliases->remove(0);
2453         delete(prop_pair);
2454     }
2455 }
2456
2457 // map_menu is used to figure out how many columns to split the menu
2458 // into.  It is a callback that is called when the menu is mapped.
2459 // If the menu is over half the height of the screen, it figures out
2460 // how many columns to make the menu, and sets its XmNnumColumns
2461 // attribute to that value.  It calculates the maximum number of columns
2462 // that would fit and never goes beyond that number.
2463
2464 static void map_alias_menu(Widget menu, XtPointer, XtPointer)
2465 {
2466     Position y;
2467     Dimension h, w;
2468     Dimension maxcols, newcols, columns;
2469     Dimension screenheight = (Dimension) HeightOfScreen(XtScreen(menu));
2470     Dimension fudgefact = 20; /* to allow for decorations on menu */
2471
2472     XtVaGetValues(
2473                 menu,
2474                 XmNheight, &h,
2475                 XmNwidth, &w,
2476                 XmNy, &y,
2477                 XmNnumColumns, &columns,
2478                 NULL);
2479
2480     if ((h + fudgefact) > (screenheight / 2))
2481     {
2482         // The menu is taller than half the screen. 
2483         // We need to find out how many more columns
2484         // to specify for the menu to make it fit.
2485
2486         newcols = (columns * ((h+fudgefact)/(screenheight/2))) + 1;
2487         maxcols = WidthOfScreen(XtScreen(menu))/(w/columns);
2488
2489         if (newcols > maxcols)
2490           newcols = maxcols;
2491
2492         XtVaSetValues(menu, XmNnumColumns, newcols, NULL);
2493     }
2494 }
2495
2496 void
2497 SendMsgDialog::aliasMenuButtonHandler(
2498                                 Widget,
2499                                 XtPointer client_data,
2500                                 XEvent *event,
2501                                 Boolean *)
2502 {
2503     Widget              menu = (Widget) client_data;
2504     XButtonEvent        *be = (XButtonEvent *) event;
2505
2506     if (event->xany.type != ButtonPress) return;
2507     if(be->button == theApplication->bMenuButton())
2508     {
2509         XmMenuPosition(menu, (XButtonEvent *)event);
2510         XtManageChild(menu);
2511     }
2512 }
2513
2514 Widget
2515 SendMsgDialog::createAliasPopupMenu(
2516                                 Widget parent,
2517                                 MenuBar **menubar,
2518                                 CmdList **cmdlist,
2519                                 DtVirtArray<PropStringPair*> *aliases)
2520 {
2521     Widget              menu = NULL;
2522     OtherAliasesCmd     *otherAliases =
2523                           new OtherAliasesCmd(
2524                                 "Other Aliases...",
2525                                 GETMSG(DT_catd, 1, 247, "Other Aliases..."),
2526                                 TRUE);
2527 #if defined(USE_TITLED_ALIAS_POPUPS)
2528     LabelCmd            *title =
2529                           new LabelCmd(
2530                                 "Mailer - Aliases",
2531                                 GETMSG(DT_catd, 1, 248, "Mailer - Aliases"),
2532                                 TRUE);
2533 #endif
2534     SeparatorCmd        *separator =
2535                           new SeparatorCmd("Separator","Separator", TRUE);
2536
2537     (*cmdlist) = new CmdList("AliasCommands", "AliasCommands");
2538 #if defined(USE_TITLED_ALIAS_POPUPS)
2539     (*cmdlist)->add(title);
2540     (*cmdlist)->add(separator);
2541 #endif
2542     for (int i=0, length=aliases->length(); i<length; i++)
2543     {
2544         PropStringPair  *prop_pair = (*aliases)[i];
2545
2546         AliasCmd *alias = new AliasCmd(
2547                                 strdup(prop_pair->label),
2548                                 strdup(prop_pair->label),
2549                                 TRUE,
2550                                 parent);
2551         (*cmdlist)->add(alias);
2552     }
2553     if (0 < aliases->length())
2554       (*cmdlist)->add(separator);
2555     (*cmdlist)->add(otherAliases);
2556
2557     *menubar = new MenuBar(parent, "AliasesPopup", XmMENU_POPUP);
2558     menu = (*menubar)->addCommands((*cmdlist), FALSE, XmMENU_POPUP);
2559
2560     XtAddEventHandler(
2561                 parent,
2562                 ButtonPressMask,
2563                 FALSE,
2564                 aliasMenuButtonHandler,
2565                 (XtPointer) menu);
2566
2567     XtVaSetValues(
2568                 menu,
2569                 XmNpacking, XmPACK_COLUMN,
2570                 XmNorientation, XmVERTICAL,
2571                 NULL);
2572
2573     XtAddCallback(
2574                 menu,
2575                 XmNmapCallback, &map_alias_menu,
2576                 NULL);
2577
2578     return menu;
2579 }
2580
2581 void
2582 SendMsgDialog::destroyAliasPopupMenu(
2583                                 Widget  parent,
2584                                 MenuBar *menubar,
2585                                 CmdList *cmdlist,
2586                                 Widget  menu)
2587 {
2588     XtRemoveEventHandler(
2589                 parent,
2590                 ButtonPressMask,
2591                 FALSE,
2592                 aliasMenuButtonHandler,
2593                 (XtPointer) menu);
2594
2595     XtRemoveCallback(
2596                 menu,
2597                 XmNmapCallback, &map_alias_menu,
2598                 NULL);
2599
2600     XtDestroyWidget(menu);
2601
2602     delete menubar;
2603     delete cmdlist;
2604 }
2605
2606 Widget
2607 SendMsgDialog::getHeaderWidget(const char *hdrname)
2608 {
2609     for (int i=0, length=_header_list.length(); i<length; i++)
2610     {
2611         HeaderList      *hdritem = _header_list[i];
2612
2613         if (0 == strncmp(hdrname, hdritem->header, strlen(hdrname)))
2614           return hdritem->field_widget;
2615     }
2616
2617     return NULL;
2618 }
2619
2620 void
2621 SendMsgDialog::createAliasPopupMenus(void)
2622 {
2623     Widget      w;
2624
2625     w = getHeaderWidget(DtMailMessageTo);
2626     if (NULL != w)
2627       _toPopupMenu = createAliasPopupMenu(
2628                                         w,
2629                                         &_toPopupMenuBar,
2630                                         &_toPopupCmdlist,
2631                                         _aliasList);
2632
2633     w = getHeaderWidget(DtMailMessageCc);
2634     if (NULL != w)
2635       _ccPopupMenu = createAliasPopupMenu(
2636                                         w,
2637                                         &_ccPopupMenuBar,
2638                                         &_ccPopupCmdlist,
2639                                         _aliasList);
2640
2641     w = getHeaderWidget(DtMailMessageBcc);
2642     if (NULL != w)
2643       _bccPopupMenu = createAliasPopupMenu(
2644                                         w,
2645                                         &_bccPopupMenuBar,
2646                                         &_bccPopupCmdlist,
2647                                         _aliasList);
2648 }
2649
2650 void
2651 SendMsgDialog::destroyAliasPopupMenus(void)
2652 {
2653     Widget      w;
2654
2655     w = getHeaderWidget(DtMailMessageTo);
2656     destroyAliasPopupMenu(w, _toPopupMenuBar, _toPopupCmdlist, _toPopupMenu);
2657     _toPopupMenuBar = NULL;
2658     _toPopupCmdlist = NULL;
2659     _toPopupMenu = NULL;
2660
2661     w = getHeaderWidget(DtMailMessageCc);
2662     destroyAliasPopupMenu(w, _ccPopupMenuBar, _ccPopupCmdlist, _ccPopupMenu);
2663     _ccPopupMenuBar = NULL;
2664     _ccPopupCmdlist = NULL;
2665     _ccPopupMenu = NULL;
2666
2667     w = getHeaderWidget(DtMailMessageBcc);
2668     destroyAliasPopupMenu(w, _bccPopupMenuBar, _bccPopupCmdlist, _bccPopupMenu);
2669     _bccPopupMenuBar = NULL;
2670     _bccPopupCmdlist = NULL;
2671     _bccPopupMenu = NULL;
2672 }
2673
2674 void
2675 SendMsgDialog::createFormatMenu()
2676 {
2677     CmdList *cmdList;
2678     _format_separator = new SeparatorCmd( "Separator","Separator", TRUE );
2679
2680     cmdList = new CmdList( "Format", GETMSG(DT_catd, 1, 152,"Format") );
2681     
2682     _format_word_wrap = new WordWrapCmd ( 
2683                                           "Word Wrap",
2684                                           GETMSG(DT_catd, 1, 153, "Word Wrap"), 
2685                                           TRUE, 
2686                                           this );
2687     _format_settings = new FormatCmd ( "Settings...",
2688                                        GETMSG(DT_catd, 1, 154, "Settings..."), 
2689                                        TRUE, 
2690                                        this );
2691     
2692     cmdList->add( _format_word_wrap );
2693     cmdList->add( _format_settings );
2694     cmdList->add( _format_separator);
2695     
2696     _templates = new CmdList ( "Templates", GETMSG(DT_catd, 1, 157, "Templates") );
2697     addTemplates(_templates);
2698     
2699     cmdList->add(_templates);
2700     
2701     cmdList->add( _format_separator );
2702
2703     _format_menu = _menuBar->addCommands ( &_format_cascade, cmdList, 
2704                         FALSE, XmMENU_BAR);
2705
2706     delete cmdList;
2707
2708     doDynamicHeaderMenus();
2709
2710     if (_template_count == 0 && _templates->getPaneWidget())
2711     {
2712         XtSetSensitive(_templates->getPaneWidget(), FALSE);
2713     }
2714     
2715 }
2716
2717 void
2718 SendMsgDialog::addTemplates(CmdList * subCmd)
2719 {
2720     DtMailEnv error;
2721
2722     _template_count = 0;
2723     
2724     if (_templateList == NULL)
2725         return;
2726
2727     DtMail::Session *m_session = theRoamApp.session()->session();
2728     char * expanded_list = m_session->expandPath(error, _templateList);
2729
2730     DtVirtArray<PropStringPair *> templates(8);
2731     parsePropString(expanded_list, templates);
2732     free(expanded_list);
2733
2734     _template_count = templates.length();
2735
2736     for (int tmp = 0; tmp < _template_count; tmp++) {
2737         PropStringPair * psp = templates[tmp];
2738         if (psp->label && psp->value) {
2739             Cmd * button = new TemplateCmd(strdup(psp->label), 
2740                                            strdup(psp->label), 
2741                                            1, 
2742                                            this, 
2743                                            psp->value);
2744             subCmd->add(button);
2745         }
2746     }
2747
2748     while (templates.length()) {
2749         PropStringPair * psp = templates[0];
2750         delete psp;
2751         templates.remove(0);
2752     }
2753 }
2754
2755 void
2756 SendMsgDialog::initialize()
2757 {
2758     Cardinal n = 0;
2759     Arg args[1];
2760     const char * hideAttachPane = NULL;
2761     DtMailEnv error;
2762     
2763     // Without the TearOffModelConverter call, there will be warning messages:
2764     // Warning: No type converter registered for 'String' to 'TearOffModel' 
2765     // conversion.
2766     
2767     XmRepTypeInstallTearOffModelConverter();
2768     MenuWindow::initialize();
2769
2770     char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
2771     setTitle(ttl);
2772
2773     XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
2774     XtSetValues( _w, args, n);
2775     
2776     _genDialog = new DtMailGenDialog("Dialog", _main);
2777     
2778     // See if the .mailrc specifies if attachPane is to be shown or hid 
2779     // at SMD startup time.
2780     
2781     DtMail::Session *m_session = theRoamApp.session()->session();
2782     m_session->mailRc(error)->getValue(error, "hideattachments", 
2783                                 &hideAttachPane);
2784     
2785     if (!hideAttachPane) {
2786         _show_attach_area = TRUE;
2787     }
2788     else {
2789         _show_attach_area = FALSE;
2790         // The user wants to hide attachments
2791
2792         this->hideAttachArea();
2793     }
2794     if (NULL != hideAttachPane)
2795       free((void*) hideAttachPane);
2796
2797     _confirm_attachment_threshold = get_confirm_attachment_threshold();
2798     
2799     // Log Message Toggle button.  A LogMsgCmd is a ToggleButtonCmd....
2800     const char * logfile = NULL;
2801     const char * value = NULL;
2802     m_session->mailRc(error)->getValue(error, "record", &logfile);
2803     if(logfile == NULL)
2804         _file_log->deactivate();
2805     else
2806         _file_log->activate();
2807
2808     m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2809     if (logfile == NULL || error.isNotSet()) {
2810         // logfile is not specified or "dontlogmessages" is TRUE
2811         setLogState(DTM_FALSE);
2812         ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2813     } else {
2814         // logfile is specified and "dontlogmessages" is FALSE
2815         setLogState(DTM_TRUE);
2816         ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2817     }
2818     
2819     if (NULL != logfile)
2820       free((void*) logfile);
2821     if (NULL != value)
2822       free((void*) value);
2823
2824     // Word Wrap Toggle button.  A WordWrapCmd is a ToggleButtonCmd...
2825     ((ToggleButtonCmd *)_format_word_wrap)->setButtonState(
2826                            ((WordWrapCmd *)_format_word_wrap)->wordWrap(), 
2827                            FALSE
2828                            );
2829
2830     // Initialize the Edit menu
2831
2832     this->text_unselected();
2833         
2834     setIconName(ComposeIcon);
2835 }
2836
2837 static void
2838 Self_destruct(XtPointer, XtIntervalId *)
2839 {
2840 #ifdef WM_TT
2841     fprintf(stderr, "DEBUG: Self_destruct(): invoked!\n");
2842 #endif
2843     
2844     
2845     XtRemoveAllCallbacks(
2846                          theApplication->baseWidget(), 
2847                          XmNdestroyCallback);
2848     delete theApplication;
2849 }
2850
2851 // Clears Compose window title, header fields, text, and attachment areas.
2852 void
2853 SendMsgDialog::reset()
2854 {
2855     _my_editor->textEditor()->clear_contents();
2856     _my_editor->attachArea()->resetPendingAction();
2857
2858     _takeDown = FALSE;
2859     // This will deselect any Attachment action, if any available now.
2860     // Also deselect text menu items....
2861
2862     this->deactivate_default_attach_menu();
2863     this->text_unselected();
2864     this->all_attachments_deselected();
2865     _att_undelete->deactivate();  // This needs to be done in addition
2866
2867     this->get_editor()->attachArea()->removeCurrentAttachments();
2868
2869     // Unmanage the dialog
2870     this->unmanage();
2871
2872     if (_show_attach_area) { // .mailrc wants default attach area invisible
2873
2874     // Unmanage the attach Area.  Set the show_pane button.
2875     // This is done because if we are caching this window (after 
2876     // unmanaging), we don't want the window to pop back up, on uncaching,
2877     // with the attachment pane visible, etc..
2878
2879         this->showAttachArea();
2880     }
2881     else {
2882         this->hideAttachArea();
2883     }
2884
2885     // Need to destroy current Message handle.
2886     delete _msgHandle;    // All its body parts are deleted.
2887     _msgHandle = NULL;
2888     _lastAttBP = NULL;    // So just set this to NULL.
2889     // Delete or set to NULL ???
2890     _inclMsgHandle = NULL; 
2891     _inclMsgHasText = 0;
2892
2893     for (int clear = 0; clear < _header_list.length(); clear++) {
2894         HeaderList * hl = _header_list[clear];
2895
2896         // Bugfix: Old selection area remained selected, after text cleared
2897         // and parent widget unmanged, and then managed again for next
2898         // Compose.  (So new text in old select area was still being selected).
2899         // Perhaps, this is a Motif bug ... but this fixes the problem.
2900         XmTextFieldClearSelection( hl->field_widget, CurrentTime );
2901
2902         if (hl->value) {
2903             XtVaSetValues(hl->field_widget,
2904                           XmNvalue, hl->value,
2905                           NULL);
2906         }
2907         else {
2908             XtVaSetValues(hl->field_widget,
2909                           XmNvalue, "",
2910                           NULL);
2911         }
2912     }
2913     // Reset the Log state in case the user happened to change it.
2914     DtMail::Session *m_session = theRoamApp.session()->session();
2915     const char * logfile = NULL;
2916     const char * value = NULL;
2917     DtMailEnv error;
2918
2919     m_session->mailRc(error)->getValue(error, "record", &logfile);
2920     if(logfile == NULL)
2921         _file_log->deactivate();
2922     else
2923         _file_log->activate();
2924
2925     m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2926     if (logfile == NULL || error.isNotSet()) {
2927         // logfile is not specified or "dontlogmessages" is TRUE
2928         setLogState(DTM_FALSE);
2929         ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2930     } else {
2931         // logfile is specified and "dontlogmessages" is FALSE
2932         setLogState(DTM_TRUE);
2933         ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2934     }
2935         
2936     if (NULL != logfile)
2937       free((void*) logfile);
2938     if (NULL != value)
2939       free((void*) value);
2940 }
2941
2942 // Recycles Compose window.
2943 void
2944 SendMsgDialog::quit(Boolean delete_win)
2945 {
2946
2947     // There are several ways we could have reached here.
2948     // 1) From the user choosing Send.
2949     // 2) From the user clicking on the Close button or Close menu item
2950     // 3) The user choosing Close from window manager menu.
2951     // For (1), we just forge ahead.  For that, the _takeDown boolean
2952     // is set in send_message() method.
2953     // For (2), the boolean is set in goAway().
2954     // For (3), we call goAway() which sets the _takeDown depending on
2955     // a dialog negotiation if SMD has contents.
2956
2957      if (_file_include->fileBrowser() != NULL) 
2958          XtPopdown(XtParent(_file_include->fileBrowser())); 
2959      if (_att_add->fileBrowser() != NULL) 
2960          XtPopdown(XtParent(_att_add->fileBrowser())); 
2961
2962      if (_file_save_as->fileBrowser() != NULL) 
2963          XtPopdown(XtParent(_file_save_as->fileBrowser())); 
2964      if (_att_save->fileBrowser() != NULL) 
2965          XtPopdown(XtParent(_att_save->fileBrowser())); 
2966
2967     if (!_takeDown) {
2968         // Check to see if it's the first time through the quit()
2969         // method.  Set _first_time to FALSE so that we don't come
2970         // down this path again until we're done quitting or bad
2971         // things will happen.
2972         if (_first_time == TRUE) {
2973             _first_time = FALSE;
2974             this->goAway(TRUE);
2975             // We're done quitting, so we can set _first_time to TRUE again.
2976             _first_time = TRUE;
2977         }
2978         return;
2979     }  
2980
2981     stopAutoSave();
2982     
2983 #ifdef DTMAIL_TOOLTALK
2984     //  For explanation of dtmail_mapped, look at RoamApp.h.
2985     if ( started_by_tt && (0 == theCompose.getTimeOutId()) &&
2986          (theCompose.numUnusedWindows() == theCompose.numCreatedWindows()) &&
2987          !dtmail_mapped )
2988     {
2989         int id;
2990         id = XtAppAddTimeOut(
2991                         theApplication->appContext(), 
2992                         (unsigned long)DESTRUCT_TIMEOUT, 
2993                         Self_destruct, NULL);
2994         theCompose.putTimeOutId(id);
2995     }
2996 #endif
2997
2998     if (delete_win)
2999      delete this;
3000     else
3001     {
3002         this->reset();
3003         theCompose.putWin(this, FALSE);
3004     }
3005     
3006     //
3007     // If there are no composer timeouts, check if its time to shutdown.
3008     //
3009     if (0 == theCompose.getTimeOutId()) theRoamApp.checkForShutdown();
3010 }
3011
3012 void
3013 SendMsgDialog::panicQuit()
3014 {
3015     //
3016     // Need to make sure the message is still valid before proceeding.
3017     // ::reset may have been called so the message may no longer be valid.
3018     //
3019     if (!isMsgValid())
3020       return;
3021
3022     doAutoSave();
3023 }
3024
3025
3026 // Given a file name, include the file as attachment.
3027 void
3028 SendMsgDialog::inclAsAttmt(char *file, char *name)
3029 {
3030     this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3031                                                     file, name);
3032     this->setLastAttBP();
3033     
3034 }
3035
3036 // Given a buffer, include its content as an attachment.
3037 void
3038 SendMsgDialog::inclAsAttmt(unsigned char *contents, int len, char *name)
3039 {
3040     DtMailBuffer mbuf;
3041
3042     mbuf.buffer = (void *)contents;
3043     mbuf.size = (unsigned long)len;
3044     this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3045                                                     (String)name, mbuf);
3046     this->setLastAttBP();
3047 }
3048
3049 // Given a RFC_822_Message formatted buffer, parse it and fill the Compose Window.
3050 void
3051 SendMsgDialog::parseNplace(char *contents, int len)
3052 {
3053     // 1. Create message handle for contents
3054
3055     DtMailEnv error;
3056     DtMail::Session * d_session = theRoamApp.session()->session();
3057     DtMailBuffer mbuf;
3058     
3059     mbuf.buffer = (void *)contents;
3060     mbuf.size = (unsigned long)len;
3061     
3062     DtMail::Message * msg = d_session->messageConstruct(error,
3063                                                         DtMailBufferObject,
3064                                                         &mbuf,
3065                                                         NULL,
3066                                                         NULL,
3067                                                         NULL);
3068     if ( !msg ) {
3069         return;
3070     } else if ( error.isSet() ) {
3071         if  ( (DTMailError_t) error == DTME_UnknownFormat ) {
3072             // The content does not have header info.  Therefore, store
3073             // everything as text.
3074             _my_editor->textEditor()->set_contents((const char *)mbuf.buffer,
3075                                                    mbuf.size);
3076             return;
3077         }
3078     }
3079
3080     char * status_string;
3081     DtMailBoolean first_bp_handled;
3082     first_bp_handled = _my_editor->textEditor()->set_message(
3083                                                              msg,
3084                                                              &status_string,
3085                                                              Editor::HF_NONE
3086                                                              );
3087     
3088     int num_bodyParts = msg->getBodyCount(error);
3089
3090     // Don't use setInclMsgHnd() because it causes the SMD's attachments
3091     // to get out of sink with the BE.  Just assign the newly created message
3092     // to _msgHandle.
3093     //
3094     if ((num_bodyParts > 1) || (!first_bp_handled)) {
3095         int start;
3096         if (first_bp_handled) {
3097             start = 2;
3098 //          setInclMsgHnd(msg, TRUE);
3099         }
3100         else {
3101             start = 1;
3102 //          setInclMsgHnd(msg, FALSE);
3103         }
3104
3105         if (_msgHandle) {
3106             delete _msgHandle;
3107         }
3108         _msgHandle = msg;
3109         _my_editor->attachArea()->parseAttachments(error,
3110                                                    msg,
3111                                                    TRUE,
3112 //                                                 FALSE,
3113                                                    start);
3114         // Need to call this after calling parseAttachments() so attachments
3115         // will be displayed in the attachment pane.
3116         _my_editor->manageAttachArea();
3117
3118         // Need to update this compose window's internal message handle.
3119         //
3120         // GL - calling updateMsgHndAtt is no longer necessary because we
3121         // just assigning the newly created msg to _msgHandle.
3122 //      updateMsgHndAtt();
3123     }
3124
3125     loadHeaders(msg, DTM_TRUE);
3126 }
3127
3128 // Given a RFC_822_Message formatted file, parse it and fill the Compose Window.
3129 void
3130 SendMsgDialog::parseNplace(const char * path)
3131 {
3132     // 1. Get file content into buffer.
3133     int fd = SafeOpen(path, O_RDONLY);
3134     if (fd < 0) {
3135         return;
3136     }
3137     
3138     struct stat buf;
3139     if (SafeFStat(fd, &buf) < 0) {
3140         close(fd);
3141         return;
3142     }
3143     
3144     _dead_letter_buf = new char[buf.st_size];
3145     if (!_dead_letter_buf) {
3146         close(fd);
3147         return;
3148     }
3149     
3150     if (SafeRead(fd, _dead_letter_buf, 
3151                  (unsigned int) buf.st_size) != buf.st_size) {
3152         delete _dead_letter_buf;
3153         close(fd);
3154         return;
3155     }
3156     
3157     parseNplace(_dead_letter_buf, (int) buf.st_size);
3158 }
3159
3160 void
3161 SendMsgDialog::text( const char *text )
3162 {
3163     _my_editor->textEditor()->set_contents( text, strlen(text) );
3164 }
3165
3166 void
3167 SendMsgDialog::append( const char *text )
3168 {
3169     _my_editor->textEditor()->append_to_contents( text, strlen(text) );
3170 }
3171
3172 char *
3173 SendMsgDialog::text()
3174 {
3175     // Potential memory leak here.  Because XmTextGetString returns 
3176     // pointer to space containing all the text in the widget.  Need 
3177     // to call XtFree after we use this space
3178     // Also DtEditor widget requires application to free data.
3179     
3180     return (_my_editor->textEditor()->get_contents());
3181     
3182 }
3183
3184
3185 void
3186 SendMsgDialog::text_selected()
3187 {
3188     // turn on sensitivity for Cut/Clear/Copy/Delete
3189     _edit_cut->activate();
3190     _edit_copy->activate();
3191     _edit_clear->activate();
3192     _edit_delete->activate();
3193     _edit_select_all->activate();
3194 }
3195
3196 void
3197 SendMsgDialog::text_unselected()
3198 {
3199     // turn off sensitivity for those items
3200     _edit_cut->deactivate();
3201     _edit_copy->deactivate();
3202     _edit_clear->deactivate();
3203     _edit_delete->deactivate();
3204 }
3205
3206 // Attachments
3207
3208 void
3209 SendMsgDialog::attachment_selected()
3210 {
3211     _att_save->activate();
3212     _att_delete->activate();
3213     _att_rename->activate();
3214     
3215 }
3216
3217 void
3218 SendMsgDialog::all_attachments_selected()
3219 {
3220     _att_delete->activate();
3221     _att_save->deactivate();
3222     _att_rename->deactivate();
3223
3224     if (_attachmentActionsList != NULL) {
3225         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3226         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3227                                 _attachmentActionsList);
3228         delete _attachmentActionsList;
3229         _attachmentActionsList = NULL;
3230     }
3231
3232 }
3233
3234
3235 void
3236 SendMsgDialog::all_attachments_deselected()
3237 {
3238     _att_save->deactivate();
3239     _att_delete->deactivate();
3240     _att_rename->deactivate();
3241     
3242     if (_attachmentActionsList != NULL) {
3243         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3244         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3245                                 _attachmentActionsList);
3246         delete _attachmentActionsList;
3247         _attachmentActionsList = NULL;
3248     }
3249     
3250 }
3251
3252
3253 void
3254 SendMsgDialog::addAttachmentActions(
3255                                     char **actions,
3256                                     int indx
3257                                     )
3258 {
3259     int i;
3260     char *anAction;
3261     AttachmentActionCmd *attachActionCmd;
3262     
3263     if (_attachmentActionsList == NULL) { 
3264         _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3265     }
3266     else {
3267         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3268         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3269                                         _attachmentActionsList);
3270         delete _attachmentActionsList;
3271         _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3272     }
3273     
3274     char *actionLabel;         
3275     for (i = 0; i < indx; i++) {
3276         anAction = actions[i];
3277         actionLabel = DtActionLabel(anAction);   // get the localized action label
3278         attachActionCmd = new AttachmentActionCmd(
3279                                                   anAction,
3280                                                   actionLabel,
3281                                                   this,
3282                                                   i);
3283         _attachmentActionsList->add(attachActionCmd);
3284         
3285     }
3286     _attachmentMenu = _menuBar->addCommands(
3287                                             _attachmentMenu, 
3288                                             _attachmentActionsList
3289                                             );
3290     _attachmentPopupMenu = _menuPopupAtt->addCommands(
3291                                             _attachmentPopupMenu, 
3292                                             _attachmentActionsList
3293                                             );
3294 }
3295
3296 void
3297 SendMsgDialog::removeAttachmentActions()
3298 {
3299     
3300     // Stubbed out for now
3301 }
3302
3303 void
3304 SendMsgDialog::invokeAttachmentAction(
3305                                       int index
3306                                       )
3307 {
3308     DtMailEditor *editor = this->get_editor();
3309     AttachArea *attacharea = editor->attachArea();
3310     Attachment *attachment = attacharea->getSelectedAttachment();
3311     
3312     attachment->invokeAction(index);
3313 }
3314
3315 void
3316 SendMsgDialog::selectAllAttachments()
3317 {
3318     
3319     DtMailEditor *editor = this->get_editor();
3320     AttachArea *attachArea = editor->attachArea();
3321     
3322     attachArea->selectAllAttachments();
3323     
3324 }
3325
3326
3327 void
3328 SendMsgDialog::activate_default_attach_menu()
3329 {
3330     _att_select_all->activate();
3331 }
3332
3333 void
3334 SendMsgDialog::deactivate_default_attach_menu()
3335 {
3336     _att_select_all->deactivate();
3337 }
3338
3339
3340 void
3341 SendMsgDialog::delete_selected_attachments()
3342 {
3343     DtMailEnv mail_error;
3344     
3345     // Initialize the mail_error.
3346
3347     mail_error.clear();
3348     
3349     AttachArea *attachArea = _my_editor->attachArea();
3350     attachArea->deleteSelectedAttachments(mail_error);
3351     
3352     if (mail_error.isSet()) {
3353         // do something
3354     }
3355     
3356     // Activate this button to permit the user to undelete.
3357     
3358     _att_undelete->activate();
3359     
3360     // Deactivate buttons that will be activated when another
3361     // selection applies.
3362     
3363     _att_save->deactivate();
3364     _att_delete->deactivate();
3365     _att_rename->deactivate();
3366
3367     if (_attachmentActionsList) {
3368         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3369         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3370                                       _attachmentActionsList);
3371         delete _attachmentActionsList;
3372         _attachmentActionsList = NULL;
3373     }
3374 }
3375
3376 void
3377 SendMsgDialog::undelete_last_deleted_attachment()
3378 {
3379     DtMailEnv mail_error;
3380     
3381     // Initialize the mail_error.
3382     
3383     mail_error.clear();
3384     
3385     AttachArea *attachArea = _my_editor->attachArea();
3386     attachArea->undeleteLastDeletedAttachment(mail_error);
3387     
3388     if (mail_error.isSet()) {
3389         // do something
3390     }
3391     
3392     if(_my_editor->attachArea()->getIconSelectedCount()) 
3393         _att_delete->activate();
3394
3395     if (attachArea->getDeleteCount() == 0) {
3396         _att_undelete->deactivate();
3397     }
3398 }
3399
3400 Boolean
3401 SendMsgDialog::renameAttachmentOK()
3402 {
3403     AttachArea *attachArea = _my_editor->attachArea();
3404     
3405     if (attachArea->getIconSelectedCount() > 1) {
3406        char *buf = new char[512];
3407         
3408        sprintf(buf, "%s", GETMSG(DT_catd, 5, 4, "Select only one attachment\n\
3409 and then choose rename"));
3410         
3411         _genDialog->setToQuestionDialog(
3412                                         GETMSG(DT_catd, 5, 2, "Mailer"),
3413                                         buf);
3414         
3415         char * helpId = DTMAILHELPSELECTONEATTACH;
3416         
3417         int answer = _genDialog->post_and_return(helpId);
3418         
3419         delete [] buf;
3420         return(FALSE);
3421     }
3422     else {
3423         return(TRUE);
3424     }
3425 }
3426
3427 void
3428 SendMsgDialog::showAttachArea()
3429 {
3430     DtMailEditor *editor = this->get_editor();
3431     editor->showAttachArea();
3432     ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, FALSE);
3433 }
3434
3435 void
3436 SendMsgDialog::hideAttachArea()
3437 {
3438     DtMailEditor *editor = this->get_editor();
3439     editor->hideAttachArea();
3440     ((ToggleButtonCmd *)_att_show_pane)->setButtonState(FALSE, FALSE);
3441 }
3442
3443 int
3444 SendMsgDialog::lookupHeader(const char * name)
3445 {
3446     for (int h = 0; h < _header_list.length(); h++) {
3447         HeaderList * hl = _header_list[h];
3448         if (hl->show != SMD_NEVER && 
3449                 strcmp(hl->label, name) == 0) {
3450             return(h);
3451         }
3452     }
3453
3454     return(-1);
3455 }
3456
3457 void
3458 SendMsgDialog::headerValueChanged(Widget,
3459                                   XtPointer client_data,
3460                                   XtPointer)
3461 {
3462     SendMsgDialog * self = (SendMsgDialog *)client_data;
3463     self->_headers_changed = DTM_TRUE;
3464 }
3465
3466 void
3467 SendMsgDialog::reattachHeaders(void)
3468 {
3469     // We have to walk through the entire list of headers, attaching
3470     // the shown headers to the ones above them.
3471     //
3472     HeaderList * hl = _header_list[0];
3473     Widget previous_form = hl->form_widget;
3474
3475     for (int h = 1; h < _header_list.length(); h++) {
3476         hl = _header_list[h];
3477
3478         switch(hl->show) {
3479           case SMD_ALWAYS:
3480             previous_form = hl->form_widget;
3481             break;
3482
3483           case SMD_SHOWN:
3484             XtVaSetValues(hl->form_widget,
3485                           XmNtopAttachment, XmATTACH_WIDGET,
3486                           XmNtopWidget, previous_form,
3487                           NULL);
3488             previous_form = hl->form_widget;
3489             break;
3490
3491           default:
3492             break;
3493         }
3494     }
3495
3496     forceFormResize(_main_form);
3497     forceFormResize(_header_form);
3498 }
3499
3500 void
3501 SendMsgDialog::justifyHeaders(void)
3502 {
3503     // Find out which header label has the longest display width to right
3504     // justify all labels.
3505     //
3506     Dimension longest = 0;
3507     Dimension w = 0;
3508     Dimension margin;
3509     for (int count = 0;  count < _header_list.length(); count++) {
3510         HeaderList * hl = _header_list[count];
3511         if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3512             continue;
3513         }
3514
3515         XtVaGetValues(hl->label_widget,
3516                       XmNwidth, &w,
3517                       XmNmarginLeft, &margin,
3518                       NULL);
3519         w -= margin;
3520         if ( w > longest ) {
3521             longest = w;
3522         }
3523     }
3524
3525     for (int adjust = 0;  adjust < _header_list.length();  adjust++) {
3526         HeaderList * hl = _header_list[adjust];
3527         if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3528             continue;
3529         }
3530
3531         XtVaGetValues(hl->label_widget,
3532                       XmNwidth, &w,
3533                       XmNmarginLeft, &margin,
3534                       NULL);
3535         w -= margin;
3536         XtVaSetValues(hl->label_widget, XmNmarginLeft, (longest-w) > 0 ? longest-w : 1, NULL );
3537     }
3538 }
3539
3540 void
3541 SendMsgDialog::forceFormResize(Widget form)
3542 {
3543     // The Motif Form widget is at least a little bit brain damaged.
3544     // We need to convince it to do the right thing after we make
3545     // minor adjustments in the children.
3546     //
3547     Dimension width, height, border;
3548     XtVaGetValues(form,
3549                   XmNwidth, &width,
3550                   XmNheight, &height,
3551                   XmNborderWidth, &border,
3552                   NULL);
3553
3554     XtVaSetValues(form,
3555                   XmNwidth, width + 1,
3556                   XmNheight, height + 1,
3557                   NULL);
3558
3559     XtVaSetValues(form,
3560                   XmNwidth, width,
3561                   XmNheight, height,
3562                   NULL);
3563 }
3564
3565
3566 Boolean
3567 SendMsgDialog::unfilled_headers()
3568 {
3569     // Walk through the headers. See if any of them have a value.
3570     //
3571     for (int scan = 0; scan < _header_list.length(); scan++) {
3572         HeaderList * hl = _header_list[scan];
3573         if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
3574             continue;
3575         }
3576
3577         char * value = NULL;
3578         XtVaGetValues(hl->field_widget,
3579                       XmNvalue, &value,
3580                       NULL);
3581         if (value) {
3582           if (strlen(value) > 0) {
3583               XtFree(value);
3584               return(FALSE);
3585           }
3586           XtFree(value);
3587         }
3588     }
3589     return(TRUE);
3590 }
3591
3592 // Method checks if self has text in it.  
3593
3594 Boolean
3595 SendMsgDialog::checkDirty()
3596 {
3597     if (this->unfilled_headers() && 
3598        (this->get_editor()->textEditor()->no_text()) && 
3599        (this->get_editor()->attachArea()->getIconCount() == 0) ) {
3600
3601         // return FALSE so quit() can go ahead.
3602
3603         return(FALSE);
3604
3605     } 
3606     else {
3607         return(TRUE);
3608     }
3609 }
3610
3611 Boolean
3612 SendMsgDialog::handleQuitDialog()
3613 {
3614     char *helpId;
3615
3616     DtMail::Session *m_session = theRoamApp.session()->session();
3617     const char * value = NULL;
3618     DtMailEnv error;
3619
3620     m_session->mailRc(error)->getValue(error, "expert", &value);
3621     if (error.isNotSet() && value != NULL)
3622     {
3623         if (NULL != value)
3624           free((void*) value);
3625
3626         return (TRUE); 
3627     }
3628
3629     DtMailGenDialog *dialog = this->genDialog();
3630
3631     dialog->setToQuestionDialog(
3632                         GETMSG(
3633                                 DT_catd, 
3634                                 1, 
3635                                 99, 
3636                                 "Mailer - Close"),
3637                         GETMSG(
3638                                 DT_catd, 
3639                                 3, 
3640                                 58, 
3641                                 "The Compose window contains text or\n\
3642 attachments that will be lost if\n\
3643 the window is closed.\n\
3644 Close the Compose window?")
3645                 );
3646     helpId = DTMAILHELPCLOSECOMPOSEWINDOW;
3647     if ( dialog->post_and_return(
3648                         GETMSG(
3649                                DT_catd, 
3650                                1, 
3651                                100, 
3652                                "OK"),
3653                         GETMSG(
3654                                DT_catd, 
3655                                1, 
3656                                101, 
3657                                "Cancel"),
3658                         helpId) == 1 ) {    // Close selected
3659             
3660         return(TRUE);
3661     }
3662     else {
3663         return(FALSE);  // Cancel selected
3664     }
3665 }
3666
3667 void
3668 SendMsgDialog::goAway(
3669     Boolean checkForDirty
3670 )
3671 {
3672     
3673     if (!checkForDirty) {
3674         _takeDown = TRUE;
3675         this->quit();
3676     }
3677     else {
3678         // Check to see if self has contents (ie., is dirty)
3679
3680         Boolean is_dirty = this->checkDirty();
3681
3682         if (is_dirty) {
3683             if (isIconified()) {
3684                 MainWindow::manage();
3685             }
3686
3687             // Enquire if user really wants this window to go away
3688
3689             Boolean really_quit = this->handleQuitDialog();
3690             if (!really_quit) {
3691                 return;
3692             }
3693         }
3694         _takeDown = TRUE;
3695         this->quit();
3696     }
3697 }
3698
3699 void
3700 SendMsgDialog::manage()
3701 {
3702     MenuWindow::manage();
3703     // Set focus 
3704     HeaderList * hl = _header_list[0];
3705     (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3706    
3707 }
3708
3709 void
3710 SendMsgDialog::unmanage()
3711 {
3712     MenuWindow::unmanage();
3713     XFlush(XtDisplay(this->_main_form));
3714     XSync(XtDisplay(this->_main_form), False);
3715 }
3716     
3717
3718 Compose::Compose()
3719 {
3720     _compose_head = NULL;
3721     _not_in_use = 0;
3722     _num_created = 0;
3723     _timeout_id = 0;
3724 }
3725
3726 Compose::~Compose()
3727 {
3728     Compose::Compose_Win *a_node;
3729     Compose::Compose_Win *next_node;
3730
3731     theRoamApp.registerPendingTask();
3732
3733     a_node = _compose_head;
3734     while (a_node)
3735     {
3736         next_node = a_node->next;
3737         if (!a_node->in_use)
3738         {
3739             delete a_node->win;
3740             free((void*) a_node);
3741         }
3742         a_node = next_node;
3743     }
3744
3745     theRoamApp.unregisterPendingTask();
3746 }
3747
3748 void
3749 Compose::putWin(SendMsgDialog *smd, Boolean in_use)
3750 {
3751     Compose::Compose_Win* a_node = NULL; 
3752     Compose::Compose_Win *tmp = NULL;
3753
3754     //
3755     // Update the _not_in_use count.
3756     //
3757     if (! in_use)
3758       _not_in_use++;
3759
3760     //
3761     // Check to see if compose window is already in the list.
3762     //
3763     for (a_node = _compose_head; a_node; a_node=a_node->next)
3764     {
3765         if (a_node->win == smd)
3766         {
3767             a_node->in_use = in_use;
3768             return;
3769         }
3770     }
3771
3772     // Need new node with smd.
3773     tmp = (Compose::Compose_Win *)malloc(sizeof(Compose::Compose_Win));
3774     tmp->win = smd;
3775     tmp->next = NULL;
3776     tmp->in_use = in_use;
3777
3778     // If nothing is cached so far, add this Compose window to the head.
3779     if (NULL == _compose_head)
3780     {
3781         _compose_head = tmp;
3782         return;
3783     }
3784     
3785     // There exists a cache.  Add this compose window to the tail.
3786     for (a_node=_compose_head; a_node; a_node=a_node->next)
3787     {
3788         if (NULL == a_node->next)
3789         {
3790             a_node->next = tmp;
3791             return;
3792         }
3793     }
3794 }
3795
3796 SendMsgDialog*
3797 Compose::getUnusedWin()
3798 {
3799     if (NULL == _compose_head) return NULL;
3800    
3801     Compose::Compose_Win* a_node = NULL; 
3802     Compose::Compose_Win* the_node = NULL; 
3803     
3804     // Find a node with unused smd.  Return smd
3805     for (a_node=_compose_head; a_node; a_node=a_node->next)
3806     {
3807         if (!a_node->in_use)
3808         {
3809             a_node->in_use = TRUE;
3810             _not_in_use--;
3811             return a_node->win;
3812         }
3813     }
3814
3815    return NULL;
3816 }
3817
3818 // Get a compose window either by creating a new SendMsgDialog or
3819 // from the recycle list.
3820 SendMsgDialog *
3821 Compose::getWin()
3822 {
3823     SendMsgDialog *newsend = NULL;
3824     
3825 #ifdef DTMAIL_TOOLTALK
3826     if (_timeout_id)
3827     {
3828         XtRemoveTimeOut(_timeout_id);
3829         _timeout_id = 0;
3830     }
3831 #endif
3832     
3833     newsend = getUnusedWin();
3834
3835     if (!newsend)
3836     {
3837         // We have no unused SMDs around; therefore, create new window.
3838         theRoamApp.busyAllWindows();
3839         newsend = new SendMsgDialog();
3840         newsend->initialize();
3841         _num_created++;
3842         putWin(newsend, TRUE);
3843         theRoamApp.unbusyAllWindows();
3844     }
3845     else
3846     {
3847         newsend->resetHeaders();
3848         newsend->displayInCurrentWorkspace();
3849     }
3850
3851     newsend->text_unselected();
3852     newsend->manage();
3853     newsend->startAutoSave();
3854     
3855     // Get new Message Handle
3856     newsend->setMsgHnd();
3857     char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
3858     newsend->setTitle(ttl);
3859     newsend->setIconTitle(ttl);
3860     return newsend;
3861 }
3862
3863
3864 void
3865 SendMsgDialog::setTitle(char *subject)
3866 {
3867     char *format = "%s - %s";
3868     char *prefix = GETMSG(DT_catd, 1, 6, "Mailer");
3869     char *new_title;
3870     int   len;
3871
3872     len = strlen(format) + strlen(prefix) + strlen(subject) + 1;
3873     new_title = new char[len];
3874     sprintf(new_title, format, prefix, subject);
3875
3876     title(new_title);
3877     delete [] new_title; 
3878 }
3879
3880 void
3881 SendMsgDialog::resetHeaders(void)
3882 {
3883     DtMail::Session *m_session = theRoamApp.session()->session();
3884     const char * value = NULL;
3885     DtMailEnv error;
3886     int i, j;
3887
3888     m_session->mailRc(error)->getValue(error, "additionalfields", &value);
3889
3890     // Return if no props were applied and headers did not change.
3891     if ((_additionalfields == NULL && value == NULL) ||
3892         ( _additionalfields != NULL && value != NULL &&
3893           strcmp (_additionalfields, value) == 0))
3894     {
3895         if (NULL != value)
3896           free((void*) value);
3897
3898         return;
3899     }
3900
3901     // User changed the Header list via props. Recreate list
3902     // and menus...
3903
3904     // First hide all shown headers 
3905     for (i=0; i < _header_list.length(); i++) {
3906         HeaderList * hl = _header_list[i];
3907         if (hl->show == SMD_SHOWN) {
3908                 hl->show = SMD_HIDDEN;
3909                 XtUnmanageChild(hl->form_widget);
3910         }
3911     }
3912
3913     // Now remove the old list.
3914     DtVirtArray<PropStringPair *> results(8);
3915     parsePropString(_additionalfields, results);
3916     for (i=0, j=results.length(); i < j; i++) {
3917         PropStringPair * psp = results[i];
3918         int slot = lookupHeader(psp->label);
3919         // dont allow removal of default headers.
3920         HeaderList * hl = _header_list[slot];
3921         if (!reservedHeader(hl->label)) {
3922                 if (slot != -1) 
3923                         hl->show = SMD_NEVER;
3924         }
3925         else if (hl->value != NULL) {
3926                 free(hl->value);
3927                 hl->value = NULL;
3928         }
3929     }
3930     while(results.length()) {
3931         PropStringPair * psp = results[0];
3932         delete psp;
3933         results.remove(0);
3934     }
3935
3936     if (_additionalfields != NULL) 
3937         free(_additionalfields);
3938     if (value != NULL && *value != '\0')
3939         _additionalfields = strdup(value);
3940     else
3941         _additionalfields = NULL;
3942
3943     parsePropString(value, results);
3944
3945     // New List...
3946     for (j=results.length(), i=0; i < j; i++) {
3947         PropStringPair * psp = results[i];
3948         int slot = lookupHeader(psp->label);
3949         if (slot != -1) {
3950                 // Already in list
3951                 HeaderList * hl = _header_list[slot];
3952                 if (!reservedHeader(hl->label)) 
3953                         hl->show = SMD_HIDDEN;
3954                 if (hl->value != NULL) {
3955                         free(hl->value);
3956                         hl->value = NULL;
3957                 }
3958                 if (psp->value != NULL)
3959                         hl->value = strdup(psp->value);
3960                 continue;
3961         }       
3962         HeaderList * copy_hl = new HeaderList;
3963         copy_hl->label = strdup(psp->label);
3964         copy_hl->header = strdup(psp->label);
3965         if (psp->value)
3966                 copy_hl->value = strdup(psp->value);
3967         copy_hl->show = SMD_HIDDEN;
3968         _header_list.append(copy_hl);
3969     }
3970     while(results.length()) {
3971         PropStringPair * psp = results[0];
3972         delete psp;
3973         results.remove(0);
3974     }
3975
3976     createHeaders(_header_form);
3977     doDynamicHeaderMenus();
3978
3979     if (NULL != value)
3980       free((void*) value);
3981 }
3982
3983 void
3984 SendMsgDialog::setInputFocus(const int mode)
3985 {
3986     if (mode == 0) {
3987         // Set focus
3988         HeaderList * hl = _header_list[0];
3989         (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3990     }
3991     else if (mode == 1) {
3992         Widget edWid = _my_editor->textEditor()->get_editor();
3993         (void) XmProcessTraversal(edWid, XmTRAVERSE_CURRENT);
3994     }
3995 }
3996  
3997 void
3998 SendMsgDialog::attachmentFeedback(
3999     Boolean bval
4000 )
4001 {
4002     if (bval) {
4003         this->busyCursor();
4004     }
4005     else {
4006         this->normalCursor();
4007     }
4008 }
4009
4010 Boolean
4011 SendMsgDialog::hasAddressee()
4012 {
4013   DtMailEnv error;
4014   
4015   DtMail::Envelope * env = _msgHandle->getEnvelope(error);
4016   
4017   // Walk through the headers. 
4018   // Return TRUE if the message has a value for either of the
4019   // following headers: To:, Cc:, or Bcc:.
4020   // Return FALSE if none of the three headers have any value.
4021   
4022   for (int scan = 0; scan < _header_list.length(); scan++) {
4023     HeaderList * hl = _header_list[scan];
4024     if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
4025       continue;
4026     }
4027     if ((strcmp(hl->label, "To") == 0) ||
4028         (strcmp(hl->label, "Cc") == 0) || 
4029         (strcmp(hl->label, "Bcc") == 0)) {
4030       char * value = NULL;
4031       XtVaGetValues(hl->field_widget,
4032                     XmNvalue, &value,
4033                     NULL);
4034       if (value) {
4035         for (char *cv = value; *cv; cv++) {
4036           if (!isspace(*cv)) {
4037             XtFree(value);
4038             return(TRUE);       // text value contains contents
4039           }
4040         }
4041         XtFree(value);          // text value is "content free" - try the next one
4042       }
4043     }
4044   }
4045   return(FALSE);                // no field has contents 
4046 }