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