remove OSF1 support
[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     _textPopupMenuList->add( _edit_delete );
2376     _textPopupMenuList->add( _edit_select_all );
2377
2378     Widget parent = _my_editor->textEditor()->get_editor();
2379     _menuPopupText = new MenuBar(parent, "SendMsgTextPopup", XmMENU_POPUP);
2380     _textPopupMenu = _menuPopupText->addCommands(_textPopupMenuList, 
2381                                 FALSE, XmMENU_POPUP);
2382 }
2383
2384 static int cmp_prop_pair(const void *v1, const void *v2)
2385 {
2386     PropStringPair      *p1 = *((PropStringPair **) v1);
2387     PropStringPair      *p2 = *((PropStringPair **) v2);
2388     int                 ret;
2389
2390     ret =  strcmp((const char *) p1->label, (const char *) p2->label);
2391     return ret;
2392 }
2393
2394 static void alias_stuffing_func(char * key, void * data, void * client_data)
2395 {
2396     DtVirtArray<PropStringPair *>       *alias_list;
2397     PropStringPair                      *new_pair;
2398
2399     alias_list = (DtVirtArray<PropStringPair*> *) client_data;
2400     new_pair = new PropStringPair;
2401     new_pair->label = strdup(key);
2402     new_pair->value = strdup((char *)data);
2403     alias_list->append(new_pair);
2404 }
2405
2406 void
2407 SendMsgDialog::createAliasList(DtVirtArray<PropStringPair*> *aliases)
2408 {
2409     DtMailEnv           error;
2410     DtMail::Session     *d_session = theRoamApp.session()->session();
2411     DtMail::MailRc      *mail_rc = d_session->mailRc(error);
2412     int                 nalias = 0;
2413
2414     mail_rc->getAliasList(alias_stuffing_func, aliases);
2415
2416     if (nalias = aliases->length())
2417     {
2418         PropStringPair  **prop_pairs = NULL;
2419
2420         prop_pairs = (PropStringPair**) malloc(nalias*sizeof(PropStringPair*));
2421         int i;
2422         for (i=0; i<nalias; i++)
2423         {
2424             prop_pairs[i] = (*aliases)[0];
2425             aliases->remove(0);
2426         }
2427         qsort(prop_pairs, nalias, sizeof(PropStringPair*), cmp_prop_pair);
2428         for (i=0; i<nalias; i++)
2429           aliases->append(prop_pairs[i]);
2430         
2431         free((void*) prop_pairs);
2432     }
2433 }
2434
2435 void
2436 SendMsgDialog::destroyAliasList(DtVirtArray<PropStringPair*> *aliases)
2437 {
2438     while (aliases->length() >= 0)
2439     {
2440         PropStringPair  *prop_pair = (*aliases)[0];
2441         aliases->remove(0);
2442         delete(prop_pair);
2443     }
2444 }
2445
2446 // map_menu is used to figure out how many columns to split the menu
2447 // into.  It is a callback that is called when the menu is mapped.
2448 // If the menu is over half the height of the screen, it figures out
2449 // how many columns to make the menu, and sets its XmNnumColumns
2450 // attribute to that value.  It calculates the maximum number of columns
2451 // that would fit and never goes beyond that number.
2452
2453 static void map_alias_menu(Widget menu, XtPointer, XtPointer)
2454 {
2455     Position y;
2456     Dimension h, w;
2457     Dimension maxcols, newcols, columns;
2458     Dimension screenheight = (Dimension) HeightOfScreen(XtScreen(menu));
2459     Dimension fudgefact = 20; /* to allow for decorations on menu */
2460
2461     XtVaGetValues(
2462                 menu,
2463                 XmNheight, &h,
2464                 XmNwidth, &w,
2465                 XmNy, &y,
2466                 XmNnumColumns, &columns,
2467                 NULL);
2468
2469     if ((h + fudgefact) > (screenheight / 2))
2470     {
2471         // The menu is taller than half the screen. 
2472         // We need to find out how many more columns
2473         // to specify for the menu to make it fit.
2474
2475         newcols = (columns * ((h+fudgefact)/(screenheight/2))) + 1;
2476         maxcols = WidthOfScreen(XtScreen(menu))/(w/columns);
2477
2478         if (newcols > maxcols)
2479           newcols = maxcols;
2480
2481         XtVaSetValues(menu, XmNnumColumns, newcols, NULL);
2482     }
2483 }
2484
2485 void
2486 SendMsgDialog::aliasMenuButtonHandler(
2487                                 Widget,
2488                                 XtPointer client_data,
2489                                 XEvent *event,
2490                                 Boolean *)
2491 {
2492     Widget              menu = (Widget) client_data;
2493     XButtonEvent        *be = (XButtonEvent *) event;
2494
2495     if (event->xany.type != ButtonPress) return;
2496     if(be->button == theApplication->bMenuButton())
2497     {
2498         XmMenuPosition(menu, (XButtonEvent *)event);
2499         XtManageChild(menu);
2500     }
2501 }
2502
2503 Widget
2504 SendMsgDialog::createAliasPopupMenu(
2505                                 Widget parent,
2506                                 MenuBar **menubar,
2507                                 CmdList **cmdlist,
2508                                 DtVirtArray<PropStringPair*> *aliases)
2509 {
2510     Widget              menu = NULL;
2511     OtherAliasesCmd     *otherAliases =
2512                           new OtherAliasesCmd(
2513                                 "Other Aliases...",
2514                                 GETMSG(DT_catd, 1, 247, "Other Aliases..."),
2515                                 TRUE);
2516 #if defined(USE_TITLED_ALIAS_POPUPS)
2517     LabelCmd            *title =
2518                           new LabelCmd(
2519                                 "Mailer - Aliases",
2520                                 GETMSG(DT_catd, 1, 248, "Mailer - Aliases"),
2521                                 TRUE);
2522 #endif
2523     SeparatorCmd        *separator =
2524                           new SeparatorCmd("Separator","Separator", TRUE);
2525
2526     (*cmdlist) = new CmdList("AliasCommands", "AliasCommands");
2527 #if defined(USE_TITLED_ALIAS_POPUPS)
2528     (*cmdlist)->add(title);
2529     (*cmdlist)->add(separator);
2530 #endif
2531     for (int i=0, length=aliases->length(); i<length; i++)
2532     {
2533         PropStringPair  *prop_pair = (*aliases)[i];
2534
2535         AliasCmd *alias = new AliasCmd(
2536                                 strdup(prop_pair->label),
2537                                 strdup(prop_pair->label),
2538                                 TRUE,
2539                                 parent);
2540         (*cmdlist)->add(alias);
2541     }
2542     if (0 < aliases->length())
2543       (*cmdlist)->add(separator);
2544     (*cmdlist)->add(otherAliases);
2545
2546     *menubar = new MenuBar(parent, "AliasesPopup", XmMENU_POPUP);
2547     menu = (*menubar)->addCommands((*cmdlist), FALSE, XmMENU_POPUP);
2548
2549     XtAddEventHandler(
2550                 parent,
2551                 ButtonPressMask,
2552                 FALSE,
2553                 aliasMenuButtonHandler,
2554                 (XtPointer) menu);
2555
2556     XtVaSetValues(
2557                 menu,
2558                 XmNpacking, XmPACK_COLUMN,
2559                 XmNorientation, XmVERTICAL,
2560                 NULL);
2561
2562     XtAddCallback(
2563                 menu,
2564                 XmNmapCallback, &map_alias_menu,
2565                 NULL);
2566
2567     return menu;
2568 }
2569
2570 void
2571 SendMsgDialog::destroyAliasPopupMenu(
2572                                 Widget  parent,
2573                                 MenuBar *menubar,
2574                                 CmdList *cmdlist,
2575                                 Widget  menu)
2576 {
2577     XtRemoveEventHandler(
2578                 parent,
2579                 ButtonPressMask,
2580                 FALSE,
2581                 aliasMenuButtonHandler,
2582                 (XtPointer) menu);
2583
2584     XtRemoveCallback(
2585                 menu,
2586                 XmNmapCallback, &map_alias_menu,
2587                 NULL);
2588
2589     XtDestroyWidget(menu);
2590
2591     delete menubar;
2592     delete cmdlist;
2593 }
2594
2595 Widget
2596 SendMsgDialog::getHeaderWidget(const char *hdrname)
2597 {
2598     for (int i=0, length=_header_list.length(); i<length; i++)
2599     {
2600         HeaderList      *hdritem = _header_list[i];
2601
2602         if (0 == strncmp(hdrname, hdritem->header, strlen(hdrname)))
2603           return hdritem->field_widget;
2604     }
2605
2606     return NULL;
2607 }
2608
2609 void
2610 SendMsgDialog::createAliasPopupMenus(void)
2611 {
2612     Widget      w;
2613
2614     w = getHeaderWidget(DtMailMessageTo);
2615     if (NULL != w)
2616       _toPopupMenu = createAliasPopupMenu(
2617                                         w,
2618                                         &_toPopupMenuBar,
2619                                         &_toPopupCmdlist,
2620                                         _aliasList);
2621
2622     w = getHeaderWidget(DtMailMessageCc);
2623     if (NULL != w)
2624       _ccPopupMenu = createAliasPopupMenu(
2625                                         w,
2626                                         &_ccPopupMenuBar,
2627                                         &_ccPopupCmdlist,
2628                                         _aliasList);
2629
2630     w = getHeaderWidget(DtMailMessageBcc);
2631     if (NULL != w)
2632       _bccPopupMenu = createAliasPopupMenu(
2633                                         w,
2634                                         &_bccPopupMenuBar,
2635                                         &_bccPopupCmdlist,
2636                                         _aliasList);
2637 }
2638
2639 void
2640 SendMsgDialog::destroyAliasPopupMenus(void)
2641 {
2642     Widget      w;
2643
2644     w = getHeaderWidget(DtMailMessageTo);
2645     destroyAliasPopupMenu(w, _toPopupMenuBar, _toPopupCmdlist, _toPopupMenu);
2646     _toPopupMenuBar = NULL;
2647     _toPopupCmdlist = NULL;
2648     _toPopupMenu = NULL;
2649
2650     w = getHeaderWidget(DtMailMessageCc);
2651     destroyAliasPopupMenu(w, _ccPopupMenuBar, _ccPopupCmdlist, _ccPopupMenu);
2652     _ccPopupMenuBar = NULL;
2653     _ccPopupCmdlist = NULL;
2654     _ccPopupMenu = NULL;
2655
2656     w = getHeaderWidget(DtMailMessageBcc);
2657     destroyAliasPopupMenu(w, _bccPopupMenuBar, _bccPopupCmdlist, _bccPopupMenu);
2658     _bccPopupMenuBar = NULL;
2659     _bccPopupCmdlist = NULL;
2660     _bccPopupMenu = NULL;
2661 }
2662
2663 void
2664 SendMsgDialog::createFormatMenu()
2665 {
2666     CmdList *cmdList;
2667     _format_separator = new SeparatorCmd( "Separator","Separator", TRUE );
2668
2669     cmdList = new CmdList( "Format", GETMSG(DT_catd, 1, 152,"Format") );
2670     
2671     _format_word_wrap = new WordWrapCmd ( 
2672                                           "Word Wrap",
2673                                           GETMSG(DT_catd, 1, 153, "Word Wrap"), 
2674                                           TRUE, 
2675                                           this );
2676     _format_settings = new FormatCmd ( "Settings...",
2677                                        GETMSG(DT_catd, 1, 154, "Settings..."), 
2678                                        TRUE, 
2679                                        this );
2680     
2681     cmdList->add( _format_word_wrap );
2682     cmdList->add( _format_settings );
2683     cmdList->add( _format_separator);
2684     
2685     _templates = new CmdList ( "Templates", GETMSG(DT_catd, 1, 157, "Templates") );
2686     addTemplates(_templates);
2687     
2688     cmdList->add(_templates);
2689     
2690     cmdList->add( _format_separator );
2691
2692     _format_menu = _menuBar->addCommands ( &_format_cascade, cmdList, 
2693                         FALSE, XmMENU_BAR);
2694
2695     delete cmdList;
2696
2697     doDynamicHeaderMenus();
2698
2699     if (_template_count == 0 && _templates->getPaneWidget())
2700     {
2701         XtSetSensitive(_templates->getPaneWidget(), FALSE);
2702     }
2703     
2704 }
2705
2706 void
2707 SendMsgDialog::addTemplates(CmdList * subCmd)
2708 {
2709     DtMailEnv error;
2710
2711     _template_count = 0;
2712     
2713     if (_templateList == NULL)
2714         return;
2715
2716     DtMail::Session *m_session = theRoamApp.session()->session();
2717     char * expanded_list = m_session->expandPath(error, _templateList);
2718
2719     DtVirtArray<PropStringPair *> templates(8);
2720     parsePropString(expanded_list, templates);
2721     free(expanded_list);
2722
2723     _template_count = templates.length();
2724
2725     for (int tmp = 0; tmp < _template_count; tmp++) {
2726         PropStringPair * psp = templates[tmp];
2727         if (psp->label && psp->value) {
2728             Cmd * button = new TemplateCmd(strdup(psp->label), 
2729                                            strdup(psp->label), 
2730                                            1, 
2731                                            this, 
2732                                            psp->value);
2733             subCmd->add(button);
2734         }
2735     }
2736
2737     while (templates.length()) {
2738         PropStringPair * psp = templates[0];
2739         delete psp;
2740         templates.remove(0);
2741     }
2742 }
2743
2744 void
2745 SendMsgDialog::initialize()
2746 {
2747     Cardinal n = 0;
2748     Arg args[1];
2749     const char * hideAttachPane = NULL;
2750     DtMailEnv error;
2751     
2752     // Without the TearOffModelConverter call, there will be warning messages:
2753     // Warning: No type converter registered for 'String' to 'TearOffModel' 
2754     // conversion.
2755     
2756     XmRepTypeInstallTearOffModelConverter();
2757     MenuWindow::initialize();
2758
2759     char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
2760     setTitle(ttl);
2761
2762     XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++;
2763     XtSetValues( _w, args, n);
2764     
2765     _genDialog = new DtMailGenDialog("Dialog", _main);
2766     
2767     // See if the .mailrc specifies if attachPane is to be shown or hid 
2768     // at SMD startup time.
2769     
2770     DtMail::Session *m_session = theRoamApp.session()->session();
2771     m_session->mailRc(error)->getValue(error, "hideattachments", 
2772                                 &hideAttachPane);
2773     
2774     if (!hideAttachPane) {
2775         _show_attach_area = TRUE;
2776     }
2777     else {
2778         _show_attach_area = FALSE;
2779         // The user wants to hide attachments
2780
2781         this->hideAttachArea();
2782     }
2783     if (NULL != hideAttachPane)
2784       free((void*) hideAttachPane);
2785
2786     _confirm_attachment_threshold = get_confirm_attachment_threshold();
2787     
2788     // Log Message Toggle button.  A LogMsgCmd is a ToggleButtonCmd....
2789     const char * logfile = NULL;
2790     const char * value = NULL;
2791     m_session->mailRc(error)->getValue(error, "record", &logfile);
2792     if(logfile == NULL)
2793         _file_log->deactivate();
2794     else
2795         _file_log->activate();
2796
2797     m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2798     if (logfile == NULL || error.isNotSet()) {
2799         // logfile is not specified or "dontlogmessages" is TRUE
2800         setLogState(DTM_FALSE);
2801         ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2802     } else {
2803         // logfile is specified and "dontlogmessages" is FALSE
2804         setLogState(DTM_TRUE);
2805         ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2806     }
2807     
2808     if (NULL != logfile)
2809       free((void*) logfile);
2810     if (NULL != value)
2811       free((void*) value);
2812
2813     // Word Wrap Toggle button.  A WordWrapCmd is a ToggleButtonCmd...
2814     ((ToggleButtonCmd *)_format_word_wrap)->setButtonState(
2815                            ((WordWrapCmd *)_format_word_wrap)->wordWrap(), 
2816                            FALSE
2817                            );
2818
2819     // Initialize the Edit menu
2820
2821     this->text_unselected();
2822         
2823     setIconName(ComposeIcon);
2824 }
2825
2826 static void
2827 Self_destruct(XtPointer, XtIntervalId *)
2828 {
2829 #ifdef WM_TT
2830     fprintf(stderr, "DEBUG: Self_destruct(): invoked!\n");
2831 #endif
2832     
2833     
2834     XtRemoveAllCallbacks(
2835                          theApplication->baseWidget(), 
2836                          XmNdestroyCallback);
2837     delete theApplication;
2838 }
2839
2840 // Clears Compose window title, header fields, text, and attachment areas.
2841 void
2842 SendMsgDialog::reset()
2843 {
2844     _my_editor->textEditor()->clear_contents();
2845     _my_editor->attachArea()->resetPendingAction();
2846
2847     _takeDown = FALSE;
2848     // This will deselect any Attachment action, if any available now.
2849     // Also deselect text menu items....
2850
2851     this->deactivate_default_attach_menu();
2852     this->text_unselected();
2853     this->all_attachments_deselected();
2854     _att_undelete->deactivate();  // This needs to be done in addition
2855
2856     this->get_editor()->attachArea()->removeCurrentAttachments();
2857
2858     // Unmanage the dialog
2859     this->unmanage();
2860
2861     if (_show_attach_area) { // .mailrc wants default attach area invisible
2862
2863     // Unmanage the attach Area.  Set the show_pane button.
2864     // This is done because if we are caching this window (after 
2865     // unmanaging), we don't want the window to pop back up, on uncaching,
2866     // with the attachment pane visible, etc..
2867
2868         this->showAttachArea();
2869     }
2870     else {
2871         this->hideAttachArea();
2872     }
2873
2874     // Need to destroy current Message handle.
2875     delete _msgHandle;    // All its body parts are deleted.
2876     _msgHandle = NULL;
2877     _lastAttBP = NULL;    // So just set this to NULL.
2878     // Delete or set to NULL ???
2879     _inclMsgHandle = NULL; 
2880     _inclMsgHasText = 0;
2881
2882     for (int clear = 0; clear < _header_list.length(); clear++) {
2883         HeaderList * hl = _header_list[clear];
2884
2885         // Bugfix: Old selection area remained selected, after text cleared
2886         // and parent widget unmanged, and then managed again for next
2887         // Compose.  (So new text in old select area was still being selected).
2888         // Perhaps, this is a Motif bug ... but this fixes the problem.
2889         XmTextFieldClearSelection( hl->field_widget, CurrentTime );
2890
2891         if (hl->value) {
2892             XtVaSetValues(hl->field_widget,
2893                           XmNvalue, hl->value,
2894                           NULL);
2895         }
2896         else {
2897             XtVaSetValues(hl->field_widget,
2898                           XmNvalue, "",
2899                           NULL);
2900         }
2901     }
2902     // Reset the Log state in case the user happened to change it.
2903     DtMail::Session *m_session = theRoamApp.session()->session();
2904     const char * logfile = NULL;
2905     const char * value = NULL;
2906     DtMailEnv error;
2907
2908     m_session->mailRc(error)->getValue(error, "record", &logfile);
2909     if(logfile == NULL)
2910         _file_log->deactivate();
2911     else
2912         _file_log->activate();
2913
2914     m_session->mailRc(error)->getValue(error, "dontlogmessages", &value);
2915     if (logfile == NULL || error.isNotSet()) {
2916         // logfile is not specified or "dontlogmessages" is TRUE
2917         setLogState(DTM_FALSE);
2918         ((ToggleButtonCmd *)_file_log)->setButtonState(FALSE, TRUE);
2919     } else {
2920         // logfile is specified and "dontlogmessages" is FALSE
2921         setLogState(DTM_TRUE);
2922         ((ToggleButtonCmd *)_file_log)->setButtonState(TRUE, TRUE);
2923     }
2924         
2925     if (NULL != logfile)
2926       free((void*) logfile);
2927     if (NULL != value)
2928       free((void*) value);
2929 }
2930
2931 // Recycles Compose window.
2932 void
2933 SendMsgDialog::quit(Boolean delete_win)
2934 {
2935
2936     // There are several ways we could have reached here.
2937     // 1) From the user choosing Send.
2938     // 2) From the user clicking on the Close button or Close menu item
2939     // 3) The user choosing Close from window manager menu.
2940     // For (1), we just forge ahead.  For that, the _takeDown boolean
2941     // is set in send_message() method.
2942     // For (2), the boolean is set in goAway().
2943     // For (3), we call goAway() which sets the _takeDown depending on
2944     // a dialog negotiation if SMD has contents.
2945
2946      if (_file_include->fileBrowser() != NULL) 
2947          XtPopdown(XtParent(_file_include->fileBrowser())); 
2948      if (_att_add->fileBrowser() != NULL) 
2949          XtPopdown(XtParent(_att_add->fileBrowser())); 
2950
2951      if (_file_save_as->fileBrowser() != NULL) 
2952          XtPopdown(XtParent(_file_save_as->fileBrowser())); 
2953      if (_att_save->fileBrowser() != NULL) 
2954          XtPopdown(XtParent(_att_save->fileBrowser())); 
2955
2956     if (!_takeDown) {
2957         // Check to see if it's the first time through the quit()
2958         // method.  Set _first_time to FALSE so that we don't come
2959         // down this path again until we're done quitting or bad
2960         // things will happen.
2961         if (_first_time == TRUE) {
2962             _first_time = FALSE;
2963             this->goAway(TRUE);
2964             // We're done quitting, so we can set _first_time to TRUE again.
2965             _first_time = TRUE;
2966         }
2967         return;
2968     }  
2969
2970     stopAutoSave();
2971     
2972 #ifdef DTMAIL_TOOLTALK
2973     //  For explanation of dtmail_mapped, look at RoamApp.h.
2974     if ( started_by_tt && (0 == theCompose.getTimeOutId()) &&
2975          (theCompose.numUnusedWindows() == theCompose.numCreatedWindows()) &&
2976          !dtmail_mapped )
2977     {
2978         int id;
2979         id = XtAppAddTimeOut(
2980                         theApplication->appContext(), 
2981                         (unsigned long)DESTRUCT_TIMEOUT, 
2982                         Self_destruct, NULL);
2983         theCompose.putTimeOutId(id);
2984     }
2985 #endif
2986
2987     if (delete_win)
2988      delete this;
2989     else
2990     {
2991         this->reset();
2992         theCompose.putWin(this, FALSE);
2993     }
2994     
2995     //
2996     // If there are no composer timeouts, check if its time to shutdown.
2997     //
2998     if (0 == theCompose.getTimeOutId()) theRoamApp.checkForShutdown();
2999 }
3000
3001 void
3002 SendMsgDialog::panicQuit()
3003 {
3004     //
3005     // Need to make sure the message is still valid before proceeding.
3006     // ::reset may have been called so the message may no longer be valid.
3007     //
3008     if (!isMsgValid())
3009       return;
3010
3011     doAutoSave();
3012 }
3013
3014
3015 // Given a file name, include the file as attachment.
3016 void
3017 SendMsgDialog::inclAsAttmt(char *file, char *name)
3018 {
3019     this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3020                                                     file, name);
3021     this->setLastAttBP();
3022     
3023 }
3024
3025 // Given a buffer, include its content as an attachment.
3026 void
3027 SendMsgDialog::inclAsAttmt(unsigned char *contents, int len, char *name)
3028 {
3029     DtMailBuffer mbuf;
3030
3031     mbuf.buffer = (void *)contents;
3032     mbuf.size = (unsigned long)len;
3033     this->get_editor()->attachArea()->addAttachment(_msgHandle, _lastAttBP,
3034                                                     (String)name, mbuf);
3035     this->setLastAttBP();
3036 }
3037
3038 // Given a RFC_822_Message formatted buffer, parse it and fill the Compose Window.
3039 void
3040 SendMsgDialog::parseNplace(char *contents, int len)
3041 {
3042     // 1. Create message handle for contents
3043
3044     DtMailEnv error;
3045     DtMail::Session * d_session = theRoamApp.session()->session();
3046     DtMailBuffer mbuf;
3047     
3048     mbuf.buffer = (void *)contents;
3049     mbuf.size = (unsigned long)len;
3050     
3051     DtMail::Message * msg = d_session->messageConstruct(error,
3052                                                         DtMailBufferObject,
3053                                                         &mbuf,
3054                                                         NULL,
3055                                                         NULL,
3056                                                         NULL);
3057     if ( !msg ) {
3058         return;
3059     } else if ( error.isSet() ) {
3060         if  ( (DTMailError_t) error == DTME_UnknownFormat ) {
3061             // The content does not have header info.  Therefore, store
3062             // everything as text.
3063             _my_editor->textEditor()->set_contents((const char *)mbuf.buffer,
3064                                                    mbuf.size);
3065             return;
3066         }
3067     }
3068
3069     char * status_string;
3070     DtMailBoolean first_bp_handled;
3071     first_bp_handled = _my_editor->textEditor()->set_message(
3072                                                              msg,
3073                                                              &status_string,
3074                                                              Editor::HF_NONE
3075                                                              );
3076     
3077     int num_bodyParts = msg->getBodyCount(error);
3078
3079     // Don't use setInclMsgHnd() because it causes the SMD's attachments
3080     // to get out of sink with the BE.  Just assign the newly created message
3081     // to _msgHandle.
3082     //
3083     if ((num_bodyParts > 1) || (!first_bp_handled)) {
3084         int start;
3085         if (first_bp_handled) {
3086             start = 2;
3087 //          setInclMsgHnd(msg, TRUE);
3088         }
3089         else {
3090             start = 1;
3091 //          setInclMsgHnd(msg, FALSE);
3092         }
3093
3094         if (_msgHandle) {
3095             delete _msgHandle;
3096         }
3097         _msgHandle = msg;
3098         _my_editor->attachArea()->parseAttachments(error,
3099                                                    msg,
3100                                                    TRUE,
3101 //                                                 FALSE,
3102                                                    start);
3103         // Need to call this after calling parseAttachments() so attachments
3104         // will be displayed in the attachment pane.
3105         _my_editor->manageAttachArea();
3106
3107         // Need to update this compose window's internal message handle.
3108         //
3109         // GL - calling updateMsgHndAtt is no longer necessary because we
3110         // just assigning the newly created msg to _msgHandle.
3111 //      updateMsgHndAtt();
3112     }
3113
3114     loadHeaders(msg, DTM_TRUE);
3115 }
3116
3117 // Given a RFC_822_Message formatted file, parse it and fill the Compose Window.
3118 void
3119 SendMsgDialog::parseNplace(const char * path)
3120 {
3121     // 1. Get file content into buffer.
3122     int fd = SafeOpen(path, O_RDONLY);
3123     if (fd < 0) {
3124         return;
3125     }
3126     
3127     struct stat buf;
3128     if (SafeFStat(fd, &buf) < 0) {
3129         close(fd);
3130         return;
3131     }
3132     
3133     _dead_letter_buf = new char[buf.st_size];
3134     if (!_dead_letter_buf) {
3135         close(fd);
3136         return;
3137     }
3138     
3139     if (SafeRead(fd, _dead_letter_buf, 
3140                  (unsigned int) buf.st_size) != buf.st_size) {
3141         delete [] _dead_letter_buf;
3142         close(fd);
3143         return;
3144     }
3145     
3146     parseNplace(_dead_letter_buf, (int) buf.st_size);
3147 }
3148
3149 void
3150 SendMsgDialog::text( const char *text )
3151 {
3152     _my_editor->textEditor()->set_contents( text, strlen(text) );
3153 }
3154
3155 void
3156 SendMsgDialog::append( const char *text )
3157 {
3158     _my_editor->textEditor()->append_to_contents( text, strlen(text) );
3159 }
3160
3161 char *
3162 SendMsgDialog::text()
3163 {
3164     // Potential memory leak here.  Because XmTextGetString returns 
3165     // pointer to space containing all the text in the widget.  Need 
3166     // to call XtFree after we use this space
3167     // Also DtEditor widget requires application to free data.
3168     
3169     return (_my_editor->textEditor()->get_contents());
3170     
3171 }
3172
3173
3174 void
3175 SendMsgDialog::text_selected()
3176 {
3177     // turn on sensitivity for Cut/Clear/Copy/Delete
3178     _edit_cut->activate();
3179     _edit_copy->activate();
3180     _edit_clear->activate();
3181     _edit_delete->activate();
3182     _edit_select_all->activate();
3183 }
3184
3185 void
3186 SendMsgDialog::text_unselected()
3187 {
3188     // turn off sensitivity for those items
3189     _edit_cut->deactivate();
3190     _edit_copy->deactivate();
3191     _edit_clear->deactivate();
3192     _edit_delete->deactivate();
3193 }
3194
3195 // Attachments
3196
3197 void
3198 SendMsgDialog::attachment_selected()
3199 {
3200     _att_save->activate();
3201     _att_delete->activate();
3202     _att_rename->activate();
3203     
3204 }
3205
3206 void
3207 SendMsgDialog::all_attachments_selected()
3208 {
3209     _att_delete->activate();
3210     _att_save->deactivate();
3211     _att_rename->deactivate();
3212
3213     if (_attachmentActionsList != NULL) {
3214         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3215         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3216                                 _attachmentActionsList);
3217         delete _attachmentActionsList;
3218         _attachmentActionsList = NULL;
3219     }
3220
3221 }
3222
3223
3224 void
3225 SendMsgDialog::all_attachments_deselected()
3226 {
3227     _att_save->deactivate();
3228     _att_delete->deactivate();
3229     _att_rename->deactivate();
3230     
3231     if (_attachmentActionsList != NULL) {
3232         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3233         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3234                                 _attachmentActionsList);
3235         delete _attachmentActionsList;
3236         _attachmentActionsList = NULL;
3237     }
3238     
3239 }
3240
3241
3242 void
3243 SendMsgDialog::addAttachmentActions(
3244                                     char **actions,
3245                                     int indx
3246                                     )
3247 {
3248     int i;
3249     char *anAction;
3250     AttachmentActionCmd *attachActionCmd;
3251     
3252     if (_attachmentActionsList == NULL) { 
3253         _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3254     }
3255     else {
3256         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3257         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3258                                         _attachmentActionsList);
3259         delete _attachmentActionsList;
3260         _attachmentActionsList = new CmdList("AttachmentActions", "AttachmentActions");
3261     }
3262     
3263     char *actionLabel;         
3264     for (i = 0; i < indx; i++) {
3265         anAction = actions[i];
3266         actionLabel = DtActionLabel(anAction);   // get the localized action label
3267         attachActionCmd = new AttachmentActionCmd(
3268                                                   anAction,
3269                                                   actionLabel,
3270                                                   this,
3271                                                   i);
3272         _attachmentActionsList->add(attachActionCmd);
3273         
3274     }
3275     _attachmentMenu = _menuBar->addCommands(
3276                                             _attachmentMenu, 
3277                                             _attachmentActionsList
3278                                             );
3279     _attachmentPopupMenu = _menuPopupAtt->addCommands(
3280                                             _attachmentPopupMenu, 
3281                                             _attachmentActionsList
3282                                             );
3283 }
3284
3285 void
3286 SendMsgDialog::removeAttachmentActions()
3287 {
3288     
3289     // Stubbed out for now
3290 }
3291
3292 void
3293 SendMsgDialog::invokeAttachmentAction(
3294                                       int index
3295                                       )
3296 {
3297     DtMailEditor *editor = this->get_editor();
3298     AttachArea *attacharea = editor->attachArea();
3299     Attachment *attachment = attacharea->getSelectedAttachment();
3300     
3301     attachment->invokeAction(index);
3302 }
3303
3304 void
3305 SendMsgDialog::selectAllAttachments()
3306 {
3307     
3308     DtMailEditor *editor = this->get_editor();
3309     AttachArea *attachArea = editor->attachArea();
3310     
3311     attachArea->selectAllAttachments();
3312     
3313 }
3314
3315
3316 void
3317 SendMsgDialog::activate_default_attach_menu()
3318 {
3319     _att_select_all->activate();
3320 }
3321
3322 void
3323 SendMsgDialog::deactivate_default_attach_menu()
3324 {
3325     _att_select_all->deactivate();
3326 }
3327
3328
3329 void
3330 SendMsgDialog::delete_selected_attachments()
3331 {
3332     DtMailEnv mail_error;
3333     
3334     // Initialize the mail_error.
3335
3336     mail_error.clear();
3337     
3338     AttachArea *attachArea = _my_editor->attachArea();
3339     attachArea->deleteSelectedAttachments(mail_error);
3340     
3341     if (mail_error.isSet()) {
3342         // do something
3343     }
3344     
3345     // Activate this button to permit the user to undelete.
3346     
3347     _att_undelete->activate();
3348     
3349     // Deactivate buttons that will be activated when another
3350     // selection applies.
3351     
3352     _att_save->deactivate();
3353     _att_delete->deactivate();
3354     _att_rename->deactivate();
3355
3356     if (_attachmentActionsList) {
3357         _menuBar->removeCommands(_attachmentMenu, _attachmentActionsList);
3358         _menuPopupAtt->removeCommands(_attachmentPopupMenu, 
3359                                       _attachmentActionsList);
3360         delete _attachmentActionsList;
3361         _attachmentActionsList = NULL;
3362     }
3363 }
3364
3365 void
3366 SendMsgDialog::undelete_last_deleted_attachment()
3367 {
3368     DtMailEnv mail_error;
3369     
3370     // Initialize the mail_error.
3371     
3372     mail_error.clear();
3373     
3374     AttachArea *attachArea = _my_editor->attachArea();
3375     attachArea->undeleteLastDeletedAttachment(mail_error);
3376     
3377     if (mail_error.isSet()) {
3378         // do something
3379     }
3380     
3381     if(_my_editor->attachArea()->getIconSelectedCount()) 
3382         _att_delete->activate();
3383
3384     if (attachArea->getDeleteCount() == 0) {
3385         _att_undelete->deactivate();
3386     }
3387 }
3388
3389 Boolean
3390 SendMsgDialog::renameAttachmentOK()
3391 {
3392     AttachArea *attachArea = _my_editor->attachArea();
3393     
3394     if (attachArea->getIconSelectedCount() > 1) {
3395        char *buf = new char[512];
3396         
3397        sprintf(buf, "%s", GETMSG(DT_catd, 5, 4, "Select only one attachment\n\
3398 and then choose rename"));
3399         
3400         _genDialog->setToQuestionDialog(
3401                                         GETMSG(DT_catd, 5, 2, "Mailer"),
3402                                         buf);
3403         
3404         char * helpId = DTMAILHELPSELECTONEATTACH;
3405         
3406         int answer = _genDialog->post_and_return(helpId);
3407         
3408         delete [] buf;
3409         return(FALSE);
3410     }
3411     else {
3412         return(TRUE);
3413     }
3414 }
3415
3416 void
3417 SendMsgDialog::showAttachArea()
3418 {
3419     DtMailEditor *editor = this->get_editor();
3420     editor->showAttachArea();
3421     ((ToggleButtonCmd *)_att_show_pane)->setButtonState(TRUE, FALSE);
3422 }
3423
3424 void
3425 SendMsgDialog::hideAttachArea()
3426 {
3427     DtMailEditor *editor = this->get_editor();
3428     editor->hideAttachArea();
3429     ((ToggleButtonCmd *)_att_show_pane)->setButtonState(FALSE, FALSE);
3430 }
3431
3432 int
3433 SendMsgDialog::lookupHeader(const char * name)
3434 {
3435     for (int h = 0; h < _header_list.length(); h++) {
3436         HeaderList * hl = _header_list[h];
3437         if (hl->show != SMD_NEVER && 
3438                 strcmp(hl->label, name) == 0) {
3439             return(h);
3440         }
3441     }
3442
3443     return(-1);
3444 }
3445
3446 void
3447 SendMsgDialog::headerValueChanged(Widget,
3448                                   XtPointer client_data,
3449                                   XtPointer)
3450 {
3451     SendMsgDialog * self = (SendMsgDialog *)client_data;
3452     self->_headers_changed = DTM_TRUE;
3453 }
3454
3455 void
3456 SendMsgDialog::reattachHeaders(void)
3457 {
3458     // We have to walk through the entire list of headers, attaching
3459     // the shown headers to the ones above them.
3460     //
3461     HeaderList * hl = _header_list[0];
3462     Widget previous_form = hl->form_widget;
3463
3464     for (int h = 1; h < _header_list.length(); h++) {
3465         hl = _header_list[h];
3466
3467         switch(hl->show) {
3468           case SMD_ALWAYS:
3469             previous_form = hl->form_widget;
3470             break;
3471
3472           case SMD_SHOWN:
3473             XtVaSetValues(hl->form_widget,
3474                           XmNtopAttachment, XmATTACH_WIDGET,
3475                           XmNtopWidget, previous_form,
3476                           NULL);
3477             previous_form = hl->form_widget;
3478             break;
3479
3480           default:
3481             break;
3482         }
3483     }
3484
3485     forceFormResize(_main_form);
3486     forceFormResize(_header_form);
3487 }
3488
3489 void
3490 SendMsgDialog::justifyHeaders(void)
3491 {
3492     // Find out which header label has the longest display width to right
3493     // justify all labels.
3494     //
3495     Dimension longest = 0;
3496     Dimension w = 0;
3497     Dimension margin;
3498     for (int count = 0;  count < _header_list.length(); count++) {
3499         HeaderList * hl = _header_list[count];
3500         if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3501             continue;
3502         }
3503
3504         XtVaGetValues(hl->label_widget,
3505                       XmNwidth, &w,
3506                       XmNmarginLeft, &margin,
3507                       NULL);
3508         w -= margin;
3509         if ( w > longest ) {
3510             longest = w;
3511         }
3512     }
3513
3514     for (int adjust = 0;  adjust < _header_list.length();  adjust++) {
3515         HeaderList * hl = _header_list[adjust];
3516         if (hl->show == SMD_HIDDEN || hl->show == SMD_NEVER) {
3517             continue;
3518         }
3519
3520         XtVaGetValues(hl->label_widget,
3521                       XmNwidth, &w,
3522                       XmNmarginLeft, &margin,
3523                       NULL);
3524         w -= margin;
3525         XtVaSetValues(hl->label_widget, XmNmarginLeft, (longest-w) > 0 ? longest-w : 1, NULL );
3526     }
3527 }
3528
3529 void
3530 SendMsgDialog::forceFormResize(Widget form)
3531 {
3532     // The Motif Form widget is at least a little bit brain damaged.
3533     // We need to convince it to do the right thing after we make
3534     // minor adjustments in the children.
3535     //
3536     Dimension width, height, border;
3537     XtVaGetValues(form,
3538                   XmNwidth, &width,
3539                   XmNheight, &height,
3540                   XmNborderWidth, &border,
3541                   NULL);
3542
3543     XtVaSetValues(form,
3544                   XmNwidth, width + 1,
3545                   XmNheight, height + 1,
3546                   NULL);
3547
3548     XtVaSetValues(form,
3549                   XmNwidth, width,
3550                   XmNheight, height,
3551                   NULL);
3552 }
3553
3554
3555 Boolean
3556 SendMsgDialog::unfilled_headers()
3557 {
3558     // Walk through the headers. See if any of them have a value.
3559     //
3560     for (int scan = 0; scan < _header_list.length(); scan++) {
3561         HeaderList * hl = _header_list[scan];
3562         if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
3563             continue;
3564         }
3565
3566         char * value = NULL;
3567         XtVaGetValues(hl->field_widget,
3568                       XmNvalue, &value,
3569                       NULL);
3570         if (value) {
3571           if (strlen(value) > 0) {
3572               XtFree(value);
3573               return(FALSE);
3574           }
3575           XtFree(value);
3576         }
3577     }
3578     return(TRUE);
3579 }
3580
3581 // Method checks if self has text in it.  
3582
3583 Boolean
3584 SendMsgDialog::checkDirty()
3585 {
3586     if (this->unfilled_headers() && 
3587        (this->get_editor()->textEditor()->no_text()) && 
3588        (this->get_editor()->attachArea()->getIconCount() == 0) ) {
3589
3590         // return FALSE so quit() can go ahead.
3591
3592         return(FALSE);
3593
3594     } 
3595     else {
3596         return(TRUE);
3597     }
3598 }
3599
3600 Boolean
3601 SendMsgDialog::handleQuitDialog()
3602 {
3603     char *helpId;
3604
3605     DtMail::Session *m_session = theRoamApp.session()->session();
3606     const char * value = NULL;
3607     DtMailEnv error;
3608
3609     m_session->mailRc(error)->getValue(error, "expert", &value);
3610     if (error.isNotSet() && value != NULL)
3611     {
3612         if (NULL != value)
3613           free((void*) value);
3614
3615         return (TRUE); 
3616     }
3617
3618     DtMailGenDialog *dialog = this->genDialog();
3619
3620     dialog->setToQuestionDialog(
3621                         GETMSG(
3622                                 DT_catd, 
3623                                 1, 
3624                                 99, 
3625                                 "Mailer - Close"),
3626                         GETMSG(
3627                                 DT_catd, 
3628                                 3, 
3629                                 58, 
3630                                 "The Compose window contains text or\n\
3631 attachments that will be lost if\n\
3632 the window is closed.\n\
3633 Close the Compose window?")
3634                 );
3635     helpId = DTMAILHELPCLOSECOMPOSEWINDOW;
3636     if ( dialog->post_and_return(
3637                         GETMSG(
3638                                DT_catd, 
3639                                1, 
3640                                100, 
3641                                "OK"),
3642                         GETMSG(
3643                                DT_catd, 
3644                                1, 
3645                                101, 
3646                                "Cancel"),
3647                         helpId) == 1 ) {    // Close selected
3648             
3649         return(TRUE);
3650     }
3651     else {
3652         return(FALSE);  // Cancel selected
3653     }
3654 }
3655
3656 void
3657 SendMsgDialog::goAway(
3658     Boolean checkForDirty
3659 )
3660 {
3661     
3662     if (!checkForDirty) {
3663         _takeDown = TRUE;
3664         this->quit();
3665     }
3666     else {
3667         // Check to see if self has contents (ie., is dirty)
3668
3669         Boolean is_dirty = this->checkDirty();
3670
3671         if (is_dirty) {
3672             if (isIconified()) {
3673                 MainWindow::manage();
3674             }
3675
3676             // Enquire if user really wants this window to go away
3677
3678             Boolean really_quit = this->handleQuitDialog();
3679             if (!really_quit) {
3680                 return;
3681             }
3682         }
3683         _takeDown = TRUE;
3684         this->quit();
3685     }
3686 }
3687
3688 void
3689 SendMsgDialog::manage()
3690 {
3691     MenuWindow::manage();
3692     // Set focus 
3693     HeaderList * hl = _header_list[0];
3694     (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3695    
3696 }
3697
3698 void
3699 SendMsgDialog::unmanage()
3700 {
3701     MenuWindow::unmanage();
3702     XFlush(XtDisplay(this->_main_form));
3703     XSync(XtDisplay(this->_main_form), False);
3704 }
3705     
3706
3707 Compose::Compose()
3708 {
3709     _compose_head = NULL;
3710     _not_in_use = 0;
3711     _num_created = 0;
3712     _timeout_id = 0;
3713 }
3714
3715 Compose::~Compose()
3716 {
3717     Compose::Compose_Win *a_node;
3718     Compose::Compose_Win *next_node;
3719
3720     theRoamApp.registerPendingTask();
3721
3722     a_node = _compose_head;
3723     while (a_node)
3724     {
3725         next_node = a_node->next;
3726         if (!a_node->in_use)
3727         {
3728             delete a_node->win;
3729             free((void*) a_node);
3730         }
3731         a_node = next_node;
3732     }
3733
3734     theRoamApp.unregisterPendingTask();
3735 }
3736
3737 void
3738 Compose::putWin(SendMsgDialog *smd, Boolean in_use)
3739 {
3740     Compose::Compose_Win* a_node = NULL; 
3741     Compose::Compose_Win *tmp = NULL;
3742
3743     //
3744     // Update the _not_in_use count.
3745     //
3746     if (! in_use)
3747       _not_in_use++;
3748
3749     //
3750     // Check to see if compose window is already in the list.
3751     //
3752     for (a_node = _compose_head; a_node; a_node=a_node->next)
3753     {
3754         if (a_node->win == smd)
3755         {
3756             a_node->in_use = in_use;
3757             return;
3758         }
3759     }
3760
3761     // Need new node with smd.
3762     tmp = (Compose::Compose_Win *)malloc(sizeof(Compose::Compose_Win));
3763     tmp->win = smd;
3764     tmp->next = NULL;
3765     tmp->in_use = in_use;
3766
3767     // If nothing is cached so far, add this Compose window to the head.
3768     if (NULL == _compose_head)
3769     {
3770         _compose_head = tmp;
3771         return;
3772     }
3773     
3774     // There exists a cache.  Add this compose window to the tail.
3775     for (a_node=_compose_head; a_node; a_node=a_node->next)
3776     {
3777         if (NULL == a_node->next)
3778         {
3779             a_node->next = tmp;
3780             return;
3781         }
3782     }
3783 }
3784
3785 SendMsgDialog*
3786 Compose::getUnusedWin()
3787 {
3788     if (NULL == _compose_head) return NULL;
3789    
3790     Compose::Compose_Win* a_node = NULL; 
3791     Compose::Compose_Win* the_node = NULL; 
3792     
3793     // Find a node with unused smd.  Return smd
3794     for (a_node=_compose_head; a_node; a_node=a_node->next)
3795     {
3796         if (!a_node->in_use)
3797         {
3798             a_node->in_use = TRUE;
3799             _not_in_use--;
3800             return a_node->win;
3801         }
3802     }
3803
3804    return NULL;
3805 }
3806
3807 // Get a compose window either by creating a new SendMsgDialog or
3808 // from the recycle list.
3809 SendMsgDialog *
3810 Compose::getWin()
3811 {
3812     SendMsgDialog *newsend = NULL;
3813     
3814 #ifdef DTMAIL_TOOLTALK
3815     if (_timeout_id)
3816     {
3817         XtRemoveTimeOut(_timeout_id);
3818         _timeout_id = 0;
3819     }
3820 #endif
3821     
3822     newsend = getUnusedWin();
3823
3824     if (!newsend)
3825     {
3826         // We have no unused SMDs around; therefore, create new window.
3827         theRoamApp.busyAllWindows();
3828         newsend = new SendMsgDialog();
3829         newsend->initialize();
3830         _num_created++;
3831         putWin(newsend, TRUE);
3832         theRoamApp.unbusyAllWindows();
3833     }
3834     else
3835     {
3836         newsend->resetHeaders();
3837         newsend->displayInCurrentWorkspace();
3838     }
3839
3840     newsend->text_unselected();
3841     newsend->manage();
3842     newsend->startAutoSave();
3843     
3844     // Get new Message Handle
3845     newsend->setMsgHnd();
3846     char *ttl = GETMSG(DT_catd, 1, 160, "New Message");
3847     newsend->setTitle(ttl);
3848     newsend->setIconTitle(ttl);
3849     return newsend;
3850 }
3851
3852
3853 void
3854 SendMsgDialog::setTitle(char *subject)
3855 {
3856     char *format = "%s - %s";
3857     char *prefix = GETMSG(DT_catd, 1, 6, "Mailer");
3858     char *new_title;
3859     int   len;
3860
3861     len = strlen(format) + strlen(prefix) + strlen(subject) + 1;
3862     new_title = new char[len];
3863     sprintf(new_title, format, prefix, subject);
3864
3865     title(new_title);
3866     delete [] new_title; 
3867 }
3868
3869 void
3870 SendMsgDialog::resetHeaders(void)
3871 {
3872     DtMail::Session *m_session = theRoamApp.session()->session();
3873     const char * value = NULL;
3874     DtMailEnv error;
3875     int i, j;
3876
3877     m_session->mailRc(error)->getValue(error, "additionalfields", &value);
3878
3879     // Return if no props were applied and headers did not change.
3880     if ((_additionalfields == NULL && value == NULL) ||
3881         ( _additionalfields != NULL && value != NULL &&
3882           strcmp (_additionalfields, value) == 0))
3883     {
3884         if (NULL != value)
3885           free((void*) value);
3886
3887         return;
3888     }
3889
3890     // User changed the Header list via props. Recreate list
3891     // and menus...
3892
3893     // First hide all shown headers 
3894     for (i=0; i < _header_list.length(); i++) {
3895         HeaderList * hl = _header_list[i];
3896         if (hl->show == SMD_SHOWN) {
3897                 hl->show = SMD_HIDDEN;
3898                 XtUnmanageChild(hl->form_widget);
3899         }
3900     }
3901
3902     // Now remove the old list.
3903     DtVirtArray<PropStringPair *> results(8);
3904     parsePropString(_additionalfields, results);
3905     for (i=0, j=results.length(); i < j; i++) {
3906         PropStringPair * psp = results[i];
3907         int slot = lookupHeader(psp->label);
3908         // dont allow removal of default headers.
3909         HeaderList * hl = _header_list[slot];
3910         if (!reservedHeader(hl->label)) {
3911                 if (slot != -1) 
3912                         hl->show = SMD_NEVER;
3913         }
3914         else if (hl->value != NULL) {
3915                 free(hl->value);
3916                 hl->value = NULL;
3917         }
3918     }
3919     while(results.length()) {
3920         PropStringPair * psp = results[0];
3921         delete psp;
3922         results.remove(0);
3923     }
3924
3925     if (_additionalfields != NULL) 
3926         free(_additionalfields);
3927     if (value != NULL && *value != '\0')
3928         _additionalfields = strdup(value);
3929     else
3930         _additionalfields = NULL;
3931
3932     parsePropString(value, results);
3933
3934     // New List...
3935     for (j=results.length(), i=0; i < j; i++) {
3936         PropStringPair * psp = results[i];
3937         int slot = lookupHeader(psp->label);
3938         if (slot != -1) {
3939                 // Already in list
3940                 HeaderList * hl = _header_list[slot];
3941                 if (!reservedHeader(hl->label)) 
3942                         hl->show = SMD_HIDDEN;
3943                 if (hl->value != NULL) {
3944                         free(hl->value);
3945                         hl->value = NULL;
3946                 }
3947                 if (psp->value != NULL)
3948                         hl->value = strdup(psp->value);
3949                 continue;
3950         }       
3951         HeaderList * copy_hl = new HeaderList;
3952         copy_hl->label = strdup(psp->label);
3953         copy_hl->header = strdup(psp->label);
3954         if (psp->value)
3955                 copy_hl->value = strdup(psp->value);
3956         copy_hl->show = SMD_HIDDEN;
3957         _header_list.append(copy_hl);
3958     }
3959     while(results.length()) {
3960         PropStringPair * psp = results[0];
3961         delete psp;
3962         results.remove(0);
3963     }
3964
3965     createHeaders(_header_form);
3966     doDynamicHeaderMenus();
3967
3968     if (NULL != value)
3969       free((void*) value);
3970 }
3971
3972 void
3973 SendMsgDialog::setInputFocus(const int mode)
3974 {
3975     if (mode == 0) {
3976         // Set focus
3977         HeaderList * hl = _header_list[0];
3978         (void) XmProcessTraversal(hl->field_widget, XmTRAVERSE_CURRENT);
3979     }
3980     else if (mode == 1) {
3981         Widget edWid = _my_editor->textEditor()->get_editor();
3982         (void) XmProcessTraversal(edWid, XmTRAVERSE_CURRENT);
3983     }
3984 }
3985  
3986 void
3987 SendMsgDialog::attachmentFeedback(
3988     Boolean bval
3989 )
3990 {
3991     if (bval) {
3992         this->busyCursor();
3993     }
3994     else {
3995         this->normalCursor();
3996     }
3997 }
3998
3999 Boolean
4000 SendMsgDialog::hasAddressee()
4001 {
4002   DtMailEnv error;
4003   
4004   DtMail::Envelope * env = _msgHandle->getEnvelope(error);
4005   
4006   // Walk through the headers. 
4007   // Return TRUE if the message has a value for either of the
4008   // following headers: To:, Cc:, or Bcc:.
4009   // Return FALSE if none of the three headers have any value.
4010   
4011   for (int scan = 0; scan < _header_list.length(); scan++) {
4012     HeaderList * hl = _header_list[scan];
4013     if (hl->show != SMD_ALWAYS && hl->show != SMD_SHOWN) {
4014       continue;
4015     }
4016     if ((strcmp(hl->label, "To") == 0) ||
4017         (strcmp(hl->label, "Cc") == 0) || 
4018         (strcmp(hl->label, "Bcc") == 0)) {
4019       char * value = NULL;
4020       XtVaGetValues(hl->field_widget,
4021                     XmNvalue, &value,
4022                     NULL);
4023       if (value) {
4024         for (char *cv = value; *cv; cv++) {
4025           if (!isspace(*cv)) {
4026             XtFree(value);
4027             return(TRUE);       // text value contains contents
4028           }
4029         }
4030         XtFree(value);          // text value is "content free" - try the next one
4031       }
4032     }
4033   }
4034   return(FALSE);                // no field has contents 
4035 }