mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
3265 lines
108 KiB
C++
3265 lines
108 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) 1997 Netscape Communications Corporation. All Rights
|
||
* Reserved.
|
||
*/
|
||
|
||
|
||
// Created 3/25/96 - Tim Craycroft
|
||
|
||
#include "CThreadView.h"
|
||
|
||
// App specific
|
||
#include "resgui.h"
|
||
#include "macutil.h"
|
||
#include "Netscape_Constants.h"
|
||
#include "CBrowserContext.h"
|
||
|
||
#include "libi18n.h"
|
||
|
||
// Mail/News Specific
|
||
#include "CNewsSubscriber.h"
|
||
#include "Netscape_Constants.h"
|
||
#include "CMailNewsContext.h"
|
||
#include "CMessageWindow.h"
|
||
#include "CMessageView.h"
|
||
#include "MailNewsgroupWindow_Defines.h"
|
||
#include "LTableViewHeader.h"
|
||
#include "CThreadWindow.h"
|
||
#include "UMailFolderMenus.h"
|
||
#include "CMailFolderButtonPopup.h"
|
||
#include "UMessageLibrary.h"
|
||
#include "CMailProgressWindow.h"
|
||
#include "CProgressListener.h"
|
||
#include "UMailSelection.h"
|
||
|
||
// utility
|
||
#include "UPropFontSwitcher.h"
|
||
#include "UUTF8TextHandler.h"
|
||
#include "SearchHelpers.h"
|
||
#include "CDrawingState.h"
|
||
#include "URobustCreateWindow.h"
|
||
#include "CApplicationEventAttachment.h"
|
||
// remove next line when we move the PlaceUTF8TextInRect to utility classes
|
||
#include "UGraphicGizmos.h"
|
||
#include "CTargetedUpdateMenuRegistry.h"
|
||
#include "CProxyDragTask.h"
|
||
#include "UNewFolderDialog.h"
|
||
#include "UDeferredTask.h"
|
||
|
||
// XP
|
||
#include "ntypes.h"
|
||
#include "msg_srch.h" // for MSG_GetPriorityName, MSG_GetStatusName
|
||
|
||
// application
|
||
#include "uapp.h"
|
||
#include "prefapi.h"
|
||
#include "secnav.h"
|
||
#include "shist.h"
|
||
|
||
// PowerPlant
|
||
#include <LTableMultiSelector.h>
|
||
#include <LTableArrayStorage.h>
|
||
#include <URegions.h>
|
||
#include <LDropFlag.h>
|
||
#include <LSharable.h>
|
||
#include <UModalDialogs.h>
|
||
|
||
// Mac
|
||
#include <Drag.h>
|
||
|
||
// ANSI
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
#define kPriorityMenuID 10528
|
||
|
||
enum {
|
||
kThreadedIcon = 10501,
|
||
kUnthreadedIcon = 10502
|
||
};
|
||
|
||
static const UInt16 kMaxSubjectLength = 256;
|
||
|
||
#pragma mark -
|
||
|
||
MSG_MessageLine CMessage::sMessageLineData;
|
||
MSG_Pane* CMessage::sCacheMessageList = NULL;
|
||
MSG_ViewIndex CMessage::sCacheIndex = 0;
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CMessage::CMessage(MSG_ViewIndex inIndex, MSG_Pane* inMessageList)
|
||
//----------------------------------------------------------------------------------------
|
||
: mIndex(inIndex-1)
|
||
, mMessageList(inMessageList)
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CMessage::~CMessage()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CMessage::UpdateMessageCache() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mMessageList && (mIndex != sCacheIndex || mMessageList != sCacheMessageList))
|
||
{
|
||
if (!::MSG_GetThreadLineByIndex(mMessageList, mIndex, 1, &sMessageLineData))
|
||
{
|
||
InvalidateCache();
|
||
return false;
|
||
}
|
||
sCacheIndex = mIndex;
|
||
sCacheMessageList = mMessageList;
|
||
}
|
||
return true;
|
||
} // CMessage::UpdateMessageCache
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CMessage::InvalidateCache()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
sCacheMessageList = NULL;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::TestXPFlag(UInt32 inMask) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return ((sMessageLineData.flags & inMask) != 0);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::IsOffline() const // db has offline news article
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return TestXPFlag(MSG_FLAG_OFFLINE);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::IsDeleted() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return TestXPFlag(MSG_FLAG_IMAP_DELETED);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::IsTemplate() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return TestXPFlag(MSG_FLAG_TEMPLATE);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::HasBeenRead() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return TestXPFlag(MSG_FLAG_READ);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::IsFlagged() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return TestXPFlag(MSG_FLAG_MARKED);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::IsThread() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return (sMessageLineData.numChildren > 0);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::HasAttachments() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return TestXPFlag(MSG_FLAG_ATTACHMENT);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::IsOpenThread() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Assert_(IsThread());
|
||
return !TestXPFlag(MSG_FLAG_ELIDED);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Boolean CMessage::HasBeenRepliedTo() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return TestXPFlag(MSG_FLAG_REPLIED);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline MessageId CMessage::GetThreadID() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.threadId;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
MessageKey CMessage::GetMessageKey() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.messageKey;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
uint16 CMessage::GetNumChildren() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.numChildren;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
uint16 CMessage::GetNumNewChildren() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.numNewChildren;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
/* static */ const char* CMessage::GetSubject(
|
||
MSG_MessageLine* data,
|
||
char* buffer,
|
||
UInt16 bufSize)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
*buffer = 0;
|
||
if ((data->flags & MSG_FLAG_HAS_RE) != 0)
|
||
{
|
||
const CString** sh = (const CString**)GetString(REPLY_FORM_RESID);
|
||
if (sh)
|
||
{
|
||
Assert_(bufSize > (**sh).Length());
|
||
XP_SAFE_SPRINTF(buffer, bufSize - 1, (const char*)(**sh), data->subject);
|
||
::ReleaseResource((Handle)sh);
|
||
return buffer;
|
||
}
|
||
}
|
||
return XP_STRNCPY_SAFE(buffer, data->subject, bufSize);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
const char* CMessage::GetSubject(char* buffer, UInt16 bufSize) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return GetSubject(&sMessageLineData, buffer, bufSize);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
const char* CMessage::GetSubject() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.subject;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline MSG_PRIORITY CMessage::GetPriority() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.priority;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CMessage::GetPriorityColor(MSG_PRIORITY priority, RGBColor& priorityColor) /* static */
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Initialize to black
|
||
memset(&priorityColor, 0, sizeof(RGBColor));
|
||
switch (priority)
|
||
{
|
||
case MSG_LowestPriority: // green
|
||
priorityColor.green = 0x7FFF;
|
||
break;
|
||
case MSG_LowPriority: // blue
|
||
priorityColor.blue = 0xFFFF;
|
||
break;
|
||
case MSG_HighPriority: // purple
|
||
priorityColor.red = 0x7FFF;
|
||
priorityColor.blue = 0x7FFF;
|
||
break;
|
||
case MSG_HighestPriority: // red
|
||
priorityColor.red = 0xFFFF;
|
||
break;
|
||
case MSG_NormalPriority:
|
||
case MSG_PriorityNotSet:
|
||
case MSG_NoPriority:
|
||
default:
|
||
break;
|
||
}
|
||
} // CMessage::GetPriorityColor
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CMessage::GetPriorityColor(RGBColor& priorityColor) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Don't draw deleted messages in color.
|
||
MSG_PRIORITY priority = IsDeleted() ? MSG_NormalPriority : GetPriority();
|
||
GetPriorityColor(priority, priorityColor);
|
||
} // CMessage::GetPriorityColor
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline Int16 CMessage::PriorityToMenuItem(MSG_PRIORITY inPriority) /* static */
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return (inPriority > MSG_NoPriority) ? inPriority - 1 : 3;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline MSG_PRIORITY CMessage::MenuItemToPriority(Int16 inMenuItem) /* static */
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return (MSG_PRIORITY)(inMenuItem + 1);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline const char* CMessage::GetSender() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.author;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline const char* CMessage::GetDateString() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// UpdateMessageCache();
|
||
return MSG_FormatDate(mMessageList, GetDate());
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline const char* CMessage::GetAddresseeString() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.author;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
const char * CMessage::GetSizeStr() const
|
||
// <09> FIX ME: Is there an XP call for constructing the size string ?
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
static char sizeString[32];
|
||
UInt32 messageSize = GetSize();
|
||
CStr255 formatString;
|
||
if (messageSize > (1024*1000))
|
||
{
|
||
::GetIndString(formatString, 7099, 4);
|
||
float floatSize = (float)messageSize / (1024*1000);
|
||
sprintf(sizeString, formatString, floatSize);
|
||
}
|
||
else
|
||
{
|
||
if (messageSize > 1024)
|
||
{
|
||
::GetIndString(formatString, 7099, 5);
|
||
messageSize /= 1024;
|
||
}
|
||
else
|
||
{
|
||
::GetIndString(formatString, 7099, 6);
|
||
}
|
||
sprintf(sizeString, formatString, messageSize);
|
||
}
|
||
return sizeString;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
const char * CMessage::GetPriorityStr() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
static char buffer[256];
|
||
buffer[0] = '\0';
|
||
MSG_PRIORITY priority = GetPriority();
|
||
if (priority == MSG_PriorityNotSet
|
||
|| priority == MSG_NoPriority
|
||
|| priority == MSG_NormalPriority)
|
||
return buffer;
|
||
MSG_GetPriorityName(priority, buffer, sizeof(buffer)-1);
|
||
return buffer;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
const char * CMessage::GetStatusStr() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
static char buffer[256];
|
||
MSG_GetStatusName(GetStatus(), buffer, sizeof(buffer)-1);
|
||
return buffer;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline time_t CMessage::GetDate() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.date;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline UInt32 CMessage::GetSize() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.messageLines; // bytes for mail, lines for news. Oh well!
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline UInt32 CMessage::GetStatus() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.flags;
|
||
// actually, not all bits are relevant, but there's no mask in msgcom.h.
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
inline int8 CMessage::GetThreadLevel() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return sMessageLineData.level;
|
||
}
|
||
|
||
//======================================
|
||
// Icon suite IDs
|
||
//======================================
|
||
#pragma mark
|
||
enum IconTable
|
||
{
|
||
kMessageReadIcon = 15234 // icon to mark a message as read
|
||
, kFlagIcon = 15236 // icon to mark a message as read
|
||
|
||
// Icons for representing mail messages
|
||
, kMailMessageIcon = 15228
|
||
, kReadMailMessageIcon = 15229
|
||
, kMailMessageWithAttachmentIcon = 10511
|
||
, kFlaggedMailMessageIcon = 10512
|
||
, kFlaggedMailMessageWithAttachmentIcon = 10513
|
||
|
||
// News articles
|
||
, kNewsArticleMessageIcon = 15231
|
||
, kReadNewsArticleMessageIcon = 15232
|
||
, kNewsArticleReadFlaggedMessageIcon = 15232
|
||
, kNewsArticleFlaggedMessageIcon = 15234
|
||
};
|
||
|
||
static ResIDT gIconIDTable[] = {
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
// NAME UNREAD READ NEW (UNUSED)
|
||
// LOCAL ONLINE LOCAL ONLINE LOCAL ONLINE LOCAL ONLINE
|
||
//-------------------------------------------------------------------------------------------------------------
|
||
/* POP */ 15228 , 0 , 15229 , 0 , 15407 , 0 , 0 , 0 ,
|
||
/* IMAP */ 15228 , 15226 , 15229 , 15226 , 15407 , 15407 , 15523 , 15523 ,
|
||
/* Art */ 15518 , 15233 , 15519 , 15396 , 0 , 0 , 0 , 0 ,
|
||
/* Drft */ 15230 , 15230 , 15230 , 15230 , 15230 , 15230 , 0 , 0 ,
|
||
}; //----------------------------------------------------------------------------------------------------
|
||
|
||
enum {
|
||
kOnline = 1 // offset
|
||
, kRead = 2 // offset
|
||
, kNew = 4 // offset
|
||
, kDeleted = 6 // offset
|
||
, kKindsPerRow = 8
|
||
, kMessageIx = 0
|
||
, kIMAPIx = 1 * kKindsPerRow
|
||
, kArticleIx = 2 * kKindsPerRow
|
||
, kDraftIx = 3 * kKindsPerRow
|
||
};
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
ResIDT CMessage::GetIconID(UInt16 inFolderFlags) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
return GetIconID(inFolderFlags, sMessageLineData.flags);
|
||
} // CMessage::GetIconID
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
ResIDT CMessage::GetIconID(UInt16 inFolderFlags, UInt32 inMessageFlags)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
#define IMAP_FOLDER (MSG_FOLDER_FLAG_IMAPBOX | MSG_FOLDER_FLAG_MAIL)
|
||
short iconIndex = 0;
|
||
// Special case of pop folder because MSG_FLAG_OFFLINE is not correct for pop folders.
|
||
Boolean isPopFolder = (inFolderFlags & IMAP_FOLDER) == MSG_FOLDER_FLAG_MAIL;
|
||
Boolean isNews = (inFolderFlags & MSG_FOLDER_FLAG_NEWSGROUP) == MSG_FOLDER_FLAG_NEWSGROUP;
|
||
|
||
// First, what kind of folder are we in (determines the basic icon type, the row in
|
||
// the table above)
|
||
if ((inFolderFlags & IMAP_FOLDER) == IMAP_FOLDER)
|
||
iconIndex = kIMAPIx;
|
||
else if (inFolderFlags & MSG_FOLDER_FLAG_MAIL)
|
||
iconIndex = kMessageIx; // FIXME: How do we tell whether it has attachments?
|
||
else if (inFolderFlags & MSG_FOLDER_FLAG_NEWSGROUP)
|
||
iconIndex = kArticleIx;
|
||
else if (inFolderFlags & MSG_FOLDER_FLAG_DRAFTS)
|
||
iconIndex = kDraftIx;
|
||
|
||
// Next, which of the four major columns are we in.
|
||
if (!isNews && !isPopFolder && (inMessageFlags & MSG_FLAG_IMAP_DELETED) != 0)
|
||
iconIndex += kDeleted;
|
||
else if (!isNews && (inMessageFlags & MSG_FLAG_NEW) != 0)
|
||
iconIndex += kNew;
|
||
else if ((inMessageFlags & MSG_FLAG_READ) != 0)
|
||
iconIndex += kRead;
|
||
|
||
// Finally, choose the local/offline column.
|
||
// Note: pop does not support the "offline" flag bit, it should always be set
|
||
// (but isn't)
|
||
if (!isPopFolder && (inMessageFlags & MSG_FLAG_OFFLINE) == 0)
|
||
iconIndex += kOnline;
|
||
|
||
return gIconIDTable[iconIndex];
|
||
} // CMessage::GetIconID
|
||
|
||
//======================================
|
||
// Thread Icon IDs
|
||
//======================================
|
||
enum ThreadIconTable
|
||
{
|
||
// Icons for representing threaded messages
|
||
kThreadIcon = 15399
|
||
, kNewThreadIcon = 15401
|
||
, kWatchedThreadIcon = 15403
|
||
, kWatchedNewThreadIcon = 15404
|
||
, kIgnoredThreadIcon = 15429
|
||
|
||
};
|
||
|
||
static ResIDT gThreadIconIDTable[] = {
|
||
//-----------------------------------------------------------
|
||
// NAME UNWATCHED WATCHED
|
||
// OLD UNREAD OLD UNREAD
|
||
//-----------------------------------------------------------
|
||
/* Thread */ 15399 , 15401 , 15403 , 15404
|
||
}; //--------------------------------------------------
|
||
|
||
enum {
|
||
kUnread = 1 // offset
|
||
, kWatched = 2 // offset
|
||
};
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
ResIDT CMessage::GetThreadIconID() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UpdateMessageCache();
|
||
short iconIndex = 0;
|
||
if ((sMessageLineData.flags & MSG_FLAG_IGNORED) != 0)
|
||
return kIgnoredThreadIcon;
|
||
if (GetNumNewChildren() > 0)
|
||
iconIndex += kUnread;
|
||
if ((sMessageLineData.flags & MSG_FLAG_WATCHED) != 0)
|
||
iconIndex += kWatched;
|
||
return gThreadIconIDTable[iconIndex];
|
||
} // CMessage::GetIconID
|
||
|
||
#pragma mark -
|
||
|
||
//========================================================================================
|
||
class CDeferredLoadFolderTask
|
||
//========================================================================================
|
||
: public CDeferredTask
|
||
{
|
||
public:
|
||
CDeferredLoadFolderTask(
|
||
CThreadView* inView,
|
||
CNSContext* inContext,
|
||
CMessageFolder inFolder);
|
||
protected:
|
||
virtual ExecuteResult ExecuteSelf();
|
||
// data
|
||
protected:
|
||
CThreadView* mThreadView;
|
||
CNSContext* mContextToLoadAtIdle;
|
||
CMessageFolder mFolderToLoadAtIdle;
|
||
UInt32 mEarliestExecuteTime;
|
||
}; // class CDeferredLoadFolderTask
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredLoadFolderTask::CDeferredLoadFolderTask(
|
||
CThreadView* inView,
|
||
CNSContext* inContext,
|
||
CMessageFolder inFolder)
|
||
//----------------------------------------------------------------------------------------
|
||
: mThreadView(inView)
|
||
, mContextToLoadAtIdle(inContext)
|
||
, mFolderToLoadAtIdle(inFolder)
|
||
, mEarliestExecuteTime(::TickCount() + GetDblTime())
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredTask::ExecuteResult CDeferredLoadFolderTask::ExecuteSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Wait for the earliest execute time. It's expensive to load a folder, and if the
|
||
// user is nervously clicking on multiple folders, we only want to load the last one.
|
||
if (::TickCount() < mEarliestExecuteTime)
|
||
return eWaitStayFront;
|
||
if (CMailProgressWindow::GetModal())
|
||
return eWaitStayFront; // wait until other modal tasks are done.
|
||
mThreadView->LoadMessageFolder(mContextToLoadAtIdle, mFolderToLoadAtIdle, true);
|
||
return eDoneDelete;
|
||
} // CDeferredLoadFolderTask::ExecuteSelf
|
||
|
||
#pragma mark -
|
||
|
||
//========================================================================================
|
||
class CDeferredUndoTask
|
||
//========================================================================================
|
||
: public CDeferredTask
|
||
{
|
||
public:
|
||
CDeferredUndoTask(
|
||
CThreadView* inView);
|
||
void SetUndoStatus(UndoStatus inStatus)
|
||
{
|
||
mUndoStatus = inStatus;
|
||
}
|
||
protected:
|
||
virtual ExecuteResult ExecuteSelf();
|
||
// data
|
||
protected:
|
||
CThreadView* mThreadView;
|
||
UndoStatus mUndoStatus;
|
||
}; // class CDeferredUndoTask
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredUndoTask::CDeferredUndoTask(
|
||
CThreadView* inView)
|
||
//----------------------------------------------------------------------------------------
|
||
: mThreadView(inView)
|
||
, mUndoStatus(UndoInProgress)
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredTask::ExecuteResult CDeferredUndoTask::ExecuteSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mUndoStatus == UndoComplete)
|
||
return eDoneDelete; // remove from queue
|
||
else if (mUndoStatus == UndoInProgress)
|
||
{
|
||
mUndoStatus = MSG_GetUndoStatus(mThreadView->GetMessagePane());
|
||
switch (mUndoStatus)
|
||
{
|
||
case UndoComplete:
|
||
MSG_FolderInfo* finfo;
|
||
MessageKey key;
|
||
MSG_Pane* pane = mThreadView->GetMessagePane();
|
||
if (MSG_GetUndoMessageKey(pane, &finfo, &key))
|
||
{
|
||
if (finfo && finfo != mThreadView->GetOwningFolder())
|
||
{
|
||
mThreadView->LoadMessageFolder(mThreadView->GetContext(), finfo, false);
|
||
}
|
||
mThreadView->SelectMessageWhenReady(key);
|
||
}
|
||
break;
|
||
case UndoInProgress:
|
||
return eWaitStayFront; // block, leave in queue
|
||
default:
|
||
return eDoneDelete;
|
||
} // switch
|
||
}
|
||
return eDoneDelete; // remove from queue
|
||
} // CDeferredUndoTask::ExecuteSelf
|
||
|
||
#pragma mark -
|
||
|
||
//========================================================================================
|
||
class CDeferredSelectRowTask
|
||
//========================================================================================
|
||
: public CDeferredTask
|
||
{
|
||
public:
|
||
CDeferredSelectRowTask(
|
||
CThreadView* inView,
|
||
TableIndexT inRow);
|
||
protected:
|
||
virtual ExecuteResult ExecuteSelf();
|
||
// data
|
||
protected:
|
||
CThreadView* mThreadView;
|
||
TableIndexT mRow;
|
||
}; // class CDeferredSelectRowTask
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredSelectRowTask::CDeferredSelectRowTask(
|
||
CThreadView* inView,
|
||
TableIndexT inRow)
|
||
//----------------------------------------------------------------------------------------
|
||
: mThreadView(inView)
|
||
, mRow(inRow)
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredTask::ExecuteResult CDeferredSelectRowTask::ExecuteSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
mThreadView->SelectAfterDelete(mRow);
|
||
return eDoneDelete; // remove from queue
|
||
} // CDeferredSelectRowTask::ExecuteSelf
|
||
|
||
#pragma mark -
|
||
|
||
//========================================================================================
|
||
class CScrollToNewMailTask
|
||
//========================================================================================
|
||
: public CDeferredTask
|
||
{
|
||
public:
|
||
CScrollToNewMailTask(
|
||
CThreadView* inView);
|
||
protected:
|
||
virtual ExecuteResult ExecuteSelf();
|
||
// data
|
||
protected:
|
||
CThreadView* mThreadView;
|
||
}; // class CScrollToNewMailTask
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CScrollToNewMailTask::CScrollToNewMailTask(
|
||
CThreadView* inView)
|
||
//----------------------------------------------------------------------------------------
|
||
: mThreadView(inView)
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredTask::ExecuteResult CScrollToNewMailTask::ExecuteSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
mThreadView->ScrollToGoodPlace();
|
||
return eDoneDelete; // remove from queue
|
||
} // CScrollToNewMailTask::ExecuteSelf
|
||
|
||
#pragma mark -
|
||
|
||
//========================================================================================
|
||
class CDeferredSelectKeyTask
|
||
//========================================================================================
|
||
: public CDeferredTask
|
||
{
|
||
public:
|
||
CDeferredSelectKeyTask(
|
||
CThreadView* inView,
|
||
MessageKey inKey);
|
||
protected:
|
||
virtual ExecuteResult ExecuteSelf();
|
||
// data
|
||
protected:
|
||
CThreadView* mThreadView;
|
||
MessageKey mPendingMessageKeyToSelect; // Hack to support ShowSearchMessage()
|
||
}; // class CDeferredSelectKeyTask
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredSelectKeyTask::CDeferredSelectKeyTask(
|
||
CThreadView* inView,
|
||
MessageKey inKey)
|
||
//----------------------------------------------------------------------------------------
|
||
: mThreadView(inView)
|
||
, mPendingMessageKeyToSelect(inKey)
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredTask::ExecuteResult CDeferredSelectKeyTask::ExecuteSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
mThreadView->SelectMessageWhenReady(mPendingMessageKeyToSelect);
|
||
return eDoneDelete;
|
||
} // CDeferredSelectKeyTask::ExecuteSelf
|
||
|
||
#pragma mark -
|
||
|
||
//========================================================================================
|
||
class CDeferredThreadViewCommand
|
||
//========================================================================================
|
||
: public CDeferredCommand
|
||
{
|
||
private:
|
||
typedef CDeferredCommand Inherited;
|
||
public:
|
||
CDeferredThreadViewCommand(
|
||
CThreadView* inView,
|
||
CommandT inCommand,
|
||
void* ioParam);
|
||
protected:
|
||
virtual ExecuteResult ExecuteSelf();
|
||
// data
|
||
protected:
|
||
CThreadView* mThreadView;
|
||
}; // class CDeferredThreadViewCommand
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredThreadViewCommand::CDeferredThreadViewCommand(
|
||
CThreadView* inView,
|
||
CommandT inCommand,
|
||
void* ioParam)
|
||
//----------------------------------------------------------------------------------------
|
||
: CDeferredCommand(inView, inCommand, ioParam)
|
||
, mThreadView(inView)
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CDeferredTask::ExecuteResult CDeferredThreadViewCommand::ExecuteSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mCommand == cmd_GetNewMail)
|
||
{
|
||
if (mThreadView->IsStillLoading())
|
||
return eWaitStayFront; // don't stop idling
|
||
// && ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_INBOX) != 0) && // if this is an inbox
|
||
// ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_IMAPBOX) == 0) // and our server is pop
|
||
}
|
||
return Inherited::ExecuteSelf();
|
||
} // CDeferredThreadViewCommand::ExecuteSelf
|
||
|
||
#pragma mark -
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CThreadView::CThreadView(LStream *inStream)
|
||
//----------------------------------------------------------------------------------------
|
||
: Inherited(inStream)
|
||
, mXPFolder(NULL)
|
||
, mSavedSelection(nil)
|
||
, mExpectingNewMail(false)
|
||
, mGotNewMail(false)
|
||
, mRowToSelect(0)
|
||
, mSelectAfterDelete(false) // This new "hidden" pref was a demand from a big customer
|
||
, mMotionPendingCommand((MSG_MotionType)-1)
|
||
, mUndoTask(nil)
|
||
, mScrollToShowNew(false)
|
||
{
|
||
XP_Bool noNextRowSelection;
|
||
if (PREF_GetBoolPref("mail.no_select_after_delete", &noNextRowSelection) == PREF_NOERROR
|
||
&& noNextRowSelection)
|
||
SetSelectAfterDelete(false);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CThreadView::~CThreadView()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
SaveSelectedMessage();
|
||
delete mSavedSelection;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ApplyTextStyle(TableIndexT inRow) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
CMessage message(inRow, GetMessagePane());
|
||
// Set bold if new
|
||
if (!message.HasBeenRead())
|
||
::TextFace(bold);
|
||
else if (message.IsThread() && !message.IsOpenThread() && message.GetNumNewChildren() > 0)
|
||
::TextFace(underline);
|
||
else
|
||
::TextFace(normal);
|
||
::TextMode(message.IsDeleted() ? grayishTextOr : srcOr);
|
||
} // CMessageFolderView::ApplyTextStyle
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ApplyTextColor(TableIndexT inRow) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
CMessage message(inRow, GetMessagePane());
|
||
// Set up the text color based on priority
|
||
RGBColor priorityColor;
|
||
message.GetPriorityColor(priorityColor);
|
||
::RGBForeColor(&priorityColor);
|
||
} // CMessageFolderView::ApplyTextStyle
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DrawCellContents(
|
||
const STableCell &inCell,
|
||
const Rect &inLocalRect)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
ApplyTextColor(inCell.row);
|
||
|
||
CMessage message(inCell.row, GetMessagePane());
|
||
PaneIDT cellType = GetCellDataType(inCell);
|
||
switch (cellType)
|
||
{
|
||
case kThreadMessageColumn:
|
||
{
|
||
// draw a "thread dragger" /*if we're sorted by thread and*/ this
|
||
// message is a thread header... or if we are watched/ignored
|
||
//if (GetSortedColumn() == kThreadMessageColumn && message.IsThread())
|
||
|
||
ResIDT iconID = message.GetThreadIconID();
|
||
if( /*GetSortedColumn() == kThreadMessageColumn &&*/ ( message.IsThread() || iconID >= kWatchedThreadIcon ) )
|
||
DrawIconFamily( iconID, 16, 16, 0, inLocalRect);
|
||
break;
|
||
|
||
}
|
||
case kMarkedReadMessageColumn:
|
||
{
|
||
if (!message.HasBeenRead())
|
||
DrawIconFamily(kMessageReadIcon, 16, 16, 0, inLocalRect);
|
||
break;
|
||
}
|
||
case kFlagMessageColumn:
|
||
if (message.IsFlagged())
|
||
DrawIconFamily(kFlagIcon, 16, 16, 0, inLocalRect);
|
||
break;
|
||
case kSubjectMessageColumn:
|
||
SInt16 newLeft = DrawIcons(inCell, inLocalRect);
|
||
Rect subjectRect = inLocalRect;
|
||
subjectRect.left = newLeft;
|
||
DrawMessageSubject(inCell.row, subjectRect);
|
||
break;
|
||
case kSenderMessageColumn:
|
||
{
|
||
const char* raw = message.GetSender();
|
||
char* conv = IntlDecodeMimePartIIStr(raw, mContext->GetWinCSID(), FALSE);
|
||
|
||
if(mContext->GetWinCSID() == CS_UTF8)
|
||
DrawUTF8TextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect);
|
||
else
|
||
DrawTextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect);
|
||
|
||
if(conv)
|
||
XP_FREE(conv);
|
||
}
|
||
break;
|
||
case kDateMessageColumn:
|
||
DrawTextString(message.GetDateString(), &mTextFontInfo, 2, inLocalRect, teFlushLeft, true, truncEnd);
|
||
break;
|
||
case kPriorityMessageColumn:
|
||
DrawMessagePriority(message, inLocalRect);
|
||
break;
|
||
case kSizeMessageColumn:
|
||
DrawMessageSize(message, inLocalRect);
|
||
break;
|
||
case kStatusMessageColumn:
|
||
DrawMessageStatus(message, inLocalRect);
|
||
break;
|
||
case kAddresseeMessageColumn:
|
||
{
|
||
const char* raw = message.GetAddresseeString();
|
||
char* conv = IntlDecodeMimePartIIStr(raw, mContext->GetWinCSID(), FALSE);
|
||
if(mContext->GetWinCSID() == CS_UTF8)
|
||
DrawUTF8TextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect);
|
||
else
|
||
DrawTextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect);
|
||
DrawTextString((conv ? conv : raw), &mTextFontInfo, 2, inLocalRect);
|
||
if(conv)
|
||
XP_FREE(conv);
|
||
}
|
||
break;
|
||
case kTotalMessageColumn:
|
||
if (GetSortedColumn() == kThreadMessageColumn && message.IsThread())
|
||
DrawCountCell(message.GetNumChildren(), inLocalRect);
|
||
break;
|
||
case kUnreadMessageColumn:
|
||
if (GetSortedColumn() == kThreadMessageColumn && message.IsThread())
|
||
DrawCountCell(message.GetNumNewChildren(), inLocalRect);
|
||
break;
|
||
default:
|
||
Assert_(false);
|
||
break;
|
||
}
|
||
// The selection of a new message after one is removed is handled by a timer, and is keyed
|
||
// off mRowToSelect. This data member is set when we are notified that a row has been removed.
|
||
// We used to start this timer immediately on initiating the delete operation (in DeleteSelection)
|
||
// but this ended up firing before the refresh occurred, resulting in two rows selected, which
|
||
// was ugly. So we turn on the idler here, now, to make sure that the row isn't selected until
|
||
// after the refresh is done. 98/01/14
|
||
if (mRowToSelect)
|
||
{
|
||
CDeferredTaskManager::Post1(new CDeferredSelectRowTask(this, mRowToSelect), this);
|
||
mRowToSelect = LArray::index_Bad;
|
||
}
|
||
if (mScrollToShowNew)
|
||
{
|
||
CDeferredTaskManager::Post1(new CScrollToNewMailTask(this), this);
|
||
mScrollToShowNew = false;
|
||
}
|
||
} // CThreadView::DrawCellContents
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DrawIconsSelf(
|
||
const STableCell& inCell,
|
||
IconTransformType inTransformType,
|
||
const Rect& inIconRect) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
const ResIDT kPaperClipBackLayer = 15555;
|
||
const ResIDT kPaperClipFrontLayer = 15556;
|
||
CMessage message(inCell.row, GetMessagePane());
|
||
Boolean hasAttachments = message.HasAttachments();
|
||
if (hasAttachments)
|
||
DrawIconFamily(kPaperClipBackLayer, 16, 16, inTransformType, inIconRect);
|
||
Inherited::DrawIconsSelf(inCell, inTransformType, inIconRect);
|
||
if (hasAttachments)
|
||
DrawIconFamily(kPaperClipFrontLayer, 16, 16, inTransformType, inIconRect);
|
||
} // CThreadView::DrawIconsSelf
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::GetDragCopyStatus(
|
||
DragReference inDragRef
|
||
, const CMailSelection& inSelection
|
||
, Boolean& outCopy)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
SInt16 modifiers;
|
||
::GetDragModifiers(inDragRef, NULL, &modifiers, NULL);
|
||
outCopy = ((modifiers & optionKey) != 0); // user wants copy if possible.
|
||
MSG_DragEffect effect = outCopy ? MSG_Require_Copy : MSG_Default_Drag;
|
||
MSG_FolderInfo* destFolder = GetOwningFolder();
|
||
if (!destFolder)
|
||
return false; // eg, when the pane is blank because a server is selected.
|
||
// Ask the back end for guidance on what is possible:
|
||
effect = ::MSG_DragMessagesIntoFolderStatus(
|
||
inSelection.xpPane,
|
||
inSelection.GetSelectionList(),
|
||
inSelection.selectionSize,
|
||
destFolder,
|
||
effect);
|
||
if (effect == MSG_Drag_Not_Allowed && outCopy)
|
||
{
|
||
// Well, maybe a move is ok
|
||
effect = ::MSG_DragMessagesIntoFolderStatus(
|
||
inSelection.xpPane,
|
||
inSelection.GetSelectionList(),
|
||
inSelection.selectionSize,
|
||
destFolder,
|
||
MSG_Default_Drag);
|
||
}
|
||
outCopy = (effect & MSG_Require_Copy) != 0;
|
||
return (effect != MSG_Drag_Not_Allowed); // this looks ok as a drop operation.
|
||
} // CThreadView::GetSelectionAndCopyStatusFromDrag
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::EnterDropArea(
|
||
DragReference inDragRef,
|
||
Boolean inDragHasLeftSender)
|
||
// CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row
|
||
// scenario
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
LDragAndDrop::EnterDropArea(inDragRef, inDragHasLeftSender);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::LeaveDropArea(DragReference inDragRef)
|
||
// CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row
|
||
// scenario
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
LDragAndDrop::LeaveDropArea(inDragRef);
|
||
mDropRow = 0;
|
||
mIsDropBetweenRows = false;
|
||
mIsInternalDrop = false;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::ItemIsAcceptable(
|
||
DragReference inDragRef,
|
||
ItemReference /*inItemRef*/ )
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Boolean dropOK = false;
|
||
Boolean doCopy = false;
|
||
|
||
CMailSelection selection;
|
||
if (GetSelectionFromDrag(inDragRef, selection)
|
||
&& selection.xpPane != GetMessagePane()
|
||
&& GetDragCopyStatus(inDragRef, selection, doCopy))
|
||
dropOK = true;
|
||
if (dropOK && doCopy)
|
||
{
|
||
CursHandle copyDragCursor = ::GetCursor(curs_CopyDrag);
|
||
if (copyDragCursor != nil)
|
||
::SetCursor(*copyDragCursor);
|
||
}
|
||
else
|
||
::SetCursor(&qd.arrow);
|
||
return dropOK;
|
||
} // CThreadView::ItemIsAcceptable
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ReceiveDragItem(
|
||
DragReference inDragRef,
|
||
DragAttributes /*inDragAttrs*/,
|
||
ItemReference inItemRef,
|
||
Rect& /*inItemBounds*/)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Assert_(inItemRef == CMailFlexTable::eMailNewsSelectionDragItemRefNum);
|
||
|
||
// What are the items being dragged?
|
||
CMailSelection selection;
|
||
if (!GetSelectionFromDrag(inDragRef, selection))
|
||
return;
|
||
|
||
// Don't do anything if the user aborted by dragging back into the same view.
|
||
if (selection.xpPane == GetMessagePane())
|
||
return;
|
||
|
||
Boolean copyMessages;
|
||
if (!GetDragCopyStatus(inDragRef, selection, copyMessages))
|
||
return;
|
||
|
||
// display modal dialog
|
||
short stringID = (copyMessages ? 19 : 15); // Copying / Moving Messages
|
||
CStr255 commandString;
|
||
::GetIndString(commandString, 7099, stringID);
|
||
CMailProgressWindow* progressWindow = CMailProgressWindow::CreateModalParasite(
|
||
CMailProgressWindow::res_ID_modal,
|
||
selection.xpPane,
|
||
commandString);
|
||
if (progressWindow)
|
||
progressWindow->SetDelay(0);
|
||
|
||
// copy / move messages
|
||
if (! copyMessages)
|
||
::MSG_MoveMessagesIntoFolder(
|
||
selection.xpPane,
|
||
selection.GetSelectionList(),
|
||
selection.selectionSize,
|
||
mXPFolder.GetFolderInfo()
|
||
);
|
||
else
|
||
::MSG_CopyMessagesIntoFolder(
|
||
selection.xpPane,
|
||
selection.GetSelectionList(),
|
||
selection.selectionSize,
|
||
mXPFolder.GetFolderInfo()
|
||
);
|
||
// Close any windows associated with these messages, if the preferences
|
||
// say we should.
|
||
CMessageWindow::NoteSelectionFiledOrDeleted(selection);
|
||
mUndoCommand = cmd_Undo;
|
||
} // CThreadView::ReceiveDragItem
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DrawMessageSize(
|
||
const CMessage& inMessage,
|
||
const Rect& inLocalRect )
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
DrawTextString(inMessage.GetSizeStr(),
|
||
&mTextFontInfo, 2, inLocalRect, teFlushLeft, true, truncEnd);
|
||
} // CThreadView::DrawMessageSize
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DrawMessagePriority(
|
||
const CMessage& inMessage,
|
||
const Rect& inLocalRect)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
DrawTextString(inMessage.GetPriorityStr(),
|
||
&mTextFontInfo, 0, inLocalRect);
|
||
} // CThreadView::DrawMessagePriority
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DrawMessageStatus(
|
||
const CMessage& inMessage,
|
||
const Rect& inLocalRect)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
DrawTextString(inMessage.GetStatusStr(),
|
||
&mTextFontInfo, 0, inLocalRect);
|
||
} //CThreadView::DrawMessageStatus
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
ResIDT CThreadView::GetIconID(TableIndexT inRow) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
CMessage message(inRow, GetMessagePane());
|
||
return message.GetIconID(mXPFolder.GetFolderFlags());
|
||
} // CMessageFolderView::GetIconID
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
UInt16 CThreadView::GetNestedLevel(TableIndexT inRow) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
CMessage message(inRow, GetMessagePane());
|
||
return message.GetThreadLevel();
|
||
} // CThreadView::GetNestedLevel
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::GetHiliteTextRect(
|
||
const TableIndexT inRow,
|
||
Rect& outRect) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
STableCell cell(inRow, GetHiliteColumn());
|
||
if (!GetLocalCellRect(cell, outRect))
|
||
return false;
|
||
Rect iconRect;
|
||
GetIconRect(cell, outRect, iconRect);
|
||
outRect.left = iconRect.right;
|
||
char subject[kMaxSubjectLength];
|
||
GetHiliteText(inRow, subject, sizeof(subject), &outRect);
|
||
return true;
|
||
} // CThreadView::GetHiliteRect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::GetDropFlagRect(
|
||
const Rect& inLocalCellRect,
|
||
Rect& outFlagRect ) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Inherited::GetDropFlagRect(inLocalCellRect, outFlagRect);
|
||
if (GetSortedColumn() != kThreadMessageColumn)
|
||
{
|
||
// Return a rectangle of zero width.
|
||
outFlagRect.left = inLocalCellRect.left;
|
||
outFlagRect.right = outFlagRect.left;
|
||
}
|
||
} // CThreadView::GetDropFlagRect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::GetMainRowText(
|
||
TableIndexT inRow,
|
||
char* outText,
|
||
UInt16 inMaxBufferLength) const
|
||
// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
|
||
// Return result indicates if any of the text is visible in the cell
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (!outText || inMaxBufferLength < 2)
|
||
return;
|
||
CMessage message(inRow, GetMessagePane());
|
||
const char* raw = message.GetSubject(outText, inMaxBufferLength - 1);
|
||
char* conv = IntlDecodeMimePartIIStr(raw, mContext->GetWinCSID(), FALSE);
|
||
if (conv)
|
||
{
|
||
XP_STRNCPY_SAFE(outText, conv, inMaxBufferLength);
|
||
XP_FREE(conv);
|
||
}
|
||
} // CThreadView::GetMainRowText
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DrawMessageSubject(
|
||
TableIndexT inRow,
|
||
const Rect& inLocalRect)
|
||
// Draw the message drop flag, icon and subject
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// draw a drop flag if we're sorted by thread and this
|
||
// message is a thread header...
|
||
char subject[kMaxSubjectLength];
|
||
Rect textRect = inLocalRect;
|
||
if (GetHiliteText(inRow, subject, sizeof(subject), &textRect))
|
||
{
|
||
if (mContext->GetWinCSID() == CS_UTF8)
|
||
{
|
||
textRect.right = inLocalRect.right; // calculation will have been wrong.
|
||
DrawUTF8TextString(subject, &mTextFontInfo, 0, textRect);
|
||
}
|
||
else
|
||
DrawTextString(subject, &mTextFontInfo, 0, textRect);
|
||
// if (inRow == mDropRow)
|
||
// ::InvertRect(&textRect);
|
||
}
|
||
} // CThreadView::DrawMessageSubject
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::CellSelects(const STableCell& inCell) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT cellType = GetCellDataType(inCell);
|
||
switch (cellType)
|
||
{
|
||
case kThreadMessageColumn:
|
||
{
|
||
CMessage message(inCell.row, GetMessagePane());
|
||
return (message.IsThread());
|
||
}
|
||
case kMarkedReadMessageColumn:
|
||
case kFlagMessageColumn:
|
||
return false;
|
||
default:
|
||
return true;
|
||
}
|
||
} // CThreadView::CellSelects
|
||
|
||
//-----------------------------------
|
||
void CThreadView::SetRowCount()
|
||
// Queries the back end pane and sets the number of rows.
|
||
// Overridden to call MSG_GetThreadLineByIndex, which forces
|
||
// authentication if the preference specifies it. A result of false
|
||
// means that the folder is password protected.
|
||
//-----------------------------------
|
||
{
|
||
MSG_MessageLine ignored;
|
||
MSG_Pane* pane = GetMessagePane();
|
||
if (!pane || !::MSG_GetThreadLineByIndex(pane, 0, 1, &ignored))
|
||
{
|
||
// Access denied!
|
||
TableIndexT rows, cols;
|
||
GetTableSize(rows, cols);
|
||
if (rows > 0)
|
||
RemoveRows(rows, 1, false);
|
||
return;
|
||
}
|
||
Inherited::SetRowCount();
|
||
} // CThreadView::SetRowCount()
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::CellWantsClick ( const STableCell& inCell ) const
|
||
// Overloaded to ensure that certain columns get the mouse click
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT cellType = GetCellDataType(inCell);
|
||
switch (cellType)
|
||
{
|
||
// case kThreadMessageColumn:
|
||
case kMarkedReadMessageColumn:
|
||
case kFlagMessageColumn:
|
||
case kPriorityMessageColumn:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
|
||
} // CThreadView::CellWantsClick
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ClickCell(
|
||
const STableCell &inCell,
|
||
const SMouseDownEvent &inMouseDown)
|
||
// Handles marking message read/unread
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
CMailProgressWindow::RemoveModalParasite();
|
||
PaneIDT cellType = GetCellDataType(inCell);
|
||
switch (cellType)
|
||
{
|
||
case kThreadMessageColumn:
|
||
{
|
||
if (GetSortedColumn() == kThreadMessageColumn)
|
||
{
|
||
CMessage message(inCell.row, GetMessagePane());
|
||
if (message.IsThread())
|
||
{
|
||
if (! message.IsOpenThread())
|
||
ToggleExpandAction(inCell.row);
|
||
DoSelectThread(inCell.row);
|
||
CMessage::InvalidateCache(); // Because BE has changed the message state
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
// toggle the message's read/unread state
|
||
case kMarkedReadMessageColumn:
|
||
{
|
||
MSG_ViewIndex index = inCell.row - 1;
|
||
MSG_Command(GetMessagePane(), MSG_ToggleMessageRead, &index, 1);
|
||
CMessage::InvalidateCache(); // Because BE has changed the message state
|
||
RefreshCell(inCell);
|
||
break;
|
||
}
|
||
case kFlagMessageColumn:
|
||
{
|
||
CMessage message(inCell.row, GetMessagePane());
|
||
MSG_CommandType cmd = message.IsFlagged() ? MSG_UnmarkMessages : MSG_MarkMessages;
|
||
MSG_ViewIndex index = inCell.row - 1;
|
||
MSG_Command(GetMessagePane(), cmd, &index, 1);
|
||
CMessage::InvalidateCache(); // Because BE has changed the message state
|
||
RefreshCell(inCell);
|
||
break;
|
||
}
|
||
case kPriorityMessageColumn:
|
||
{
|
||
if ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) != 0)
|
||
break; // no priority for news.
|
||
|
||
// calculate popup location
|
||
FocusDraw();
|
||
Point where = inMouseDown.wherePort;
|
||
PortToGlobalPoint(where);
|
||
Rect cellRect;
|
||
if (GetLocalCellRect(inCell, cellRect))
|
||
{
|
||
where = topLeft(cellRect);
|
||
::LocalToGlobal(&where);
|
||
}
|
||
|
||
// get popup default value
|
||
CMessage message(inCell.row, GetMessagePane());
|
||
MSG_PRIORITY oldPriority = message.GetPriority();
|
||
Int16 defaultChoice = CMessage::PriorityToMenuItem(oldPriority);
|
||
|
||
// display popup
|
||
Int16 menuItem = 0;
|
||
LMenu * thePopup = new LMenu(kPriorityMenuID);
|
||
|
||
{ // copied from HandlePopupMenuSelect()
|
||
|
||
MenuHandle menuH = thePopup->GetMacMenuH();
|
||
|
||
// we decided that all the context menus should look the same and
|
||
// this meant drawing them in the system font (at least for 4.0)
|
||
// to re-enable drawing in another font just take out my #ifdef's
|
||
// and #endif's
|
||
#ifdef USE_SOME_OTHER_FONT_FOR_THE_PRIORITY_MENU
|
||
Int16 saveFont = ::LMGetSysFontFam();
|
||
Int16 saveSize = ::LMGetSysFontSize();
|
||
|
||
StMercutioMDEFTextState theMercutioMDEFTextState;
|
||
#endif
|
||
try {
|
||
#ifdef USE_SOME_OTHER_FONT_FOR_THE_PRIORITY_MENU
|
||
|
||
// Reconfigure the system font so that the menu will be drawn in our desired
|
||
// font and size
|
||
if ( mTextTraitsID ) {
|
||
FocusDraw();
|
||
TextTraitsH traitsH = UTextTraits::LoadTextTraits(mTextTraitsID);
|
||
if ( traitsH ) {
|
||
// Bug #64133
|
||
// If setting to application font, get the application font for current script
|
||
if((**traitsH).fontNumber == 1)
|
||
::LMSetSysFontFam ( ::GetScriptVariable(::FontToScript(1), smScriptAppFond) );
|
||
else
|
||
::LMSetSysFontFam ( (**traitsH).fontNumber );
|
||
::LMSetSysFontSize((**traitsH).size);
|
||
::LMSetLastSPExtra(-1L);
|
||
}
|
||
}
|
||
#endif
|
||
// Handle the actual insertion into the hierarchical menubar
|
||
::InsertMenu(menuH, hierMenu);
|
||
|
||
// Call PopupMenuSelect and wait for it to return
|
||
Int32 result = ::PopUpMenuSelect(menuH, where.v, where.h, defaultChoice);
|
||
|
||
// Extract the values from the returned result.
|
||
menuItem = LoWord(result);
|
||
}
|
||
catch(...) {
|
||
// Ignore errors
|
||
}
|
||
|
||
// this is the last one of these that you'd want to take out in order
|
||
// to re-enable the ability to use some font (other than the system font)
|
||
// to draw the priority context menu
|
||
#ifdef USE_SOME_OTHER_FONT_FOR_THE_PRIORITY_MENU
|
||
// Restore the system font
|
||
::LMSetSysFontFam(saveFont);
|
||
::LMSetSysFontSize(saveSize);
|
||
::LMSetLastSPExtra(-1L);
|
||
#endif
|
||
// Finally get the menu removed
|
||
::DeleteMenu(kPriorityMenuID);
|
||
}
|
||
|
||
if (menuItem)
|
||
{
|
||
MSG_PRIORITY newPriority = CMessage::MenuItemToPriority(menuItem);
|
||
if (newPriority != oldPriority)
|
||
{
|
||
// BE bug? We're not getting told that a change happened. So
|
||
// do it ourselves. If this gets fixed later, we can remove these
|
||
// change calls.
|
||
ChangeStarting(GetMessagePane(), MSG_NotifyChanged, inCell.row, 1);
|
||
MSG_SetPriority(GetMessagePane(), message.GetMessageKey(), newPriority);
|
||
ChangeFinished(GetMessagePane(), MSG_NotifyChanged, inCell.row, 1);
|
||
CMessage::InvalidateCache(); // Because BE has changed the message state
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
} // CThreadView::ClickCell
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
TableIndexT CThreadView::GetHiliteColumn() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return mTableHeader->ColumnFromID(kSubjectMessageColumn);
|
||
} // CThreadView::GetHiliteColumn
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SetCellExpansion(
|
||
const STableCell& inCell,
|
||
Boolean inExpanded)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Boolean currentlyExpanded;
|
||
if (!CellHasDropFlag(inCell, currentlyExpanded) || (inExpanded == currentlyExpanded))
|
||
return;
|
||
ToggleExpandAction(inCell.row);
|
||
CMessage::InvalidateCache(); // Because BE has changed the message state
|
||
} // CThreadView::SetCellExpansion
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::CellInitiatesDrag(const STableCell& inCell) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT cellType = GetCellDataType(inCell);
|
||
return (cellType == kSubjectMessageColumn
|
||
|| (cellType == kThreadMessageColumn && GetSortedColumn() == kThreadMessageColumn));
|
||
} // CThreadView::CellInitiatesDrag
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::CellHasDropFlag(
|
||
const STableCell& inCell,
|
||
Boolean& outExpanded) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT cellType = GetCellDataType(inCell);
|
||
|
||
// Only show drop flags when we're sorted by thread
|
||
if (cellType == kSubjectMessageColumn && GetSortedColumn() == kThreadMessageColumn)
|
||
{
|
||
CMessage message(inCell.row, GetMessagePane());
|
||
if (message.IsThread())
|
||
{
|
||
outExpanded = message.IsOpenThread();
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
} // CThreadView::CellHasDropFlag
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
TableIndexT CThreadView::CountExtraRowsControlledByCell(const STableCell& inCell) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT cellType = GetCellDataType(inCell);
|
||
// Only show drop flags when we're sorted by thread
|
||
if (cellType != kThreadMessageColumn || GetSortedColumn() != kThreadMessageColumn)
|
||
return 0;
|
||
int32 msgExpansionDelta = MSG_ExpansionDelta(GetMessagePane(), inCell.row - 1);
|
||
if (msgExpansionDelta >= 0)
|
||
return 0;
|
||
return -msgExpansionDelta;
|
||
} // CThreadView::CountExtraRowsControlledByCell
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::OpenRow(TableIndexT inRow)
|
||
// Open the message into its own window.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Assert_(mContext);
|
||
|
||
// Check the application heap.
|
||
if (Memory_MemoryIsLow() || ::MaxBlock() < 100*1024)
|
||
throw memFullErr;
|
||
// Check the Mozilla allocator
|
||
void* temp = XP_ALLOC(20*1024);
|
||
if (!temp)
|
||
throw memFullErr;
|
||
XP_FREE(temp);
|
||
|
||
::SetCursor(*::GetCursor(watchCursor));
|
||
Boolean multipleSelection = GetSelectedRowCount() > 1;
|
||
CMessage message(inRow, GetMessagePane());
|
||
// if a Draft, template, or Queue message open the compose window
|
||
if (
|
||
(mXPFolder.GetFolderFlags()
|
||
& (MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_TEMPLATES))
|
||
|| message.IsTemplate()
|
||
)
|
||
{
|
||
::MSG_OpenDraft(GetMessagePane() , mXPFolder.GetFolderInfo(), message.GetMessageKey());
|
||
return;
|
||
}
|
||
// First see if there's an open window open to this message. If there is,
|
||
// bring it to the front.
|
||
CMessageWindow* messageWindow = CMessageWindow::FindAndShow(message.GetMessageKey());
|
||
if (messageWindow)
|
||
{
|
||
messageWindow->GetMessageView()->SetDefaultCSID(mContext->GetDefaultCSID());
|
||
// Found it. Bring it to the front.
|
||
messageWindow->Select();
|
||
return;
|
||
}
|
||
// First, if recycling a window is indicated, ask for any existing one.
|
||
if (mContext->GetCurrentCommand() != cmd_OpenSelectionNewWindow)
|
||
{
|
||
// With a multiple selection, we never recycle!
|
||
XP_Bool prefReuseWindow = 0; // recycle any message window
|
||
PREF_GetBoolPref("mailnews.reuse_message_window", &prefReuseWindow);
|
||
if (!multipleSelection &&
|
||
(CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) ^ prefReuseWindow))
|
||
{
|
||
messageWindow = CMessageWindow::FindAndShow(0);
|
||
}
|
||
}
|
||
// If we couldn't (or shouldn't) recycle one, make a new one.
|
||
if (!messageWindow)
|
||
{
|
||
try
|
||
{
|
||
messageWindow =
|
||
(CMessageWindow*)URobustCreateWindow::CreateWindow(
|
||
CMessageWindow::res_ID,
|
||
LCommander::GetTopCommander());
|
||
CBrowserContext* theContext = new CBrowserContext(MWContextMailMsg);
|
||
StSharer theShareLock(theContext); // if we throw, theContext will have no users & die
|
||
messageWindow->SetWindowContext(theContext);
|
||
}
|
||
catch(...)
|
||
{
|
||
delete messageWindow;
|
||
messageWindow = NULL;
|
||
throw;
|
||
}
|
||
}
|
||
// Whether it's a new one or an old one, load the message now.
|
||
if (messageWindow)
|
||
{
|
||
try
|
||
{
|
||
messageWindow->GetMessageView()->ShowMessage(
|
||
CMailNewsContext::GetMailMaster(),
|
||
mXPFolder.GetFolderInfo(),
|
||
message.GetMessageKey()); // pass flags to tell what sort of message it is
|
||
messageWindow->GetMessageView()->SetDefaultCSID(mContext->GetDefaultCSID());
|
||
messageWindow->Select();
|
||
// messageWindow->Show();
|
||
// The window will now show itself when a successful load occurs
|
||
}
|
||
catch(...)
|
||
{
|
||
delete messageWindow;
|
||
messageWindow = NULL;
|
||
throw;
|
||
}
|
||
}
|
||
CMessage::InvalidateCache(); // So that the "read" flag gets changed.
|
||
} // CThreadView::OpenRow
|
||
|
||
#define THREE_PANE 1
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SaveSelectedMessage()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
MSG_Pane* curPane = GetMessagePane();
|
||
if (curPane)
|
||
{
|
||
MSG_FolderInfo* curFolder = ::MSG_GetThreadFolder(curPane);
|
||
if (curFolder)
|
||
{
|
||
if (GetSelectedRowCount() == 1)
|
||
{
|
||
TableIndexT curRow = mTableSelector->GetFirstSelectedRow();
|
||
CMessage message(curRow, curPane);
|
||
MessageKey curMessage = message.GetMessageKey();
|
||
::MSG_SetLastMessageLoaded(curFolder, curMessage);
|
||
}
|
||
else
|
||
::MSG_SetLastMessageLoaded(curFolder, MSG_MESSAGEKEYNONE);
|
||
}
|
||
}
|
||
} // CThreadView::SaveSelectedMessage
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::RestoreSelectedMessage()
|
||
// Called on FolderLoaded pane notification. We probably should check the preference.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
XP_Bool remember = true;
|
||
if (PREF_NOERROR != PREF_GetBoolPref("mailnews.remember_selected_message", &remember))
|
||
return false;
|
||
if (!remember)
|
||
return false;
|
||
MSG_Pane* curPane = GetMessagePane();
|
||
if (curPane)
|
||
{
|
||
MSG_FolderInfo* curFolder = ::MSG_GetThreadFolder(curPane);
|
||
if (curFolder)
|
||
{
|
||
MessageKey savedMessage = ::MSG_GetLastMessageLoaded(curFolder);
|
||
if (savedMessage != MSG_MESSAGEKEYNONE)
|
||
{
|
||
MSG_ViewIndex index = ::MSG_GetMessageIndexForKey(curPane, savedMessage, true);
|
||
if (index != MSG_VIEWINDEXNONE)
|
||
{
|
||
mRowToSelect = index + 1;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
} // CThreadView::RestoreSelectedMessage
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::LoadMessageFolder(
|
||
CNSContext* inContext, // My window's context
|
||
const CMessageFolder& inFolder,
|
||
Boolean inLoadNow)
|
||
// Load a given message folder into the message table view.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Assert_(inContext);
|
||
// Load the folder into the message list. We'll get a PaneChanged message
|
||
// when loading is complete, and another one to say that the folder info
|
||
// has changed. When these callbacks occur, we'll call SetFolder(). However,
|
||
// the first time the window is initialized, the title needs to look nice
|
||
// even during the load process. Hence this call, which will be made twice:
|
||
SetFolder(inFolder);
|
||
if (!inLoadNow)
|
||
{
|
||
CDeferredLoadFolderTask* task
|
||
= new CDeferredLoadFolderTask(this, inContext, inFolder);
|
||
CDeferredTaskManager::Post1(task, this);
|
||
return;
|
||
}
|
||
try
|
||
{
|
||
::SetCursor(*::GetCursor(watchCursor));
|
||
SaveSelectedMessage();
|
||
UnselectAllCells();
|
||
// Else it crashes, because the messageview (if displaying a message), doesn't
|
||
// get cleaned up properly. #81060
|
||
mContext = inContext;
|
||
#if !ONECONTEXTPERWINDOW
|
||
SetMessagePane(nil); // release the memory first.
|
||
#endif
|
||
// A null inFolder is possible, and the result should be to clear the thread area.
|
||
// This behavior is new in 5.0 with the 3-pane window - 97/10/06
|
||
if (!inFolder.GetFolderInfo())
|
||
{
|
||
RemoveAllRows( true );
|
||
SetMessagePane(nil);
|
||
return;
|
||
}
|
||
#if ONECONTEXTPERWINDOW
|
||
if (!GetMessagePane())
|
||
#endif
|
||
{
|
||
SetMessagePane(::MSG_CreateThreadPane(*inContext, CMailNewsContext::GetMailMaster()));
|
||
ThrowIfNULL_(GetMessagePane());
|
||
// plug into the view so we get notified when the data changes
|
||
::MSG_SetFEData(GetMessagePane(), CMailCallbackManager::Get());
|
||
}
|
||
if (inFolder.GetFolderInfo() == MSG_GetThreadFolder(GetMessagePane()))
|
||
return;
|
||
|
||
#if USING_MODAL_FOLDER_LOAD
|
||
CStr255 commandString;
|
||
::GetIndString(commandString, 7099, 16); // "Loading Folder"
|
||
CMailProgressWindow::CreateModalParasite(
|
||
CMailProgressWindow::res_ID_modal,
|
||
GetMessagePane(),
|
||
commandString);
|
||
#endif
|
||
// Close any other windows showing this folder
|
||
CWindowIterator iter(WindowType_MailThread);
|
||
CThreadWindow* thisWindow
|
||
= dynamic_cast<CThreadWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
||
|
||
CThreadWindow* window;
|
||
for (iter.Next(window); window; iter.Next(window))
|
||
{
|
||
window = dynamic_cast<CThreadWindow*>(window);
|
||
if (window && window != thisWindow)
|
||
{
|
||
CMessageView* messageView = window->GetMessageView();
|
||
if (inFolder.GetFolderInfo() == messageView->GetFolderInfo())
|
||
window->AttemptClose();
|
||
}
|
||
}
|
||
#if 1
|
||
// IMAP mailboxes do "get new mail" automatically. So set up the flags to expect new
|
||
// mail.
|
||
if ((inFolder.GetFolderFlags() & MSG_FOLDER_FLAG_IMAPBOX) == MSG_FOLDER_FLAG_IMAPBOX)
|
||
ExpectNewMail();
|
||
#else
|
||
// IMAP inbox does "get new mail" automatically. So set up the flags to expect new
|
||
// mail.
|
||
#define IMAP_INBOX (MSG_FOLDER_FLAG_INBOX | MSG_FOLDER_FLAG_IMAPBOX)
|
||
if ((inFolder.GetFolderFlags() & IMAP_INBOX) == IMAP_INBOX)
|
||
ExpectNewMail();
|
||
#endif
|
||
// Now finally, load the folder
|
||
ThrowIfError_(::MSG_LoadFolder(GetMessagePane(), inFolder.GetFolderInfo()));
|
||
|
||
#ifndef THREE_PANE
|
||
SwitchTarget(this);
|
||
#endif
|
||
|
||
// Change the i18n encoding and font to match the new folder:
|
||
int foldercsid = ::MSG_GetFolderCSID(inFolder.GetFolderInfo());
|
||
if ((CS_UNKNOWN == foldercsid) || (CS_DEFAULT == foldercsid))
|
||
{
|
||
foldercsid = INTL_DefaultDocCharSetID(0);
|
||
::MSG_SetFolderCSID(inFolder.GetFolderInfo(), foldercsid);
|
||
}
|
||
SetDefaultCSID(foldercsid);
|
||
ResetTextTraits(); // May need to change the font when we change folder
|
||
}
|
||
catch(...)
|
||
{
|
||
}
|
||
} // CThreadView::LoadMessageFolder
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::RelocateViewToFolder(const CMessageFolder& inFolder)
|
||
// Retarget the view to the specified BE folder.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (!inFolder.GetFolderInfo() || inFolder == mXPFolder)
|
||
return false;
|
||
|
||
if (CThreadWindow::FindAndShow(inFolder.GetFolderInfo()) == nil)
|
||
{
|
||
MSG_Master* master = CMailNewsContext::GetMailMaster();
|
||
LoadMessageFolder(mContext, inFolder);
|
||
return true;
|
||
}
|
||
return false;
|
||
} // CThreadWindow::RelocateViewToFolder
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::FileMessagesToSelectedPopupFolder(const char *inFolderName,
|
||
Boolean inMoveMessages)
|
||
// File selected messages to the specified BE folder. If inMoveMessages is true, move
|
||
// the messages, otherwise copy them.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (!inFolderName || !*inFolderName)
|
||
return;
|
||
CMailSelection selection;
|
||
if ( !GetSelection(selection) ) {
|
||
Assert_(false); // Method should not be called in this case!
|
||
return; // Nothing selected!
|
||
}
|
||
|
||
#ifdef Debug_Signal
|
||
const char *curName = ::MSG_GetFolderNameFromID(GetOwningFolder());
|
||
Assert_(strcasecomp(curName, inFolderName) != 0); // Specified folder should not be the same as this folder
|
||
#endif // Debug_Signal
|
||
|
||
CStr255 commandString;
|
||
::GetIndString(commandString, 7099, (inMoveMessages ? 15 : 19)); // "Moving/Copying Messages"
|
||
CMailProgressWindow* progressWindow = CMailProgressWindow::CreateModalParasite(
|
||
CMailProgressWindow::res_ID_modal,
|
||
GetMessagePane(),
|
||
commandString);
|
||
if (progressWindow)
|
||
progressWindow->SetDelay(0);
|
||
if ( inMoveMessages )
|
||
::MSG_MoveMessagesInto(selection.xpPane, selection.GetSelectionList(), selection.selectionSize,
|
||
inFolderName);
|
||
else
|
||
::MSG_CopyMessagesInto(selection.xpPane, selection.GetSelectionList(), selection.selectionSize,
|
||
inFolderName);
|
||
mUndoCommand = cmd_Undo;
|
||
} // CThreadWindow::FileMessagesToSelectedPopupFolder
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::ScrollToGoodPlace()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mRows == 0)
|
||
return false;
|
||
MessageKey id;
|
||
MSG_ViewIndex index;
|
||
MSG_ViewIndex threadIndex;
|
||
MSG_ViewNavigate(GetMessagePane(), MSG_FirstNew, 0, &id, &index, &threadIndex, NULL);
|
||
if (index != MSG_VIEWINDEXNONE)
|
||
{
|
||
ScrollRowIntoFrame(mRows); // show bottom cell...
|
||
ScrollRowIntoFrame(index + 1); // ... then show first unread.
|
||
return true;
|
||
}
|
||
return false;
|
||
} // CThreadView::ScrollToGoodPlace
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::NoteSortByThreadColumn(Boolean isThreaded) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (isThreaded)
|
||
GetTableHeader()->ChangeIconOfColumn(kThreadMessageColumn, kThreadedIcon);
|
||
else
|
||
GetTableHeader()->ChangeIconOfColumn(kThreadMessageColumn, kUnthreadedIcon);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ChangeSort(const LTableHeader::SortChange* inSortChange)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
CStandardFlexTable::ChangeSort(inSortChange);
|
||
if (GetMessagePane() != NULL)
|
||
{
|
||
MSG_CommandType sortCommand;
|
||
switch (inSortChange->sortColumnID)
|
||
{
|
||
case kDateMessageColumn: sortCommand = MSG_SortByDate; break;
|
||
case kSubjectMessageColumn: sortCommand = MSG_SortBySubject; break;
|
||
case kThreadMessageColumn: sortCommand = MSG_SortByThread; break;
|
||
case kSenderMessageColumn:
|
||
case kAddresseeMessageColumn: sortCommand = MSG_SortBySender; break;
|
||
|
||
case kMarkedReadMessageColumn: sortCommand = MSG_SortByUnread; break;
|
||
case kPriorityMessageColumn: sortCommand = MSG_SortByPriority; break;
|
||
case kSizeMessageColumn: sortCommand = MSG_SortBySize; break;
|
||
case kStatusMessageColumn: sortCommand = MSG_SortByStatus; break;
|
||
case kFlagMessageColumn: sortCommand = MSG_SortByFlagged; break;
|
||
case kHiddenOrderReceivedColumn: sortCommand = MSG_SortByMessageNumber; break;
|
||
//case kUnreadMessageColumn: sortCommand =
|
||
//case kTotalMessageColumn: sortCommand =
|
||
|
||
default: sortCommand = (MSG_CommandType) 0;
|
||
}
|
||
if (sortCommand != 0)
|
||
{
|
||
CMailProgressWindow::RemoveModalParasite();
|
||
::MSG_Command(GetMessagePane(), sortCommand, NULL, 0);
|
||
// Make sure the menus and headers reflect the new state of affairs
|
||
SyncSortToBackend();
|
||
}
|
||
}
|
||
} // CThreadView::ChangeSort
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DeleteSelection()
|
||
// Delete all selected messages
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
try
|
||
{
|
||
CMailSelection selection;
|
||
if (GetSelection(selection))
|
||
{
|
||
#if 0
|
||
// Don't do this any more! Interrupting IMAP will cause a crash if the timing
|
||
// is just right. 98/03/31
|
||
CThreadWindow* threadWindow
|
||
= dynamic_cast<CThreadWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
||
XP_ASSERT(threadWindow);
|
||
CMessageView* messageView
|
||
= dynamic_cast<CMessageView *>(threadWindow->
|
||
FindPaneByID(CMessageView::class_ID));
|
||
XP_ASSERT(messageView);
|
||
messageView->ObeyCommand(cmd_Stop, NULL);
|
||
#endif
|
||
// Try to close any open windows on these messages
|
||
const MSG_ViewIndex* index = selection.GetSelectionList();
|
||
for (int i = 0; i < selection.selectionSize; i++,index++)
|
||
{
|
||
CMessage message((*index + 1), GetMessagePane());
|
||
// (Add one to convert to TableIndexT)
|
||
CMessageWindow::CloseAll(message.GetMessageKey());
|
||
}
|
||
MSG_CommandType cmd =
|
||
((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) == 0)
|
||
? UMessageLibrary::GetMSGCommand(cmd_Clear)
|
||
: MSG_CancelMessage; // cancel my posting, if possible.
|
||
CMailProgressWindow::RemoveModalParasite();
|
||
MSG_Command(
|
||
GetMessagePane(),
|
||
cmd,
|
||
(MSG_ViewIndex*)selection.GetSelectionList(),
|
||
selection.selectionSize);
|
||
UnselectAllCells();
|
||
}
|
||
// The selection of a new message after one is removed is handled by a deferred task.
|
||
// We used to start this timer immediately on initiating the delete operation (here)
|
||
// but this ended up firing before the refresh occurred, resulting in two rows
|
||
// selected, which was ugly. So we moved the posting call to DrawCellContents,
|
||
// to make sure that the row isn't selected until after the refresh is done. 98/01/14
|
||
}
|
||
catch(...) {}
|
||
} // CThreadView::DeleteSelection()
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ChangeStarting(
|
||
MSG_Pane* inPane,
|
||
MSG_NOTIFY_CODE inChangeCode,
|
||
TableIndexT inStartRow,
|
||
SInt32 inRowCount)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mMysticPlane <= kMysticUpdateThreshHold
|
||
&& (inChangeCode == MSG_NotifyScramble || inChangeCode == MSG_NotifyAll))
|
||
{
|
||
try
|
||
{
|
||
// Get the selection, convert it to non-volatile MSG_FolderInfo...
|
||
CMailSelection selection;
|
||
if (GetSelection(selection))
|
||
{
|
||
delete mSavedSelection;
|
||
mSavedSelection = new CPersistentMessageSelection(selection);
|
||
}
|
||
}
|
||
catch (...) {}
|
||
}
|
||
Inherited::ChangeStarting(inPane, inChangeCode, inStartRow, inRowCount);
|
||
} // CThreadView::ChangeStarting
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ChangeFinished(
|
||
MSG_Pane* inPane,
|
||
MSG_NOTIFY_CODE inChangeCode,
|
||
TableIndexT inStartRow,
|
||
SInt32 inRowCount)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Inherited::ChangeFinished(inPane, inChangeCode, inStartRow, inRowCount);
|
||
if (mMysticPlane <= kMysticUpdateThreshHold)
|
||
{
|
||
switch (inChangeCode)
|
||
{
|
||
case MSG_NotifyScramble:
|
||
case MSG_NotifyAll:
|
||
{
|
||
SetNotifyOnSelectionChange(false);
|
||
try
|
||
{
|
||
UnselectAllCells();
|
||
if (mSavedSelection)
|
||
{
|
||
const MSG_ViewIndex* index = mSavedSelection->GetSelectionList();
|
||
MSG_ViewIndex count = mSavedSelection->selectionSize;
|
||
for (MSG_ViewIndex i = 0; i < count; i++, index++)
|
||
{
|
||
if (*index != MSG_VIEWINDEXNONE)
|
||
SelectRow(1 + *index);
|
||
}
|
||
}
|
||
}
|
||
catch (...) {}
|
||
SetNotifyOnSelectionChange(true);
|
||
SelectionChanged();
|
||
delete mSavedSelection;
|
||
mSavedSelection = NULL;
|
||
}
|
||
break;
|
||
case MSG_NotifyInsertOrDelete:
|
||
if (CMailProgressWindow::GettingMail()) // could happen from the folder pane etc.
|
||
mExpectingNewMail = true;
|
||
if (mExpectingNewMail || inRowCount > 0)
|
||
mGotNewMail = true;
|
||
// If we're deleting the only selected row, we may need to select
|
||
// something later. Don't do this as a result of "get new mail", though.
|
||
// The intention here is that this behavior is only to happen as a result
|
||
// of an explicit user delete, on the current machine (Note that you may
|
||
// log on to another machine and delete the message that is currently
|
||
// selected. Note also that, during folder load, filters can cause a
|
||
// "row-deleted" message here.)
|
||
if (inRowCount == -1 // one row was deleted
|
||
&& mSelectAfterDelete // the preference to do this is on
|
||
&& GetSelectedRowCount() == 0 // there is nothing selected now
|
||
&& inStartRow <= mRows + 1 // range check (+1 if deleting last row)
|
||
&& !mExpectingNewMail) // this is not a folder load, or get mail
|
||
mRowToSelect = inStartRow;
|
||
break;
|
||
}
|
||
CMessage::InvalidateCache(); // most changes will make the index invalid.
|
||
}
|
||
} // CThreadView::ChangeFinished
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SetFolder(const CMessageFolder& inFolder)
|
||
// Set the window title to the folder name.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
mXPFolder = inFolder;
|
||
|
||
Boolean isNewsgroup = ((mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) != 0);
|
||
|
||
char newName[255];
|
||
const char* nameToUse = (inFolder.GetPrettyName() ?
|
||
inFolder.GetPrettyName() : inFolder.GetName());
|
||
if (nameToUse)
|
||
{
|
||
strcpy(newName, nameToUse);
|
||
NET_UnEscape(newName);
|
||
}
|
||
else
|
||
*newName = '\0';
|
||
|
||
CThreadWindow* threadWindow
|
||
= dynamic_cast<CThreadWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
||
if (threadWindow)
|
||
threadWindow->SetFolderName(newName, isNewsgroup);
|
||
} // CThreadView::SetFolderName
|
||
|
||
// -------------------------
|
||
void CThreadView::SyncSortToBackend()
|
||
//---------------------------
|
||
{
|
||
// This is ugly it would be better if we did this in FindCommandStatus.
|
||
// since the BE could provide us the needed info. The difficutly would be in keeping
|
||
// the headers in sync. If done properly would help simplify FindCommandStatus
|
||
// and eliminate all this extra stuff
|
||
// Something to think about for 5.0
|
||
MSG_Pane* inPane = GetMessagePane();
|
||
XP_Bool selectable;
|
||
MSG_COMMAND_CHECK_STATE checkedState;
|
||
const char* display_string = nil;
|
||
MSG_CommandType cmd;
|
||
XP_Bool plural;
|
||
for (cmd = MSG_SortByDate;
|
||
cmd <= MSG_SortByUnread;
|
||
cmd = MSG_CommandType( int(cmd) + 1))
|
||
{
|
||
::MSG_CommandStatus(
|
||
inPane, cmd, nil, 0, &selectable, &checkedState, &display_string, &plural );
|
||
if (checkedState == MSG_Checked)
|
||
break;
|
||
}
|
||
PaneIDT pane = 0;
|
||
switch ( cmd )
|
||
{
|
||
case MSG_SortByDate: pane = kDateMessageColumn; break;
|
||
case MSG_SortBySubject: pane = kSubjectMessageColumn; break;
|
||
case MSG_SortBySender: pane = kSenderMessageColumn; break;
|
||
case MSG_SortByUnread: pane = kMarkedReadMessageColumn;break;
|
||
case MSG_SortByPriority: pane = kPriorityMessageColumn; break;
|
||
case MSG_SortBySize: pane = kSizeMessageColumn; break;
|
||
case MSG_SortByStatus: pane = kStatusMessageColumn; break;
|
||
case MSG_SortByFlagged: pane = kFlagMessageColumn; break;
|
||
case MSG_SortByMessageNumber: pane = kHiddenOrderReceivedColumn; break;
|
||
default:
|
||
case MSG_SortByThread: pane = kThreadMessageColumn; break;
|
||
}
|
||
NoteSortByThreadColumn(cmd == MSG_SortByThread);
|
||
UInt16 col = mTableHeader->ColumnFromID( pane );
|
||
::MSG_CommandStatus(
|
||
inPane, MSG_SortBackward, nil, 0, &selectable, &checkedState,
|
||
&display_string, &plural);
|
||
Boolean backwards = checkedState == MSG_Checked;
|
||
mTableHeader->SetWithoutSort( col, backwards, true );
|
||
// Make sure the sort submenus on the menu bar are showing the correct state.
|
||
UpdateSortMenuCommands();
|
||
} // CThreadView::SyncSortToBackend()
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::PaneChanged(
|
||
MSG_Pane* inPane,
|
||
MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode,
|
||
int32 value)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
switch (inNotifyCode)
|
||
{
|
||
case MSG_PaneNotifyFolderLoaded:
|
||
if (value > 0)
|
||
{
|
||
LWindow* win = LWindow::FetchWindowObject(GetMacPort());
|
||
if (win)
|
||
win->Show();
|
||
SetFolder(CMessageFolder((MSG_FolderInfo*)value));
|
||
CMessage::InvalidateCache();
|
||
SetRowCount();
|
||
Refresh();
|
||
}
|
||
// This is the first opportunity to do stuff that needs the folder etc.
|
||
// Update history entry and load an empty message into the message view.
|
||
// Calling SelectionChanged will do these things.
|
||
|
||
SelectionChanged();
|
||
|
||
EnableStopButton(false);
|
||
if (mMotionPendingCommand != (MSG_MotionType)-1)
|
||
{
|
||
ObeyMotionCommand( mMotionPendingCommand );
|
||
mMotionPendingCommand = (MSG_MotionType)-1;
|
||
}
|
||
#ifdef PREFERENCE_IMPLEMENTED
|
||
if (!RestoreSelectedMessage())
|
||
ScrollToGoodPlace();
|
||
#else
|
||
RestoreSelectedMessage(); // select the last selected message AND
|
||
mScrollToShowNew = true;
|
||
//ScrollToGoodPlace(); // scroll to show anything new.
|
||
#endif
|
||
DontExpectNewMail();
|
||
// break; NO! Fall through, because we need to update the rowcount and sort order.
|
||
case MSG_PaneNotifyFolderInfoChanged:
|
||
if (mXPFolder == (MSG_FolderInfo*)value)
|
||
{
|
||
SyncSortToBackend();
|
||
CMessage::InvalidateCache();
|
||
}
|
||
mXPFolder.FolderInfoChanged();
|
||
break;
|
||
case MSG_PaneProgressDone: // comes from progress window
|
||
if ((MessageT)value == msg_NSCAllConnectionsComplete)
|
||
{
|
||
if (GotNewMail()) // ie in THIS folder in THIS operation
|
||
{
|
||
CMessage::InvalidateCache();
|
||
SetRowCount();
|
||
Refresh();
|
||
ScrollToGoodPlace();
|
||
}
|
||
if (CMailProgressWindow::GettingMail())
|
||
DontExpectNewMail();
|
||
}
|
||
break;
|
||
case MSG_PaneNotifyFolderDeleted:
|
||
if ((MSG_FolderInfo*)value == mXPFolder.GetFolderInfo())
|
||
{
|
||
RemoveAllRows( false );
|
||
::MSG_LoadFolder(GetMessagePane(), nil);
|
||
mXPFolder.SetFolderInfo(nil); // prevent crashes
|
||
}
|
||
break;
|
||
default:
|
||
Inherited::PaneChanged(inPane, inNotifyCode, value);
|
||
}
|
||
} // CThreadView::PaneChanged
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ResizeFrameBy(
|
||
Int16 inWidthDelta,
|
||
Int16 inHeightDelta,
|
||
Boolean inRefresh)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Inherited::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh);
|
||
ScrollSelectionIntoFrame();
|
||
} // CThreadView::ResizeFrameBy
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::FindSortCommandStatus(CommandT inCommand, Int16& outMark)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT paneID = GetSortedColumn();
|
||
outMark = 0;
|
||
switch (inCommand)
|
||
{
|
||
case cmd_SortByDate:
|
||
if (paneID == kDateMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortBySubject:
|
||
if (paneID == kSubjectMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortBySender:
|
||
if (paneID == kSenderMessageColumn || paneID == kAddresseeMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortByThread:
|
||
if (paneID == kThreadMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortByPriority:
|
||
if (paneID == kPriorityMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortBySize:
|
||
if (paneID == kSizeMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortByReadness:
|
||
if (paneID == kMarkedReadMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortByStatus:
|
||
if (paneID == kStatusMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortByFlagged:
|
||
if (paneID == kFlagMessageColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortByOrderReceived:
|
||
if (paneID == kHiddenOrderReceivedColumn)
|
||
outMark = checkMark;
|
||
break;
|
||
case cmd_SortAscending:
|
||
case cmd_SortDescending:
|
||
outMark =
|
||
((inCommand == cmd_SortDescending) == IsSortedBackwards()) ?
|
||
checkMark : 0;
|
||
break;
|
||
}
|
||
} // CThreadView::FindSortCommandStatus
|
||
|
||
// -------------------------
|
||
void CThreadView::UpdateSortMenuCommands() const
|
||
//---------------------------
|
||
{
|
||
list<CommandT> commandsToUpdate;
|
||
commandsToUpdate.push_front(cmd_SortByDate);
|
||
commandsToUpdate.push_front(cmd_SortBySubject);
|
||
commandsToUpdate.push_front(cmd_SortBySender);
|
||
commandsToUpdate.push_front(cmd_SortByThread);
|
||
commandsToUpdate.push_front(cmd_SortByPriority);
|
||
commandsToUpdate.push_front(cmd_SortBySize);
|
||
commandsToUpdate.push_front(cmd_SortByReadness);
|
||
commandsToUpdate.push_front(cmd_SortByStatus);
|
||
commandsToUpdate.push_front(cmd_SortByFlagged);
|
||
commandsToUpdate.push_front(cmd_SortByOrderReceived);
|
||
commandsToUpdate.push_front(cmd_SortAscending);
|
||
commandsToUpdate.push_front(cmd_SortDescending);
|
||
CTargetedUpdateMenuRegistry::SetCommands(commandsToUpdate);
|
||
CTargetedUpdateMenuRegistry::UpdateMenus();
|
||
} // CThreadView::UpdateSortMenuCommands
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::ObeySortCommand(CommandT inCommand)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT oldColumnID = GetSortedColumn();
|
||
Boolean oldReverse = IsSortedBackwards();
|
||
PaneIDT newColumnID;
|
||
Boolean newReverse = oldReverse;
|
||
switch (inCommand)
|
||
{
|
||
case cmd_SortByDate:
|
||
newColumnID = kDateMessageColumn;
|
||
break;
|
||
case cmd_SortBySubject:
|
||
newColumnID = kSubjectMessageColumn;
|
||
break;
|
||
case cmd_SortBySender:
|
||
newColumnID = kSenderMessageColumn;
|
||
break;
|
||
case cmd_SortByThread:
|
||
newColumnID = kThreadMessageColumn;
|
||
break;
|
||
case cmd_SortByPriority:
|
||
newColumnID = kPriorityMessageColumn;
|
||
break;
|
||
case cmd_SortBySize:
|
||
newColumnID = kSizeMessageColumn;
|
||
break;
|
||
case cmd_SortByReadness:
|
||
newColumnID = kMarkedReadMessageColumn;
|
||
break;
|
||
case cmd_SortByStatus:
|
||
newColumnID = kStatusMessageColumn;
|
||
break;
|
||
case cmd_SortByFlagged:
|
||
newColumnID = kFlagMessageColumn;
|
||
break;
|
||
case cmd_SortByOrderReceived:
|
||
newColumnID = kHiddenOrderReceivedColumn;
|
||
break;
|
||
case cmd_SortAscending:
|
||
case cmd_SortDescending:
|
||
newColumnID = GetSortedColumn();
|
||
newReverse = (cmd_SortDescending == inCommand);
|
||
break;
|
||
default:
|
||
return false;
|
||
}
|
||
if (oldColumnID != newColumnID || oldReverse != newReverse)
|
||
{
|
||
mTableHeader->SetSortedColumn(mTableHeader->ColumnFromID(newColumnID), newReverse, true);
|
||
}
|
||
return true;
|
||
} // CThreadView::ObeySortCommand
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::FindCommandStatus(
|
||
CommandT inCommand,
|
||
Boolean &outEnabled,
|
||
Boolean &outUsesMark,
|
||
Char16 &outMark,
|
||
Str255 outName)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
outEnabled =false; // This is probably unnecessary, I think PP does it before calling.
|
||
if (::StillDown())
|
||
{
|
||
// Assume this is a call from the context menu attachment.
|
||
Point whereLocal;
|
||
::GetMouse(&whereLocal);
|
||
SPoint32 imagePoint;
|
||
LocalToImagePoint(whereLocal, imagePoint);
|
||
STableCell hitCell;
|
||
if (GetCellHitBy(imagePoint, hitCell))
|
||
{
|
||
PaneIDT cellType = GetCellDataType(hitCell);
|
||
if (cellType == kPriorityMessageColumn)
|
||
return; // disable all commands, because we do it ourselves.
|
||
// Fix me: there's no reason why the priority popup can't be done
|
||
// using the context menu mechanism now.
|
||
}
|
||
}
|
||
if (inCommand == cmd_Stop && mStillLoading)
|
||
{
|
||
outEnabled = true; // stop button on, nothing else.
|
||
return;
|
||
// ... otherwise, fall through and pass it up to the window
|
||
}
|
||
// Note: for cmd_Stop, the window may also be able to handle the command, if
|
||
// a message pane exists.
|
||
CMailSelection selection;
|
||
GetSelection(selection);
|
||
switch (inCommand)
|
||
{
|
||
// Single-selection items
|
||
case cmd_SelectThread:
|
||
outEnabled = selection.selectionSize == 1;
|
||
return;
|
||
// Items always available
|
||
case cmd_SubscribeNewsgroups:
|
||
outEnabled = true;
|
||
return;
|
||
case cmd_AddToBookmarks:
|
||
outEnabled = (mXPFolder.GetFolderFlags() & MSG_FOLDER_FLAG_IMAPBOX) == 0;
|
||
return;
|
||
case cmd_SelectMarkedMessages:
|
||
case cmd_MarkReadByDate:
|
||
case cmd_NewFolder:
|
||
outEnabled = true;
|
||
outUsesMark = false;
|
||
return;
|
||
case cmd_GetMoreMessages:
|
||
CStr255 cmdStr;
|
||
CStr255 numStr;
|
||
::GetIndString(cmdStr, kMailNewsMenuStrings, kNextChunkMessagesStrID);
|
||
::NumToString(CPrefs::GetLong(CPrefs::NewsArticlesMax), numStr);
|
||
cmdStr += numStr;
|
||
memcpy(outName, (StringPtr)cmdStr, cmdStr.Length() + 1);
|
||
break;
|
||
}
|
||
// Now check for message library commands. Remember (###), some of these are
|
||
// handled by CMessageView(), and that might be our super commander, so
|
||
// return here only if msglib enables the item for the thread pane.
|
||
// Also don't want the message pane to only deal with cases where 1 message is selected
|
||
if (FindMessageLibraryCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName)
|
||
&& ( outEnabled || selection.selectionSize != 1) )
|
||
return;
|
||
MSG_MotionType cmd = UMessageLibrary::GetMotionType(inCommand);
|
||
if (UMessageLibrary::IsValidMotion(cmd))
|
||
{
|
||
XP_Bool selectable;
|
||
outEnabled = (
|
||
GetMessagePane()
|
||
&& MSG_NavigateStatus(
|
||
GetMessagePane(),
|
||
cmd,
|
||
selection.GetSelectionList() ? *selection.GetSelectionList() : MSG_VIEWINDEXNONE,
|
||
&selectable,
|
||
NULL) == 0
|
||
&& selectable
|
||
);
|
||
outUsesMark = false;
|
||
if (outEnabled) // ONLY in this case. See the previous comment (###).
|
||
return;
|
||
}
|
||
if ( (inCommand == cmd_MoveMailMessages) || (inCommand == cmd_CopyMailMessages) ||
|
||
CMailFolderSubmenu::IsMailFolderCommand(&inCommand) ) {
|
||
// Mail folder commands
|
||
outEnabled = (GetSelectedRowCount() > 0);
|
||
return;
|
||
}
|
||
// Default for if and switch: fall through into second switch
|
||
switch (inCommand)
|
||
{
|
||
case cmd_GetInfo:
|
||
outEnabled = false; // FIXME: implement it.
|
||
break;
|
||
case cmd_SortByDate:
|
||
case cmd_SortBySubject:
|
||
case cmd_SortBySender:
|
||
case cmd_SortByThread:
|
||
case cmd_SortByPriority:
|
||
case cmd_SortBySize:
|
||
// case cmd_SortByTotal:
|
||
case cmd_SortByReadness:
|
||
case cmd_SortByStatus:
|
||
case cmd_SortByFlagged:
|
||
case cmd_SortByOrderReceived:
|
||
case cmd_SortAscending:
|
||
case cmd_SortDescending:
|
||
if (!mStillLoading)
|
||
{
|
||
outUsesMark = true;
|
||
outEnabled = true;
|
||
FindSortCommandStatus(inCommand, outMark);
|
||
}
|
||
break;
|
||
default:
|
||
if (inCommand >= ENCODING_BASE && inCommand < ENCODING_CEILING)
|
||
{
|
||
if (mContext)
|
||
{
|
||
outUsesMark = true;
|
||
outEnabled = true;
|
||
|
||
int16 csid = CPrefs::CmdNumToDocCsid( inCommand );
|
||
outMark = (csid == mContext->GetDefaultCSID()) ? 0x12 : ' ';
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// if (inCommand == cmd_OpenSelection)
|
||
// outEnabled = false;
|
||
// else
|
||
Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName);
|
||
}
|
||
}
|
||
} // CThreadView::FindCommandStatus
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SelectAfterDelete(TableIndexT row)
|
||
// Called from ObeyCommand after command completion.
|
||
// In "expanded mode", the user probably wants a new message to
|
||
// be loaded into the message pane after a deletion.
|
||
// So detect the case when a message was showing and the message has
|
||
// been moved from the folder. We want to display a new message.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
SetUpdateCommandStatus(true); // Always do this.
|
||
if (row > 0)
|
||
SelectRow(row > mRows ? mRows : row);
|
||
} // CThreadView::SelectAfterDelete
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::ObeyMotionCommand(MSG_MotionType cmd)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (!GetMessagePane()) return false;
|
||
Assert_(UMessageLibrary::IsValidMotion(cmd));
|
||
try
|
||
{
|
||
CMailSelection selection;
|
||
GetSelection(selection);
|
||
MSG_ViewIndex resultIndex;
|
||
MessageKey resultKey = MSG_MESSAGEKEYNONE;
|
||
MSG_FolderInfo* finfo;
|
||
if (MSG_ViewNavigate(
|
||
GetMessagePane(),
|
||
cmd,
|
||
selection.GetSelectionList() ? *selection.GetSelectionList() : MSG_VIEWINDEXNONE,
|
||
&resultKey,
|
||
&resultIndex,
|
||
NULL,
|
||
&finfo) == 0)
|
||
{
|
||
if (resultKey != MSG_MESSAGEKEYNONE)
|
||
{
|
||
SetNotifyOnSelectionChange(false);
|
||
UnselectAllCells();
|
||
SetNotifyOnSelectionChange(true);
|
||
if (finfo && finfo != GetOwningFolder())
|
||
RelocateViewToFolder(finfo);
|
||
SelectMessageWhenReady(resultKey);
|
||
}
|
||
else if (finfo)
|
||
{
|
||
switch( cmd )
|
||
{
|
||
case MSG_NextFolder:
|
||
case MSG_NextMessage:
|
||
mMotionPendingCommand = MSG_FirstMessage;
|
||
break;
|
||
case MSG_NextUnreadMessage:
|
||
case MSG_NextUnreadThread:
|
||
case MSG_NextUnreadGroup:
|
||
case MSG_LaterMessage:
|
||
case (MSG_MotionType)MSG_ToggleThreadKilled:
|
||
mMotionPendingCommand = MSG_NextUnreadMessage;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
RelocateViewToFolder( finfo );
|
||
}
|
||
return true;
|
||
}
|
||
mUndoCommand = cmd_Undo;
|
||
}
|
||
catch (...)
|
||
{
|
||
SysBeep(1);
|
||
}
|
||
return false;
|
||
} // CThreadView::ObeyMotionCommand
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SelectionChanged()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Inherited::SelectionChanged();
|
||
} // CThreadView::SelectionChanged
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SelectMessageWhenReady(MessageKey inKey)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mStillLoading || (!GetMessagePane()))
|
||
CDeferredTaskManager::Post1(new CDeferredSelectKeyTask(this, inKey), this);
|
||
else
|
||
SelectMessage(inKey);
|
||
} // CThreadView::SelectMessageWhenReady
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SelectMessage(MessageKey inKey)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Assert_(GetMessagePane());
|
||
if (GetMessagePane())
|
||
{
|
||
MSG_ViewIndex index = ::MSG_GetMessageIndexForKey(GetMessagePane(), inKey, true);
|
||
if (index != MSG_VIEWINDEXNONE)
|
||
SelectRow(index + 1);
|
||
}
|
||
} // CThreadView::SelectMessage
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::UpdateHistoryEntry()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
TableIndexT rowCount = GetSelectedRowCount();
|
||
MSG_Pane* pane = GetMessagePane();
|
||
if (!pane)
|
||
return; // e.g., during window creation
|
||
URL_Struct* url = nil;
|
||
char entryName[64];
|
||
entryName[0] = '\0';
|
||
if (rowCount == 1)
|
||
{
|
||
#if 0 // Auto-scroll should not be done here (+ it was redundant)
|
||
if (!CApplicationEventAttachment::CurrentEventHasModifiers(cmdKey) &&
|
||
!CApplicationEventAttachment::CurrentEventHasModifiers(shiftKey))
|
||
{
|
||
STableCell cell = GetFirstSelectedCell();
|
||
if (IsValidCell(cell))
|
||
ScrollCellIntoFrame(cell);
|
||
}
|
||
#endif
|
||
CMailSelection selection;
|
||
GetSelection( selection );
|
||
CMessage message(*selection.GetSelectionList() + 1, selection.xpPane);
|
||
MessageKey id = message.GetMessageKey();
|
||
url = ::MSG_ConstructUrlForMessage(pane, id);
|
||
message.GetSubject(entryName, sizeof(entryName));
|
||
}
|
||
else
|
||
{
|
||
// zero selection or multiple selection - use folder for history entry
|
||
url = CreateURLForProxyDrag(entryName);
|
||
}
|
||
if (url && *entryName)
|
||
{
|
||
LO_DiscardDocument(*mContext);
|
||
// i18n problem- we need to convert entryName to UTF8 before call SHIST_CreateHistryEntry
|
||
// We didn't do that because mail/news is not alive yet....
|
||
|
||
History_entry* theNewEntry = ::SHIST_CreateHistoryEntry(
|
||
url,
|
||
entryName);
|
||
::SHIST_AddDocument(*mContext, theNewEntry);
|
||
}
|
||
XP_FREEIF(url);
|
||
} // CThreadView::UpdateHistoryEntry
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::ObeyMarkReadByDateCommand()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
StDialogHandler handler(10525, NULL);
|
||
LWindow* dlog = handler.GetDialog();
|
||
if (! dlog)
|
||
return true;
|
||
|
||
CSearchDateField* dateField = dynamic_cast<CSearchDateField*>
|
||
(dlog->FindPaneByID(CSearchDateField::class_ID));
|
||
if (dateField)
|
||
{
|
||
dateField->SetToToday();
|
||
MessageT message;
|
||
do
|
||
{
|
||
message = handler.DoDialog();
|
||
} while (message != msg_OK && message != msg_Cancel);
|
||
if (message == msg_OK)
|
||
{
|
||
Int16 outYear; UInt8 outMonth, outDay;
|
||
dateField->GetDate(&outYear, &outMonth, &outDay);
|
||
|
||
tm time;
|
||
time.tm_sec = 1;
|
||
time.tm_min = 1;
|
||
time.tm_hour = 1;
|
||
time.tm_mday = outDay;
|
||
time.tm_mon = outMonth - 1;
|
||
time.tm_year = outYear - 1900;
|
||
time.tm_wday = -1;
|
||
time.tm_yday = -1;
|
||
time.tm_isdst = -1;
|
||
|
||
time_t endDate = ::mktime(&time);
|
||
MSG_MarkReadByDate(GetMessagePane(), 0, endDate);
|
||
}
|
||
}
|
||
return true;
|
||
} // CThreadView::ObeyMarkReadByDateCommand
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DoSelectThread(TableIndexT inSelectedRow)
|
||
// Select all messages belonging to the same thread as this row.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
CMessage message(inSelectedRow, GetMessagePane());
|
||
MSG_ViewIndex threadIndex = MSG_ThreadIndexOfMsg(GetMessagePane(), message.GetMessageKey());
|
||
if (threadIndex == MSG_VIEWINDEXNONE)
|
||
return;
|
||
TableIndexT first = 1 + threadIndex;
|
||
STableCell firstCell(first, 1);
|
||
TableIndexT last = first + CountExtraRowsControlledByCell(firstCell);
|
||
Assert_(first < last || first == inSelectedRow);
|
||
if (LTableRowSelector * selector = dynamic_cast<LTableRowSelector*>(mTableSelector))
|
||
{
|
||
// Powerplant's ClickSelect has already been called, so use the sense of the
|
||
// clicked cell and turn all other cells in the thread on or off to match.
|
||
STableCell clickedCell(inSelectedRow, 1);
|
||
Boolean doSelect = selector->CellIsSelected(clickedCell);
|
||
SetNotifyOnSelectionChange(false);
|
||
try
|
||
{
|
||
if (!CApplicationEventAttachment::CurrentEventHasModifiers(shiftKey|cmdKey))
|
||
UnselectAllCells();
|
||
for (TableIndexT i = first; i <= last; i++)
|
||
selector->DoSelect(i, doSelect, true, false);
|
||
}
|
||
catch (...) {}
|
||
SetNotifyOnSelectionChange(true);
|
||
SelectionChanged();
|
||
}
|
||
} // CThreadView::DoSelectThread
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DoSelectFlaggedMessages()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
SetNotifyOnSelectionChange(false);
|
||
try
|
||
{
|
||
if (!CApplicationEventAttachment::CurrentEventHasModifiers(shiftKey|cmdKey))
|
||
UnselectAllCells();
|
||
for (TableIndexT i = 1; i <= mRows; i++)
|
||
{
|
||
CMessage message(i, GetMessagePane());
|
||
if (message.IsFlagged())
|
||
SelectCell(STableCell(i, 1));
|
||
}
|
||
}
|
||
catch (...) {}
|
||
SetNotifyOnSelectionChange(true);
|
||
SelectionChanged();
|
||
} // CThreadView::DoSelectFlaggedMessages
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CThreadView::ObeyCommand(
|
||
CommandT inCommand,
|
||
void *ioParam)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (!mContext)
|
||
return false;
|
||
|
||
if (inCommand == cmd_UnselectAllCells)
|
||
{
|
||
UnselectAllCells();
|
||
return true;
|
||
}
|
||
|
||
if (inCommand != cmd_Stop && XP_IsContextBusy((MWContext*)(*mContext)))
|
||
return LCommander::GetTopCommander()->ObeyCommand(inCommand, ioParam); // global commands OK.
|
||
if (inCommand == msg_TabSelect)
|
||
return (GetOwningFolder() != nil); // Allow selection only if a folder is loaded.
|
||
|
||
Boolean commandHandled = false;
|
||
CommandT originalCommand = mContext->GetCurrentCommand();
|
||
CNSContext* originalContext = mContext; // in case we close the window & delete it!
|
||
mContext->SetCurrentCommand(inCommand);
|
||
if (inCommand == cmd_GetNewMail || inCommand == cmd_GetMoreMessages)
|
||
{
|
||
ExpectNewMail();
|
||
|
||
// getting new messages: slow down the status bar
|
||
// to reduce flickers and improve performance
|
||
CThreadWindow* threadWindow
|
||
= dynamic_cast<CThreadWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
||
if (threadWindow)
|
||
threadWindow->GetProgressListener()->SetLaziness(
|
||
CProgressListener::lazy_VeryButForThisCommandOnly);
|
||
}
|
||
// Don't let msgLib do the deletion since we want(?) to close open mail windows
|
||
if ( inCommand == cmd_Clear )
|
||
{
|
||
DeleteSelection();
|
||
return true;
|
||
}
|
||
if ( inCommand == cmd_NewFolder )
|
||
{
|
||
UFolderDialogs::ConductNewFolderDialog(GetOwningFolder());
|
||
return true;
|
||
}
|
||
|
||
// If you're reading a news group, and you want to compose a new message, the new message
|
||
// is, by default, addressed to that news group... this test and reassignment are necessary
|
||
// to make this happen
|
||
if ( (inCommand == cmd_NewMailMessage) && (GetFolderFlags() & MSG_FOLDER_FLAG_NEWSGROUP) ) {
|
||
inCommand = cmd_PostNew;
|
||
}
|
||
|
||
// For msglib commands, we have to be careful to check whether the command
|
||
// can be handled for THIS pane, because the message pane might have
|
||
// enabled the menu item. Failing to check again here leads to a nasty
|
||
// crash.
|
||
Boolean enabled; Boolean usesMark; Char16 mark; Str255 name;
|
||
if (FindMessageLibraryCommandStatus(inCommand, enabled, usesMark, mark, name)
|
||
&& enabled)
|
||
{
|
||
commandHandled = ObeyMessageLibraryCommand(inCommand, ioParam);
|
||
if (inCommand == cmd_Undo || inCommand == cmd_Redo)
|
||
{
|
||
UnselectAllCells();
|
||
mUndoTask = new CDeferredUndoTask(this);
|
||
CDeferredTaskManager::Post1(mUndoTask, this);
|
||
}
|
||
else if (mUndoTask)
|
||
CDeferredTaskManager::Remove(mUndoTask, this);
|
||
}
|
||
if (!commandHandled)
|
||
{
|
||
MSG_MotionType mcmd = UMessageLibrary::GetMotionType(inCommand);
|
||
if (UMessageLibrary::IsValidMotion(mcmd))
|
||
commandHandled = ObeyMotionCommand(mcmd);
|
||
if (!commandHandled)
|
||
{
|
||
// Mail folder commands. We come here either from a button (broadcast) or
|
||
// from a menu command (synthetic).
|
||
const char* folderPath = nil;
|
||
if (inCommand == cmd_MoveMailMessages || inCommand == cmd_CopyMailMessages)
|
||
{
|
||
// Button case
|
||
if ( ioParam ) // The BE dereferences this ASAP
|
||
folderPath = ::MSG_GetFolderNameFromID((MSG_FolderInfo*)ioParam);
|
||
}
|
||
else if (!CMailFolderSubmenu::IsMailFolderCommand(&inCommand, &folderPath)) // menu case
|
||
{
|
||
folderPath = nil; // any other case.
|
||
}
|
||
if (folderPath && *folderPath)
|
||
{
|
||
FileMessagesToSelectedPopupFolder(
|
||
folderPath,
|
||
inCommand == cmd_MoveMailMessages);
|
||
commandHandled = true;
|
||
}
|
||
}
|
||
}
|
||
if (!commandHandled) switch(inCommand)
|
||
{
|
||
case cmd_SubscribeNewsgroups:
|
||
MSG_Host* selectedHost = MSG_GetHostForFolder(GetOwningFolder());
|
||
CNewsSubscriber::DoSubscribeNewsGroup(selectedHost);
|
||
break;
|
||
case cmd_MarkReadByDate:
|
||
ObeyMarkReadByDateCommand();
|
||
commandHandled = true;
|
||
break;
|
||
#if 0
|
||
case cmd_RelocateViewToFolder:
|
||
// Don't handle this in the three-pane view. The folder pane has to change, too,
|
||
// so pass it up to CThreadWindow and let it delegate.
|
||
commandHandled = RelocateViewToFolder((MSG_FolderInfo*)ioParam);
|
||
break;
|
||
#endif // 0
|
||
case cmd_SortByDate:
|
||
case cmd_SortBySubject:
|
||
case cmd_SortBySender:
|
||
case cmd_SortByThread:
|
||
case cmd_SortByPriority:
|
||
case cmd_SortBySize:
|
||
case cmd_SortByStatus:
|
||
case cmd_SortByFlagged:
|
||
case cmd_SortByOrderReceived:
|
||
case cmd_SortByReadness:
|
||
case cmd_SortAscending:
|
||
case cmd_SortDescending:
|
||
if (ObeySortCommand(inCommand))
|
||
commandHandled = true;
|
||
break;
|
||
case cmd_SelectThread:
|
||
CMailSelection selection;
|
||
GetSelection( selection );
|
||
TableIndexT selectedRow = *selection.GetSelectionList() + 1;
|
||
DoSelectThread(selectedRow);
|
||
break;
|
||
case cmd_SelectMarkedMessages:
|
||
DoSelectFlaggedMessages();
|
||
break;
|
||
} // switch
|
||
if (!commandHandled && inCommand > ENCODING_BASE && inCommand < ENCODING_CEILING)
|
||
{
|
||
Int16 default_csid = CPrefs::CmdNumToDocCsid(inCommand);
|
||
SetDefaultCSID(default_csid);
|
||
LCommander::SetUpdateCommandStatus(true); // bug #80474
|
||
commandHandled = true;
|
||
}
|
||
if (!commandHandled)
|
||
commandHandled = Inherited::ObeyCommand(inCommand, ioParam);
|
||
//-----------------------------------
|
||
// Cleanup
|
||
//-----------------------------------
|
||
// The following test against originalContext protects against the cases (quit, close)
|
||
// when the object has been deleted. The test against cmdHandled protects against
|
||
// re-entrant calls to ListenToMessage.
|
||
if (mContext == originalContext)
|
||
if (commandHandled)
|
||
mContext->SetCurrentCommand(cmd_Nothing); // watch out for re-entrant broadcast msgs.
|
||
else
|
||
{
|
||
// It wasn't a command, so restore damage done if we're processing a broadcast.
|
||
mContext->SetCurrentCommand(originalCommand);
|
||
}
|
||
return commandHandled;
|
||
} // CThreadView::ObeyCommand
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ResetTextTraits()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Int16 wincsid = mContext->GetWinCSID();
|
||
ResIDT newTextTraitsID;
|
||
// ## Begin Hacky Code copy from Akbar (v3.0) mnews.cp
|
||
// This will make everyone happy.
|
||
// If the window is MAC_ROMAN, it will use the default mnews font.
|
||
// Otherwise, get it from the Font preference
|
||
if ( wincsid == INTL_CharSetNameToID(INTL_ResourceCharSet()) )
|
||
newTextTraitsID = 130;
|
||
else
|
||
newTextTraitsID = CPrefs::GetTextFieldTextResIDs(wincsid);
|
||
// ## End of Hacky Code copy from Akbar (v3.0) mnews.cp
|
||
|
||
if (newTextTraitsID != mTextTraitsID )
|
||
{
|
||
SetTextTraits(newTextTraitsID);
|
||
Refresh();
|
||
}
|
||
} // CThreadView::ResetTextTraits
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Int16 CThreadView::DefaultCSIDForNewWindow(void)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mContext)
|
||
return mContext->GetDefaultCSID();
|
||
return 0;
|
||
} // CThreadView::DefaultCSIDForNewWindow
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::SetDefaultCSID(Int16 default_csid)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Set the csid in the context
|
||
mContext->SetDefaultCSID(default_csid);
|
||
mContext->SetWinCSID(INTL_DocToWinCharSetID(default_csid));
|
||
|
||
// Set the csid for the folder
|
||
MSG_SetFolderCSID(GetOwningFolder(), default_csid);
|
||
|
||
// We need to set the csid for the view with the window
|
||
// Ask the CThreadWindow to do it.
|
||
CThreadWindow* threadWindow
|
||
= dynamic_cast<CThreadWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
||
if (threadWindow)
|
||
threadWindow->SetDefaultCSID(default_csid);
|
||
|
||
// We need to change the text info and redraw all the header
|
||
ResetTextTraits();
|
||
|
||
// If we have Message Window appear, we need to set the csid for it.
|
||
try {
|
||
// Find the all the MessageWindow which view the some folder
|
||
// and set the default csid for its view.
|
||
|
||
CWindowIterator iter(WindowType_Message);
|
||
CMessageWindow* window;
|
||
for (iter.Next(window); window; iter.Next(window))
|
||
{
|
||
window = dynamic_cast<CMessageWindow*>(window);
|
||
if (window)
|
||
{
|
||
CMessageView* messageView = window->GetMessageView();
|
||
if (GetOwningFolder() == messageView->GetFolderInfo())
|
||
{
|
||
messageView->SetDefaultCSID(default_csid);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch( ... )
|
||
{
|
||
}
|
||
|
||
} // CThreadView::SetDefaultCSID
|
||
|
||
// <20><><EFBFBD>Fix me, this should go into some utility classes
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::DrawUTF8TextString(
|
||
const char* inText,
|
||
const FontInfo* inFontInfo,
|
||
SInt16 inMargin,
|
||
const Rect& inBounds,
|
||
SInt16 inJustification,
|
||
Boolean inDoTruncate,
|
||
TruncCode inTruncWhere)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Rect r = inBounds;
|
||
|
||
r.left += inMargin;
|
||
r.right -= inMargin;
|
||
|
||
PlaceUTF8TextInRect(inText,
|
||
strlen(inText),
|
||
r,
|
||
inJustification,
|
||
teCenter,
|
||
inFontInfo,
|
||
inDoTruncate,
|
||
inTruncWhere );
|
||
}
|
||
|
||
// <20><><EFBFBD>Fix me, this should go into some utility classes
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::PlaceUTF8TextInRect(
|
||
const char* inText,
|
||
Uint32 inTextLength,
|
||
const Rect &inRect,
|
||
Int16 inHorizJustType,
|
||
Int16 inVertJustType,
|
||
const FontInfo* /*inFontInfo*/,
|
||
Boolean inDoTruncate,
|
||
TruncCode /*inTruncWhere*/)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
FontInfo utf8FontInfo;
|
||
UFontSwitcher *fs;
|
||
UMultiFontTextHandler *th;
|
||
th = UUTF8TextHandler::Instance();
|
||
fs = UPropFontSwitcher::Instance();
|
||
th->GetFontInfo(fs, &utf8FontInfo);
|
||
|
||
const char* text = inText;
|
||
short length = inTextLength;
|
||
if (inDoTruncate)
|
||
{
|
||
// <20><> Fix ME: Don't know how to do text truncation for UTF8 now.
|
||
}
|
||
Point thePoint = UGraphicGizmos::CalcStringPosition(
|
||
inRect,
|
||
th->TextWidth(fs, (char*)text, length),
|
||
inHorizJustType,
|
||
inVertJustType,
|
||
&utf8FontInfo);
|
||
::MoveTo(thePoint.h, thePoint.v);
|
||
th->DrawText(fs, (char*)text ,length);
|
||
} // CThreadView::PlaceUTF8TextInRect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ListenToMessage(
|
||
MessageT inCommand,
|
||
void *ioParam)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Check ObeyCommand first, for a button message, but ONLY IF WE'RE ON DUTY.
|
||
if (!IsOnDuty() || !ObeyCommand((CommandT)inCommand, ioParam)) // button message?
|
||
{
|
||
Inherited::ListenToMessage(inCommand, ioParam);
|
||
switch (inCommand)
|
||
{
|
||
case msg_NSCAllConnectionsComplete:
|
||
{
|
||
// if (mPendingCommand)
|
||
// {
|
||
// CommandT cmd = mPendingCommand;
|
||
// mPendingCommand = 0;
|
||
// ObeyCommand(cmd, nil);
|
||
//
|
||
// }
|
||
// else
|
||
#ifdef REMOVED_870427
|
||
if (GotNewMail())
|
||
{
|
||
ScrollToGoodPlace();
|
||
DontExpectNewMail();
|
||
}
|
||
break;
|
||
#endif
|
||
if (GetContext()->GetCurrentCommand() == cmd_GetNewMail)
|
||
DontExpectNewMail();
|
||
}
|
||
} // switch
|
||
}
|
||
} // CThreadView::ListenToMessage
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ActivateSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Inherited::ActivateSelf();
|
||
} // CThreadView::ActivateSelf
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::ObeyCommandWhenReady(CommandT inCommand)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (inCommand != cmd_Nothing)
|
||
CDeferredTaskManager::Post(new CDeferredThreadViewCommand(this, inCommand, nil), this);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
URL_Struct* CThreadView::CreateURLForProxyDrag(char* outTitle)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
MSG_Pane* pane = GetMessagePane();
|
||
if (!pane)
|
||
return nil;
|
||
MSG_FolderInfo* folderInfo = GetOwningFolder();
|
||
if (!folderInfo)
|
||
return nil;
|
||
CMessageFolder folder(folderInfo);
|
||
if (outTitle)
|
||
{
|
||
strcpy(outTitle, folder.GetName());
|
||
NET_UnEscape(outTitle);
|
||
}
|
||
return ::MSG_ConstructUrlForFolder(pane, folderInfo);
|
||
} // CThreadView::CreateURLForProxyDrag
|
||
|
||
#if defined(QAP_BUILD)
|
||
//----------------------------------------------------------------------------------------
|
||
void CThreadView::GetQapRowText(
|
||
TableIndexT inRow,
|
||
char* outText,
|
||
UInt16 inMaxBufferLength) const
|
||
// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
|
||
// Return result indicates if any of the text is visible in the cell
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (!outText || inMaxBufferLength == 0)
|
||
return;
|
||
|
||
cstring rowText("");
|
||
short colCount = mTableHeader->CountVisibleColumns();
|
||
CMessage message(inRow, GetMessagePane());
|
||
|
||
CMailNewsWindow * myWindow = dynamic_cast<CMailNewsWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
||
if (!myWindow) return;
|
||
|
||
for (short col = 1; col <= colCount; col ++)
|
||
{
|
||
STableCell aCell(inRow, col);
|
||
LTableHeader::SColumnData * colData = mTableHeader->GetColumnData(col);
|
||
if (!colData) break;
|
||
LPane * colPane = myWindow->FindPaneByID(colData->paneID);
|
||
if (!colPane) break;
|
||
|
||
// get column name
|
||
CStr255 descriptor;
|
||
switch (GetCellDataType(aCell))
|
||
{
|
||
case kThreadMessageColumn: descriptor = "Thread"; break;
|
||
case kMarkedReadMessageColumn: descriptor = "MarkRead"; break;
|
||
case kFlagMessageColumn: descriptor = "Flag"; break;
|
||
default:
|
||
colPane->GetDescriptor(descriptor);
|
||
break;
|
||
}
|
||
rowText += descriptor;
|
||
rowText += "=\042";
|
||
|
||
// add cell text
|
||
switch (PaneIDT dataType = GetCellDataType(aCell))
|
||
{
|
||
case kThreadMessageColumn:
|
||
if (message.IsThread())
|
||
{
|
||
if (message.IsOpenThread())
|
||
rowText += "-";
|
||
else
|
||
rowText += "+";
|
||
}
|
||
else
|
||
rowText += " ";
|
||
break;
|
||
|
||
// note: no intl conversions (yet?) for subject, sender and addressee
|
||
case kMarkedReadMessageColumn: rowText += (message.HasBeenRead() ? "+" : " "); break;
|
||
case kFlagMessageColumn: rowText += (message.IsFlagged() ? "+" : " "); break;
|
||
case kSubjectMessageColumn: rowText += message.GetSubject(); break;
|
||
case kSenderMessageColumn: rowText += message.GetSender(); break;
|
||
case kDateMessageColumn: rowText += message.GetDateString(); break;
|
||
case kPriorityMessageColumn: rowText += message.GetPriorityStr(); break;
|
||
case kSizeMessageColumn: rowText += message.GetSizeStr(); break;
|
||
case kStatusMessageColumn: rowText += message.GetStatusStr(); break;
|
||
case kAddresseeMessageColumn: rowText += message.GetAddresseeString(); break;
|
||
|
||
case kTotalMessageColumn:
|
||
case kUnreadMessageColumn:
|
||
if (GetSortedColumn() == kThreadMessageColumn && message.IsThread())
|
||
{
|
||
int theNum = (dataType == kTotalMessageColumn
|
||
? message.GetNumChildren()
|
||
: message.GetNumNewChildren());
|
||
char tempStr[32];
|
||
sprintf(tempStr, "%d", theNum);
|
||
rowText += tempStr;
|
||
}
|
||
break;
|
||
}
|
||
if (col < colCount)
|
||
rowText += "\042 | ";
|
||
else
|
||
rowText += "\042\r";
|
||
}
|
||
strncpy(outText, (char*)rowText, inMaxBufferLength);
|
||
outText[inMaxBufferLength - 1] = '\0';
|
||
} // CThreadView::GetQapRowText
|
||
#endif //QAP_BUILD
|