4 * $TOG: MsgScrollingList.C /main/38 1998/12/10 19:08:02 mgreess $
6 * RESTRICTED CONFIDENTIAL INFORMATION:
8 * The information in this document is subject to special
9 * restrictions in a confidential disclosure agreement between
10 * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
11 * document outside HP, IBM, Sun, USL, SCO, or Univel without
12 * Sun's specific written approval. This document and all copies
13 * and derivative works thereof must be returned or destroyed at
16 * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
21 #include <EUSCompat.h>
27 #include <DtMail/DtMailError.hh>
28 #include <DtMail/IO.hh>
29 #include "DtMailGenDialog.hh"
30 #include "DtMailHelp.hh"
31 #include "Help.hh" // Remove after fixing problem with empty time headers
33 #include "MemUtils.hh"
34 #include "MsgHndArray.hh"
35 #include "MsgScrollingList.hh"
37 #include "QueryDialogManager.hh"
38 #endif /* DEAD_WOOD */
40 #include "RoamMenuWindow.h"
44 #include <X11/IntrinsicP.h> // Include for moving X location of titles
46 extern force( Widget );
48 MsgScrollingList::MsgScrollingList(
49 RoamMenuWindow *menuwindow,
59 _numbered = DTM_FALSE;
60 _selected_item_position=-1;
61 _displayed_item_position=-1;
62 _selection_on = FALSE;
63 _xmstr_collector = NULL;
64 _xtarg_collector = NULL;
65 _selected_items = NULL;
66 _sorter = new Sort ();
69 XmNextendedSelectionCallback,
71 &MsgScrollingList::extendedSelectionCallback,
74 _msgs = new MsgHndArray(1024);
75 _deleted_messages = new MsgHndArray(1024);
77 num_deleted_messages = 0;
78 session_message_number = 0;
80 // Can later initialize these from the last use of the session.
81 // Each folder will have some idea of which message was last
82 // read. We should select and display it at next load, no?
84 _selected_item_position = 0;
85 _displayed_item_position = 0;
88 // Initialize the mail_error.
90 DtMail::Session * d_session = theRoamApp.session()->session();
91 DtMail::MailRc * mailrc = d_session->mailRc(mail_error);
94 const char * value = NULL;
95 mailrc->getValue(mail_error, "showto", &value);
96 if (mail_error.isNotSet())
97 _header_info.number_of_names = 5;
99 _header_info.number_of_names = 4;
104 _header_info.number_of_names = 4;
106 // Set up array for 5 items. The DtMailMessageTo is used only
107 // if showto is set, but create placeholder for 5th item in case
108 // they apply props in this same session. Then we just have to
109 // toggle between 4 or 5 number_of_nanes.
111 _header_info.header_name = new (char* [5]);
113 _header_info.header_name[0] = NULL;
114 _header_info.header_name[1] = NULL;
115 _header_info.header_name[2] = NULL;
116 _header_info.header_name[3] = NULL;
117 _header_info.header_name[4] = NULL;
119 _header_info.header_name[0] = strdup(DtMailMessageSender);
120 _header_info.header_name[1] = strdup(DtMailMessageReceivedTime);
121 _header_info.header_name[2] = strdup(DtMailMessageContentLength);
122 _header_info.header_name[3] = strdup(DtMailMessageSubject);
123 _header_info.header_name[4] = strdup(DtMailMessageTo);
126 MsgScrollingList::~MsgScrollingList()
133 if (_header_info.header_name[i])
135 free(_header_info.header_name[i]);
136 _header_info.header_name[i] = NULL;
139 delete _header_info.header_name;
141 for (i=0, length=_deleted_messages->length(); i<length; i++)
143 ms = _deleted_messages->at(i);
146 _deleted_messages->clear();
147 delete _deleted_messages;
149 for (i=0, length=_msgs->length(); i<length; i++)
161 MsgScrollingList::get_scrolling_list()
167 MsgScrollingList::items(
179 MsgScrollingList::addChooseCommand(
187 MsgScrollingList::addDeleteCommand(
193 #endif /* DEAD_WOOD */
197 MsgScrollingList::select_next_item()
201 FORCE_SEGV_DECL(MsgStruct, tmpMS);
202 DtMailEnv mail_error;
204 // Initialize the mail_error.
208 XmNitemCount, &num_msgs,
211 _selected_item_position = _displayed_item_position + 1;
213 if (_selected_item_position <= num_msgs &&
214 _selected_item_position > 0 ) {
216 tmpMS = get_message_struct(_selected_item_position);
221 // Deselect all items currently selected.
222 // display_and_select_message() will select, highlight
223 // and display the "next" message.
225 XmListDeselectAllItems(_w);
227 this->display_and_select_message(mail_error,
228 tmpMS->message_handle);
229 if (mail_error.isSet()) {
230 // Post an exception here...
240 MsgScrollingList::select_prev_item()
244 FORCE_SEGV_DECL(MsgStruct, tmpMS);
245 DtMailEnv mail_error;
247 // Initialize the mail_error.
251 XmNitemCount, &num_msgs,
254 if( _displayed_item_position != 1 )
255 _selected_item_position = _displayed_item_position - 1 ;
257 _selected_item_position = _displayed_item_position ;
259 if (_selected_item_position >= 1) {
260 tmpMS = get_message_struct(_selected_item_position);
265 // Deselect all items currently selected.
266 // display_and_select_message() will select, highlight
267 // and display the "previous" message.
269 XmListDeselectAllItems(_w);
271 this->display_and_select_message(mail_error,
272 tmpMS->message_handle);
273 if (mail_error.isSet()) {
274 // Post an exception here...
284 MsgScrollingList::msgno(
292 return _msgs->at(index-1)->message_handle;
297 MsgScrollingList::get_message_struct(
305 return(_msgs->at(index-1));
310 MsgScrollingList::position(
311 DtMailMessageHandle msgno
314 return (_msgs->indexof(msgno))+1;
319 MsgScrollingList::position(
320 MsgStruct *a_msg_struct
323 return (_msgs->indexof(a_msg_struct))+1;
327 MsgScrollingList::appendMsg(
328 DtMailMessageHandle msg_hndl
333 // A new message has come in.
334 // Increase the session_message_number which keeps track of the
335 // number of messages in this session for this folder (scrolling list).
338 newMS = new MsgStruct();
339 newMS->message_handle = msg_hndl;
340 newMS->indexNumber = session_message_number-num_deleted_messages;
341 newMS->sessionNumber = session_message_number;
342 newMS->is_deleted = FALSE;
343 _msgs->append(newMS);
344 session_message_number++;
347 #endif /* DEAD_WOOD */
351 MsgScrollingList::insertMsg(
352 DtMailMessageHandle msg_hndl
357 // A new message has come in.
358 // Increase the session_message_number which keeps track of the
359 // number of messages in this session for this folder (scrolling list).
362 newMS = new MsgStruct();
363 newMS->message_handle = msg_hndl;
364 newMS->indexNumber = session_message_number-num_deleted_messages;
365 newMS->sessionNumber = session_message_number;
366 newMS->is_deleted = FALSE;
367 _msgs->append(newMS);
369 session_message_number++;
373 MsgScrollingList::insertDeletedMsg(DtMailMessageHandle msg_hndl)
377 // A new message has come in.
378 // Increase the session_message_number which keeps track of the
379 // number of messages in this session for this folder (scrolling list).
382 newMS = new MsgStruct();
383 newMS->message_handle = msg_hndl;
384 newMS->indexNumber = num_deleted_messages;
385 newMS->sessionNumber = session_message_number;
386 newMS->is_deleted = FALSE;
387 _deleted_messages->append(newMS);
389 session_message_number++;
390 num_deleted_messages++;
394 MsgScrollingList::load_headers(
395 DtMailEnv &mail_error
399 DtMailMessageHandle tmpMH;
401 XmString new_status, read_status;
403 XmString complete_header; // text of header w/ glyphs
406 DtMailHeaderLine info;
407 DtMail::MailBox * mbox = this->parent()->mailbox();
409 DtMailBoolean first_new = DTM_TRUE;
411 // Create a class to collect the mail header XmStrings
412 // then get all the items from the current list.
413 _xmstr_collector = new XmStrCollector();
417 * In a mailer container window's message scrolling list, a "N" appears
418 * to the left of a mail message header indicating that the mail message
419 * is "new" (just arrived and not yet viewed by the user).
420 * There is only space to display 1 character. If "N" needs to be translated,
421 * please make sure the translation is only 1 character.
423 new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 110, "N"));
424 read_status = XmStringCreateLocalized(" ");
427 // Allocate memory for the XmString array and initialize it.
430 XtVaGetValues(_w, XmNvisibleItemCount, &visible, NULL);
432 // Retrieve the message_handles, and from them their headers.
433 // Create an XmString and toss it into the XmStrCollector.
439 for (tmpMH = mbox->getFirstMessageSummary(error, _header_info, info);
440 tmpMH && !error.isSet();
441 tmpMH = mbox->getNextMessageSummary(error, tmpMH, _header_info, info),
444 DtMail::Message * msg = mbox->getMessage(error, tmpMH);
445 if (msg->flagIsSet(error, DtMailMessageDeletePending) == DTM_TRUE) {
446 insertDeletedMsg(tmpMH);
454 ms = get_message_struct(get_num_messages());
455 complete_header = formatHeader(
458 show_with_attachments(msg),
459 msg->flagIsSet(error, DtMailMessageNew));
462 if (msg->flagIsSet(error, DtMailMessageNew) == DTM_TRUE) {
465 // We want to select the last read message before the
466 // first new message. We will select the first new
467 // message if it is the first message.
470 first_new = DTM_FALSE;
472 select_item = num_items - 1;
478 // Insert the XmString into the array.
479 _xmstr_collector->AddItemToList (complete_header);
481 // Free the space allocated for info
482 // delete []info.header_values;
483 mbox->clearMessageSummary(info);
486 // If there were no new messages, select and display the last message.
488 select_item = num_items - 1;
491 select_item += 1; // Message slots start at 1.
493 select_item -= num_deleted_messages; // List does not have deleted msgs.
495 // Add the items to the XmList.
496 // All XmStrings are freed in the XmStrCollector destructor.
497 _xtarg_collector = new XtArgCollector;
499 // The first time the headers are loaded, they should all be loaded
500 // at the same time. XtVaSetValues is used for this rather than
501 // XmListAddItems so that all the other resource will be set at
502 // the same time. This prevents multiple repaints.
504 // However, in the case where only an item or two are being added,
505 // XmListAddItems should be used. This prevents an unnecessary
506 // repaint in this case.
507 XmListAddItems (_w, _xmstr_collector->GetItems(),
508 _xmstr_collector->GetNumItems(), 0);
510 display_message_summary();
511 display_message(mail_error, select_item);
513 _xtarg_collector->SetValues(_w);
516 XmStringFree (_selected_items);
517 _selected_items = NULL;
520 delete _xtarg_collector;
521 delete _xmstr_collector;
522 _xtarg_collector = NULL;
523 _xmstr_collector = NULL;
529 MsgScrollingList::load_headers(
530 DtMailEnv &mail_error,
531 DtMailMessageHandle last
534 DtMailMessageHandle tmpMH;
536 XmString read_status, new_status;
538 XmString complete_header; // read status + attach + header_text.
540 int num_new = 0, num_vis = 0;
541 DtMailHeaderLine info;
543 DtMail::MailBox * mbox = this->parent()->mailbox();
546 // Create a class to collect the mail header XmStrings
547 // then get all the items from the current list.
548 _xmstr_collector = new XmStrCollector();
554 * In a mailer container window's message scrolling list, a "N" appears
555 * to the left of a mail message header indicating that the mail message
556 * is "new" (just arrived and not yet viewed by the user).
557 * There is only space to display 1 character. If "N" needs to be translated,
558 * please make sure the translation is only 1 character.
560 new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 111, "N"));
561 read_status = XmStringCreateLocalized(" ");
564 // Allocate memory for the XmString array and initialize it.
566 XtVaGetValues(_w, XmNvisibleItemCount, &num_items, NULL);
570 for (tmpMH = mbox->getNextMessageSummary(error, last, _header_info, info);
571 tmpMH && !error.isSet();
572 tmpMH = mbox->getNextMessageSummary(error, tmpMH, _header_info, info),
575 DtMail::Message * msg = mbox->getMessage(error, tmpMH);
576 if (msg->flagIsSet(error, DtMailMessageDeletePending) == DTM_TRUE) {
577 insertDeletedMsg(tmpMH);
585 ms = get_message_struct(get_num_messages());
586 complete_header = formatHeader(
589 show_with_attachments(msg),
590 msg->flagIsSet(error, DtMailMessageNew));
592 if (msg->flagIsSet(error, DtMailMessageNew) == DTM_TRUE) {
596 // Insert the XmString into the array.
597 _xmstr_collector->AddItemToList (complete_header);
599 // Free the space allocated for info
600 // delete []info.header_values;
601 mbox->clearMessageSummary(info);
604 // Add the items to the XmList.
605 // All XmStrings are freed in the XmStrCollector destructor.
606 _xtarg_collector = new XtArgCollector;
608 // The first time the headers are loaded, they should all be loaded
609 // at the same time. XtVaSetValues is used for this rather than
610 // XmListAddItems so that all the other resource will be set at
611 // the same time. This prevents multiple repaints.
613 // However, in the case where only an item or two are being added,
614 // XmListAddItems should be used. This prevents an unnecessary
615 // repaint in this case.
616 XmListAddItems (_w, _xmstr_collector->GetItems(),
617 _xmstr_collector->GetNumItems(), 0);
619 do_list_vis_adjustment();
621 display_message_summary();
623 _xtarg_collector->SetValues(_w);
626 XmStringFree (_selected_items);
627 _selected_items = NULL;
630 delete _xtarg_collector;
631 delete _xmstr_collector;
632 _xtarg_collector = NULL;
633 _xmstr_collector = NULL;
636 void MsgScrollingList::do_list_vis_adjustment()
639 int numNew = _xmstr_collector->GetNumItems();
640 int focItm = _selected_item_position;
643 int cItmCnt, pItmCnt, sItmCnt;
644 int cFocItm, cTopItm, cBotItm, cInvItm;
646 XtVaGetValues(list, XmNvisibleItemCount, &numItems, NULL);
647 XtVaGetValues(list, XmNitemCount, &cItmCnt, NULL);
648 XtVaGetValues(list, XmNselectedItemCount, &sItmCnt, NULL);
649 XtVaGetValues(list, XmNtopItemPosition, &cTopItm, NULL);
650 pItmCnt = cItmCnt - numNew;
652 cBotItm = cTopItm + numItems - 1; //[cTopItm...cBotItm is our window
654 //[User has chosen to view some messages and that view needs to be
656 if (cBotItm != pItmCnt)
659 //[If Brand New Mailbox
660 if (cItmCnt <= numItems)
663 //[If no items selected, simply synch right till bottom
665 XmListSetBottomPos(list, cItmCnt);
669 // assert(cFocItm != -1);
672 cInvItm = pItmCnt - cBotItm; //[Num below bottom-most, or hidden
674 if ((cFocItm <= cBotItm) && (cFocItm >= cTopItm)) {
675 int winM = cFocItm - cTopItm - cInvItm;
676 if (winM <= 0) { //[There is no scope of adjustment
679 //[All the new messages can be accomodated w/o scrolling curr selection
680 if (numNew <= winM) {
681 XmListSetBottomPos(list, cItmCnt);
684 //[All the new messages cannot be accomodated, but we will do best fit
686 int numNotShow = numNew - winM;
687 XmListSetBottomPos(list, cItmCnt - numNotShow);
692 XmListSetBottomPos(list, cItmCnt);
698 MsgScrollingList::deleteSelected(Boolean silent)
700 // SR - Added stuff below. Made code more efficient also.
701 FORCE_SEGV_DECL(MsgStruct, a_del_msg_struct);
702 FORCE_SEGV_DECL(MsgStruct, tmpMS);
703 FORCE_SEGV_DECL(ViewMsgDialog, tmpView);
704 DtMailMessageHandle tmpMH;
705 int position_in_list, i;
706 FORCE_SEGV_DECL(int, position_list);
708 Boolean any_selected;
710 UndelFromListDialog *undel_dialog;
711 int first_selected_pos;
712 DtMailEnv mail_error, error;
713 DtMailBoolean cur_state;
714 char *status_message, *str;
716 // Initialize the mail_error.
719 XtVaGetValues( _w, XmNitemCount, &num_msgs, NULL );
720 any_selected = XmListGetSelectedPos(_w, &position_list, &position_count);
721 if (!any_selected) return;
724 // Of the items in the list, potentially many could be selected
725 // for delete. And those selected for deleted need not be
726 // contiguous necessarily. Note the position of the item that
727 // appears first in the list. When the items are deleted, other
728 // items if any exist will shift up to take the places of the
729 // deleted items. You want to display the corresponding item that
730 // takes the "first position".
732 // Say, if you have messages A, B, C, D, Evisible in that order.
733 // You select B and C for delete.
734 // The first selected pos is 2.
735 // When B and C are deleted, D (and E...) move up.
736 // The new order is A, D, E...
737 // You want to display the second message which is D.
739 first_selected_pos = *position_list;
740 undel_dialog = parent()->get_undel_dialog();
742 for (i=0; i < position_count; i++ )
744 position_in_list = *(position_list + i);
745 a_del_msg_struct = get_message_struct(position_in_list);
746 _msgs->mark_for_delete(position_in_list - 1);
747 _deleted_messages->append(a_del_msg_struct);
749 undel_dialog->insertMsg(mail_error, a_del_msg_struct);
750 if (mail_error.isSet()) parent()->postErrorDialog(mail_error);
752 // See if there is a standalone view of the message.
753 // If there is, quit it.
755 tmpMH = a_del_msg_struct->message_handle;
756 tmpView = parent()->ifViewExists(tmpMH);
757 if (tmpView) tmpView->quit();
759 // Update the status of the message in the persistent store.
761 DtMail::Message * msg = _parent->mailbox()->getMessage(
763 a_del_msg_struct->message_handle);
764 msg->setFlag(error, DtMailMessageDeletePending);
766 // Check if message is new. If new and it is being deleted,
767 // reduce the new messages count.
769 cur_state = msg->flagIsSet(error, DtMailMessageNew);
771 if (cur_state == DTM_TRUE) num_new_messages--;
776 // Delete the items from the scrolling list
778 XmListDeletePositions(_w, position_list, position_count);
779 num_deleted_messages += position_count;
781 // XnListGetSelectedPos allocated memory for the array and
782 // wants us to free it. See documentation.
784 num_msgs = _msgs->length();
786 // Always remove the current attachments from the attachment area
787 // regardless of whether this is the last message in the list or not
788 // We call clearAttachArea if there are any other messages in the folder
789 // as parseAttachments will be called which will clean up the attachment
790 // status window. In the case of deleting the last message we must call
791 // removeCurrentAttachments which cleans up the status window as well.
794 parent()->get_editor()->attachArea()->removeCurrentAttachments();
796 parent()->get_editor()->attachArea()->clearAttachArea();
800 //No more messages left; clear the editor.
801 parent()->get_editor()->textEditor()->set_contents("", 1);
803 // NOTE: Need to obtain the attachArea and clear it up too
805 _displayed_item_position = 0; // Not displaying any.
806 _selected_item_position = 0; // Reset...
808 display_message_summary();
812 // If there are messages 1, 2 and 3 and 2 is selected and deleted
813 // first_selected_pos will be 2;
814 // after removing it, will need to display the 2nd message
815 // (which will now be 3)
816 // If 2 and 3 have been deleted, and there is no 2nd message,
817 // display the nth message (in our example, n = 1)
819 _selected_item_position = first_selected_pos;
820 if (_selected_item_position > num_msgs)
821 _selected_item_position = num_msgs;
823 // Having determined which message to display, confirm that it is
824 // within the bounds. Call display_message().
825 // FYI, display_message() sets the _displayed_item_position.
827 if ((_selected_item_position > 0) &&
828 (_selected_item_position <= num_msgs))
830 XmListSelectPos(_w, _selected_item_position, FALSE);
831 tmpMS = this->get_message_struct(_selected_item_position);
839 this->display_message(mail_error, tmpMS->message_handle);
840 if (mail_error.isSet()) parent()->postErrorDialog(mail_error);
846 if (position_count > 1)
849 * The following sentence means %d number of mail messages have
850 * been deleted from the mail folder. This is the plural form
851 * of the message that gets printed if more than one message
854 str = GETMSG(DT_catd, 3, 84, "%d messages deleted");
859 * The following sentence means %d number of mail messages have
860 * been deleted from the mail folder. This is the singular
861 * form of the message that gets printed if only one message
864 str = GETMSG(DT_catd, 3, 85, "%d message deleted");
866 status_message = new char[strlen(str) + 10];
867 sprintf(status_message, str, position_count);
868 parent()->message(status_message);
869 delete [] status_message;
872 updateListItems(-1, TRUE, NULL);
873 display_message_summary();
875 XtFree((char*) position_list);
890 // copySelected() will either copy or move the selected messages
891 // into the container called destname. If the delete_after_copy
892 // flag is set to TRUE, it is effectively a move; otherwise, it
893 // is a copy. If the container named by destname is a relative
894 // path, but it isn't prefixed with a '+', then a '+' will be
895 // prepended to the name so that the open() call will "do the
897 // If silent is TRUE then no status messages are displayed and the
898 // destname is not added to the copy/move cache.
901 MsgScrollingList::copySelected(
902 DtMailEnv &mail_error,
904 int delete_after_copy,
908 FORCE_SEGV_DECL(DtMail::MailBox, mbox);
909 FORCE_SEGV_DECL(int, position_list);
910 FORCE_SEGV_DECL(DtMail::MailBox, target);
911 DtMail::MailRc * mailrc;
912 DtMail::Session * d_session = theRoamApp.session()->session();
913 int position_count, position, i;
914 Boolean any_selected = FALSE;
915 DtMailMessageHandle msg;
916 char *status_message, *str;
921 any_selected = XmListGetSelectedPos(_w,
924 // If there aren't any selected messages, then there isn't
925 // anything for us to do.
928 display_no_message();
931 char * helpId = NULL;
932 DtMailGenDialog *dialog = _parent->genDialog();
934 dialog->setToErrorDialog(
935 GETMSG(DT_catd, 3, 50, "Mailer"),
936 GETMSG(DT_catd, 2, 16, "No message selected."));
937 dialog->post_and_return(helpId);
942 mbox = parent()->mailbox();
943 mailrc = mbox->session()->mailRc(mail_error);
946 // The following is an error message. "mailrc" is the name of the
947 // mail resource file. Translate as appropriate.
949 parent()->message(GETMSG(DT_catd, 2, 15,"Error - Unable to get mailrc."));
953 // If the first character of destname is alphanumeric, we can
954 // safely assume that it is a relative path, so we prepend a
956 if (isalnum(destname[0])) {
957 // Make sure we allocate enough for the name + '+' + null terminator.
958 newdestname = (char *) malloc(strlen(destname) + 2);
959 memset(newdestname, 0, strlen(destname) + 2);
960 sprintf(newdestname, "+%s", destname);
961 char *path = d_session->expandPath(mail_error, newdestname);
962 target = theRoamApp.session()->open(mail_error,
973 target = theRoamApp.session()->open(mail_error,
981 if (mail_error.isSet()) {
982 // if the error is DTME_AlreadyOpened, we don't care.
983 // go ahead to clear the error
985 // We couldn't open the container, so we want to post an
987 if ((DTMailError_t)mail_error == DTME_AlreadyOpened)
991 parent()->postErrorDialog(mail_error);
996 // Go through the selected messages and copy them to the
997 // specified container.
999 if ( position_count > 0 )
1000 parent()->busyCursor() ;
1002 for (i=0; i < position_count; i++) {
1003 position = *(position_list + i);
1004 msg = msgno(position);
1005 DtMail::Message * dtmsg = mbox->getMessage(mail_error, msg);
1006 if (mail_error.isSet()) {
1007 parent()->normalCursor() ;
1008 parent()->postErrorDialog(mail_error);
1009 theRoamApp.session()->close(mail_error, target);
1014 target->copyMessage(mail_error, dtmsg);
1015 if (mail_error.isSet()) {
1016 parent()->normalCursor() ;
1017 parent()->postErrorDialog(mail_error);
1018 theRoamApp.session()->close(mail_error, target);
1022 parent()->normalCursor() ;
1027 rmw = theRoamApp.session()->getRMW(target);
1029 if (rmw) rmw->checkForMail(mail_error);
1031 if (delete_after_copy) {
1036 // The following sentence means %d number of mail messages have
1037 // been moved to the %s mail folder. The %s is the name of a
1038 // mail folder. This is the plural form of the message that gets
1039 // printed if more than one message is moved.
1041 str = GETMSG(DT_catd, 3, 65, "%d messages moved to %s");
1044 // The following sentence means %d number of mail messages have
1045 // been moved to the %s mail folder. The %s is the name of a
1046 // mail folder. This is the singular form of the message that
1047 // gets printed if only one message is moved.
1049 str = GETMSG(DT_catd, 3, 66, "%d message moved to %s");
1055 // The following sentence means %d number of mail messages have been
1056 // copied to the %s mail folder. This is the plural form of the
1057 // message that gets printed if more than one message is copied.
1059 str = GETMSG(DT_catd, 3, 67, "%d messages copied to %s");
1062 // The following sentence means %d number of mail messages have been
1063 // copied to the %s mail folder. This is the singular form of the
1064 // message that gets printed if only one message is copied.
1066 str = GETMSG(DT_catd, 3, 68, "%d message copied to %s");
1070 newdestname = d_session->getRelativePath(mail_error, destname);
1071 status_message = new char[strlen(str) + strlen(newdestname) + 10];
1072 sprintf(status_message, str, i, newdestname);
1075 theRoamApp.globalAddToCachedContainerList(newdestname);
1076 parent()->message(status_message);
1081 theRoamApp.session()->close(mail_error, target);
1083 delete [] status_message;
1088 //-----------------------------------------------------------------------------
1089 // This method returns a list of the currently selected messages. This list
1090 // must be deleted by the calling method.
1091 //-----------------------------------------------------------------------------
1094 MsgScrollingList::selected()
1096 FORCE_SEGV_DECL(MsgStruct, a_msg_struct);
1097 FORCE_SEGV_DECL(int, position_list);
1098 int position_in_list, i;
1100 Boolean any_selected;
1102 // Find out first if any have been selected.
1103 // If i has been selected, how many?
1104 // We need the number selected so that we can allocate
1105 // space for that many mesasgeStructs to be returned.
1107 any_selected = XmListGetSelectedPos(_w,
1111 // If nothing selected, return
1113 if (!any_selected) return NULL;
1115 // Allocate memory for position_count number of messageStructs
1118 MsgHndArray *msgList = new MsgHndArray(position_count);
1120 for (i=0; i < position_count; i++ ) {
1121 position_in_list = *(position_list + i);
1122 a_msg_struct = get_message_struct(position_in_list);
1123 msgList->append(a_msg_struct);
1129 MsgScrollingList::show_with_attachments(DtMailMessageHandle msg_num)
1131 DtMailBoolean has_attachments = DTM_FALSE;
1132 DtMailEnv mail_error;
1133 DtMail::MailBox *mbox=parent()->mailbox();
1134 DtMail::Message *msg = mbox->getMessage(mail_error, msg_num);
1136 has_attachments = show_with_attachments(msg);
1137 return has_attachments;
1142 MsgScrollingList::show_with_attachments(DtMail::Message * msg)
1144 DtMail::BodyPart *bp;
1146 DtMailBoolean has_attachments = DTM_FALSE;
1147 DtMailBoolean is_multipart = DTM_FALSE;
1151 is_multipart = msg->flagIsSet(error, DtMailMessageMultipart);
1155 num_bodyParts = msg->getBodyCount(error);
1156 if (num_bodyParts > 1)
1159 bp = msg->getFirstBodyPart(error);
1160 bp->getContents(error, NULL, NULL, &type, NULL, NULL, NULL);
1165 attr = DtDtsDataTypeToAttributeValue(type, DtDTS_DA_IS_TEXT, NULL);
1166 if (attr && strcasecmp(attr, "true") != 0)
1167 has_attachments = DTM_TRUE;
1172 DtDtsFreeAttributeValue(attr);
1174 return has_attachments;
1179 MsgScrollingList::display_message(
1180 DtMailEnv &mail_error,
1181 DtMailMessageHandle msg_num
1185 DtMail::MailBox *mbox=parent()->mailbox();
1187 DtMailBoolean cur_state;
1191 // If there is a status message displayed, clear it first.
1192 parent()->clear_message();
1195 // We could have called display_msg from anywhere.
1196 // Need to calculate what is the position of that item
1197 // in the scrolling list, given the DtMailMessageHandle.
1198 // Determine the index at the _msgs array; increment by 1
1199 // since array begins at 0 and XmList begins at 1.
1201 item_index = _msgs->indexof(msg_num);
1202 if (item_index < 0) return;
1204 _displayed_item_position = item_index + 1;
1206 // Make sure the header is visible in the scrolling list.
1207 this->scroll_to_position(_displayed_item_position);
1209 // Retrieve the header text and insert it. We need to retrieve
1210 // message from the handle and inset the headers.
1213 DtMail::Message * msg = mbox->getMessage(mail_error, msg_num);
1214 DtMail::Envelope * env = msg->getEnvelope(mail_error);
1217 // There are multiple paths to this place:
1218 // 1) user has already read this message but is re-reading it;
1219 // 2) user has undeleted this message (implicitly, they have
1220 // read this message);
1221 // 3) user has not read this message yet.
1223 // For (1), we don't need to do anything fancy.
1224 // For (2), we have already reset the IsDeleted flag while
1226 // For (3), we need to reset the flag in store to indicate
1229 cur_state = msg->flagIsSet(mail_error, DtMailMessageNew);
1231 // If the message was previously new, we need to reset the list
1232 // item to remove the "N" on the item. We do this by reconstructing
1233 // the header line without the "N".
1235 if (cur_state == DTM_TRUE) {
1236 DtMailHeaderLine info;
1238 msg->resetFlag(mail_error, DtMailMessageNew);
1240 mbox->getMessageSummary(mail_error, msg_num, _header_info, info);
1243 ms = get_message_struct(_displayed_item_position);
1245 XmString complete_header;
1246 complete_header = formatHeader(
1249 show_with_attachments(msg),
1250 msg->flagIsSet(mail_error, DtMailMessageNew));
1252 mbox->clearMessageSummary(info);
1254 XmListReplaceItemsPos(_w, &complete_header,1, _displayed_item_position);
1255 XmStringFree(complete_header);
1257 // The default selection policy is extended_select.
1259 // User extend selects multiple items
1260 // The last item selected is a "N" message
1261 // We display the "N" message and have to replace it without
1263 // There is no way to replace with something else and select at
1265 // Therefre, we need to explicitly select the replacement.
1266 // The problem is: selecting the replacement deselects the other
1268 // To work around this, we switch temporarily to MULTIPLE_SELECT,
1269 // select the replacement, and switch back to extend_select.
1270 // This will select the replacement and *not* drop the
1271 // selection on the other selected items.
1272 // Say "Amen" to OSF for such convoluted thinking!
1275 XmNselectedItemCount, &num_selected,
1278 if (num_selected > 1) {
1279 // Change to MULTIPLE_SELECT.
1281 // Change back to EXTEND_SELECT
1283 XmNselectionPolicy, XmMULTIPLE_SELECT,
1284 XmNselectionMode, XmNORMAL_MODE,
1287 // When loading mail headers, we need to make sure that
1288 // the XmList resources are all set at the same time to
1289 // avoid painting multiple times. So here we collect
1291 if (_xtarg_collector && _xmstr_collector)
1293 XmString *items = _xmstr_collector->GetItems();
1295 // Keep a handle to the malloced string copy so that
1296 // we can free it after the XtSetValues
1298 = XmStringCopy (items[_displayed_item_position - 1]);
1300 _xtarg_collector->AddItemToList (XmNselectedItems,
1301 (XtArgVal) &_selected_items);
1302 _xtarg_collector->AddItemToList (
1303 XmNselectedItemCount, 1);
1304 _xtarg_collector->AddItemToList (
1305 XmNselectionPolicy, XmEXTENDED_SELECT);
1309 XmListSelectPos(_w, _displayed_item_position, FALSE);
1312 XmNselectionPolicy, XmEXTENDED_SELECT,
1313 XmNselectionMode, XmNORMAL_MODE,
1318 if (_xtarg_collector && _xmstr_collector)
1320 XmString *items = _xmstr_collector->GetItems();
1323 = XmStringCopy (items[_displayed_item_position - 1]);
1325 _xtarg_collector->AddItemToList (XmNselectedItems,
1326 (XtArgVal) &_selected_items);
1327 _xtarg_collector->AddItemToList (
1328 XmNselectedItemCount, 1);
1332 XmListSelectPos(_w, _displayed_item_position, FALSE);
1339 display_message_summary();
1341 // Display the message now.
1342 rmw_editor = parent()->get_editor()->textEditor();
1343 rmw_editor->disable_redisplay();
1344 rmw_editor->auto_show_cursor_off();
1345 rmw_editor->clear_contents();
1347 num_bodyParts = msg->getBodyCount(mail_error);
1349 char * status_string;
1350 DtMailBoolean firstBPHandled;
1352 // Turn on the busy Cursor
1354 parent()->busyCursor();
1356 if (parent()->fullHeader())
1358 rmw_editor->set_message(msg, &status_string, Editor::HF_FULL);
1361 rmw_editor->set_message(msg, &status_string, Editor::HF_ABBREV);
1363 if (status_string) parent()->message(status_string);
1364 if (mail_error.isSet()) {} // do something
1366 if ((num_bodyParts > 1) || (!firstBPHandled))
1368 // If the message has attachments, then let the attach pane
1369 // handle attachments but not the first bodyPart (which has
1370 // already been handled here).
1372 if (firstBPHandled) {
1374 // The first bodyPart has already been handled.
1375 // The others, beginning from the second, need to be parsed
1376 // and put into the attachPane.
1378 parent()->get_editor()->attachArea()->parseAttachments(
1385 // The first bodyPart was not handled.
1386 // It may not have been of type text.
1387 // The attachment pane needs to handle all the bodyParts
1388 // beginning with the first.
1390 parent()->get_editor()->attachArea()->parseAttachments(
1397 // Check for errors.
1398 // Manage the attach area to display attachments.
1399 if (mail_error.isSet()) {} // do something
1401 parent()->get_editor()->manageAttachArea();
1402 parent()->activate_default_attach_menu();
1406 parent()->deactivate_default_attach_menu();
1407 parent()->get_editor()->unmanageAttachArea();
1410 parent()->sync_work_area_size();
1411 rmw_editor->auto_show_cursor_restore();
1413 // Turn on text editor and manage attachPane
1414 rmw_editor->set_to_top();
1415 rmw_editor->enable_redisplay();
1417 parent()->normalCursor();
1421 MsgScrollingList::display_and_select_message(
1422 DtMailEnv &mail_error,
1423 DtMailMessageHandle msg_num
1428 // Need to calculate what is the position of that item
1429 // in the scrolling list, given the DtMailMessageHandle.
1430 // Determine the index at the _msgs array; increment by 1
1431 // since array begins at 0 and XmList begins at 1.
1435 item_index = _msgs->indexof(msg_num);
1436 if (item_index < 0) return;
1438 _displayed_item_position = item_index + 1;
1439 _selected_item_position = _displayed_item_position;
1441 // Select this message in the scrolling list and display
1444 // When loading mail headers, we need to make sure that
1445 // the XmList resources are all set at the same time to
1446 // avoid painting multiple times. So here we collect
1448 if (_xtarg_collector && _xmstr_collector)
1450 XmString *items = _xmstr_collector->GetItems();
1452 // Keep a handle to the malloced string copy so that
1453 // we can free it after the XtSetValues
1455 = XmStringCopy (items[_displayed_item_position - 1]);
1457 _xtarg_collector->AddItemToList (XmNselectedItems,
1458 (XtArgVal) &_selected_items);
1459 _xtarg_collector->AddItemToList (
1460 XmNselectedItemCount, 1);
1464 XmListSelectPos(_w, _displayed_item_position, FALSE);
1466 this->display_message(mail_error, msg_num);
1472 MsgScrollingList::select_all_and_display_last(
1477 register int item_pos;
1479 MsgHndArray * msgHandles = get_messages();
1483 if (this->get_num_messages() == 0) return;
1485 // If no message selected, return.
1487 if (this->get_selected_item() == 0) return;
1489 XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
1491 // We have to go to the end of the list and go backwards.
1492 // We display the last one, and select the rest.
1494 // A NULL terminated list.
1497 XmListDeselectAllItems(baseWidget());
1498 XtVaGetValues(baseWidget(), XmNitemCount, &num_items, NULL);
1500 for (item_pos = 1; item_pos < num_items; item_pos++) {
1501 XmListSelectPos(baseWidget(), item_pos, FALSE);
1504 // Invoke the selection callback on the last item.
1505 display_and_select_message(error,
1506 msgHandles->at(item_pos-1)->message_handle);
1507 XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
1508 XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
1512 MsgScrollingList::select_all_and_display_last(
1514 DtMailMessageHandle *handleArray,
1515 unsigned int elements
1518 register int handleOffset = 0;
1519 register int item_pos;
1523 XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
1525 // We have to go to the end of the list and go backwards.
1526 // We display the last one, and select the rest.
1528 // A NULL terminated list.
1530 handleOffset = elements;
1532 XmListDeselectAllItems(baseWidget());
1534 while (--handleOffset >= 0 && error.isNotSet()) {
1536 item_pos = _msgs->indexof(handleArray[handleOffset]); // Get position
1542 // Select this message in the scrolling list and IF
1543 // it is the last message, display it.
1545 if (handleOffset == elements - 1) {
1546 display_and_select_message(error, handleArray[handleOffset]);
1548 XmListSelectPos(_w, item_pos + 1, FALSE);
1551 XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
1552 XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
1558 MsgScrollingList::display_no_message()
1562 * No mail message has been selected by the user.
1565 parent()->message(GETMSG(DT_catd, 2, 16, "No message selected."));
1566 _displayed_item_position = 0;
1567 _selected_item_position = 0;
1569 parent()->get_editor()->textEditor()->clear_contents();
1570 parent()->get_editor()->attachArea()->removeCurrentAttachments();
1573 // In need of a simple but useful optimization here.
1574 // By the time we get to this method, we already have displayed
1575 // the selected message in the lower section of the combo window.
1576 // Instead of having to parse the messageHandle and construct the
1577 // text buffer to display, we can just get the parsed-and-formatted
1578 // text from the lower section of the combo window and stick it into
1579 // the VMD's editor.
1580 // This will save us the effort of parsing and formatting a message
1584 MsgScrollingList::viewInSeparateWindow(DtMailEnv &mail_error)
1586 FORCE_SEGV_DECL(const char, title);
1587 FORCE_SEGV_DECL(char, header_txt);
1588 FORCE_SEGV_DECL(MsgStruct, tmpMS);
1589 DtMailMessageHandle msgHandle;
1590 DtMail::MailBox *mbox=parent()->mailbox();
1591 ViewMsgDialog *newview;
1596 DtMail::Session *d_session = theRoamApp.session()->session();
1597 DtMail::MailRc *mail_rc = d_session->mailRc(error);
1598 const char *value = NULL;
1600 // If no message selected, return.
1601 if (this->get_selected_item() <= 0) return;
1603 // Double-check. If none selected, return
1604 MsgHndArray *selected_msgs = this->selected();
1605 if (!selected_msgs) return;
1607 for (int a_msg_num = 0; a_msg_num < selected_msgs->length(); a_msg_num++)
1609 tmpMS = selected_msgs->at(a_msg_num);
1610 if (tmpMS == NULL) return;
1612 msgHandle = tmpMS->message_handle;
1613 newview = parent()->ifViewExists(msgHandle);
1615 if (newview != NULL)
1618 * The current mail message selected is already displayed in a
1619 * separate window. Therefore this 'separate' window will be
1620 * raised in front of existing windows so the user can see it.
1625 "View already exists. Raising it."));
1626 newview->displayInCurrentWorkspace();
1630 // No view exists. Display it. For feedback, set busyCursor().
1631 parent()->busyCursor();
1632 mail_rc->getValue(error, "separatemessageviewer", &value);
1634 newview = new ViewMsgDialog(parent(), xmDialogShellWidgetClass);
1637 if (value) free((void*) value);
1638 newview = new ViewMsgDialog(parent(), topLevelShellWidgetClass);
1640 parent()->registerDialog(newview);
1641 newview->initialize();
1642 vmd_editor = newview->get_editor()->textEditor();
1644 // Set the VMD's msgHandle. This unique handle is what is
1645 // used to raise the VMD when the same message is double
1646 // clicked on later.
1647 newview->msgno(msgHandle);
1650 DtMail::Message * msg = mbox->getMessage(error, msgHandle);
1651 DtMail::Envelope * env = msg->getEnvelope(error);
1653 DtMailValueSeq title_value;
1654 env->getHeader(error, DtMailMessageSubject, DTM_TRUE, title_value);
1656 title = "NO SUBJECT!";
1658 title = *(title_value[0]);
1660 newview->title((char *)title);
1661 newview->auto_show_cursor_off();
1663 // Unset the error produced when obtaining title...
1665 vmd_editor->disable_redisplay();
1667 char * status_string;
1668 DtMailBoolean firstBPHandled;
1670 if (parent()->fullHeader())
1671 firstBPHandled = newview->get_editor()->textEditor()->set_message(
1672 msg, &status_string, Editor::HF_FULL);
1674 firstBPHandled = newview->get_editor()->textEditor()->set_message(
1675 msg, &status_string, Editor::HF_ABBREV);
1677 // If the message has attachments, then let the attach pane
1678 // handle attachments but not the first bodyPart (which has
1679 // already been handled here).
1680 num_bodyParts = msg->getBodyCount(mail_error);
1681 if (mail_error.isSet()) {} // do something
1683 if ((num_bodyParts == 1) && firstBPHandled)
1685 newview->get_editor()->unmanageAttachArea();
1686 newview->deactivate_default_attach_menu();
1688 else if ((num_bodyParts > 1) || (!firstBPHandled))
1690 // If the message has attachments, then let the attach pane
1691 // handle attachments but not the first bodyPart (which has
1692 // already been handled here).
1696 // The first bodyPart has already been handled.
1697 // The others, beginning from the second, need to be parsed
1698 // and put into the attachPane.
1699 newview->get_editor()->attachArea()->parseAttachments(
1700 mail_error, msg, TRUE, 2);
1704 // The first bodyPart was not handled.
1705 // It may not have been of type text.
1706 // The attachment pane needs to handle all the bodyParts
1707 // beginning with the first.
1708 newview->get_editor()->attachArea()->parseAttachments(
1709 mail_error, msg, TRUE, 1);
1712 // Check for errors.
1713 // Manage the attach area to display attachments.
1714 if (mail_error.isSet()) {} // do something
1715 newview->get_editor()->manageAttachArea();
1716 newview->activate_default_attach_menu();
1719 newview->set_to_top();
1720 newview->auto_show_cursor_restore();
1721 vmd_editor->enable_redisplay();
1725 parent()->normalCursor();
1728 // defaultAction() gets called *after* extendedSelectionCallback() is
1730 // Display the message in extendedSelectionCallback().
1731 // viewInSeparateWindow() in defaultAction().
1734 MsgScrollingList::defaultAction(Widget, XtPointer, XmListCallbackStruct *cbs)
1736 FORCE_SEGV_DECL(MsgStruct, tmpMS);
1737 DtMailEnv mail_error;
1739 // Initialize the mail_error.
1741 _selected_item_position = cbs->item_position;
1743 tmpMS = get_message_struct(_selected_item_position);
1748 this->viewInSeparateWindow(mail_error);
1749 if (mail_error.isSet()) {} // Post dialog indicating error
1755 MsgScrollingList::extendedSelectionCallback(
1757 XtPointer clientData,
1758 XmListCallbackStruct *cbs
1761 int last_clicked_on_pos;
1762 int above_selected_pos;
1763 int tmp_selected_pos;
1764 int i, num_selected;
1765 DtMailEnv mail_error;
1767 // Initialize the mail_error.
1771 Boolean IS_SELECTION = FALSE;
1772 Boolean SELECTION_MADE = FALSE;
1774 MsgScrollingList *obj=(MsgScrollingList *) clientData;
1776 // If all items have been deselected
1777 if (cbs->selected_item_count == 0) {
1778 obj->extended_selection(mail_error, 0);
1779 if (mail_error.isSet()) {
1782 SELECTION_MADE = TRUE;
1786 last_clicked_on_pos = cbs->item_position;
1788 // Check to see if this was a selection or deselection
1789 // We do that by seeing if last_clicked_on_pos is in the
1790 // selected_item_positions array. If it is, then it is a
1791 // selection; if its not there, then its a deselection.
1793 num_selected = cbs->selected_item_count;
1795 for (i = 0; i < num_selected; i++) {
1796 if (last_clicked_on_pos == cbs->selected_item_positions[i]) {
1797 // Yes, it was a selection
1799 IS_SELECTION = TRUE;
1800 obj->extended_selection(mail_error, last_clicked_on_pos);
1801 if (mail_error.isSet()) {
1805 SELECTION_MADE = TRUE;
1809 // If it was not a selection, then we need to find the selected
1810 // item nearest to the deselected item. We have a choice there -
1811 // we can find the nearest one above, or the nearest one below
1812 // the deselected item. Let's find the nearest one above.
1813 // We do that by cruising through the selected_item_positions array.
1814 // Motif arranges the array in ascending order, no matter what order
1815 // you selected the items! If you deselect an item, the items on
1816 // either side of it are the ones above and below it. We use that
1817 // ordering to now select and display the one above it.
1820 if (!IS_SELECTION) {
1822 // If the first selected item is below the deselected item,
1823 // it follows that all selected items are below the deselected
1824 // item. Display the first selected item and be done.
1826 if (cbs->selected_item_positions[0] > last_clicked_on_pos) {
1827 obj->extended_selection(mail_error,
1828 cbs->selected_item_positions[0]);
1829 if (mail_error.isSet()) {
1832 SELECTION_MADE = TRUE;
1834 // If the last selected item is above the deselected item,
1835 // it follows that all selected items are above the deselected
1836 // item. Display the last selected item and be done.
1837 else if (cbs->selected_item_positions[num_selected - 1] <
1838 last_clicked_on_pos) {
1839 obj->extended_selection(mail_error,
1840 cbs->selected_item_positions[num_selected - 1]);
1841 if (mail_error.isSet()) {
1844 SELECTION_MADE = TRUE;
1846 // Otherwise, the deselected item must lie in between other
1847 // selected items. We choose to find the closest selected
1848 // item above the deselected item.
1850 // There are selected items that are above the deselected
1852 // Need to find the one that is both above the deselected
1853 // item and closest to it.
1854 // Iterate until you find the nearest above; select it and
1855 // drop out of loop when after selection.
1857 for (i = 0; i < (num_selected - 1) && !SELECTION_MADE; i++) {
1858 above_selected_pos = cbs->selected_item_positions[i];
1859 tmp_selected_pos = cbs->selected_item_positions[i + 1];
1860 if (tmp_selected_pos > last_clicked_on_pos) {
1861 obj->extended_selection(mail_error,
1862 above_selected_pos);
1863 if (mail_error.isSet()) {
1866 SELECTION_MADE = TRUE;
1876 MsgScrollingList::extended_selection(
1877 DtMailEnv &mail_error,
1881 FORCE_SEGV_DECL(MsgStruct, tmpMS);
1883 // Disable the SaveAttachments menu item first.
1884 // It gets enabled when an attachment is selected.
1886 _parent->all_attachments_deselected();
1888 if (position == 0) { // all items deselected.
1889 _parent->hideAttachArea();
1890 this->display_no_message();
1891 _selection_on = FALSE;
1892 _parent->deactivate_default_message_menu();
1896 // if there were no selected item(s) before and now we
1897 // have one/some, we need to turn on the message menu at
1898 // the parent level.
1900 if (!_selection_on) {
1901 _selection_on = TRUE;
1902 _parent->activate_default_message_menu();
1905 // If selected message is the displayed message, return.
1906 // No need to redisplay the same message.
1908 if (position == _selected_item_position) return;
1910 // Retrieve message...
1911 // Display the selected message...
1912 _selected_item_position = position;
1914 tmpMS = get_message_struct(_selected_item_position);
1915 if (tmpMS == NULL) {
1919 display_message_summary();
1920 this->display_message(mail_error, tmpMS->message_handle);
1921 if (mail_error.isSet()) {
1929 MsgScrollingList::get_selected_item()
1931 return(_selected_item_position);
1935 MsgScrollingList::get_displayed_item()
1937 return(_displayed_item_position);
1941 MsgScrollingList::scroll_to_bottom()
1943 XmListSetBottomPos( this->baseWidget() , 0 );
1946 // Scroll the list so that the item at position is in the
1947 // middle of the scrolling list. If the number of items
1948 // that the scrolling list can display at one time is greater
1949 // than the total number of items, then display them all.
1950 // If the item at position is close to the bottom of the list
1951 // make sure we make as many items visible as possible.
1953 // If the item at position is already visible, then don't
1957 MsgScrollingList::scroll_to_position(
1961 int top, visible, total;
1964 // Determine the position of the header that we want to select.
1965 // If the XtArgCollector exists, then add
1966 // the resources to the XtArgCollector so that we can prevent
1967 // multiple redisplays in the XmList widget.
1968 if (_xmstr_collector)
1971 XmNtopItemPosition, &top,
1972 XmNvisibleItemCount, &visible,
1974 total = _xmstr_collector->GetNumItems();
1979 XmNtopItemPosition, &top,
1980 XmNvisibleItemCount, &visible,
1981 XmNitemCount, &total,
1985 if (( position < top ) || ( position >= top+visible )) {
1987 if ((total <= visible) || (position <= visible/2)) {
1988 // If we can display them all, make the first item appear
1989 // at the top of the list.
1991 } else if (position > (total-(visible/2))) {
1992 top_pos = total - visible + 1;
1994 top_pos = position - visible/2 + 1;
1997 // Determine the position of the header that we want to select.
1998 // If the XtArgCollector exists, then add
1999 // the resources to the XtArgCollector so that we can prevent
2000 // multiple redisplays in the XmList widget.
2001 if (_xtarg_collector)
2002 _xtarg_collector->AddItemToList (
2003 XmNtopItemPosition, top_pos);
2005 XmListSetPos (_w, top_pos);
2010 MsgScrollingList::undelete_messages(MsgHndArray *tmpMHlist)
2012 FORCE_SEGV_DECL(MsgStruct, tmpMS);
2013 FORCE_SEGV_DECL(XmString, deleted_headers);
2014 int i, num_entries, entry_position, del_pos;
2015 int whichToSelectDisplay = 0;
2016 DtMail::MailBox *mbox=parent()->mailbox();
2017 DtMail::Message * tmpMsg;
2018 DtMailEnv mail_error;
2019 DtMailMessageHandle tmpMH;
2021 XmString read_status, new_status;
2023 XmString complete_header; // read status + glyph + header_text.
2027 * In a mailer container window's message scrolling list, a "N" appears
2028 * to the left of a mail message header indicating that the mail message
2029 * is "new" (just arrived and not yet viewed by the user).
2030 * There is only space to display 1 character. If "N" needs to be translated,
2031 * please make sure the translation is only 1 character.
2034 // gregl - new_status and read_status are not used in this function.
2035 // either comment them out (like I'm doing) or free them.
2037 new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 112, "N"));
2038 read_status = XmStringCreateLocalized(" ");
2041 // Initialize the mail_error.
2043 num_entries = tmpMHlist->length();
2044 if (num_entries == 0) return;
2046 // Deselect all items currently selected.
2047 // display_and_select_message() will select, highlight
2048 // and display the last "undeleted" message.
2050 XmListDeselectAllItems(_w);
2052 XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2054 for (i = 0; i < num_entries; i++)
2056 DtMailHeaderLine info;
2058 tmpMS = tmpMHlist->at(i);
2059 tmpMS->is_deleted = FALSE;
2061 // Reset the flag of the message in message store so that the
2062 // message will not be expunged when the folder is quit.
2065 tmpMsg = mbox->getMessage(mail_error, tmpMS->message_handle);
2066 tmpMsg->resetFlag(mail_error, DtMailMessageDeletePending);
2067 tmpMH = tmpMS->message_handle;
2069 // Remove chosen item from list of deleted messages;
2070 // insert it back into _msgs at the right place (which is
2071 // determined by session_number of retrieved MsgStruct).
2072 // Insert back into scrolling list for visual display
2073 // at the position session_number.
2075 entry_position = _msgs->insert(tmpMS);
2077 // Increment by one, because the index into the scrolling
2078 // list is always one greater than the index into the
2079 // message handle array that we got the index from.
2081 entry_position = entry_position + 1;
2083 // Maintain the assumption that the item at entry_position
2084 // is the selected item
2086 _selected_item_position = entry_position;
2088 mbox->getMessageSummary(mail_error, tmpMS->message_handle,
2089 _header_info, info);
2090 DtMail::Message * msg = mbox->getMessage(mail_error, tmpMH);
2091 complete_header = formatHeader(
2094 show_with_attachments(msg),
2095 msg->flagIsSet(mail_error, DtMailMessageNew));
2097 mbox->clearMessageSummary(info);
2099 if (msg->flagIsSet(mail_error, DtMailMessageNew) == DTM_TRUE)
2102 XmListAddItem(_w, complete_header, entry_position);
2103 XmListSelectItem(_w, complete_header, FALSE);
2104 XmStringFree(complete_header);
2106 // Get position of undeleted message structure in _deleted_messages
2107 // and remove the entry from _deleted_messages
2109 del_pos = _deleted_messages->indexof(tmpMS);
2110 _deleted_messages->remove_entry(del_pos);
2113 // Display this message and select it.
2115 this->display_message(mail_error, tmpMS->message_handle);
2116 XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
2117 XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
2119 if (mail_error.isSet()) return;
2121 num_deleted_messages -= num_entries;
2123 MsgHndArray *selected_messages = selected();
2124 updateListItems(-1, TRUE, selected_messages);
2125 delete selected_messages;
2127 display_message_summary();
2131 MsgScrollingList::undelete_last_deleted()
2133 FORCE_SEGV_DECL(MsgStruct, tmpMS);
2136 DtMail::MailBox *mbox=parent()->mailbox();
2137 UndelFromListDialog *undel_dialog;
2138 DtMailEnv mail_error;
2139 DtMailHeaderLine info;
2140 DtMail::Message * tmpMsg;
2141 DtMailMessageHandle tmpMH;
2143 XmString read_status, new_status;
2145 XmString complete_header; // read status + glyph + header_text.
2149 * In a mailer container window's message scrolling list, a "N" appears
2150 * to the left of a mail message header indicating that the mail message
2151 * is "new" (just arrived and not yet viewed by the user).
2152 * There is only space to display 1 character. If "N" needs to be translated,
2153 * please make sure the translation is only 1 character.
2155 new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 113, "N"));
2156 read_status = XmStringCreateLocalized(" ");
2160 if (num_deleted_messages == 0) return;
2162 // Initialize the mail_error.
2166 // Delete the message from the Deleted Messages Dialog.
2167 undel_dialog = parent()->get_undel_dialog();
2169 undel_dialog->undelLast();
2171 // Restore the message in RoamMenuWindow:MessageScrollingList.
2172 len = _deleted_messages->length();
2174 tmpMS = _deleted_messages->at(len - 1);
2175 tmpMS->is_deleted = FALSE;
2177 // Reset the flag of the message in message store so that the
2178 // message will not be expunged when the folder is quit.
2181 tmpMsg = mbox->getMessage(mail_error, tmpMS->message_handle);
2182 tmpMsg->resetFlag(mail_error, DtMailMessageDeletePending);
2183 tmpMH = tmpMS->message_handle;
2185 // Remove chosen item from list of deleted messages;
2186 // insert it back into _msgs at the right place (which is
2187 // determined by session_number of retrieved MsgStruct).
2188 // Insert back into scrolling list for visual display
2189 // at the position session_number.
2191 entry_position = _msgs->insert(tmpMS);
2193 // Increment by one, because the index into the scrolling
2194 // list is always greater than the index into the
2195 // message handle array that we got the index from.
2197 entry_position = entry_position + 1;
2199 mbox->getMessageSummary(mail_error, tmpMS->message_handle,
2200 _header_info, info);
2201 DtMail::Message * msg = mbox->getMessage(mail_error, tmpMH);
2203 complete_header = formatHeader(
2206 show_with_attachments(msg),
2207 msg->flagIsSet(mail_error, DtMailMessageNew));
2209 mbox->clearMessageSummary(info);
2211 if (msg->flagIsSet(mail_error, DtMailMessageNew) == DTM_TRUE) {
2215 _deleted_messages->remove_entry(len - 1);
2216 num_deleted_messages--;
2217 XmListAddItemUnselected(_w, complete_header, entry_position);
2218 XmStringFree(complete_header);
2221 // If the undeleted message is before the currently viewed message,
2222 // then need to readjust our numbers by adding one to them -- there
2223 // is one more message above the currently-viewed message.
2224 // Don't need to do anything if the undeleted message is after the
2225 // currently-viewed message.
2228 // Deselect all items currently selected.
2229 // display_and_select_message() will select, highlight
2230 // and display the last "undeleted" message.
2232 XmListDeselectAllItems(_w);
2234 _selected_item_position = entry_position;
2235 this->display_and_select_message(mail_error, tmpMS->message_handle);
2236 if (mail_error.isSet()) return;
2238 updateListItems(-1, TRUE, NULL);
2239 display_message_summary();
2243 MsgScrollingList::senderIsToHeaderWhenMailFromMe(void)
2245 if (_header_info.number_of_names == 5)
2252 MsgScrollingList::checkDisplayProp(void)
2254 DtMail::MailBox * mbox;
2255 DtMail::MailRc * mailrc;
2256 DtMailEnv mail_error;
2257 Boolean state_changed = FALSE;
2259 mbox = parent()->mailbox();
2260 mailrc = mbox->session()->mailRc(mail_error);
2262 const char * value = NULL;
2263 mailrc->getValue(mail_error, "showto", &value);
2264 if (mail_error.isNotSet()) {
2265 // showto is set...if number_of_names is 4 then they
2266 // just applied props and the showto value
2267 if (_header_info.number_of_names == 4) {
2268 _header_info.number_of_names = 5;
2269 state_changed = TRUE;
2272 else if (_header_info.number_of_names == 5) {
2273 // They just applied props and changed the showto value
2274 _header_info.number_of_names = 4;
2275 state_changed = TRUE;
2278 free((void*) value);
2280 // Here we need to adjust the header labels to maintain them left
2281 // justified. It looks better than centered. We only whant to change
2282 // the labels if message numbering has been enabled. Otherwise we will
2283 // leave them alone. Note: The _numbered stores the previous state for
2284 // message numbering. Check the error return when getting the value uses
2285 // reverse logic. ugly...
2286 DtMailBoolean use_msg_numbers;
2290 mailrc->getValue(mail_error, "showmsgnum", &value);
2292 use_msg_numbers = (mail_error.isNotSet()) ? DTM_TRUE : DTM_FALSE;
2293 if (NULL != value) free((void*) value);
2295 if (use_msg_numbers != _numbered)
2297 _numbered = use_msg_numbers;
2298 layoutLabels(_sender_lbl, _subject_lbl, _date_lbl, _size_lbl);
2300 else if (!state_changed) return;
2302 // We have to build two lists from the current normal and deleted
2303 // lists. These will contain the new header lines.
2305 DtMailHeaderLine info;
2306 XmString * normal_list = new XmString[_msgs->length()];
2308 for (int m = 0; m < _msgs->length(); m++) {
2309 MsgStruct * ms = _msgs->at(m);
2311 DtMail::Message * msg =
2312 mbox->getMessage(mail_error, ms->message_handle);
2313 mbox->getMessageSummary(
2314 mail_error, ms->message_handle,
2315 _header_info, info);
2316 normal_list[m] = formatHeader(
2319 show_with_attachments(msg),
2320 msg->flagIsSet(mail_error, DtMailMessageNew));
2321 mbox->clearMessageSummary(info);
2324 XmListReplaceItemsPos(_w, normal_list, _msgs->length(), 1);
2325 for (int fr = 0; fr < _msgs->length(); fr++) {
2326 XmStringFree(normal_list[fr]);
2330 UndelFromListDialog * del_dialog = _parent->get_undel_dialog();
2332 XmString * del_list = new XmString[_deleted_messages->length()];
2334 for (int m2 = 0; m2 < _deleted_messages->length(); m2++) {
2335 MsgStruct * ms = _deleted_messages->at(m2);
2337 DtMail::Message * msg =
2338 mbox->getMessage(mail_error, ms->message_handle);
2339 mbox->getMessageSummary(
2340 mail_error, ms->message_handle,
2341 _header_info, info);
2342 del_list[m2] = formatHeader(
2345 show_with_attachments(msg),
2346 msg->flagIsSet(mail_error, DtMailMessageNew));
2347 mbox->clearMessageSummary(info);
2350 del_dialog->replaceItems(del_list, _deleted_messages->length());
2351 for (int fr2 = 0; fr2 < _deleted_messages->length(); fr2++) {
2352 XmStringFree(del_list[fr2]);
2359 // Update the scrolling list. Current is the index of the message
2360 // to position the scrolling list to. -1 to keep it as is.
2363 MsgScrollingList::updateListItems(int current,
2364 Boolean renumber_only,
2365 MsgHndArray *selected_messages)
2367 DtMail::MailBox * mbox = NULL;
2368 DtMail::MailRc * mailrc;
2369 DtMailEnv mail_error;
2370 const char * value = NULL;
2373 mbox = parent()->mailbox();
2374 mailrc = mbox->session()->mailRc(mail_error);
2376 if (current < 0) current = _displayed_item_position;
2379 mailrc->getValue(mail_error, "showmsgnum", &value);
2380 if (mail_error.isSet() && renumber_only) return;
2382 if (selected_messages)
2383 XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL);
2386 // We need to build a new list of strings to display
2387 // in the scrolling list. Initialize that now.
2389 DtMailHeaderLine info;
2393 nmsgs = _msgs->length();
2394 newList = new XmString[nmsgs];
2395 memset (newList, '0', nmsgs * sizeof (XmString *));
2397 // Loop through _msgs and create new strings to display in the
2398 // scrolling list. This is inefficient and dominates the time
2399 // spent in sort. It may be worth while finding a way to just
2400 // rearrange the existing strings.
2401 for (int m = 0; m < nmsgs; m++)
2403 DtMail::Message * msg = mbox->getMessage(mail_error,
2404 _msgs->at(m)->message_handle);
2406 if (mail_error.isSet())
2407 fprintf(stderr, "dtmail: getMessage: Couldn't get message #%d\n", m);
2409 mbox->getMessageSummary(
2410 mail_error, _msgs->at(m)->message_handle,
2411 _header_info, info);
2413 if (mail_error.isSet())
2415 "dtmail: getMessageSummary: Couldn't get summary for msg # %d\n", m);
2417 if ((msg == NULL) || (mbox == NULL))
2424 ms = get_message_struct(m + 1);
2429 show_with_attachments(msg),
2430 msg->flagIsSet(mail_error, DtMailMessageNew));
2433 // Free the space allocated for info
2434 // delete []info.header_values;
2435 mbox->clearMessageSummary(info);
2438 XmListReplaceItemsPos(_w, newList, session_message_number, 1);
2439 for (int fr = 0; fr < nmsgs; fr++)
2440 XmStringFree(newList[fr]);
2442 // Update current message
2443 _selected_item_position = current;
2444 _displayed_item_position = current;
2445 scroll_to_position(_displayed_item_position);
2447 if (selected_messages)
2449 XmListDeselectAllItems(_w);
2450 for (int m=0, nselected=selected_messages->length(); m<nmsgs; m++)
2452 MsgStruct *ms = get_message_struct(m+1);
2453 for (int s=0; s<nselected; s++)
2455 MsgStruct *sms = selected_messages->at(s);
2457 XmListSelectPos(_w, m+1, FALSE);
2460 XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
2463 XmListSelectPos(_w, _displayed_item_position, FALSE);
2465 XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL);
2466 XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL);
2471 MsgScrollingList::formatHeader(DtMailHeaderLine & info,
2473 DtMailBoolean has_attachments,
2474 DtMailBoolean new_msg)
2476 char *buf = new char[BUFSIZ];
2477 memset(buf, 0, BUFSIZ);
2478 const char *from=NULL;
2481 char contentStr[20];
2482 char *date = new char[BUFSIZ];
2483 int msg_num = sess_num + 1;
2484 static XmString attachment_glyph = NULL;
2485 static XmString no_attachment_glyph = NULL;
2486 static XmString new_status = NULL;
2487 static XmString read_status = NULL;
2488 static unsigned char attach_symbol[16];
2489 Boolean showto = FALSE;
2491 if (!attachment_glyph) {
2492 attach_symbol[0] = 168;
2493 attach_symbol[1] = 0;
2494 attachment_glyph = XmStringCreate((char *)attach_symbol, "attach");
2495 no_attachment_glyph = XmStringCreateLocalized(" ");
2496 new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 114, "N"));
2497 read_status = XmStringCreateLocalized(" ");
2500 // strip out the Name of sender and retain only the address.
2501 //Later, we will have separate entries for name and address
2502 //and this stripping will not need to be done.
2504 // IMAP is incapable of handling a message header that begins
2505 // with From (space) (NOTE: not a From:).
2506 // Thus, if a message has only a From but does not have a From:
2507 // or a Reply-To: or Received-From:, IMAP doesn't tell us who
2508 // it is from. In such cases, we set it to "???".
2510 DtMailAddressSeq * addr_seq = NULL;
2511 DtMailValueAddress * addr = NULL;
2513 if (info.header_values[0].length() != 0) {
2514 addr_seq = ((info.header_values[0])[0])->toAddress();
2515 addr = (*addr_seq)[0];
2516 if (_header_info.number_of_names == 5 && addr
2517 && addr->dtm_address && info.header_values[4].length() != 0) {
2518 // Check if mail is from me
2521 GetPasswordEntry(pw);
2522 if ((ptr = strchr(addr->dtm_address, '@')) != NULL) {
2523 if (strncmp(pw.pw_name, addr->dtm_address,
2524 ptr-addr->dtm_address) == 0) {
2525 from = *((info.header_values[4])[0]);
2530 if (strcmp(pw.pw_name, addr->dtm_address) == 0) {
2531 from = *((info.header_values[4])[0]);
2536 if (addr && addr->dtm_person)
2537 from = addr->dtm_person;
2538 else if (addr && addr->dtm_address)
2539 from = addr->dtm_address;
2546 // If the Subject is nil
2548 if (info.header_values[3].length() == 0) {
2549 subject = new char[1];
2552 // Get the BE store of header. It may contain newlines or
2553 // tab chars which can munge the scrolling list's display!
2555 const char *real_subj_header = *((info.header_values[3])[0]);
2560 // Check if BE store contains the funky chars.
2562 for (fc = 0, subj_len = strlen(real_subj_header),
2563 tmp_subj = (char *)real_subj_header;
2564 fc < subj_len; fc++, tmp_subj++) {
2574 subject = new char[fc+1];
2575 strncpy((char *)subject, real_subj_header, fc);
2580 // Skip the first (beginning) space in from; search for the next
2584 const char *dateformat;
2585 if (info.header_values[1].length() > 0)
2587 DtMailValueDate ds = ((info.header_values[1])[0])->toDate();
2589 if (ds.dtm_date && ds.dtm_tz_offset_secs)
2591 #define USE_YEAR_FORMAT_SECONDS (60 * 60 * 24 * 180)
2595 SafeLocaltime(&ds.dtm_date, tm_struct);
2597 // Refer to strftime man page for explanation of the date format.
2599 if (USE_YEAR_FORMAT_SECONDS < now - ds.dtm_date)
2600 dateformat = GETMSG(DT_catd, 1, 259, "%a %b %d %Y");
2605 dateformat = GETMSG(DT_catd, 1, 260, "%a %b %d %k:%M");
2607 dateformat = GETMSG(DT_catd, 1, 261, "%a %b %d %H:%M");
2611 SafeStrftime(date, BUFSIZ, dateformat, &tm_struct);
2614 // Couldn't get Date string from Message. Make it empty.
2615 sprintf(date, "%s", " ");
2620 memset(&epoch, 0, sizeof(tm));
2622 /* Refer to strftime man page for explanation of the date format. */
2623 dateformat = GETMSG(DT_catd, 1, 259, "%a %b %d %Y");
2624 SafeStrftime(date, BUFSIZ, dateformat, &epoch);
2627 if (info.header_values[2].length() > 0) {
2628 contentLength = (int) strtol(*((info.header_values[2])[0]), NULL, 10);
2634 if (contentLength < 1000) {
2635 sprintf(contentStr, "%d", contentLength);
2637 else if (contentLength < 1000000) {
2638 sprintf(contentStr, "%dK", contentLength / 1000);
2641 sprintf(contentStr, "%dM", contentLength / 1000000);
2644 // If we are to print the message_number in the header_list,
2645 // use msg_num as the first element in the sprintf.
2646 // Introduce a %d at the beginning though.
2648 DtMail::MailBox * mbox;
2649 DtMail::MailRc * mailrc;
2650 DtMailEnv mail_error;
2652 mbox = parent()->mailbox();
2653 mailrc = mbox->session()->mailRc(mail_error);
2655 const char * value = NULL;
2656 mailrc->getValue(mail_error, "showmsgnum", &value);
2657 if (mail_error.isSet()) {
2658 // No message numbers ... keep usual "35" col. "Subject".
2660 sprintf(buf, " To %-15.15s %-35.35s %-17.17s %-5.5s",
2666 sprintf(buf, " %-18.18s %-35.35s %-17.17s %-5.5s",
2673 // To keep 80 column format use 5 less columns of
2674 // subject , when msg numbers are on.
2676 sprintf(buf, " To %-15.15s %-30.30s %-17.17s %-5.5s",
2682 sprintf(buf, " %-18.18s %-30.30s %-17.17s %-5.5s",
2689 free((void*) value);
2691 XmString header_text = XmStringCreateLocalized(buf);
2692 XmString item, item2, complete_header;
2694 if (has_attachments == DTM_TRUE) {
2695 item = XmStringConcat(attachment_glyph, header_text);
2696 XmStringFree(header_text);
2699 item = XmStringConcat(no_attachment_glyph, header_text);
2700 XmStringFree(header_text);
2703 if (new_msg == DTM_FALSE) {
2704 item2 = XmStringConcat(read_status, item);
2708 item2 = XmStringConcat(new_status, item);
2713 mailrc->getValue(mail_error, "showmsgnum", &value);
2714 if (mail_error.isSet()) {
2715 // complete_header = XmStringCopy(item2);
2716 complete_header = item2;
2717 _numbered = DTM_FALSE;
2723 free((void*) value);
2725 mailrc->getValue(mail_error, "nerdmode", &value);
2726 if (mail_error.isSet()) {
2727 sprintf(num_buf, "%4d ", msg_num);
2730 sprintf(num_buf, "%4x ", msg_num - 1);
2733 XmString num_str = XmStringCreateLocalized(num_buf);
2734 complete_header = XmStringConcat(num_str, item2);
2735 XmStringFree(item2);
2736 XmStringFree(num_str);
2737 _numbered = DTM_TRUE;
2740 free((void*) value);
2747 return(complete_header);
2751 MsgScrollingList::shutdown()
2754 FORCE_SEGV_DECL(MsgStruct, tmpMS);
2755 DtMailMessageHandle tmpMH;
2756 DtMail::MailBox *mbox=parent()->mailbox();
2757 DtMailEnv mail_error;
2759 // Initialize the mail_error.
2762 if (num_deleted_messages == 0) return;
2764 num_entries = _deleted_messages->length();
2766 for (i = 0; i < num_entries; i++) {
2767 tmpMS = _deleted_messages->at(i);
2768 tmpMH = tmpMS->message_handle;
2770 // mbox->deleteMsg(mail_error, tmpMH);
2777 MsgScrollingList::lastMsg()
2779 if (_msgs->length() > 0) {
2780 return((_msgs->at(_msgs->length() - 1))->message_handle);
2782 else { // Currently an empty folder
2786 #endif /* DEAD_WOOD */
2789 MsgScrollingList::clearMsgs()
2791 if ( _msgs->length() > 0 )
2797 MsgScrollingList::display_message_summary()
2799 parent()->message_summary(
2800 selected_item_position(),
2802 get_num_new_messages(),
2803 get_num_deleted_messages());
2807 MsgScrollingList::display_message(
2808 DtMailEnv &mail_error,
2812 if ((pos > 0) && (pos <= session_message_number)) {
2813 // When loading mail headers, we need to make sure that
2814 // the XmList resources are all set at the same time to
2815 // avoid painting multiple times. So here we collect
2817 if (_xtarg_collector && _xmstr_collector)
2819 XmString *items = _xmstr_collector->GetItems();
2821 // Keep a handle to the malloced string copy so that
2822 // we can free it after the XtSetValues
2823 _selected_items = XmStringCopy (items[pos-1]);
2825 _xtarg_collector->AddItemToList (XmNselectedItems,
2826 (XtArgVal) &_selected_items);
2827 _xtarg_collector->AddItemToList (
2828 XmNselectedItemCount, 1);
2832 XmListSelectPos(_w, pos, FALSE);
2835 this->extended_selection(mail_error, pos);
2836 if (mail_error.isSet()) {
2837 // Return whatever error mailbox->get_next_msg() returned.
2845 MsgScrollingList::current_msg_handle()
2847 DtMailMessageHandle msg_number;
2849 if ( _displayed_item_position > 0 )
2850 msg_number=msgno( _displayed_item_position );
2858 MsgScrollingList::expunge(void)
2860 for (int i = _deleted_messages->length() - 1; i >= 0; i--) {
2861 _deleted_messages->remove_entry(i);
2865 num_deleted_messages = 0;
2866 display_message_summary();
2870 MsgScrollingList::resetIndexNums(void)
2873 int length = _msgs->length();
2876 for (m = 0; m < length; m++)
2879 ms->indexNumber = m;
2882 length = _deleted_messages->length();
2883 for (m = 0; m < length; m++)
2885 ms = _deleted_messages->at(m);
2886 ms->indexNumber = m;
2893 MsgScrollingList::resetSessionNums(void)
2896 int length = _msgs->length();
2899 for (m = 0; m < length; m++)
2902 ms->sessionNumber = m;
2904 session_message_number = m;
2905 return session_message_number;
2909 MsgScrollingList::sort_messages(void)
2911 DtMail::MailBox *mbox;
2915 MsgHndArray *selected_messages = selected();
2917 mbox = _parent->mailbox();
2918 sortby = _parent->last_sorted_by();
2920 // Sort array of message handles
2921 current_msg = _sorter->sortMessages(this, mbox, sortby);
2923 // The array of message handles is sorted. Now we need to update
2924 // the display preserving the selected state of the messages.
2925 updateListItems(current_msg, FALSE, selected_messages);
2927 delete selected_messages;
2929 _parent->last_sorted_by(sortby);
2930 display_message_summary();
2934 // Layout out the row of labels above the scrolling list
2937 MsgScrollingList::layoutLabels(
2943 // Save the input values
2944 _sender_lbl = sender;
2945 _subject_lbl = subject;
2953 // Layout out the row of labels above the scrolling list
2956 MsgScrollingList::layoutLabels()
2958 // Width of fields. +1 for spaces
2960 sender_width = 18 + 1,
2961 subject_width = 35 + 1,
2962 date_width = 3+1 + 3+1 + 2+1 + 5 + 2; // DDD mmm dd hh:mm 2spaces
2963 int char_width; // Width of a single character
2966 XmFontList font_list;
2968 // Calculate the width of date format to allocate the label
2969 // width dynamically
2974 clock = time((time_t *) 0);
2975 tm = localtime(&clock);
2979 GETMSG(DT_catd, 1, 222, "%a %b %d %k:%M"),
2984 GETMSG(DT_catd, 1, 223, "%a %b %d %H:%M"),
2989 // List uses a fixed width font. Therefore all characters are the
2990 // same size. So we use a space to determine the width of a char
2991 xmstr = XmStringCreateLocalized(" ");
2992 XtVaGetValues(_w, XmNfontList, &font_list, NULL);
2993 char_width = XmStringWidth(font_list, xmstr);
2998 subject_width -= num_width;
3003 // XXX dipol: Need to take into account if the scrollbar is on the
3007 XtWidgetGeometry geom;
3008 geom.request_mode = CWX;
3009 XtQueryGeometry(_sender_lbl, NULL, &geom);
3010 if (geom.x != (n * char_width)) {
3011 geom.x = (n * char_width);
3012 XtMoveWidget(_sender_lbl, geom.x, geom.y);
3013 } // Move the X location of the sender title
3015 XtVaSetValues(_sender_lbl, XmNx, n * char_width, NULL);
3018 XtQueryGeometry(_subject_lbl, NULL, &geom);
3019 if (geom.x != (n * char_width)) {
3020 geom.x = (n * char_width);
3021 XtMoveWidget(_subject_lbl, geom.x, geom.y);
3022 } // Move the X location of the subject title
3024 XtVaSetValues(_subject_lbl, XmNx, n * char_width, NULL);
3026 XtVaSetValues(_date_lbl, XmNx, n * char_width, NULL);
3028 XtVaSetValues(_size_lbl, XmNx, n * char_width, NULL);
3030 XmStringFree(xmstr);