556 lines
12 KiB
C++
556 lines
12 KiB
C++
/* -*- 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 <toshok@netscape.com>, 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 */
|
|
}
|
|
|
|
|