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