/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ /* FolderDropdown.cpp --- a combo box for selecting folders/newsgroups Created: Chris Toshok , 6-Mar-97 */ #include "FolderFrame.h" #include "FolderDropdown.h" #include "MozillaApp.h" #undef DEBUG_dora #ifdef DEBUG_toshok #define D(x) x #else #define D(x) #endif #ifdef DEBUG_dora #define DD(x) x #else #define DD(x) #endif const char * XFE_FolderDropdown::folderSelected = "XFE_FolderDropdown::folderSelected"; XFE_FolderDropdown::XFE_FolderDropdown(XFE_Component *toplevel_component, Widget parent, XP_Bool allowServerSelection, XP_Bool showNewsgroups, XP_Bool boldWithNew, XP_Bool showFolders) : XFE_Component(toplevel_component) { m_popupServer = True; m_master = fe_getMNMaster(); Widget combo; Colormap cmap; Cardinal depth; Visual *v; XtVaGetValues(getToplevel()->getBaseWidget(), XmNvisual, &v, XmNcolormap, &cmap, XmNdepth, &depth, NULL); m_allowServerSelection = allowServerSelection; m_showNewsgroups = showNewsgroups; m_boldWithNew = boldWithNew; m_foldersHaveChanged = FALSE; m_showFolders = showFolders; combo = XtVaCreateWidget("folderDropdown", dtComboBoxWidgetClass, parent, XmNshadowThickness, 1, XmNmarginWidth, 0, XmNmarginHeight, 0, XmNarrowType, XmMOTIF, XmNtype, XmDROP_DOWN_LIST_BOX, XmNcolumns, 30, XmNvisibleItemCount, 20, XmNvisual, v, XmNcolormap, cmap, XmNdepth, depth, NULL); m_lastSelectedPosition = 0; m_numNewsHosts = 0; m_numinfos = 0; m_infos = 0; XFE_MozillaApp::theApp()->registerInterest(XFE_MNView::foldersHaveChanged, this, (XFE_FunctionNotification)rebuildFolderDropdown_cb); XFE_MozillaApp::theApp()->registerInterest(XFE_MNView::newsgroupsHaveChanged, this, (XFE_FunctionNotification)rebuildFolderDropdown_cb); if (m_boldWithNew) XFE_MozillaApp::theApp()->registerInterest(XFE_MNView::folderChromeNeedsUpdating, this, (XFE_FunctionNotification)boldFolderInfo_cb); XtAddCallback(combo, XmNselectionCallback, folderSelect_cb, this); XtAddCallback(combo, XmNmenuPostCallback, folderMenuPost_cb, this); setBaseWidget(combo); installDestroyHandler(); resyncDropdown(); } XFE_FolderDropdown::~XFE_FolderDropdown() { if (m_infos) delete [] m_infos; XFE_MozillaApp::theApp()->unregisterInterest(XFE_MNView::foldersHaveChanged, this, (XFE_FunctionNotification)rebuildFolderDropdown_cb); XFE_MozillaApp::theApp()->unregisterInterest(XFE_MNView::newsgroupsHaveChanged, this, (XFE_FunctionNotification)rebuildFolderDropdown_cb); if (m_boldWithNew) XFE_MozillaApp::theApp()->unregisterInterest(XFE_MNView::folderChromeNeedsUpdating, this, (XFE_FunctionNotification)boldFolderInfo_cb); } void XFE_FolderDropdown::setPopupServer(XP_Bool p) { m_popupServer = p; } MSG_FolderInfo * XFE_FolderDropdown::getSelectedFolder() { int info; if (m_infos == NULL) return NULL; XtVaGetValues(m_widget, XmNselectedPosition, &info, NULL); if (info < 0 || info > m_numinfos) return NULL; else return m_infos[info]; } void XFE_FolderDropdown::selectFolder(MSG_FolderInfo *info, Boolean notify) { int i; if (m_foldersHaveChanged) { m_foldersHaveChanged = FALSE; resyncDropdown(); } for (i = 0; i < m_numinfos; i ++) { if (info == m_infos[i]) { if ( notify ) folderSelect(i); else { m_lastSelectedPosition = i; } XtVaSetValues(m_widget, XmNselectedPosition, i, 0); return; } } /* we might really want to know if the folder isn't there in the list, but this actually can happen frequently if the user deletes folders */ //XP_ASSERT(0); } void XFE_FolderDropdown::buildList(MSG_FolderInfo *info, int *position) { int num_children = MSG_GetFolderChildren(m_master, info, NULL, 0); #ifdef DEBUG_dora // For debugging P0 bug on XP only. MSG_FolderLine line; printf("buildList %x\n", info); printf("num_children %d\n", num_children); if ( info ) { MSG_GetFolderLineById(m_master, info, &line); if ( line.name) printf("folder name=%s\n", line.name); else printf("no folder name...\n"); } #endif if (num_children == 0) { return; } else { MSG_FolderInfo **tmp; int i; tmp = new MSG_FolderInfo* [num_children]; /* first we handle local mail stuff. */ MSG_GetFolderChildren(m_master, info, tmp, num_children); for (i = 0; i < num_children; i ++) { if (*position >= m_numinfos) return; m_infos[*position] = tmp[i]; *position += 1; XP_ASSERT(tmp[i]); // don't recurse if we're at the top level (info==NULL) // and we only want servers (m_showFolders=False) if (info || m_showFolders) buildList(tmp[i], position); } delete [] tmp; } } void XFE_FolderDropdown::syncFolderList() { int num_infos; if (m_infos) delete [] m_infos; /* this should be the number of MSG_FolderInfo's for mail, including servers. */ num_infos = MSG_GetFoldersWithFlag(m_master, MSG_FOLDER_FLAG_MAIL, NULL, 0); if (m_showNewsgroups) { num_infos += MSG_GetFoldersWithFlag(m_master, MSG_FOLDER_FLAG_NEWSGROUP, NULL, 0); m_numNewsHosts = MSG_GetFoldersWithFlag(m_master, MSG_FOLDER_FLAG_NEWS_HOST, NULL, 0); num_infos += m_numNewsHosts; } m_numinfos = num_infos; m_infos = new MSG_FolderInfo* [num_infos]; int pos = 0; /* build the default mail tree */ buildList(NULL, &pos); /* now we build each newshost */ if (m_numNewsHosts > 0) { MSG_FolderInfo **news_hosts; int i; news_hosts = new MSG_FolderInfo* [m_numNewsHosts]; MSG_GetFoldersWithFlag(m_master, MSG_FOLDER_FLAG_NEWS_HOST, news_hosts, m_numNewsHosts); for (i = 0; i < m_numNewsHosts; i++) buildList(news_hosts[i], &pos); delete [] news_hosts; } DD(printf("\nold m_numinfos = %d new numinfos %d\n", m_numinfos, pos);) m_numinfos = pos; } void XFE_FolderDropdown::syncCombo() { int i; XmStringTable xmstrings; XmStringTable old_xmstrings = NULL; DtComboBoxDeleteAllItems(m_widget); m_lastSelectedPosition = -1; D(printf ("In syncCombo(). There are %d entries\n", m_numinfos);) xmstrings = (XmString*)XtCalloc(m_numinfos, sizeof(XmString)); for (i = 0; i < m_numinfos; i ++) { MSG_FolderLine line; if ( !m_infos[i] ) { // This block is just workaround for temp fix on the P0 crash // bug when trying to bring up ThreadWindow. // This is to prevent a P0 crash in XP code. // We found that the all (subscribed & non-subscribed) news // group was parsed when we tried to genterate a // Combobox of all subscribed newsgroup. // MSG_GetFolderChildren() should not try to parse // unsubscribed newsgroup.... // David B. will investigate this further DD(printf("[XFE_FolderDropdown Warning:]! Hitting NULL FolderInfo\n");) for (i = 0; i < m_numinfos; i ++) { if (xmstrings[i]) XmStringFree(xmstrings[i]); } XtFree((char*)xmstrings); return; } MSG_GetFolderLineById(m_master, m_infos[i], &line); xmstrings[i] = makeListItemFromLine(&line); } XtVaGetValues(m_widget, XmNitems, &old_xmstrings, NULL); if (old_xmstrings) XtFree((char*)old_xmstrings); XtVaSetValues(m_widget, XmNitems, xmstrings, XmNitemCount, m_numinfos, NULL); } void XFE_FolderDropdown::resyncDropdown() { MSG_FolderInfo *saved_info = getSelectedFolder(); syncFolderList(); syncCombo(); if (saved_info) selectFolder(saved_info); } XFE_CALLBACK_DEFN(XFE_FolderDropdown, rebuildFolderDropdown)(XFE_NotificationCenter *, void *, void *) { resyncDropdown(); m_foldersHaveChanged = TRUE; } XFE_CALLBACK_DEFN(XFE_FolderDropdown, boldFolderInfo)(XFE_NotificationCenter *, void *, void *cd) { /* This is wasteful and duplicates much of syncCombo above, but only for one folder info */ MSG_FolderInfo *info = (MSG_FolderInfo*)cd; MSG_FolderLine line; int index; Widget list; Boolean isSelected; if (m_foldersHaveChanged) { m_foldersHaveChanged = FALSE; resyncDropdown(); } for (index = 0; index < m_numinfos; index ++) { if (info == m_infos[ index ]) { break; } } /* not found */ if (index == m_numinfos) return; if (!MSG_GetFolderLineById(m_master, info, &line)) return; XmString both = makeListItemFromLine(&line); XtVaGetValues(m_widget, XmNlist, &list, NULL); isSelected = XmListPosSelected(list, index + 1); XmListReplaceItemsPos(list, &both, 1, index + 1); if (isSelected) XmListSelectPos(list, index + 1, False); XmStringFree(both); } void XFE_FolderDropdown::folderSelect(int row) { MSG_FolderLine line; XP_ASSERT(row >= 0 && row < m_numinfos); if (!MSG_GetFolderLineById(m_master, m_infos[row], &line)) return; if (line.level == 1) // it's a server ' { if (m_popupServer) fe_showFoldersWithSelected(XtParent(getToplevel()->getBaseWidget()), NULL, NULL, m_infos[row]); if (!m_allowServerSelection) /* do not allow select; revert it! */ row = m_lastSelectedPosition; if (row == -1) /* We shall loop through our list to * retrieve the first non-server entry instead of hard-wire it... */ row = 0; XtVaSetValues(m_widget, XmNselectedPosition, row, NULL); } m_lastSelectedPosition = row; notifyInterested(XFE_FolderDropdown::folderSelected, (void*)m_infos[row]); } void XFE_FolderDropdown::folderMenuPost() { if (m_foldersHaveChanged) { m_foldersHaveChanged = FALSE; resyncDropdown(); } } void XFE_FolderDropdown::folderSelect_cb(Widget, XtPointer clientData, XtPointer cd) { DtComboBoxCallbackStruct *cbs = (DtComboBoxCallbackStruct*)cd; XFE_FolderDropdown *obj = (XFE_FolderDropdown*)clientData; obj->folderSelect(cbs->item_position); } void XFE_FolderDropdown::folderMenuPost_cb(Widget, XtPointer clientData, XtPointer) { XFE_FolderDropdown *obj = (XFE_FolderDropdown*)clientData; obj->folderMenuPost(); } XmString XFE_FolderDropdown::makeListItemFromLine(MSG_FolderLine* line) { char buf[500]; memset(buf, 32, line->level - 1); buf[line->level - 1] = 0; XmString space; space = XmStringCreate(buf, "BOLD"); if (line->prettyName) PR_snprintf(buf, sizeof(buf), "%s", line->prettyName); else PR_snprintf(buf, sizeof(buf), "%s", line->name); XmString xmstr; if (m_boldWithNew && line->unseen > 0) xmstr = XmStringCreate(buf, "BOLD"); else xmstr = XmStringCreate(buf, XmFONTLIST_DEFAULT_TAG); XmString both; both = XmStringConcat(space, xmstr); XmStringFree(space); XmStringFree(xmstr); return both; } void XFE_FolderDropdown::selectFolder(char *name) { if (name && XP_STRLEN(name)) { const char *this_folder_name; for (int i=0; i < m_numinfos; i++) { MSG_FolderLine line; MSG_GetFolderLineById(m_master, m_infos[i], &line); this_folder_name = MSG_GetFolderNameFromID(m_infos[i]); if (this_folder_name && (XP_STRCMP(this_folder_name,name) == 0)) { XP_ASSERT(line.prettyName || line.name); XmString both = makeListItemFromLine(&line); DtComboBoxSelectItem(m_widget, both); XmStringFree(both); m_lastSelectedPosition = i; break; }/* if */ }/* for i*/ }/* if */ }