eudora-mac/boxact.c

1 line
166 KiB
C
Executable File
Raw Permalink Blame History

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
#include "boxact.h"
#define FILE_NUM 5
/* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
#pragma segment Boxes
/**********************************************************************
* Private prototypes
**********************************************************************/
// Drawing
void InvertSum(TOCHandle tocH,short sumNum,Boolean forActivate);
void BoxCenter(MyWindowPtr win, short mNum);
void BoxSizeBox(MyWindowPtr win,Rect *r,TOCHandle tocH);
short BoxPreviewHeightWish(TOCHandle tocH);
void BoxZoomSize(MyWindowPtr win,Rect *zoom);
void InvertSumByColumns(TOCHandle tocH,Rect *r);
Boolean BoxDrawerUseless(MyWindowPtr win, short *left, short *right);
// Clicking
void BoxTrack(TOCHandle tocH,int anchor,Boolean shift);
Boolean BoxDragDividers(Point pt, MyWindowPtr win);
Boolean BoxFindDivider(Point pt,short *which);
TOCHandle TOCHUnder(Point pt);
void BoxSetPreview(MyWindowPtr win,Boolean openIt);
void BoxDragPreviewDivider(TOCHandle tocH,Point pt);
Boolean BoxListKey(MyWindowPtr win, EventRecord *event);
Boolean BoxPreviewKey(MyWindowPtr win, EventRecord *event);
void BoxConConProfControl(TOCHandle tocH,ControlHandle cntl,Point pt);
// Dragging
OSErr BoxDrop(MyWindowPtr win, DragReference drag);
RgnHandle BoxBuildDragRgn(TOCHandle tocH);
void BoxDroppedOn(TOCHandle origTocH,MessHandle messH,Point pt);
OSErr BoxMessDragSendLo(FlavorType flavor, void *dragSendRefCon, DragReference drag, TOCHandle tocH,short sumNum);
// Menus
void BoxPriorMenu(TOCHandle tocH,short mNum,Point pt);
void BoxServerMenu(TOCHandle tocH,short mNum,Point pt);
void BoxStateMenu(TOCHandle tocH,short mNum,Point pt);
void BoxMailboxMenu(TOCHandle tocH,short mNum,Point pt);
void BoxAttachMenu(TOCHandle tocH,short mNum,Point pt);
void BoxBuildConConProfMenu(TOCHandle tocH,ControlHandle cntl);
// Misc
void DoSaveSelectedAs(TOCHandle tocH);
void SizeBoxClick(MyWindowPtr win,short modifiers);
OSErr CopySummary(TOCHandle tocH, short sumNum, Handle into);
void BoxType2Select(TOCHandle tocH,PStr string);
OSErr CopySelectedSummaries(TOCHandle tocH);
short BoxCountSelected(TOCHandle tocH);
void SetTAEScoreLo(TOCHandle tocH,short sumNum,short score);
void OneAnalMess(TOCHandle tocH,short sumNum);
OSErr BoxReply(TOCHandle tocH,short menu,short item,short modifiers);
void BoxRefreshPreview(TOCHandle tocH);
Boolean BoxRequestFocus(MyWindowPtr win, PETEHandle pte);
PStr BoxCurAddr(MyWindowPtr win, PStr addr);
// Sorting
#define DisplayLength(sum) ((sum)->length-(sum)->bodyOffset+1023)
#define EffectiveLength(sum) ((sum->flags&FLAG_SKIPPED) ? -1 K : ((sum->opts&OPT_JUSTSUB) ? -2 K : DisplayLength(sum)))
void BoxToggleLaurence(TOCHandle tocH);
void BoxCustomSort(TOCHandle tocH,short i);
void SortTOCMenu(TOCHandle tocH, short item, Boolean reverse);
void SortTOC(TOCHandle tocH, Boolean reverse, int (*compare)());
void SwapSum(MSumPtr sum1, MSumPtr sum2);
short BoxLine2Item(short line);
short BoxItem2Line(short item);
void MBRemoveSort(TOCHandle tocH,short index);
void MBAddSort(TOCHandle tocH,short index,Byte sortOrder);
#define MBGetSort(tocH,index) (*tocH)->sorts[index-1]
int (*FindMBSort(short item,Boolean reverse))();
int MBResortCompare(MSumPtr s1, MSumPtr s2);
void MBResort(TOCHandle tocH);
void MBSortHit(TOCHandle tocH, short index, Boolean reverse, Boolean extend);
Boolean MBIsSticky(TOCHandle tocH);
int SumSubjIdCompare(MSumPtr sum1, MSumPtr sum2);
void BoxMakePreview(MyWindowPtr win);
short BoxSizeWidth(TOCHandle tocH);
void SetPriorityLo(TOCHandle tocH,short sumNum,short priority);
long MyPopupMenuSelect(MyWindowPtr win,MenuHandle mh,Point where,short item);
#define NSORT (sizeof((*tocH)->sorts)/sizeof(Byte))
#define SORTER(what) int Sum##what##Compare(MSumPtr,MSumPtr); int RevSum##what##Compare(MSumPtr,MSumPtr)
SORTER(Time);
SORTER(Stat);
SORTER(Junk);
SORTER(Att);
SORTER(Size);
SORTER(Prior);
SORTER(Subj);
SORTER(SubjTimed);
SORTER(From);
SORTER(Offset);
SORTER(Label);
SORTER(SizeK);
SORTER(TimeFuzz);
SORTER(Select);
SORTER(Mailbox);
SORTER(Anal);
#define SORT_BUTTON_WIDE 11
#define kHeaderButtonHeight (GROW_SIZE+5)
#define kTabHeight 25
#define kTabGap 2
#define kLargeUniqueValue 0x7f7f7f7f
short BoxPreviewHeight(TOCHandle tocH);
pascal OSErr MessDragSend(FlavorType flavor, void *dragSendRefCon, ItemReference theItemRef, DragReference drag);
pascal OSErr BoxDragSend(FlavorType flavor, void *dragSendRefCon, ItemReference theItemRef, DragReference drag);
void HitTab(MyWindowPtr win,ControlHandle tabCntl,TOCHandle tocH);
// globals
TOCHandle gSortTOC;
MyWindowPtr gDrawerOpenedWin;
/************************************************************************
* BoxLimits - return the left and right sides of a particular mailbox
* column. Values are NOT shifted according to scroll bars. Values are
* in pixels, not character widths
************************************************************************/
short BoxLimits(BoxLinesEnum which,short *left,short *right,TOCHandle tocH)
{
short column;
short wi;
short i;
*right = 0;
for (i=1;;i++)
{
column = TOCInversionMatrix[0][i];
if (!BoxColumnShowing(column,tocH))
wi = 0;
else
{
ASSERT(column > 0 && column<BoxLinesLimit);
wi = (*BoxWidths)[column-1] ? (*BoxWidths)[column-1]*FontWidth : 4;
*right += wi;
}
if (column==which) break;
}
*left = *right-wi;
return(*right-*left);
}
/**********************************************************************
* BoxColumnShowing - is a column showing?
**********************************************************************/
Boolean BoxColumnShowing(BoxLinesEnum which,TOCHandle tocH)
{
// junk column is hidden by default in all mailboxes except junk,
// where it always shows
if (which==blJunk)
{
if (((*tocH)->which==JUNK) || IsIMAPJunkMailbox(TOCToMbox(tocH))) return true;
else return 0!=(GetPrefLong(PREF_BOX_HIDDEN)&(1<<(which-1)));
}
// Ordinary column processing
if (GetPrefLong(PREF_BOX_HIDDEN)&(1<<(which-1))) return false;
// other special items
if (which==blMailbox && !(tocH && (*tocH)->virtualTOC)) return false;
if (which==blAnal && (AnalDisabled() || !AnalDoIncoming())) return false;
return true;
}
/**********************************************************************
* BoxUpdate - handle an update event for a mailbox window
**********************************************************************/
#define SUMMARY_ICON_SETUP do { \
r.bottom = pr.bottom-1; \
r.top = pr.top+1; \
r.left = h0+left+2; r.right = h0+right-2; \
SetRect(&ir,0,0,16,16); \
CenterRectIn(&ir,&r); \
r.bottom = MIN(r.bottom,win->contR.bottom); \
r.top = MAX(r.top,cr.bottom-win->vPitch); \
ClipRect(&r); \
} while (0);
void BoxUpdate(MyWindowPtr win)
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
CGrafPtr winWPPort = GetWindowPort(winWP);
TOCHandle tocH = (TOCHandle) GetWindowPrivateData(winWP);
MSumType sum;
int min, max, sumNum;
int line;
int h,v;
Str31 states,scratch;
Str31 pfix;
Rect r,pr,cr,ir;
short h0;
short greyMax;
Boolean isOut = (*tocH)->which==OUT;
short left,right;
Boolean hasColor = IsColorWin(winWP);
RGBColor oldColor, color, textColor;
Str255 string;
short label;
PersHandle pers;
short lPref;
short depth = RectDepth(&win->contR);
Boolean useUglyStupidIconsBecauseLettersAreTooClearAndUnambiguous = !PrefIsSet(PREF_NO_SUMMARY_ICONS);
Rect visR = *GetVisibleRgnBounds(winWP,&visR);
short inIn = GetRLong(IN_IN_STYLE); // Style to be used for incoming messages in an incoming box
short outOut = GetRLong(OUT_OUT_STYLE); // Style to be used for outgoing messages in the Out box
short inOut = GetRLong(IN_OUT_STYLE); // Style to be used for incoming messages in the Out box
Pattern thePattern;
/*
* redraw it all if we need to calculate the window width
*/
if (win->hMax < 0)
{
(*tocH)->needRedo = 0;
return;
}
/*
* reset font & size, just in case
*/
TextFont(FontID);
TextSize(FontSize);
/*
* Set the clipping region to be the content minus the scroll bars plus the top margin
*/
r = win->contR;
r.top = 0;
ClipRect(&r);
/*
* Set min and max to be the top & bottom visible summary lines
*/
min = GetControlValue(win->vBar);
max = min+RoundDiv(win->contR.bottom-win->contR.top/*-win->topMargin*/,win->vPitch)+1;
max = MIN((*tocH)->count-1,max);
h = h0 = -GetControlValue(win->hBar)*win->hPitch;
r = win->contR;
/*
* draw the column colors
*/
if (UseListColors)
{
for (line=1;line<BoxLinesLimit;line++)
{
if (BoxLimits(line,&r.left,&r.right,tocH))
{
OffsetRect(&r,h0,0);
SetThemeBackground(MBGetSort(tocH,line) ? kThemeBrushListViewSortColumnBackground:kThemeBrushListViewBackground,RectDepth(&r),true);
EraseRect(&r);
}
}
SET_COLORS;
}
/*
* draw the grey lines
*/
if (hasColor)
{
GetRColor(&textColor,TEXT_COLOR);
Zero(oldColor);
}
if ((lPref=GetPrefLong(PREF_NO_LINES))!=3)
{
if (hasColor)
{
if (UseListColors)
SetThemePen(kThemeBrushListViewSeparator,RectDepth(&win->contR),true);
else
{
GetRColor(&color,ColorIsLight(&win->backColor) ? SEP_LINE_DARK_COLOR:SEP_LINE_LIGHT_COLOR);
RGBForeColor(&color);
}
}
else
PenPat(GetQDGlobalsGray(&thePattern));
greyMax = win->topMargin+(max-min+1)*(short)win->vPitch;
if (!(lPref & 1)) for (v=win->topMargin;v<=greyMax;v+=win->vPitch)
{
MoveTo(0,v); Line(REAL_BIG,0);
}
if (!(lPref & 2)) for (line=1;line<BoxLinesLimit;line++)
{
if (BoxLimits(line,&left,&right,tocH))
{
MoveTo(h0+right-1,win->topMargin);
Line(0,10000);
}
}
}
// Adjust max
if (max>(*tocH)->count-1) max = (*tocH)->count-1;
/*
* now, draw the contents
*/
SET_COLORS;
if (UseListColors) SetThemeTextColor(kThemeTextColorListView,depth,True); else RGBForeColor(&textColor);
GetRString(pfix,(*tocH)->which==OUT?COMP_OUT_INTRO:COMP_IN_INTRO);
sum=(*tocH)->sums[min];
PenPat(GetQDGlobalsBlack(&thePattern));
GetRString(states,STATE_LABELS);
v = win->topMargin+(1-(GetControlValue(win->vBar)-min))*win->vPitch - FontDescent;
r.left = 0;
r.right = REAL_BIG;
BoxLimits(blPrior,&pr.left,&pr.right,tocH);
pr.left += (pr.right-pr.left-16+1)/2;
pr.top = win->topMargin + (win->vPitch-16+1)/2;
pr.bottom = pr.top+16;
pr.right = pr.left+16;
OffsetRect(&pr,h0,(min-GetControlValue(win->vBar))*win->vPitch);
for (line=0,sumNum=min; line<=(max-min); line++,sumNum++,v+=win->vPitch,OffsetRect(&pr,0,win->vPitch))
{
sum = (*tocH)->sums[sumNum];
if (pr.bottom<visR.top || pr.top>visR.bottom) continue;
if (isOut)
TextFace(outOut);
else if (sum.state==SENT || sum.state==UNSENT || (sum.flags & FLAG_OUT) || (sum.opts & OPT_IMAP_SENT))
TextFace(inOut);
else
TextFace(inIn);
pers = SUM_TO_PPERS(&sum);
BoxLimits(blPrior,&left,&right,tocH);
cr.bottom = MIN(v+FontDescent+1,win->contR.bottom);
cr.top = MAX(win->contR.top,cr.bottom-win->vPitch);
cr.left = h0+left+1; cr.right = h0+right-2;
ClipRect(&cr);
if (right-left>6) DrawPriority(&pr,sum.priority);
if (sum.flags & FLAG_HAS_ATT)
{
if (BoxLimits(blAttach,&left,&right,tocH)>6)
{
SUMMARY_ICON_SETUP;
PlotIconID(&ir,atNone, ttNone,ATTACH_ICON);
}
}
// moodmail
if (BoxLimits(blAnal,&left,&right,tocH)>6)
{
// if it scored above one, display it
if (sum.score > 1)
{
SUMMARY_ICON_SETUP;
PlotIconID(&ir,atNone, ttNone,ANAL_ICON_BASE+sum.score-1);
}
}
// draw the icon in the server status column
if ((*tocH)->imapTOC)
{
if (BoxLimits(blServer,&left,&right,tocH)>6)
{
SUMMARY_ICON_SETUP;
// draw the DELETED icon if the message is marked for deletion
if (sum.opts&OPT_DELETED) PlotIconID(&ir,atAbsoluteCenter,ttNone,IMAP_DELETED_ICON);
// draw the MISSING EVERYTHING icon if nothing has been fetched yet
else if (sum.offset<0) PlotIconID(&ir,atAbsoluteCenter,ttNone,IMAP_NOTHING_FETCHED);
// draw the MISSING ATTACHMENTS icon if one or more attachments is stubular
else if (sum.opts&OPT_FETCH_ATTACHMENTS) PlotIconID(&ir,atAbsoluteCenter,ttNone,IMAP_FETCHED_BODY);
// otherwise draw the COMPLETELY FETCHED icon if the message is totally present
else PlotIconID(&ir,atAbsoluteCenter,ttNone,IMAP_FETCHED_ALL);
}
}
if (IdIsOnPOPD(PERS_POPD_TYPE(pers),POPD_ID,sum.uidHash))
{
short iconID;
if (BoxLimits(blServer,&left,&right,tocH)>6)
{
SUMMARY_ICON_SETUP;
iconID = ON_SERVER_ICON;
if ((sum.flags & FLAG_SKIPPED) && IdIsOnPOPD(PERS_POPD_TYPE(pers),FETCH_ID,sum.uidHash))
{
if (IdIsOnPOPD(PERS_POPD_TYPE(pers),DELETE_ID,sum.uidHash))
iconID = ON_BOTH_ICON;
else
iconID = ON_FETCH_ICON;
}
else if (IdIsOnPOPD(PERS_POPD_TYPE(pers),DELETE_ID,sum.uidHash))
iconID = ON_DELETE_ICON;
PlotIconID(&ir,atAbsoluteCenter,ttNone,iconID);
}
}
if (label = SumColor(&sum))
{
MyGetLabel(label,&color,string);
if (hasColor && !BlackWhite(&color) && !PrefIsSet(PREF_SMALL_COLOR)) RGBForeColor(DarkenColor(&color,GetRLong(TEXT_DARKER)));
}
else *string = 0;
BoxLimits(blStat,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth)
{
ClipRect(&cr);
MoveTo(h0+left+FontWidth/2+1,v);
if (useUglyStupidIconsBecauseLettersAreTooClearAndUnambiguous)
{
SUMMARY_ICON_SETUP;
PlotIconID(&ir,atAbsoluteCenter,ttNone,MESSAGE_STATUS_ICON_BASE+sum.state-1);
}
else
DrawChar(states[sum.state]);
}
//if (!isOut && sum.flags &FLAG_OUT) TextFace(italic);
BoxLimits(blLabel,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth)
{
Boolean mustRestore = false;
short saveTextMode = GetPortTextMode(GetQDGlobalsThePort());
if (*string && PrefIsSet(PREF_SMALL_COLOR) && hasColor && !BlackWhite(&color))
{
Rect rPaint;
rPaint.bottom = v+FontDescent+1;
rPaint.top = MAX(win->contR.top,rPaint.bottom-win->vPitch);
rPaint.bottom = MIN(rPaint.bottom,win->contR.bottom)-1;
rPaint.left = cr.left-1;
rPaint.right = cr.right+1;
ClipRect(&rPaint);
RGBForeColor(LightenColor(&color,GetRLong(AREA_LIGHTER)));
PaintRect(&rPaint);
TextMode(notSrcCopy);
mustRestore = true;
}
ClipRect(&cr);
MoveTo(h0+left+FontWidth/2+1,v);
DrawString(string);
if (mustRestore)
{
TextMode(saveTextMode);
if (hasColor) if (UseListColors) SetThemeTextColor(kThemeTextColorListView,depth,True); else RGBForeColor(&textColor);
}
}
BoxLimits(blFrom,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth)
{
ClipRect(&cr);
MoveTo(h0+left+FontWidth/2+1,v);
if (*pfix && (isOut || (sum.flags & FLAG_OUT) || (sum.opts & OPT_IMAP_SENT))) DrawString(pfix);
if(!(sum.flags & FLAG_UTF8) || !HasUnicode() || DrawUTF8Text(&sum.from[1], sum.from[0], right - left - 3, normFonts)) {
DrawString(sum.from);
}
}
BoxLimits(blDate,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth)
{
ClipRect(&cr);
MoveTo(h0+left+FontWidth/2+1,v);
if (sum.seconds)
{
ComputeLocalDate(&sum,scratch);
DrawString(scratch);
}
}
BoxLimits(blSize,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth)
{
ClipRect(&cr);
#ifdef DEBUG
if (RunType!=Production && BUG2)
{
NumToString(sum.serialNum,scratch);
#ifdef NEVER
// Check for and flag duplicate serial numbers with '<27>'
// Only works if mailbox is sorted by serial numbers
if (sum.serialNum == sum[1].serialNum || sum.serialNum == sum[-1].serialNum)
PCat(scratch,"\p <20>");
#endif
#ifdef NEVER
NumToString(sum.score,scratch);
#endif
}
else
#endif
if (sum.flags&FLAG_SKIPPED)
{
*scratch = 1;
scratch[1] = '<EFBFBD>';
}
#ifdef NOBODY_SPECIAL
else if (sum.opts & OPT_JUSTSUB)
{
*scratch = 1;
scratch[1] = '<EFBFBD>';
}
#endif // NOBODY_SPECIAL
else
{
NumToString(DisplayLength(&sum)/1024,scratch);
}
MoveTo(h0+right-FontWidth/4-StringWidth(scratch),v);
if (!isOut && (sum.state==SENT || sum.state==UNSENT || (sum.flags & FLAG_OUT) || (sum.opts & OPT_IMAP_SENT))) Move(-(2*FontWidth)/3,0);
DrawString(scratch);
}
BoxLimits(blJunk,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth && sum.spamScore > 0)
{
ClipRect(&cr);
NumToString(sum.spamScore,scratch);
#ifdef DEBUG
if (BUG7)
{
PCatC(scratch,'-');
PCatC(scratch,'0'+sum.spamBecause);
}
#endif
MoveTo(h0+right-FontWidth/4-StringWidth(scratch),v);
if (!isOut && (sum.state==SENT || sum.state==UNSENT || (sum.flags & FLAG_OUT) || (sum.opts & OPT_IMAP_SENT))) Move(-(2*FontWidth)/3,0);
DrawString(scratch);
}
BoxLimits(blMailbox,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth)
{
ClipRect(&cr);
MoveTo(h0+left+FontWidth/2+1,v);
GetMailboxName(tocH,sumNum,scratch);
DrawString(scratch);
}
BoxLimits(blSubject,&left,&right,tocH);
cr.left = h0+left+1; cr.right = h0+right-2;
cr.right = MIN(cr.right,win->contR.right);
if (cr.right-cr.left+2>=FontWidth)
{
ClipRect(&cr);
MoveTo(h0+left+FontWidth/2,v);
if(!(sum.flags & FLAG_UTF8) || !HasUnicode() || DrawUTF8Text(&sum.subj[1], sum.subj[0], cr.right - cr.left, normFonts)) {
DrawString(sum.subj);
}
}
ClipRect(&win->contR);
if (sum.selected) InvertSum(tocH,sumNum,False);
TextFace(normal);
if (UseListColors) SetThemeTextColor(kThemeTextColorListView,depth,True); else RGBForeColor(&textColor);
}
/*
* cleanup
*/
TextFace(normal);
InfiniteClip(winWPPort);
}
/**********************************************************************
* Item2Status - turn a menu item into a status value
**********************************************************************/
short Item2Status(short item)
{
switch (item)
{
case statmUnread: return(UNREAD);
case statmRead: return(READ);
case statmReplied: return(REPLIED);
case statmForwarded: return(FORWARDED);
case statmRedirected: return(REDIST);
case statmUnsendable: return(UNSENDABLE);
case statmSendable: return(SENDABLE);
case statmQueued: return(QUEUED);
case statmTimed: return(TIMED);
case statmUnsent: return(UNSENT);
case statmSent: return(SENT);
case statmMesgError: return(MESG_ERR);
case statmRecovered: return(REBUILT);
default: return(0);
}
}
/**********************************************************************
* Status2Item - turn a status value into a menu item
**********************************************************************/
short Status2Item(short item)
{
switch (item)
{
case UNREAD: return statmUnread;
case READ: return statmRead;
case REPLIED: return statmReplied;
case FORWARDED: return statmForwarded;
case REDIST: return statmRedirected;
case UNSENDABLE: return statmUnsendable;
case SENDABLE: return statmSendable;
case QUEUED: return statmQueued;
case TIMED: return statmTimed;
case UNSENT: return statmUnsent;
case SENT: return statmSent;
case MESG_ERR: return statmMesgError;
case REBUILT: return statmRecovered;
default: return(0);
}
}
/************************************************************************
* SelectBoxRange - make a particular range in a mailbox the current selection.
************************************************************************/
void SelectBoxRange(TOCHandle tocH,int start,int end,Boolean cmd,int eStart,int eEnd)
{
MyWindowPtr win = (*tocH)->win;
/*
* topVis and botVis are overly generous, since it won't hurt to be so
*/
int topVis = GetControlValue(win->vBar);
int botVis = (*tocH)->count-(GetControlMaximum(win->vBar)-GetControlValue(win->vBar));
int sNum;
int r1, r2;
if (!(*tocH)->count) return;
PushGWorld();
SetPort_(GetMyWindowCGrafPtr((*tocH)->win));
if (GetPrefLong(PREF_MANUAL_BOX_SELECTION)==0)
TOCSetDirty(tocH,true); // now that we're saving the selection...
ClipRect(&win->contR);
if (end<start) {r1=start;start=end;end=r1;} /* swap */
if (eEnd<eStart) {r1=eStart;eStart=eEnd;eEnd=r1;} /* swap */
r1 = MIN(start,(*tocH)->count);
r2 = MIN(end,(*tocH)->count-1);
r1 = MAX(r1,0);
r2 = MAX(r2,0);
win->hasSelection = False;
/*
* first range; from the beginning to just before the new selection range
*/
if (cmd)
{
for (sNum=0;sNum<r1;sNum++)
if ((*tocH)->sums[sNum].selected)
{
win->hasSelection = True;
break;
}
}
else
{
for (sNum=0;sNum<r1;sNum++)
if ((*tocH)->sums[sNum].selected)
{
(*tocH)->userActive = false;
(*tocH)->sums[sNum].selected = False;
if (topVis <= sNum && sNum <= botVis)
InvertSum(tocH,sNum,False);
}
}
/*
* the new selection range
*/
if (cmd)
{
for (sNum=r1;sNum<=r2;sNum++)
if (sNum<eStart || sNum > eEnd)
{
(*tocH)->userActive = false;
(*tocH)->sums[sNum].selected = !(*tocH)->sums[sNum].selected;
if (topVis <= sNum && sNum <= botVis)
InvertSum(tocH,sNum,False);
win->hasSelection = win->hasSelection || (*tocH)->sums[sNum].selected;
}
}
else
{
for (sNum=r1;sNum<=r2;sNum++)
{
if (!(*tocH)->sums[sNum].selected)
{
(*tocH)->userActive = false;
(*tocH)->sums[sNum].selected = True;
if (topVis <= sNum && sNum <= botVis)
InvertSum(tocH,sNum,False);
}
}
win->hasSelection = r1<=r2;
}
/*
* above the new selection range
*/
if (cmd)
{
if (!win->hasSelection)
for (sNum=r2+1;sNum<(*tocH)->count;sNum++)
if ((*tocH)->sums[sNum].selected)
{
win->hasSelection = True;
break;
}
}
else
{
for (sNum=r2+1;sNum<(*tocH)->count;sNum++)
if ((*tocH)->sums[sNum].selected)
{
(*tocH)->userActive = false;
(*tocH)->sums[sNum].selected = False;
if (topVis <= sNum && sNum <= botVis)
InvertSum(tocH,sNum,False);
}
}
InfiniteClip(GetMyWindowCGrafPtr(win));
PopGWorld();
(*tocH)->updateBoxSizes = true;
(*tocH)->conConMultiScan = true;
}
/**********************************************************************
* BoxSetSummarySelected - make sure a summary is selected or not
**********************************************************************/
void BoxSetSummarySelected(TOCHandle tocH,short sumNum,Boolean selected)
{
if ((*tocH)->sums[sumNum].selected != selected)
{
(*tocH)->sums[sumNum].selected = selected;
InvalSum(tocH,sumNum);
(*tocH)->updateBoxSizes = true;
(*tocH)->conConMultiScan = true;
}
}
/**********************************************************************
* BoxActivate - perform extra mailbox processing for activate/deactivate
**********************************************************************/
void BoxActivate(MyWindowPtr win)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
SetPort_(GetMyWindowCGrafPtr(win));
if (!win->pte && !(*tocH)->searchFocus) BoxListFocus(tocH,win->isActive);
ShowBoxSizes(win);
if ((*tocH)->drawerWin)
// Keep drawer in sync
MBDrawerActivate((*tocH)->drawerWin,win->isActive);
}
/**********************************************************************
* BoxListFocus - focus on the list
**********************************************************************/
void BoxListFocus(TOCHandle tocH,Boolean focus)
{
int topVis;
int botVis;
int sNum;
MyWindowPtr win = (*tocH)->win;
CGrafPtr winPort = GetMyWindowCGrafPtr(win);
SetPort_(winPort);
if (focus!=(*tocH)->listFocus)
{
(*tocH)->listFocus = focus;
if (focus) (*tocH)->searchFocus = false;
if (FirstMsgSelected(tocH)!=-1)
{
ClipRect(&win->contR);
topVis = GetControlValue(win->vBar);
botVis = (*tocH)->count -
(GetControlMaximum(win->vBar)-GetControlValue(win->vBar));
if (botVis==(*tocH)->count) botVis--;
for (sNum=topVis; sNum<=botVis; sNum++)
if ((*tocH)->sums[sNum].selected) InvertSum(tocH,sNum,True);
InfiniteClip(winPort);
}
}
}
/**********************************************************************
* InvertSum - invert a summary, doing it correctly (more or less)
**********************************************************************/
void InvertSum(TOCHandle tocH,short sumNum,Boolean forActivate)
{
Rect r, r2;
Boolean above, below; // are sums above and below selected?
MyWindowPtr win = (*tocH)->win;
r.top = win->topMargin+(sumNum-GetControlValue(win->vBar))*win->vPitch;
r.bottom = r.top + win->vPitch;
r.left = 0;
r.right = win->contR.right;
//adjust top of rectangle, which otherwise overlaps top line
r.top++;
RGBBackColor(&win->backColor);
if (!forActivate && (*tocH)->listFocus && !InBG)
{
UseListColors ? InvertSumByColumns(tocH,&r) : HiInvertRect(&r);
}
else
{
// are sums above or below selected?
//above = sumNum && (*tocH)->sums[sumNum-1].selected;
//below = sumNum<(*tocH)->count-1 && (*tocH)->sums[sumNum+1].selected;
above = below = False;
if (above) r.top--; //if selected above, overlap with separator line
if (below) r.bottom++; //if selected below, overlap with separator line
if (forActivate)
{
InsetRect(&r,1,1); //if activation, don't do rim, just inside
UseListColors ? InvertSumByColumns(tocH,&r) : HiInvertRect(&r);
}
else
{
// top
r2 = r;
r2.bottom = r.top+1;
UseListColors ? InvertSumByColumns(tocH,&r2) : HiInvertRect(&r2);
// bottom
r2 = r;
r2.top = r.bottom-1;
UseListColors ? InvertSumByColumns(tocH,&r2) : HiInvertRect(&r2);
// left
r2 = r;
r2.top++; r2.bottom--; //take care of overlap with top, bottom
r2.right = r2.left+1;
UseListColors ? InvertSumByColumns(tocH,&r2) : HiInvertRect(&r2);
// right
r2 = r;
r2.top++; r2.bottom--; //take care of overlap with top, bottom
r2.left = r2.right-1;
UseListColors ? InvertSumByColumns(tocH,&r2) : HiInvertRect(&r2);
}
}
}
/**********************************************************************
* InvertSumByColums - invert a summary, paying attention to column backgrounds
**********************************************************************/
void InvertSumByColumns(TOCHandle tocH,Rect *r)
{
Rect r2;
short line;
short h = -GetControlValue((*tocH)->win->hBar)*(*tocH)->win->hPitch;
for (line=1;line<BoxLinesLimit;line++)
{
r2 = *r;
if (BoxLimits(line,&r2.left,&r2.right,tocH))
{
OffsetRect(&r2,h,0);
if (SectRect(r,&r2,&r2))
{
SetThemeBackground(MBGetSort(tocH,line) ? kThemeBrushListViewSortColumnBackground:kThemeBrushListViewBackground,RectDepth(&r2),true);
HiInvertRect(&r2);
}
}
}
SET_COLORS;
}
/**********************************************************************
* BoxMenu - handle a menu choice for a mailbox
**********************************************************************/
Boolean BoxMenu(MyWindowPtr win,int menu,int item,short modifiers)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
short state;
short tableId;
uLong when;
MSumPtr sum;
Boolean swap = False;
TextAddrHandle addr=nil;
short sumNum;
#ifdef TWO
FilterPB fpb;
#endif
if (menu==MESSAGE_MENU && item==MESSAGE_QUEUE_ITEM && modifiers&optionKey)
{
menu = CHANGE_HIER_MENU;
item = CHANGE_QUEUEING_ITEM;
swap = True;
}
switch (menu)
{
case FILE_MENU:
switch(item)
{
case FILE_OPENSEL_ITEM:
if (win->hasSelection)
BoxOpen(win);
else
return(False);
break;
case FILE_SAVE_AS_ITEM:
DoSaveSelectedAs(tocH);
return(True);
break;
case FILE_BROWSE_ITEM:
DoIterativeThingy(tocH,menu,modifiers,(void*)item);
return(true);
break;
case FILE_PRINT_ONE_ITEM:
case FILE_PRINT_ITEM:
if (win->hasSelection)
{
long beginSel, endSel;
PETEHandle pte = nil;
if ((*tocH)->previewID && (*tocH)->previewPTE && PeteLen((*tocH)->previewPTE))
{
if ((modifiers&shiftKey) && AttIsSelected(win,win->pte,-1,-1,attOpen+attPrint,nil,nil))
break;// done
else
PeteGetTextAndSelection((*tocH)->previewPTE,nil,&beginSel,&endSel);
pte = (*tocH)->previewPTE;
}
else beginSel = endSel = 0;
#ifdef GX_PRINTING
if (gGXIsPresent)
GXPrintSelectedMessages(tocH,beginSel!=endSel && (modifiers&shiftKey),item==FILE_PRINT_ONE_ITEM,beginSel,endSel);
else
#endif //GX_PRINTING
(void) PrintSelectedMessages(tocH,beginSel!=endSel && (modifiers&shiftKey),item==FILE_PRINT_ONE_ITEM,beginSel,endSel,pte);
}
else
return(False);
break;
default:
return(False);
}
break;
case EDIT_MENU:
#ifdef SPEECH_ENABLED
if (win->pte && item != EDIT_SPEAK_ITEM) return(false); // preview focus!
#else
if (win->pte) return(false); // preview focus!
#endif
switch(item)
{
case EDIT_CLEAR_ITEM:
if ((*tocH)->previewID && PreviewReadDelete && (sumNum=LastMsgSelected(tocH))>=0)
BeenThereDoneThat(tocH,sumNum);
DoIterativeThingy(tocH,MESSAGE_DELETE_ITEM,modifiers,0);
break;
case EDIT_COPY_ITEM:
CopySelectedSummaries(tocH);
break;
case EDIT_SELECT_ITEM:
if ((modifiers&optionKey) && -1<(sumNum=LastMsgSelected(tocH)))
BoxSelectSame(tocH,(*tocH)->lastSort==SORT_SUBJECT_ITEM?SORT_SUBJECT_ITEM:SORT_SENDER_ITEM,sumNum);
else
SelectBoxRange(tocH,0,(*tocH)->count-1,False,0,0);
break;
#ifdef SPEECH_ENABLED
case EDIT_SPEAK_ITEM:
(void) SpeakSelectedMessages (tocH);
break;
#endif
default:
return(False);
break;
}
break;
case SORT_HIER_MENU:
SetMyCursor(watchCursor);
if (item==SORT_GROUP_ITEM)
BoxToggleLaurence(tocH);
else if (item>SORT_BAR2_ITEM)
BoxCustomSort(tocH,item-SORT_BAR2_ITEM);
else
{
MBSortHit(tocH,BoxItem2Line(item),(modifiers&optionKey)!=0,(modifiers&shiftKey)!=0);
}
break;
case MESSAGE_MENU:
switch(item)
{
case MESSAGE_DELETE_ITEM:
if ((*tocH)->previewID && PreviewReadDelete && (sumNum=LastMsgSelected(tocH))>=0)
BeenThereDoneThat(tocH,sumNum);
NoSaves = True;
case MESSAGE_FORWARD_ITEM:
case MESSAGE_REDISTRIBUTE_ITEM:
case MESSAGE_SALVAGE_ITEM:
DoIterativeThingy(tocH,item,modifiers,0);
NoSaves = False;
break;
case MESSAGE_QUEUE_ITEM:
QueueSelectedMessages(tocH,QUEUED,0);
if (!Offline && PrefIsSet(PREF_AUTO_SEND))
XferMail(False,True,False,False,True,0);
break;
case MESSAGE_JUNK_ITEM:
case MESSAGE_NOTJUNK_ITEM:
Junk(tocH,-1,item==MESSAGE_JUNK_ITEM,false);
break;
case MESSAGE_REPLY_ITEM:
BoxReply(tocH,menu,item,modifiers);
NoSaves = false;
default:
return(False);
}
break;
case CHANGE_HIER_MENU:
switch(item)
{
case CHANGE_QUEUEING_ITEM:
when = 0;
for (sum=(*tocH)->sums;sum<(*tocH)->sums+(*tocH)->count;sum++)
if (sum->selected)
{
if (sum->state==TIMED) when = sum->seconds;
state = sum->state;
break;
}
if (ModifyQueue(&state,&when,swap))
{
Boolean now = state==SENT;
if (now) state = QUEUED;
QueueSelectedMessages(tocH,state,when);
if (now) XferMail(False,True,False,False,True,0);
}
break;
default: return(False);
}
break;
case TABLE_HIER_MENU:
if (Menu2TableId(tocH,GetMHandle(TABLE_HIER_MENU),item,&tableId))
DoIterativeThingy(tocH,menu,modifiers,(void*)tableId);
break;
case STATE_HIER_MENU:
if (item == statmQueued) QueueSelectedMessages(tocH,QUEUED,0);
else
{
DoIterativeThingy(tocH,menu,modifiers,(void*)item);
SetSendQueue();
}
break;
case PERS_HIER_MENU:
if (HasFeature (featureMultiplePersonalities)) {
UseFeature (featureMultiplePersonalities);
DoIterativeThingy(tocH,menu,modifiers,(void*)item);
}
break;
case LABEL_HIER_MENU:
case SERVER_HIER_MENU:
DoIterativeThingy(tocH,menu,modifiers,(void*)item);
break;
case SPECIAL_MENU:
switch(AdjustSpecialMenuSelection(item))
{
case SPECIAL_MAKE_NICK_ITEM:
if (win->pte && modifiers&shiftKey) MakeNickFromSelection(win);
else if ((*tocH)->which==OUT) MakeCboxNick(win);
else MakeMboxNick(win,modifiers);
break;
case SPECIAL_MAKEFILTER_ITEM:
DoMakeFilter(win);
break;
case SPECIAL_FILTER_ITEM:
FilterSelectedMessages(flkManual,tocH,&fpb);
FilterPostprocess(flkManual,&fpb);
break;
default:
return(False);
}
break;
case REPLY_WITH_HIER_MENU:
if (HasFeature (featureStationery)) {
UseFeature (featureStationery);
DoIterativeThingy(tocH,MESSAGE_REPLY_ITEM,modifiers,(void*)item);
}
break;
case FORWARD_TO_HIER_MENU:
DoIterativeThingy(tocH,MESSAGE_FORWARD_ITEM,modifiers,addr=MenuItem2Handle(menu,item));
break;
case REDIST_TO_HIER_MENU:
DoIterativeThingy(tocH,MESSAGE_REDISTRIBUTE_ITEM,modifiers,addr=MenuItem2Handle(menu,item));
break;
case PRIOR_HIER_MENU:
DoIterativeThingy(tocH,menu,modifiers,(void*)item);
break;
case WINDOW_MENU:
{
Str255 s;
if (!(win->pte && *PeteSelectedString(s,win->pte)))
if (item==WIN_PH_ITEM && (modifiers&shiftKey) && 0<=(sumNum=FirstMsgSelected(tocH)))
{
OpenPh(PCopy(s,(*tocH)->sums[sumNum].from));
break;
}
}
return(False);
break;
case TLATE_SEL_HIER_MENU:
if ((*tocH)->previewID && (*tocH)->previewPTE)
{
HeadSpec hs;
UHandle text;
PeteGetTextAndSelection((*tocH)->previewPTE,&text,&hs.value,&hs.stop);
if (hs.value==hs.stop)
{
hs.value = BodyOffset(text);
hs.stop = PeteLen((*tocH)->previewPTE);
}
hs.start = hs.value;
ETLTransSelection((*tocH)->previewPTE,&hs,item);
}
else
return(false);
break;
default:
return(TransferMenuChoice(menu,item,tocH,-1,modifiers,False));
break;
}
ZapHandle(addr);
return(True);
}
/************************************************************************
* BoxReply - reply to a selection in a mailbox
************************************************************************/
OSErr BoxReply(TOCHandle tocH,short menu,short item,short modifiers)
{
short count = CountSelectedMessages(tocH);
if (PrefIsSet(PREF_MULTI_REPLY) || count <= 1)
{
DoIterativeThingy(tocH,item,modifiers,nil);
return noErr;
}
else
{
if (count > GetRLong(MULT_RESPOND_WARNING_THRESH))
if (!MultiMessageOpOK(MULT_RESPOND_WARNING,count))
return userCanceledErr;
return DoReplyMultiple(tocH,modifiers)?noErr:userCanceledErr;
}
}
/************************************************************************
* MultiMessageOpOK - is it ok to do something to a lot of messages?
************************************************************************/
Boolean MultiMessageOpOK(short fmt,short count)
{
return kAlertStdAlertCancelButton!=ComposeStdAlert(Caution,fmt,count);
}
void BoxToggleLaurence(TOCHandle tocH)
{
Str255 s;
ControlHandle cntl;
GetRString(s,ColumnHeadStrn+blSubject);
if ((*tocH)->laurence)
(*tocH)->laurence = false;
else
{
PCatC(s,'<EFBFBD>');
(*tocH)->laurence = true;
}
(*tocH)->resort = kResortNow;
if (cntl=FindControlByRefCon((*tocH)->win,'wide'+blSubject))
SetControlTitle(cntl,s);
}
/************************************************************************
* ApplySortString - apply a custom sort
************************************************************************/
void ApplySortString(TOCHandle tocH,PStr s)
{
Boolean group;
Byte sorts[12];
short i;
ControlHandle cntl;
if (!InterpretSortString(s,&group,sorts,nil))
{
if (group!=(*tocH)->laurence) BoxToggleLaurence(tocH);
for (i=0;i<12;i++)
if (cntl=FindControlByRefCon((*tocH)->win,'wide'+i+1))
SetControlValue(cntl,(sorts[i]!=0) ? 1:0);
BMD(sorts,(*tocH)->sorts,sizeof((*tocH)->sorts));
(*tocH)->resort = kResortNow;
}
}
/************************************************************************
* BoxCustomSort - apply a custom sort
************************************************************************/
void BoxCustomSort(TOCHandle tocH,short which)
{
Str255 s;
GetRString(s,SortsStrn+which);
ApplySortString(tocH,s);
}
/**********************************************************************
* MenuItem2Handle - convert a menu item to a handle
**********************************************************************/
TextAddrHandle MenuItem2Handle(short menu, short item)
{
Str255 itemStr;
MyGetItem(GetMHandle(menu),item,itemStr);
return((TextAddrHandle)PStr2Handle(itemStr));
}
/**********************************************************************
* CheckSortItems - check the appropriate items in the sort menu
**********************************************************************/
void CheckSortItems(MyWindowPtr win)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
MenuHandle mh = GetMHandle(SORT_HIER_MENU);
short item;
short sortNum;
for (item=1;item<=SORT_SUBJECT_ITEM;item++)
{
sortNum = BoxItem2Line(item);
SetItemMark(mh,item,MBGetSort(tocH,sortNum) ? checkMark : noMark);
}
SetItemMark(mh,SORT_GROUP_ITEM,(*tocH)->laurence ? checkMark:noMark);
EnableIf(mh,SORT_MOOD_ITEM,AnalDoIncoming());
EnableIf(mh,SORT_MAILBOX_ITEM,(*tocH)->virtualTOC);
}
/************************************************************************
* InterpretSortString - interpret a string that represents a complex sort
************************************************************************/
OSErr InterpretSortString(PStr s,Boolean *group,Byte *sorts,PStr menuItem)
{
UPtr spot;
Str63 token;
short i;
OSErr err = fnfErr;
long num;
spot = s+1;
if (PToken(s,token,&spot," "))
{
if (group) *group = 0!=atoi(token+1);
for (i=0;i<12;i++)
if (PToken(s,token,&spot," "))
if (sorts)
{
StringToNum(token,&num);
sorts[i] = num;
}
if (PToken(s,token,&spot,"\377"))
{
err = noErr;
if (menuItem) PCopy(menuItem,token);
}
}
return(err);
}
/************************************************************************
* Sort2String - represent a mailbox sort as a string
************************************************************************/
PStr Sort2String(PStr s,TOCHandle tocH)
{
short i;
Str15 num;
*s = 0;
PCatC(s,((*tocH)->laurence)!=0?'1':'0');
PCatC(s,' ');
for (i=0;i<12;i++)
{
NumToString((*tocH)->sorts[i],num);
PCat(s,num);
PCatC(s,' ');
}
PCatR(s,UNTITLED);
return s;
}
/**********************************************************************
* BoxFind - find in the window
**********************************************************************/
Boolean BoxFind(MyWindowPtr win,PStr what)
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
short sumNum,start;
Boolean wrapped = false;
MSumType *sum;
DEC_STATE(tocH);
if (win->pte && win->pte==(*tocH)->previewPTE)
// search in preview pane
return FindInPTE(win,win->pte,what);
if ((*tocH)->count) // don't search if window is empty
{
// search summaries
// find last non-selected and start from there
for (start=(*tocH)->count-1;start>=0;start--)
if ((*tocH)->sums[start].selected) break;
start++;
for(sumNum = start;sumNum != start || !wrapped;sumNum++)
{
if (sumNum>=(*tocH)->count)
{
// start from the top again
sumNum = 0;
wrapped = true;
if (!start)
break; // this is where we started
}
sum=(*tocH)->sums+sumNum;
L_STATE(tocH);
if (FindStrStr(what,sum->from)>=0 || FindStrStr(what,sum->subj)>=0)
{
U_STATE(tocH);
// found
SelectBoxRange(tocH,sumNum,sumNum,false,-1,-1);
BoxCenterSelection((*tocH)->win);
if (!IsWindowVisible(winWP))
ShowMyWindow(winWP);
UserSelectWindow(winWP);
UpdateMyWindow(winWP);
SFWTC=True;
return true;
}
U_STATE(tocH);
}
}
return false;
}
/**********************************************************************
* BoxClose - close a mailbox window, saving the toc if it's dirty
**********************************************************************/
Boolean BoxClose(MyWindowPtr win)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
TOCType **tocH = (TOCType **) GetWindowPrivateData (winWP);
short sumNum;
Boolean justHide = False;
Boolean superClose = PrefIsSet(PREF_SUPERCLOSE) && IsWindowVisible (winWP);
Boolean isOut = (*tocH)->which == OUT;
FSSpec spec;
FileViewHandle fvh = GetFileViewInfo(win);
if (IsWindowVisible(GetMyWindowWindowPtr((*tocH)->win)) || TOCIsDirty(tocH)) AnyTOCDirty++;
// if this is an IMAP mailbox, we'll need to do some extra stuff to close it.
if ((*tocH)->imapTOC != 0)
{
IMAPAbortResync(tocH);
justHide = IsIMAPMailboxBusy(tocH) || IMAPAutoExpungeMailbox(tocH); // simply hide this mailbox if it's busy for some reason.
}
if (fvh && (*tocH)->fileView)
(*tocH)->previewHi = (*fvh)->savePreviewHi;
if (!GrowZoning)
{
/*
* should we compact?
*/
if (NeedAutoCompact(tocH))
{
spec = GetMailboxSpec(tocH,-1);
StackPush(&spec,CompactStack);
}
/*
* first, close all associated message windows
*/
for (sumNum=(*tocH)->count-1;sumNum>=0;sumNum--)
{
if ((*tocH)->sums[sumNum].messH)
{
if (superClose && (!isOut ||!(*(*tocH)->sums[sumNum].messH)->win->isDirty))
{
if (!CloseMyWindow(GetMyWindowWindowPtr((*(*tocH)->sums[sumNum].messH)->win)))
return(False);
}
else
justHide = True;
}
if (!justHide && (*tocH)->sums[sumNum].cache) ZapHandle((*tocH)->sums[sumNum].cache);
}
/*
* dispose of transport error message data
*/
for (sumNum=(*tocH)->count-1;sumNum>=0;sumNum--)
ZapHandle((*tocH)->sums[sumNum].mesgErrH);
/*
* write if we're dirty
*/
if (TOCIsDirty(tocH)) WriteTOC(tocH);
/*
* Save the IMAP mailbox information
*/
if ((*tocH)->imapTOC) UpdateIMAPMailboxInfo(tocH);
}
BoxFClose(tocH,true);
if (justHide)
{
HideWindow_(winWP);
return(False);
}
if ((*tocH)->drawerWin)
{
// Close drawer
CloseMyWindow(GetMyWindowWindowPtr((*tocH)->drawerWin));
(*tocH)->drawerWin = NULL;
}
if (fvh)
{
// save changes?
if ((*fvh)->dirtyPrefs || (*fvh)->expandList.fExpandListChanged)
{
short oldResFile = CurResFile(),refN = 0;
if ((*tocH)->refN) UseResFile((*tocH)->refN);
else
{
FSSpec resSpec = GetMailboxSpec(tocH,0);
refN = FSpOpenResFile(&resSpec,fsRdWrPerm);
if (refN == -1) refN = 0;
}
if ((*tocH)->refN || refN)
{
Handle hFVPrefs;
Handle hExpandList;
if ((*fvh)->expandList.fExpandListChanged)
{
hExpandList = Get1Resource(kExandListResType,kExandListResID);
if (!hExpandList)
{
hExpandList = NuHandle(0);
AddResource_(hExpandList,kExandListResType,kExandListResID,"");
}
if (hExpandList)
{
SetHandleSize(hExpandList,0);
HandAndHand((Handle)(*fvh)->expandList.hExpandList,hExpandList);
ChangedResource(hExpandList);
}
}
if ((*fvh)->dirtyPrefs)
{
hFVPrefs = Get1Resource(kFVPrefsResType,kFVPrefsResID);
if (!hFVPrefs)
{
hFVPrefs = NuHandle(sizeof(FVPrefs));
(*(FVPrefsHandle)hFVPrefs)->version = kFVPrefsVersion;
AddResource_(hFVPrefs,kFVPrefsResType,kFVPrefsResID,"");
}
if (hFVPrefs)
{
BMD(&(*fvh)->prefs,*hFVPrefs,sizeof(FVPrefs));
ChangedResource(hFVPrefs);
}
}
if (refN) CloseResFile(refN);
}
UseResFile(oldResFile);
}
// close file viewer
FVDispose(fvh);
}
/*
* now, unlink ourselves from the list
*/
LL_Remove(TOCList,tocH,(TOCType **));
/*
* bye, bye love
*/
ZapHandle(tocH);
/*
* turn off some stuff
*/
ZeroWinFuncs(win);
return(True);
}
/**********************************************************************
* BoxOpen - open some messages from within a mailbox
**********************************************************************/
void BoxOpen(MyWindowPtr win)
{
// Carbon version can't preallocate windows and doesn't need to
MyWindowPtr lastWin=nil, w;
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
int sum,limit;
uLong count = CountSelectedMessages(tocH);
if (count>GetRLong(WIN_GEN_WARNING_THRESH) && !MultiMessageOpOK(WIN_GEN_WARNING,count))
return;
// Walk through the selected entries, activating windows as needed
for (limit=(*tocH)->count,sum=0; sum<limit; sum++)
{
if ((*tocH)->sums[sum].selected)
if ((*tocH)->sums[sum].messH)
{
MyWindowPtr tocMessWin = (*(MessHandle)(*tocH)->sums[sum].messH)->win;
WindowPtr tocMessWinWP = GetMyWindowWindowPtr (tocMessWin);
if (!IsWindowVisible (tocMessWinWP))
ShowMyWindow(tocMessWinWP);
#ifdef FLOAT_WIN
UserSelectWindow(tocMessWinWP);
#else //FLOAT_WIN
SelectWindow_(tocMessWinWP);
#endif //FLOAT_WIN
lastWin = tocMessWin;
}
}
for (sum=(*tocH)->count-1;sum>=0; sum--)
{
if ((*tocH)->sums[sum].selected && !(*tocH)->sums[sum].messH)
{
short realSum;
TOCHandle realTOC;
MiniEvents(); if (CommandPeriod) break;
if (!(realTOC = GetRealTOC(tocH,sum,&realSum))) break;
if (!(w=GetAMessage(realTOC,realSum,nil,nil,True))) break;
if (tocH != realTOC)
{
BeenThereDoneThat(tocH,sum); // set status to READ in virtual mailbox
(*Win2MessH(w))->openedFromTocH = tocH; // opened from virtual mailbox
(*Win2MessH(w))->openedFromSerialNum = (*tocH)->sums[sum].serialNum;
}
if (lastWin)
{
WindowPtr lastWinWP = GetMyWindowWindowPtr(lastWin);
ActivateMyWindow(lastWinWP,False);
NotUsingWindow(lastWinWP);
}
lastWin = w;
MonitorGrow(True);
}
}
}
/************************************************************************
* BoxKey - handle keystrokes in a mailbox window
************************************************************************/
Boolean BoxKey(MyWindowPtr win, EventRecord *event)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
short c = event->message & charCodeMask;
Boolean result = true;
if (c==tabKey && ((*tocH)->previewID || (*tocH)->drawer))
{
if (win->pte)
{
// Preview -> drawer or box list
PeteFocus(win,nil,true);
if ((*tocH)->drawer)
{
// Have drawer
if ((*tocH)->drawerWin) MBDrawerSetFocus((*tocH)->drawerWin, true);
}
else
// Do box list
BoxListFocus(tocH,true);
}
else if ((*tocH)->listFocus)
{
// Box list -> preview or drawer
BoxListFocus(tocH,false);
if ((*tocH)->previewPTE)
{
// Have preview
PeteFocus(win,(*tocH)->previewPTE,true);
if (PreviewReadFocus) BeenThereDoneThat(tocH,-1);
}
else
{
// Do drawer
if ((*tocH)->drawerWin) MBDrawerSetFocus((*tocH)->drawerWin, true);
}
}
else
{
// Drawer -> box list
if ((*tocH)->drawerWin) MBDrawerSetFocus((*tocH)->drawerWin, false);
BoxListFocus(tocH,true);
}
}
else if (win->pte) result = BoxPreviewKey(win,event); // Key goes to preview
else if ((*tocH)->drawer && (*tocH)->drawerWin && !(*tocH)->listFocus)
{
// Key goes to drawer
(*tocH)->userActive = true;
// Don't use tocH after this point. May no longer be valid
return ((*tocH)->drawerWin->key)((*tocH)->drawerWin,event);
}
else result = BoxListKey(win,event); // Key goes to box list
(*tocH)->userActive = true;
return(result);
}
/************************************************************************
* BoxListKey - keystroke to list portion of mailbox
************************************************************************/
Boolean BoxListKey(MyWindowPtr win,EventRecord *event)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
short c = event->message & charCodeMask;
Boolean cmd = (event->modifiers & (cmdKey|shiftKey)) != 0;
int mNum = -1;
long uLetter = UnadornMessage(event)&charCodeMask;
Boolean shift = 0!=(event->modifiers & shiftKey);
SFWTC = True;
if (leftArrowChar <= uLetter && uLetter <= downArrowChar &&
win->hasSelection &&
IsArrowSwitch(event->modifiers))
c = enterChar;
switch (c)
{
case leftArrowChar:
case upArrowChar:
for (mNum=0;mNum<(*tocH)->count;mNum++)
if ((*tocH)->sums[mNum].selected) break;
mNum--;
if (mNum<0) mNum = 0;
break;
case rightArrowChar:
case downArrowChar:
for (mNum=(*tocH)->count-1;mNum>=0;mNum--)
if ((*tocH)->sums[mNum].selected) break;
mNum++;
if (mNum==(*tocH)->count) mNum--;
break;
case delChar:
case deleteKey:
if (event->what!=autoKey && (!PrefIsSet(PREF_NO_DELKEY) || 0!=(event->modifiers&cmdKey))) BoxMenu(win,EDIT_MENU,EDIT_CLEAR_ITEM,event->modifiers);
else return(false);
break;
case ' ':
BoxIdle(win);
if (win->hasSelection && (*tocH)->previewID)
{
if (PeteScroll((*tocH)->previewPTE,0,shift ? psePageUp : psePageDn) && !shift)
{
mNum = LastMsgSelected(tocH);
if (PreviewReadNext) BeenThereDoneThat(tocH,-1);
EzOpen(tocH,mNum,(*tocH)->ezOpenSerialNum,event->modifiers,true,false);
mNum = -1;
}
else if (PreviewReadFocus) BeenThereDoneThat(tocH,-1);
break;
}
// fall through is deliberate if no preview
case returnChar:
case enterChar:
if (win->hasSelection)
{
BoxOpen(win);
break;
}
/* fall through is deliberate for no selection */
default:
if (event->modifiers & cmdKey)
{
return(False);
}
else if (*Type2SelString)
BoxType2Select(tocH,Type2SelString);
break;
}
if (mNum > -1)
{
SelectBoxRange(tocH,mNum,mNum,cmd,-1,-1);
BoxCenter((*tocH)->win,mNum);
}
return(True);
}
/************************************************************************
* BoxPreviewKey - keystroke to preview portion of mailbox
************************************************************************/
Boolean BoxPreviewKey(MyWindowPtr win,EventRecord *event)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
short c = event->message & charCodeMask;
Boolean cmd = (event->modifiers & (cmdKey|shiftKey)) != 0;
long uLetter = UnadornMessage(event)&charCodeMask;
short mNum;
Boolean shift = 0 != (event->modifiers & shiftKey);
SFWTC = True;
if (cmd) return(false);
if ((*tocH)->previewID && (mNum=FirstMsgSelected(tocH))!=-1 && PreviewReadNext)
BeenThereDoneThat(tocH,-1);
if (c==' ')
{
if (PeteScroll((*tocH)->previewPTE,0,shift ? psePageUp : psePageDn) && !shift)
{
mNum = FirstMsgSelected(tocH);
if (PreviewReadNext) BeenThereDoneThat(tocH,mNum);
EzOpen(tocH,mNum,(*tocH)->ezOpenSerialNum,event->modifiers,true,false);
}
}
else if (c==returnKey || c==enterKey)
return(BoxListKey(win,event));
else if (DirtyKey(event->message))
AlertStr(READ_ONLY_ALRT, Stop, nil);
else
{
PeteEdit(win,win->pte,peeEvent,(void*)event);
}
(*tocH)->userActive = true;
return(True);
}
/************************************************************************
* BoxCenter - center a mailbox around a given line
************************************************************************/
void BoxType2Select(TOCHandle tocH,PStr string)
{
UPtr spot;
short difference;
short i;
short subFound = -1;
short fromFound = -1;
short thisDiff;
short found;
/* subject */
difference = REAL_BIG;
for (i=(*tocH)->count;i--;)
if (spot=PPtrFindSub(Type2SelString,(*tocH)->sums[i].subj+1,*(*tocH)->sums[i].subj))
{
thisDiff = spot-(*tocH)->sums[i].subj;
if (spot>(*tocH)->sums[i].subj && IsWordChar[spot[-1]]) thisDiff += 100;
if (difference>thisDiff)
{
subFound = i;
difference = thisDiff;
}
}
/* sender */
difference = REAL_BIG;
for (i=(*tocH)->count;i--;)
if (spot=PPtrFindSub(Type2SelString,(*tocH)->sums[i].from+1,*(*tocH)->sums[i].from))
{
thisDiff = spot-(*tocH)->sums[i].from;
if (spot>(*tocH)->sums[i].from && IsWordChar[spot[-1]]) thisDiff += 100;
if (difference>thisDiff)
{
fromFound = i;
difference = thisDiff;
}
}
UL(tocH);
// if the string is found in only one column, use that column
if (subFound < 0) found = fromFound;
else if (fromFound < 0) found = subFound;
// if it's found in both, use from unless the mailbox is sorted by subject
else if ((*tocH)->lastSort==SORT_SUBJECT_ITEM) found = subFound;
else found = fromFound;
if (found>=0)
{
SelectBoxRange(tocH,found,found,False,-1,-1);
BoxCenterSelection((*tocH)->win);
}
}
/************************************************************************
* BoxCenter - center a mailbox around a given line
************************************************************************/
void BoxCenter(MyWindowPtr win, short mNum)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
int topVis = GetControlValue(win->vBar);
int botVis = topVis + RectHi(win->contR)/win->vPitch-1;
if (mNum<topVis || mNum>botVis)
{
UpdateMyWindow(GetMyWindowWindowPtr(win)); /* this is a hack. ScrollIt should be smarter */
ScrollIt(win,0,(topVis+botVis)/2-mNum);
}
}
/************************************************************************
* BoxSelectAfter - select the message on or after a given message,
* if no messages are already selected
************************************************************************/
void BoxSelectAfter(MyWindowPtr win, short mNum)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
if (mNum >=0 && BoxNextSelected(tocH,-1)<0)
if (win->hasSelection = ((*tocH)->count>0))
{
mNum = MIN(mNum,(*tocH)->count-1);
(*tocH)->sums[mNum].selected = True;
BoxCenter((*tocH)->win,mNum);
}
}
/************************************************************************
* BoxCenterSelection - center a selection in a mailbox window
************************************************************************/
void BoxCenterSelection(MyWindowPtr win)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
int top, bottom;
for (top=0;top<(*tocH)->count;top++)
if ((*tocH)->sums[top].selected) break;
for (bottom=(*tocH)->count-1;bottom>=0;bottom--)
if ((*tocH)->sums[bottom].selected) break;
if (top<=bottom) BoxCenter(win,(top+bottom)/2);
}
/************************************************************************
*
************************************************************************/
Boolean BoxPosition(Boolean save,MyWindowPtr win)
{
Rect r;
Boolean zoomed;
WindowPtr winWP = GetMyWindowWindowPtr (win);
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
Str31 name;
FSSpec spec = GetMailboxSpec(tocH,-1);
PCopy(name,spec.name);
if (save)
{
utl_SaveWindowPos(winWP,&r,&zoomed);
SavePosFork(spec.vRefNum,spec.parID,name,&r,zoomed);
}
else
{
if (!RestorePosFork(spec.vRefNum,spec.parID,name,&r,&zoomed))
{UL(tocH); return(False);}
utl_RestoreWindowPos(winWP,&r,zoomed,1,TitleBarHeight(winWP),LeftRimWidth(winWP),(void*)FigureZoom,(void*)DefPosition);
}
return(True);
}
/************************************************************************
* SaveMessageAs - save a message in text only form
************************************************************************/
void DoSaveSelectedAs(TOCHandle tocH)
{
long creator;
short refN;
short err;
Str31 scratch;
int sumNum;
MessHandle messH;
short lastSelected = -1;
Str255 title;
FSSpec spec;
MyWindowPtr win;
short count = CountSelectedMessages(tocH);
ModalFilterYDUPP filter=nil;
/*
* tickle stdfile
*/
GetPref(scratch,PREF_CREATOR);
if (*scratch!=4) GetRString(scratch,TEXT_CREATOR);
BMD(scratch+1,&creator,4);
sumNum = FirstMsgSelected(tocH);
MakeMessFileName(tocH,sumNum,spec.name);
Stationery = (HasFeature(featureStationery)&&(*tocH)->which==OUT&&LastMsgSelected(tocH)==sumNum) ? False : 2; /* 2 is our signal to the filter to hide the item */
if (err=SFPutOpen(&spec,creator,'TEXT',&refN,nil,filter,SAVEAS_DLOG,nil,nil,nil))
return;
OpenProgress();
ProgressR(NoBar,count,MOVING_MESSAGES,LEFT_TO_TRANSFER,nil);
for (;!err && sumNum<(*tocH)->count;sumNum++)
if ((*tocH)->sums[sumNum].selected)
{
// if this is an IMAP message, make sure it's been downloaded before we try to save it
if ((*tocH)->imapTOC) EnsureMsgDownloaded(tocH, sumNum, false);
MakeMessTitle(title,tocH,sumNum,True);
Progress(NoBar,count--,nil,nil,title);
messH = (*tocH)->sums[sumNum].messH;
if (win=GetAMessage(tocH,sumNum,nil,nil,False))
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
err = SaveAsToOpenFile(refN,Win2MessH(win));
if (!IsWindowVisible (winWP)) CloseMyWindow(winWP);
}
}
CloseProgress();
/*
* close
*/
(void) MyFSClose(refN);
/*
* report error
*/
if (err)
{
FileSystemError(COULDNT_SAVEAS,spec.name,err);
FSpDelete(&spec);
}
}
/************************************************************************
* MakeMessFileName - make a default name for save as
************************************************************************/
void MakeMessFileName(TOCHandle tocH,short sumNum, UPtr name)
{
UPtr spot;
short len = MIN(MAX_BOX_NAME,*(*tocH)->sums[sumNum].subj);
BMD((*tocH)->sums[sumNum].subj,name,len+1);
*name = len;
for (spot=name+1;spot<=name+len;spot++)
if (*spot==':') *spot = '-';
}
/************************************************************************
* ShowBoxSizes - title a mailbox window
************************************************************************/
void ShowBoxSizes(MyWindowPtr win)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
Str31 string;
long usedBytes,totalBytes;
short depth=1;
ControlHandle cntl = FindControlByRefCon(win,kBoxSizeRefCon);
PushGWorld();
SetPort_(GetMyWindowCGrafPtr(win));
if (cntl && IsControlVisible(cntl))
{
usedBytes = (*tocH)->usedK K;
totalBytes = (*tocH)->totalK K;
if ((*tocH)->virtualTOC)
if (PrefIsSet(PREF_SHOW_NUM_SELECTED))
ComposeRString(string,BOX_SIZE_SELECT_SRCH_FMT,BoxCountSelected(tocH),(*tocH)->count);
else
NumToString((*tocH)->count,string);
else
if (PrefIsSet(PREF_SHOW_NUM_SELECTED))
ComposeRString(string,BOX_SIZE_SELECT_FMT,BoxCountSelected(tocH),(*tocH)->count,usedBytes,
totalBytes-usedBytes);
else
ComposeRString(string,BOX_SIZE_FMT,(*tocH)->count,usedBytes,
totalBytes-usedBytes);
MySetControlTitle(cntl,string);
}
PopGWorld();
(*tocH)->updateBoxSizes = false;
}
/************************************************************************
* BoxDidResize - handle the size display area
************************************************************************/
void BoxDidResize(MyWindowPtr win, Rect *oldContR)
{
ControlHandle hBar = win->hBar;
ControlHandle vBar = win->vBar;
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
ControlHandle cntl, placard;
short previewHeight;
Boolean again = false;
Rect r;
short goalTopMargin = kHeaderButtonHeight + (ETLHasMBoxContextFolder(win) ? kTabHeight+INSET+kTabGap : 0);
CGrafPtr winPort = GetMyWindowCGrafPtr (win);
Boolean fileView = (*tocH)->hasFileView && (*tocH)->fileView;
if (cntl=FindControlByRefCon(win,'plug'+1))
goalTopMargin += INSET + ControlHi(cntl);
RedoTOC(tocH);
if (win->topMargin!=goalTopMargin && !(*tocH)->virtualTOC)
{
SetTopMargin(win,goalTopMargin);
InvalContent(win);
again = true;
}
// preview height?
previewHeight = BoxPreviewHeight(tocH);
if (win->botMargin != previewHeight)
{
SetBotMargin(win,previewHeight);
InvalContent(win);
again = true;
BoxMakePreview(win);
}
if (again)
{
MyWindowDidResize(win,oldContR); /* reposition window elements */
return;
}
win->dontGreyOnMe = win->contR; // not much grey for us...
if ((*tocH)->previewPTE)
{
Rect rPort;
r = win->contR;
r.top = r.bottom + GROW_SIZE+1;
r.right += GROW_SIZE;
rPort = *GetPortBounds(winPort,&rPort);
r.bottom = rPort.bottom;
PeteDidResize((*tocH)->previewPTE,&r);
}
if (cntl=FindControlByRefCon(win,PREVIEW_TOGGLE_CNTL))
MySetCtlValue(cntl,win->botMargin!=0);
if (hBar)
{
Rect r = *GetControlBounds(hBar,&r);
short delta = GROW_SIZE + BoxSizeWidth(tocH);
short conConPrevProfWidth = (HasFeature(featureConCon) && (*tocH)->previewPTE) ? GetRLong(CONCON_PREV_PROF_WIDTH) : 0;
r.left += delta + conConPrevProfWidth;
MySetCntlRect(hBar,&r);
if (cntl=FindControlByRefCon(win,kBoxSizeRefCon))
{
BoxSizeBox(win,&r,tocH);
InsetRect(&r,-1,-1);
OffsetRect(&r,GROW_SIZE,0);
MySetCntlRect(cntl,&r);
if (fileView) HideControl(cntl); else ShowControl(cntl);
if (cntl=FindControlByRefCon(win,PREVIEW_TOGGLE_CNTL))
{
r.right = r.left;
r.left = -1;
if (!GetSuperControl(cntl,&placard))
MySetCntlRect(placard,&r);
InsetRect(&r,MAX_APPEAR_RIM-1,MAX_APPEAR_RIM-1);
//OffsetRect(&r,-1,-1);
MySetCntlRect(cntl,&r);
}
}
// Size the con con preview profile control
if (cntl=FindControlByRefCon(win,kConConProfRefCon))
if (!fileView && conConPrevProfWidth)
{
BoxSizeBox(win,&r,tocH);
OffsetRect(&r,GROW_SIZE,0);
InsetRect(&r,-1,-1);
r.left = r.right;
r.right = r.left + conConPrevProfWidth-1;
MySetCntlRect(cntl,&r);
ShowMyControl(cntl);
}
else HideControl(cntl);
if (cntl=FindControlByRefCon(win,PREVIEW_DIVIDE_CNTL))
{
r.top = win->contR.bottom;
r.bottom = r.top + GROW_SIZE + 1;
r.left = win->contR.right;
r.right = r.left + GROW_SIZE + 1;
if (!GetSuperControl(cntl,&placard))
MySetCntlRect(placard,&r);
InsetRect(&r,4,4);
MySetCntlRect(cntl,&r);
}
SetRect(&r,win->contR.left,oldContR?oldContR->bottom:win->contR.bottom,win->contR.left+delta,
(oldContR?oldContR->bottom:win->contR.bottom)+GROW_SIZE);
InvalWindowRect(GetMyWindowWindowPtr(win),&r);
}
BoxPlaceBevelButtons(win);
if (cntl=FindControlByRefCon(win,'tabs'))
{
Rect rWin;
FileViewHandle fvh = GetFileViewInfo(win);
GetPortBounds(GetMyWindowCGrafPtr(win),&rWin);
SetRect(&r,-1,win->topMargin-kTabHeight-kHeaderButtonHeight+kTabGap,rWin.right+1,win->topMargin-kHeaderButtonHeight);
SetControlBounds(cntl,&r);
if (fvh)
{
short i;
GetPortBounds(winPort,&r);
r.top = win->topMargin;
if (!(*tocH)->fileView)
OffsetRect(&r,-4000,-4000); // hide the list
LVSize((*fvh)->list,&r,nil);
FVSizeHeaderButtons(win,fvh);
for(i=0;i<kFDColCount;i++)
SetControlVisibility((*fvh)->controls[i],fileView,false);
}
}
if ((*tocH)->drawerWin)
{
// resize drawer
MyWindowDidResize((*tocH)->drawerWin,NULL);
}
}
/************************************************************************
* BoxPreviewHeight - find the preview height for a mailbox
************************************************************************/
short BoxPreviewHeight(TOCHandle tocH)
{
MyWindowPtr win = (*tocH)->win;
CGrafPtr winPort = GetMyWindowCGrafPtr (win);
Rect rPort = *GetPortBounds(winPort,&rPort);
short totalHi = RectHi(rPort) - GROW_SIZE - win->topMargin;
short hi = totalHi;
short min = GetRLong(PREVIEW_USELESS)*FontLead;
short max = totalHi - GetRLong(PREVIEW_USELESS)*win->vPitch;
hi = BoxPreviewHeightWish(tocH);
// fine-tune
//hi -= (totalHi-hi)%win->vPitch; // supposed to make sure we have integral height for summaries
// but causes window size to grow on close/open cycle.
if (hi/FontLead < GetRLong(PREVIEW_USELESS)) hi = 0; // too small to be useful?
if (max<min) hi = 0; // remainder too small to be useful?
else hi = MIN(max,hi);
return(hi);
}
/************************************************************************
* BoxPreviewHeightWish - find the height we'd like the preview to be
************************************************************************/
short BoxPreviewHeightWish(TOCHandle tocH)
{
short hi;
if ((*tocH)->previewHi < 0) return(0); // negative preview means user collapsed it
else if ((*tocH)->previewHi > 1) hi = (*tocH)->previewHi; // positive value > 1 means use it
else if ((*tocH)->previewHi == 1) hi = FontLead*GetRLong(PREVIEW_HI); // 1 means default size
else if (PrefIsSet(PREF_NO_PREVIEW)) return(0); // no percent, default off
else hi = FontLead*GetRLong(PREVIEW_HI); // no percent, default on--use standard percent
return(hi);
}
/************************************************************************
* BoxMakePreview - make the preview pane for a mailbox
************************************************************************/
void BoxMakePreview(MyWindowPtr win)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
ControlHandle cntl, placard;
Rect r;
PETEHandle pte;
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
if (win->botMargin && !(*tocH)->previewPTE)
{
PETEDocInitInfo pdi;
// first make the edit region
DefaultPII(win,false,peVScroll|peGrowBox,&pdi);
pdi.docWidth = RectWi(win->contR)-PETE_SCROLLY_DIFFERENCE;
pdi.inParaInfo.paraFlags = 0;
PeteCreate(win,&pte,0,&pdi);
(*PeteExtra(pte))->emoDesired = true;
(*tocH)->previewPTE = pte;
CleanPII(&pdi);
Zero(r);
if (cntl=GetNewControl(PREVIEW_DIVIDE_CNTL,winWP))
{
if (placard = GetNewControl(PLACARD_CNTL,winWP))
EmbedControl(cntl,placard);
}
}
else if (!win->botMargin && (*tocH)->previewPTE)
{
if (cntl=FindControlByRefCon(win,PREVIEW_DIVIDE_CNTL))
{
if (!GetSuperControl(cntl,&placard))
DisposeControl(placard); // Disposes embedded cntl also
else
DisposeControl(cntl);
}
BoxListFocus(tocH,true);
PeteDispose(win,(*tocH)->previewPTE);
(*tocH)->previewPTE = nil;
}
}
/************************************************************************
* BoxSetPreview - set the preview state for a mailbox
************************************************************************/
void BoxSetPreview(MyWindowPtr win,Boolean openIt)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
short hi = (*tocH)->previewHi;
Rect r = *GetPortBounds(GetWindowPort(winWP),&r);
short totalHi = RectHi(r);
if (!openIt)
{
hi = (!(MainEvent.modifiers&optionKey)&&hi) ? -ABS(hi) : -1;
(*tocH)->previewHi = hi;
hi = win->contR.bottom+GROW_SIZE;
}
else
{
if (!(MainEvent.modifiers&optionKey)&&hi) hi = ABS(hi);
else hi = 1;
(*tocH)->previewHi = hi;
if (hi==1) hi = FontLead*GetRLong(PREVIEW_HI);
hi += totalHi;
{
ControlHandle cntl;
if (cntl=FindControlByRefCon(win,kConConProfRefCon))
ShowMyControl(cntl);
}
}
r.bottom = r.top + hi;
SanitizeSize(win,&r);
SizeWindow(winWP,RectWi(r),RectHi(r),false);
MyWindowDidResize(win,nil);
BoxRefreshPreview(tocH);
TOCSetDirty(tocH,true);
}
/************************************************************************
* BoxRefreshPreview - make the preview go again
************************************************************************/
void BoxRefreshPreview(TOCHandle tocH)
{
(*tocH)->previewID = 0;
(*tocH)->lastSameTicks = 0;
(*tocH)->conConMultiScan = true;
}
/************************************************************************
* BoxRequestFocus - somebody wants to change the focus
************************************************************************/
Boolean BoxRequestFocus(MyWindowPtr win, PETEHandle pte)
{
TOCHandle tocH = Win2TOC(win);
ASSERT(tocH);
if (!tocH) return false;
if (!(*tocH)->previewID) return false;
BoxListFocus(tocH,false); // not on the list of summaries
PeteFocus(win,pte,true); // on the petehandle!
return true;
}
/************************************************************************
* BoxCurAddr - get the address of the first selected message
************************************************************************/
PStr BoxCurAddr(MyWindowPtr win, PStr addr)
{
TOCHandle tocH = Win2TOC(win);
short sumNum = FirstMsgSelected(tocH);
if (sumNum < 0) return nil;
{
MessHandle messH = (*tocH)->sums[sumNum].messH;
Boolean opened = false;
if (messH==nil)
{
TOCHandle realTocH;
short realSumNum;
if (realTocH = GetRealTOC(tocH,sumNum,&realSumNum))
{
EnsureMsgDownloaded(realTocH,realSumNum,false); // FSOIMAP
GetAMessage(realTocH,realSumNum,nil,nil,false);
messH = (*realTocH)->sums[realSumNum].messH;
opened = messH!=nil;
if (!opened) return nil;
}
}
addr = ((*messH)->win)->curAddr((*messH)->win,addr);
if (opened) CloseMyWindow((*messH)->win);
return addr;
}
}
/************************************************************************
* BoxIdle - idle routine for mailboxes
************************************************************************/
void BoxIdle(MyWindowPtr win)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
TOCHandle tocH;
short mNum, startNum;
static long waitTicks;
static long previewTicks = 5;
static long previewMultiTicks = 30;
ASSERT(winWP);
if (!winWP) return;
tocH = (TOCHandle)GetWindowPrivateData(winWP);
ASSERT(tocH);
if (!tocH) return;
if (!(*tocH)->previewPTE)
(*tocH)->previewID = 0;
else if (IsWindowVisible (winWP) && TickCount()-(*tocH)->mouseTicks>previewTicks)
{
previewTicks = GetRLong(PREVIEW_SINGLE_DELAY);
previewMultiTicks = GetRLong(PREVIEW_MULTI_DELAY);
startNum = -1;
for (mNum=(*tocH)->count-1;mNum>=0;mNum--)
{
if ((*tocH)->sums[mNum].selected)
if (startNum!=-1) {startNum = -2 ; break;} // multiple selection same as no selection?
else startNum = mNum;
}
if (startNum!=-2 || TickCount()-(*tocH)->mouseTicks>previewMultiTicks)
{
(*tocH)->mouseTicks = TickCount();
Preview(tocH,startNum);
}
}
// Leave imap mailboxes alone that are in the middle of a resync operation.
if (!((*tocH)->imapTOC && FindNodeByToc(tocH)))
{
if ((*tocH)->updateBoxSizes)
ShowBoxSizes(win);
// Moodwatch one message while we're here
if (IsWindowVisible(winWP) && DiskSpunUp() && AnalDoExisting() && !(*tocH)->analScanned
&& GlobalIdleTicks > 120)
{
Boolean didOne = false;
// visible on down
for (mNum=GetControlValue((*tocH)->win->vBar);mNum<(*tocH)->count;mNum++)
if (!(*tocH)->sums[mNum].score)
{
didOne = true;
OneAnalMess(tocH,mNum);
break;
}
// invisible on up
if (!didOne)
{
for (mNum=GetControlValue((*tocH)->win->vBar)-1;mNum>=0;mNum--)
if (!(*tocH)->sums[mNum].score)
{
didOne = true;
OneAnalMess(tocH,mNum);
break;
}
}
(*tocH)->analScanned = !didOne;
}
}
if ((*tocH)->imapTOC)
UpdateIMAPMailbox(tocH); // see if we need to update IMAP mailbox
UpdatePOPMailboxes(); // attend to any pending POP message deletions
{
ControlHandle cntl;
if ((cntl=FindControlByRefCon(win,kConConProfRefCon)) && IsControlVisible(cntl))
HiliteControl(cntl, (*tocH)->previewID ? 0 : 255);
}
}
/************************************************************************
* BoxDrawerUseless - how much space does the drawer add to each side of the window?
************************************************************************/
Boolean BoxDrawerUseless(MyWindowPtr win, short *left, short *right)
{
TOCHandle tocH = Win2TOC(win);
if ((*tocH)->drawerWin && (*tocH)->drawer)
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
WindowPtr drawWP = GetMyWindowWindowPtr((*tocH)->drawerWin);
if (winWP && drawWP)
{
Rect rDrawStruc;
Rect rWinStruc;
short myLeft, myRight;
GetWindowBounds(winWP,kWindowStructureRgn,&rWinStruc);
GetWindowBounds(drawWP,kWindowStructureRgn,&rDrawStruc);
myLeft = rDrawStruc.right < rWinStruc.right ? RectWi(rDrawStruc) : 0;
myRight = rDrawStruc.left > rWinStruc.left ? RectWi(rDrawStruc) : 0;
if (left) *left = myLeft;
if (right) *right = myRight;
return true;
}
}
// no drawer or couldn't get window pointers
if (left) *left = 0;
if (right) *right = 0;
return false;
}
/************************************************************************
* BoxCountSelected - how many messages are selected
************************************************************************/
short BoxCountSelected(TOCHandle tocH)
{
short count = 0;
short mNum;
for (mNum=(*tocH)->count;mNum--;) if ((*tocH)->sums[mNum].selected) count++;
return count;
}
/************************************************************************
* BoxGrowSize - adjust grow size
************************************************************************/
void BoxGrowSize(MyWindowPtr win,Point *newSize)
{
short nonMsgSumHt = win->topMargin + GROW_SIZE;
short msgSumHt = ((newSize->v-nonMsgSumHt+win->vPitch/2)/win->vPitch)*win->vPitch;
// Calculate new window height
newSize->v = msgSumHt + nonMsgSumHt;
}
/************************************************************************
* BoxSizeBox - return rect for window's size display
************************************************************************/
void BoxSizeBox(MyWindowPtr win,Rect *r,TOCHandle tocH)
{
*r = win->contR;
r->top = r->bottom;
r->bottom = r->top+GROW_SIZE;
r->right = r->left + BoxSizeWidth(tocH);
InsetRect(r,1,1);
}
/************************************************************************
* BoxSizeWidth - return width of window's size display
************************************************************************/
short BoxSizeWidth(TOCHandle tocH)
{
if (tocH && (*tocH)->virtualTOC)
{
static short vBoxWidth;
if (!vBoxWidth)
{
vBoxWidth = GetRLong(SEARCH_HITS_SIZE)*CharWidthInFont('0',SmallSysFontID(),SmallSysFontSize());
if (PrefIsSet(PREF_SHOW_NUM_SELECTED)) vBoxWidth += GetRLong(BOX_SIZE_SELECT_EXTRA);
}
return vBoxWidth;
}
return PrefIsSet(PREF_SHOW_NUM_SELECTED) ? GetRLong(BOX_SIZE_SIZE) + GetRLong(BOX_SIZE_SELECT_EXTRA) : GetRLong(BOX_SIZE_SIZE);
}
/************************************************************************
* BoxGonnaShow - get ready to show a mailbox window
************************************************************************/
OSErr BoxGonnaShow(MyWindowPtr win)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
ControlHandle cntl, placard;
Str255 title;
PushGWorld();
SetPort_(GetWindowPort(winWP));
(*tocH)->needRedo = 0;
win->update = BoxUpdate;
win->scroll = BoxScroll;
win->drag = BoxDragHandler;
win->button = BoxButton;
win->idle = BoxIdle;
win->selection = BoxHasSelection;
win->zoomSize = BoxZoomSize;
win->drawerUseless = BoxDrawerUseless;
win->requestPeteFocus = BoxRequestFocus;
win->curAddr = BoxCurAddr;
win->dontControl = true;
(*tocH)->listFocus = true;
(*tocH)->conConMultiScan = true;
MyWindowDidResize(win,nil);
AnyTOCDirty++;
// Handle AMO (goddam it)
GetWTitle(winWP,title);
if (GetPrefLong(PREF_AMO_AVOIDANCE)==kAMOAvoidInvisible && title[2]==0 && *title>2)
{
--*title;
BMD(title+3,title+2,*title-1);
SetWTitle_(winWP,title);
}
if ((*tocH)->resort) MBResort(tocH);
BoxInitialSelection(tocH);
ScrollIt(win,0,SortedDescending(tocH)?REAL_BIG:-REAL_BIG);
if (!FindControlByRefCon(win,PREVIEW_TOGGLE_CNTL))
{
if (cntl = GetNewControl(PREVIEW_TOGGLE_CNTL,winWP))
if (placard = GetNewControl(PLACARD_CNTL,winWP))
EmbedControl(cntl,placard);
}
if (ETLHasMBoxContextFolder(win))
{
// Set up file view
ControlRef ctlTabs;
Rect rTab;
GrafPtr oldPort;
FileViewHandle hData;
GetPort(&oldPort);
SetPort_(GetMyWindowCGrafPtr(win));
rTab = win->contR; // doesn't really matter what size, will get resized later anyway
ctlTabs = FindControlByRefCon(win,'tabs'); // Do we already have a tab control?
if (!ctlTabs)
// Don't already have tab control. Create one.
ctlTabs = NewControl(winWP,&rTab,"\p",true,FILE_VIEW_TABS,0,0,kControlTabSmallProc,0);
if (ctlTabs)
{
LetsGetSmall(ctlTabs);
EmbedControl(ctlTabs,win->topMarginCntl);
SetControlReference(ctlTabs,'tabs');
if ((*tocH)->fileView) SetControlValue(ctlTabs,2);
if (hData = NuHandleClear(sizeof(FileViewInfo)))
{
short oldResFile = CurResFile(),refN = 0;
short vRefNum;
long dirID;
if (ETLMBoxContextFolder(win, &vRefNum, &dirID) == EMSR_OK)
{
(*hData)->vRefNum = vRefNum;
(*hData)->dirId = dirID;
}
else
{
(*hData)->vRefNum = -1;
(*hData)->dirId = 2;
}
(*tocH)->hFileView = (Handle)hData;
(*tocH)->hasFileView = true;
// Get saved preferences
if ((*tocH)->refN) UseResFile((*tocH)->refN);
else
{
FSSpec resSpec = GetMailboxSpec(tocH,0);
refN = FSpOpenResFile(&resSpec,fsRdPerm);
if (refN == -1) refN = 0;
}
if ((*tocH)->refN || refN)
{
FVPrefsHandle hFVPrefs;
if ((*hData)->expandList.hExpandList = (ExpandListHandle)Get1Resource(kExandListResType,kExandListResID))
DetachResource((Handle)(*hData)->expandList.hExpandList);
if (hFVPrefs = (FVPrefsHandle)Get1Resource(kFVPrefsResType,kFVPrefsResID))
{
(*hData)->prefs = **hFVPrefs;
ReleaseResource((Handle)hFVPrefs);
}
if (refN) CloseResFile(refN);
}
UseResFile(oldResFile);
NewFileView(win,hData);
(*hData)->savePreviewHi = (*tocH)->previewHi;
HitTab(win,ctlTabs,tocH); // set up initial window configuration
}
}
SetPort_(oldPort);
}
else (*tocH)->hasFileView = false;
BoxAddBevelButtons(win);
PopGWorld();
return(noErr);
}
/************************************************************************
* BoxInitialSelection - make an initial selection in a mailbox
************************************************************************/
void BoxInitialSelection(TOCHandle tocH)
{
short fumlub;
switch (GetPrefLong(PREF_MANUAL_BOX_SELECTION))
{
case 2:
if (SortedDescending(tocH)) SelectBoxRange(tocH,0,0,False,0,0);
else SelectBoxRange(tocH,(*tocH)->count-1,(*tocH)->count-1,False,0,0);
break;
case 1:
fumlub = FumLub(tocH);
SelectBoxRange(tocH,fumlub,fumlub,False,0,0);
break;
default:
break; // leave well enough alone...
}
}
/************************************************************************
* BoxZoomSize - How big do we grow a mailbox?
************************************************************************/
void BoxZoomSize(MyWindowPtr win,Rect *zoom)
{
Rect listZoom = *zoom;
short zoomHi = RectHi(*zoom);
short hi;
// compute list box size, assuming no bottom margin
hi = win->botMargin;
win->botMargin = 0;
MaxSizeZoom(win,&listZoom);
win->botMargin = hi;
hi = RectHi(listZoom) + BoxPreviewHeightWish(Win2TOC(win));
hi = MIN(hi,zoomHi); hi = MAX(hi,win->minSize.v);
zoom->right = zoom->left+RectWi(listZoom);
zoom->bottom = zoom->top+hi;
}
/************************************************************************
* BoxHasSelection - is there a selection?
************************************************************************/
Boolean BoxHasSelection(MyWindowPtr win)
{
return (-1!=LastMsgSelected((TOCHandle)GetMyWindowPrivateData(win)));
}
/************************************************************************
* BoxAddBevelButtons - add the bevel buttons
************************************************************************/
void BoxAddBevelButtons(MyWindowPtr win)
{
ControlHandle cntl;
WindowPtr winWP = GetMyWindowWindowPtr (win);
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
short i;
ControlButtonGraphicAlignment align;
ControlButtonTextPlacement placement;
Boolean iconMania = !PrefIsSet(PREF_NO_SUMMARY_ICONS);
for (i=1;i<BoxLinesLimit;i++)
if (!FindControlByRefCon(win,'wide'+i))
{
if (cntl = GetNewControlSmall(BOX_HEAD_CNTL,winWP))
{
SetControlReference(cntl,'wide'+i);
// if sticky sorting on this column, turn on the column header
if (MBGetSort(tocH,i)) SetControlValue(cntl,1);
// if K, right-justify the header
if (i==blSize)
{
align = kControlBevelButtonAlignRight;
placement = kControlBevelButtonPlaceToLeftOfGraphic;
SetBevelTextAlign(cntl,teFlushRight);
}
else
{
SetBevelTextAlign(cntl,teFlushDefault);
align = kControlBevelButtonAlignSysDirection;
placement = kControlBevelButtonPlaceSysDirection;
}
//SetBevelTextOffset(cntl,2);
if (iconMania && i!=blSubject)
{
SetBevelIcon(cntl,i==blMailbox ? MAILBOX_ONLY_ICON : COLUMN_ICON_BASE+i-1,nil,nil,nil);
SetControlData(cntl,0,kControlBevelButtonTextPlaceTag,sizeof(placement),(void*)&placement);
SetControlData(cntl,0,kControlBevelButtonGraphicAlignTag,sizeof(align),(void*)&align);
}
EmbedControl(cntl,win->topMarginCntl);
}
}
if (MBDrawerSupported(win))
{
// Put in drawer switch
if (!FindControlByRefCon(win,kDrawerSwitch))
if (cntl = GetNewControlSmall(BOX_HEAD_CNTL,winWP))
{
SetControlReference(cntl,kDrawerSwitch);
SetBevelIcon(cntl,DRAWER_ICON,nil,nil,nil);
EmbedControl(cntl,win->topMarginCntl);
}
}
// and the size box
if (!(cntl=FindControlByRefCon(win,kBoxSizeRefCon)))
if (cntl=GetNewControlSmall(BOX_SIZE_CNTL,winWP))
{
SetControlReference(cntl,kBoxSizeRefCon);
}
// and the content concentrator preview control
if (HasFeature(featureConCon) && !(cntl=FindControlByRefCon(win,kConConProfRefCon)))
if (cntl=GetNewControlSmall(CONCON_PROF_PREVIEW_POP,winWP))
{
SetControlReference(cntl,kConConProfRefCon);
}
// buttons for workgroup
if (ETLBoxTagWidth(win))
ETLAddBoxButtons(tocH);
}
/************************************************************************
* BoxPlaceBevelButtons - place the bevel buttons
************************************************************************/
void BoxPlaceBevelButtons(MyWindowPtr win)
{
ControlHandle cntl,cntlDrawer;
Rect r,rDrawerCntl;
short i;
short h0 = -GetControlValue(win->hBar)*win->hPitch;
Str255 s;
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
Boolean fileView = (*tocH)->hasFileView && (*tocH)->fileView;
WindowPtr winWP = GetMyWindowWindowPtr(win);
Boolean winVis = IsWindowVisible(winWP);
r = *GetControlBounds(win->topMarginCntl,&r);
r.left = 0;
r.top = r.bottom - (kHeaderButtonHeight);
// First check drawer switch
if (cntlDrawer = FindControlByRefCon(win,kDrawerSwitch))
{
rDrawerCntl = r;
rDrawerCntl.right = WindowWi(winWP);
rDrawerCntl.left = rDrawerCntl.right - 20;
}
for (i=1;i<BoxLinesLimit;i++)
if (cntl = FindControlByRefCon(win,'wide'+i))
{
if (BoxLimits(i,&r.left,&r.right,tocH) && !fileView)
{
OffsetRect(&r,h0,0);
if (cntlDrawer && r.right >= rDrawerCntl.left)
// Don't overlap drawer control
r.right = rDrawerCntl.left;
MySetCntlRect(cntl,&r);
if (RectWi(r)<32)
{
MySetControlTitle(cntl,"");
SetBevelGraphicAlign(cntl,kControlBevelButtonAlignCenter);
}
else
{
GetRString(s,ColumnHeadStrn+i);
// display group subjects by adding a bullet to the subject label
if (i==blSubject && MBIsSticky(tocH) && ((*tocH)->laurence)!=0)
PCatC(s,bulletChar);
MySetControlTitle(cntl,s);
SetBevelGraphicAlign(cntl,kControlBevelButtonAlignSysDirection);
SetBevelGraphicOffset(cntl,2);
SetBevelTextOffset(cntl,2);
}
SetControlVisibility(cntl,true,winVis);
}
else
SetControlVisibility(cntl,false,winVis);
}
if (cntlDrawer)
MySetCntlRect(cntlDrawer,&rDrawerCntl);
// Layout the buttons
if (ETLBoxTagWidth(win))
{
h0 = INSET;
for (i=1;cntl = FindControlByRefCon(win,'plug'+i);i++)
{
MoveMyCntl(cntl,h0,INSET,0,0);
h0 += ControlWi(cntl) + INSET;
}
}
}
/************************************************************************
* BoxSetBevelButtonValues - set bevel button values by sort values
************************************************************************/
void BoxSetBevelButtonValues(MyWindowPtr win)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
short i;
ControlHandle cntl;
for (i=1;i<BoxLinesLimit;i++)
if (cntl=FindControlByRefCon(win,'wide'+i))
SetControlValue(cntl,MBGetSort(tocH,i) ? 1 : 0);
}
/************************************************************************
* BoxButton - handle a hit in the box buttons
************************************************************************/
void BoxButton(MyWindowPtr win,ControlHandle buttonHandle,long modifiers,short part)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
uLong contrlRfCon = GetControlReference(buttonHandle);
uLong i = contrlRfCon - 'wide';
uLong iPrime = contrlRfCon - 'plug';
// audit clicks on only these controls ...
if ((i<BoxLinesLimit) || (contrlRfCon==kBoxSizeRefCon) || (contrlRfCon==PREVIEW_TOGGLE_CNTL))
AuditHit((modifiers&shiftKey)!=0, (modifiers&controlKey)!=0, (modifiers&optionKey)!=0, (modifiers&cmdKey)!=0, false, GetWindowKind(GetMyWindowWindowPtr(win)), contrlRfCon, mouseDown);
BoxListFocus(tocH,true);
if (contrlRfCon==kBoxSizeRefCon)
SizeBoxClick(win,modifiers);
else if (contrlRfCon==kConConProfRefCon)
;
else if (contrlRfCon==PREVIEW_TOGGLE_CNTL)
BoxSetPreview(win,!GetControlValue(buttonHandle));
else if (contrlRfCon==kDrawerSwitch)
MBDrawerToggle(win);
else if (contrlRfCon=='tabs')
HitTab(win,buttonHandle,tocH);
else if (i>0 && i<BoxLinesLimit)
MBSortHit(tocH,i,0!=(modifiers&optionKey),0!=(modifiers&shiftKey));
else if (iPrime > 0 && iPrime < 10 /* arbitrary hack alert */)
ETLButtonHit(win,iPrime);
else if ((*tocH)->hasFileView && (*tocH)->fileView)
FileViewButton(win,buttonHandle,modifiers,part);
}
/**********************************************************************
* RedoAllTOCs - fix all toc's to use new date format
**********************************************************************/
void RedoAllTOCs(void)
{
WindowPtr theWindow;
TOCHandle tocH;
for (theWindow = GetWindowList(); theWindow; theWindow = GetNextWindow (theWindow))
if (IsKnownWindowMyWindow(theWindow) && (GetWindowKind(theWindow)==CBOX_WIN || GetWindowKind(theWindow)==MBOX_WIN))
{
tocH = (TOCHandle)GetWindowPrivateData(theWindow);
(*tocH)->needRedo = 0;
InvalTocBox(tocH,-1,blDate);
}
}
/**********************************************************************
* BoxScroll - do (part of) the scrolling for a mailbox. Scrolls the header part h-wise
**********************************************************************/
Boolean BoxScroll(MyWindowPtr win,short h,short v)
{
if (h)
{
BoxPlaceBevelButtons(win);
}
return(True);
}
/************************************************************************
* PriorityString - turn a priority into a string
************************************************************************/
UPtr PriorityString(UPtr string,Byte priority)
{
/*
* normalize
*/
priority=Prior2Display(priority);
GetRString(string,PRIOR_STRN+5+priority);
return(string);
}
/**********************************************************************
* SetPriority - set a message's priority,
* handle virtual TOCs, too
**********************************************************************/
void SetPriority(TOCHandle tocH,short sumNum,short priority)
{
TOCHandle realTOC;
short realSum;
SetPriorityLo(tocH,sumNum,priority);
realTOC = GetRealTOC(tocH,sumNum,&realSum);
if (realTOC && realTOC != tocH)
{
// do real mailbox also if working in virtual mailbox
SetPriorityLo(realTOC,realSum,priority);
tocH = realTOC;
sumNum = realSum;
}
SearchUpdateSum(tocH, sumNum, tocH, (*tocH)->sums[sumNum].serialNum, false, false); // Notify search window
}
/**********************************************************************
* SetTAEScore - set a message's score,
* handle virtual TOCs, too
**********************************************************************/
void SetTAEScore(TOCHandle tocH,short sumNum,short score)
{
TOCHandle realTOC;
short realSum;
if ((*tocH)->sums[sumNum].score != score)
{
SetTAEScoreLo(tocH,sumNum,score);
realTOC = GetRealTOC(tocH,sumNum,&realSum);
if (realTOC && realTOC != tocH)
{
// do real mailbox also if working in virtual mailbox
SetTAEScoreLo(realTOC,realSum,score);
tocH = realTOC;
sumNum = realSum;
}
SearchUpdateSum(tocH, sumNum, tocH, (*tocH)->sums[sumNum].serialNum, false, false); // Notify search window
}
}
/************************************************************************
* SetPriorityLo - set a message's priority
************************************************************************/
void SetPriorityLo(TOCHandle tocH,short sumNum,short priority)
{
MessHandle messH;
ControlHandle cntl;
short dp = Prior2Display(priority);
MenuHandle mh;
if (dp!=Prior2Display((*tocH)->sums[sumNum].priority))
{
/* invalidate priority box in the toc */
InvalTocBox(tocH,sumNum,blPrior);
// make sure the menus know
if (mh=GetMHandle(PRIOR_HIER_MENU))
{
CheckNone(mh);
SetItemMark(mh,dp,diamondMark);
}
/* set the priority display in the message */
if ((messH=(*tocH)->sums[sumNum].messH) && IsWindowVisible(GetMyWindowWindowPtr((*messH)->win)))
{
if (cntl = FindControlByRefCon((*messH)->win,PRIOR_HIER_MENU))
{
SetBevelIcon(cntl,dp==3 ? PRIORITY_HOLLOW_ICON : PRIOR_SICN_BASE+dp-1,0,0,nil);
SetBevelMenuValue(cntl,dp);
Draw1Control(cntl);
}
if ((*messH)->hStationerySpec) (*messH)->win->isDirty = True;
}
}
(*tocH)->sums[sumNum].priority = priority;
TOCSetDirty(tocH,true);
}
/************************************************************************
* SetTAEScoreLo - set a message's score
************************************************************************/
void SetTAEScoreLo(TOCHandle tocH,short sumNum,short score)
{
if (score != (*tocH)->sums[sumNum].score)
{
if (!(*tocH)->noInvalBox) InvalTocBox(tocH,sumNum,blAnal);
(*tocH)->sums[sumNum].score = score;
TOCSetDirty(tocH,true);
}
}
/************************************************************************
* InvalTocBox - invalidate one area of a mailbox window
************************************************************************/
void InvalTocBox(TOCHandle tocH,short sumNum,short box)
{
InvalTocBoxLo(tocH,sumNum,box);
if (sumNum >= 0)
SearchInvalTocBox(tocH,sumNum,box);
}
/************************************************************************
* InvalTocBoxLo - invalidate one area of a mailbox window
************************************************************************/
void InvalTocBoxLo(TOCHandle tocH,short sumNum,short box)
{
WindowPtr tocWinWP = GetMyWindowWindowPtr ((*tocH)->win);
Rect r;
if ((*tocH)->win && IsWindowVisible (tocWinWP))
{
PushGWorld();
SetPort_(GetWindowPort(tocWinWP));
if (sumNum==-1)
{
r.top = 0;
r.bottom = (*tocH)->win->topMargin;
}
else if (sumNum==-2)
{
r.top = (*tocH)->win->topMargin;
r.bottom = (*tocH)->win->contR.bottom;
}
else
{
r.top = (*tocH)->win->topMargin + (sumNum-GetControlValue((*tocH)->win->vBar))*(*tocH)->win->vPitch;
r.bottom = r.top + (*tocH)->win->vPitch;
}
BoxLimits(box,&r.left,&r.right,tocH);
OffsetRect(&r,-GetControlValue((*tocH)->win->hBar)*(*tocH)->win->hPitch,0);
InvalWindowRect(tocWinWP,&r);
if (MBGetSort(tocH,box)) (*tocH)->resort = MAX((*tocH)->resort,kNoSlowResort);
PopGWorld();
}
}
/**********************************************************************
* CopySummary - put the contents of a summary onto a text handle
**********************************************************************/
OSErr CopySummary(TOCHandle tocH, short sumNum, Handle into)
{
Str255 summary;
Str255 sMBName;
Str63 states;
Str63 priority;
Str63 labelString;
Str31 dateStr;
MSumType sum = (*tocH)->sums[sumNum];
RGBColor color;
GetRString(states,STATE_LABELS);
if (GetSumColor(tocH,sumNum)) MyGetLabel(GetSumColor(tocH,sumNum),&color,&labelString);
*labelString = 0;
GetRString(priority,PRIOR_STRN+Prior2Display(sum.priority));
if ((*tocH)->virtualTOC)
{
// Include mailbox name for virtual mailboxes
GetMailboxName(tocH,sumNum,sMBName);
ComposeRString(summary,SUM_SEARCH_COPY_FMT,
states[sum.state],
priority,
(sum.flags & FLAG_HAS_ATT) ? YesStr : "",
labelString,
sum.from,
ComputeLocalDate(&sum,dateStr),
sum.length/(1 K),
sMBName,
sum.subj);
}
else
{
ComposeRString(summary,SUM_COPY_FMT,
states[sum.state],
priority,
(sum.flags & FLAG_HAS_ATT) ? YesStr : "",
labelString,
sum.from,
ComputeLocalDate(&sum,dateStr),
sum.length/(1 K),
sum.subj);
}
return(PtrPlusHand_(summary+1,into,*summary));
}
/**********************************************************************
* CopySelectedSummaries - copy selected summaries from a mailbox
**********************************************************************/
OSErr CopySelectedSummaries(TOCHandle tocH)
{
Handle copyText = NuHandle(0);
OSErr err = noErr;
short i;
if (!copyText) return(WarnUser(COPY_FAILED,MemError()));
for (i=0;i<(*tocH)->count;i++)
if ((*tocH)->sums[i].selected && (err=CopySummary(tocH,i,copyText))) break;
if (!err)
{
ScrapRef scrap;
ClearCurrentScrap();
GetCurrentScrap(&scrap);
err = PutScrapFlavor(scrap,kScrapFlavorTypeText,kScrapFlavorMaskNone,GetHandleSize(copyText),LDRef(copyText));
}
ZapHandle(copyText);
if (err) WarnUser(COPY_FAILED,err);
return(err);
}
/**********************************************************************
* BoxLine2Item - turn a line # into a sort menu item
**********************************************************************/
short BoxLine2Item(short line)
{
short item;
switch (line)
{
case blStat: item = SORT_STATUS_ITEM; break;
case blJunk: item = SORT_JUNK_ITEM; break;
case blPrior: item = SORT_PRIORITY_ITEM; break;
case blFrom: item = SORT_SENDER_ITEM; break;
case blDate: item = SORT_TIME_ITEM; break;
case blMailbox: item = SORT_MAILBOX_ITEM; break;
case blLabel: item = SORT_LABEL_ITEM; break;
case blSize: item = SORT_SIZE_ITEM; break;
case blAttach: item = SORT_ATTACHMENTS_ITEM; break;
case blSubject: item = SORT_SUBJECT_ITEM; break;
case blAnal: item = SORT_MOOD_ITEM; break;
default: item = 0; break;
}
return(item);
}
/**********************************************************************
* BoxItem2Line - turn a sort menu item into a line number
**********************************************************************/
short BoxItem2Line(short item)
{
short line;
switch (item)
{
case SORT_STATUS_ITEM: line = blStat; break;
case SORT_JUNK_ITEM: line = blJunk; break;
case SORT_PRIORITY_ITEM: line = blPrior; break;
case SORT_SENDER_ITEM: line = blFrom; break;
case SORT_TIME_ITEM: line = blDate; break;
case SORT_MAILBOX_ITEM: line = blMailbox; break;
case SORT_LABEL_ITEM: line = blLabel; break;
case SORT_SIZE_ITEM: line = blSize; break;
case SORT_ATTACHMENTS_ITEM: line = blAttach; break;
case SORT_SUBJECT_ITEM: line = blSubject; break;
case SORT_MOOD_ITEM: line = blAnal; break;
default: line = 0; break;
}
return(line);
}
/************************************************************************
* BoxDragDividers - see if the user is trying to drag dividers, and help
* him if he is.
************************************************************************/
Boolean BoxDragDividers(Point pt, MyWindowPtr win)
{
short line;
long ticks = TickCount()+GetDblTime();
Point mouse;
short slop = GetRLong(DOUBLE_TOLERANCE);
RgnHandle greyRgn;
Rect r,bounds;
short h0;
short dh;
long dvdh;
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
PushGWorld();
if (pt.v > win->topMargin && BoxFindDivider(pt,&line))
{
while (TickCount()<ticks)
{
if (!StillDown()) {PopGWorld();return(False);}
GetMouse(&mouse);
if (ABS(mouse.v-pt.v)>slop) {PopGWorld();return(False);}
if (ABS(mouse.h-pt.h)>slop) break;
}
if (greyRgn = NewRgn())
{
GetMouse(&mouse);
r = bounds = win->contR;
r.left = pt.h;
r.right = pt.h+1;
r.top = win->topMargin - (GROW_SIZE+4);
RectRgn(greyRgn,&r);
h0 = -win->hPitch*GetControlValue(win->hBar);
BoxLimits(line,&bounds.left,(short*)&dvdh,tocH);
bounds.left += h0;
bounds.top = 0;
dvdh = DragGrayRgn(greyRgn,mouse,&bounds,&bounds,hAxisOnly,nil);
dh = dvdh&0xffff;
/*
* I don't understand why I have to do this; I guess I don't understand
* DragGrayRgn.
*/
GetMouse(&mouse);
if (mouse.h<=pt.h && dh>0) dh = 0;
if (pt.h+dh<bounds.left) dh = 0;
if (dh&0x7fff && (dh = RoundDiv(dh,FontWidth)))
{
WindowPtr theWindow;
(*BoxWidths)[line-1] = MAX((*BoxWidths)[line-1]+dh,0);
SaveBoxLines();
InfiniteClip(GetMyWindowCGrafPtr(win));
for (theWindow = GetWindowList(); theWindow; theWindow = GetNextWindow (theWindow))
if (IsKnownWindowMyWindow(theWindow) && (GetWindowKind(theWindow)==CBOX_WIN || GetWindowKind(theWindow)==MBOX_WIN))
{
SetPort_(GetWindowPort(theWindow));
(*tocH)->needRedo = 0;
InvalContent(GetWindowMyWindowPtr(theWindow));
BoxPlaceBevelButtons(GetWindowMyWindowPtr(theWindow));
}
}
}
PopGWorld();
return(True);
}
PopGWorld();
return(False);
}
/************************************************************************
* BoxFindDivider - is the current point over a dividing line?
* Returns true if it is, and puts the line number in which.
* puts the line to the right of the mouse in which if it isn't
************************************************************************/
Boolean BoxFindDivider(Point pt,short *which)
{
short i;
WindowPtr winWP = FrontWindow_();
MyWindowPtr win = GetWindowMyWindowPtr (winWP);
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
short h0 = -win->hPitch*GetControlValue(win->hBar);
short l,r;
if (pt.v>win->contR.bottom) return false;
if (pt.h>win->contR.right) return false;
for (i=1;i<BoxLinesLimit;i++)
{
BoxLimits(i,&l,&r,tocH);
r += h0;
l += h0;
if (r-2<=pt.h && pt.h<=r) {*which=i;return(True);}
if (l < pt.h && pt.h < r) {*which=i;return(False);}
}
*which = BoxLinesLimit;
return(False);
}
/************************************************************************
* BoxCursor - change the cursor appropriately
************************************************************************/
void BoxCursor(Point mouse)
{
WindowPtr winWP = FrontWindow_();
MyWindowPtr win = GetWindowMyWindowPtr (winWP);
short h0 = -win->hPitch*GetControlValue(win->hBar);
short which,mNum;
Rect r = win->contR;
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
r.top = 0;
if (!PtInRect(mouse,&r))
SetMyCursor(arrowCursor);
else
{
mNum = (mouse.v-win->topMargin)/win->vPitch + GetControlValue(win->vBar);
if (mouse.v>win->topMargin && BoxFindDivider(mouse,&which))
{
SetMyCursor(DIVIDER_CURS);
}
else if (mouse.v<win->topMargin)
SetMyCursor(arrowCursor);
else
{
#ifdef TWO
if (!(MainEvent.modifiers&(shiftKey|optionKey|cmdKey)) &&
(which<=blAttach || which==blLabel || which==blServer || which==blMailbox))
{
mNum = (mouse.v-win->topMargin)/win->vPitch + GetControlValue(win->vBar);
if (mNum<(*tocH)->count && (*tocH)->sums[mNum].selected)
SetMyCursor(MENU_CURS);
else
SetMyCursor(plusCursor);
}
else
#endif
SetMyCursor(plusCursor);
}
}
}
#pragma segment BoxMain
/************************************************************************
* RedoTOC - figure out how wide a toc is
************************************************************************/
Boolean RedoTOC(TOCHandle tocH)
{
short subMax;
#ifdef NEVER
short subStart;
short subWidth;
short sumNum;
#endif
Boolean needResort;
Rect r;
MyWindowPtr tocWin;
WindowPtr tocWinWP;
short maxValid = MIN((*tocH)->needRedo,(*tocH)->maxValid);
Boolean result = false;
FSSpec spec;
long top;
short col;
short colStart, colEnd;
short rightCol;
spec = GetMailboxSpec(tocH,-1);
if (InAThread() || (*tocH)->which==IN_TEMP || (*tocH)->which==OUT_TEMP || IsDelivery(&spec))
{
(*tocH)->needRedo = (*tocH)->maxValid = (*tocH)->count;
return false;
}
UL(tocH); // just in case...
// if (maxValid<(*tocH)->count && PrefIsSet(PREF_DELDUP)) TOCDelDup(tocH);
if (!MBIsSticky(tocH)) needResort = (*tocH)->resort = kDontResort;
else
needResort = (*tocH)->resort>kNoSlowResort || (*tocH)->resort&&!PrefIsSet(PREF_SLOW_RESORT);
tocWin = (*tocH)->win;
tocWinWP = GetMyWindowWindowPtr (tocWin);
if (maxValid < (*tocH)->count)
{
PushGWorld();
SetPort_(GetMyWindowCGrafPtr(tocWin));
#ifdef NEVER
if (RunType!=Production)
{
Dprintf("\pRedoing <20>%p<> thePort %x tocWin %x;wh %x;g",spec.name,qd.thePort,(*tocH)->win,*tocH);
}
#endif
result = true; // we did something
if (maxValid<0) maxValid = 0;
#ifdef NEVER
/*
* measure all the subject lines
*/
BoxLimits(blSubject,&subStart,&subMax,tocH);
subMax = 0;
if (FontIsFixed)
{
for (sumNum=maxValid;sumNum<(*tocH)->count;sumNum++)
subMax = MAX(subMax,*(*tocH)->sums[sumNum].subj);
subMax *= tocWin->hPitch;
}
else
{
LDRef(tocH);
for (sumNum=maxValid;sumNum<(*tocH)->count;sumNum++)
{
subWidth = StringWidth((*tocH)->sums[sumNum].subj);
subMax = MAX(subMax,subWidth);
}
UL(tocH);
}
#endif
/*
* find the rightmost column
*/
rightCol = 0;
for (col=1;col<BoxLinesLimit;col++)
if (BoxColumnShowing(col,tocH))
{
BoxLimits(col,&colStart,&colEnd,tocH);
rightCol = MAX(rightCol,colEnd);
}
subMax = RoundDiv(rightCol,tocWin->hPitch);
/*
* install new maximums; take the old max into account only if we're
* doing a partial
*/
if ((*tocH)->needRedo) subMax = MAX(subMax,tocWin->hMax);
(*tocH)->needRedo = (*tocH)->count; /* don't need to do this again */
/*
* invalidate all the new ones
*/
r = tocWin->contR;
top = tocWin->topMargin + tocWin->vPitch * (maxValid-GetControlValue(tocWin->vBar));
if (top < r.bottom)
{
r.top = MAX(top,tocWin->contR.top);
InvalWindowRect(GetMyWindowWindowPtr(tocWin),&r);
}
(*tocH)->maxValid = (*tocH)->count;
if (!needResort) UpdateMyWindow(tocWinWP);
MyWindowMaxes(tocWin,subMax,(*tocH)->count);
}
if (FrontWindow_()!=tocWinWP && tocWin->isActive)
{
result = true;
ActivateMyWindow(tocWinWP,False);
}
if (needResort && tocWin && IsWindowVisible (tocWinWP))
{
if ((*tocH)->resort == kResortWhenever)
{
uLong elapsedTicks = TickCount() - (*tocH)->lastSortTicks;
if (elapsedTicks < GetRLong(MIN_MB_SORT_TICKS))
needResort = false; // Let's not resort so soon
}
if (needResort)
{
MBResort(tocH);
result = true;
}
}
if (MyWinHasSelection((*tocH)->win))
(*tocH)->win->hasSelection = true;
else
(*tocH)->win->hasSelection = false;
if (result)
{
ShowBoxSizes((*tocH)->win);
PopGWorld();
}
return(result);
}
/**********************************************************************
* MBIsSticky - is a mailbox set for sticky sort
**********************************************************************/
Boolean MBIsSticky(TOCHandle tocH)
{
short i;
short max=0;
for (i=1;i<=NSORT;i++) if (MBGetSort(tocH,i)) max = MBGetSort(tocH,i);
return(max>0);
}
#pragma segment Balloon
/************************************************************************
* BoxHelp - help for mailboxes
************************************************************************/
void BoxHelp(MyWindowPtr win, Point mouse)
{
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
Rect r;
Rect tallContR = win->contR;
short hnum=0;
short column;
short hoff = win->hPitch*GetControlValue(win->hBar);
ControlHandle cntl;
short factor = BoxLinesLimit-1;
SetRect(&r,0,0,REAL_BIG/2,REAL_BIG/2);
tallContR.top = win->topMargin - (GROW_SIZE+4);
if (PtInRect(mouse,&tallContR))
{
if (BoxFindDivider(mouse,&column))
{
BoxLimits(column,&r.left,&r.right,tocH);
r.right++; r.left = r.right-3;
OffsetRect(&r,-hoff,0);
SectRect(&r,&tallContR,&r);
MyBalloon(&r,70,0,MBOX_HELP_STRN+column,0,nil);
}
else
{
BoxLimits(column,&r.left,&r.right,tocH);
OffsetRect(&r,-hoff,0);
r.right = MIN(r.right,win->contR.right);
if (mouse.v<win->topMargin)
{
Boolean s = MBGetSort(tocH,column);
r.bottom = win->topMargin;
r.top = r.bottom - (GROW_SIZE+4);
MyBalloon(&r,70,0,MBOX_HELP_STRN+(s?3:2)*factor+column,0,nil);
}
else
{
r.top = win->topMargin; r.bottom = win->contR.bottom;
if (column==blServer)
{
if ((*tocH)->imapTOC)
MyBalloon(&r,70,0,0,IMAP_BOX_SERVER_COLUMN_HELP_PICT,nil);
else
MyBalloon(&r,70,0,0,133,nil);
}
else if (column>2)
MyBalloon(&r,70,0,MBOX_HELP_STRN+factor+column,0,nil);
else if (column==blPrior)
MyBalloon(&r,70,0,0,PRIOR_HELP_PICT,nil);
else if (column==blStat)
MyBalloon(&r,70,0,0,STATUS_HELP_PICT,nil);
}
}
}
else if (cntl=MyFindControl(mouse,win))
{
short n = 4*factor+1;
switch(GetControlReference(cntl))
{
case PREVIEW_DIVIDE_CNTL: n+=2; break;
case PREVIEW_TOGGLE_CNTL: n++; break;
case kBoxSizeRefCon:
if ((*tocH)->imapTOC)
{
r = *GetControlBounds(cntl,&r);
MyBalloon(&r,70,0,PrefIsSet(PREF_SHOW_NUM_SELECTED)?IMAP_COMPACT_SHOW_NUM_HELP:IMAP_COMPACT_HELP,0,nil);
n = 0;
}
else if (PrefIsSet(PREF_SHOW_NUM_SELECTED))
n += 3;
break;
default: n = 0;
}
if (n)
{
r = *GetControlBounds(cntl,&r);
MyBalloon(&r,70,0,MBOX_HELP_STRN+n,0,nil);
}
}
}
#pragma segment BoxSort
/**********************************************************************
* BoxSelectSame - select all messages with the same <something>
**********************************************************************/
void BoxSelectSame(TOCHandle tocH, short item,short clickedSum)
{
short selected, candidate;
int (*compare)();
short last;
Boolean need;
/*
* phase 1 - figure out the comparator
*/
switch(item)
{
case SORT_STATUS_ITEM:
compare = SumStatCompare;
break;
case SORT_JUNK_ITEM:
compare = SumJunkCompare;
break;
case SORT_PRIORITY_ITEM:
compare = SumPriorCompare;
break;
case SORT_SUBJECT_ITEM:
compare = SumSubjCompare;
break;
case SORT_SENDER_ITEM:
compare = SumFromCompare;
break;
case SORT_TIME_ITEM:
compare = SumTimeFuzzCompare;
break;
case SORT_MAILBOX_ITEM:
compare = SumMailboxCompare;
break;
case SORT_SIZE_ITEM:
compare = SumSizeKCompare;
break;
case SORT_ATTACHMENTS_ITEM:
compare = SumAttCompare;
break;
case SORT_LABEL_ITEM:
compare = SumLabelCompare;
break;
case SORT_MOOD_ITEM:
compare = SumAnalCompare;
break;
default: return;
}
LDRef(tocH);
gSortTOC = tocH;
/*
* phase 2 - figure out which ones we need to select
*/
for (selected=0;selected<(*tocH)->count;selected++)
{
if ((*tocH)->sums[selected].selected)
{
(*tocH)->sums[selected].spareShort2 = 0; /* for compare's benefit */
for (candidate=0;candidate<(*tocH)->count;candidate++)
if (candidate!=selected && !((*tocH)->sums[candidate].opts&OPT_WILL_SEL))
{
(*tocH)->sums[candidate].spareShort2 = 0;
if (!(*compare)(&(*tocH)->sums[selected],&(*tocH)->sums[candidate]))
(*tocH)->sums[candidate].opts |= OPT_WILL_SEL;
}
}
}
UL(tocH);
/*
* phase 3 - select them
*/
for (candidate=0;candidate<(*tocH)->count;candidate++)
if ((*tocH)->sums[candidate].opts&OPT_WILL_SEL)
{
if (!(*tocH)->sums[candidate].selected)
SelectBoxRange(tocH,candidate,candidate,True,-1,-1);
(*tocH)->sums[candidate].opts &= ~OPT_WILL_SEL;
}
/*
* phase 4 - sort them
*/
last = -1;
need = False;
for (candidate=0;candidate<(*tocH)->count;candidate++)
{
if ((*tocH)->sums[candidate].selected)
{
if (last!=-1 && last != candidate-1)
{
need = True;
break;
}
last = candidate;
}
}
if (need)
{
for (candidate=0;candidate<(*tocH)->count;candidate++)
{
if ((*tocH)->sums[candidate].selected) (*tocH)->sums[candidate].spareShort = 1;
else (*tocH)->sums[candidate].spareShort = (candidate<clickedSum) ? 0 : 2;
}
SortTOC(tocH,False,SumSelectCompare);
InvalContent((*tocH)->win);
}
}
/**********************************************************************
* MBSortHit - handle hits on the box headers
**********************************************************************/
void MBSortHit(TOCHandle tocH, short index, Boolean reverse, Boolean extend)
{
Byte sort = MBGetSort(tocH,index);
short i;
short sortOrder;
ControlHandle cntl;
if (!extend)
for (i=1;i<=NSORT;i++) MBRemoveSort(tocH,i);
if (!sort)
{
sort = reverse ? SORT_DESCEND : SORT_ASCEND;
MBAddSort(tocH,index,sort);
}
else if (reverse)
{
sortOrder = sort&0x3;
sortOrder = (sortOrder==SORT_ASCEND) ? SORT_DESCEND : SORT_ASCEND;
MBGetSort(tocH,index) = (sort & ~0x3) | sortOrder;
(*tocH)->resort = kResortNow;
if (cntl=FindControlByRefCon((*tocH)->win,'wide'+index))
SetControlValue(cntl,1);
}
else if (extend)
{
MBRemoveSort(tocH,index);
}
}
/**********************************************************************
* MBRemoveSort - remove a sort
**********************************************************************/
void MBRemoveSort(TOCHandle tocH,short index)
{
Byte oldSort = MBGetSort(tocH,index);
Boolean needResort=False;
ControlHandle cntl;
if (MBGetSort(tocH,index))
{
MBGetSort(tocH,index) = 0;
if (UseListColors) InvalTocBox(tocH,-2,index);
if (cntl=FindControlByRefCon((*tocH)->win,'wide'+index))
SetControlValue(cntl,0);
for (index=1;index<=NSORT;index++)
if (MBGetSort(tocH,index)>oldSort)
{
MBGetSort(tocH,index) -= 4;
needResort = 2;
}
(*tocH)->resort = kResortNow;
TOCSetDirty(tocH,true);
}
}
/**********************************************************************
* MBAddSort - add a sort
**********************************************************************/
void MBAddSort(TOCHandle tocH,short index,Byte sortOrder)
{
short i;
Byte max=0;
ControlHandle cntl;
for (i=1;i<=NSORT;i++)
if (MBGetSort(tocH,i)>max) max = MBGetSort(tocH,i);
max = (((max>>2)+1)<<2) + sortOrder;
MBGetSort(tocH,index) = max;
(*tocH)->resort = kResortNow;
TOCSetDirty(tocH,true);
if (cntl=FindControlByRefCon((*tocH)->win,'wide'+index))
SetControlValue(cntl,1);
}
/**********************************************************************
* MBResort - resort a mailbox
**********************************************************************/
int (*MBCompareTable[BoxLinesLimit+2])();
void MBResort(TOCHandle tocH)
{
Byte max=0;
short i;
short n;
Boolean reverse;
short sub=1;
Boolean group = ((*tocH)->laurence)!=0;
// don't sort any IMAP mailboxes that are in a senstive stage of resynchronization
if ((*tocH)->imapTOC)
{
MailboxNodeHandle mBox = TOCToMbox(tocH);
if (mBox && *mBox)
{
if (DoesIMAPMailboxNeed(mBox, kNeedsSortLock))
{
(*tocH)->resort = kResortNow;
return;
}
}
}
if (group)
{
int (*compare)();
//sort by subject
GroupSubjThreshTime = GetRLong(GROUP_SUBJ_MAX_TIME) * 3600*24;
compare = GroupSubjThreshTime ? SumSubjTimedCompare : SumSubjCompare;
MBCompareTable[0] = SumSubjCompare;
// if using a time threshhold, sort secondarily by date
MBCompareTable[1] = GroupSubjThreshTime ? SumTimeCompare : nil;
MBCompareTable[2] = nil;
SortTOC(tocH,False,MBResortCompare);
// give identical id's to identical subjects
(*tocH)->sums[0].subjId = sub;
LDRef(tocH);
for (i=1;i<(*tocH)->count;i++)
{
if (compare((*tocH)->sums+i,(*tocH)->sums+i-1)) sub++;
(*tocH)->sums[i].subjId = sub;
}
UL(tocH);
}
for (i=1;i<=NSORT;i++) if (MBGetSort(tocH,i)>max) max = MBGetSort(tocH,i);
reverse = SORT_DESCEND==(max&3);
max >>= 2;
max = MIN(max,BoxLinesLimit);
if (MBGetSort(tocH,blMailbox) && !(tocH && (*tocH)->virtualTOC))
{
// This mailbox was sorted by subject with an older version of Eudora
MBGetSort(tocH,blSubject) = MBGetSort(tocH,blMailbox);
MBGetSort(tocH,blMailbox) = 0;
}
if (max)
{
for (n=1;n<=max;n++)
for (i=1;i<=NSORT;i++)
if (((*tocH)->sorts[i-1]>>2)==n)
{
MBCompareTable[n-1] = FindMBSort(BoxLine2Item(i),(MBGetSort(tocH,i)&3)==SORT_DESCEND);
(*tocH)->lastSort = BoxLine2Item(i);
break;
}
MBCompareTable[n-1] = nil;
SortTOC(tocH,reverse,MBResortCompare);
TOCSetDirty(tocH,true);
}
(*tocH)->resort = kDontResort;
if (group)
{
// zero the spare short
for (i=(*tocH)->count-1;i>=0;i--) (*tocH)->sums[i].spareShort = 0;
// for each equal subject, put the index of the highest subj
// in the spare short.
for (i=(*tocH)->count-1;i>=0;i--)
{
if (!(*tocH)->sums[(*tocH)->sums[i].subjId-1].spareShort)
{
// we haven't seen this one yet; assign id
(*tocH)->sums[(*tocH)->sums[i].subjId-1].spareShort = sub;
(*tocH)->sums[i].subjId = sub;
sub--;
}
else //we have seen it; copy
(*tocH)->sums[i].subjId = (*tocH)->sums[(*tocH)->sums[i].subjId-1].spareShort;
}
// and now, one final sort
MBCompareTable[0] = SumSubjIdCompare;
MBCompareTable[1] = nil;
SortTOC(tocH,False,MBResortCompare);
}
if ((*tocH)->win)
{
InvalContent((*tocH)->win);
BoxCenterSelection((*tocH)->win);
}
(*tocH)->lastSortTicks = TickCount();
}
/**********************************************************************
* MBResortCompare - resort a mailbox with multiple conditions
**********************************************************************/
int MBResortCompare(MSumPtr s1, MSumPtr s2)
{
short i;
long res = 0;
// Check for sentinel first
if (s1->length==kLargeUniqueValue) return 1;
if (s2->length==kLargeUniqueValue) return -1;
for (i=0;MBCompareTable[i] && !res;i++)
res = (*MBCompareTable[i])(s1,s2);
return (res ? res : s1->spareShort2-s2->spareShort2);
}
/**********************************************************************
* FindMBSort - find the right sort function
**********************************************************************/
int (*FindMBSort(short item,Boolean reverse))()
{
switch(item)
{
case SORT_STATUS_ITEM:
return(reverse?RevSumStatCompare:SumStatCompare);
break;
case SORT_JUNK_ITEM:
return(reverse?RevSumJunkCompare:SumJunkCompare);
break;
case SORT_PRIORITY_ITEM:
return(reverse?RevSumPriorCompare:SumPriorCompare);
break;
case SORT_SUBJECT_ITEM:
return(reverse?RevSumSubjCompare:SumSubjCompare);
break;
case SORT_SENDER_ITEM:
return(reverse?RevSumFromCompare:SumFromCompare);
break;
case SORT_TIME_ITEM:
if (MainEvent.modifiers&controlKey)
return(reverse?RevSumOffsetCompare:SumOffsetCompare);
else
return(reverse?RevSumTimeCompare:SumTimeCompare);
break;
case SORT_MAILBOX_ITEM:
return(reverse?RevSumMailboxCompare:SumMailboxCompare);
break;
case SORT_SIZE_ITEM:
return(reverse?RevSumSizeCompare:SumSizeCompare);
break;
case SORT_ATTACHMENTS_ITEM:
return(reverse?RevSumAttCompare:SumAttCompare);
break;
case SORT_LABEL_ITEM:
return(reverse?RevSumLabelCompare:SumLabelCompare);
case SORT_MOOD_ITEM:
return(reverse?RevSumAnalCompare:SumAnalCompare);
break;
}
return(nil);
}
/************************************************************************
* SwapSum
************************************************************************/
void SwapSum(MSumPtr sum1, MSumPtr sum2)
{
MSumType tempSum;
tempSum = *sum1;
*sum1 = *sum2;
*sum2 = tempSum;
}
/************************************************************************
* SumTimeCompare - compare the arrival times of two sums
************************************************************************/
int SumTimeCompare(MSumPtr sum1, MSumPtr sum2)
{
long res = (unsigned)sum1->seconds > (unsigned)sum2->seconds ? 1 :
(sum1->seconds==sum2->seconds ? 0 : -1);
return(res);
}
/************************************************************************
* SumMailboxCompare - compare the mailboxes of two (virtual mailbox) sums
************************************************************************/
int SumMailboxCompare(MSumPtr sum1, MSumPtr sum2)
{
FSSpecHandle specList = (*gSortTOC)->mailbox.virtualMB.specList;
Str63 s1,s2;
PSCopy(s1,(*specList)[sum1->u.virtualMess.virtualMBIdx].name);
PSCopy(s2,(*specList)[sum2->u.virtualMess.virtualMBIdx].name);
return(StringComp(s1,s2));
}
/************************************************************************
* SumOffsetCompare - compare the disk offsets of two sums
************************************************************************/
int SumOffsetCompare(MSumPtr sum1, MSumPtr sum2)
{
long res = (unsigned)sum1->offset > (unsigned)sum2->offset ? 1 :
(sum1->offset==sum2->offset ? 0 : -1);
return(res);
}
/************************************************************************
* SumTimeFuzzCompare - compare the arrival times of two sums, fuzzily
************************************************************************/
int SumTimeFuzzCompare(MSumPtr sum1, MSumPtr sum2)
{
Str31 string1, string2;
return(StringComp(ComputeLocalDate(sum1,string1),ComputeLocalDate(sum2,string2)));
}
/************************************************************************
* SumSubjCompare - compare the subjects of two sums
************************************************************************/
int SumSubjCompare(MSumPtr sum1, MSumPtr sum2)
{
short res = SubjCompare(sum1->subj,sum2->subj);
return (res);
}
/************************************************************************
* SumSubjTimedCompare - compare the subjects of two summaries, but only let
* them be equal if they are within a certain amount of time
************************************************************************/
int SumSubjTimedCompare(MSumPtr sum1, MSumPtr sum2)
{
long diff = sum1->seconds-sum2->seconds;
if (ABS(diff) > GroupSubjThreshTime) return diff;
return SubjCompare(sum1->subj,sum2->subj);
}
/************************************************************************
* SumSubjIdCompare - compare the spare shorts of two sums
************************************************************************/
int SumSubjIdCompare(MSumPtr sum1, MSumPtr sum2)
{
short res = sum1->subjId-sum2->subjId;
return (res);
}
/**********************************************************************
* SubjCompare - compare two subjects
**********************************************************************/
int SubjCompare(PStr in1, PStr in2)
{
Str127 s1,s2;
Boolean maxed1, maxed2;
MSumType sum;
PSCopy(s1,in1);
PSCopy(s2,in2);
maxed1 = *s1>=sizeof(sum.subj)-2;
maxed2 = *s2>=sizeof(sum.subj)-2;
#ifdef NEVER
ComposeLogS(-1,nil,"\pComparing <20>%p<> <20>%p<>",s1,s2);
#endif
TrimRe(s1,true);
TrimRe(s2,true);
TrimInitialWhite(s1);
TrimInitialWhite(s2);
TrimWhite(s1);
TrimWhite(s2);
TrimInternalWhite(s1);
TrimInternalWhite(s2);
if (maxed1) *s2 = MIN(*s2,*s1); // subject 1 maxed out--trim subject 2
if (maxed2) *s1 = MIN(*s2,*s1); // subject 2 maxed out--trim subject 1
return(StringComp(s1,s2));
}
/************************************************************************
* SumFromCompare - compare the senders of two sums
************************************************************************/
int SumFromCompare(MSumPtr sum1, MSumPtr sum2)
{
short res=StringComp(sum1->from,sum2->from);
return (res);
}
/************************************************************************
* SumSizeCompare - compare two messages by size
************************************************************************/
int SumSizeCompare(MSumPtr sum1, MSumPtr sum2)
{
long l1 = EffectiveLength(sum1);
long l2 = EffectiveLength(sum2);
#ifdef DEBUG
if (RunType!=Production && BUG2) return sum1->serialNum - sum2->serialNum;
#endif
return (l1-l2);
}
/************************************************************************
* SumSizeKCompare - compare two messages by size in K
************************************************************************/
int SumSizeKCompare(MSumPtr sum1, MSumPtr sum2)
{
long l1 = EffectiveLength(sum1)/(1 K);
long l2 = EffectiveLength(sum2)/(1 K);
#ifdef DEBUG
if (RunType!=Production && BUG2) return sum1->serialNum - sum2->serialNum;
#endif
return (l1-l2);
}
/************************************************************************
* SumLabelCompare - compare two messages by color
************************************************************************/
int SumLabelCompare(MSumPtr sum1, MSumPtr sum2)
{
short res = SumColor(sum1)-SumColor(sum2);
return (res);
}
/************************************************************************
* SumAnalCompare - compare two messages by analysis
************************************************************************/
int SumAnalCompare(MSumPtr sum1, MSumPtr sum2)
{
short res = (unsigned)sum1->score > (unsigned)sum2->score ? 1 :
(sum1->score==sum2->score ? 0 : -1);
return (res);
}
/************************************************************************
* SumAttCompare - compare two summaries based on whether or not they have attachments
************************************************************************/
int SumAttCompare(MSumPtr sum1, MSumPtr sum2)
{
long res = (long)(sum1->flags&FLAG_HAS_ATT)-(long)(sum2->flags&FLAG_HAS_ATT);
return (res);
}
/************************************************************************
* SumSelectCompare - compare two summaries based on whether or not they are selected
************************************************************************/
int SumSelectCompare(MSumPtr sum1, MSumPtr sum2)
{
long res = (long)(sum1->spareShort)-(long)(sum2->spareShort);
// Check for sentinel first
if (sum1->length==kLargeUniqueValue) return 1;
if (sum2->length==kLargeUniqueValue) return -1;
if (!res) res = sum1->spareShort2-sum2->spareShort2;
return (res);
}
/**********************************************************************
* SumStatCompare - compare two messages based on state
**********************************************************************/
int SumStatCompare(MSumPtr sum1, MSumPtr sum2)
{
static Byte table[] = {1,2,3,4,5,6,8,7,10,11,9,12,13,14,15,16,17};
short state1,state2;
// Check if state is 255 (sentinel) or otherwise outside range or table
state1 = sum1->state==255 ? 255 : (sum1->state<0 || sum1->state>=sizeof(table)) ? 100 : table[sum1->state];
state2 = sum2->state==255 ? 255 : (sum2->state<0 || sum2->state>=sizeof(table)) ? 100 : table[sum2->state];
return state1-state2;
}
/**********************************************************************
* SumPriorCompare - compare two messages by priority
**********************************************************************/
int SumPriorCompare(MSumPtr sum1, MSumPtr sum2)
{
short p1,p2,res;
p1 = sum1->priority;
p2 = sum2->priority;
if (!p1) p1=Display2Prior(3);
if (!p2) p2=Display2Prior(3);
res = p1-p2;
return (res);
}
/**********************************************************************
* SumJunkCompare - compare two messages by junk score
**********************************************************************/
int SumJunkCompare(MSumPtr sum1, MSumPtr sum2)
{
return sum1->spamScore-sum2->spamScore;
}
/**********************************************************************
* reverses of all of the above
**********************************************************************/
int RevSumSizeCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumSizeCompare(sum1,sum2));}
int RevSumLabelCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumLabelCompare(sum1,sum2));}
int RevSumAnalCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumAnalCompare(sum1,sum2));}
int RevSumAttCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumAttCompare(sum1,sum2));}
int RevSumStatCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumStatCompare(sum1,sum2));}
int RevSumPriorCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumPriorCompare(sum1,sum2));}
int RevSumJunkCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumJunkCompare(sum1,sum2));}
int RevSumFromCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumFromCompare(sum1,sum2));}
int RevSumSubjCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumSubjCompare(sum1,sum2));}
int RevSumTimeCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumTimeCompare(sum1,sum2));}
int RevSumMailboxCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumMailboxCompare(sum1,sum2));}
int RevSumOffsetCompare(MSumPtr sum1, MSumPtr sum2) {return(-SumOffsetCompare(sum1,sum2));}
/************************************************************************
* SortTOC - sort a toc, given a comparison function
************************************************************************/
void SortTOC(TOCHandle tocH, Boolean reverse, int (*compare)())
{
MSumPtr sums, sPtr;
MSumType sentinel;
int count = (*tocH)->count;
SetHandleBig_(tocH,GetHandleSize_(tocH)+sizeof(MSumType));
if (MemError()) {WarnUser(MEM_ERR,MemError()); return;}
sums=LDRef(tocH)->sums;
// Set up sentinel
Zero(sentinel);
InfiniteString(sentinel.from,sizeof(sentinel.from));
InfiniteString(sentinel.subj,sizeof(sentinel.subj));
sentinel.seconds = 0xffffffff;
sentinel.length = kLargeUniqueValue;
sentinel.state = 255;
sentinel.flags = 0xffffffff-FLAG_SKIPPED;
sentinel.opts = 0xffffffff-OPT_JUSTSUB;
sentinel.priority = 255;
sentinel.spareShort = 0x7fff;
sentinel.subjId = 0x7fff;
sentinel.flags |= FLAG_HAS_ATT;
sentinel.selected = True;
sentinel.spareShort2 = 0x7fff;
sentinel.u.virtualMess.virtualMBIdx = 0x7fff;
sentinel.score = 0xf;
sums[count] = sentinel;
/* tag with original positions */
for (sPtr=sums;sPtr<sums+(*tocH)->count;sPtr++)
sPtr->spareShort2=/*reverse*/ 0 ?((*tocH)->count-(sPtr-sums)) : (sPtr-sums);
gSortTOC = tocH;
QuickSort((void*)sums,sizeof(MSumType),0,count-1,(void*)compare,(void*)SwapSum);
for (sPtr=sums;sPtr<sums+(*tocH)->count;sPtr++)
if (sPtr->messH) (*(MessHandle)sPtr->messH)->sumNum = sPtr-sums;
UL(tocH);
SetHandleBig_(tocH,GetHandleSize_(tocH)-sizeof(MSumType));
TOCSetDirty(tocH,true);
}
#pragma segment BoxWidget
/**********************************************************************
* MessDragSend - drag data proc for messages
**********************************************************************/
pascal OSErr MessDragSend(FlavorType flavor, void *dragSendRefCon, ItemReference theItemRef, DragReference drag)
{
OSErr err=cantGetFlavorErr;
MessHandle messH;
UHandle data;
if (!(err=MyGetDragItemData(drag,1,MESS_FLAVOR,(void*)&data)))
{
messH = **(MessHandle**)data;
ZapHandle(data);
err = BoxMessDragSendLo(flavor,dragSendRefCon,drag,(*messH)->tocH,(*messH)->sumNum);
}
return(err);
}
/**********************************************************************
* BoxDragSend - drag data proc for mailboxes
**********************************************************************/
pascal OSErr BoxDragSend(FlavorType flavor, void *dragSendRefCon, ItemReference theItemRef, DragReference drag)
{
OSErr err=cantGetFlavorErr;
FSSpecPtr spec = (FSSpecPtr)dragSendRefCon;
TOCHandle tocH;
UHandle data;
if (!(err=MyGetDragItemData(drag,1,TOC_FLAVOR,(void*)&data)))
{
tocH = **(TOCHandle**)data;
ZapHandle(data);
err = BoxMessDragSendLo(flavor,dragSendRefCon,drag,tocH,FirstMsgSelected(tocH));
}
return(err);
}
/**********************************************************************
* BoxMessDragSendLo - common stuff for message & summary dragging
**********************************************************************/
OSErr BoxMessDragSendLo(FlavorType flavor, void *dragSendRefCon, DragReference drag, TOCHandle tocH,short sumNum)
{
AEDesc dropLocation;
OSErr err=cantGetFlavorErr;
FSSpecPtr spec = (FSSpecPtr)dragSendRefCon;
UHandle addresses=nil;
/*
* return the filename
*/
if (flavor==SPEC_FLAVOR)
{
NullADList(&dropLocation,nil);
if (!(err=GetDropLocation(drag,&dropLocation)))
if (!(err=GetDropLocationDirectory(&dropLocation,&spec->vRefNum,&spec->parID)))
{
MakeMessFileName(tocH,sumNum,spec->name);
UniqueSpec(spec,MAX_BOX_NAME);
FSpCreateResFile(spec,CREATOR,'TEXT',smSystemScript);
WhackFinder(spec);
if (!(err = ResError()))
err = MySetDragItemFlavorData(drag,1,SPEC_FLAVOR,spec,sizeof(FSSpec));
}
DisposeADList(&dropLocation,nil);
}
else if (flavor==A822_FLAVOR)
{
if (!MyGetDragItemData(drag,1,MESS_FLAVOR,nil))
err = GatherBoxAddresses(tocH,DragOrMods(drag),sumNum,sumNum,&addresses,true);
else
err = GatherBoxAddresses(tocH,DragOrMods(drag),-1,-1,&addresses,true);
if (!err)
{
CommaList(addresses);
MoveHHi(addresses);
err = MySetDragItemFlavorData(drag,1,A822_FLAVOR,LDRef(addresses),GetHandleSize(addresses));
ZapHandle(addresses);
}
}
return(err);
}
/************************************************************************
* DragXfer - drag messages from one mailbox to the next
************************************************************************/
void DragXfer(Point pt, TOCHandle tocH, MessHandle messH)
{
RgnHandle dragRgn = tocH ? BoxBuildDragRgn(tocH) : MessBuildDragRgn(messH);
DragReference drag;
MyWindowPtr win = tocH ? (*tocH)->win : (*messH)->win;
OSErr err;
PromiseHFSFlavor promise;
FSSpec spec;
Boolean trash = False;
FSSpecHandle handle=nil;
FSSpec moveSpec;
FSSpecPtr specPtr = &moveSpec;
DECLARE_UPP(MessDragSend,DragSendData);
DECLARE_UPP(BoxDragSend,DragSendData);
INIT_UPP(MessDragSend,DragSendData);
INIT_UPP(BoxDragSend,DragSendData);
if (dragRgn)
{
Zero(spec);
Zero(moveSpec);
SetMyCursor(arrowCursor); SFWTC = True;
/*
* create a drag
*/
if (!MyNewDrag(win,&drag))
{
OutlineRgn(dragRgn,1);
GlobalizeRgn(dragRgn);
Zero(promise);
promise.fileType = 'TEXT';
promise.fileCreator = CREATOR;
promise.promisedFlavor = SPEC_FLAVOR;
if (messH) DragTOCSource = (*(*messH)->tocH)->win; // real source for data
else DragTOCSource = (*tocH)->win;
gDrawerOpenedWin = nil;
if (!(err=SetDragSendProc(drag,tocH?BoxDragSendUPP:MessDragSendUPP,(void*)&spec)))
if (!(err=AddDragItemFlavor(drag,1,flavorTypePromiseHFS,&promise,sizeof(promise),0)))
if (!(err=AddDragItemFlavor(drag,1,'m822',nil,0,flavorNotSaved)))
if (!tocH || !(err=AddDragItemFlavor(drag,1,TOC_FLAVOR,&tocH,sizeof(tocH),flavorSenderOnly|flavorNotSaved)))
if (!messH || !(err=AddDragItemFlavor(drag,1,MESS_FLAVOR,&messH,sizeof(messH),flavorSenderOnly|flavorNotSaved)))
if (!(err=AddDragItemFlavor(drag,1,A822_FLAVOR,nil,0,0)))
if (!(err=AddDragItemFlavor(drag,1,SPEC_FLAVOR,nil,0,0)))
if (!(err=AddDragItemFlavor(drag,1,'euXX',&specPtr,sizeof(FSSpecPtr),flavorSenderOnly|flavorNotSaved)))
{
// MyTrackDrag can close the window being dragged. Save information about the message we may need after the drag ...
FSSpec sourceSpec = tocH ? GetMailboxSpec(tocH,-1) : GetMailboxSpec((*messH)->tocH,-1);
TOCHandle messToc = messH ? (*messH)->tocH : nil;
short messSum = messH ? (*messH)->sumNum : 0;
if (!MyTrackDrag(drag,&MainEvent,dragRgn))
{
trash = DragTargetWasTrash(drag);
MyDisposeDrag(drag); drag=nil;
if (*moveSpec.name)
{
spec = moveSpec;
trash = !(CurrentModifiers()&optionKey);
}
if (*spec.name && !SameSpec(&sourceSpec,&spec))
{
if (tocH) MoveSelectedMessages(tocH,&spec,!trash);
else
{
if (trash)
{
AddXfUndo(messToc,TOCBySpec(&spec),messSum);
EzOpen(messToc,messSum,0,MainEvent.modifiers,True,True);
}
MoveMessage(messToc,messSum,&spec,!trash);
}
}
}
}
if (drag) MyDisposeDrag(drag);
if (gDrawerOpenedWin == win)
{
// Opened the mailbox drawer for drag. Close it.
MBDrawerToggle(win);
gDrawerOpenedWin = nil;
}
}
DisposeRgn(dragRgn);
}
}
/**********************************************************************
*
**********************************************************************/
OSErr BoxDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag)
{
OSErr err=noErr;
RgnHandle rgn;
ControlHandle cntl;
Rect r;
Point mouse;
if (!DragIsInteresting(drag,TOC_FLAVOR,MESS_FLAVOR,nil)) return(dragNotAcceptedErr);
if (DragTOCSource==win)
{
if (which ==kDragTrackingInWindow)
{
// Dragging within this window
// If cursor is over mailbox drawer button, open drawer if not already open
cntl = FindControlByRefCon(win,kDrawerSwitch);
if (cntl && !GetControlValue(cntl))
{
GetControlBounds(cntl,&r);
GetDragMouse(drag, &mouse, nil);
GlobalToLocal(&mouse);
if (PtInRect(mouse,&r))
{
MBDrawerToggle(win);
gDrawerOpenedWin = win;
}
}
}
return(dragNotAcceptedErr);
}
SetPort_(GetMyWindowCGrafPtr(win));
switch (which)
{
case kDragTrackingEnterWindow:
if (rgn=NewRgn())
{
RectRgn(rgn,&win->contR);
OutlineRgn(rgn,2);
ShowDragHilite(drag,rgn,True);
DisposeRgn(rgn);
}
err = noErr;
break;
case kDragTrackingLeaveWindow:
HideDragHilite(drag);
err = noErr;
break;
case 0xfff:
err = BoxDrop(win,drag);
break;
}
return(err);
}
/**********************************************************************
*
**********************************************************************/
OSErr BoxDrop(MyWindowPtr win, DragReference drag)
{
TOCHandle winTocH = (TOCHandle) GetMyWindowPrivateData(win);
FSSpec spec = GetMailboxSpec(winTocH,-1);
TOCHandle tocH;
short sumNum;
OSErr err;
UHandle data=nil;
if (!(err=MyGetDragItemData(drag,1,MESS_FLAVOR,(void*)&data)))
{
tocH = (***(MessHandle**)data)->tocH;
sumNum = (***(MessHandle**)data)->sumNum;
}
else if (!(err=MyGetDragItemData(drag,1,TOC_FLAVOR,(void*)&data)))
{
tocH = **(TOCHandle**)data;
sumNum = -1;
}
ZapHandle(data);
if (!err)
{
DragSumNum = sumNum;
DragTOCFrom = tocH;
DragTOCTo = (TOCHandle) winTocH;
DragModsWere = DragOrMods(drag);
}
return(err);
}
/************************************************************************
* FinishBoxDrag - finish a drag outside of the actual drag mgr
************************************************************************/
OSErr FinishBoxDrag(void)
{
FSSpec spec = GetMailboxSpec(DragTOCTo,-1);
if (DragSumNum==-1)
MoveSelectedMessages(DragTOCFrom,&spec,(DragModsWere&optionKey)!=0);
else
{
if (!(DragModsWere&optionKey))
{
AddXfUndo(DragTOCFrom,DragTOCTo,DragSumNum);
EzOpen(DragTOCFrom,DragSumNum,0,DragModsWere,True,True);
}
MoveMessage(DragTOCFrom,DragSumNum,&spec,(DragModsWere&optionKey)!=0);
}
DragTOCFrom = nil;
return noErr;
}
/************************************************************************
* TOCHUnder - what tocH is under a point?
************************************************************************/
TOCHandle TOCHUnder(Point pt)
{
WindowPtr theWindow;
if (FindWindow(pt,&theWindow) &&
(GetWindowKind(theWindow)==MBOX_WIN ||
GetWindowKind(theWindow)==CBOX_WIN))
{
return((TOCHandle)GetWindowPrivateData(theWindow));
}
return(nil);
}
/************************************************************************
* BoxBuildDragRgn - drag messages from one mailbox to the next
************************************************************************/
RgnHandle BoxBuildDragRgn(TOCHandle tocH)
{
RgnHandle theRgn = NewRgn();
short mNum;
Rect r, sumR;
MyWindowPtr win = (*tocH)->win;
short minVis, maxVis;
if (theRgn)
{
OpenRgn();
SetRect(&r,win->contR.left,0,win->contR.right,win->vPitch);
minVis = GetControlValue(win->vBar);
OffsetRect(&r,0,-minVis*win->vPitch);
maxVis = minVis + (win->contR.bottom-win->contR.top)/win->vPitch+1;
maxVis = MIN(maxVis,(*tocH)->count-1);
for (mNum=minVis;mNum<=maxVis;mNum++)
{
if ((*tocH)->sums[mNum].selected)
{
sumR = r;
OffsetRect(&sumR,0,win->topMargin+mNum*win->vPitch);
FrameRect(&sumR);
}
}
CloseRgn(theRgn);
}
return(theRgn);
}
/**********************************************************************
* BoxClick - handle a click in the content region of a window
**********************************************************************/
void BoxClick(MyWindowPtr win,EventRecord *event)
{
Point pt;
short startNum,endNum,mNum;
WindowPtr winWP = GetMyWindowWindowPtr(win);
TOCHandle tocH = (TOCHandle)GetWindowPrivateData(winWP);
Boolean cmd,shift,opt,ctl;
short which;
ControlHandle cntl;
Boolean clickOnSelected;
pt = event->where;
SetPort_(GetMyWindowCGrafPtr(win));
GlobalToLocal(&pt);
(*tocH)->userActive = true;
if (win->isActive)
{
if (BoxDragDividers(pt,win)) return;
if ((*tocH)->previewPTE && PtInPETE(pt,(*tocH)->previewPTE) && PeteLen((*tocH)->previewPTE))
{
PeteEditWFocus(win,(*tocH)->previewPTE,peeEvent,(void*)event,nil);
if (win->pte) BoxListFocus(tocH,false);
if ((*tocH)->drawerWin) MBDrawerSetFocus((*tocH)->drawerWin, false);
if ((*tocH)->previewID && (mNum=FirstMsgSelected(tocH))>=0 && PreviewReadFocus)
BeenThereDoneThat(tocH,-1);
return;
}
else if ((cntl=FindControlByRefCon(win,PREVIEW_DIVIDE_CNTL)) && PtInControl(pt,cntl))
{
BoxDragPreviewDivider(tocH,pt);
return;
}
else if ((cntl=FindControlByRefCon(win,'wide'+blServer)) && PtInControl(pt,cntl))
return; // ignore clicks
else if ((cntl=FindControlByRefCon(win,kBoxSizeRefCon)) && PtInControl(pt,cntl) && (*tocH)->virtualTOC)
return; // ignore clicks in box size button for virtual mailboxes
else if ((cntl=FindControlByRefCon(win,kConConProfRefCon)) && PtInControl(pt,cntl))
{
BoxConConProfControl(tocH,cntl,pt);
return; // ignore clicks in box size button for virtual mailboxes
}
else if (HandleControl(pt,win))
return;
}
if (!PtInRect(pt,&win->contR))
{
if (!win->isActive) SelectWindow_(winWP);
(*tocH)->userActive = true;
return;
}
shift = (event->modifiers & shiftKey) != 0;
cmd = (event->modifiers & cmdKey) != 0;
opt = (event->modifiers & optionKey) != 0;
ctl = (event->modifiers & controlKey) != 0;
mNum = (pt.v-win->topMargin)/win->vPitch + GetControlValue(win->vBar);
clickOnSelected = mNum<(*tocH)->count && (*tocH)->sums[mNum].selected;
if (!win->isActive && !DragPrefSumImmediate && !ctl && !clickOnSelected)
{
SelectWindow_(winWP);
return;
}
if (win->isActive)
{
PeteFocus(win,nil,true);
BoxListFocus(tocH,true);
if ((*tocH)->drawerWin) MBDrawerSetFocus((*tocH)->drawerWin, false);
/*
* menus in to summary
*/
if (!cmd && !opt && !shift && ClickType==Single && clickOnSelected)
{
BoxFindDivider(pt,&which);
if (which<=blAttach || which==blLabel || which==blServer || which==blMailbox)
{
EnableMenuItems(False);
SetMenuTexts(0,False);
SetMyCursor(arrowCursor);
if (which==blLabel)
BoxLabelMenu(tocH,mNum,nil,pt);
else
if (which==blPrior)
BoxPriorMenu(tocH,mNum,pt);
else if (which==blAttach)
BoxAttachMenu(tocH,mNum,pt);
else if (which==blStat)
BoxStateMenu(tocH,mNum,pt);
else if (which==blServer)
BoxServerMenu(tocH,mNum,pt);
else if (which==blMailbox)
BoxMailboxMenu(tocH,mNum,pt);
return;
}
}
}
/*
* drag xfer
*/
if (!shift && !cmd && clickOnSelected)
{
if (MyWaitMouseMoved(event->where,True))
{
DragXfer(pt,tocH,nil);
(*tocH)->userActive = true;
return;
}
PeteFocus(win,nil,true);
BoxListFocus(tocH,true);
}
if (shift)
{
for (startNum=0;startNum<(*tocH)->count;startNum++)
if ((*tocH)->sums[startNum].selected) break;
if (startNum < (*tocH)->count)
{
if (startNum < mNum) endNum = mNum;
else
{
for (endNum=startNum+1;endNum<(*tocH)->count;endNum++)
if (!(*tocH)->sums[endNum].selected) break;
endNum--;
if (!(*tocH)->sums[endNum].selected) endNum = startNum;
startNum = mNum;
}
}
else startNum=endNum=mNum;
}
else startNum=endNum=mNum;
SelectBoxRange(tocH,startNum,endNum,cmd,-1,-1);
(*tocH)->userActive = true;
if (mNum<(*tocH)->count && (*tocH)->sums[mNum].selected)
{
if (!shift && !cmd && DragPrefSumImmediate && MyWaitMouseMoved(event->where,True))
{
DragXfer(pt,tocH,nil);
(*tocH)->userActive = true;
return;
}
}
if (!win->isActive) {SelectWindow_(winWP);}
else
{
EnableMenus(winWP,False);
if (ClickType==Double)
DoDependentMenu(winWP,FILE_MENU,FILE_OPENSEL_ITEM,event->modifiers);
else
{
BoxTrack(tocH,mNum==startNum?endNum:startNum,cmd&&!shift);
if (opt)
{
BoxFindDivider(pt,&which);
BoxSelectSame(tocH,BoxLine2Item(which),mNum);
}
(*tocH)->userActive = true;
}
}
}
/************************************************************************
* BoxConConProfControl - Handle clicks in the profile control
************************************************************************/
void BoxConConProfControl(TOCHandle tocH,ControlHandle cntl,Point pt)
{
short item, oldItem;
Str63 name;
// update the menu
BoxBuildConConProfMenu(tocH,cntl);
oldItem = GetBevelMenuValue(cntl);
// let the control manager track
HandleControl(pt,(*tocH)->win);
item = GetBevelMenuValue(cntl);
// Now, see what we can see
if (item!=oldItem)
{
MenuHandle mh = (MenuHandle)GetBevelMenu(cntl);
if (mh)
{
uLong cmd;
short section, subItem;
uLong hash;
// what's the command id?
GetMenuItemCommandID(mh,item,&cmd);
section = cmd>>16;
subItem = cmd&0xffff;
// Figure out what profile was chosen
switch (subItem)
{
case 0: // Default
hash = subItem;
GetRString(name,section==1?CONCON_PREVIEW_PROFILE:CONCON_MULTI_PREVIEW_PROFILE);
break;
case CONCON_NONE: // None
hash = subItem;
GetRString(name,subItem);
break;
default: // actual profile
MyGetItem(mh,item,name);
hash = Hash(name);
}
// if section is 1, single selection.
// if section is 2, multi selection.
// set the proper one
if (section==1)
(*tocH)->singlePreviewProfileHash = hash;
else
(*tocH)->multiPreviewProfileHash = hash;
TOCSetDirty(tocH,true);
// Update the control
SetControlTitle(cntl,name);
SetBevelMenuValue(cntl,item);
// Now let somebody else figure out what to do with the selection...
BoxRefreshPreview(tocH);
}
}
}
/************************************************************************
* BoxBuildConConProfMenu - Build the profile menu for the preview
************************************************************************/
void BoxBuildConConProfMenu(TOCHandle tocH,ControlHandle cntl)
{
MenuHandle mh = (MenuHandle)GetBevelMenu(cntl);
Str255 itemStr;
Str63 defaultStr;
short i;
short j;
short n;
short selected = CountSelectedMessages(tocH);
uLong myHash;
ConConProH ccph;
if (!mh) return; // uh-oh...
TrashMenu(mh,1);
n = 0;
for (i=1;i<=2;i++) // 1 == single preview, 2 == multi preview
{
// Which set of profiles?
MyAppendMenu(mh,GetRString(itemStr,i==1?CONCON_PROF_SINGLE:CONCON_PROF_MULTI));
DisableItem(mh,++n);
// which profile are we using for this section of the menu?
myHash = i==1?(*tocH)->singlePreviewProfileHash:(*tocH)->multiPreviewProfileHash;
// the default
GetRString(defaultStr,i==1?CONCON_PREVIEW_PROFILE:CONCON_MULTI_PREVIEW_PROFILE);
ComposeRString(itemStr,CONCON_PROF_DEFAULT,defaultStr);
MyAppendMenu(mh,itemStr);
SetMenuItemCommandID(mh,++n,i<<16);
if (myHash==0) SetItemMark(mh,n,checkMark);
EnableIf(mh,n,i==1 && selected==1 || i==2 && selected>1);
// None
MyAppendMenu(mh,GetRString(itemStr,CONCON_NONE));
SetMenuItemCommandID(mh,++n,i<<16|CONCON_NONE);
if (myHash==CONCON_NONE) SetItemMark(mh,n,checkMark);
EnableIf(mh,n,i==1 && selected==1 || i==2 && selected>1);
// if the default for this mailbox isn't done already,
// grab a copy of its name here
if (myHash!=CONCON_NONE && myHash!=0 && (ccph=ConConProFindHash(myHash)))
PSCopy(defaultStr,(*ccph)->name);
else
*defaultStr = 0;
// The defined profiles
ConConAddItems(mh);
// Now fix up the items
for (j=CountMenuItems(mh);j>n;j--)
{
SetMenuItemCommandID(mh,j,(i<<16)|j);
EnableIf(mh,j,i==1 && selected==1 || i==2 && selected>1);
MyGetItem(mh,j,itemStr);
if (StringSame(itemStr,defaultStr)) SetItemMark(mh,j,checkMark);
}
// and reset our count
n = CountMenuItems(mh);
// separate the types
if (i==1)
{
n++;
AppendMenu(mh,"\p-");
}
}
}
/************************************************************************
* BoxDragPreviewDivider
************************************************************************/
void BoxDragPreviewDivider(TOCHandle tocH,Point pt)
{
Rect r, bounds;
MyWindowPtr tocWin = (*tocH)->win;
CGrafPtr winPort = GetMyWindowCGrafPtr(tocWin);
short hi = 0;
short fudge = pt.v - tocWin->contR.bottom - GROW_SIZE;
Rect rPort = *GetPortBounds(winPort,&rPort);
short totalHi = RectHi(rPort);
SetRect(&r,0,tocWin->contR.bottom,rPort.right,tocWin->contR.bottom+GROW_SIZE);
bounds.top = tocWin->contR.top + GetRLong(PREVIEW_USELESS)*tocWin->vPitch + GROW_SIZE + fudge;
bounds.bottom = totalHi - GetRLong(PREVIEW_USELESS)*FontLead - fudge - GROW_SIZE - tocWin->topMargin;
bounds.left = -REAL_BIG/2;
bounds.right = REAL_BIG/2;
pt = DragDivider(pt,&r,&bounds,tocWin);
if (pt.v==0)
{
GetMouse(&pt);
if (pt.v > tocWin->contR.bottom) hi = -1;
}
else
{
hi = totalHi - pt.v - fudge; // where the user put the line
hi = totalHi-hi-GROW_SIZE-tocWin->topMargin; // the room the user left for summaries
hi = RoundDiv(hi,tocWin->vPitch)*tocWin->vPitch; // round to nearest summary
hi = totalHi - tocWin->topMargin - GROW_SIZE - hi; // back to the preview height
}
if (hi)
{
(*tocH)->previewHi = hi;
TOCSetDirty(tocH,true);
MyWindowDidResize(tocWin,nil);
}
AuditHit(false, false, false, false, false, GetWindowKind(GetMyWindowWindowPtr(tocWin)), PREVIEW_DIVIDE_CNTL, mouseDown);
}
/**********************************************************************
* BoxLabelMenu - pop up the label menu in a mailbox window
**********************************************************************/
void BoxLabelMenu(TOCHandle tocH,short mNum,MessHandle messH,Point pt)
{
short currentLabel = GetSumColor(tocH,mNum);
MenuHandle mh = GetMHandle(LABEL_HIER_MENU);
long res;
MyWindowPtr win;
LocalToGlobal(&pt);
#ifdef REFRESH_LABELS_MENU
RefreshLabelsMenu();
#endif //REFRESH_LABELS_MENU
SetItemMark(mh,Label2Menu(currentLabel),checkMark);
res = MyPopupMenuSelect((*tocH)->win,mh,pt,Label2Menu(currentLabel));
if (res&0xffff0000)
if (messH) DoMenu2(GetMyWindowWindowPtr((*messH)->win),LABEL_HIER_MENU,res&0xff,nil);
else DoMenu2(GetMyWindowWindowPtr((*tocH)->win),LABEL_HIER_MENU,res&0xff,nil);
// Audit the fact that the label menu was used ...
if (tocH)
{
win = (*tocH)->win;
if (win)
{
AuditHit(false, false, false, false, false, GetWindowKind(GetMyWindowWindowPtr(win)), AUDITCONTROLID(GetWindowKind(GetMyWindowWindowPtr(win)),LABEL_HIER_MENU), mouseDown);
}
}
}
/**********************************************************************
* BoxPriorMenu - pop up the priority menu in a mailbox window
**********************************************************************/
void BoxPriorMenu(TOCHandle tocH,short mNum,Point pt)
{
short currentPri = Prior2Display((*tocH)->sums[mNum].priority);
MenuHandle mh = GetMHandle(PRIOR_HIER_MENU);
long res;
LocalToGlobal(&pt);
res = MyPopupMenuSelect((*tocH)->win,mh,pt,currentPri);
if (res&0xffff0000) BoxMenu((*tocH)->win,PRIOR_HIER_MENU,res&0xff,nil);
}
/**********************************************************************
* BoxServerMenu - pop up the server menu in a mailbox window
**********************************************************************/
void BoxServerMenu(TOCHandle tocH,short mNum,Point pt)
{
MenuHandle mh = GetMHandle(SERVER_HIER_MENU);
long res;
short item=0;
LocalToGlobal(&pt);
item = EnableServerItems((*tocH)->win,mNum,false,false);
res = MyPopupMenuSelect((*tocH)->win,mh,pt,item);
if (res&0xffff0000) BoxMenu((*tocH)->win,SERVER_HIER_MENU,res&0xff,nil);
}
/************************************************************************
* ServerMenuChoice - choose an item from the server menu
************************************************************************/
void ServerMenuChoice(TOCHandle tocH,short mNum,short item,Boolean shiftPressed)
{
// the server menu looks different for IMAP mailboxes
if ((*tocH)->imapTOC)
{
switch(item)
{
case isvmDelete:
{
Handle uids = nil;
long c = CountSelectedMessages(tocH);
long sumNum;
// build a list of uids to be deleted
uids = NuHandleClear(c*sizeof(unsigned long));
if (uids)
{
for (sumNum=0;sumNum<(*tocH)->count && c;sumNum++)
if ((*tocH)->sums[sumNum].selected)
BMD(&((*tocH)->sums[sumNum].uidHash),&((unsigned long *)(*uids))[--c],sizeof(unsigned long));
// and delete them.
IMAPDeleteMessages(tocH, uids, false, false, (((*tocH)->sums[mNum].opts&OPT_DELETED)!=0), false);
}
else
{
WarnUser(MemError(), MEM_ERR);
}
break;
}
case isvmRemoveCache:
{
IMAPRemoveSelectedCachedContents(tocH);
break;
}
case isvmFetchMessage:
{
if (shiftPressed) IMAPRemoveSelectedCachedContents(tocH);
IMAPFetchSelectedMessages(tocH, false);
break;
}
case isvmFetchAttachments:
{
if (shiftPressed) IMAPRemoveSelectedCachedContents(tocH);
IMAPFetchSelectedMessages(tocH, true);
break;
}
}
}
else
{
switch(item)
{
case svmNone:
RemTSFromPOPD(DELETE_ID,tocH,mNum);
RemTSFromPOPD(FETCH_ID,tocH,mNum);
InvalTocBox(tocH,mNum,blServer);
if ((*tocH)->sums[mNum].messH) Fix1MessServerArea((*(*tocH)->sums[mNum].messH)->win);
break;
case svmFetch:
RemTSFromPOPD(DELETE_ID,tocH,mNum);
AddTSToPOPD(FETCH_ID,tocH,mNum,False);
InvalTocBox(tocH,mNum,blServer);
if ((*tocH)->sums[mNum].messH) Fix1MessServerArea((*(*tocH)->sums[mNum].messH)->win);
break;
case svmDelete:
RemTSFromPOPD(FETCH_ID,tocH,mNum);
AddTSToPOPD(DELETE_ID,tocH,mNum,False);
InvalTocBox(tocH,mNum,blServer);
if ((*tocH)->sums[mNum].messH) Fix1MessServerArea((*(*tocH)->sums[mNum].messH)->win);
break;
case svmBoth:
AddTSToPOPD(FETCH_ID,tocH,mNum,False);
AddTSToPOPD(DELETE_ID,tocH,mNum,False);
InvalTocBox(tocH,mNum,blServer);
if ((*tocH)->sums[mNum].messH) Fix1MessServerArea((*(*tocH)->sums[mNum].messH)->win);
break;
}
}
}
/************************************************************************
* EnableServerItems - enable the server menu & return the current item
************************************************************************/
short EnableServerItems(MyWindowPtr win,short selectedSum,Boolean all,Boolean shiftPressed)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
MenuHandle mh = GetMHandle(SERVER_HIER_MENU);
TOCHandle tocH;
short mNum;
Boolean onServer=true, skipped, onDelete, onFetch;
Boolean lmos = PrefIsSet(PREF_LMOS);
short item = 0;
short kind = win ? GetWindowKind (winWP) : 0;
Boolean imapTOC = false;
CheckNone(mh);
if (all)
{
EnableItem(mh,0);
for (item=CountMenuItems(mh);item>0;item--)
EnableItem(mh,item);
return(0);
}
if (kind!=MESS_WIN && kind!=MBOX_WIN)
onServer = false;
else if (kind==MESS_WIN)
{
tocH = (*Win2MessH(win))->tocH;
mNum = (*Win2MessH(win))->sumNum;
}
else
{
tocH = (TOCHandle) GetWindowPrivateData (winWP);
mNum = FirstMsgSelected(tocH);
if (mNum==-1) onServer = false;
}
imapTOC = (kind==MESS_WIN || kind==MBOX_WIN) && (tocH) && ((*tocH)->imapTOC);
if (onServer && !imapTOC)
{
#ifdef ENABLE_BASED_ON_FIRST_SELECTED
if ((*tocH)->sums[mNum].flags & FLAG_OUT) onServer = false;
else if (!TSOnPOPD(POPD_ID,tocH,mNum)) onServer = false;
else
{
skipped = ((*tocH)->sums[mNum].flags & FLAG_SKIPPED)!=0;
onFetch = skipped && onServer && TSOnPOPD(FETCH_ID,tocH,mNum);
onDelete = onServer && TSOnPOPD(DELETE_ID,tocH,mNum);
}
#else //ENABLE_BASED_ON_FIRST_SELECTED
short i, sumNum;
Boolean on = false, skip = false, fetch = false, delete = false;
enum {firstMessage = 1, lastMessage, clickedMessage};
short firstSelected = FirstMsgSelected(tocH);
short lastSelected = LastMsgSelected(tocH);
// check the first and last selected message, as well as the message clicked on.
// Note: checking them all will be a significant performance hit.
onServer = skipped = onFetch = onDelete = false;
for (i = firstMessage; i <= clickedMessage; i++)
{
sumNum = -1;
switch (i)
{
case firstMessage:
sumNum = firstSelected;
break;
case lastMessage:
if (lastSelected != firstSelected) sumNum = lastSelected;
break;
case clickedMessage:
if ((lastSelected != selectedSum) && (selectedSum != firstSelected)) sumNum = selectedSum;
break;
}
if (sumNum >= 0)
{
on = true;
if ((*tocH)->sums[sumNum].flags & FLAG_OUT) on = false;
else if (!TSOnPOPD(POPD_ID,tocH,sumNum)) on = false;
else
{
skip = ((*tocH)->sums[sumNum].flags & FLAG_SKIPPED)!=0;
fetch = skip && on && TSOnPOPD(FETCH_ID,tocH,sumNum);
delete = on && TSOnPOPD(DELETE_ID,tocH,sumNum);
}
}
skipped = skipped || skip;
onServer = onServer || on;
onFetch = onFetch || fetch;
onDelete = onDelete || delete;
}
#endif //ENABLE_BASED_ON_FIRST_SELECTED
}
if (imapTOC)
{
short firstItem = shiftPressed ? kisvmLimit : ksvmLimit;
short lastItem = shiftPressed ? kimsvmLimit : kisvmLimit;;
// put the right item into the server menu
for (item = firstItem+1; item < lastItem; item++)
SetItemR(mh, item - firstItem, ServerMenuStrnStrn + item);
EnableItem(mh,0);
if (!FancyTrashForThisPers(tocH))
{
EnableItem(mh,isvmDelete);
if (((*tocH)->sums[mNum].opts&OPT_DELETED)!=0)
SetItemMark(mh,item=isvmDelete,checkMark);
}
else DisableItem(mh,isvmDelete);
EnableItem(mh,isvmRemoveCache);
EnableItem(mh,isvmFetchMessage);
EnableItem(mh,isvmFetchAttachments);
}
else
{
// put the right items into the server menu
for (item = 1; item < ksvmLimit; item++)
SetItemR(mh, item, ServerMenuStrnStrn + item);
if (!onServer)
DisableItem(mh,0);
else
{
EnableItem(mh,0);
EnableItem(mh,svmFetch);
EnableItem(mh,svmBoth);
EnableItem(mh,svmDelete);
if (!skipped) {DisableItem(mh,svmFetch); DisableItem(mh,svmBoth);}
if (onFetch)
{
if (!lmos || onDelete)
item = svmBoth;
else
item = svmFetch;
}
else if (onDelete)
item = svmDelete;
else
item = svmNone;
if (item) SetItemMark(mh,item,checkMark);
if (!lmos) DisableItem(mh,svmFetch);
}
}
return(item);
}
/**********************************************************************
* BoxStateMenu - pop up the state menu in a mailbox window
**********************************************************************/
void BoxStateMenu(TOCHandle tocH,short mNum,Point pt)
{
short newStatus;
newStatus = StatusMenu((*tocH)->win,(*tocH)->sums[mNum].state,pt);
if (newStatus) BoxMenu((*tocH)->win,STATE_HIER_MENU,Status2Item(newStatus),nil);
}
/**********************************************************************
* BoxMailboxMenu - pop up a mailbox hierarchy
**********************************************************************/
void BoxMailboxMenu(TOCHandle tocH,short mNum,Point pt)
{
short realSum;
TOCHandle realTOC;
LocalToGlobal(&pt);
if (realTOC = GetRealTOC(tocH,mNum,&realSum))
{
PopupMailboxPath(nil,realTOC,realSum,pt);
}
}
/**********************************************************************
* BoxAttachMenu - popup a menu of attachments
**********************************************************************/
void BoxAttachMenu(TOCHandle tocH,short mNum,Point pt)
{
long res;
MenuHandle mh=NewMenu(BOX_ATTACH_MENU,"");
StackHandle stack = nil;
CSpec spec;
Handle text;
short i;
FindAttData attData;
if (mh && !StackInit(sizeof(spec),&stack))
{
// first, find all the files
for (mNum=0;mNum<(*tocH)->count;mNum++)
{
if ((*tocH)->sums[mNum].selected)
{
if ((*tocH)->sums[mNum].opts&OPT_HTML)
{
CacheMessage(tocH,mNum);
if (text=(*tocH)->sums[mNum].cache)
{
HNoPurge(text);
InitAttachmentFinder(&attData,text,false,tocH,&(*tocH)->sums[mNum]);
while (GetNextAttachment(&attData,&spec.spec))
{
spec.count = mNum;
if ((*stack)->elCount==255) break;
StackQueue(&spec,stack);
}
HPurge(text);
}
}
if ((*tocH)->sums[mNum].flags&FLAG_HAS_ATT)
{
CacheMessage(tocH,mNum);
if (text=(*tocH)->sums[mNum].cache)
{
HNoPurge(text);
InitAttachmentFinder(&attData,text,true,tocH,&(*tocH)->sums[mNum]);
while (GetNextAttachment(&attData,&spec.spec))
{
spec.count = mNum;
if ((*stack)->elCount==255) break;
StackQueue(&spec,stack);
}
HPurge(text);
}
}
}
if ((*stack)->elCount==255) break;
}
// next, build the menu
if ((*stack)->elCount)
{
for (i=0;!StackItem(&spec,i,stack);i++)
{
MyAppendMenu(mh,spec.spec.name);
if (FileTypeOf(&spec.spec)==MIME_FTYPE) DisableItem(mh,CountMenuItems(mh)); // currently dangerous to open EMSAPI docs; FIX THIS
}
// now pop it up
InsertMenu(mh,-1);
LocalToGlobal(&pt);
res = MyPopupMenuSelect((*tocH)->win,mh,pt,0);
DeleteMenu(GetMenuID(mh));
// and act
if (res&0xffff0000)
{
i = res&0xff;
if (!StackItem(&spec,i-1,stack))
{
// if the user selected an IMAP attachment stub ...
if (IsIMAPAttachmentStub(&spec.spec))
{
// and it can be fetched ...
if (CanFetchAttachment(&spec.spec))
{
// get it.
short realSum;
DownloadIMAPAttachment(&spec.spec, TOCToMbox(GetRealTOC(tocH,spec.count,&realSum)), false);
}
}
else
{
if (!OpenOtherDoc(&spec.spec,0!=(MainEvent.modifiers&controlKey),false,nil))
BeenThereDoneThat(tocH,spec.count);
}
}
}
}
ZapHandle(stack);
}
if (mh) DisposeMenu(mh);
}
/**********************************************************************
* StatusMenu - popup the status menu
**********************************************************************/
short StatusMenu(MyWindowPtr win,short origStatus,Point where)
{
long res;
MenuHandle mh;
short item = Status2Item(origStatus);
CheckState(win,!win,origStatus);
mh = GetMHandle(STATE_HIER_MENU);
LocalToGlobal(&where);
res = MyPopupMenuSelect(win,mh,where,item);
if (res&0xffff0000) return(Item2Status(res&0xff));
else return(0);
}
/**********************************************************************
* MyPopupMenuSelect - popup a menu adjusting the position
**********************************************************************/
long MyPopupMenuSelect(MyWindowPtr win,MenuHandle mh,Point where,short item)
{
short left;
GDHandle gd;
Rect screenRect, windRect;
Boolean hasMB;
// Adjust left position to point within menu
left = where.h-2*MenuWidth(mh)/3;
// Make sure it's still on the same screen
utl_GetWindGD(GetMyWindowWindowPtr(win),&gd,&screenRect,&windRect,&hasMB);
if (left < screenRect.left)
left = screenRect.left;
return AFPopUpMenuSelect(mh,where.v,left,item);
}
/************************************************************************
* BoxTrack - track the mouse in a mailbox window
************************************************************************/
void BoxTrack(TOCHandle tocH,int anchor,Boolean cmd)
{
MyWindowPtr win = (*tocH)->win;
Point pt;
int lastSpot=anchor;
int spot;
int vVal,vMin,vMax,topVis,botVis;
long lastTicks=0;
while (StillDown())
if (TickCount()-lastTicks>GetRLong(SCROLL_ARROW_THROTTLE))
{
lastTicks = TickCount();
vVal = GetControlValue(win->vBar);
vMin = GetControlMinimum(win->vBar);
vMax = GetControlMaximum(win->vBar);
topVis = vVal;
botVis = (*tocH)->count - (vMax-vVal+1);
GetMouse(&pt);
spot = vVal + (pt.v-win->topMargin)/(int)win->vPitch;
if (spot<vMin) spot=vMin;
else if (spot>=(*tocH)->count) spot=(*tocH)->count-1;
if (spot<topVis && vVal>vMin || spot>botVis && vVal<vMax)
{
if (spot<topVis)
ScrollIt(win,0,topVis-spot);
else
ScrollIt(win,0,botVis-spot);
}
if (spot!=lastSpot)
{
if (!cmd)
SelectBoxRange(tocH,anchor,spot,cmd,0,0);
else
{
SelectBoxRange(tocH,anchor,spot,cmd,anchor,lastSpot);
SelectBoxRange(tocH,anchor,lastSpot,cmd,anchor,spot);
}
lastSpot = spot;
}
}
}
/************************************************************************
* SizeBoxClick - handle click in window's size box
************************************************************************/
void SizeBoxClick(MyWindowPtr win,short modifiers)
{
FSSpec spec;
TOCHandle tocH = (TOCHandle)GetMyWindowPrivateData(win);
if ((*tocH)->virtualTOC) return;
if (modifiers&optionKey)
{
CInfoPBRec hfi;
Str255 scratch;
GetRString(scratch,TOC_SUFFIX);
DoCompact(MailRoot.vRef,MailRoot.dirId,&hfi,*scratch);
if (IMAPExists())
DoCompact(IMAPMailRoot.vRef,IMAPMailRoot.dirId,&hfi,*scratch);
}
else if (modifiers&cmdKey && !AnalDisabled())
{
short sumNum;
for (sumNum=0;sumNum<(*tocH)->count;sumNum++)
(*tocH)->sums[sumNum].score = 0;
AnalBox(tocH,0,(*tocH)->count-1);
}
else if (modifiers&shiftKey)
{
// if this is an imap mailbox, clean it up.
if ((*tocH)->imapTOC)
{
short count;
for (count=0;count<(*tocH)->count;count++)
{
if ((*tocH)->sums[count].uidHash==0) DeleteSum(tocH,count);
else count++;
}
}
else if ((*tocH)->which==JUNK)
{
JunkRescanJunkMailbox ();
ArchiveJunk ( tocH );
}
else
JunkRescanBox ( tocH );
}
else
{
// if this is an IMAP toc, expunge deleted messages rather than compacting the mailbox.
if ((*tocH)->imapTOC) IMAPRemoveDeletedMessages(tocH);
else
{
spec = GetMailboxSpec(tocH,-1);
CompactMailbox(&spec,false);
}
}
}
/************************************************************************
* BeenThereDoneThat - mark this message as read
************************************************************************/
void BeenThereDoneThat(TOCHandle tocH,short sumNum)
{
if (sumNum<0)
{
for (sumNum=(*tocH)->count-1;sumNum>=0;sumNum--)
if ((*tocH)->sums[sumNum].selected)
BeenThereDoneThat(tocH,sumNum);
}
else
{
if ((*tocH)->sums[sumNum].state==UNREAD) SetState(tocH,sumNum,READ);
if ((*tocH)->virtualTOC)
{
// Virtual mailbox, do original also
TOCHandle realTOC;
short realSum;
if (realTOC = GetRealTOC(tocH,sumNum,&realSum))
if ((*realTOC)->sums[realSum].state==UNREAD)
SetState(realTOC,realSum,READ);
}
}
}
/************************************************************************
* AnalBox - process all the mail in a mailbox for anality
************************************************************************/
void AnalBox(TOCHandle tocH,short first, short last)
{
short sumNum;
if (first<0) first = 0;
if (last<0 || last>=(*tocH)->count) last = (*tocH)->count-1;
for (sumNum=first;sumNum<=last;sumNum++)
{
CycleBalls();
OneAnalMess(tocH,sumNum);
}
}
/************************************************************************
* OneAnalMess - process a single message for anality
************************************************************************/
void OneAnalMess(TOCHandle tocH,short sumNum)
{
short score;
Boolean inHeader;
if (!(*tocH)->sums[sumNum].score)
{
#ifdef DEBUG
if (RunType!=Production)
{
Str63 franklin;
PSCopy(franklin,(*tocH)->sums[sumNum].from);
if (EqualStrRes(franklin,FRANKLIN))
{
SetTAEScore(tocH,sumNum,5);
return;
}
}
#endif
CacheMessage(tocH,sumNum);
if ((*tocH)->sums[sumNum].cache)
{
inHeader = true;
score = AnalScanHandle((*tocH)->sums[sumNum].cache,0,-1,&inHeader);
if (score>=0)
SetTAEScore(tocH,sumNum,score+1);
}
}
}
/************************************************************************
* BoxInversionSetup - setup the inversion matrix for mailbox columns
************************************************************************/
void BoxInversionSetup(void)
{
Str255 scratch;
Str15 word;
UPtr spot;
short i;
long col;
GetRString(scratch,TOC_INVERSION_MATRIX);
for (i=1,spot=scratch+1;PToken(scratch,word,&spot," ");i++)
{
StringToNum(word,&col);
TOCInversionMatrix[0][i] = col;
TOCInversionMatrix[1][col] = i;
}
}
/**********************************************************************
* HitTab - clicked a tab
**********************************************************************/
void HitTab(MyWindowPtr win,ControlHandle tabCntl,TOCHandle tocH)
{
Boolean fileView;
fileView = GetControlValue(tabCntl)==2 ? true : false;
if (fileView != (*tocH)->fileView) TOCSetDirty(tocH,true);
(*tocH)->fileView = fileView;
SetFileView(win,tocH, GetControlValue(tabCntl)==2 ? true : false);
}
/**********************************************************************
* SetFileView - enable/disable fileview
**********************************************************************/
void SetFileView(MyWindowPtr win,TOCHandle tocH, Boolean fileView)
{
ControlHandle cntl,placard;
short i;
Boolean boxCtlsVisible;
PETEHandle previewPTE;
Rect oldContR = win->contR;
FileViewHandle fvh = GetFileViewInfo(win);
RgnHandle saveRgn;
boxCtlsVisible = !fileView;
saveRgn = SavePortClipRegion(GetMyWindowCGrafPtr(win));
SetEmptyClipRgn(GetMyWindowCGrafPtr(win));
for (i=1;i<BoxLinesLimit;i++) // hide or show mailbox header buttons
if (cntl = FindControlByRefCon(win,'wide'+i))
SetControlVisibility(cntl,boxCtlsVisible,true);
SetControlVisibility(win->vBar,boxCtlsVisible,true);
SetControlVisibility(win->hBar,boxCtlsVisible,true);
if ((cntl=FindControlByRefCon(win,PREVIEW_TOGGLE_CNTL)) && !GetSuperControl(cntl,&placard))
SetControlVisibility(placard,boxCtlsVisible,true);
if ((cntl=FindControlByRefCon(win,PREVIEW_DIVIDE_CNTL)) && !GetSuperControl(cntl,&placard))
SetControlVisibility(placard,boxCtlsVisible,true);
if (cntl=FindControlByRefCon(win,kBoxSizeRefCon))
SetControlVisibility(cntl,boxCtlsVisible,true);
if (previewPTE = (*tocH)->previewPTE)
{
// make preview pane visible/invisible by moving it
Rect r;
short offset;
PeteRect(previewPTE,&r);
offset = boxCtlsVisible ? 4000 : -4000;
OffsetRect(&r,offset,offset);
PeteDidResize(previewPTE,&r);
}
if (fileView)
{
(*fvh)->savePreviewHi = (*tocH)->previewHi;
SetBotMargin(win,0);
(*tocH)->previewHi = -1;
win->update = FileViewUpdate;
win->click = FileViewClick;
win->bgClick = FileViewClick;
win->cursor = FileViewCursor;
win->drag = FileViewDragHandler;
win->key = FileViewKey;
win->find = FileViewFind;
win->menu = FileViewMenu;
win->activate = FileViewActivate;
win->idle = FileViewIdle;
}
else
{
(*tocH)->previewHi = (*fvh)->savePreviewHi;
win->update = BoxUpdate;
win->click = BoxClick;
win->bgClick = BoxClick;
win->cursor = BoxCursor;
win->drag = BoxDragHandler;
win->key = BoxKey;
win->find = BoxFind;
win->menu = BoxMenu;
win->activate = BoxActivate;
win->idle = BoxIdle;
}
InvalBotMargin(win);
SetControlVisibility((*(*fvh)->list->hList)->vScroll,!boxCtlsVisible,true);
RestorePortClipRegion(GetMyWindowCGrafPtr(win),saveRgn);
BoxDidResize(win,nil);
// InvalContent(win);
}
/**********************************************************************
* BoxPreviewProfile - what's the proper preview profile for this mailbox?
**********************************************************************/
PStr BoxPreviewProfile(PStr profileName,TOCHandle tocH,short previewTypeID)
{
ConConProH ccph = nil;
Str63 name;
uLong hash = 0;
ControlHandle cntl;
// In case we find nothing
if (profileName) *profileName = 0;
// load up the hash from the mailbox
if (previewTypeID==CONCON_PREVIEW_PROFILE)
hash = (*tocH)->singlePreviewProfileHash;
else if (previewTypeID==CONCON_MULTI_PREVIEW_PROFILE)
hash = (*tocH)->multiPreviewProfileHash;
// look for the hash, unless it's the magic "none" value
if (hash!=CONCON_NONE)
{
if (hash) ccph = ConConProFindHash(hash);
// if we didn't find it, or hash is zero (meaning use the default), look up the default
if (!ccph) ccph = ConConProFind(GetRString(name,previewTypeID));
// And copy the name we found, if any
if (ccph && profileName) PCopy(profileName,(*ccph)->name);
}
// Set the title of the bevel button
if (cntl=FindControlByRefCon((*tocH)->win,kConConProfRefCon))
{
if (profileName && *profileName) SetControlTitle(cntl,profileName);
else SetControlTitle(cntl,GetRString(name,CONCON_NONE));
}
return profileName;
}