Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtmail / dtmail / AttachArea.C
1 /*
2  *+SNOTICE
3  *
4  *      $TOG: AttachArea.C /main/18 1999/03/25 14:16:24 mgreess $
5  *
6  *      RESTRICTED CONFIDENTIAL INFORMATION:
7  *      
8  *      The information in this document is subject to special
9  *      restrictions in a confidential disclosure agreement between
10  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
11  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
12  *      Sun's specific written approval.  This document and all copies
13  *      and derivative works thereof must be returned or destroyed at
14  *      Sun's request.
15  *
16  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
17  *
18  *+ENOTICE
19  */
20
21 #ifndef I_HAVE_NO_IDENT
22 #endif
23
24 #include <EUSCompat.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <sys/stat.h>
28 #if defined(USL) || defined(__uxp__)
29 #define S_ISSOCK(mode) ((mode & S_IFMT) == S_IFSOCK)
30 #endif
31 #include <stdio.h>
32 #include <Dt/Editor.h>
33 #include <Xm/ColorObjP.h>
34
35 #include "EUSDebug.hh"
36
37 #if defined(NEED_MMAP_WRAPPER)
38 extern "C" {
39 #endif
40
41 #include <sys/mman.h>
42
43 #if defined(NEED_MMAP_WRAPPER)
44 }
45 #endif
46
47
48 extern "C" {
49 extern XtPointer _XmStringUngenerate (
50                                 XmString string,
51                                 XmStringTag tag,
52                                 XmTextType tag_type,
53                                 XmTextType output_type);
54 }
55
56
57 #include <stdio.h>
58 #include <ctype.h>
59 #include <sys/stat.h>
60 #include <sys/types.h>
61
62
63 #include <Xm/BulletinB.h>
64 #include <Xm/Form.h>
65 #include <Xm/RowColumn.h>
66 #include <Xm/ScrolledW.h>
67 #include <Xm/DrawingA.h>
68 #include <Xm/ScrollBar.h>
69 #include <Xm/PushBG.h>
70 #include <Xm/FileSB.h>
71 #include <Xm/SelectioB.h>
72 #include <Xm/Label.h>
73 #include <Xm/LabelG.h>
74 #include <Xm/AtomMgr.h>
75 #include <Xm/Xm.h>
76 #include <Xm/Screen.h>
77 #include <Xm/ToggleB.h>
78 #include <X11/IntrinsicP.h>
79 #include <X11/Xatom.h>
80
81 #include "Attachment.h"
82 #include "AttachArea.h"
83 #include "Icon.h"
84 #include "MenuBar.h"
85 #include "RoamApp.h"
86 #include "RoamMenuWindow.h"
87
88 #include "MsgScrollingList.hh"
89 #include "ViewMsgDialog.h"
90 #include "SendMsgDialog.h"
91 #include "MailMsg.h"            // DT_catd defined here
92 #include <DtMail/DtMail.hh>    // time_t defined here
93 #include <DtMail/IO.hh>                    // SafeAccess...
94
95 #include "Help.hh"
96 #include "DtMailHelp.hh"
97
98 extern nl_catd  DtMailMsgCat;
99
100 #define equal(a, b) (!strcmp(a,b))
101 #define HSPACE 10
102 #define VSPACE 10
103 #define MAXATOM 2048    // ?????
104
105 // This is the new one
106 AttachArea::AttachArea ( 
107         Widget parent, 
108         DtMailEditor *owner,
109         char *name
110 ) : UIComponent (name)
111 {
112     // Unique stuff
113
114     _myOwner = owner;
115     _parent  = parent;
116
117     _attachmentList=NULL;
118
119     _w = NULL;
120     _iconCount = 0;
121     _iconSelectedCount = 0;
122     _deleteCount = 0;
123
124     _fsDialog = NULL;
125     _fsState = NOTSET;
126     _lastRow = 0;
127     _currentRow = 0;
128     _attachmentsSize = 0;
129     _selectedAttachmentsSize = 0;
130     _clientData = NULL;
131     _renameDialog = NULL;
132
133     _myRMW = NULL;
134     _myVMD = NULL;
135     _mySMD = NULL;
136     
137     _pendingAction = FALSE;
138     _numPendingActions = 0;
139
140     _attach_area_selection_state = AA_SEL_NONE;
141     _cache_single_attachment = NULL;
142
143 }
144
145 void
146 AttachArea::initialize()
147 {
148
149     // We're making the assumption here that this widget's parent`
150     // is also a form
151
152     XtWidgetGeometry size;
153     Dimension parWid, parHeight;
154     Dimension txt_w, txt_h;
155     XmFontList fl;
156     XmString xms;
157
158     int         colorUse;
159     short       act, inact, prim, second, text;
160     XmPixelSet  pixels[XmCO_NUM_COLORS];
161
162
163     _w = XtVaCreateManagedWidget (
164                         "AttachPane",
165                         xmFormWidgetClass, _parent, 
166                         NULL);
167
168     // Get pixel data.
169     XmeGetColorObjData(XtScreen(_parent), &colorUse, pixels, XmCO_NUM_COLORS,
170                        &act, &inact, &prim, &second, &text);
171     _foreground = pixels[text].fg;
172     _background = pixels[text].sc;
173
174     parWid = _myOwner->textEditor()->get_text_width();
175
176     fl = _myOwner->textEditor()->get_text_fontList();
177     xms = XmStringCreateLocalized("Xyb");
178     XmStringExtent(fl, xms, &txt_w, &txt_h);
179     parHeight = txt_h + Icon::maxIconHeight() + (2*VSPACE);
180
181     _appBackground = _background;
182     _appForeground = _foreground;
183
184     _sw = XtVaCreateManagedWidget ( 
185                                     "AttachPane_ScrolledWindow", 
186                                     xmScrolledWindowWidgetClass, _w, 
187                                     XmNscrollingPolicy,  XmAPPLICATION_DEFINED,
188                                     XmNrightAttachment,  XmATTACH_FORM,
189                                     XmNleftAttachment,   XmATTACH_FORM,
190                                     XmNtopAttachment,    XmATTACH_FORM,
191                                     XmNshadowThickness, (Dimension)1,
192                                     XmNspacing, 2,
193                                     XmNwidth,        parWid,
194                                     XmNheight,       parHeight,
195                                     NULL);
196
197     rowOfAttachmentsStatus = XtCreateManagedWidget("Attachments_Status",
198                                 xmFormWidgetClass,
199                                 _w, NULL, 0);
200
201      XtVaSetValues(rowOfAttachmentsStatus,
202         XmNrightAttachment,     XmATTACH_FORM, 
203         XmNleftAttachment,      XmATTACH_FORM, 
204         XmNtopAttachment,       XmATTACH_WIDGET,
205         XmNtopWidget,           _sw,
206         XmNtopOffset,           5,
207         XmNbottomOffset,        5,
208         NULL );
209
210     this->addToRowOfAttachmentsStatus();
211
212     size.request_mode = CWHeight;
213     XtQueryGeometry(rowOfAttachmentsStatus, NULL, &size);
214
215     XtVaSetValues(
216         rowOfAttachmentsStatus,
217         XmNpaneMaximum, size.height,
218         XmNpaneMinimum, size.height,
219         NULL
220     );
221
222     _vsb = XtVaCreateManagedWidget("vsb", xmScrollBarWidgetClass, _sw,
223                     XmNorientation, XmVERTICAL,
224                     XmNsliderSize, 1,
225                     XmNmaximum, 1,
226                     XmNpageIncrement, 1,
227                     NULL);
228
229     XtAddCallback(
230         _vsb, 
231         XmNvalueChangedCallback,&AttachArea::valueChangedCallback,
232         (XtPointer) this 
233     );
234
235     XtAddCallback(
236         _vsb, 
237         XmNdragCallback, &AttachArea::dragCallback,
238         (XtPointer) this 
239     );
240
241     _clipWindow = XtVaCreateManagedWidget("AttachArea_clipWindow", 
242                     xmDrawingAreaWidgetClass, _sw,
243                     XmNresizePolicy, XmRESIZE_NONE,
244                     XmNbackground, _background,
245                     XmNwidth,        parWid,
246                     XmNheight,       parHeight,
247                     NULL);
248     XmScrolledWindowSetAreas(_sw, NULL, _vsb, _clipWindow);
249
250     XtManageChild(_clipWindow);
251     XtManageChild(_vsb);
252     XtManageChild(_sw);
253
254     
255     // Set RowCol to NULL here.
256     // It gets set in the expose_all_attachments.
257
258     _rc = NULL;
259
260     CalcSizeOfAttachPane();
261
262     installDestroyHandler();
263 }
264
265 AttachArea::~AttachArea()
266 {
267 }
268
269 void AttachArea::addToList( Attachment *attachment )
270 {
271     Attachment **newList;
272     int i;
273
274     newList = new Attachment*[ _iconCount + 1 ];
275     for(i=0; i < _iconCount; i++)
276         newList[i] = _attachmentList[i];
277  
278     if (_attachmentList)
279         delete []_attachmentList;
280
281     _attachmentList = newList;
282
283     _attachmentList[ _iconCount ] = attachment;
284
285     _iconCount++;
286     
287 //    setAttachmentsLabel();
288 }
289
290 #ifdef DEAD_WOOD
291 void AttachArea::setAttachmentsLabel( )
292 {
293     char *c = new char[256];
294     XmString xmstr;
295     String str;
296     unsigned int last_displayCount, last_selectedCount;
297     unsigned int displayCount = _iconCount - _deleteCount;
298     unsigned int attachmentsSize;
299
300     if((displayCount) == 0) {
301         XtUnmanageChild(_no_attachments_label);
302         XtUnmanageChild(_attachments_label);
303         XtUnmanageChild(_size_attachments_label);
304         XtUnmanageChild(_no_selected_label);
305         XtUnmanageChild(_selected_label);
306         XtUnmanageChild(_size_selected_label);
307     } else {
308         CalcAttachmentsSize();
309         attachmentsSize = getAttachmentsSize();
310
311         // Number of Attachments
312         XtVaGetValues(_no_attachments_label,
313                 XmNlabelString, &xmstr,
314                 NULL);
315         str = NULL;
316         str = (char *) _XmStringUngenerate(
317                                         xmstr, NULL,
318                                         XmMULTIBYTE_TEXT, XmMULTIBYTE_TEXT);
319         if (NULL == str) return; // internal error
320         last_displayCount = (unsigned int)strtol(str, NULL, 10);
321         XtFree(str);
322
323         // Number of Attachments Selected
324         XtVaGetValues(_no_selected_label,
325                 XmNlabelString, &xmstr,
326                 NULL);
327         str = NULL;
328         str = (char *) _XmStringUngenerate(
329                                         xmstr, NULL,
330                                         XmMULTIBYTE_TEXT, XmMULTIBYTE_TEXT);
331         if (NULL == str) return; // internal error
332         last_selectedCount = (unsigned int)strtol(str, NULL, 10);
333         XtFree(str);
334
335         if((last_displayCount == 0 && displayCount == 1) ||
336            (last_displayCount == 2 && displayCount == 1)) {
337             sprintf(c, GETMSG(DT_catd, 12, 1, "Attachment"));
338             XtVaSetValues(_attachments_label,
339                 XtVaTypedArg, XmNlabelString, XtRString, c, strlen(c)+1,
340                 NULL);
341         } else if(last_displayCount == 1 && displayCount == 2) {
342             sprintf(c, GETMSG(DT_catd, 12, 2, "Attachments"));
343             XtVaSetValues(_attachments_label,
344                 XtVaTypedArg, XmNlabelString, XtRString, c, strlen(c)+1,
345                 NULL);
346         }
347         if(last_displayCount != displayCount) {
348             sprintf(c, GETMSG(DT_catd, 12, 3, "displayCount"));
349             XtVaSetValues(_no_attachments_label,
350                 XtVaTypedArg, XmNlabelString, XtRString, c, strlen(c)+1,
351                 NULL);
352         }
353         sprintf(c, "(%s),", calcKbytes(attachmentsSize));
354         XtVaSetValues(_size_attachments_label,
355             XtVaTypedArg, XmNlabelString, XtRString, c, strlen(c)+1,
356             NULL);
357
358         if(last_selectedCount != _iconSelectedCount) {
359             sprintf(c, "%d", _iconSelectedCount); 
360             XtVaSetValues(_no_selected_label,
361                 XtVaTypedArg, XmNlabelString, XtRString, c, strlen(c)+1,
362                 NULL);
363             sprintf(c, "(%s)", calcKbytes(getSelectedAttachmentsSize())); 
364             XtVaSetValues(_size_selected_label,
365                 XtVaTypedArg, XmNlabelString, XtRString, c, strlen(c)+1,
366                 NULL);
367         }
368         if(!XtIsManaged(_no_attachments_label)) {
369             XtManageChild(_no_attachments_label);
370             XtManageChild(_attachments_label);
371             XtManageChild(_size_attachments_label);
372             XtManageChild(_no_selected_label);
373             XtManageChild(_selected_label);
374             XtManageChild(_size_selected_label);
375         }
376     }
377     delete [] c;
378 }
379 #endif /* DEAD_WOOD */
380
381 int AttachArea::getSelectedIconCount()
382 {
383     Attachment **list = getList();
384     int num_selected = 0;
385
386     for (int i = 0; i < getIconCount(); i++) {
387         if (!list[i]->isDeleted() && list[i]->isSelected())
388             num_selected++;
389     }
390     return (num_selected);
391 }
392
393 #ifdef DEAD_WOOD
394 void AttachArea::CalcAttachmentsSize( )
395 {
396      Attachment **list = getList();
397      unsigned int total = 0;
398      int i;
399
400      int num_icons = getIconCount();
401
402  
403      for(i=0;i<num_icons;i++)
404         if(!list[i]->isDeleted())
405             total += (unsigned int)list[i]->getContentsSize();
406      
407      setAttachmentsSize(total);
408 }
409 #endif /* DEAD_WOOD */
410
411 Attachment *
412 AttachArea::getSelectedAttachment()
413 {
414     return _cache_single_attachment;
415 }
416
417
418 void
419 AttachArea::MenuButtonHandler(
420     Widget ,
421     XtPointer cd,
422     XEvent *event,
423     Boolean *)
424 {
425         AttachArea *obj = (AttachArea *)cd;
426
427         if(event->xany.type != ButtonPress)
428                 return;
429
430         XButtonEvent *be = (XButtonEvent *)event;
431
432         if(be->button == theApplication->bMenuButton())
433                 obj->_myOwner->owner()->postAttachmentPopup(event);
434 }
435
436 void 
437 AttachArea::inputCallback(Widget, XtPointer client_data, XtPointer call_data)
438 {
439     AttachArea *obj = (AttachArea *) client_data;
440     XmDrawingAreaCallbackStruct *cb = (XmDrawingAreaCallbackStruct *)call_data;
441
442     if(cb->reason != XmCR_INPUT         ||
443         cb->event->xany.type != ButtonPress)
444         return;
445
446     if(((XButtonEvent *)cb->event)->button == Button1)
447         obj->unselectOtherSelectedAttachments(NULL);
448 }
449
450 void AttachArea::resizeCallback ( 
451                                   Widget w, 
452                                   XtPointer clientData, 
453                                   XtPointer //callData
454                                   )
455 {
456     Dimension wid;
457
458     AttachArea *obj = (AttachArea *) clientData;
459     XtVaGetValues(
460         w,
461         XmNwidth, &wid,
462         NULL
463     );
464
465     obj->resize(wid);
466 }
467
468 void AttachArea::resize(
469     Dimension wid
470 )
471 {
472     int i;
473     Attachment **list = getList();
474
475     _attachAreaWidth = wid;
476     XtVaSetValues(_clipWindow, XmNwidth, _attachAreaWidth, NULL);
477
478     for (i=0; i<getIconCount(); i++)
479       list[i]->unmanageIconWidget();
480
481     CalcAllAttachmentPositions();
482     CalcLastRow();
483     AdjustCurrentRow();
484     SetScrollBarSize(getLastRow()+1);
485     DisplayAttachmentsInRow(_currentRow);
486 }
487
488 void AttachArea::CalcSizeOfAttachPane( )
489 {
490     _attachAreaWidth = _myOwner->textEditor()->get_text_width();
491     XtVaSetValues(_clipWindow, XmNwidth, _attachAreaWidth, NULL);
492 }
493
494 void AttachArea::activateDeactivate()
495 {
496     //
497     // If exactly one icon is selected then activate the open command 
498     //
499
500     if(getIconSelectedCount() == 1) {
501         openCmd()->activate();
502     } else {
503         openCmd()->deactivate();
504     }
505
506     // If no icons are selected then deactivate the OK button on the FS Dialog
507
508     if(getIconSelectedCount() > 0) {
509         if(getFsDialog())
510             XtSetSensitive(
511                 XmSelectionBoxGetChild( 
512                     getFsDialog(), XmDIALOG_OK_BUTTON), TRUE
513                 );
514     } else {
515         if(getFsDialog())
516             XtSetSensitive(
517                 XmSelectionBoxGetChild( 
518                     getFsDialog(), XmDIALOG_OK_BUTTON
519                 ), 
520                 (getFsState() == SAVEAS) ? FALSE : TRUE
521             );
522     }
523 }
524
525 // Used by Compose window (SMD)
526 // Given a filename, add it to message and to attachArea.
527
528 Attachment*
529 AttachArea::addAttachment(
530                           DtMail::Message* msg,
531                           DtMail::BodyPart *lastAttBP,
532                           char *filename,
533                           char *name
534                           )
535 {
536     int fd;
537     struct stat s;
538     Boolean validtype = TRUE;
539     DtMail::BodyPart * bp = NULL;
540     DtMailEnv mail_error;
541     int answer;
542     char *helpId = NULL;
543
544     mail_error.clear();
545
546     char *errormsg = new char[512];
547     char *buf = new char[2048];
548     char *buffer = NULL, *lbl;
549     char *fname_start;
550
551     for (fname_start = filename + strlen(filename) - 1;
552          fname_start >= filename && *fname_start != '/'; fname_start--) {
553         continue;
554     }
555     if (*fname_start == '/') {
556         fname_start += 1;
557     }
558
559     bp = msg->newBodyPart(mail_error, lastAttBP);       
560
561     if (SafeAccess(filename, F_OK) != 0) {
562         sprintf(buf, GETMSG(DT_catd, 3, 34, "%s does not exist."),
563                 filename);
564         answer = this->handleErrorDialog(GETMSG(DT_catd, 1, 81, "Mailer"), 
565                                          buf);
566         delete [] buf;
567         delete [] errormsg;
568         return(NULL);
569     }
570
571     SafeStat(filename, &s);
572
573     if(S_ISFIFO(s.st_mode)) {
574         sprintf(errormsg,
575                 GETMSG(DT_catd, 12, 4, "Cannot attach FIFO files: %s"), filename);
576         validtype = FALSE;
577     } else if(S_ISCHR(s.st_mode)) {
578         sprintf(
579             errormsg,
580             GETMSG(DT_catd, 12, 5, "Cannot attach character special files: %s"), filename
581         );
582         validtype = FALSE;
583     } else if(S_ISDIR(s.st_mode)) {
584         sprintf(
585             errormsg,
586             GETMSG(DT_catd, 12, 6, "Cannot attach directories: %s"), filename
587         );
588         validtype = FALSE;
589     } else if(S_ISBLK(s.st_mode)) {
590         sprintf(errormsg,
591                 GETMSG(DT_catd, 12, 7, "Cannot attach block special files: %s"), filename
592         );
593         validtype = FALSE;
594     } else if(S_ISSOCK(s.st_mode)) {
595         sprintf(errormsg,
596                 GETMSG(DT_catd, 12, 8, "Cannot attach socket files: %s"), filename
597         );
598         validtype = FALSE;
599     }
600     if(validtype == FALSE) {
601         answer = this->handleErrorDialog(GETMSG(DT_catd, 1, 81, "Mailer"), 
602                                          errormsg,
603                                          NULL);
604         delete [] buf;
605         delete [] errormsg;
606         return(NULL);
607     }
608
609     fd = SafeOpen(filename, O_RDONLY);
610         
611     if (fd < 0) {
612         sprintf(buf, GETMSG(DT_catd, 3, 35, "Unable to open %s."), filename);
613         helpId = DTMAILHELPNOOPEN;
614         answer = this->handleErrorDialog(GETMSG(DT_catd, 1, 82, "Mailer"), 
615                                          buf,
616                                          helpId);
617         delete [] buf;
618         delete [] errormsg;
619         return(NULL);
620     }
621
622     int page_size = (int)sysconf(_SC_PAGESIZE);
623     size_t map_size = (size_t) (s.st_size + 
624                                     (page_size - (s.st_size % page_size)));
625     char * map;
626
627 #if defined(__osf__)
628     // This version of mmap does NOT allow requested length to be
629     // greater than the file size ...  in contradiction to the
630     // documentation (don't round up).
631     map = (char *) mmap(0, s.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
632 #else
633     map = (char *) mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
634 #endif
635
636     if (map == (char *)-1) {
637         // We could not map it for some reason. Let's just read it into
638         // buffer and pass it to XmText.
639         //
640
641         buffer = new char[s.st_size + 1];
642
643         if (!buffer) {
644             sprintf(buf, 
645                     GETMSG(DT_catd, 3, 36, "Unable to allocate memory."));
646             helpId = DTMAILHELPNOALLOCMEM;
647             answer = this->handleErrorDialog(GETMSG(DT_catd, 1, 83, "Mailer"), 
648                                              buf,
649                                              helpId);
650             return(NULL);
651         }
652
653         if (read(fd, buffer, (unsigned int) s.st_size) < 0) {
654             SafeClose(fd);
655             return(NULL);
656         }
657         buffer[s.st_size] = 0;
658         bp->setContents(
659                 mail_error, buffer, s.st_size, NULL, fname_start, 0, NULL
660                 );
661     }
662     else {
663         // We now have a mapped file. XmText wants a zero terminated
664         // buffer. We get luck with mmap because unless the file is
665         // an even page size, we will have some zero fill bytes that
666         // are legal to access.
667         //
668         // Of course in the case of an even page size file we must
669         // copy the buffer, terminate it and then give it to XmText.
670         //
671         bp->setContents(
672             mail_error, map, s.st_size, NULL, fname_start, 0, NULL
673         );
674         munmap(map, map_size);
675     }
676     SafeClose(fd);
677
678
679     // _iconCount + 1 because iconCount starts at 0 and we want 
680     // attachmentCount to begin at 1.  attachmentCount is set to be
681     // in the widget's userData.  
682
683
684     if(name)
685         lbl = strdup(name);
686     else {
687         if(strchr(filename, '/') == NULL) // The name does not include a slash
688             lbl = strdup(filename);
689         else                       // The name does include a slash
690             lbl = strdup(strrchr(filename, '/')+1);
691     }    
692     Attachment *attachment = new Attachment(this, lbl, bp, _iconCount + 1);
693     attachment->setAttachArea(this);
694     attachment->initialize();
695     addToList( attachment );
696
697     // Update the display.  The Compose Window needs immediate update.
698
699     this->manageList();
700
701     delete [] buf;
702     delete [] errormsg;
703     return(attachment);
704 }
705
706 Attachment*
707 AttachArea::addAttachment(
708     DtMail::Message *msg,
709     DtMail::BodyPart *lastAttBP,
710     String name,
711     DtMailBuffer buf
712 )
713 {
714     DtMailEnv mail_error;
715     DtMail::BodyPart * bp = NULL;
716
717     if (!name)
718         name = "noname";
719
720     mail_error.clear();
721
722     bp = msg->newBodyPart(mail_error, lastAttBP);
723     bp->setContents(mail_error, buf.buffer, buf.size, NULL, name, 0, NULL);
724
725     Attachment *attachment = new Attachment(this,
726                                             name,
727                                             bp,
728                                             _iconCount + 1);
729     attachment->setAttachArea(this);
730     attachment->initialize();
731     addToList(attachment);
732
733     // Update the display.  The Compose Window needs immediate update.
734
735     this->manageList();
736
737     return(attachment);
738 }
739
740 Attachment* 
741 AttachArea::addAttachment( 
742     String name,
743     DtMail::BodyPart *body_part
744 )
745 {
746     // _iconCount + 1 because iconCount starts at 0 and we want 
747     //  attachmentCount to begin at 1.  attachmentCount is set to be
748     // in the widget's userData.  
749
750     Attachment *attachment = new Attachment(
751                                         this, 
752                                         name, 
753                                         body_part, 
754                                         _iconCount + 1
755                                 );
756     attachment->setAttachArea(this);
757     attachment->initialize();
758     addToList( attachment );
759
760     return(attachment);
761 }
762
763 #ifdef DEAD_WOOD
764 void
765 AttachArea::add_attachment(
766     Attachment *attachment
767 )
768 {
769     
770      attachment->setAttachArea(this);
771      attachment->initialize();
772      addToList( attachment );
773 }
774
775 //
776 // This function truly deletes all the attachments in the AttachArea
777 // The widgets are unmanaged and the attachment classes are deleted.
778 //
779
780 void AttachArea::deleteAttachments( )
781 {
782     int i;
783     WidgetList deleteList;
784     int count;
785
786     Attachment **list = getList();
787  
788      // First, unmanaged all the attachment at once so there is no
789      // flickering when we delete them
790
791      deleteList = (WidgetList)XtMalloc(sizeof(Widget) * getIconCount());
792
793      for(i=0;i<getIconCount();i++)
794         deleteList[i] = list[i]->baseWidget();
795      XtUnmanageChildren(deleteList, i);
796
797      delete deleteList;
798      XtFree((char *)deleteList);
799  
800     // Delete each attachment in the list
801     count = getIconCount();
802     for(i=count-1;i>=0;i--) {
803         delete list[i];
804         decIconCount();
805     }
806
807      _iconCount = 0;
808      _iconSelectedCount = 0;
809      _deleteCount = 0;
810      CalcLastRow();
811      AdjustCurrentRow();
812      SetScrollBarSize(getLastRow()+1);
813      activateDeactivate();
814      _attachmentList=NULL;
815      _attachmentsSize = 0;
816      _selectedAttachmentsSize = 0;
817 }
818 #endif /* DEAD_WOOD */
819
820 void AttachArea::manageList( )
821 {
822     int i;
823     Attachment **list = getList();
824
825     for (i=0; i<getIconCount(); i++)
826       list[i]->unmanageIconWidget();
827
828     CalcAllAttachmentPositions();
829     CalcLastRow();
830     AdjustCurrentRow();
831     SetScrollBarSize(getLastRow()+1);
832     DisplayAttachmentsInRow(_currentRow);
833 }
834
835 #ifdef DEAD_WOOD
836 //
837 // Find the x and y position for a newly created attachment
838 //
839
840 void AttachArea::CalcAttachmentPosition(Attachment *item)
841 {
842     int i, j;
843     Boolean found_managed = FALSE;
844
845      Attachment **list = getList();
846      for(i=0, j=0;i<getIconCount();i++)
847         if(!list[i]->isDeleted()) {
848             j = i;
849             found_managed = TRUE;
850         }
851     calculate_attachment_position(
852        found_managed ? list[j] : (Attachment *)NULL, item
853        );
854
855 }      
856 #endif /* DEAD_WOOD */
857
858 //
859 // Display the attachments in row X
860 //
861
862 void AttachArea::DisplayAttachmentsInRow(unsigned int X)
863 {
864     int i;
865     int managecount, unmanagecount;
866
867     Attachment **list = getList();
868     WidgetList manageIconList, unmanageIconList;
869
870     if (getIconCount())
871     {
872         manageIconList = (WidgetList) XtMalloc(sizeof(Widget)*getIconCount());
873         unmanageIconList = (WidgetList) XtMalloc(sizeof(Widget)*getIconCount());
874      
875         managecount = unmanagecount = 0;
876         for(i=0;i<getIconCount();i++) {
877             if(!list[i]->isDeleted()) {
878                 if(list[i]->getRow() == X) {
879                     if(!list[i]->isManaged()) {
880                         manageIconList[managecount] = list[i]->baseWidget();
881                         managecount++;
882                     } 
883                 }
884                 else { // if deleted
885                     if(list[i]->isManaged()) {
886                         unmanageIconList[unmanagecount] = list[i]->baseWidget();
887                         unmanagecount++;
888                     }
889                 }
890             }
891         }
892
893         XtUnmanageChildren(unmanageIconList, unmanagecount);
894         XtManageChildren(manageIconList, managecount);
895
896
897         XtFree((char *)manageIconList);
898         XtFree((char *)unmanageIconList);
899     }
900
901     this->attachment_summary(_iconCount - _deleteCount, _deleteCount);
902 }
903
904 //
905 // Calculate the position of every non-deleted Attachment
906 //
907
908 void AttachArea::CalcAllAttachmentPositions()
909 {
910     int i, j;
911     Attachment **list = getList();
912  
913     j = -1;
914     for(i=0;i<getIconCount();i++) {
915         if(!list[i]->isDeleted()) {
916             calculate_attachment_position(
917                         (j == -1) ? (Attachment *)NULL : list[j],
918                         list[i]);
919             j = i;
920         }
921     }
922 }
923
924 //
925 // Determine the position of attachment "item" given reference
926 // attachment "ref"
927 //
928
929 void AttachArea::calculate_attachment_position(
930     Attachment *ref,
931     Attachment *item
932 )
933 {
934     if(ref == NULL) {
935         item->setX(HSPACE);
936         item->setY(VSPACE);
937         item->setRow(0);
938         return;
939     }
940     if(((Dimension)(ref->getX() + 
941                     ref->getWidth() + 
942                     HSPACE + 
943                     item->getWidth()))  > getAAWidth() ) {
944         item->setX(HSPACE);
945         item->setY(VSPACE);
946         item->setRow(ref->getRow() + 1);
947     } else {
948         item->setX(ref->getX() + ref->getWidth() + HSPACE);
949         item->setRow(ref->getRow());
950         item->setY(VSPACE);
951     }
952 }
953
954 //
955 // Invoked when the user moves the slider by any method
956 // If the user is dragging the slider then this callback
957 // is only invoked when the user releases the mouse button
958 //
959
960 void AttachArea::valueChangedCallback ( 
961     Widget, 
962     XtPointer clientData, 
963     XtPointer callData 
964 )
965 {
966     AttachArea *obj = (AttachArea *) clientData;
967
968     obj->valueChanged( callData );
969 }
970
971 void AttachArea::valueChanged( XtPointer callData )
972 {
973     XmScrollBarCallbackStruct *cbs = (XmScrollBarCallbackStruct *)callData;
974
975     _currentRow = cbs->value;
976     DisplayAttachmentsInRow(_currentRow);
977 }
978
979 //
980 // Invoked when the user drags the slider
981 //
982
983 void AttachArea::dragCallback ( 
984     Widget, 
985     XtPointer clientData, 
986     XtPointer callData 
987 )
988 {
989     AttachArea *obj = (AttachArea *) clientData;
990
991     obj->dragSlider( callData );
992 }
993
994 void AttachArea::dragSlider( XtPointer callData )
995 {
996     XmScrollBarCallbackStruct *cbs = (XmScrollBarCallbackStruct *)callData;
997
998     if(cbs->value == _currentRow)
999         return;
1000     _currentRow = cbs->value;
1001     DisplayAttachmentsInRow(_currentRow);
1002 }
1003
1004 //
1005 // Calculate the number of rows
1006 //
1007
1008 void AttachArea::CalcLastRow()
1009 {
1010     int i;
1011     unsigned row = 0;
1012      Attachment **list = getList();
1013  
1014      for(i=0;i<getIconCount();i++) {
1015         if(!list[i]->isDeleted()) {
1016             row = list[i]->getRow();
1017         }
1018      }
1019     _lastRow = row;
1020 }
1021
1022 //
1023 // Set the XmmNmaximum resource to size
1024 //
1025
1026 void AttachArea::SetScrollBarSize(unsigned int size)
1027 {
1028     XtVaSetValues(_vsb, 
1029         XmNmaximum, size,
1030         NULL);
1031 }
1032
1033 //
1034 // If the current row is greater than the last row, adjust the
1035 // current row to be equal to the last row.
1036 //
1037
1038 void AttachArea::AdjustCurrentRow()
1039 {
1040     if(_currentRow > _lastRow) {
1041         _currentRow = _lastRow;
1042         XtVaSetValues(_vsb, XmNvalue, _currentRow, NULL);
1043     }
1044 }
1045
1046 XmString
1047 AttachArea::getSelectedAttachName()
1048 {
1049     XmString str = (XmString)NULL;
1050
1051     if(_cache_single_attachment)
1052         str = _cache_single_attachment->getLabel();
1053
1054     return(str);
1055 }
1056
1057 void
1058 AttachArea::setSelectedAttachName(
1059     XmString new_name
1060 )
1061 {
1062     int i;
1063     Boolean set = FALSE;
1064     Attachment **list = this->getList();
1065
1066     XmString nn = XmStringCopy(new_name);
1067
1068     // Set name of first selected attachment to new_name
1069     for(i=0;i<this->getIconCount() && !set;i++)
1070         if(list[i]->isSelected()) {
1071             list[i]->rename(nn);
1072             set = TRUE;
1073         }
1074     this->manageList();
1075 }
1076
1077
1078 void
1079 AttachArea::attachmentSelected(
1080     Attachment *attachment
1081
1082 {
1083     // First deselect other selected attachments
1084     this->unselectOtherSelectedAttachments(attachment);
1085     
1086     // Enable the menu item at the toplevel shell's menubar.
1087     if(_cache_single_attachment == NULL)
1088         _myOwner->owner()->attachment_selected();
1089
1090     // Cache the single selected attachment
1091     _cache_single_attachment = attachment;
1092     _attach_area_selection_state = AA_SEL_SINGLE;
1093 }
1094
1095 void
1096 AttachArea::attachmentFeedback(
1097     Boolean value
1098 )
1099 {
1100     _myOwner->owner()->attachmentFeedback(value);
1101 }
1102     
1103 char *
1104 AttachArea::calcKbytes(unsigned int bytes)
1105 {
1106     static char kstring[64];
1107
1108     if(bytes < 103)
1109         sprintf(kstring, "%d bytes",bytes);
1110     else if(bytes < 1024)
1111         sprintf(kstring, " .%dk", bytes/103);
1112     else
1113         sprintf(kstring, "%dk", bytes/1024);
1114
1115     return kstring;
1116 }
1117
1118 void
1119 AttachArea::addToRowOfAttachmentsStatus()
1120 {
1121     XmString labelStr2;
1122
1123     // Size of first label
1124     
1125     labelStr2 = XmStringCreateLocalized(
1126                         GETMSG(DT_catd, 3, 37, "Summary of attachments"));
1127
1128
1129     _attachments_summary = XtCreateManagedWidget("Attachments_Summary", 
1130                                                  xmLabelWidgetClass,
1131                                                  rowOfAttachmentsStatus, NULL, 0);
1132
1133     XtVaSetValues(_attachments_summary,
1134                   XmNalignment, XmALIGNMENT_END,
1135                   XmNlabelString, labelStr2,
1136                   XmNrightAttachment, XmATTACH_FORM,
1137                   NULL );
1138
1139      XmStringFree(labelStr2);
1140 }
1141
1142 void
1143 AttachArea::parseAttachments(
1144     DtMailEnv &mail_error,
1145     DtMail::Message* msg,
1146     Boolean empty,
1147     int startBP
1148 )
1149 {
1150     DtMail::BodyPart * tmpBP;
1151     int index = 1;
1152     int num_attachments = 0;
1153     char * name;
1154
1155
1156     // First unmanage and empty out the current contents.
1157
1158     // SMD sets this boolean to FALSE so that previous message's attachments
1159     // are not cleared.  E.g. Including/forwarding multiple messages each
1160     // with attachments.
1161     // RMW sets this boolean to TRUE so that all attachments are cleared in
1162     // the attachment pane everytime a new message is displayed.
1163
1164     if ( empty ) {
1165
1166         // First unmanage the clipWindow.
1167         // Unmanaging the attachment pane is visually ugly
1168
1169         XtUnmanageChild(_clipWindow);
1170         this->clearAttachArea();
1171     }
1172
1173     _deleteCount = 0;
1174
1175     // Now fill list with new attachments.
1176
1177     tmpBP = msg->getFirstBodyPart(mail_error);
1178     if (mail_error.isSet()) {
1179         // do something
1180     }
1181
1182     // Sync up the index with the bodyPart from which to begin
1183     // adding attachments into attachPane.
1184     // 
1185     while (startBP > index) {
1186         tmpBP = msg->getNextBodyPart(mail_error, tmpBP);
1187         index++;
1188     }
1189     
1190     while (tmpBP != NULL) {
1191         num_attachments++;
1192         tmpBP->getContents(
1193                         mail_error,
1194                         NULL,
1195                         NULL,
1196                         NULL,
1197                         &name,
1198                         NULL,
1199                         NULL);
1200         
1201         if (mail_error.isSet()) {
1202             // do something
1203         }
1204
1205         // It is possible for an attachment to not have a name.
1206         if (!name) {
1207             name = "NoName";
1208         }
1209
1210         this->addAttachment(name, tmpBP);
1211         tmpBP = msg->getNextBodyPart(mail_error, tmpBP);
1212         if (mail_error.isSet()) {
1213             // do something
1214         }
1215
1216         free(name);
1217     }
1218 }
1219
1220 void
1221 AttachArea::attachment_summary(
1222     int live,
1223     int dead
1224 )
1225 {
1226
1227     char *buf = NULL;
1228     char * tmp1;
1229     char * tmp2;
1230
1231     if ((live == 1) && (dead == 0)) {
1232         tmp1 = GETMSG(DT_catd, 3, 38, "attachment");
1233         buf = new char[strlen(tmp1) + 64];
1234         sprintf(buf, "%d %s", live, tmp1);
1235     }
1236     else if ((live >= 0) && (dead == 0)) {
1237         /* NL_COMMENT
1238          * "attachments" is the plural form of "attachment".
1239          */
1240         tmp1 = GETMSG(DT_catd, 3, 39, "attachments");
1241         buf = new char[strlen(tmp1) + 64];
1242         sprintf(buf, "%d %s", live, tmp1);
1243     }
1244     else if ((live >= 0) && (dead > 0)) {
1245         tmp1 = GETMSG(DT_catd, 3, 40, "attachments");
1246         tmp2 = GETMSG(DT_catd, 3, 41, "deleted");
1247         buf = new char[strlen(tmp1) + strlen(tmp2) + 64];
1248         sprintf(buf, "%d %s, %d %s", live, tmp1, dead, tmp2);
1249     }
1250     
1251     if (buf) {
1252         XmString buf_str = XmStringCreateLocalized(buf);
1253         XtVaSetValues(_attachments_summary,
1254             XmNlabelString, buf_str,
1255             NULL );
1256     
1257         delete [] buf;
1258         XmStringFree(buf_str);
1259     }
1260 }
1261
1262 void
1263 AttachArea::manage()
1264 {
1265     Dimension wid;
1266     Dimension ht;
1267     Dimension pht;
1268     Widget sww;
1269
1270     // Update the display
1271     XtVaGetValues(this->baseWidget(), XmNwidth, &wid, NULL);
1272     sww = getSWWindow();
1273     this->manageList();
1274
1275     XtAddCallback(
1276                 _clipWindow, 
1277                 XmNresizeCallback, &AttachArea::resizeCallback,
1278                 (XtPointer) this );
1279
1280     XtAddCallback(
1281                 _clipWindow, 
1282                 XmNinputCallback, &AttachArea::inputCallback,
1283                 (XtPointer) this);
1284
1285     XtAddEventHandler(
1286                 _clipWindow, ButtonPressMask,
1287                 FALSE, MenuButtonHandler,
1288                 (XtPointer) this);
1289
1290     // hack
1291     XtVaGetValues(sww, XmNheight, &ht, NULL);
1292     XtVaGetValues(_w, XmNheight, &pht, NULL);
1293     // Manage the clipWindow back
1294     if (!XtIsManaged(_clipWindow)) XtManageChild(_clipWindow);
1295
1296     // hack
1297     XtVaSetValues(sww, XmNheight, ht, NULL);
1298     UIComponent::manage();
1299     XtVaSetValues(_w, XmNheight, pht, NULL);
1300 }
1301
1302 void
1303 AttachArea::unmanage()
1304 {
1305     int i;
1306     Attachment **list = getList();
1307
1308     // Unmanage the widgets it currently has
1309     for (i=0; i<getIconCount(); i++)
1310       list[i]->unmanageIconWidget();
1311
1312     XtRemoveCallback(
1313         _clipWindow, 
1314         XmNresizeCallback, &AttachArea::resizeCallback,
1315         (XtPointer) this );
1316
1317     XtRemoveCallback(
1318         _clipWindow, 
1319         XmNinputCallback, &AttachArea::inputCallback,
1320         (XtPointer) this );
1321
1322     XtRemoveEventHandler(
1323         _clipWindow, ButtonPressMask,
1324         FALSE, MenuButtonHandler,
1325         (XtPointer) this);
1326
1327     UIComponent::unmanage();
1328 }
1329
1330 void
1331 AttachArea::removeCurrentAttachments()
1332 {
1333
1334     Attachment **list = getList();
1335     int i;
1336
1337     // Unmanage the widgets it currently has
1338
1339     for (i=0; i<getIconCount(); i++) {
1340
1341         list[i]->unmanageIconWidget();
1342         list[i]->deleteIt();
1343     }
1344
1345     // Reset
1346     if (_attachmentList)
1347         delete []_attachmentList;
1348     _attachmentList = NULL;
1349     _iconCount = 0;
1350     _deleteCount = 0;
1351     _attach_area_selection_state = AA_SEL_NONE;
1352     _cache_single_attachment = NULL;
1353
1354     this->attachment_summary(_iconCount, _deleteCount);
1355 }
1356
1357 // Similar to removeCurrentAttachments().
1358 // Except we don't display a summary that there are no attachments.
1359 // Plus has potential for other (different) usage.
1360
1361 void
1362 AttachArea::clearAttachArea()
1363 {
1364
1365     Attachment **list = getList();
1366     int i;
1367
1368     // Unmanage the widgets it currently has
1369
1370     for (i=0; i<getIconCount(); i++) {
1371
1372         list[i]->unmanageIconWidget();
1373         delete list[i];
1374     }
1375
1376     // Reset
1377     if (_attachmentList)
1378         delete []_attachmentList;
1379     _attachmentList = NULL;
1380     _iconCount = 0;
1381     _deleteCount = 0;
1382     _attach_area_selection_state = AA_SEL_NONE;
1383     _cache_single_attachment = NULL;
1384 }
1385
1386 #ifdef DEAD_WOOD
1387 void
1388 AttachArea::saveAttachmentToFile(
1389     DtMailEnv &mail_error,
1390     char *save_path
1391 )
1392 {
1393
1394     Attachment *attachment = this->getSelectedAttachment();
1395
1396     if(attachment != NULL)
1397         attachment->saveToFile(mail_error, save_path);
1398
1399 }
1400 #endif /* DEAD_WOOD */
1401
1402 void
1403 AttachArea::deleteSelectedAttachments(
1404     DtMailEnv &         //mail_error
1405 )
1406 {
1407     Attachment **list = getList();
1408     int i;
1409     
1410     for (i = 0; i<getIconCount(); i++) {
1411         if (list[i]->isSelected() && !list[i]->isDeleted()) {
1412
1413             // unselect it first.  Else, when undeleted it comes
1414             // off selected
1415             
1416             list[i]->unselect();
1417             list[i]->deleteIt();
1418             _deleteCount++;
1419         }
1420     }
1421     
1422     // Unmanage all.
1423     // Their positions need to get recomputed and the undeleted
1424     // ones get remanaged in manageList().
1425
1426     for (i=0; i<getIconCount(); i++) {
1427         list[i]->unmanageIconWidget();
1428     }
1429
1430     _cache_single_attachment = NULL;
1431     _attach_area_selection_state = AA_SEL_NONE;
1432
1433     this->manageList();
1434 }
1435
1436
1437 #ifdef DEAD_WOOD
1438 void
1439 AttachArea::undeleteAllDeletedAttachments(
1440     DtMailEnv &         //mail_error
1441 )
1442 {
1443 }
1444 #endif /* DEAD_WOOD */
1445
1446 void
1447 AttachArea::undeleteLastDeletedAttachment(
1448     DtMailEnv &mail_error
1449 )
1450 {
1451
1452     Attachment *tmpAttachment;
1453     Attachment **list;
1454     time_t time_deleted = NULL, tmpTime = NULL;
1455     int i;
1456
1457     if (_deleteCount == 0) {
1458         return;
1459     }
1460
1461     list = getList();
1462
1463     tmpAttachment = list[0];
1464     time_deleted = tmpAttachment->getBodyPart()->getDeleteTime(mail_error);
1465     if (mail_error.isSet()) {
1466         // do something
1467     }
1468
1469     for (i=1; i<getIconCount(); i++) {
1470         if (list[i]->isDeleted()) {
1471             tmpTime = list[i]->getBodyPart()->getDeleteTime(mail_error);
1472             if (mail_error.isSet()) {
1473                 // do something
1474             }
1475             if ( tmpTime > time_deleted) {
1476                 time_deleted = tmpTime;
1477                 tmpAttachment = list[i];
1478             }
1479         }
1480     }
1481
1482     tmpAttachment->undeleteIt();
1483     _deleteCount--;
1484
1485     // Unmanage all.
1486     // Their positions need to get recomputed and the deleted
1487     // ones get remanaged in manageList().
1488
1489     for (i=0; i<getIconCount(); i++) {
1490         list[i]->unmanageIconWidget();
1491     }
1492
1493
1494     this->manageList();
1495
1496 }
1497
1498     
1499 void
1500 AttachArea::unselectOtherSelectedAttachments(
1501     Attachment *attachment
1502 )
1503 {
1504     if(_attach_area_selection_state == AA_SEL_NONE)
1505         return;
1506
1507     if(_attach_area_selection_state == AA_SEL_ALL) {
1508         int i;
1509         Attachment **list;
1510
1511         list = getList(); 
1512
1513         for (i=0; i < getIconCount(); i++) 
1514             if (list[i]->isSelected() && list[i] != attachment) 
1515                 list[i]->unselect();
1516     } 
1517     else if (_cache_single_attachment && 
1518             (attachment != _cache_single_attachment)) {
1519         _cache_single_attachment->unselect();
1520         _cache_single_attachment = NULL;
1521     }
1522
1523    if(attachment == NULL) {
1524         // Grey out the appropriate menu items in the RMW...
1525         _myOwner->owner()->all_attachments_deselected();
1526         _attach_area_selection_state = AA_SEL_NONE;
1527         _cache_single_attachment = NULL;
1528    }
1529 }
1530
1531 void
1532 AttachArea::addAttachmentActions(
1533     char **actions,
1534     int indx
1535 )
1536 {
1537
1538     _myOwner->owner()->addAttachmentActions(
1539                                 actions,
1540                                 indx
1541                         );
1542
1543 }
1544
1545 void
1546 AttachArea::setOwnerShell(
1547     RoamMenuWindow *rmw
1548 )
1549 {
1550     _myRMW = rmw;
1551 }
1552
1553 void
1554 AttachArea::setOwnerShell(
1555     ViewMsgDialog *vmd
1556 )
1557 {
1558     _myVMD = vmd;
1559 }
1560
1561 void
1562 AttachArea::setOwnerShell(
1563     SendMsgDialog *smd
1564 )
1565 {
1566     _mySMD = smd;
1567 }
1568
1569 Widget
1570 AttachArea::ownerShellWidget()
1571 {
1572     if (_myRMW) {
1573         return(_myRMW->baseWidget());
1574     }
1575     else if (_myVMD) {
1576         return(_myVMD->baseWidget());
1577     }
1578     else if (_mySMD) {
1579         return(_mySMD->baseWidget());
1580     }
1581     else {
1582         // Error out
1583     }
1584     return((Widget) NULL);
1585 }
1586
1587 Boolean
1588 AttachArea::isOwnerShellEditable()
1589 {
1590     // only SMD is editable
1591     if (_mySMD != NULL) {
1592         return TRUE;
1593     }
1594     else {
1595         return FALSE;
1596     }
1597 }
1598
1599 void
1600 AttachArea::setPendingAction(
1601     Boolean bval
1602 )
1603 {
1604     _pendingAction = bval;
1605     if (bval) {
1606         _numPendingActions++;
1607     }
1608     else {
1609         if (_numPendingActions > 0) {
1610             _numPendingActions--;
1611         }
1612     }
1613 }
1614
1615 void
1616 AttachArea::resetPendingAction()
1617 {
1618
1619     _numPendingActions = 0;
1620 }
1621
1622 int
1623 AttachArea::getNumPendingActions()
1624 {
1625     return(_numPendingActions);
1626 }
1627
1628 void
1629 AttachArea::selectAllAttachments()
1630 {
1631     
1632     Attachment **list;
1633     int numAttachments = getIconCount();
1634
1635     list = getList(); 
1636     
1637     if(list == NULL)
1638         return;
1639
1640     // if there's only 1 attachment, select it and
1641     // add its actions to the menu bar...
1642
1643     if (numAttachments == 1) {
1644         list[0]->primitive_select();
1645         list[0]->set_selected();
1646     }
1647     else {
1648         // More than 1 attachment.
1649         // Select them all. Don't enable their actions however.
1650
1651         for (int i=0; i < numAttachments; i++) 
1652             list[i]->primitive_select();
1653
1654         // Grey out the appropriate menu items in the RMW...
1655         _myOwner->owner()->all_attachments_selected();
1656         _cache_single_attachment = NULL;
1657         _attach_area_selection_state = AA_SEL_ALL;
1658     }
1659 }
1660
1661 int
1662 AttachArea::handleQuestionDialog(
1663     char *title,
1664     char *buf,
1665     char * helpId
1666 )
1667 {
1668     DtMailGenDialog *dialog;
1669     int answer;
1670
1671     if (_myRMW) {
1672         dialog = _myRMW->genDialog();
1673     }
1674     else if (_myVMD) {
1675         dialog = _myVMD->genDialog();
1676     }
1677     else if ( _mySMD) {
1678         dialog = _mySMD->genDialog();
1679     }
1680     else return(-1);
1681
1682     if (!dialog) return(-1);
1683
1684     dialog->setToQuestionDialog(
1685                         title,
1686                         buf);
1687     answer = dialog->post_and_return(helpId);
1688     return(answer);
1689 }
1690
1691 int
1692 AttachArea::handleErrorDialog(
1693     char *title,
1694     char *buf,
1695     char *helpId
1696 )
1697 {
1698     DtMailGenDialog *dialog;
1699     int answer;
1700
1701     if (_myRMW) {
1702         dialog = _myRMW->genDialog();
1703     }
1704     else if (_myVMD) {
1705         dialog = _myVMD->genDialog();
1706     }
1707     else if ( _mySMD) {
1708         dialog = _mySMD->genDialog();
1709     }
1710     else return(-1);
1711
1712     if (!dialog) return(-1);
1713
1714     dialog->setToErrorDialog(title, buf);
1715     answer = dialog->post_and_return(helpId);
1716     return(answer);
1717 }