/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* *+SNOTICE * * $TOG: MsgScrollingList.C /main/38 1998/12/10 19:08:02 mgreess $ * * RESTRICTED CONFIDENTIAL INFORMATION: * * The information in this document is subject to special * restrictions in a confidential disclosure agreement between * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this * document outside HP, IBM, Sun, USL, SCO, or Univel without * Sun's specific written approval. This document and all copies * and derivative works thereof must be returned or destroyed at * Sun's request. * * Copyright 1993 Sun Microsystems, Inc. All rights reserved. * *+ENOTICE */ #include #include #include #include #include
#include #include #include "DtMailGenDialog.hh" #include "DtMailHelp.hh" #include "Help.hh" // Remove after fixing problem with empty time headers #include "MailMsg.h" #include "MemUtils.hh" #include "MsgHndArray.hh" #include "MsgScrollingList.hh" #ifdef DEAD_WOOD #include "QueryDialogManager.hh" #endif /* DEAD_WOOD */ #include "RoamApp.h" #include "RoamMenuWindow.h" #include "Sort.hh" #include // Include for moving X location of titles extern int force( Widget ); MsgScrollingList::MsgScrollingList( RoamMenuWindow *menuwindow, Widget parent, char *name ) : ScrollingList( parent, name ) { _parent=menuwindow; _numbered = DTM_FALSE; _selected_item_position=-1; _displayed_item_position=-1; _selection_on = FALSE; _xmstr_collector = NULL; _xtarg_collector = NULL; _selected_items = NULL; _sorter = new Sort (); XtAddCallback( _w, XmNextendedSelectionCallback, (XtCallbackProc) &MsgScrollingList::extendedSelectionCallback, this ); _msgs = new MsgHndArray(1024); _deleted_messages = new MsgHndArray(1024); num_new_messages = 0; num_deleted_messages = 0; session_message_number = 0; // Can later initialize these from the last use of the session. // Each folder will have some idea of which message was last // read. We should select and display it at next load, no? _selected_item_position = 0; _displayed_item_position = 0; DtMailEnv mail_error; // Initialize the mail_error. mail_error.clear(); DtMail::Session * d_session = theRoamApp.session()->session(); DtMail::MailRc * mailrc = d_session->mailRc(mail_error); if (mailrc) { const char * value = NULL; mailrc->getValue(mail_error, "showto", &value); if (mail_error.isNotSet()) _header_info.number_of_names = 5; else _header_info.number_of_names = 4; if (NULL != value) free((void*) value); } else _header_info.number_of_names = 4; // Set up array for 5 items. The DtMailMessageTo is used only // if showto is set, but create placeholder for 5th item in case // they apply props in this same session. Then we just have to // toggle between 4 or 5 number_of_nanes. _header_info.header_name = new (char* [5]); _header_info.header_name[0] = NULL; _header_info.header_name[1] = NULL; _header_info.header_name[2] = NULL; _header_info.header_name[3] = NULL; _header_info.header_name[4] = NULL; _header_info.header_name[0] = strdup(DtMailMessageSender); _header_info.header_name[1] = strdup(DtMailMessageReceivedTime); _header_info.header_name[2] = strdup(DtMailMessageContentLength); _header_info.header_name[3] = strdup(DtMailMessageSubject); _header_info.header_name[4] = strdup(DtMailMessageTo); } MsgScrollingList::~MsgScrollingList() { MsgStruct *ms; int i, length; for (i=0; i<5; i++) { if (_header_info.header_name[i]) { free(_header_info.header_name[i]); _header_info.header_name[i] = NULL; } } delete _header_info.header_name; for (i=0, length=_deleted_messages->length(); iat(i); delete ms; } _deleted_messages->clear(); delete _deleted_messages; for (i=0, length=_msgs->length(); iat(i); delete ms; } _msgs->clear(); delete _msgs; delete _sorter; } Widget MsgScrollingList::get_scrolling_list() { return(_w); } void MsgScrollingList::items( XmString items[], int count ) { XtVaSetValues( _w, XmNitems, items, XmNitemCount, count, NULL ); } #ifdef DEAD_WOOD void MsgScrollingList::addChooseCommand( ChooseCmd *cmd ) { _choose=cmd; } void MsgScrollingList::addDeleteCommand( DeleteCmd *cmd ) { _delete=cmd; } #endif /* DEAD_WOOD */ void MsgScrollingList::select_next_item() { INSERT_STACK_PROBE int num_msgs = 0; FORCE_SEGV_DECL(MsgStruct, tmpMS); DtMailEnv mail_error; // Initialize the mail_error. mail_error.clear(); XtVaGetValues( _w, XmNitemCount, &num_msgs, NULL ); _selected_item_position = _displayed_item_position + 1; if (_selected_item_position <= num_msgs && _selected_item_position > 0 ) { tmpMS = get_message_struct(_selected_item_position); if (tmpMS == NULL) { return; } else { // Deselect all items currently selected. // display_and_select_message() will select, highlight // and display the "next" message. XmListDeselectAllItems(_w); this->display_and_select_message(mail_error, tmpMS->message_handle); if (mail_error.isSet()) { // Post an exception here... } } } else { return; } } void MsgScrollingList::select_prev_item() { INSERT_STACK_PROBE int num_msgs; FORCE_SEGV_DECL(MsgStruct, tmpMS); DtMailEnv mail_error; // Initialize the mail_error. mail_error.clear(); XtVaGetValues( _w, XmNitemCount, &num_msgs, NULL ); if( _displayed_item_position != 1 ) _selected_item_position = _displayed_item_position - 1 ; else _selected_item_position = _displayed_item_position ; if (_selected_item_position >= 1) { tmpMS = get_message_struct(_selected_item_position); if (tmpMS == NULL) { return; } else { // Deselect all items currently selected. // display_and_select_message() will select, highlight // and display the "previous" message. XmListDeselectAllItems(_w); this->display_and_select_message(mail_error, tmpMS->message_handle); if (mail_error.isSet()) { // Post an exception here... } } } else { return; } } DtMailMessageHandle MsgScrollingList::msgno( int index ) { if (index <= 0) { return(NULL); } else { return _msgs->at(index-1)->message_handle; } } MsgStruct* MsgScrollingList::get_message_struct( int index ) { if (index <= 0) { return(NULL); } else { return(_msgs->at(index-1)); } } int MsgScrollingList::position( DtMailMessageHandle msgno ) { return (_msgs->indexof(msgno))+1; } #ifdef DEAD_WOOD int MsgScrollingList::position( MsgStruct *a_msg_struct ) { return (_msgs->indexof(a_msg_struct))+1; } void MsgScrollingList::appendMsg( DtMailMessageHandle msg_hndl ) { MsgStruct *newMS; // A new message has come in. // Increase the session_message_number which keeps track of the // number of messages in this session for this folder (scrolling list). // newMS = new MsgStruct(); newMS->message_handle = msg_hndl; newMS->indexNumber = session_message_number-num_deleted_messages; newMS->sessionNumber = session_message_number; newMS->is_deleted = FALSE; _msgs->append(newMS); session_message_number++; } #endif /* DEAD_WOOD */ void MsgScrollingList::insertMsg( DtMailMessageHandle msg_hndl ) { MsgStruct *newMS; // A new message has come in. // Increase the session_message_number which keeps track of the // number of messages in this session for this folder (scrolling list). // newMS = new MsgStruct(); newMS->message_handle = msg_hndl; newMS->indexNumber = session_message_number-num_deleted_messages; newMS->sessionNumber = session_message_number; newMS->is_deleted = FALSE; _msgs->append(newMS); session_message_number++; } void MsgScrollingList::insertDeletedMsg(DtMailMessageHandle msg_hndl) { MsgStruct *newMS; // A new message has come in. // Increase the session_message_number which keeps track of the // number of messages in this session for this folder (scrolling list). // newMS = new MsgStruct(); newMS->message_handle = msg_hndl; newMS->indexNumber = num_deleted_messages; newMS->sessionNumber = session_message_number; newMS->is_deleted = FALSE; _deleted_messages->append(newMS); session_message_number++; num_deleted_messages++; } int MsgScrollingList::load_headers( DtMailEnv &mail_error ) { DtMailMessageHandle tmpMH; #ifdef undef XmString new_status, read_status; #endif XmString complete_header; // text of header w/ glyphs int num_items = 0; int select_item; DtMailHeaderLine info; DtMail::MailBox * mbox = this->parent()->mailbox(); DtMailEnv error; DtMailBoolean first_new = DTM_TRUE; // Create a class to collect the mail header XmStrings // then get all the items from the current list. _xmstr_collector = new XmStrCollector(); #ifdef undef /* NL_COMMENT * In a mailer container window's message scrolling list, a "N" appears * to the left of a mail message header indicating that the mail message * is "new" (just arrived and not yet viewed by the user). * There is only space to display 1 character. If "N" needs to be translated, * please make sure the translation is only 1 character. */ new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 110, "N")); read_status = XmStringCreateLocalized(" "); #endif // Allocate memory for the XmString array and initialize it. int visible; XtVaGetValues(_w, XmNvisibleItemCount, &visible, NULL); // Retrieve the message_handles, and from them their headers. // Create an XmString and toss it into the XmStrCollector. select_item = 0; int n_vis = 0; MsgStruct *ms; for (tmpMH = mbox->getFirstMessageSummary(error, _header_info, info); tmpMH && !error.isSet(); tmpMH = mbox->getNextMessageSummary(error, tmpMH, _header_info, info), num_items++) { DtMail::Message * msg = mbox->getMessage(error, tmpMH); if (msg->flagIsSet(error, DtMailMessageDeletePending) == DTM_TRUE) { insertDeletedMsg(tmpMH); continue; } else { insertMsg(tmpMH); n_vis += 1; } ms = get_message_struct(get_num_messages()); complete_header = formatHeader( info, ms->indexNumber, show_with_attachments(msg), msg->flagIsSet(error, DtMailMessageNew)); if (msg->flagIsSet(error, DtMailMessageNew) == DTM_TRUE) { num_new_messages++; // We want to select the last read message before the // first new message. We will select the first new // message if it is the first message. if (first_new) { first_new = DTM_FALSE; if (num_items > 0) select_item = num_items - 1; else select_item = 0; } } // Insert the XmString into the array. _xmstr_collector->AddItemToList (complete_header); // Free the space allocated for info // delete []info.header_values; mbox->clearMessageSummary(info); } // If there were no new messages, select and display the last message. if (first_new) { select_item = num_items - 1; } select_item += 1; // Message slots start at 1. select_item -= num_deleted_messages; // List does not have deleted msgs. // Add the items to the XmList. // All XmStrings are freed in the XmStrCollector destructor. _xtarg_collector = new XtArgCollector; // The first time the headers are loaded, they should all be loaded // at the same time. XtVaSetValues is used for this rather than // XmListAddItems so that all the other resource will be set at // the same time. This prevents multiple repaints. // // However, in the case where only an item or two are being added, // XmListAddItems should be used. This prevents an unnecessary // repaint in this case. XmListAddItems (_w, _xmstr_collector->GetItems(), _xmstr_collector->GetNumItems(), 0); display_message_summary(); display_message(mail_error, select_item); _xtarg_collector->SetValues(_w); if (_selected_items) { XmStringFree (_selected_items); _selected_items = NULL; } delete _xtarg_collector; delete _xmstr_collector; _xtarg_collector = NULL; _xmstr_collector = NULL; return(num_items); } void MsgScrollingList::load_headers( DtMailEnv &mail_error, DtMailMessageHandle last ) { DtMailMessageHandle tmpMH; #ifdef undef XmString read_status, new_status; #endif XmString complete_header; // read status + attach + header_text. int num_items; int num_new = 0, num_vis = 0; DtMailHeaderLine info; DtMailEnv error; DtMail::MailBox * mbox = this->parent()->mailbox(); // Create a class to collect the mail header XmStrings // then get all the items from the current list. _xmstr_collector = new XmStrCollector(); mail_error.clear(); #ifdef undef /* NL_COMMENT * In a mailer container window's message scrolling list, a "N" appears * to the left of a mail message header indicating that the mail message * is "new" (just arrived and not yet viewed by the user). * There is only space to display 1 character. If "N" needs to be translated, * please make sure the translation is only 1 character. */ new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 111, "N")); read_status = XmStringCreateLocalized(" "); #endif // Allocate memory for the XmString array and initialize it. XtVaGetValues(_w, XmNvisibleItemCount, &num_items, NULL); MsgStruct *ms; for (tmpMH = mbox->getNextMessageSummary(error, last, _header_info, info); tmpMH && !error.isSet(); tmpMH = mbox->getNextMessageSummary(error, tmpMH, _header_info, info), num_new++) { DtMail::Message * msg = mbox->getMessage(error, tmpMH); if (msg->flagIsSet(error, DtMailMessageDeletePending) == DTM_TRUE) { insertDeletedMsg(tmpMH); continue; } else { insertMsg(tmpMH); num_vis += 1; } ms = get_message_struct(get_num_messages()); complete_header = formatHeader( info, ms->indexNumber, show_with_attachments(msg), msg->flagIsSet(error, DtMailMessageNew)); if (msg->flagIsSet(error, DtMailMessageNew) == DTM_TRUE) { num_new_messages++; } // Insert the XmString into the array. _xmstr_collector->AddItemToList (complete_header); // Free the space allocated for info // delete []info.header_values; mbox->clearMessageSummary(info); } // Add the items to the XmList. // All XmStrings are freed in the XmStrCollector destructor. _xtarg_collector = new XtArgCollector; // The first time the headers are loaded, they should all be loaded // at the same time. XtVaSetValues is used for this rather than // XmListAddItems so that all the other resource will be set at // the same time. This prevents multiple repaints. // // However, in the case where only an item or two are being added, // XmListAddItems should be used. This prevents an unnecessary // repaint in this case. XmListAddItems (_w, _xmstr_collector->GetItems(), _xmstr_collector->GetNumItems(), 0); do_list_vis_adjustment(); display_message_summary(); _xtarg_collector->SetValues(_w); if (_selected_items) { XmStringFree (_selected_items); _selected_items = NULL; } delete _xtarg_collector; delete _xmstr_collector; _xtarg_collector = NULL; _xmstr_collector = NULL; } void MsgScrollingList::do_list_vis_adjustment() { Widget list = _w; int numNew = _xmstr_collector->GetNumItems(); int focItm = _selected_item_position; int numItems; int cItmCnt, pItmCnt, sItmCnt; int cFocItm, cTopItm, cBotItm, cInvItm; XtVaGetValues(list, XmNvisibleItemCount, &numItems, NULL); XtVaGetValues(list, XmNitemCount, &cItmCnt, NULL); XtVaGetValues(list, XmNselectedItemCount, &sItmCnt, NULL); XtVaGetValues(list, XmNtopItemPosition, &cTopItm, NULL); pItmCnt = cItmCnt - numNew; cBotItm = cTopItm + numItems - 1; //[cTopItm...cBotItm is our window //[User has chosen to view some messages and that view needs to be //[maintained. if (cBotItm != pItmCnt) return; //[If Brand New Mailbox if (cItmCnt <= numItems) return; //[If no items selected, simply synch right till bottom if (sItmCnt == 0) { XmListSetBottomPos(list, cItmCnt); return; } // assert(cFocItm != -1); cFocItm = focItm; cInvItm = pItmCnt - cBotItm; //[Num below bottom-most, or hidden if ((cFocItm <= cBotItm) && (cFocItm >= cTopItm)) { int winM = cFocItm - cTopItm - cInvItm; if (winM <= 0) { //[There is no scope of adjustment return; } //[All the new messages can be accommodated w/o scrolling curr selection if (numNew <= winM) { XmListSetBottomPos(list, cItmCnt); return; } //[All the new messages cannot be accommodated, but we will do best fit else { int numNotShow = numNew - winM; XmListSetBottomPos(list, cItmCnt - numNotShow); return; } } else { XmListSetBottomPos(list, cItmCnt); return; } } void MsgScrollingList::deleteSelected(Boolean silent) { // SR - Added stuff below. Made code more efficient also. FORCE_SEGV_DECL(MsgStruct, a_del_msg_struct); FORCE_SEGV_DECL(MsgStruct, tmpMS); FORCE_SEGV_DECL(ViewMsgDialog, tmpView); DtMailMessageHandle tmpMH; int position_in_list, i; FORCE_SEGV_DECL(int, position_list); int position_count; Boolean any_selected; int num_msgs; UndelFromListDialog *undel_dialog; int first_selected_pos; DtMailEnv mail_error, error; DtMailBoolean cur_state; char *status_message, *str; // Initialize the mail_error. mail_error.clear(); XtVaGetValues( _w, XmNitemCount, &num_msgs, NULL ); any_selected = XmListGetSelectedPos(_w, &position_list, &position_count); if (!any_selected) return; // Of the items in the list, potentially many could be selected // for delete. And those selected for deleted need not be // contiguous necessarily. Note the position of the item that // appears first in the list. When the items are deleted, other // items if any exist will shift up to take the places of the // deleted items. You want to display the corresponding item that // takes the "first position". // Say, if you have messages A, B, C, D, Evisible in that order. // You select B and C for delete. // The first selected pos is 2. // When B and C are deleted, D (and E...) move up. // The new order is A, D, E... // You want to display the second message which is D. first_selected_pos = *position_list; undel_dialog = parent()->get_undel_dialog(); for (i=0; i < position_count; i++ ) { position_in_list = *(position_list + i); a_del_msg_struct = get_message_struct(position_in_list); _msgs->mark_for_delete(position_in_list - 1); _deleted_messages->append(a_del_msg_struct); if (undel_dialog) undel_dialog->insertMsg(mail_error, a_del_msg_struct); if (mail_error.isSet()) parent()->postErrorDialog(mail_error); // See if there is a standalone view of the message. // If there is, quit it. tmpMH = a_del_msg_struct->message_handle; tmpView = parent()->ifViewExists(tmpMH); if (tmpView) tmpView->quit(); // Update the status of the message in the persistent store. // DtMail::Message * msg = _parent->mailbox()->getMessage( error, a_del_msg_struct->message_handle); msg->setFlag(error, DtMailMessageDeletePending); // Check if message is new. If new and it is being deleted, // reduce the new messages count. cur_state = msg->flagIsSet(error, DtMailMessageNew); if (cur_state == DTM_TRUE) num_new_messages--; } _msgs->compact(0); // Delete the items from the scrolling list XmListDeletePositions(_w, position_list, position_count); num_deleted_messages += position_count; // XnListGetSelectedPos allocated memory for the array and // wants us to free it. See documentation. num_msgs = _msgs->length(); // Always remove the current attachments from the attachment area // regardless of whether this is the last message in the list or not // We call clearAttachArea if there are any other messages in the folder // as parseAttachments will be called which will clean up the attachment // status window. In the case of deleting the last message we must call // removeCurrentAttachments which cleans up the status window as well. // if (num_msgs == 0) parent()->get_editor()->attachArea()->removeCurrentAttachments(); else parent()->get_editor()->attachArea()->clearAttachArea(); if (num_msgs == 0) { //No more messages left; clear the editor. parent()->get_editor()->textEditor()->set_contents("", 1); // NOTE: Need to obtain the attachArea and clear it up too _displayed_item_position = 0; // Not displaying any. _selected_item_position = 0; // Reset... free(position_list); display_message_summary(); return; } // If there are messages 1, 2 and 3 and 2 is selected and deleted // first_selected_pos will be 2; // after removing it, will need to display the 2nd message // (which will now be 3) // If 2 and 3 have been deleted, and there is no 2nd message, // display the nth message (in our example, n = 1) _selected_item_position = first_selected_pos; if (_selected_item_position > num_msgs) _selected_item_position = num_msgs; // Having determined which message to display, confirm that it is // within the bounds. Call display_message(). // FYI, display_message() sets the _displayed_item_position. if ((_selected_item_position > 0) && (_selected_item_position <= num_msgs)) { XmListSelectPos(_w, _selected_item_position, FALSE); tmpMS = this->get_message_struct(_selected_item_position); if (tmpMS == NULL) { free(position_list); return; } else { this->display_message(mail_error, tmpMS->message_handle); if (mail_error.isSet()) parent()->postErrorDialog(mail_error); } } if (!silent) { if (position_count > 1) { /* NL_COMMENT * The following sentence means %d number of mail messages have * been deleted from the mail folder. This is the plural form * of the message that gets printed if more than one message * is moved. */ str = GETMSG(DT_catd, 3, 84, "%d messages deleted"); } else { /* NL_COMMENT * The following sentence means %d number of mail messages have * been deleted from the mail folder. This is the singular * form of the message that gets printed if only one message * is moved. */ str = GETMSG(DT_catd, 3, 85, "%d message deleted"); } status_message = new char[strlen(str) + 10]; sprintf(status_message, str, position_count); parent()->message(status_message); delete [] status_message; } updateListItems(-1, TRUE, NULL); display_message_summary(); XtFree((char*) position_list); } DtMailBoolean copyCallback( DtMailCallbackOp, const char *, const char *, void *, ... ) { return(DTM_FALSE); } // copySelected() will either copy or move the selected messages // into the container called destname. If the delete_after_copy // flag is set to TRUE, it is effectively a move; otherwise, it // is a copy. If the container named by destname is a relative // path, but it isn't prefixed with a '+', then a '+' will be // prepended to the name so that the open() call will "do the // right thing." // If silent is TRUE then no status messages are displayed and the // destname is not added to the copy/move cache. int MsgScrollingList::copySelected( DtMailEnv &mail_error, char *destname, int delete_after_copy, int silent ) { FORCE_SEGV_DECL(DtMail::MailBox, mbox); FORCE_SEGV_DECL(int, position_list); FORCE_SEGV_DECL(DtMail::MailBox, target); DtMail::MailRc * mailrc; DtMail::Session * d_session = theRoamApp.session()->session(); int position_count, position, i; Boolean any_selected = FALSE; DtMailMessageHandle msg; char *status_message, *str; char *newdestname; RoamMenuWindow *rmw; any_selected = XmListGetSelectedPos(_w, &position_list, &position_count); // If there aren't any selected messages, then there isn't // anything for us to do. if (!any_selected) { display_no_message(); if (! silent) { char * helpId = NULL; DtMailGenDialog *dialog = _parent->genDialog(); dialog->setToErrorDialog( GETMSG(DT_catd, 3, 50, "Mailer"), GETMSG(DT_catd, 2, 16, "No message selected.")); dialog->post_and_return(helpId); } return(1); } mbox = parent()->mailbox(); mailrc = mbox->session()->mailRc(mail_error); if (!mailrc) { // NL_COMMENT // The following is an error message. "mailrc" is the name of the // mail resource file. Translate as appropriate. // parent()->message(GETMSG(DT_catd, 2, 15,"Error - Unable to get mailrc.")); return(1); } // If the first character of destname is alphanumeric, we can // safely assume that it is a relative path, so we prepend a // '+' to it. if (isalnum(destname[0])) { // Make sure we allocate enough for the name + '+' + null terminator. newdestname = (char *) malloc(strlen(destname) + 2); memset(newdestname, 0, strlen(destname) + 2); sprintf(newdestname, "+%s", destname); char *path = d_session->expandPath(mail_error, newdestname); target = theRoamApp.session()->open(mail_error, path, copyCallback, NULL, DTM_TRUE, DTM_FALSE, DTM_FALSE); free(newdestname); free(path); newdestname = NULL; } else { target = theRoamApp.session()->open(mail_error, destname, copyCallback, NULL, DTM_TRUE, DTM_FALSE, DTM_FALSE); } if (mail_error.isSet()) { // if the error is DTME_AlreadyOpened, we don't care. // go ahead to clear the error // otherwise, // We couldn't open the container, so we want to post an // error dialog. if ((DTMailError_t)mail_error == DTME_AlreadyOpened) { mail_error.clear(); } else { parent()->postErrorDialog(mail_error); return(0); } } // Go through the selected messages and copy them to the // specified container. if ( position_count > 0 ) parent()->busyCursor() ; for (i=0; i < position_count; i++) { position = *(position_list + i); msg = msgno(position); DtMail::Message * dtmsg = mbox->getMessage(mail_error, msg); if (mail_error.isSet()) { parent()->normalCursor() ; parent()->postErrorDialog(mail_error); theRoamApp.session()->close(mail_error, target); return(0); } mail_error.clear(); target->copyMessage(mail_error, dtmsg); if (mail_error.isSet()) { parent()->normalCursor() ; parent()->postErrorDialog(mail_error); theRoamApp.session()->close(mail_error, target); return(0); } parent()->normalCursor() ; } rmw = theRoamApp.session()->getRMW(target); mail_error.clear(); if (rmw) rmw->checkForMail(mail_error); if (delete_after_copy) { deleteSelected(); if (i > 1) { // NL_COMMENT // The following sentence means %d number of mail messages have // been moved to the %s mail folder. The %s is the name of a // mail folder. This is the plural form of the message that gets // printed if more than one message is moved. // str = GETMSG(DT_catd, 3, 65, "%d messages moved to %s"); } else { // NL_COMMENT // The following sentence means %d number of mail messages have // been moved to the %s mail folder. The %s is the name of a // mail folder. This is the singular form of the message that // gets printed if only one message is moved. // str = GETMSG(DT_catd, 3, 66, "%d message moved to %s"); } } else { if (i > 1) { // NL_COMMENT // The following sentence means %d number of mail messages have been // copied to the %s mail folder. This is the plural form of the // message that gets printed if more than one message is copied. // str = GETMSG(DT_catd, 3, 67, "%d messages copied to %s"); } else { // NL_COMMENT // The following sentence means %d number of mail messages have been // copied to the %s mail folder. This is the singular form of the // message that gets printed if only one message is copied. // str = GETMSG(DT_catd, 3, 68, "%d message copied to %s"); } } newdestname = d_session->getRelativePath(mail_error, destname); status_message = new char[strlen(str) + strlen(newdestname) + 10]; sprintf(status_message, str, i, newdestname); if (!silent) { theRoamApp.globalAddToCachedContainerList(newdestname); parent()->message(status_message); } free(newdestname); theRoamApp.session()->close(mail_error, target); delete [] status_message; return(0); } //----------------------------------------------------------------------------- // This method returns a list of the currently selected messages. This list // must be deleted by the calling method. //----------------------------------------------------------------------------- MsgHndArray * MsgScrollingList::selected() { FORCE_SEGV_DECL(MsgStruct, a_msg_struct); FORCE_SEGV_DECL(int, position_list); int position_in_list, i; int position_count; Boolean any_selected; // Find out first if any have been selected. // If i has been selected, how many? // We need the number selected so that we can allocate // space for that many mesasgeStructs to be returned. any_selected = XmListGetSelectedPos(_w, &position_list, &position_count); // If nothing selected, return if (!any_selected) return NULL; // Allocate memory for position_count number of messageStructs // in MsgHndArray. MsgHndArray *msgList = new MsgHndArray(position_count); for (i=0; i < position_count; i++ ) { position_in_list = *(position_list + i); a_msg_struct = get_message_struct(position_in_list); msgList->append(a_msg_struct); } return msgList; } DtMailBoolean MsgScrollingList::show_with_attachments(DtMailMessageHandle msg_num) { DtMailBoolean has_attachments = DTM_FALSE; DtMailEnv mail_error; DtMail::MailBox *mbox=parent()->mailbox(); DtMail::Message *msg = mbox->getMessage(mail_error, msg_num); has_attachments = show_with_attachments(msg); return has_attachments; } DtMailBoolean MsgScrollingList::show_with_attachments(DtMail::Message * msg) { DtMail::BodyPart *bp; DtMailEnv error; DtMailBoolean has_attachments = DTM_FALSE; DtMailBoolean is_multipart = DTM_FALSE; int num_bodyParts; char *type; is_multipart = msg->flagIsSet(error, DtMailMessageMultipart); if (! is_multipart) return DTM_FALSE; num_bodyParts = msg->getBodyCount(error); if (num_bodyParts > 1) return DTM_TRUE; bp = msg->getFirstBodyPart(error); bp->getContents(error, NULL, NULL, &type, NULL, NULL, NULL); if (NULL != type) { char *attr; attr = DtDtsDataTypeToAttributeValue(type, DtDTS_DA_IS_TEXT, NULL); if (attr && strcasecmp(attr, "true") != 0) has_attachments = DTM_TRUE; if (type) free(type); if (attr) DtDtsFreeAttributeValue(attr); } return has_attachments; } void MsgScrollingList::display_message( DtMailEnv &mail_error, DtMailMessageHandle msg_num ) { int item_index; DtMail::MailBox *mbox=parent()->mailbox(); Editor* rmw_editor; DtMailBoolean cur_state; int num_selected; int num_bodyParts; // If there is a status message displayed, clear it first. parent()->clear_message(); // We could have called display_msg from anywhere. // Need to calculate what is the position of that item // in the scrolling list, given the DtMailMessageHandle. // Determine the index at the _msgs array; increment by 1 // since array begins at 0 and XmList begins at 1. item_index = _msgs->indexof(msg_num); if (item_index < 0) return; _displayed_item_position = item_index + 1; // Make sure the header is visible in the scrolling list. this->scroll_to_position(_displayed_item_position); // Retrieve the header text and insert it. We need to retrieve // message from the handle and inset the headers. // DtMail::Message * msg = mbox->getMessage(mail_error, msg_num); DtMail::Envelope * env = msg->getEnvelope(mail_error); // There are multiple paths to this place: // 1) user has already read this message but is re-reading it; // 2) user has undeleted this message (implicitly, they have // read this message); // 3) user has not read this message yet. // // For (1), we don't need to do anything fancy. // For (2), we have already reset the IsDeleted flag while // undeleting. // For (3), we need to reset the flag in store to indicate // its read. cur_state = msg->flagIsSet(mail_error, DtMailMessageNew); // If the message was previously new, we need to reset the list // item to remove the "N" on the item. We do this by reconstructing // the header line without the "N". // if (cur_state == DTM_TRUE) { DtMailHeaderLine info; msg->resetFlag(mail_error, DtMailMessageNew); mbox->getMessageSummary(mail_error, msg_num, _header_info, info); MsgStruct *ms; ms = get_message_struct(_displayed_item_position); XmString complete_header; complete_header = formatHeader( info, ms->indexNumber, show_with_attachments(msg), msg->flagIsSet(mail_error, DtMailMessageNew)); mbox->clearMessageSummary(info); XmListReplaceItemsPos(_w, &complete_header,1, _displayed_item_position); XmStringFree(complete_header); // The default selection policy is extended_select. // Problem: // User extend selects multiple items // The last item selected is a "N" message // We display the "N" message and have to replace it without // the "N". // There is no way to replace with something else and select at // the same time. // Therefre, we need to explicitly select the replacement. // The problem is: selecting the replacement deselects the other // selected items. // To work around this, we switch temporarily to MULTIPLE_SELECT, // select the replacement, and switch back to extend_select. // This will select the replacement and *not* drop the // selection on the other selected items. // Say "Amen" to OSF for such convoluted thinking! XtVaGetValues(_w, XmNselectedItemCount, &num_selected, NULL); if (num_selected > 1) { // Change to MULTIPLE_SELECT. // Select item // Change back to EXTEND_SELECT XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, XmNselectionMode, XmNORMAL_MODE, NULL); // When loading mail headers, we need to make sure that // the XmList resources are all set at the same time to // avoid painting multiple times. So here we collect // these resources. if (_xtarg_collector && _xmstr_collector) { XmString *items = _xmstr_collector->GetItems(); // Keep a handle to the malloced string copy so that // we can free it after the XtSetValues _selected_items = XmStringCopy (items[_displayed_item_position - 1]); _xtarg_collector->AddItemToList (XmNselectedItems, (XtArgVal) &_selected_items); _xtarg_collector->AddItemToList ( XmNselectedItemCount, 1); _xtarg_collector->AddItemToList ( XmNselectionPolicy, XmEXTENDED_SELECT); } else { XmListSelectPos(_w, _displayed_item_position, FALSE); XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, XmNselectionMode, XmNORMAL_MODE, NULL); } } else { if (_xtarg_collector && _xmstr_collector) { XmString *items = _xmstr_collector->GetItems(); _selected_items = XmStringCopy (items[_displayed_item_position - 1]); _xtarg_collector->AddItemToList (XmNselectedItems, (XtArgVal) &_selected_items); _xtarg_collector->AddItemToList ( XmNselectedItemCount, 1); } else { XmListSelectPos(_w, _displayed_item_position, FALSE); } } num_new_messages--; } display_message_summary(); // Display the message now. rmw_editor = parent()->get_editor()->textEditor(); rmw_editor->disable_redisplay(); rmw_editor->auto_show_cursor_off(); rmw_editor->clear_contents(); num_bodyParts = msg->getBodyCount(mail_error); char * status_string; DtMailBoolean firstBPHandled; // Turn on the busy Cursor parent()->busyCursor(); if (parent()->fullHeader()) firstBPHandled = rmw_editor->set_message(msg, &status_string, Editor::HF_FULL); else firstBPHandled = rmw_editor->set_message(msg, &status_string, Editor::HF_ABBREV); if (status_string) parent()->message(status_string); if (mail_error.isSet()) {} // do something if ((num_bodyParts > 1) || (!firstBPHandled)) { // If the message has attachments, then let the attach pane // handle attachments but not the first bodyPart (which has // already been handled here). if (firstBPHandled) { // The first bodyPart has already been handled. // The others, beginning from the second, need to be parsed // and put into the attachPane. parent()->get_editor()->attachArea()->parseAttachments( mail_error, msg, TRUE, 2); } else { // The first bodyPart was not handled. // It may not have been of type text. // The attachment pane needs to handle all the bodyParts // beginning with the first. parent()->get_editor()->attachArea()->parseAttachments( mail_error, msg, TRUE, 1); } // Check for errors. // Manage the attach area to display attachments. if (mail_error.isSet()) {} // do something parent()->get_editor()->manageAttachArea(); parent()->activate_default_attach_menu(); } else { parent()->deactivate_default_attach_menu(); parent()->get_editor()->unmanageAttachArea(); } parent()->sync_work_area_size(); rmw_editor->auto_show_cursor_restore(); // Turn on text editor and manage attachPane rmw_editor->set_to_top(); rmw_editor->enable_redisplay(); parent()->normalCursor(); } void MsgScrollingList::display_and_select_message( DtMailEnv &mail_error, DtMailMessageHandle msg_num ) { int item_index; // Need to calculate what is the position of that item // in the scrolling list, given the DtMailMessageHandle. // Determine the index at the _msgs array; increment by 1 // since array begins at 0 and XmList begins at 1. mail_error.clear(); item_index = _msgs->indexof(msg_num); if (item_index < 0) return; _displayed_item_position = item_index + 1; _selected_item_position = _displayed_item_position; // Select this message in the scrolling list and display // the message. // When loading mail headers, we need to make sure that // the XmList resources are all set at the same time to // avoid painting multiple times. So here we collect // these resources. if (_xtarg_collector && _xmstr_collector) { XmString *items = _xmstr_collector->GetItems(); // Keep a handle to the malloced string copy so that // we can free it after the XtSetValues _selected_items = XmStringCopy (items[_displayed_item_position - 1]); _xtarg_collector->AddItemToList (XmNselectedItems, (XtArgVal) &_selected_items); _xtarg_collector->AddItemToList ( XmNselectedItemCount, 1); } else { XmListSelectPos(_w, _displayed_item_position, FALSE); } this->display_message(mail_error, msg_num); return; } void MsgScrollingList::select_all_and_display_last( DtMailEnv & error ) { register int item_pos; int num_items; MsgHndArray * msgHandles = get_messages(); error.clear(); if (this->get_num_messages() == 0) return; // If no message selected, return. if (this->get_selected_item() == 0) return; XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL); // // We have to go to the end of the list and go backwards. // We display the last one, and select the rest. // // A NULL terminated list. // XmListDeselectAllItems(baseWidget()); XtVaGetValues(baseWidget(), XmNitemCount, &num_items, NULL); for (item_pos = 1; item_pos < num_items; item_pos++) { XmListSelectPos(baseWidget(), item_pos, FALSE); } // Invoke the selection callback on the last item. display_and_select_message(error, msgHandles->at(item_pos-1)->message_handle); XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL); XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL); } void MsgScrollingList::select_all_and_display_last( DtMailEnv & error, DtMailMessageHandle *handleArray, unsigned int elements ) { register int handleOffset = 0; register int item_pos; error.clear(); XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL); // // We have to go to the end of the list and go backwards. // We display the last one, and select the rest. // // A NULL terminated list. // handleOffset = elements; XmListDeselectAllItems(baseWidget()); while (--handleOffset >= 0 && error.isNotSet()) { item_pos = _msgs->indexof(handleArray[handleOffset]); // Get position if (item_pos < 0) { continue; } // // Select this message in the scrolling list and IF // it is the last message, display it. // if (handleOffset == elements - 1) { display_and_select_message(error, handleArray[handleOffset]); } else { XmListSelectPos(_w, item_pos + 1, FALSE); } } XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL); XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL); return; } void MsgScrollingList::display_no_message() { /* NL_COMMENT * No mail message has been selected by the user. */ parent()->message(GETMSG(DT_catd, 2, 16, "No message selected.")); _displayed_item_position = 0; _selected_item_position = 0; parent()->get_editor()->textEditor()->clear_contents(); parent()->get_editor()->attachArea()->removeCurrentAttachments(); } // In need of a simple but useful optimization here. // By the time we get to this method, we already have displayed // the selected message in the lower section of the combo window. // Instead of having to parse the messageHandle and construct the // text buffer to display, we can just get the parsed-and-formatted // text from the lower section of the combo window and stick it into // the VMD's editor. // This will save us the effort of parsing and formatting a message // void MsgScrollingList::viewInSeparateWindow(DtMailEnv &mail_error) { FORCE_SEGV_DECL(const char, title); FORCE_SEGV_DECL(char, header_txt); FORCE_SEGV_DECL(MsgStruct, tmpMS); DtMailMessageHandle msgHandle; DtMail::MailBox *mbox=parent()->mailbox(); ViewMsgDialog *newview; int num_bodyParts; Editor* vmd_editor; DtMailEnv error; DtMail::Session *d_session = theRoamApp.session()->session(); DtMail::MailRc *mail_rc = d_session->mailRc(error); const char *value = NULL; // If no message selected, return. if (this->get_selected_item() <= 0) return; // Double-check. If none selected, return MsgHndArray *selected_msgs = this->selected(); if (!selected_msgs) return; for (int a_msg_num = 0; a_msg_num < selected_msgs->length(); a_msg_num++) { tmpMS = selected_msgs->at(a_msg_num); if (tmpMS == NULL) return; msgHandle = tmpMS->message_handle; newview = parent()->ifViewExists(msgHandle); if (newview != NULL) { /* NL_COMMENT * The current mail message selected is already displayed in a * separate window. Therefore this 'separate' window will be * raised in front of existing windows so the user can see it. */ parent()->message( GETMSG( DT_catd, 3, 69, "View already exists. Raising it.")); newview->displayInCurrentWorkspace(); return; } // No view exists. Display it. For feedback, set busyCursor(). parent()->busyCursor(); mail_rc->getValue(error, "separatemessageviewer", &value); if (error.isSet()) newview = new ViewMsgDialog(parent(), xmDialogShellWidgetClass); else { if (value) free((void*) value); newview = new ViewMsgDialog(parent(), topLevelShellWidgetClass); } parent()->registerDialog(newview); newview->initialize(); vmd_editor = newview->get_editor()->textEditor(); // Set the VMD's msgHandle. This unique handle is what is // used to raise the VMD when the same message is double // clicked on later. newview->msgno(msgHandle); DtMailEnv error; DtMail::Message * msg = mbox->getMessage(error, msgHandle); DtMail::Envelope * env = msg->getEnvelope(error); DtMailValueSeq title_value; env->getHeader(error, DtMailMessageSubject, DTM_TRUE, title_value); if (error.isSet()) title = "NO SUBJECT!"; else title = *(title_value[0]); newview->title((char *)title); newview->auto_show_cursor_off(); // Unset the error produced when obtaining title... mail_error.clear(); vmd_editor->disable_redisplay(); char * status_string; DtMailBoolean firstBPHandled; if (parent()->fullHeader()) firstBPHandled = newview->get_editor()->textEditor()->set_message( msg, &status_string, Editor::HF_FULL); else firstBPHandled = newview->get_editor()->textEditor()->set_message( msg, &status_string, Editor::HF_ABBREV); // If the message has attachments, then let the attach pane // handle attachments but not the first bodyPart (which has // already been handled here). num_bodyParts = msg->getBodyCount(mail_error); if (mail_error.isSet()) {} // do something if ((num_bodyParts == 1) && firstBPHandled) { newview->get_editor()->unmanageAttachArea(); newview->deactivate_default_attach_menu(); } else if ((num_bodyParts > 1) || (!firstBPHandled)) { // If the message has attachments, then let the attach pane // handle attachments but not the first bodyPart (which has // already been handled here). if (firstBPHandled) { // The first bodyPart has already been handled. // The others, beginning from the second, need to be parsed // and put into the attachPane. newview->get_editor()->attachArea()->parseAttachments( mail_error, msg, TRUE, 2); } else { // The first bodyPart was not handled. // It may not have been of type text. // The attachment pane needs to handle all the bodyParts // beginning with the first. newview->get_editor()->attachArea()->parseAttachments( mail_error, msg, TRUE, 1); } // Check for errors. // Manage the attach area to display attachments. if (mail_error.isSet()) {} // do something newview->get_editor()->manageAttachArea(); newview->activate_default_attach_menu(); } newview->set_to_top(); newview->auto_show_cursor_restore(); vmd_editor->enable_redisplay(); newview->manage(); } parent()->normalCursor(); } // defaultAction() gets called *after* extendedSelectionCallback() is // called. // Display the message in extendedSelectionCallback(). // viewInSeparateWindow() in defaultAction(). void MsgScrollingList::defaultAction(Widget, XtPointer, XmListCallbackStruct *cbs) { FORCE_SEGV_DECL(MsgStruct, tmpMS); DtMailEnv mail_error; // Initialize the mail_error. mail_error.clear(); _selected_item_position = cbs->item_position; tmpMS = get_message_struct(_selected_item_position); if (tmpMS == NULL) return; else { this->viewInSeparateWindow(mail_error); if (mail_error.isSet()) {} // Post dialog indicating error } } void MsgScrollingList::extendedSelectionCallback( Widget , // w XtPointer clientData, XmListCallbackStruct *cbs ) { int last_clicked_on_pos; int above_selected_pos; int tmp_selected_pos; int i, num_selected; DtMailEnv mail_error; // Initialize the mail_error. mail_error.clear(); Boolean IS_SELECTION = FALSE; Boolean SELECTION_MADE = FALSE; MsgScrollingList *obj=(MsgScrollingList *) clientData; // If all items have been deselected if (cbs->selected_item_count == 0) { obj->extended_selection(mail_error, 0); if (mail_error.isSet()) { return; } SELECTION_MADE = TRUE; } else { last_clicked_on_pos = cbs->item_position; // Check to see if this was a selection or deselection // We do that by seeing if last_clicked_on_pos is in the // selected_item_positions array. If it is, then it is a // selection; if its not there, then its a deselection. num_selected = cbs->selected_item_count; for (i = 0; i < num_selected; i++) { if (last_clicked_on_pos == cbs->selected_item_positions[i]) { // Yes, it was a selection IS_SELECTION = TRUE; obj->extended_selection(mail_error, last_clicked_on_pos); if (mail_error.isSet()) { return; } SELECTION_MADE = TRUE; } } // If it was not a selection, then we need to find the selected // item nearest to the deselected item. We have a choice there - // we can find the nearest one above, or the nearest one below // the deselected item. Let's find the nearest one above. // We do that by cruising through the selected_item_positions array. // Motif arranges the array in ascending order, no matter what order // you selected the items! If you deselect an item, the items on // either side of it are the ones above and below it. We use that // ordering to now select and display the one above it. // if (!IS_SELECTION) { // If the first selected item is below the deselected item, // it follows that all selected items are below the deselected // item. Display the first selected item and be done. if (cbs->selected_item_positions[0] > last_clicked_on_pos) { obj->extended_selection(mail_error, cbs->selected_item_positions[0]); if (mail_error.isSet()) { return; } SELECTION_MADE = TRUE; } // If the last selected item is above the deselected item, // it follows that all selected items are above the deselected // item. Display the last selected item and be done. else if (cbs->selected_item_positions[num_selected - 1] < last_clicked_on_pos) { obj->extended_selection(mail_error, cbs->selected_item_positions[num_selected - 1]); if (mail_error.isSet()) { return; } SELECTION_MADE = TRUE; } // Otherwise, the deselected item must lie in between other // selected items. We choose to find the closest selected // item above the deselected item. else { // There are selected items that are above the deselected // item. // Need to find the one that is both above the deselected // item and closest to it. // Iterate until you find the nearest above; select it and // drop out of loop when after selection. for (i = 0; i < (num_selected - 1) && !SELECTION_MADE; i++) { above_selected_pos = cbs->selected_item_positions[i]; tmp_selected_pos = cbs->selected_item_positions[i + 1]; if (tmp_selected_pos > last_clicked_on_pos) { obj->extended_selection(mail_error, above_selected_pos); if (mail_error.isSet()) { return; } SELECTION_MADE = TRUE; } } } } } } void MsgScrollingList::extended_selection( DtMailEnv &mail_error, int position ) { FORCE_SEGV_DECL(MsgStruct, tmpMS); // Disable the SaveAttachments menu item first. // It gets enabled when an attachment is selected. _parent->all_attachments_deselected(); if (position == 0) { // all items deselected. _parent->hideAttachArea(); this->display_no_message(); _selection_on = FALSE; _parent->deactivate_default_message_menu(); return; } // if there were no selected item(s) before and now we // have one/some, we need to turn on the message menu at // the parent level. if (!_selection_on) { _selection_on = TRUE; _parent->activate_default_message_menu(); } // If selected message is the displayed message, return. // No need to redisplay the same message. if (position == _selected_item_position) return; // Retrieve message... // Display the selected message... _selected_item_position = position; tmpMS = get_message_struct(_selected_item_position); if (tmpMS == NULL) { return; } else { display_message_summary(); this->display_message(mail_error, tmpMS->message_handle); if (mail_error.isSet()) { return; } } } int MsgScrollingList::get_selected_item() { return(_selected_item_position); } int MsgScrollingList::get_displayed_item() { return(_displayed_item_position); } void MsgScrollingList::scroll_to_bottom() { XmListSetBottomPos( this->baseWidget() , 0 ); } // Scroll the list so that the item at position is in the // middle of the scrolling list. If the number of items // that the scrolling list can display at one time is greater // than the total number of items, then display them all. // If the item at position is close to the bottom of the list // make sure we make as many items visible as possible. // // If the item at position is already visible, then don't // do anything. void MsgScrollingList::scroll_to_position( int position ) { int top, visible, total; int top_pos; // Determine the position of the header that we want to select. // If the XtArgCollector exists, then add // the resources to the XtArgCollector so that we can prevent // multiple redisplays in the XmList widget. if (_xmstr_collector) { XtVaGetValues( _w, XmNtopItemPosition, &top, XmNvisibleItemCount, &visible, NULL ); total = _xmstr_collector->GetNumItems(); } else { XtVaGetValues( _w, XmNtopItemPosition, &top, XmNvisibleItemCount, &visible, XmNitemCount, &total, NULL ); } if (( position < top ) || ( position >= top+visible )) { if ((total <= visible) || (position <= visible/2)) { // If we can display them all, make the first item appear // at the top of the list. top_pos = 1; } else if (position > (total-(visible/2))) { top_pos = total - visible + 1; } else { top_pos = position - visible/2 + 1; } // Determine the position of the header that we want to select. // If the XtArgCollector exists, then add // the resources to the XtArgCollector so that we can prevent // multiple redisplays in the XmList widget. if (_xtarg_collector) _xtarg_collector->AddItemToList ( XmNtopItemPosition, top_pos); else XmListSetPos (_w, top_pos); } } void MsgScrollingList::undelete_messages(MsgHndArray *tmpMHlist) { FORCE_SEGV_DECL(MsgStruct, tmpMS); FORCE_SEGV_DECL(XmString, deleted_headers); int i, num_entries, entry_position, del_pos; int whichToSelectDisplay = 0; DtMail::MailBox *mbox=parent()->mailbox(); DtMail::Message * tmpMsg; DtMailEnv mail_error; DtMailMessageHandle tmpMH; #ifdef undef XmString read_status, new_status; #endif XmString complete_header; // read status + glyph + header_text. #ifdef undef /* NL_COMMENT * In a mailer container window's message scrolling list, a "N" appears * to the left of a mail message header indicating that the mail message * is "new" (just arrived and not yet viewed by the user). * There is only space to display 1 character. If "N" needs to be translated, * please make sure the translation is only 1 character. */ // // gregl - new_status and read_status are not used in this function. // either comment them out (like I'm doing) or free them. // new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 112, "N")); read_status = XmStringCreateLocalized(" "); #endif // Initialize the mail_error. mail_error.clear(); num_entries = tmpMHlist->length(); if (num_entries == 0) return; // Deselect all items currently selected. // display_and_select_message() will select, highlight // and display the last "undeleted" message. XmListDeselectAllItems(_w); XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL); for (i = 0; i < num_entries; i++) { DtMailHeaderLine info; tmpMS = tmpMHlist->at(i); tmpMS->is_deleted = FALSE; // Reset the flag of the message in message store so that the // message will not be expunged when the folder is quit. // tmpMsg = mbox->getMessage(mail_error, tmpMS->message_handle); tmpMsg->resetFlag(mail_error, DtMailMessageDeletePending); tmpMH = tmpMS->message_handle; // Remove chosen item from list of deleted messages; // insert it back into _msgs at the right place (which is // determined by session_number of retrieved MsgStruct). // Insert back into scrolling list for visual display // at the position session_number. entry_position = _msgs->insert(tmpMS); // Increment by one, because the index into the scrolling // list is always one greater than the index into the // message handle array that we got the index from. entry_position = entry_position + 1; // Maintain the assumption that the item at entry_position // is the selected item _selected_item_position = entry_position; mbox->getMessageSummary(mail_error, tmpMS->message_handle, _header_info, info); DtMail::Message * msg = mbox->getMessage(mail_error, tmpMH); complete_header = formatHeader( info, tmpMS->indexNumber, show_with_attachments(msg), msg->flagIsSet(mail_error, DtMailMessageNew)); mbox->clearMessageSummary(info); if (msg->flagIsSet(mail_error, DtMailMessageNew) == DTM_TRUE) num_new_messages++; XmListAddItem(_w, complete_header, entry_position); XmListSelectItem(_w, complete_header, FALSE); XmStringFree(complete_header); // Get position of undeleted message structure in _deleted_messages // and remove the entry from _deleted_messages del_pos = _deleted_messages->indexof(tmpMS); _deleted_messages->remove_entry(del_pos); } // Display this message and select it. this->display_message(mail_error, tmpMS->message_handle); XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL); XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL); if (mail_error.isSet()) return; num_deleted_messages -= num_entries; MsgHndArray *selected_messages = selected(); updateListItems(-1, TRUE, selected_messages); delete selected_messages; display_message_summary(); } void MsgScrollingList::undelete_last_deleted() { FORCE_SEGV_DECL(MsgStruct, tmpMS); int entry_position; int len; DtMail::MailBox *mbox=parent()->mailbox(); UndelFromListDialog *undel_dialog; DtMailEnv mail_error; DtMailHeaderLine info; DtMail::Message * tmpMsg; DtMailMessageHandle tmpMH; #ifdef undef XmString read_status, new_status; #endif XmString complete_header; // read status + glyph + header_text. #ifdef undef /* NL_COMMENT * In a mailer container window's message scrolling list, a "N" appears * to the left of a mail message header indicating that the mail message * is "new" (just arrived and not yet viewed by the user). * There is only space to display 1 character. If "N" needs to be translated, * please make sure the translation is only 1 character. */ new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 113, "N")); read_status = XmStringCreateLocalized(" "); #endif if (num_deleted_messages == 0) return; // Initialize the mail_error. mail_error.clear(); // Delete the message from the Deleted Messages Dialog. undel_dialog = parent()->get_undel_dialog(); if (undel_dialog) undel_dialog->undelLast(); // Restore the message in RoamMenuWindow:MessageScrollingList. len = _deleted_messages->length(); tmpMS = _deleted_messages->at(len - 1); tmpMS->is_deleted = FALSE; // Reset the flag of the message in message store so that the // message will not be expunged when the folder is quit. // tmpMsg = mbox->getMessage(mail_error, tmpMS->message_handle); tmpMsg->resetFlag(mail_error, DtMailMessageDeletePending); tmpMH = tmpMS->message_handle; // Remove chosen item from list of deleted messages; // insert it back into _msgs at the right place (which is // determined by session_number of retrieved MsgStruct). // Insert back into scrolling list for visual display // at the position session_number. entry_position = _msgs->insert(tmpMS); // Increment by one, because the index into the scrolling // list is always greater than the index into the // message handle array that we got the index from. entry_position = entry_position + 1; mbox->getMessageSummary(mail_error, tmpMS->message_handle, _header_info, info); DtMail::Message * msg = mbox->getMessage(mail_error, tmpMH); complete_header = formatHeader( info, tmpMS->indexNumber, show_with_attachments(msg), msg->flagIsSet(mail_error, DtMailMessageNew)); mbox->clearMessageSummary(info); if (msg->flagIsSet(mail_error, DtMailMessageNew) == DTM_TRUE) { num_new_messages++; } _deleted_messages->remove_entry(len - 1); num_deleted_messages--; XmListAddItemUnselected(_w, complete_header, entry_position); XmStringFree(complete_header); // If the undeleted message is before the currently viewed message, // then need to readjust our numbers by adding one to them -- there // is one more message above the currently-viewed message. // Don't need to do anything if the undeleted message is after the // currently-viewed message. // Deselect all items currently selected. // display_and_select_message() will select, highlight // and display the last "undeleted" message. XmListDeselectAllItems(_w); _selected_item_position = entry_position; this->display_and_select_message(mail_error, tmpMS->message_handle); if (mail_error.isSet()) return; updateListItems(-1, TRUE, NULL); display_message_summary(); } DtMailBoolean MsgScrollingList::senderIsToHeaderWhenMailFromMe(void) { if (_header_info.number_of_names == 5) return(DTM_TRUE); return(DTM_FALSE); } void MsgScrollingList::checkDisplayProp(void) { DtMail::MailBox * mbox; DtMail::MailRc * mailrc; DtMailEnv mail_error; Boolean state_changed = FALSE; mbox = parent()->mailbox(); mailrc = mbox->session()->mailRc(mail_error); const char * value = NULL; mailrc->getValue(mail_error, "showto", &value); if (mail_error.isNotSet()) { // showto is set...if number_of_names is 4 then they // just applied props and the showto value if (_header_info.number_of_names == 4) { _header_info.number_of_names = 5; state_changed = TRUE; } } else if (_header_info.number_of_names == 5) { // They just applied props and changed the showto value _header_info.number_of_names = 4; state_changed = TRUE; } if (NULL != value) free((void*) value); // Here we need to adjust the header labels to maintain them left // justified. It looks better than centered. We only whant to change // the labels if message numbering has been enabled. Otherwise we will // leave them alone. Note: The _numbered stores the previous state for // message numbering. Check the error return when getting the value uses // reverse logic. ugly... DtMailBoolean use_msg_numbers; value = NULL; mail_error.clear(); mailrc->getValue(mail_error, "showmsgnum", &value); use_msg_numbers = (mail_error.isNotSet()) ? DTM_TRUE : DTM_FALSE; if (NULL != value) free((void*) value); if (use_msg_numbers != _numbered) { _numbered = use_msg_numbers; layoutLabels(_sender_lbl, _subject_lbl, _date_lbl, _size_lbl); } else if (!state_changed) return; // We have to build two lists from the current normal and deleted // lists. These will contain the new header lines. // DtMailHeaderLine info; XmString * normal_list = new XmString[_msgs->length()]; for (int m = 0; m < _msgs->length(); m++) { MsgStruct * ms = _msgs->at(m); DtMail::Message * msg = mbox->getMessage(mail_error, ms->message_handle); mbox->getMessageSummary( mail_error, ms->message_handle, _header_info, info); normal_list[m] = formatHeader( info, ms->indexNumber, show_with_attachments(msg), msg->flagIsSet(mail_error, DtMailMessageNew)); mbox->clearMessageSummary(info); } XmListReplaceItemsPos(_w, normal_list, _msgs->length(), 1); for (int fr = 0; fr < _msgs->length(); fr++) { XmStringFree(normal_list[fr]); } delete normal_list; UndelFromListDialog * del_dialog = _parent->get_undel_dialog(); if (del_dialog) { XmString * del_list = new XmString[_deleted_messages->length()]; for (int m2 = 0; m2 < _deleted_messages->length(); m2++) { MsgStruct * ms = _deleted_messages->at(m2); DtMail::Message * msg = mbox->getMessage(mail_error, ms->message_handle); mbox->getMessageSummary( mail_error, ms->message_handle, _header_info, info); del_list[m2] = formatHeader( info, ms->indexNumber, show_with_attachments(msg), msg->flagIsSet(mail_error, DtMailMessageNew)); mbox->clearMessageSummary(info); } del_dialog->replaceItems(del_list, _deleted_messages->length()); for (int fr2 = 0; fr2 < _deleted_messages->length(); fr2++) { XmStringFree(del_list[fr2]); } delete del_list; } } // // Update the scrolling list. Current is the index of the message // to position the scrolling list to. -1 to keep it as is. // void MsgScrollingList::updateListItems(int current, Boolean renumber_only, MsgHndArray *selected_messages) { DtMail::MailBox * mbox = NULL; DtMail::MailRc * mailrc; DtMailEnv mail_error; const char * value = NULL; int nmsgs; mbox = parent()->mailbox(); mailrc = mbox->session()->mailRc(mail_error); if (current < 0) current = _displayed_item_position; resetIndexNums(); mailrc->getValue(mail_error, "showmsgnum", &value); if (mail_error.isSet() && renumber_only) return; if (selected_messages) XtVaSetValues (_w, XmNselectionPolicy, XmMULTIPLE_SELECT, NULL); // // We need to build a new list of strings to display // in the scrolling list. Initialize that now. // DtMailHeaderLine info; XmString * newList; MsgStruct *ms; nmsgs = _msgs->length(); newList = new XmString[nmsgs]; memset (newList, 0, nmsgs * sizeof (XmString *)); // Loop through _msgs and create new strings to display in the // scrolling list. This is inefficient and dominates the time // spent in sort. It may be worth while finding a way to just // rearrange the existing strings. for (int m = 0; m < nmsgs; m++) { DtMail::Message * msg = mbox->getMessage(mail_error, _msgs->at(m)->message_handle); if (mail_error.isSet()) fprintf(stderr, "dtmail: getMessage: Couldn't get message #%d\n", m); mbox->getMessageSummary( mail_error, _msgs->at(m)->message_handle, _header_info, info); if (mail_error.isSet()) fprintf(stderr, "dtmail: getMessageSummary: Couldn't get summary for msg # %d\n", m); if (msg == NULL) { // Error ; } else { ms = get_message_struct(m + 1); newList [m] = formatHeader( info, ms->indexNumber, show_with_attachments(msg), msg->flagIsSet(mail_error, DtMailMessageNew)); } // Free the space allocated for info // delete []info.header_values; mbox->clearMessageSummary(info); } XmListReplaceItemsPos(_w, newList, session_message_number, 1); for (int fr = 0; fr < nmsgs; fr++) XmStringFree(newList[fr]); // Update current message _selected_item_position = current; _displayed_item_position = current; scroll_to_position(_displayed_item_position); if (selected_messages) { XmListDeselectAllItems(_w); for (int m=0, nselected=selected_messages->length(); mat(s); if (ms == sms) XmListSelectPos(_w, m+1, FALSE); } } XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL); } else XmListSelectPos(_w, _displayed_item_position, FALSE); XtVaSetValues (_w, XmNselectionPolicy, XmEXTENDED_SELECT, NULL); XtVaSetValues (_w, XmNselectionMode, XmNORMAL_MODE, NULL); delete newList; } XmString MsgScrollingList::formatHeader(DtMailHeaderLine & info, int sess_num, DtMailBoolean has_attachments, DtMailBoolean new_msg) { char *buf = new char[BUFSIZ]; memset(buf, 0, BUFSIZ); const char *from=NULL; char *subject; int contentLength; char contentStr[20]; char *date = new char[BUFSIZ]; int msg_num = sess_num + 1; static XmString attachment_glyph = NULL; static XmString no_attachment_glyph = NULL; static XmString new_status = NULL; static XmString read_status = NULL; static unsigned char attach_symbol[16]; Boolean showto = FALSE; if (!attachment_glyph) { attach_symbol[0] = 168; attach_symbol[1] = 0; attachment_glyph = XmStringCreate((char *)attach_symbol, "attach"); no_attachment_glyph = XmStringCreateLocalized(" "); new_status = XmStringCreateLocalized(GETMSG(DT_catd, 1, 114, "N")); read_status = XmStringCreateLocalized(" "); } // strip out the Name of sender and retain only the address. //Later, we will have separate entries for name and address //and this stripping will not need to be done. // IMAP is incapable of handling a message header that begins // with From (space) (NOTE: not a From:). // Thus, if a message has only a From but does not have a From: // or a Reply-To: or Received-From:, IMAP doesn't tell us who // it is from. In such cases, we set it to "???". DtMailAddressSeq * addr_seq = NULL; DtMailValueAddress * addr = NULL; if (info.header_values[0].length() != 0) { addr_seq = ((info.header_values[0])[0])->toAddress(); addr = (*addr_seq)[0]; if (_header_info.number_of_names == 5 && addr && addr->dtm_address && info.header_values[4].length() != 0) { // Check if mail is from me const char *ptr; passwd pw; GetPasswordEntry(pw); if ((ptr = strchr(addr->dtm_address, '@')) != NULL) { if (strncmp(pw.pw_name, addr->dtm_address, ptr-addr->dtm_address) == 0) { from = *((info.header_values[4])[0]); showto = TRUE; } } else if (strcmp(pw.pw_name, addr->dtm_address) == 0) { from = *((info.header_values[4])[0]); showto = TRUE; } } if (from == NULL) { if (addr && addr->dtm_person) from = addr->dtm_person; else if (addr && addr->dtm_address) from = addr->dtm_address; else from = "???"; } } else from = "???"; // If the Subject is nil if (info.header_values[3].length() == 0) { subject = new char[1]; subject[0] = '\0'; } else { // Get the BE store of header. It may contain newlines or // tab chars which can munge the scrolling list's display! // const char *real_subj_header = *((info.header_values[3])[0]); int fc; int subj_len; char *tmp_subj; // Check if BE store contains the funky chars. for (fc = 0, subj_len = strlen(real_subj_header), tmp_subj = (char *)real_subj_header; fc < subj_len; fc++, tmp_subj++) { char c = *tmp_subj; if ( (c == '\n') || (c == '\t') || (c == '\r')) { break; } } subject = new char[fc+1]; strncpy((char *)subject, real_subj_header, fc); subject[fc] = '\0'; } // Skip the first (beginning) space in from; search for the next // occurring space. const char *dateformat; if (info.header_values[1].length() > 0) { DtMailValueDate ds = ((info.header_values[1])[0])->toDate(); #define USE_MAX_TZ_SECONDS (60 * 60 * 12) if (ds.dtm_date && ds.dtm_tz_offset_secs >= -USE_MAX_TZ_SECONDS && ds.dtm_tz_offset_secs <= USE_MAX_TZ_SECONDS) { #define USE_YEAR_FORMAT_SECONDS (60 * 60 * 24 * 180) time_t now; tm tm_struct; SafeLocaltime(&ds.dtm_date, tm_struct); // Refer to strftime man page for explanation of the date format. now = time(NULL); if (USE_YEAR_FORMAT_SECONDS < now - ds.dtm_date) dateformat = GETMSG(DT_catd, 1, 259, "%a %b %d %Y"); else { #ifdef sun dateformat = GETMSG(DT_catd, 1, 260, "%a %b %d %k:%M"); #else dateformat = GETMSG(DT_catd, 1, 261, "%a %b %d %H:%M"); #endif } SafeStrftime(date, BUFSIZ, dateformat, &tm_struct); } else // Couldn't get Date string from Message. Make it empty. sprintf(date, "%s", " "); } else { tm epoch; memset(&epoch, 0, sizeof(tm)); /* Refer to strftime man page for explanation of the date format. */ dateformat = GETMSG(DT_catd, 1, 259, "%a %b %d %Y"); SafeStrftime(date, BUFSIZ, dateformat, &epoch); } if (info.header_values[2].length() > 0) { contentLength = (int) strtol(*((info.header_values[2])[0]), NULL, 10); } else { contentLength = 0; } if (contentLength < 1000) { sprintf(contentStr, "%d", contentLength); } else if (contentLength < 1000000) { sprintf(contentStr, "%dK", contentLength / 1000); } else { sprintf(contentStr, "%dM", contentLength / 1000000); } // If we are to print the message_number in the header_list, // use msg_num as the first element in the sprintf. // Introduce a %d at the beginning though. DtMail::MailBox * mbox; DtMail::MailRc * mailrc; DtMailEnv mail_error; mbox = parent()->mailbox(); mailrc = mbox->session()->mailRc(mail_error); const char * value = NULL; mailrc->getValue(mail_error, "showmsgnum", &value); if (mail_error.isSet()) { // No message numbers ... keep usual "35" col. "Subject". if (showto) sprintf(buf, " To %-15.15s %-35.35s %-17.17s %-5.5s", from, subject, date, contentStr); else sprintf(buf, " %-18.18s %-35.35s %-17.17s %-5.5s", from, subject, date, contentStr); } else { // To keep 80 column format use 5 less columns of // subject , when msg numbers are on. if (showto) sprintf(buf, " To %-15.15s %-30.30s %-17.17s %-5.5s", from, subject, date, contentStr); else sprintf(buf, " %-18.18s %-30.30s %-17.17s %-5.5s", from, subject, date, contentStr); } if (NULL != value) free((void*) value); XmString header_text = XmStringCreateLocalized(buf); XmString item, item2, complete_header; if (has_attachments == DTM_TRUE) { item = XmStringConcat(attachment_glyph, header_text); XmStringFree(header_text); } else { item = XmStringConcat(no_attachment_glyph, header_text); XmStringFree(header_text); } if (new_msg == DTM_FALSE) { item2 = XmStringConcat(read_status, item); XmStringFree(item); } else { item2 = XmStringConcat(new_status, item); XmStringFree(item); } value = NULL; mailrc->getValue(mail_error, "showmsgnum", &value); if (mail_error.isSet()) { // complete_header = XmStringCopy(item2); complete_header = item2; _numbered = DTM_FALSE; } else { char num_buf[64]; if (NULL != value) free((void*) value); mailrc->getValue(mail_error, "nerdmode", &value); if (mail_error.isSet()) { sprintf(num_buf, "%4d ", msg_num); } else { sprintf(num_buf, "%4x ", msg_num - 1); } XmString num_str = XmStringCreateLocalized(num_buf); complete_header = XmStringConcat(num_str, item2); XmStringFree(item2); XmStringFree(num_str); _numbered = DTM_TRUE; } if (NULL != value) free((void*) value); delete addr_seq; delete subject; delete [] buf; delete [] date; return(complete_header); } void MsgScrollingList::shutdown() { int num_entries, i; FORCE_SEGV_DECL(MsgStruct, tmpMS); DtMailMessageHandle tmpMH; DtMail::MailBox *mbox=parent()->mailbox(); DtMailEnv mail_error; // Initialize the mail_error. mail_error.clear(); if (num_deleted_messages == 0) return; num_entries = _deleted_messages->length(); for (i = 0; i < num_entries; i++) { tmpMS = _deleted_messages->at(i); tmpMH = tmpMS->message_handle; // mbox->deleteMsg(mail_error, tmpMH); } } #ifdef DEAD_WOOD DtMailMessageHandle MsgScrollingList::lastMsg() { if (_msgs->length() > 0) { return((_msgs->at(_msgs->length() - 1))->message_handle); } else { // Currently an empty folder return(NULL); } } #endif /* DEAD_WOOD */ void MsgScrollingList::clearMsgs() { if ( _msgs->length() > 0 ) _msgs->clear(); } void MsgScrollingList::display_message_summary() { parent()->message_summary( selected_item_position(), get_num_messages(), get_num_new_messages(), get_num_deleted_messages()); } void MsgScrollingList::display_message( DtMailEnv &mail_error, int pos ) { if ((pos > 0) && (pos <= session_message_number)) { // When loading mail headers, we need to make sure that // the XmList resources are all set at the same time to // avoid painting multiple times. So here we collect // these resources. if (_xtarg_collector && _xmstr_collector) { XmString *items = _xmstr_collector->GetItems(); // Keep a handle to the malloced string copy so that // we can free it after the XtSetValues _selected_items = XmStringCopy (items[pos-1]); _xtarg_collector->AddItemToList (XmNselectedItems, (XtArgVal) &_selected_items); _xtarg_collector->AddItemToList ( XmNselectedItemCount, 1); } else { XmListSelectPos(_w, pos, FALSE); } this->extended_selection(mail_error, pos); if (mail_error.isSet()) { // Return whatever error mailbox->get_next_msg() returned. return; } } else return; } DtMailMessageHandle MsgScrollingList::current_msg_handle() { DtMailMessageHandle msg_number; if ( _displayed_item_position > 0 ) msg_number=msgno( _displayed_item_position ); else msg_number=NULL; return msg_number; } void MsgScrollingList::expunge(void) { for (int i = _deleted_messages->length() - 1; i >= 0; i--) { _deleted_messages->remove_entry(i); } resetSessionNums(); resetIndexNums(); num_deleted_messages = 0; display_message_summary(); } int MsgScrollingList::resetIndexNums(void) { int m; int length = _msgs->length(); MsgStruct *ms; for (m = 0; m < length; m++) { ms = _msgs->at(m); ms->indexNumber = m; } length = _deleted_messages->length(); for (m = 0; m < length; m++) { ms = _deleted_messages->at(m); ms->indexNumber = m; } return m; } int MsgScrollingList::resetSessionNums(void) { int m; int length = _msgs->length(); MsgStruct *ms; for (m = 0; m < length; m++) { ms = _msgs->at(m); ms->sessionNumber = m; } session_message_number = m; return session_message_number; } void MsgScrollingList::sort_messages(void) { DtMail::MailBox *mbox; enum sortBy sortby; int current_msg; MsgHndArray *selected_messages = selected(); mbox = _parent->mailbox(); sortby = _parent->last_sorted_by(); // Sort array of message handles current_msg = _sorter->sortMessages(this, mbox, sortby); // The array of message handles is sorted. Now we need to update // the display preserving the selected state of the messages. updateListItems(current_msg, FALSE, selected_messages); delete selected_messages; _parent->last_sorted_by(sortby); display_message_summary(); } // // Layout out the row of labels above the scrolling list // void MsgScrollingList::layoutLabels( Widget sender, Widget subject, Widget date, Widget size) { // Save the input values _sender_lbl = sender; _subject_lbl = subject; _date_lbl = date; _size_lbl = size; layoutLabels(); } // // Layout out the row of labels above the scrolling list // void MsgScrollingList::layoutLabels() { // Width of fields. +1 for spaces int num_width = 5, sender_width = 18 + 1, subject_width = 35 + 1, date_width = 3+1 + 3+1 + 2+1 + 5 + 2; // DDD mmm dd hh:mm 2spaces int char_width; // Width of a single character int n = 0; XmString xmstr; XmFontList font_list; // Calculate the width of date format to allocate the label // width dynamically struct tm *tm; time_t clock; char buf[40]; clock = time((time_t *) 0); tm = localtime(&clock); #ifdef sun SafeStrftime(buf, sizeof(buf), GETMSG(DT_catd, 1, 222, "%a %b %d %k:%M"), tm); #else SafeStrftime(buf, sizeof(buf), GETMSG(DT_catd, 1, 223, "%a %b %d %H:%M"), tm); #endif // List uses a fixed width font. Therefore all characters are the // same size. So we use a space to determine the width of a char xmstr = XmStringCreateLocalized(" "); XtVaGetValues(_w, XmNfontList, &font_list, NULL); char_width = XmStringWidth(font_list, xmstr); if (_numbered) { // Numbering is on n = num_width; subject_width -= num_width; } else { n = 0; } // XXX dipol: Need to take into account if the scrollbar is on the // right or left n += 3; // Margin. XtWidgetGeometry geom; geom.request_mode = CWX; XtQueryGeometry(_sender_lbl, NULL, &geom); if (geom.x != (n * char_width)) { geom.x = (n * char_width); XtMoveWidget(_sender_lbl, geom.x, geom.y); } // Move the X location of the sender title XtVaSetValues(_sender_lbl, XmNx, n * char_width, NULL); n += sender_width; XtQueryGeometry(_subject_lbl, NULL, &geom); if (geom.x != (n * char_width)) { geom.x = (n * char_width); XtMoveWidget(_subject_lbl, geom.x, geom.y); } // Move the X location of the subject title XtVaSetValues(_subject_lbl, XmNx, n * char_width, NULL); n += subject_width; XtVaSetValues(_date_lbl, XmNx, n * char_width, NULL); n += date_width; XtVaSetValues(_size_lbl, XmNx, n * char_width, NULL); XmStringFree(xmstr); return; }