dthelp: Change to ANSI function definitions
[oweals/cde.git] / cde / programs / dtmail / dtmail / MsgScrollingList.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: MsgScrollingList.C /main/38 1998/12/10 19:08:02 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 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42
43 #include <EUSCompat.h>
44 #include <ctype.h>
45 #include <assert.h>
46 #include <Xm/Text.h>
47 #include <Dt/Dts.h>
48
49 #include <DtMail/DtMailError.hh>
50 #include <DtMail/IO.hh>
51 #include "DtMailGenDialog.hh"
52 #include "DtMailHelp.hh"
53 #include "Help.hh"      // Remove after fixing problem with empty time headers
54 #include "MailMsg.h"
55 #include "MemUtils.hh"
56 #include "MsgHndArray.hh"
57 #include "MsgScrollingList.hh"
58 #ifdef DEAD_WOOD
59 #include "QueryDialogManager.hh"
60 #endif /* DEAD_WOOD */
61 #include "RoamApp.h"
62 #include "RoamMenuWindow.h"
63 #include "Sort.hh"
64
65
66 #include <X11/IntrinsicP.h> // Include for moving X location of titles
67
68 extern int force( Widget );
69
70 MsgScrollingList::MsgScrollingList(
71     RoamMenuWindow *menuwindow,
72     Widget parent,
73     char *name
74 )
75     : ScrollingList(
76         parent,
77         name
78 )
79 {
80     _parent=menuwindow;
81     _numbered = DTM_FALSE;
82     _selected_item_position=-1;
83     _displayed_item_position=-1;
84     _selection_on = FALSE;
85     _xmstr_collector = NULL;
86     _xtarg_collector = NULL;
87     _selected_items = NULL;
88     _sorter = new Sort ();
89
90     XtAddCallback( _w,
91                    XmNextendedSelectionCallback,
92                    (XtCallbackProc)
93                    &MsgScrollingList::extendedSelectionCallback,
94                    this );
95
96     _msgs = new MsgHndArray(1024);
97     _deleted_messages = new MsgHndArray(1024);
98     num_new_messages = 0;
99     num_deleted_messages = 0;
100     session_message_number = 0;
101    
102     // Can later initialize these from the last use of the session.
103     // Each folder will have some idea of which message was last
104     // read.  We should select and display it at next load, no?
105
106     _selected_item_position = 0;
107     _displayed_item_position = 0;
108
109     DtMailEnv mail_error;
110     // Initialize the mail_error.
111     mail_error.clear();
112     DtMail::Session * d_session = theRoamApp.session()->session();
113     DtMail::MailRc * mailrc = d_session->mailRc(mail_error);
114
115     if (mailrc) {
116         const char * value = NULL;
117         mailrc->getValue(mail_error, "showto", &value);
118         if (mail_error.isNotSet())
119           _header_info.number_of_names = 5;
120         else 
121           _header_info.number_of_names = 4;
122         if (NULL != value)
123           free((void*) value);
124     }
125     else
126          _header_info.number_of_names = 4;
127
128     // Set up array for 5 items. The DtMailMessageTo is used only 
129     // if showto is set, but create placeholder for 5th item in case 
130     // they apply props in this same session. Then we just have to
131     // toggle between 4 or 5 number_of_nanes.
132
133     _header_info.header_name = new (char* [5]);
134
135     _header_info.header_name[0] = NULL;
136     _header_info.header_name[1] = NULL;
137     _header_info.header_name[2] = NULL;
138     _header_info.header_name[3] = NULL;
139     _header_info.header_name[4] = NULL;
140
141     _header_info.header_name[0] = strdup(DtMailMessageSender);
142     _header_info.header_name[1] = strdup(DtMailMessageReceivedTime);
143     _header_info.header_name[2] = strdup(DtMailMessageContentLength);
144     _header_info.header_name[3] = strdup(DtMailMessageSubject);
145     _header_info.header_name[4] = strdup(DtMailMessageTo);
146 }
147
148 MsgScrollingList::~MsgScrollingList()
149 {
150     MsgStruct   *ms;
151     int         i, length;
152
153     for (i=0; i<5; i++)
154     {
155         if (_header_info.header_name[i])
156         {
157             free(_header_info.header_name[i]);
158             _header_info.header_name[i] = NULL;
159         }
160     }
161     delete _header_info.header_name;
162
163     for (i=0, length=_deleted_messages->length(); i<length; i++)
164     {
165         ms = _deleted_messages->at(i);
166         delete ms;
167     }
168     _deleted_messages->clear();
169     delete _deleted_messages;
170
171     for (i=0, length=_msgs->length(); i<length; i++)
172     {
173         ms = _msgs->at(i);
174         delete ms;
175     }
176     _msgs->clear();
177     delete _msgs;
178
179     delete _sorter;
180 }
181
182 Widget
183 MsgScrollingList::get_scrolling_list()
184 {
185     return(_w);
186 }
187
188 void
189 MsgScrollingList::items(
190     XmString items[],
191     int count )
192 {
193     XtVaSetValues( _w,
194                    XmNitems, items,
195                    XmNitemCount, count,
196                    NULL );
197 }
198
199 #ifdef DEAD_WOOD
200 void 
201 MsgScrollingList::addChooseCommand(
202     ChooseCmd *cmd
203
204 {
205     _choose=cmd;
206 }
207
208 void 
209 MsgScrollingList::addDeleteCommand(
210     DeleteCmd *cmd
211
212 {
213     _delete=cmd;
214 }
215 #endif /* DEAD_WOOD */
216
217
218 void
219 MsgScrollingList::select_next_item()
220 {
221     INSERT_STACK_PROBE
222     int num_msgs = 0;
223     FORCE_SEGV_DECL(MsgStruct, tmpMS);
224     DtMailEnv mail_error;
225
226     // Initialize the mail_error.
227     mail_error.clear();
228
229     XtVaGetValues( _w,
230                    XmNitemCount, &num_msgs,
231                    NULL );
232     
233     _selected_item_position = _displayed_item_position + 1;
234
235     if (_selected_item_position <= num_msgs && 
236         _selected_item_position > 0 ) {
237
238         tmpMS = get_message_struct(_selected_item_position);
239         if (tmpMS == NULL) {
240             return;
241         }
242         else {
243             // Deselect all items currently selected.
244             // display_and_select_message() will select, highlight
245             // and display the "next" message.
246
247             XmListDeselectAllItems(_w);
248
249             this->display_and_select_message(mail_error,
250                                         tmpMS->message_handle);
251             if (mail_error.isSet()) {
252                 // Post an exception here...
253             }
254         }
255     }
256     else {
257         return;
258     }
259 }
260
261 void
262 MsgScrollingList::select_prev_item()
263 {
264     INSERT_STACK_PROBE
265     int num_msgs;
266     FORCE_SEGV_DECL(MsgStruct, tmpMS);
267     DtMailEnv mail_error;
268
269     // Initialize the mail_error.
270     mail_error.clear();
271
272     XtVaGetValues( _w,
273                    XmNitemCount, &num_msgs,
274                    NULL );
275     
276     if( _displayed_item_position != 1 )
277         _selected_item_position = _displayed_item_position - 1 ;
278     else
279         _selected_item_position = _displayed_item_position ;
280     
281     if (_selected_item_position >= 1) {
282         tmpMS = get_message_struct(_selected_item_position);
283         if (tmpMS == NULL) {
284             return;
285         }
286         else {
287             // Deselect all items currently selected.
288             // display_and_select_message() will select, highlight
289             // and display the "previous" message.
290
291             XmListDeselectAllItems(_w);
292
293             this->display_and_select_message(mail_error,
294                                         tmpMS->message_handle);
295             if (mail_error.isSet()) {
296                 // Post an exception here...
297             }
298         }
299     }
300     else {
301         return;
302     }
303 }
304
305 DtMailMessageHandle
306 MsgScrollingList::msgno( 
307                          int index 
308                      ) 
309
310     if (index <= 0) {
311         return(NULL);
312     }
313     else {
314         return _msgs->at(index-1)->message_handle; 
315     }
316 }
317
318 MsgStruct*
319 MsgScrollingList::get_message_struct(
320                                      int index 
321                                  ) 
322
323     if (index <= 0) {
324         return(NULL);
325     }
326     else {
327         return(_msgs->at(index-1));
328     }
329 }
330
331 int 
332 MsgScrollingList::position( 
333                             DtMailMessageHandle msgno 
334                         ) 
335
336     return (_msgs->indexof(msgno))+1; 
337 }
338
339 #ifdef DEAD_WOOD
340 int 
341 MsgScrollingList::position( 
342                             MsgStruct *a_msg_struct
343                         ) 
344
345     return (_msgs->indexof(a_msg_struct))+1; 
346 }
347
348 void
349 MsgScrollingList::appendMsg(
350     DtMailMessageHandle msg_hndl
351 )
352 {
353     MsgStruct *newMS;
354
355     // A new message has come in.
356     // Increase the session_message_number which keeps track of the
357     // number of messages in this session for this folder (scrolling list).
358     // 
359     
360     newMS = new MsgStruct();
361     newMS->message_handle = msg_hndl;
362     newMS->indexNumber = session_message_number-num_deleted_messages;
363     newMS->sessionNumber = session_message_number;
364     newMS->is_deleted = FALSE;
365     _msgs->append(newMS);
366     session_message_number++;
367
368 }
369 #endif /* DEAD_WOOD */
370
371     
372 void
373 MsgScrollingList::insertMsg(
374     DtMailMessageHandle msg_hndl
375 )
376 {
377     MsgStruct *newMS;
378
379     // A new message has come in.
380     // Increase the session_message_number which keeps track of the
381     // number of messages in this session for this folder (scrolling list).
382     // 
383
384     newMS = new MsgStruct();
385     newMS->message_handle = msg_hndl;
386     newMS->indexNumber = session_message_number-num_deleted_messages;
387     newMS->sessionNumber = session_message_number;
388     newMS->is_deleted = FALSE;
389     _msgs->append(newMS);
390
391     session_message_number++;
392 }
393
394 void
395 MsgScrollingList::insertDeletedMsg(DtMailMessageHandle msg_hndl)
396 {
397     MsgStruct *newMS;
398
399     // A new message has come in.
400     // Increase the session_message_number which keeps track of the
401     // number of messages in this session for this folder (scrolling list).
402     // 
403
404     newMS = new MsgStruct();
405     newMS->message_handle = msg_hndl;
406     newMS->indexNumber = num_deleted_messages;
407     newMS->sessionNumber = session_message_number;
408     newMS->is_deleted = FALSE;
409     _deleted_messages->append(newMS);
410
411     session_message_number++;
412     num_deleted_messages++;
413 }
414
415 int
416 MsgScrollingList::load_headers(
417     DtMailEnv &mail_error
418 )
419 {
420
421     DtMailMessageHandle tmpMH;
422 #ifdef undef
423     XmString new_status, read_status;
424 #endif
425     XmString complete_header; // text of header w/ glyphs
426     int num_items = 0;
427     int select_item;
428     DtMailHeaderLine info;
429     DtMail::MailBox * mbox = this->parent()->mailbox();
430     DtMailEnv error;
431     DtMailBoolean first_new = DTM_TRUE;
432
433     // Create a class to collect the mail header XmStrings
434     // then get all the items from the current list.
435     _xmstr_collector = new XmStrCollector();
436
437 #ifdef undef
438 /* NL_COMMENT
439  * In a mailer container window's message scrolling list, a "N" appears
440  * to the left of a mail message header indicating that the mail message
441  * is "new" (just arrived and not yet viewed by the user).
442  * There is only space to display 1 character.  If "N" needs to be translated,
443  * please make sure the translation is only 1 character.
444  */
445    new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 110, "N"));
446    read_status = XmStringCreateLocalized(" ");
447 #endif
448
449     // Allocate memory for the XmString array and initialize it.
450
451     int visible;
452     XtVaGetValues(_w, XmNvisibleItemCount, &visible, NULL);
453
454     // Retrieve the message_handles, and from them their headers.
455     // Create an XmString and toss it into the XmStrCollector. 
456
457     select_item = 0;
458     int n_vis = 0;
459     MsgStruct *ms;
460
461     for (tmpMH = mbox->getFirstMessageSummary(error, _header_info, info);
462          tmpMH && !error.isSet();
463          tmpMH = mbox->getNextMessageSummary(error, tmpMH, _header_info, info),
464          num_items++) {
465
466         DtMail::Message * msg = mbox->getMessage(error, tmpMH);
467         if (msg->flagIsSet(error, DtMailMessageDeletePending) == DTM_TRUE) {
468             insertDeletedMsg(tmpMH);
469             continue;
470         }
471         else {
472             insertMsg(tmpMH);
473             n_vis += 1;
474         }
475
476         ms = get_message_struct(get_num_messages());
477         complete_header = formatHeader(
478                                 info,
479                                 ms->indexNumber,
480                                 show_with_attachments(msg),
481                                 msg->flagIsSet(error, DtMailMessageNew));
482
483  
484         if (msg->flagIsSet(error, DtMailMessageNew) == DTM_TRUE) {
485             num_new_messages++;
486
487             // We want to select the last read message before the
488             // first new message.  We will select the first new
489             // message if it is the first message.
490
491             if (first_new) {
492                 first_new = DTM_FALSE;
493                 if (num_items > 0)
494                     select_item = num_items - 1;
495                 else
496                     select_item = 0;
497             }
498         }
499
500         // Insert the XmString into the array.
501         _xmstr_collector->AddItemToList (complete_header);
502
503         // Free the space allocated for info
504         // delete []info.header_values;
505         mbox->clearMessageSummary(info);
506     }
507
508     // If there were no new messages, select and display the last message.
509     if (first_new) {
510         select_item = num_items - 1;
511      }
512
513     select_item += 1; // Message slots start at 1.
514
515     select_item -= num_deleted_messages; // List does not have deleted msgs.
516
517     // Add the items to the XmList.
518     // All XmStrings are freed in the XmStrCollector destructor.
519     _xtarg_collector = new XtArgCollector;
520
521     // The first time the headers are loaded, they should all be loaded
522     // at the same time.  XtVaSetValues is used for this rather than
523     // XmListAddItems so that all the other resource will be set at
524     // the same time.  This prevents multiple repaints.
525     //
526     // However, in the case where only an item or two are being added,
527     // XmListAddItems should be used.  This prevents an unnecessary
528     // repaint in this case.
529     XmListAddItems (_w, _xmstr_collector->GetItems(),
530             _xmstr_collector->GetNumItems(), 0);
531
532     display_message_summary();
533     display_message(mail_error, select_item);
534
535     _xtarg_collector->SetValues(_w);
536     if (_selected_items)
537     {
538         XmStringFree (_selected_items);
539         _selected_items = NULL;
540     }
541
542     delete _xtarg_collector;
543     delete _xmstr_collector;
544     _xtarg_collector = NULL;
545     _xmstr_collector = NULL;
546
547     return(num_items);
548 }
549
550 void
551 MsgScrollingList::load_headers(
552     DtMailEnv &mail_error,
553     DtMailMessageHandle last
554 )
555 {
556     DtMailMessageHandle tmpMH;
557 #ifdef undef
558     XmString read_status, new_status;
559 #endif
560     XmString complete_header; // read status + attach + header_text.
561     int num_items;
562     int num_new = 0, num_vis = 0;
563     DtMailHeaderLine info;
564     DtMailEnv error;
565     DtMail::MailBox * mbox = this->parent()->mailbox();
566
567
568     // Create a class to collect the mail header XmStrings
569     // then get all the items from the current list.
570     _xmstr_collector = new XmStrCollector();
571
572     mail_error.clear();
573
574 #ifdef undef
575 /* NL_COMMENT
576  * In a mailer container window's message scrolling list, a "N" appears
577  * to the left of a mail message header indicating that the mail message
578  * is "new" (just arrived and not yet viewed by the user).
579  * There is only space to display 1 character.  If "N" needs to be translated,
580  * please make sure the translation is only 1 character.
581  */
582     new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 111, "N"));
583     read_status = XmStringCreateLocalized(" ");
584 #endif
585
586     // Allocate memory for the XmString array and initialize it.
587
588     XtVaGetValues(_w, XmNvisibleItemCount, &num_items, NULL);
589
590     MsgStruct *ms;
591
592     for (tmpMH = mbox->getNextMessageSummary(error, last, _header_info, info);
593          tmpMH && !error.isSet();
594          tmpMH = mbox->getNextMessageSummary(error, tmpMH, _header_info, info),
595          num_new++) {
596
597         DtMail::Message * msg = mbox->getMessage(error, tmpMH);
598         if (msg->flagIsSet(error, DtMailMessageDeletePending) == DTM_TRUE) {
599             insertDeletedMsg(tmpMH);
600             continue;
601         }
602         else {
603             insertMsg(tmpMH);
604             num_vis += 1;
605         }
606
607         ms = get_message_struct(get_num_messages());
608         complete_header = formatHeader(
609                                 info,
610                                 ms->indexNumber,
611                                 show_with_attachments(msg),
612                                 msg->flagIsSet(error, DtMailMessageNew));
613
614         if (msg->flagIsSet(error, DtMailMessageNew) == DTM_TRUE) {
615             num_new_messages++;
616         }
617
618         // Insert the XmString into the array.
619         _xmstr_collector->AddItemToList (complete_header);
620
621         // Free the space allocated for info
622         // delete []info.header_values;
623         mbox->clearMessageSummary(info);
624     }
625
626     // Add the items to the XmList.
627     // All XmStrings are freed in the XmStrCollector destructor.
628     _xtarg_collector = new XtArgCollector;
629
630     // The first time the headers are loaded, they should all be loaded
631     // at the same time.  XtVaSetValues is used for this rather than
632     // XmListAddItems so that all the other resource will be set at
633     // the same time.  This prevents multiple repaints.
634     //
635     // However, in the case where only an item or two are being added,
636     // XmListAddItems should be used.  This prevents an unnecessary
637     // repaint in this case.
638     XmListAddItems (_w, _xmstr_collector->GetItems(),
639             _xmstr_collector->GetNumItems(), 0);
640
641         do_list_vis_adjustment();
642
643     display_message_summary();
644
645     _xtarg_collector->SetValues(_w);
646     if (_selected_items)
647     {
648         XmStringFree (_selected_items);
649         _selected_items = NULL;
650     }
651
652     delete _xtarg_collector;
653     delete _xmstr_collector;
654     _xtarg_collector = NULL;
655     _xmstr_collector = NULL;
656 }
657
658 void MsgScrollingList::do_list_vis_adjustment()
659 {
660     Widget list = _w; 
661     int numNew = _xmstr_collector->GetNumItems();
662     int focItm = _selected_item_position;
663
664     int numItems;
665     int cItmCnt, pItmCnt, sItmCnt;
666     int cFocItm, cTopItm, cBotItm, cInvItm;
667  
668     XtVaGetValues(list, XmNvisibleItemCount, &numItems, NULL);
669     XtVaGetValues(list, XmNitemCount, &cItmCnt, NULL);
670     XtVaGetValues(list, XmNselectedItemCount, &sItmCnt, NULL);
671     XtVaGetValues(list, XmNtopItemPosition, &cTopItm, NULL);
672     pItmCnt = cItmCnt - numNew;
673  
674     cBotItm = cTopItm + numItems - 1; //[cTopItm...cBotItm is our window
675  
676     //[User has chosen to view some messages and that view needs to be
677     //[maintained.
678     if (cBotItm != pItmCnt)
679         return;
680  
681     //[If Brand New Mailbox
682     if (cItmCnt <= numItems)
683         return;
684  
685     //[If no items selected, simply synch right till bottom
686     if (sItmCnt == 0) {
687         XmListSetBottomPos(list, cItmCnt);
688         return;
689     }
690  
691     // assert(cFocItm != -1);
692
693     cFocItm = focItm;      
694     cInvItm = pItmCnt - cBotItm; //[Num below bottom-most, or hidden
695  
696     if ((cFocItm <= cBotItm) && (cFocItm >= cTopItm)) {
697         int winM = cFocItm - cTopItm - cInvItm;
698         if (winM <= 0) { //[There is no scope of adjustment
699             return;
700         }
701         //[All the new messages can be accommodated w/o scrolling curr selection
702         if (numNew <= winM) {
703             XmListSetBottomPos(list, cItmCnt);
704             return;
705         }
706         //[All the new messages cannot be accommodated, but we will do best fit
707         else {
708             int numNotShow = numNew - winM;
709             XmListSetBottomPos(list, cItmCnt - numNotShow);
710             return;
711         }
712     }
713     else {
714         XmListSetBottomPos(list, cItmCnt);
715         return;
716     }
717 }
718
719 void 
720 MsgScrollingList::deleteSelected(Boolean silent)
721 {
722     // SR - Added stuff below.  Made code more efficient also.
723     FORCE_SEGV_DECL(MsgStruct, a_del_msg_struct);
724     FORCE_SEGV_DECL(MsgStruct, tmpMS);
725     FORCE_SEGV_DECL(ViewMsgDialog, tmpView);
726     DtMailMessageHandle tmpMH;
727     int  position_in_list, i;
728     FORCE_SEGV_DECL(int, position_list);
729     int position_count;
730     Boolean any_selected;
731     int num_msgs;
732     UndelFromListDialog *undel_dialog;
733     int first_selected_pos;
734     DtMailEnv mail_error, error;
735     DtMailBoolean       cur_state;
736     char *status_message, *str;
737
738     // Initialize the mail_error.
739     mail_error.clear();
740
741     XtVaGetValues( _w, XmNitemCount, &num_msgs, NULL );
742     any_selected = XmListGetSelectedPos(_w, &position_list, &position_count);
743     if (!any_selected) return;
744
745
746     // Of the items in the list, potentially many could be selected
747     // for delete.  And those selected for deleted need not be 
748     // contiguous necessarily.  Note the position of the item that 
749     // appears first in the list.  When the items are deleted, other
750     // items if any exist will shift up to take the places of the 
751     // deleted items.  You want to display the corresponding item that
752     // takes the "first position".
753
754     // Say, if you have messages A, B, C, D, Evisible in that order.
755     // You select B and C for delete.
756     // The first selected pos is 2.
757     // When B and C are deleted, D (and E...) move up.
758     // The new order is A, D, E...
759     // You want to display the second message which is D.
760
761     first_selected_pos = *position_list;
762     undel_dialog = parent()->get_undel_dialog();
763
764     for (i=0; i < position_count; i++ )
765     {
766         position_in_list = *(position_list + i);
767         a_del_msg_struct = get_message_struct(position_in_list);
768         _msgs->mark_for_delete(position_in_list - 1);
769         _deleted_messages->append(a_del_msg_struct);
770         if (undel_dialog)
771           undel_dialog->insertMsg(mail_error, a_del_msg_struct);
772         if (mail_error.isSet()) parent()->postErrorDialog(mail_error);
773
774         // See if there is a standalone view of the message.
775         // If there is, quit it.
776
777         tmpMH = a_del_msg_struct->message_handle;
778         tmpView = parent()->ifViewExists(tmpMH);
779         if (tmpView) tmpView->quit();
780
781         // Update the status of the message in the persistent store.
782         //
783         DtMail::Message * msg = _parent->mailbox()->getMessage(
784                                         error, 
785                                         a_del_msg_struct->message_handle);
786         msg->setFlag(error, DtMailMessageDeletePending);
787
788         // Check if message is new.  If new and it is being deleted,
789         // reduce the new messages count.
790
791         cur_state = msg->flagIsSet(error, DtMailMessageNew);
792
793         if (cur_state == DTM_TRUE) num_new_messages--;
794     }
795
796     _msgs->compact(0);
797
798     // Delete the items from the scrolling list
799
800      XmListDeletePositions(_w, position_list, position_count);
801     num_deleted_messages += position_count;
802
803     // XnListGetSelectedPos allocated memory for the array and
804     // wants us to free it.  See documentation.
805
806     num_msgs = _msgs->length();
807
808     // Always remove the current attachments from the attachment area
809     // regardless of whether this is the last message in the list or not
810     // We call clearAttachArea if there are any other messages in the folder
811     // as parseAttachments will be called which will clean up the attachment
812     // status window. In the case of deleting the last message we must call
813     // removeCurrentAttachments which cleans up the status window as well.
814     //
815     if (num_msgs == 0)
816       parent()->get_editor()->attachArea()->removeCurrentAttachments();
817     else
818       parent()->get_editor()->attachArea()->clearAttachArea();
819
820     if (num_msgs == 0)
821     {
822         //No more messages left; clear the editor.
823         parent()->get_editor()->textEditor()->set_contents("", 1);
824
825         // NOTE: Need to obtain the attachArea and clear it up too
826
827         _displayed_item_position = 0;   // Not displaying any.
828         _selected_item_position = 0;    // Reset...
829         free(position_list);
830         display_message_summary();
831         return;
832     }
833
834     // If there are messages 1, 2 and 3 and 2 is selected and deleted
835     // first_selected_pos will be 2;
836     // after removing it, will need to display the 2nd message
837     // (which will now be 3)
838     // If 2 and 3 have been deleted, and there is no 2nd message,
839     // display the nth message (in our example, n = 1)
840     
841     _selected_item_position = first_selected_pos;
842     if (_selected_item_position > num_msgs)
843       _selected_item_position = num_msgs; 
844
845     // Having determined which message to display, confirm that it is
846     // within the bounds.  Call display_message().
847     // FYI, display_message() sets the _displayed_item_position.
848
849     if ((_selected_item_position > 0) &&
850         (_selected_item_position <= num_msgs))
851     {
852         XmListSelectPos(_w, _selected_item_position, FALSE);
853         tmpMS = this->get_message_struct(_selected_item_position);
854         if (tmpMS == NULL)
855         {
856             free(position_list);
857             return;
858         }
859         else
860         {
861             this->display_message(mail_error, tmpMS->message_handle);
862             if (mail_error.isSet()) parent()->postErrorDialog(mail_error);
863         }
864     }
865
866     if (!silent)
867     {
868         if (position_count > 1)
869         {
870             /* NL_COMMENT
871             * The following sentence means %d number of mail messages have 
872             * been deleted from the mail folder.  This is the plural form 
873             * of the message that gets printed if more than one message 
874             * is moved.
875             */
876             str = GETMSG(DT_catd, 3, 84, "%d messages deleted"); 
877         }
878         else
879         {
880             /* NL_COMMENT
881             * The following sentence means %d number of mail messages have 
882             * been deleted from the mail folder.  This is the singular 
883             * form of the message that gets printed if only one message 
884             * is moved.
885             */
886             str = GETMSG(DT_catd, 3, 85, "%d message deleted"); 
887         }
888         status_message = new char[strlen(str) + 10];
889         sprintf(status_message, str, position_count);
890         parent()->message(status_message);
891         delete [] status_message;
892     }
893
894     updateListItems(-1, TRUE, NULL);
895     display_message_summary();
896
897     XtFree((char*) position_list);
898 }
899
900 DtMailBoolean
901 copyCallback(
902     DtMailCallbackOp,
903     const char *,
904     const char *,
905     void *,
906     ...
907 )
908 {
909     return(DTM_FALSE);
910 }
911
912 // copySelected() will either copy or move the selected messages
913 // into the container called destname.  If the delete_after_copy
914 // flag is set to TRUE, it is effectively a move; otherwise, it
915 // is a copy.  If the container named by destname is a relative
916 // path, but it isn't prefixed with a '+', then a '+' will be
917 // prepended to the name so that the open() call will "do the
918 // right thing."
919 // If silent is TRUE then no status messages are displayed and the
920 // destname is not added to the copy/move cache.
921
922 int
923 MsgScrollingList::copySelected(
924     DtMailEnv &mail_error,
925     char *destname, 
926     int delete_after_copy,
927     int silent
928 )
929 {
930     FORCE_SEGV_DECL(DtMail::MailBox, mbox);
931     FORCE_SEGV_DECL(int, position_list);
932     FORCE_SEGV_DECL(DtMail::MailBox, target);
933     DtMail::MailRc * mailrc;
934     DtMail::Session * d_session = theRoamApp.session()->session();
935     int position_count, position, i;
936     Boolean any_selected = FALSE;
937     DtMailMessageHandle msg;
938     char *status_message, *str;
939     char *newdestname;
940     RoamMenuWindow      *rmw;
941
942
943     any_selected = XmListGetSelectedPos(_w,
944                                         &position_list,
945                                         &position_count);
946     // If there aren't any selected messages, then there isn't
947     // anything for us to do.
948
949     if (!any_selected) {
950         display_no_message();
951         if (! silent)
952         {
953             char * helpId = NULL;
954             DtMailGenDialog *dialog = _parent->genDialog();
955
956             dialog->setToErrorDialog(
957                                 GETMSG(DT_catd, 3, 50, "Mailer"),
958                                 GETMSG(DT_catd, 2, 16, "No message selected."));
959             dialog->post_and_return(helpId);
960         }
961         return(1);
962     }
963
964     mbox = parent()->mailbox();
965     mailrc = mbox->session()->mailRc(mail_error);
966     if (!mailrc) {
967     // NL_COMMENT
968     // The following is an error message.  "mailrc" is the name of the
969     // mail resource file.  Translate as appropriate.
970     //
971     parent()->message(GETMSG(DT_catd, 2, 15,"Error - Unable to get mailrc."));
972     return(1);
973     }
974
975     // If the first character of destname is alphanumeric, we can
976     // safely assume that it is a relative path, so we prepend a
977     // '+' to it.
978     if (isalnum(destname[0])) {
979         // Make sure we allocate enough for the name + '+' + null terminator.
980         newdestname = (char *) malloc(strlen(destname) + 2);
981         memset(newdestname, 0, strlen(destname) + 2);
982         sprintf(newdestname, "+%s", destname);
983         char *path = d_session->expandPath(mail_error, newdestname);
984         target = theRoamApp.session()->open(mail_error,
985                                         path,
986                                         copyCallback,
987                                         NULL,
988                                         DTM_TRUE,  
989                                         DTM_FALSE,
990                                         DTM_FALSE);
991         free(newdestname);
992         free(path);
993         newdestname = NULL;
994     } else {
995         target = theRoamApp.session()->open(mail_error,
996                                         destname,
997                                         copyCallback,
998                                         NULL,
999                                         DTM_TRUE,  
1000                                         DTM_FALSE,
1001                                         DTM_FALSE);
1002     }
1003     if (mail_error.isSet()) {
1004         // if the error is DTME_AlreadyOpened, we don't care.
1005         // go ahead to clear the error
1006         // otherwise, 
1007         // We couldn't open the container, so we want to post an
1008         // error dialog.
1009         if ((DTMailError_t)mail_error == DTME_AlreadyOpened)
1010          {
1011            mail_error.clear();
1012          } else {
1013           parent()->postErrorDialog(mail_error);
1014           return(0);
1015          }
1016     }
1017
1018     // Go through the selected messages and copy them to the
1019     // specified container.
1020
1021     if ( position_count > 0 )
1022         parent()->busyCursor() ;
1023
1024     for (i=0; i < position_count; i++) {
1025         position = *(position_list + i);
1026         msg = msgno(position);
1027         DtMail::Message * dtmsg = mbox->getMessage(mail_error, msg);
1028         if (mail_error.isSet()) {
1029             parent()->normalCursor() ;
1030             parent()->postErrorDialog(mail_error);
1031             theRoamApp.session()->close(mail_error, target);
1032             return(0);
1033         }
1034
1035         mail_error.clear();
1036         target->copyMessage(mail_error, dtmsg);
1037         if (mail_error.isSet()) {
1038             parent()->normalCursor() ;
1039             parent()->postErrorDialog(mail_error);
1040             theRoamApp.session()->close(mail_error, target);
1041             return(0);
1042         }
1043
1044         parent()->normalCursor() ;
1045         
1046     }
1047
1048     
1049     rmw = theRoamApp.session()->getRMW(target);
1050     mail_error.clear();
1051     if (rmw) rmw->checkForMail(mail_error);
1052
1053     if (delete_after_copy) {
1054         deleteSelected();
1055
1056         if (i > 1) {
1057             // NL_COMMENT
1058             // The following sentence means %d number of mail messages have 
1059             // been moved to the %s mail folder.  The %s is the name of a 
1060             // mail folder.  This is the plural form of the message that gets
1061             // printed if more than one message is moved.
1062             //
1063             str = GETMSG(DT_catd, 3, 65, "%d messages moved to %s"); 
1064         } else {
1065             // NL_COMMENT
1066             // The following sentence means %d number of mail messages have 
1067             // been moved to the %s mail folder.  The %s is the name of a 
1068             // mail folder.  This is the singular form of the message that
1069             // gets printed if only one message is moved.
1070             //
1071             str = GETMSG(DT_catd, 3, 66, "%d message moved to %s"); 
1072         }
1073
1074     } else {
1075         if (i > 1) {
1076             // NL_COMMENT
1077             // The following sentence means %d number of mail messages have been
1078             // copied to the %s mail folder.  This is the plural form of the
1079             // message that gets printed if more than one message is copied.
1080             //
1081             str = GETMSG(DT_catd, 3, 67, "%d messages copied to %s"); 
1082         } else {
1083             // NL_COMMENT
1084             // The following sentence means %d number of mail messages have been
1085             // copied to the %s mail folder.  This is the singular form of the
1086             // message that gets printed if only one message is copied.
1087             //
1088             str = GETMSG(DT_catd, 3, 68, "%d message copied to %s"); 
1089         }
1090
1091     }
1092     newdestname = d_session->getRelativePath(mail_error, destname);
1093     status_message = new char[strlen(str) + strlen(newdestname) + 10];
1094     sprintf(status_message, str, i, newdestname);
1095
1096     if (!silent) {
1097         theRoamApp.globalAddToCachedContainerList(newdestname);
1098         parent()->message(status_message);
1099     }
1100
1101     free(newdestname);
1102
1103     theRoamApp.session()->close(mail_error, target);
1104
1105     delete [] status_message;
1106     return(0);
1107 }
1108
1109
1110 //-----------------------------------------------------------------------------
1111 // This method returns a list of the currently selected messages.  This list
1112 // must be deleted by the calling method.
1113 //-----------------------------------------------------------------------------
1114
1115 MsgHndArray * 
1116 MsgScrollingList::selected()
1117 {
1118     FORCE_SEGV_DECL(MsgStruct, a_msg_struct);
1119     FORCE_SEGV_DECL(int, position_list);
1120     int  position_in_list, i;
1121     int position_count;
1122     Boolean any_selected;
1123
1124     // Find out first if any have been selected.
1125     // If i has been selected, how many?
1126     // We need the number selected so that we can allocate 
1127     // space for that many mesasgeStructs to be returned.
1128
1129     any_selected = XmListGetSelectedPos(_w,
1130                                         &position_list, 
1131                                         &position_count);
1132
1133     // If nothing selected, return
1134
1135     if (!any_selected) return NULL;
1136
1137     //  Allocate memory for position_count number of messageStructs
1138     // in MsgHndArray.
1139
1140     MsgHndArray *msgList = new MsgHndArray(position_count);
1141
1142     for (i=0; i < position_count; i++ ) {
1143         position_in_list = *(position_list + i);
1144         a_msg_struct = get_message_struct(position_in_list);
1145         msgList->append(a_msg_struct);
1146     }
1147     return msgList;
1148 }
1149
1150 DtMailBoolean
1151 MsgScrollingList::show_with_attachments(DtMailMessageHandle msg_num)
1152 {
1153     DtMailBoolean       has_attachments = DTM_FALSE;
1154     DtMailEnv           mail_error;
1155     DtMail::MailBox     *mbox=parent()->mailbox();
1156     DtMail::Message     *msg = mbox->getMessage(mail_error, msg_num);
1157
1158     has_attachments = show_with_attachments(msg);
1159     return has_attachments;
1160 }
1161
1162
1163 DtMailBoolean
1164 MsgScrollingList::show_with_attachments(DtMail::Message * msg)
1165 {
1166     DtMail::BodyPart    *bp;
1167     DtMailEnv           error;
1168     DtMailBoolean       has_attachments = DTM_FALSE;
1169     DtMailBoolean       is_multipart = DTM_FALSE;
1170     int                 num_bodyParts;
1171     char                *type;
1172
1173     is_multipart = msg->flagIsSet(error, DtMailMessageMultipart);
1174     if (! is_multipart)
1175       return DTM_FALSE;
1176
1177     num_bodyParts = msg->getBodyCount(error);
1178     if (num_bodyParts > 1)
1179       return DTM_TRUE;
1180
1181     bp = msg->getFirstBodyPart(error);
1182     bp->getContents(error, NULL, NULL, &type, NULL, NULL, NULL);
1183     if (NULL != type)
1184     {
1185         char *attr;
1186
1187         attr = DtDtsDataTypeToAttributeValue(type, DtDTS_DA_IS_TEXT, NULL);
1188         if (attr && strcasecmp(attr, "true") != 0)
1189           has_attachments = DTM_TRUE;
1190         
1191         if (type)
1192           free(type);
1193         if (attr)
1194           DtDtsFreeAttributeValue(attr);
1195     }
1196     return has_attachments;
1197 }
1198
1199
1200 void
1201 MsgScrollingList::display_message(
1202     DtMailEnv   &mail_error,
1203     DtMailMessageHandle   msg_num
1204 )
1205 {
1206     int                 item_index;
1207     DtMail::MailBox     *mbox=parent()->mailbox();
1208     Editor*             rmw_editor;
1209     DtMailBoolean       cur_state;
1210     int num_selected;
1211     int num_bodyParts;
1212
1213     // If there is a status message displayed, clear it first.
1214     parent()->clear_message();
1215
1216     
1217         // We could have called display_msg from anywhere.
1218         // Need to calculate what is the position of that item
1219         // in the scrolling list, given the DtMailMessageHandle.
1220         // Determine the index at the _msgs array; increment by 1
1221         // since array begins at 0 and XmList begins at 1.
1222
1223     item_index = _msgs->indexof(msg_num);
1224     if (item_index < 0) return;
1225         
1226     _displayed_item_position = item_index + 1;
1227
1228     // Make sure the header is visible in the scrolling list.
1229     this->scroll_to_position(_displayed_item_position);
1230
1231     // Retrieve the header text and insert it. We need to retrieve
1232     // message from the handle and inset the headers.
1233     //
1234
1235     DtMail::Message * msg = mbox->getMessage(mail_error, msg_num);
1236     DtMail::Envelope * env = msg->getEnvelope(mail_error);
1237
1238
1239     // There are multiple paths to this place:
1240     // 1) user has already read this message but is re-reading it;
1241     // 2) user has undeleted this message (implicitly, they have
1242     //    read this message);
1243     // 3) user has not read this message yet.
1244     //
1245     // For (1), we don't need to do anything fancy.
1246     // For (2), we have already reset the IsDeleted flag while 
1247     // undeleting.
1248     // For (3), we need to reset the flag in store to indicate
1249     // its read.
1250
1251     cur_state = msg->flagIsSet(mail_error, DtMailMessageNew);
1252
1253     // If the message was previously new, we need to reset the list
1254     // item to remove the "N" on the item.  We do this by reconstructing
1255     // the header line without the "N".
1256     //
1257     if (cur_state == DTM_TRUE) {
1258         DtMailHeaderLine  info;
1259
1260         msg->resetFlag(mail_error, DtMailMessageNew);
1261
1262         mbox->getMessageSummary(mail_error, msg_num, _header_info, info);
1263
1264         MsgStruct *ms;
1265         ms = get_message_struct(_displayed_item_position);
1266
1267         XmString complete_header;
1268         complete_header = formatHeader(
1269                             info,
1270                             ms->indexNumber,
1271                             show_with_attachments(msg),
1272                             msg->flagIsSet(mail_error, DtMailMessageNew));
1273
1274         mbox->clearMessageSummary(info);
1275
1276         XmListReplaceItemsPos(_w, &complete_header,1, _displayed_item_position);
1277         XmStringFree(complete_header);
1278
1279         // The default selection policy is extended_select.
1280         // Problem:
1281         // User extend selects multiple items
1282         // The last item selected is a "N" message
1283         // We display the "N" message and have to replace it without
1284         // the "N".
1285         // There is no way to replace with something else and select at 
1286         // the same time.  
1287         // Therefre, we need to explicitly select the replacement.
1288         // The problem is: selecting the replacement deselects the other
1289         // selected items.
1290         // To work around this, we switch temporarily to MULTIPLE_SELECT,
1291         // select the replacement, and switch back to extend_select.
1292         // This will select the replacement and *not*  drop the
1293         // selection on the other selected items.
1294         // Say "Amen" to  OSF for such convoluted thinking!
1295
1296         XtVaGetValues(_w,
1297             XmNselectedItemCount, &num_selected,
1298             NULL);
1299
1300         if (num_selected > 1) {
1301             // Change to MULTIPLE_SELECT.
1302             // Select item
1303             // Change back to EXTEND_SELECT
1304             XtVaSetValues (_w,
1305                 XmNselectionPolicy, XmMULTIPLE_SELECT,
1306                 XmNselectionMode,   XmNORMAL_MODE,
1307                 NULL);
1308           
1309            // When loading mail headers, we need to make sure that
1310            // the XmList resources are all set at the same time to
1311            // avoid painting multiple times.  So here we collect
1312            // these resources.
1313            if (_xtarg_collector && _xmstr_collector)
1314            {
1315                 XmString *items = _xmstr_collector->GetItems();
1316         
1317                 // Keep a handle to the malloced string copy so that
1318                 // we can free it after the XtSetValues
1319                 _selected_items 
1320                     = XmStringCopy (items[_displayed_item_position - 1]);
1321
1322                 _xtarg_collector->AddItemToList (XmNselectedItems,
1323                     (XtArgVal) &_selected_items);
1324                 _xtarg_collector->AddItemToList (
1325                     XmNselectedItemCount, 1);
1326                 _xtarg_collector->AddItemToList (
1327                     XmNselectionPolicy, XmEXTENDED_SELECT);
1328             }
1329             else
1330             {
1331                 XmListSelectPos(_w, _displayed_item_position, FALSE);
1332
1333                 XtVaSetValues (_w,
1334                     XmNselectionPolicy, XmEXTENDED_SELECT,
1335                     XmNselectionMode,   XmNORMAL_MODE,
1336                     NULL);
1337             }
1338         }
1339         else {
1340            if (_xtarg_collector && _xmstr_collector)
1341            {
1342                 XmString *items = _xmstr_collector->GetItems();
1343
1344                 _selected_items 
1345                     = XmStringCopy (items[_displayed_item_position - 1]);
1346
1347                 _xtarg_collector->AddItemToList (XmNselectedItems,
1348                     (XtArgVal) &_selected_items);
1349                 _xtarg_collector->AddItemToList (
1350                     XmNselectedItemCount, 1);
1351             }
1352             else
1353             {
1354                 XmListSelectPos(_w, _displayed_item_position, FALSE);
1355             }
1356         }
1357
1358         num_new_messages--;
1359     }
1360
1361     display_message_summary();
1362
1363     // Display the message now.
1364     rmw_editor = parent()->get_editor()->textEditor();
1365     rmw_editor->disable_redisplay();
1366     rmw_editor->auto_show_cursor_off();
1367     rmw_editor->clear_contents();
1368
1369     num_bodyParts = msg->getBodyCount(mail_error);
1370
1371     char * status_string;
1372     DtMailBoolean firstBPHandled;
1373
1374     // Turn on the busy Cursor
1375
1376     parent()->busyCursor();
1377
1378     if (parent()->fullHeader())
1379       firstBPHandled =
1380         rmw_editor->set_message(msg, &status_string, Editor::HF_FULL);
1381     else
1382       firstBPHandled =
1383         rmw_editor->set_message(msg, &status_string, Editor::HF_ABBREV);
1384
1385     if (status_string) parent()->message(status_string);
1386     if (mail_error.isSet()) {} // do something
1387
1388     if ((num_bodyParts > 1) || (!firstBPHandled))
1389     {
1390         // If the message has attachments, then let the attach pane
1391         // handle attachments but not the first bodyPart (which has
1392         // already been handled here).
1393
1394         if (firstBPHandled) {
1395
1396             //  The first bodyPart has already been handled.
1397             // The others, beginning from the second, need to be parsed 
1398             // and put into the attachPane.
1399
1400             parent()->get_editor()->attachArea()->parseAttachments(
1401                                         mail_error,
1402                                         msg, 
1403                                         TRUE,
1404                                         2);
1405         }
1406         else {
1407             // The first bodyPart was not handled.
1408             // It may not have been of type text.
1409             // The attachment pane needs to handle all the bodyParts
1410             // beginning with the first.
1411
1412             parent()->get_editor()->attachArea()->parseAttachments(
1413                                         mail_error,
1414                                         msg, 
1415                                         TRUE,
1416                                         1);
1417         }
1418
1419         // Check for errors.
1420         // Manage the attach area to display attachments.
1421         if (mail_error.isSet()) {} // do something
1422
1423         parent()->get_editor()->manageAttachArea();
1424         parent()->activate_default_attach_menu();
1425     }
1426     else
1427     {
1428         parent()->deactivate_default_attach_menu();
1429         parent()->get_editor()->unmanageAttachArea();
1430     }
1431
1432     parent()->sync_work_area_size();
1433     rmw_editor->auto_show_cursor_restore();
1434
1435     // Turn on text editor and manage attachPane
1436     rmw_editor->set_to_top();
1437     rmw_editor->enable_redisplay();
1438
1439     parent()->normalCursor();
1440 }
1441
1442 void
1443 MsgScrollingList::display_and_select_message(
1444     DtMailEnv &mail_error,
1445     DtMailMessageHandle msg_num
1446 )
1447 {
1448     int         item_index;
1449
1450     // Need to calculate what is the position of that item
1451     // in the scrolling list, given the DtMailMessageHandle.
1452     // Determine the index at the _msgs array; increment by 1
1453     // since array begins at 0 and XmList begins at 1.
1454
1455     mail_error.clear();
1456
1457     item_index = _msgs->indexof(msg_num);
1458     if (item_index < 0) return;
1459         
1460     _displayed_item_position = item_index + 1;
1461     _selected_item_position = _displayed_item_position;
1462     
1463     // Select this message in the scrolling list and display
1464     // the message.
1465
1466    // When loading mail headers, we need to make sure that
1467    // the XmList resources are all set at the same time to
1468    // avoid painting multiple times.  So here we collect
1469    // these resources.
1470    if (_xtarg_collector && _xmstr_collector)
1471    {
1472         XmString *items = _xmstr_collector->GetItems();
1473
1474         // Keep a handle to the malloced string copy so that
1475         // we can free it after the XtSetValues
1476         _selected_items 
1477             = XmStringCopy (items[_displayed_item_position - 1]);
1478
1479         _xtarg_collector->AddItemToList (XmNselectedItems,
1480             (XtArgVal) &_selected_items);
1481         _xtarg_collector->AddItemToList (
1482             XmNselectedItemCount, 1);
1483     }
1484     else
1485     {
1486         XmListSelectPos(_w, _displayed_item_position, FALSE);
1487     }
1488     this->display_message(mail_error, msg_num);
1489
1490     return;
1491 }
1492
1493 void
1494 MsgScrollingList::select_all_and_display_last(
1495     DtMailEnv         & error
1496 )
1497  
1498 {
1499     register int                item_pos;
1500     int                         num_items;
1501     MsgHndArray                 * msgHandles = get_messages();
1502  
1503     error.clear();
1504
1505     if (this->get_num_messages() == 0) return;
1506
1507     // If no message selected, return.
1508
1509     if (this->get_selected_item() == 0) return;
1510
1511     XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
1512     //
1513     // We have to go to the end of the list and go backwards.
1514     // We display the last one, and select the rest.
1515     //
1516     // A NULL terminated list.
1517     //
1518  
1519     XmListDeselectAllItems(baseWidget());
1520     XtVaGetValues(baseWidget(), XmNitemCount, &num_items, NULL);
1521  
1522     for (item_pos = 1; item_pos < num_items; item_pos++) {
1523         XmListSelectPos(baseWidget(), item_pos, FALSE);
1524     }
1525  
1526     // Invoke the selection callback on the last item.
1527     display_and_select_message(error, 
1528                                 msgHandles->at(item_pos-1)->message_handle);
1529     XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
1530     XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
1531 }
1532
1533 void
1534 MsgScrollingList::select_all_and_display_last(
1535     DtMailEnv   & error,
1536     DtMailMessageHandle *handleArray,
1537     unsigned int           elements
1538 )
1539 {
1540   register int          handleOffset = 0;
1541   register int          item_pos;
1542
1543   error.clear();
1544
1545   XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
1546   //
1547   // We have to go to the end of the list and go backwards.
1548   // We display the last one, and select the rest.
1549   //
1550   // A NULL terminated list.
1551   //
1552   handleOffset = elements;
1553
1554   XmListDeselectAllItems(baseWidget());
1555
1556   while (--handleOffset >= 0 && error.isNotSet()) {
1557
1558     item_pos = _msgs->indexof(handleArray[handleOffset]);       // Get position
1559     if (item_pos < 0) {
1560       continue;
1561     }
1562     
1563     //
1564     // Select this message in the scrolling list and IF
1565     // it is the last message, display it.
1566     //
1567     if (handleOffset == elements - 1) {
1568       display_and_select_message(error, handleArray[handleOffset]);
1569     } else {
1570       XmListSelectPos(_w, item_pos + 1, FALSE);
1571     }
1572   }
1573   XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
1574   XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
1575   return;
1576 }
1577
1578
1579 void
1580 MsgScrollingList::display_no_message()
1581 {
1582
1583         /* NL_COMMENT
1584          * No mail message has been selected by the user.
1585          */
1586
1587     parent()->message(GETMSG(DT_catd, 2, 16, "No message selected."));
1588     _displayed_item_position = 0;
1589     _selected_item_position = 0;
1590
1591     parent()->get_editor()->textEditor()->clear_contents();
1592     parent()->get_editor()->attachArea()->removeCurrentAttachments();
1593 }
1594
1595 // In need of a simple but useful optimization here.
1596 // By the time we get to this method, we already have displayed
1597 // the selected message in the lower section of the combo window.
1598 // Instead of having to parse the messageHandle and construct the
1599 // text buffer to display, we can just get the parsed-and-formatted
1600 // text from the lower section of the combo window and stick it into
1601 // the VMD's editor.
1602 // This will save us the effort of parsing and formatting a message
1603 // 
1604
1605 void 
1606 MsgScrollingList::viewInSeparateWindow(DtMailEnv &mail_error)
1607 {
1608     FORCE_SEGV_DECL(const char, title);
1609     FORCE_SEGV_DECL(char, header_txt);
1610     FORCE_SEGV_DECL(MsgStruct, tmpMS);
1611     DtMailMessageHandle  msgHandle;
1612     DtMail::MailBox     *mbox=parent()->mailbox();
1613     ViewMsgDialog       *newview;
1614     int                  num_bodyParts;
1615     Editor*              vmd_editor;
1616
1617     DtMailEnv            error;
1618     DtMail::Session     *d_session = theRoamApp.session()->session();
1619     DtMail::MailRc      *mail_rc = d_session->mailRc(error);
1620     const char          *value = NULL;  
1621  
1622     // If no message selected, return.
1623     if (this->get_selected_item() <= 0) return;
1624
1625     // Double-check.  If none selected, return
1626     MsgHndArray *selected_msgs = this->selected();
1627     if (!selected_msgs) return;
1628
1629     for (int a_msg_num = 0; a_msg_num < selected_msgs->length(); a_msg_num++)
1630     {
1631         tmpMS = selected_msgs->at(a_msg_num);
1632         if (tmpMS == NULL) return;
1633            
1634         msgHandle = tmpMS->message_handle;
1635         newview = parent()->ifViewExists(msgHandle);
1636            
1637         if (newview != NULL)
1638         {
1639             /* NL_COMMENT
1640              * The current mail message selected is already displayed in a
1641              * separate window.  Therefore this 'separate' window will be
1642              * raised in front of existing windows so the user can see it.
1643              */
1644             parent()->message(
1645                         GETMSG(
1646                                 DT_catd, 3, 69,
1647                                 "View already exists.  Raising it."));
1648             newview->displayInCurrentWorkspace();
1649             return;
1650         } 
1651
1652         // No view exists.  Display it.  For feedback, set busyCursor().
1653         parent()->busyCursor();
1654         mail_rc->getValue(error, "separatemessageviewer", &value);
1655         if (error.isSet())
1656           newview = new ViewMsgDialog(parent(), xmDialogShellWidgetClass);
1657         else
1658         {
1659             if (value) free((void*) value);
1660             newview = new ViewMsgDialog(parent(), topLevelShellWidgetClass);
1661         }
1662         parent()->registerDialog(newview);
1663         newview->initialize();
1664         vmd_editor = newview->get_editor()->textEditor();
1665            
1666         // Set the VMD's msgHandle. This unique handle is what is
1667         // used to raise the VMD when the same message is double
1668         // clicked on later.
1669         newview->msgno(msgHandle);
1670            
1671         DtMailEnv error;
1672         DtMail::Message * msg = mbox->getMessage(error, msgHandle);
1673         DtMail::Envelope * env = msg->getEnvelope(error);
1674            
1675         DtMailValueSeq title_value;
1676         env->getHeader(error, DtMailMessageSubject, DTM_TRUE, title_value);
1677         if (error.isSet())
1678           title = "NO SUBJECT!";
1679         else
1680           title = *(title_value[0]);
1681            
1682         newview->title((char *)title);
1683         newview->auto_show_cursor_off();
1684            
1685         // Unset the error produced when obtaining title...
1686         mail_error.clear();
1687         vmd_editor->disable_redisplay();
1688            
1689         char * status_string;
1690         DtMailBoolean firstBPHandled;
1691            
1692         if (parent()->fullHeader())
1693           firstBPHandled = newview->get_editor()->textEditor()->set_message(
1694                                         msg, &status_string, Editor::HF_FULL);
1695         else
1696           firstBPHandled = newview->get_editor()->textEditor()->set_message(
1697                                         msg, &status_string, Editor::HF_ABBREV);
1698            
1699         // If the message has attachments, then let the attach pane
1700         // handle attachments but not the first bodyPart (which has
1701         // already been handled here).
1702         num_bodyParts = msg->getBodyCount(mail_error);
1703         if (mail_error.isSet()) {} // do something
1704            
1705         if ((num_bodyParts == 1) && firstBPHandled)
1706         {
1707             newview->get_editor()->unmanageAttachArea();
1708             newview->deactivate_default_attach_menu();
1709         }
1710         else if ((num_bodyParts > 1) || (!firstBPHandled))
1711         {
1712             // If the message has attachments, then let the attach pane
1713             // handle attachments but not the first bodyPart (which has
1714             // already been handled here).
1715                
1716             if (firstBPHandled)
1717             {
1718                 //  The first bodyPart has already been handled.
1719                 // The others, beginning from the second, need to be parsed 
1720                 // and put into the attachPane.
1721                 newview->get_editor()->attachArea()->parseAttachments(
1722                                                 mail_error, msg, TRUE, 2);
1723             }
1724             else
1725             {
1726                 // The first bodyPart was not handled.
1727                 // It may not have been of type text.
1728                 // The attachment pane needs to handle all the bodyParts
1729                 // beginning with the first.
1730                 newview->get_editor()->attachArea()->parseAttachments(
1731                                                 mail_error, msg, TRUE, 1);
1732             }
1733                
1734             // Check for errors.
1735             // Manage the attach area to display attachments.
1736             if (mail_error.isSet()) {} // do something
1737             newview->get_editor()->manageAttachArea();
1738             newview->activate_default_attach_menu();
1739         }
1740            
1741         newview->set_to_top();
1742         newview->auto_show_cursor_restore();
1743         vmd_editor->enable_redisplay();
1744         newview->manage();
1745     }
1746
1747     parent()->normalCursor();
1748 }
1749
1750 // defaultAction() gets called *after* extendedSelectionCallback() is
1751 // called.  
1752 // Display the message in extendedSelectionCallback().
1753 // viewInSeparateWindow() in defaultAction().
1754
1755 void 
1756 MsgScrollingList::defaultAction(Widget, XtPointer, XmListCallbackStruct *cbs)
1757 {
1758     FORCE_SEGV_DECL(MsgStruct, tmpMS);
1759     DtMailEnv mail_error;
1760
1761     // Initialize the mail_error.
1762     mail_error.clear();
1763    _selected_item_position = cbs->item_position;
1764
1765    tmpMS = get_message_struct(_selected_item_position);
1766    if (tmpMS == NULL)
1767      return;
1768    else
1769    {
1770        this->viewInSeparateWindow(mail_error);
1771        if (mail_error.isSet()) {} // Post dialog indicating error
1772    }
1773 }
1774
1775     
1776 void
1777 MsgScrollingList::extendedSelectionCallback(
1778     Widget ,                    // w
1779     XtPointer clientData,
1780     XmListCallbackStruct *cbs
1781 )
1782 {
1783     int last_clicked_on_pos;
1784     int above_selected_pos;
1785     int tmp_selected_pos;
1786     int i, num_selected;
1787     DtMailEnv mail_error;
1788
1789     // Initialize the mail_error.
1790     mail_error.clear();
1791
1792     
1793     Boolean IS_SELECTION = FALSE;
1794     Boolean SELECTION_MADE = FALSE;
1795
1796     MsgScrollingList *obj=(MsgScrollingList *) clientData;
1797
1798     // If all items have been deselected
1799     if (cbs->selected_item_count == 0) {
1800         obj->extended_selection(mail_error, 0);
1801         if (mail_error.isSet()) {
1802             return;
1803         }
1804         SELECTION_MADE = TRUE;
1805     }
1806     else {
1807         
1808         last_clicked_on_pos = cbs->item_position;
1809
1810     // Check to see if this was a selection or deselection
1811     // We do that by seeing if last_clicked_on_pos is in the 
1812     // selected_item_positions array.  If it is, then it is a 
1813     // selection; if its not there, then its a deselection.
1814
1815         num_selected =  cbs->selected_item_count;
1816     
1817         for (i = 0; i < num_selected; i++) {
1818             if (last_clicked_on_pos == cbs->selected_item_positions[i]) {
1819                 // Yes, it was a selection
1820             
1821                 IS_SELECTION = TRUE;
1822                 obj->extended_selection(mail_error, last_clicked_on_pos);
1823                 if (mail_error.isSet()) {
1824                     return;
1825                 }
1826
1827                 SELECTION_MADE = TRUE;
1828             }
1829         }
1830
1831     // If it was not a selection, then we need to find the selected
1832     // item nearest to the deselected item.  We have a choice there -
1833     // we can find the nearest one above, or the nearest one below 
1834     // the deselected item.  Let's find the nearest one above.
1835     // We do that by cruising through the selected_item_positions array.
1836     // Motif arranges the array in ascending order, no matter what order
1837     // you selected the items!  If you deselect an item, the items on
1838     // either side of it are the ones above and below it.  We use that
1839     // ordering to now select and display the one above it.
1840     //
1841
1842         if (!IS_SELECTION) {
1843
1844         // If the first selected item is below the deselected item,
1845         // it follows that all selected items are below the deselected
1846         // item.  Display the first selected item and be done.
1847
1848             if (cbs->selected_item_positions[0] > last_clicked_on_pos) {
1849                 obj->extended_selection(mail_error,
1850                                         cbs->selected_item_positions[0]);
1851                 if (mail_error.isSet()) {
1852                     return;
1853                 }
1854                 SELECTION_MADE = TRUE;
1855             } 
1856             // If the last selected item is above the deselected item,
1857             // it follows that all selected items are above the deselected
1858             // item.  Display the last selected item and be done.
1859             else if (cbs->selected_item_positions[num_selected - 1] <
1860                                 last_clicked_on_pos) {
1861                 obj->extended_selection(mail_error,
1862                            cbs->selected_item_positions[num_selected - 1]);
1863                 if (mail_error.isSet()) {
1864                     return;
1865                 }
1866                 SELECTION_MADE = TRUE;
1867             }
1868             // Otherwise, the deselected item must lie in between other
1869             // selected items.  We choose to find the closest selected
1870             // item above the deselected item.
1871             else {
1872                 // There are selected items that are above the deselected 
1873                 // item.
1874                 // Need to find the one that is both above the deselected 
1875                 // item and closest to it.
1876                 // Iterate until you find the nearest above; select it and
1877                 // drop out of loop when after selection.
1878
1879                 for (i = 0; i < (num_selected - 1) && !SELECTION_MADE; i++) {
1880                     above_selected_pos = cbs->selected_item_positions[i];
1881                     tmp_selected_pos = cbs->selected_item_positions[i + 1];
1882                     if (tmp_selected_pos > last_clicked_on_pos) {
1883                         obj->extended_selection(mail_error,
1884                                         above_selected_pos);
1885                         if (mail_error.isSet()) {
1886                             return;
1887                         }
1888                         SELECTION_MADE = TRUE;
1889                     }
1890                 }
1891             }
1892         }
1893     }
1894 }
1895
1896
1897 void
1898 MsgScrollingList::extended_selection(
1899     DtMailEnv &mail_error,
1900     int position 
1901 )
1902 {
1903    FORCE_SEGV_DECL(MsgStruct, tmpMS);
1904
1905    // Disable the SaveAttachments menu item first.  
1906    // It gets enabled when an attachment is selected.
1907
1908    _parent->all_attachments_deselected();
1909
1910    if (position == 0) {  // all items deselected.
1911        _parent->hideAttachArea();
1912        this->display_no_message();
1913        _selection_on = FALSE;
1914        _parent->deactivate_default_message_menu();
1915        return;
1916    }
1917
1918    // if there were no selected item(s) before and now we
1919    // have one/some, we need to turn on the message menu at
1920    // the parent level.
1921
1922    if (!_selection_on) {
1923        _selection_on = TRUE;
1924        _parent->activate_default_message_menu();
1925    }
1926    
1927    // If selected message is the displayed message, return.
1928    // No need to redisplay the same message.
1929
1930    if (position == _selected_item_position) return;
1931
1932    // Retrieve message...
1933    // Display the selected message...
1934    _selected_item_position = position;
1935
1936    tmpMS = get_message_struct(_selected_item_position);
1937    if (tmpMS == NULL) {
1938        return;
1939     }
1940    else {
1941        display_message_summary();
1942        this->display_message(mail_error, tmpMS->message_handle);
1943        if (mail_error.isSet()) {
1944            return;
1945        }
1946    }
1947 }
1948
1949
1950 int
1951 MsgScrollingList::get_selected_item()
1952 {
1953     return(_selected_item_position);
1954 }
1955
1956 int
1957 MsgScrollingList::get_displayed_item()
1958 {
1959     return(_displayed_item_position);
1960 }
1961
1962 void
1963 MsgScrollingList::scroll_to_bottom()
1964 {
1965     XmListSetBottomPos( this->baseWidget() , 0 );
1966 }
1967
1968 // Scroll the list so that the item at position is in the
1969 // middle of the scrolling list.  If the number of items
1970 // that the scrolling list can display at one time is greater
1971 // than the total number of items, then display them all.
1972 // If the item at position is close to the bottom of the list
1973 // make sure we make as many items visible as possible.
1974 //
1975 // If the item at position is already visible, then don't
1976 // do anything.
1977
1978 void
1979 MsgScrollingList::scroll_to_position(
1980     int position
1981 )
1982 {
1983     int top, visible, total;
1984     int top_pos;
1985
1986     // Determine the position of the header that we want to select.
1987     // If the XtArgCollector exists, then add
1988     // the resources to the XtArgCollector so that we can prevent
1989     // multiple redisplays in the XmList widget.
1990     if (_xmstr_collector)
1991     {
1992         XtVaGetValues( _w,
1993                    XmNtopItemPosition, &top,
1994                    XmNvisibleItemCount, &visible,
1995                    NULL );
1996         total = _xmstr_collector->GetNumItems();
1997     }
1998     else
1999     {
2000         XtVaGetValues( _w,
2001                    XmNtopItemPosition, &top,
2002                    XmNvisibleItemCount, &visible,
2003                    XmNitemCount, &total,
2004                    NULL );
2005     }
2006
2007     if (( position < top ) || ( position >= top+visible )) {
2008
2009         if ((total <= visible) || (position <= visible/2)) {
2010             // If we can display them all, make the first item appear
2011             // at the top of the list.
2012             top_pos = 1;
2013         } else if (position > (total-(visible/2))) {
2014             top_pos = total - visible + 1;
2015         } else {
2016             top_pos = position - visible/2 + 1;
2017         }
2018
2019         // Determine the position of the header that we want to select.
2020         // If the XtArgCollector exists, then add
2021         // the resources to the XtArgCollector so that we can prevent
2022         // multiple redisplays in the XmList widget.
2023         if (_xtarg_collector)
2024             _xtarg_collector->AddItemToList (
2025                 XmNtopItemPosition, top_pos);
2026         else
2027             XmListSetPos (_w, top_pos);
2028     }
2029 }
2030
2031 void
2032 MsgScrollingList::undelete_messages(MsgHndArray *tmpMHlist)
2033 {
2034     FORCE_SEGV_DECL(MsgStruct, tmpMS);
2035     FORCE_SEGV_DECL(XmString, deleted_headers);
2036     int  i, num_entries, entry_position, del_pos;
2037     int whichToSelectDisplay = 0;
2038     DtMail::MailBox     *mbox=parent()->mailbox();
2039     DtMail::Message * tmpMsg;
2040     DtMailEnv mail_error;
2041     DtMailMessageHandle tmpMH;
2042 #ifdef undef
2043     XmString read_status, new_status;
2044 #endif
2045     XmString complete_header;   // read status + glyph + header_text.
2046
2047 #ifdef undef
2048 /* NL_COMMENT
2049  * In a mailer container window's message scrolling list, a "N" appears
2050  * to the left of a mail message header indicating that the mail message
2051  * is "new" (just arrived and not yet viewed by the user).
2052  * There is only space to display 1 character.  If "N" needs to be translated,
2053  * please make sure the translation is only 1 character.
2054  */
2055 //
2056 // gregl - new_status and read_status are not used in this function.
2057 //         either comment them out (like I'm doing) or free them.
2058 //
2059     new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 112, "N"));
2060     read_status = XmStringCreateLocalized(" ");
2061 #endif
2062
2063     // Initialize the mail_error.
2064     mail_error.clear();
2065     num_entries = tmpMHlist->length();
2066     if (num_entries == 0) return;
2067
2068     // Deselect all items currently selected.
2069     // display_and_select_message() will select, highlight
2070     // and display the last "undeleted" message.
2071     
2072     XmListDeselectAllItems(_w);
2073
2074     XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2075
2076     for (i = 0; i < num_entries; i++)
2077     {
2078         DtMailHeaderLine info;
2079
2080         tmpMS = tmpMHlist->at(i);
2081         tmpMS->is_deleted = FALSE;
2082
2083         // Reset the flag of the message in message store so that the  
2084         // message will not be expunged when the folder is quit.
2085         // 
2086
2087         tmpMsg = mbox->getMessage(mail_error, tmpMS->message_handle);
2088         tmpMsg->resetFlag(mail_error, DtMailMessageDeletePending);
2089         tmpMH = tmpMS->message_handle;
2090         
2091         // Remove chosen item from list of deleted messages;
2092         // insert it back into _msgs at the right place (which is 
2093         // determined by session_number of retrieved MsgStruct).
2094         // Insert back into scrolling list for visual display
2095         // at the position session_number.
2096
2097         entry_position = _msgs->insert(tmpMS);
2098
2099         // Increment by one, because the index into the scrolling
2100         // list is always one greater than the index into the
2101         // message handle array that we got the index from.
2102
2103         entry_position = entry_position + 1;
2104
2105         // Maintain the assumption that the item at entry_position
2106         // is the selected item
2107
2108         _selected_item_position = entry_position;
2109
2110         mbox->getMessageSummary(mail_error, tmpMS->message_handle,
2111                                 _header_info, info);
2112         DtMail::Message * msg = mbox->getMessage(mail_error, tmpMH);
2113         complete_header = formatHeader(
2114                            info,
2115                            tmpMS->indexNumber,
2116                            show_with_attachments(msg),
2117                            msg->flagIsSet(mail_error, DtMailMessageNew));
2118
2119         mbox->clearMessageSummary(info);
2120
2121         if (msg->flagIsSet(mail_error, DtMailMessageNew) == DTM_TRUE)
2122           num_new_messages++;
2123
2124         XmListAddItem(_w, complete_header, entry_position);
2125         XmListSelectItem(_w, complete_header, FALSE);
2126         XmStringFree(complete_header);
2127
2128         // Get position of undeleted message structure in _deleted_messages
2129         // and remove the entry from _deleted_messages
2130
2131         del_pos = _deleted_messages->indexof(tmpMS);
2132         _deleted_messages->remove_entry(del_pos);
2133     }
2134
2135     // Display this message and select it.
2136
2137     this->display_message(mail_error, tmpMS->message_handle);
2138     XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
2139     XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
2140
2141     if (mail_error.isSet()) return;
2142
2143     num_deleted_messages -= num_entries;
2144
2145     MsgHndArray *selected_messages = selected();
2146     updateListItems(-1, TRUE, selected_messages);
2147     delete selected_messages;
2148
2149     display_message_summary();
2150 }
2151
2152 void
2153 MsgScrollingList::undelete_last_deleted()
2154 {
2155     FORCE_SEGV_DECL(MsgStruct, tmpMS);
2156     int entry_position;
2157     int len;
2158     DtMail::MailBox     *mbox=parent()->mailbox();
2159     UndelFromListDialog *undel_dialog;
2160     DtMailEnv mail_error;
2161     DtMailHeaderLine info;
2162     DtMail::Message * tmpMsg;
2163     DtMailMessageHandle tmpMH;
2164 #ifdef undef
2165     XmString read_status, new_status;
2166 #endif
2167     XmString complete_header;   // read status + glyph + header_text.
2168
2169 #ifdef undef
2170 /* NL_COMMENT
2171  * In a mailer container window's message scrolling list, a "N" appears
2172  * to the left of a mail message header indicating that the mail message
2173  * is "new" (just arrived and not yet viewed by the user).
2174  * There is only space to display 1 character.  If "N" needs to be translated,
2175  * please make sure the translation is only 1 character.
2176  */
2177     new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 113, "N"));
2178     read_status = XmStringCreateLocalized(" ");
2179 #endif
2180
2181
2182     if (num_deleted_messages == 0) return;
2183
2184     // Initialize the mail_error.
2185     mail_error.clear();
2186
2187
2188     // Delete the message from the Deleted Messages Dialog.
2189     undel_dialog = parent()->get_undel_dialog();
2190     if (undel_dialog)
2191         undel_dialog->undelLast();
2192     
2193     // Restore the message in RoamMenuWindow:MessageScrollingList.
2194     len = _deleted_messages->length();
2195
2196     tmpMS = _deleted_messages->at(len - 1);
2197     tmpMS->is_deleted = FALSE;
2198
2199     // Reset the flag of the message in message store so that the  
2200     // message will not be expunged when the folder is quit.
2201     // 
2202
2203     tmpMsg = mbox->getMessage(mail_error, tmpMS->message_handle);
2204     tmpMsg->resetFlag(mail_error, DtMailMessageDeletePending);
2205     tmpMH = tmpMS->message_handle;
2206
2207     // Remove chosen item from list of deleted messages;
2208     // insert it back into _msgs at the right place (which is 
2209     // determined by session_number of retrieved MsgStruct).
2210     // Insert back into scrolling list for visual display
2211     // at the position session_number.
2212
2213     entry_position = _msgs->insert(tmpMS);
2214
2215     // Increment by one, because the index into the scrolling
2216     // list is always greater than the index into the
2217     // message handle array that we got the index from.
2218
2219     entry_position = entry_position + 1;
2220
2221     mbox->getMessageSummary(mail_error, tmpMS->message_handle,
2222                             _header_info, info);
2223     DtMail::Message * msg = mbox->getMessage(mail_error, tmpMH);
2224
2225     complete_header = formatHeader(
2226                         info,
2227                         tmpMS->indexNumber,
2228                         show_with_attachments(msg),
2229                         msg->flagIsSet(mail_error, DtMailMessageNew));
2230
2231     mbox->clearMessageSummary(info);
2232
2233     if (msg->flagIsSet(mail_error, DtMailMessageNew) == DTM_TRUE) {
2234         num_new_messages++;
2235     }
2236
2237     _deleted_messages->remove_entry(len - 1);
2238     num_deleted_messages--;
2239     XmListAddItemUnselected(_w, complete_header, entry_position);
2240     XmStringFree(complete_header);
2241
2242     
2243     // If the undeleted message is before the currently viewed message,
2244     // then need to readjust our numbers by adding one to them -- there
2245     // is one more message above the currently-viewed message.
2246     // Don't need to do anything if the undeleted message is after the
2247     // currently-viewed message.
2248
2249     
2250     // Deselect all items currently selected.
2251     // display_and_select_message() will select, highlight
2252     // and display the last "undeleted" message.
2253     
2254     XmListDeselectAllItems(_w);
2255
2256     _selected_item_position = entry_position;
2257     this->display_and_select_message(mail_error, tmpMS->message_handle);
2258     if (mail_error.isSet()) return;
2259
2260     updateListItems(-1, TRUE, NULL);
2261     display_message_summary();
2262 }
2263
2264 DtMailBoolean
2265 MsgScrollingList::senderIsToHeaderWhenMailFromMe(void)
2266 {
2267     if (_header_info.number_of_names == 5)
2268       return(DTM_TRUE);
2269
2270     return(DTM_FALSE);
2271 }
2272
2273 void
2274 MsgScrollingList::checkDisplayProp(void)
2275 {
2276     DtMail::MailBox * mbox;
2277     DtMail::MailRc * mailrc;
2278     DtMailEnv mail_error;
2279     Boolean state_changed = FALSE;
2280
2281     mbox = parent()->mailbox();
2282     mailrc = mbox->session()->mailRc(mail_error);
2283
2284     const char * value = NULL;
2285     mailrc->getValue(mail_error, "showto", &value);
2286     if (mail_error.isNotSet()) {
2287         // showto is set...if number_of_names is 4 then they
2288         // just applied props and the showto value
2289         if (_header_info.number_of_names == 4) {
2290             _header_info.number_of_names = 5;
2291             state_changed = TRUE;
2292         }
2293     }
2294     else if (_header_info.number_of_names == 5) {
2295         // They just applied props and changed the showto value
2296         _header_info.number_of_names = 4;
2297         state_changed = TRUE;
2298     }
2299     if (NULL != value)
2300       free((void*) value);
2301
2302     // Here we need to adjust the header labels to maintain them left
2303     // justified. It looks better than centered. We only whant to change
2304     // the labels if message numbering has been enabled. Otherwise we will
2305     // leave them alone. Note: The _numbered stores the previous state for
2306     // message numbering. Check the error return when getting the value uses
2307     // reverse logic. ugly...
2308     DtMailBoolean use_msg_numbers;
2309
2310     value = NULL;
2311     mail_error.clear();
2312     mailrc->getValue(mail_error, "showmsgnum", &value);
2313
2314     use_msg_numbers = (mail_error.isNotSet()) ? DTM_TRUE : DTM_FALSE;
2315     if (NULL != value) free((void*) value);
2316
2317     if (use_msg_numbers != _numbered) 
2318     {
2319         _numbered = use_msg_numbers;
2320         layoutLabels(_sender_lbl, _subject_lbl, _date_lbl, _size_lbl);
2321     }
2322     else if (!state_changed) return;
2323
2324     // We have to build two lists from the current normal and deleted
2325     // lists. These will contain the new header lines.
2326     //
2327     DtMailHeaderLine info;
2328     XmString * normal_list = new XmString[_msgs->length()];
2329
2330     for (int m = 0; m < _msgs->length(); m++) {
2331         MsgStruct * ms = _msgs->at(m);
2332
2333         DtMail::Message * msg =
2334                         mbox->getMessage(mail_error, ms->message_handle);
2335         mbox->getMessageSummary(
2336                         mail_error, ms->message_handle,
2337                         _header_info, info);
2338         normal_list[m] = formatHeader(
2339                             info,
2340                             ms->indexNumber,
2341                             show_with_attachments(msg),
2342                             msg->flagIsSet(mail_error, DtMailMessageNew));
2343         mbox->clearMessageSummary(info);
2344     }
2345
2346     XmListReplaceItemsPos(_w, normal_list, _msgs->length(), 1);
2347     for (int fr = 0; fr < _msgs->length(); fr++) {
2348         XmStringFree(normal_list[fr]);
2349     }
2350     delete normal_list;
2351
2352     UndelFromListDialog * del_dialog = _parent->get_undel_dialog();
2353     if (del_dialog) {
2354         XmString * del_list = new XmString[_deleted_messages->length()];
2355         
2356         for (int m2 = 0; m2 < _deleted_messages->length(); m2++) {
2357             MsgStruct * ms = _deleted_messages->at(m2);
2358             
2359             DtMail::Message * msg =
2360                 mbox->getMessage(mail_error, ms->message_handle);
2361             mbox->getMessageSummary(
2362                                 mail_error, ms->message_handle,
2363                                 _header_info, info);
2364             del_list[m2] = formatHeader(
2365                              info,
2366                              ms->indexNumber,
2367                              show_with_attachments(msg),
2368                              msg->flagIsSet(mail_error, DtMailMessageNew));
2369             mbox->clearMessageSummary(info);
2370         }
2371         
2372         del_dialog->replaceItems(del_list, _deleted_messages->length());
2373         for (int fr2 = 0; fr2 < _deleted_messages->length(); fr2++) {
2374             XmStringFree(del_list[fr2]);
2375         }
2376         delete del_list;
2377     }
2378 }
2379
2380 //
2381 // Update the scrolling list. Current is the index of the message
2382 // to position the scrolling list to.  -1 to keep it as is.
2383 //
2384 void
2385 MsgScrollingList::updateListItems(int current,
2386                                   Boolean renumber_only,
2387                                   MsgHndArray *selected_messages)
2388 {
2389     DtMail::MailBox * mbox = NULL;
2390     DtMail::MailRc * mailrc;
2391     DtMailEnv mail_error;
2392     const char * value = NULL;
2393     int         nmsgs;
2394
2395     mbox = parent()->mailbox();
2396     mailrc = mbox->session()->mailRc(mail_error);
2397
2398     if (current < 0) current = _displayed_item_position;
2399     resetIndexNums();
2400
2401     mailrc->getValue(mail_error, "showmsgnum", &value);
2402     if (mail_error.isSet() && renumber_only) return;
2403
2404     if (selected_messages)
2405       XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2406
2407     //
2408     // We need to build a new list of strings to display
2409     // in the scrolling list.  Initialize that now.
2410     //
2411     DtMailHeaderLine info;
2412     XmString * newList;
2413     MsgStruct *ms;
2414
2415     nmsgs = _msgs->length();
2416     newList = new XmString[nmsgs];
2417     memset (newList, 0, nmsgs * sizeof (XmString *));
2418
2419     // Loop through _msgs and create new strings to display in the
2420     // scrolling list. This is inefficient and dominates the time
2421     // spent in sort.  It may be worth while finding a way to just
2422     // rearrange the existing strings.
2423     for (int m = 0; m < nmsgs; m++)
2424     {
2425         DtMail::Message * msg = mbox->getMessage(mail_error,
2426                         _msgs->at(m)->message_handle);
2427
2428         if (mail_error.isSet())
2429           fprintf(stderr, "dtmail: getMessage: Couldn't get message #%d\n", m);
2430         
2431         mbox->getMessageSummary(
2432                         mail_error, _msgs->at(m)->message_handle,
2433                         _header_info, info);
2434
2435         if (mail_error.isSet())
2436           fprintf(stderr,
2437            "dtmail: getMessageSummary: Couldn't get summary for msg # %d\n", m);
2438
2439         if (msg == NULL)
2440         {
2441                 // Error
2442                 ;
2443         }
2444         else
2445         {
2446                 ms = get_message_struct(m + 1);
2447                 newList [m] =
2448                   formatHeader(
2449                         info,
2450                         ms->indexNumber,
2451                         show_with_attachments(msg),
2452                         msg->flagIsSet(mail_error, DtMailMessageNew));
2453         }
2454
2455         // Free the space allocated for info
2456         // delete []info.header_values;
2457         mbox->clearMessageSummary(info);
2458     }
2459
2460     XmListReplaceItemsPos(_w, newList, session_message_number, 1);
2461     for (int fr = 0; fr < nmsgs; fr++)
2462       XmStringFree(newList[fr]);
2463
2464     // Update current message
2465     _selected_item_position = current;
2466     _displayed_item_position = current;
2467     scroll_to_position(_displayed_item_position);
2468
2469     if (selected_messages)
2470     {
2471         XmListDeselectAllItems(_w);
2472         for (int m=0, nselected=selected_messages->length(); m<nmsgs; m++)
2473         {
2474             MsgStruct *ms = get_message_struct(m+1);
2475             for (int s=0; s<nselected; s++)
2476             {
2477                 MsgStruct *sms = selected_messages->at(s);
2478                 if (ms == sms)
2479                   XmListSelectPos(_w, m+1, FALSE);
2480             }
2481         }
2482         XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
2483     }
2484     else
2485       XmListSelectPos(_w, _displayed_item_position, FALSE);
2486
2487     XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
2488     XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
2489     delete newList;
2490 }
2491
2492 XmString
2493 MsgScrollingList::formatHeader(DtMailHeaderLine & info,
2494                                int sess_num,
2495                                DtMailBoolean has_attachments,
2496                                DtMailBoolean new_msg)
2497 {
2498     char *buf = new char[BUFSIZ];
2499     memset(buf, 0, BUFSIZ);
2500     const char *from=NULL;
2501     char *subject;
2502     int contentLength;
2503     char contentStr[20];
2504     char *date = new char[BUFSIZ];
2505     int msg_num = sess_num + 1;
2506     static XmString attachment_glyph = NULL;
2507     static XmString no_attachment_glyph = NULL;
2508     static XmString new_status = NULL;
2509     static XmString read_status = NULL;
2510     static unsigned char attach_symbol[16];
2511     Boolean showto = FALSE;
2512
2513     if (!attachment_glyph) {
2514         attach_symbol[0] = 168;
2515         attach_symbol[1] = 0;
2516         attachment_glyph = XmStringCreate((char *)attach_symbol, "attach");
2517         no_attachment_glyph = XmStringCreateLocalized(" ");
2518         new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 114, "N"));
2519         read_status = XmStringCreateLocalized(" ");
2520     }
2521     
2522     // strip out the Name of sender and retain only the address.
2523     //Later, we will have separate  entries for name and address
2524     //and this stripping will not need to be done.
2525     
2526     // IMAP is incapable of handling a message header that begins
2527     // with From (space) (NOTE: not a From:).
2528     // Thus, if a message has only a From but does not have a From:
2529     // or a Reply-To: or Received-From:, IMAP doesn't tell us who
2530     // it is from.  In such cases, we set it to "???".
2531     
2532     DtMailAddressSeq * addr_seq = NULL;
2533     DtMailValueAddress * addr = NULL;
2534     
2535     if (info.header_values[0].length() != 0) {
2536         addr_seq = ((info.header_values[0])[0])->toAddress();
2537         addr = (*addr_seq)[0];
2538         if (_header_info.number_of_names == 5 && addr 
2539                 && addr->dtm_address && info.header_values[4].length() != 0) {
2540                 // Check if mail is from me
2541                 const char *ptr;
2542                 passwd pw;
2543                 GetPasswordEntry(pw);
2544                 if ((ptr = strchr(addr->dtm_address, '@')) != NULL) {
2545                         if (strncmp(pw.pw_name, addr->dtm_address, 
2546                                 ptr-addr->dtm_address) == 0) {
2547                                 from = *((info.header_values[4])[0]);
2548                                 showto = TRUE;
2549                         }
2550                 }
2551                 else 
2552                         if (strcmp(pw.pw_name, addr->dtm_address) == 0) {
2553                                 from = *((info.header_values[4])[0]);
2554                                 showto = TRUE;
2555                         }
2556         }
2557         if (from == NULL) {
2558                 if (addr && addr->dtm_person)
2559                         from = addr->dtm_person;
2560                 else if (addr && addr->dtm_address)
2561                         from = addr->dtm_address;
2562                 else from = "???";
2563         }
2564     }
2565     else
2566         from = "???";
2567     
2568     // If the Subject is nil
2569     
2570     if (info.header_values[3].length() == 0) {
2571         subject = new char[1];
2572         subject[0] = '\0';
2573     } else {
2574         // Get the BE store of header.  It may contain newlines or 
2575         // tab chars which can munge the scrolling list's display!
2576         // 
2577         const char *real_subj_header = *((info.header_values[3])[0]);
2578         int fc;
2579         int subj_len;
2580         char *tmp_subj;
2581
2582         // Check if BE store contains the funky chars.
2583
2584         for (fc = 0, subj_len = strlen(real_subj_header), 
2585               tmp_subj = (char *)real_subj_header; 
2586              fc < subj_len; fc++, tmp_subj++) {
2587
2588             char c = *tmp_subj;
2589             if ( (c == '\n') 
2590               || (c == '\t') 
2591               || (c == '\r')) {
2592
2593                 break;
2594             }
2595         }
2596         subject = new char[fc+1];
2597         strncpy((char *)subject, real_subj_header, fc);
2598         subject[fc] = '\0';
2599     }
2600
2601     
2602     // Skip the first (beginning) space in from; search for the next
2603     // occurring space.  
2604     
2605
2606     const char *dateformat;
2607     if (info.header_values[1].length() > 0)
2608     {
2609         DtMailValueDate ds = ((info.header_values[1])[0])->toDate();
2610
2611 #define USE_MAX_TZ_SECONDS      (60 * 60 * 12)
2612         if (ds.dtm_date && ds.dtm_tz_offset_secs >= -USE_MAX_TZ_SECONDS
2613                         && ds.dtm_tz_offset_secs <=  USE_MAX_TZ_SECONDS)
2614         {
2615 #define USE_YEAR_FORMAT_SECONDS (60 * 60 * 24 * 180)
2616             time_t now;
2617             tm tm_struct;
2618
2619             SafeLocaltime(&ds.dtm_date, tm_struct);
2620
2621             // Refer to strftime man page for explanation of the date format.
2622             now = time(NULL);
2623             if (USE_YEAR_FORMAT_SECONDS < now - ds.dtm_date)
2624               dateformat = GETMSG(DT_catd, 1, 259, "%a %b %d  %Y");
2625             else
2626             {
2627
2628 #ifdef sun
2629                 dateformat = GETMSG(DT_catd, 1, 260, "%a %b %d %k:%M");
2630 #else
2631                 dateformat = GETMSG(DT_catd, 1, 261, "%a %b %d %H:%M");
2632 #endif
2633             }
2634
2635             SafeStrftime(date, BUFSIZ, dateformat, &tm_struct);
2636         }
2637         else
2638           // Couldn't get Date string from Message. Make it empty.
2639           sprintf(date, "%s", " ");
2640     }
2641     else
2642     {
2643         tm epoch;
2644         memset(&epoch, 0, sizeof(tm));
2645
2646         /* Refer to strftime man page for explanation of the date format.  */
2647         dateformat = GETMSG(DT_catd, 1, 259, "%a %b %d  %Y");
2648         SafeStrftime(date, BUFSIZ, dateformat, &epoch);
2649     }
2650     
2651     if (info.header_values[2].length() > 0) {
2652         contentLength = (int) strtol(*((info.header_values[2])[0]), NULL, 10);
2653     }
2654     else {
2655         contentLength = 0;
2656     }
2657     
2658     if (contentLength < 1000) {
2659         sprintf(contentStr, "%d", contentLength);
2660     }
2661     else if (contentLength < 1000000) {
2662         sprintf(contentStr, "%dK", contentLength / 1000);
2663     }
2664     else {
2665         sprintf(contentStr, "%dM", contentLength / 1000000);
2666     }
2667     
2668     // If we are to print the message_number in the header_list,
2669     // use msg_num as the first element in the sprintf.  
2670     // Introduce a %d at the beginning though.
2671
2672     DtMail::MailBox * mbox;
2673     DtMail::MailRc * mailrc;
2674     DtMailEnv mail_error;
2675   
2676     mbox = parent()->mailbox();
2677     mailrc = mbox->session()->mailRc(mail_error);
2678
2679     const char * value = NULL;
2680     mailrc->getValue(mail_error, "showmsgnum", &value);
2681     if (mail_error.isSet()) {
2682         // No message numbers ... keep usual "35" col. "Subject".
2683       if (showto)
2684               sprintf(buf, " To %-15.15s %-35.35s %-17.17s %-5.5s",
2685                       from,
2686                       subject,
2687                       date,
2688                       contentStr);
2689       else
2690               sprintf(buf, " %-18.18s %-35.35s %-17.17s %-5.5s",
2691                       from,
2692                       subject,
2693                       date,
2694                       contentStr);
2695     }
2696     else {
2697         //  To keep 80 column format use 5 less columns of
2698         //  subject , when msg numbers are on.
2699       if (showto)
2700               sprintf(buf, " To %-15.15s %-30.30s %-17.17s %-5.5s",
2701                       from,
2702                       subject,
2703                       date,
2704                       contentStr);
2705       else
2706               sprintf(buf, " %-18.18s %-30.30s %-17.17s %-5.5s",
2707                       from,
2708                       subject,
2709                       date,
2710                       contentStr);
2711     }
2712     if (NULL != value)
2713       free((void*) value);
2714
2715     XmString header_text = XmStringCreateLocalized(buf);
2716     XmString item, item2, complete_header;
2717
2718     if (has_attachments == DTM_TRUE) {
2719         item = XmStringConcat(attachment_glyph, header_text);
2720         XmStringFree(header_text);
2721     }
2722     else {
2723         item = XmStringConcat(no_attachment_glyph, header_text);
2724         XmStringFree(header_text);
2725     }
2726
2727     if (new_msg == DTM_FALSE) {
2728         item2 = XmStringConcat(read_status, item);
2729         XmStringFree(item);
2730     }
2731     else {
2732         item2 = XmStringConcat(new_status, item);
2733         XmStringFree(item);
2734     }
2735
2736     value = NULL;
2737     mailrc->getValue(mail_error, "showmsgnum", &value);
2738     if (mail_error.isSet()) {
2739 //      complete_header = XmStringCopy(item2);
2740         complete_header = item2;
2741         _numbered = DTM_FALSE;
2742     }
2743     else {
2744         char num_buf[64];
2745
2746         if (NULL != value)
2747           free((void*) value);
2748
2749         mailrc->getValue(mail_error, "nerdmode", &value);
2750         if (mail_error.isSet()) {
2751             sprintf(num_buf, "%4d ", msg_num);
2752         }
2753         else {
2754             sprintf(num_buf, "%4x ", msg_num - 1);
2755         }
2756
2757         XmString num_str = XmStringCreateLocalized(num_buf);
2758         complete_header = XmStringConcat(num_str, item2);
2759         XmStringFree(item2);
2760         XmStringFree(num_str);
2761         _numbered = DTM_TRUE;
2762     }
2763     if (NULL != value)
2764       free((void*) value);
2765
2766     delete addr_seq;
2767     delete subject;
2768     
2769     delete [] buf;
2770     delete [] date;
2771     return(complete_header);
2772 }
2773
2774 void
2775 MsgScrollingList::shutdown()
2776 {
2777     int num_entries, i;
2778     FORCE_SEGV_DECL(MsgStruct, tmpMS);
2779     DtMailMessageHandle tmpMH;
2780     DtMail::MailBox     *mbox=parent()->mailbox();
2781     DtMailEnv mail_error;
2782
2783     // Initialize the mail_error.
2784     mail_error.clear();
2785
2786     if (num_deleted_messages == 0) return;
2787     
2788     num_entries = _deleted_messages->length();
2789
2790     for (i = 0; i < num_entries; i++) {
2791         tmpMS = _deleted_messages->at(i);
2792         tmpMH = tmpMS->message_handle;
2793
2794 //      mbox->deleteMsg(mail_error, tmpMH);
2795         
2796     }
2797 }
2798
2799 #ifdef DEAD_WOOD
2800 DtMailMessageHandle
2801 MsgScrollingList::lastMsg()
2802 {
2803     if (_msgs->length() > 0) {
2804         return((_msgs->at(_msgs->length() - 1))->message_handle);
2805     }
2806     else {  // Currently an empty folder
2807         return(NULL);
2808     }
2809 }
2810 #endif /* DEAD_WOOD */
2811
2812 void
2813 MsgScrollingList::clearMsgs()
2814 {
2815     if ( _msgs->length() > 0 )
2816         _msgs->clear();
2817 }
2818
2819
2820 void
2821 MsgScrollingList::display_message_summary()
2822 {
2823     parent()->message_summary(
2824                         selected_item_position(),
2825                         get_num_messages(), 
2826                         get_num_new_messages(),
2827                         get_num_deleted_messages());
2828 }
2829
2830 void
2831 MsgScrollingList::display_message(
2832     DtMailEnv &mail_error,
2833     int pos
2834 )
2835 {
2836     if ((pos > 0) && (pos <= session_message_number)) {
2837         // When loading mail headers, we need to make sure that
2838         // the XmList resources are all set at the same time to
2839         // avoid painting multiple times.  So here we collect
2840         // these resources.
2841         if (_xtarg_collector && _xmstr_collector)
2842         {
2843             XmString *items = _xmstr_collector->GetItems();
2844             
2845             // Keep a handle to the malloced string copy so that
2846             // we can free it after the XtSetValues
2847             _selected_items = XmStringCopy (items[pos-1]);
2848
2849             _xtarg_collector->AddItemToList (XmNselectedItems,
2850                 (XtArgVal) &_selected_items);
2851             _xtarg_collector->AddItemToList (
2852                 XmNselectedItemCount, 1);
2853         }
2854         else
2855         {
2856             XmListSelectPos(_w, pos, FALSE);
2857         }
2858
2859         this->extended_selection(mail_error, pos);
2860         if (mail_error.isSet()) {
2861             // Return whatever error mailbox->get_next_msg() returned.
2862             return;
2863         }
2864     }
2865     else return;
2866 }
2867
2868 DtMailMessageHandle
2869 MsgScrollingList::current_msg_handle()
2870 {
2871     DtMailMessageHandle msg_number;
2872
2873     if ( _displayed_item_position > 0 )
2874         msg_number=msgno( _displayed_item_position );
2875     else
2876         msg_number=NULL;
2877
2878     return msg_number;
2879 }
2880
2881 void
2882 MsgScrollingList::expunge(void)
2883 {
2884     for (int i = _deleted_messages->length() - 1; i >= 0; i--) {
2885         _deleted_messages->remove_entry(i);
2886     }
2887     resetSessionNums();
2888     resetIndexNums();
2889     num_deleted_messages = 0;
2890     display_message_summary();
2891 }
2892
2893 int
2894 MsgScrollingList::resetIndexNums(void)
2895 {
2896     int m;
2897     int length = _msgs->length();
2898     MsgStruct *ms;
2899     
2900     for (m = 0; m < length; m++)
2901     {
2902         ms = _msgs->at(m);
2903         ms->indexNumber = m;
2904     }
2905
2906     length = _deleted_messages->length();
2907     for (m = 0; m < length; m++)
2908     {
2909         ms = _deleted_messages->at(m);
2910         ms->indexNumber = m;
2911     }
2912
2913     return m;
2914 }
2915
2916 int
2917 MsgScrollingList::resetSessionNums(void)
2918 {
2919     int m;
2920     int length = _msgs->length();
2921     MsgStruct *ms;
2922     
2923     for (m = 0; m < length; m++)
2924     {
2925         ms = _msgs->at(m);
2926         ms->sessionNumber = m;
2927     }
2928     session_message_number = m;
2929     return session_message_number;
2930 }
2931
2932 void
2933 MsgScrollingList::sort_messages(void)
2934 {
2935     DtMail::MailBox     *mbox;
2936     enum sortBy         sortby;
2937     int                 current_msg;
2938
2939     MsgHndArray *selected_messages = selected();
2940
2941     mbox = _parent->mailbox();
2942     sortby = _parent->last_sorted_by();
2943
2944     // Sort array of message handles
2945     current_msg = _sorter->sortMessages(this, mbox, sortby);
2946
2947     // The array of message handles is sorted. Now we need to update
2948     // the display preserving the selected state of the messages.
2949     updateListItems(current_msg, FALSE, selected_messages);
2950
2951     delete selected_messages;
2952
2953     _parent->last_sorted_by(sortby);
2954     display_message_summary();
2955 }
2956
2957 //
2958 // Layout out the row of labels above the scrolling list
2959 //
2960 void
2961 MsgScrollingList::layoutLabels(
2962         Widget sender,
2963         Widget subject,
2964         Widget date,
2965         Widget size)
2966 {
2967     // Save the input values 
2968     _sender_lbl = sender; 
2969     _subject_lbl = subject;
2970     _date_lbl = date;
2971     _size_lbl = size;
2972
2973     layoutLabels();
2974
2975 }
2976 //
2977 // Layout out the row of labels above the scrolling list
2978 //
2979 void
2980 MsgScrollingList::layoutLabels()
2981 {
2982     // Width of fields.  +1 for spaces
2983     int num_width = 5,
2984         sender_width = 18 + 1,
2985         subject_width = 35 + 1,
2986         date_width = 3+1 + 3+1 + 2+1 + 5 + 2; // DDD mmm dd hh:mm 2spaces
2987     int char_width;             // Width of a single character
2988     int n = 0;
2989     XmString    xmstr;
2990     XmFontList  font_list;
2991
2992     // Calculate the width of date format to allocate the label 
2993     // width dynamically
2994     struct tm *tm;
2995     time_t clock;
2996     char   buf[40];
2997     
2998     clock = time((time_t *) 0);
2999     tm = localtime(&clock);  
3000     #ifdef sun
3001         SafeStrftime(buf,
3002              sizeof(buf), 
3003              GETMSG(DT_catd, 1, 222, "%a %b %d %k:%M"), 
3004              tm);
3005     #else
3006         SafeStrftime(buf, 
3007              sizeof(buf), 
3008              GETMSG(DT_catd, 1, 223, "%a %b %d %H:%M"), 
3009              tm);
3010     #endif
3011
3012
3013     // List uses a fixed width font. Therefore all characters are the
3014     // same size.  So we use a space to determine the width of a char
3015     xmstr = XmStringCreateLocalized(" ");
3016     XtVaGetValues(_w, XmNfontList, &font_list, NULL);
3017     char_width = XmStringWidth(font_list, xmstr);
3018
3019     if (_numbered) {
3020         // Numbering is on
3021         n = num_width;
3022         subject_width -= num_width;
3023     } else {
3024         n = 0;
3025     }
3026
3027     // XXX dipol: Need to take into account if the scrollbar is on the
3028     // right or left
3029     n += 3;     // Margin.
3030
3031     XtWidgetGeometry geom;
3032     geom.request_mode = CWX;
3033     XtQueryGeometry(_sender_lbl, NULL, &geom);
3034     if (geom.x != (n * char_width)) {
3035         geom.x = (n * char_width);
3036         XtMoveWidget(_sender_lbl, geom.x, geom.y);
3037     } // Move the X location of the sender title
3038
3039     XtVaSetValues(_sender_lbl, XmNx, n * char_width, NULL);
3040     n += sender_width;
3041
3042     XtQueryGeometry(_subject_lbl, NULL, &geom);
3043     if (geom.x != (n * char_width)) {
3044         geom.x = (n * char_width);
3045         XtMoveWidget(_subject_lbl, geom.x, geom.y);
3046     } // Move the X location of the subject title
3047
3048     XtVaSetValues(_subject_lbl, XmNx, n * char_width, NULL);
3049     n += subject_width;
3050     XtVaSetValues(_date_lbl, XmNx, n * char_width, NULL);
3051     n += date_width;
3052     XtVaSetValues(_size_lbl, XmNx, n * char_width, NULL);
3053
3054     XmStringFree(xmstr);
3055
3056     return;
3057 }