eudora-mac/searchwin.c

1 line
143 KiB
C
Executable File

/* 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. */
#ifdef NEWFIND
#include "searchwin.h"
#include "regexp.h"
#define FILE_NUM 114
#pragma segment Find
/* Copyright (c) 1998 by QUALCOMM Incorporated */
// constants
enum
{
kMaxCriteria = 16, // should be enough
kWinHeaderHt = 99, // height of window header excluding criteria
kCriteriaHt = 27, // height of 1 criteria line
kMBHdrHt = 18, // height of mailbox header control
kFrontWinTicks = 60, // max ticks allowed when search window is in front, hog CPU time but exit for any events
kBgdWinTicks = 10, // max ticks allowed when search window is not in front
kInBGTicks = 4, // max ticks allowed when Eudora is in background
kChaseArrowTicks = 15, // call idle function for chase arrows 4 times a second
kDateWi = 100, // Date control width
kDateHt = 22, // Date control height (must be 20)
kIOBufSize = 32 K, // Size of bulk search I/O buffers
kSpecListType = 'Spec'
};
// criteria category menu items
enum { scAnywhere=1,scHeaders,scBody,scAttachNames,scDiv1,
scSummary,scStatus,scJunkScore,scPriority,scAttachCount,scLabel,scDate,scSize,scAge,
scPersonality,
scDiv2,scTo,scFrom,scSubject,scCC,scBCC,scAnyRecipient,scLimit };
// We save the criteria differently from how they appear in the menus.
// Map saved categories to the ones we use in the menu
char SavedCategory2MenuCategory[] =
{0,1,2,3,4,5,6,7,9,10,11,12,13,14,15,16,17,18,19,20,21,22,8};
#define SafeSavedCategory2MenuCategory(x) ((x)<scLimit ? SavedCategory2MenuCategory[x]:1)
// Map the categories in the menu to the ones we save
char MenuCategory2SavedCategory[] =
{0,1,2,3,4,5,6,7,22,8,9,10,11,12,13,14,15,16,17,18,19,20,21};
enum { ssText=1,ssDate,ssAge,ssNumber };
enum { sauMinutes=1,sauHours,sauDays,sauWeeks,sauMonths,sauYears };
enum { matchAll=1,matchAny };
enum { tabMailboxes=1,tabResults };
static short MenuToDateField[] = { minuteField,hourField,dayField,dayField,monthField,yearField };
// criteria relation menu items
enum { srIs=1,srIsNot,srGreater,srLess };
enum { strContains=1,strContainsWord,strDoesNotContain,strIs,strIsNot,strStarts,strEnds,strRegExp };
// category table indicates relation and specifier
enum
{
kNeedsSum=1<<0,
kNeedsMsg=1<<1,
};
enum
{
kSearchingLocal=1<<0,
kSearchingIMAP=1<<1
};
// Auditable Search Window controls
enum
{
kctlSearch=0,
kctlMore,
kctlFewer,
kctlTabs,
kctlSrchResults,
kctlCriteria,
kctlMatch
};
struct
{
short relation;
short specifier;
short flags;
Boolean proOnly;
} gCategoryTable[] =
{
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,false, // anywhere
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // headers
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // body
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // attachment names
0,0,0,0, // --------------------
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsSum,false, // summary
SRCH_EQUAL_MENU,FILT_STATUS_MENU,kNeedsSum,true, // status
SRCH_COMPARE_MENU,ssNumber,kNeedsSum,true, // junk score
SRCH_COMPARE_MENU,FILT_PRIOR_MENU,kNeedsSum,true, // priority
SRCH_COMPARE_MENU,ssNumber,kNeedsSum,true, // attachment count
SRCH_EQUAL_MENU,LABEL_HIER_MENU,kNeedsSum,true, // label
SRCH_DATE_COMPARE_MENU,ssDate,kNeedsSum,true, // date
SRCH_COMPARE_MENU,ssNumber,kNeedsSum,true, // size
SRCH_COMPARE_MENU,ssAge,kNeedsSum,true, // age
SRCH_EQUAL_MENU,PERS_HIER_MENU,kNeedsSum,true, // personality
0,0,0,0, // --------------------
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // to
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // from
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // subject
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // Cc
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true, // Bcc
SRCH_TEXT_COMPARE_MENU,ssText,kNeedsMsg,true // any recipient
};
// structs
typedef struct
{
short box;
short lastBoxSpec;
long message;
TOCHandle tocH; // current toc
Boolean opened; // we opened the TOC (we need to close it).
} SearchMarker;
typedef struct
{
ParmBlkPtr pb;
long offset;
long size;
Boolean free;
Boolean pending;
Handle buffer;
long bSize;
} BulkSearchBuffer;
typedef struct
{
Boolean didBulkSearch;
short refN;
long spot, len;
BulkSearchBuffer buf[2];
long fullOffset;
short bulkSearchCriteriaIdx; // which criterion are we currently searching
Byte bulkSearchCriteria[kMaxCriteria]; // which criteria need bulk searching
short nBulkSearchCriteria; // how many criteria need bulk searching
} BulkSearchInfo;
typedef struct
{
short day;
short month;
short year;
} SearchDate;
typedef struct
{
ControlHandle ctlCategory;
ControlHandle ctlRelation;
ControlHandle ctlSpecifier;
ControlHandle ctlPete; // for drawing pte
PETEHandle pte; // te box for the string we're looking for
StringHandle searchString; /* the string we're finding */
StringHandle utf8SearchString; // ditto, but utf8-ified
RegexpHandle regexp; // regular expression we are finding
Accumulator hits; // all the hits in the current mailbox
short category;
short relation;
long specifier;
long misc;
SearchDate date;
long zoneSecs;
Boolean useSenderZone;
Boolean fudge8bit;
} CriteriaInfo, *CriteriaPtr, **CriteriaHandle;
typedef struct
{
short criteriaCount; // number of criteria visible
CriteriaHandle hCriteria; // criteria controls
SearchMarker curr; /* place where searching is occurring */
ControlHandle ctlSearch,ctlChaseArrows,ctlCriteriaGroup;
ControlHandle ctlMore,ctlFewer;
ControlHandle ctlMailBox,ctlTabs;
ControlHandle ctlMatch,ctlSrchResults;
ControlHandle ctlFocus; // control with focus
short searchMode; // actively searching now?
// Boolean bulk; // do bulk search?
Boolean mailboxView;
Boolean bulkSearching; // bulk searching a mailbox file?
Boolean matchAny;
Boolean searchResults;
Boolean didSearch;
Boolean noBulkSearch;
short saveBotMargin;
short savePreviewHi;
short criteriaFlags;
ViewListPtr list; // list for selecting mailboxes
BoxCountHandle BoxCount; // list of selected mailboxes
BoxCountHandle imapBoxCount; // list of IMAP selected mailboxes for server search
BoxCountHandle redoBoxCount; // list of mailboxes we need to search again because they were busy they first time
uLong redoTicks; // try again when we get to this tick count
short **imapBoxSpecIdx; // mailbox spec index+1
Byte searchOrder[kMaxCriteria];
long nHits;
long chaseArrowTicks;
BulkSearchInfo bulkData;
ExpandInfo expandList;
FSSpec saveSpec;
Boolean setup; // has the search been setup?
#ifdef VALPHA
long nSearched,nTicks;
#endif
} SearchVars, *SearchPtr, **SearchHandle;
typedef struct
{
short category;
short relation;
long specifier;
long misc;
SearchDate date;
short stringID;
} CriteriaSave;
typedef struct
{
short version;
short criteriaCount;
short matchMode;
short tabMode;
long unused[2];
CriteriaSave criteria[];
} SearchSaveInfo;
#define READY(b) (!bulkData.buf[b].pending && !bulkData.buf[b].free)
#define kFoundIt ' fnd'
// functions
static void SearchDidResize(MyWindowPtr win, Rect *oldContR);
static void SearchUpdate(MyWindowPtr win);
static Boolean SearchClose(MyWindowPtr win);
static void SearchClick(MyWindowPtr win,EventRecord *event);
static void SearchCursor(Point mouse);
static void SearchActivate(MyWindowPtr win);
static Boolean SearchKey(MyWindowPtr win, EventRecord *event);
static Boolean SearchApp1(MyWindowPtr win, EventRecord *event);
static Boolean DoSearchKey(MyWindowPtr win, EventRecord *event);
static void SearchHelp(MyWindowPtr win,Point mouse);
static Boolean SearchMenuSelect(MyWindowPtr win, int menu, int item, short modifiers);
static void SearchGrow(MyWindowPtr win,Point *newSize);
static void SearchIdle(MyWindowPtr win);
static Boolean SearchPosition(Boolean save,MyWindowPtr win);
static void GetSearchInfo(MyWindowPtr win,TOCHandle *ptoc,SearchHandle *psh);
static void StopSearch(MyWindowPtr win);
static void SearchButton(MyWindowPtr win,ControlHandle button,long modifiers,short part);
static Boolean MessPreFind(SearchHandle sh,StringPtr s,short criteriaIdx);
static Boolean FindInHits(SearchHandle sh,AccuPtr a);
static void ZapSpecList(TOCHandle toc);
static Boolean SearchHasSelection(MyWindowPtr win);
static Boolean SearchScroll(MyWindowPtr win,short h,short v);
static OSErr SearchBulk(FSSpecPtr spec,SearchHandle sh);
static long SearchLVCallBack(ViewListPtr pView, VLCallbackMessage message, long data);
static ViewListPtr InitMailboxList(MyWindowPtr win,Boolean useMBSelection);
static void SizeMBList(MyWindowPtr win,SearchHandle sh);
static TOCHandle GetBoxTOC(SearchHandle sh,SearchMarker *which);
static TOCHandle GetSpecTOC(FSSpecPtr spec,Boolean *opened);
static void FinishedTOC(SearchMarker *which);
static OSErr GetSelectedBoxes(SearchPtr sp,TOCHandle tocH,Boolean bStartIMAPSearch);
static void AddCriteria(MyWindowPtr win,SearchHandle sh,Boolean defaultString);
static void RemoveCriteria(MyWindowPtr win,SearchHandle sh);
static Boolean ClickCriteriaButton(MyWindowPtr win,SearchHandle sh,ControlHandle cntl,short part);
static void DisposeSpecifier(MyWindowPtr win,SearchHandle sh,CriteriaInfo *pCriteria);
static void CreateTextSpecifier(MyWindowPtr win,CriteriaInfo *pCriteria,Boolean number);
static Boolean SetupSearchCriteria(MyWindowPtr win,SearchPtr sp);
static Boolean SearchMessage(MyWindowPtr win,MSumPtr sumP,SearchHandle sh,SearchMarker *curr,Boolean *stop,Boolean sumOnly);
static Boolean SearchText(StringPtr value,UPtr pText,long len,long offset,short relation);
static Boolean SearchString(StringPtr value,StringPtr s,short relation);
static Boolean SearchHeader(short header,Handle text,long size,StringPtr searchString,short relation);
static short ShortCompare(short value1,short value2);
static void SetSearchTopMargin(MyWindowPtr win,SearchHandle sh);
static Boolean MustSearchBody(short category);
static void InvalidBoxSize(MyWindowPtr win);
static void CheckChaseArrows(SearchHandle sh);
static void AutoSearch(MyWindowPtr srchWin, MyWindowPtr win, short searchMode);
static void UpdateBoxSize(MyWindowPtr win);
static void ProcessSearch(MyWindowPtr win,TOCHandle toc,SearchHandle sh);
static void DoBulkSearch(SearchHandle sh);
static void StopBulkSearch(SearchHandle sh);
static void NormalizeSearchString(PStr s);
static Boolean SearchSetControls(MyWindowPtr win, SearchHandle sh);
static Boolean GetBoxCountSpec(SearchHandle sh, short boxNum, FSSpecPtr spec);
static Boolean GetBoxCountSpecLo(BoxCountHandle boxList, short boxNum, FSSpecPtr spec);
static void DisplayMBName(SearchHandle sh, PStr name);
static AccuPtr FindHits(SearchHandle sh, short idx);
static void GetHits(SearchHandle sh,short idx,AccuPtr a);
static void SetHits(SearchHandle sh,short idx,AccuPtr a);
long SearchStrText(PStr s,Ptr text,long length,long offset);
static Boolean SearchFind(MyWindowPtr win,PStr what);
static IMAPSCHandle SetupIMAPSearchCriteria(SearchPtr sp);
static void ProcessIMAPSearch(MyWindowPtr win,TOCHandle toc,SearchHandle sh);
static OSErr AddSearchResult(SearchHandle sh, TOCHandle srchTOC, TOCHandle tocH, short sumNum, Boolean addSpec, short specIdx);
static short FindMBIdx(TOCHandle tocH,FSSpecPtr toSpec,Boolean allowCreate);
static void SearchSave(MyWindowPtr win,Boolean saveAs);
static void SelectMailbox(FSSpecPtr spec,ViewListPtr pView,Boolean folder,Boolean addToSelection);
static MSumPtr FindLinkSummary(TOCHandle srchTocH, TOCHandle fromTocH, long serialNum, short *realSum, short *virtualMBIdx);
static void RemoveFocus(MyWindowPtr win);
static void GetCriteriaString(StringPtr s,CriteriaPtr criteria);
static void SearchSetWTitle(MyWindowPtr win,SearchHandle sh);
static void GetMenuText(ControlHandle cntl,StringPtr s);
static void ResizeCriterion(MyWindowPtr win,SearchHandle sh,short i);
static void AddCriteriaText(SearchHandle sh, StringPtr sText);
static pascal void SearchTextDraw(ControlHandle cntl, SInt16 part);
static void ShowHideTabs(ControlRef ctlTabs,Boolean show);
static void EmbedSearchCriteriaControls(CriteriaInfo *criteria,SearchHandle sh);
Boolean SearchBoxesInclude(SearchHandle sh,TOCHandle tocH);
Boolean SearchIncremental(MyWindowPtr win,TOCHandle tocH,short sumNum);
long DateTimeDifference(DateTimeRec *date,DateTimeRec *currDate,long seconds,short units);
// globals
short gSearchWinCount;
long gLVFlags;
/************************************************************************
* SearchOpen - open a new search window
************************************************************************/
MyWindowPtr SearchOpen(short searchMode)
{
WindowPtr winWP;
MyWindowPtr win;
WindowPtr topWinWP = FindTopUserWindow();
MyWindowPtr topWin = GetWindowMyWindowPtr(topWinWP);
SearchVars vars;
OSErr err;
TOCHandle toc=nil;
TOCPtr ptoc;
Handle hSearchInfo=nil;
Rect r;
Boolean topMBWin = topWinWP && GetWindowKind(topWinWP) == MB_WIN;
Zero(vars);
if (!(win=GetNewMyWindow(SEARCH_WIND,nil,nil,BehindModal,true,true,MBOX_WIN)))
{err=MemError(); goto fail;}
winWP = GetMyWindowWindowPtr(win);
SetWinMinSize(win,-1,-1);
SetPort_(GetWindowPort(winWP));
ConfigFontSetup(win);
win->hPitch = FontWidth;
win->vPitch = FontLead+FontDescent;
// controls
SetTopMargin(win,kWinHeaderHt+kCriteriaHt);
vars.ctlSearch = CreateControl(win,win->topMarginCntl,SEARCH_BUTTON,kControlPushButtonProc,true);
OutlineControl(vars.ctlSearch,true);
SetRect(&r,0,0,100,30);
vars.ctlTabs = NewControl(winWP,&r,"\p",true,SEARCH_WIN_TABS,0,0,kControlTabSmallProc,0);
LetsGetSmall(vars.ctlTabs);
EmbedControl(vars.ctlTabs,win->topMarginCntl);
if (HasFeature (featureSearch)) {
vars.ctlMore = CreateControl(win,win->topMarginCntl,MORE_CHOICES,kControlPushButtonProc,true);
vars.ctlFewer = CreateControl(win,win->topMarginCntl,FEWER_CHOICES,kControlPushButtonProc,true);
vars.ctlMatch = CreateMenuControl(win,win->topMarginCntl,"\p",SRCH_AND_OR_MENU,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,0,false);
vars.ctlSrchResults = CreateControl(win,win->topMarginCntl,SEARCH_RESULTS,kControlCheckBoxProc,true);
}
vars.ctlCriteriaGroup = CreateControl(win,win->topMarginCntl,0,kControlGroupBoxTextTitleProc,false);
SetRect(&r,-64,-64,-48,-48);
if (HasFeature (featureSearch)) {
SetControlVisibility(vars.ctlFewer,false,true);
SetControlVisibility(vars.ctlMatch,false,true);
SetControlVisibility(vars.ctlSrchResults,false,true);
}
vars.ctlChaseArrows = CreateControl(win,win->topMarginCntl,0,kControlChasingArrowsProc,false);
HideControl(vars.ctlChaseArrows);
vars.ctlMailBox = CreateControl(win,win->topMarginCntl,0,kControlStaticTextProc|kControlPopupUseWFontVariant,false);
SetControlVisibility(vars.ctlMailBox,false,true);
vars.mailboxView = true;
vars.expandList.resID = kSearchExpandID;
if (topMBWin)
{
// Expand same mailfolders as in Mailboxes window
ExpandInfoPtr pExpList = MBGetExpList();
if (pExpList->hExpandList)
vars.expandList.hExpandList = DupHandle(pExpList->hExpandList);
}
vars.savePreviewHi = GetPrefLong(PREF_SEARCH_PREVIEW);
// set up data for virtual mailbox
// need a TOC
vars.hCriteria = NuHandle(0);
if (!(toc=NewZH(TOCType))) goto fail;
if (err = PtrToHand(&vars,&hSearchInfo,sizeof(vars))) goto fail;
ptoc = *toc;
ptoc->which = SEARCH_WIN;
ptoc->version = CURRENT_TOC_VERS;
ptoc->minorVersion = CURRENT_TOC_MINOR;
ptoc->nextSerialNum = 1;
ptoc->virtualTOC = true;
ptoc->mailbox.virtualMB.data = hSearchInfo;
ptoc->mailbox.virtualMB.type = kSearchMB;
ptoc->win = win;
(*toc)->previewHi = vars.savePreviewHi;
SetMyWindowPrivateData(win,(long)toc);
BoxGonnaShow(win);
BoxListFocus(toc,false); // kill the list focus so the search button works
(*toc)->searchFocus = true;
win->didResize = SearchDidResize;
win->close = SearchClose;
win->update = SearchUpdate;
win->position = SearchPosition;
win->click = SearchClick;
win->bgClick = SearchClick;
win->activate = SearchActivate;
win->help = SearchHelp;
win->menu = SearchMenuSelect;
win->key = SearchKey;
win->app1 = SearchApp1;
win->button = SearchButton;
win->scroll = SearchScroll;
win->selection = SearchHasSelection;
win->cursor = SearchCursor;
win->drag = nil; // BoxGonnaShow set this up. We don't want it.
win->idle = SearchIdle;
win->dontControl = true;
win->zoomSize = MaxSizeZoom;
win->find = SearchFind;
if (searchMode)
{
AddCriteria(win,(SearchHandle)hSearchInfo,true);
}
gLVFlags = searchMode == FIND_SEARCH_ALL_ITEM ? kfAutoSelectChild + kfAutoSelectAll : kfAutoSelectChild;
(*(SearchHandle)hSearchInfo)->list = InitMailboxList(win,topMBWin);
if (searchMode)
{
ShowMyWindow(winWP);
UserSelectWindow(winWP);
if (topWin)
AutoSearch(win,topWin,searchMode);
}
SearchSetControls(win, (SearchHandle)hSearchInfo);
gSearchWinCount++;
SearchSetWTitle(win,(SearchHandle)hSearchInfo);
SearchButton(win,vars.ctlTabs,0,0); // Make sure Mailboxes tab is set up correctly
return win;
fail:
if (win) CloseMyWindow(GetMyWindowWindowPtr(win));
ZapHandle(toc);
ZapHandle(hSearchInfo);
if (err) WarnUser(COULDNT_WIN,err);
return win;
}
/**********************************************************************
* CreateControl - create a control for the find window
**********************************************************************/
static void AutoSearch(MyWindowPtr srchWin, MyWindowPtr topWin, short searchMode)
{
Str255 s;
SearchHandle sh;
Boolean selectedText=false;
// get selected text in current window
*s=0;
if (topWin && topWin->pte && topWin->hasSelection)
SetFindString(s,topWin->pte);
GetSearchInfo(srchWin,nil,&sh);
if (*s && searchMode!=FIND_SEARCH_ITEM)
{
PeteSetString(s,(*(*sh)->hCriteria)[0].pte);
selectedText = true;
}
if (searchMode != FIND_SEARCH_ALL_ITEM)
{
// get mailbox or mailfolder of top window
TOCHandle tocH = nil;
FSSpec spec;
ViewListPtr pView = (*sh)->list;
switch (GetWindowKind(GetMyWindowWindowPtr(topWin)))
{
case COMP_WIN:
case MESS_WIN:
tocH = (*Win2MessH(topWin))->tocH;
break;
case MBOX_WIN:
case CBOX_WIN:
tocH = (TOCHandle)GetMyWindowPrivateData(topWin);
break;
}
if (!tocH)
return; // there is no mailbox
// select item in mailbox list
spec = GetMailboxSpec(tocH,-1);
SelectMailbox(&spec,pView,searchMode==FIND_SEARCH_FOLDER_ITEM,false);
}
if (selectedText && searchMode!=FIND_SEARCH_ITEM)
StartSearch(srchWin);
}
/************************************************************************
* SearchTextDraw - draw the search string
************************************************************************/
static pascal void SearchTextDraw(ControlHandle cntl, SInt16 part)
{
#pragma unused(part)
PETEHandle pte = (PETEHandle)GetControlReference(cntl);
PeteUpdate(pte);
}
/**********************************************************************
* ResizeCriterion - resize this criterion
**********************************************************************/
static void ResizeCriterion(MyWindowPtr win,SearchHandle sh,short i)
{
CriteriaInfo criteria=(*(*sh)->hCriteria)[i];
short top = INSET+9+i*kCriteriaHt;
short right;
Rect r,rCntl;
MoveMyCntl(criteria.ctlCategory,2*INSET,top+1,0,0);
GetControlBounds(criteria.ctlCategory,&rCntl);
right = rCntl.right;
MoveMyCntl(criteria.ctlRelation,right+INSET,top+1,0,0);
GetControlBounds(criteria.ctlRelation,&rCntl);
right = rCntl.right;
if (criteria.ctlPete)
{
SetRect(&r,right+INSET+4,top+3,criteria.ctlSpecifier ? right+INSET+60 : WindowWi(GetMyWindowWindowPtr(win))-3*INSET,top+GetLeading(SmallSysFontID(),SmallSysFontSize())+3);
PeteDidResize(criteria.pte,&r);
MoveMyCntl(criteria.ctlPete,r.left,r.top,r.right-r.left,r.bottom-r.top);
SetControlVisibility(criteria.ctlPete,true,false);
right = r.right;
}
if (criteria.ctlSpecifier)
{
short specTop = top+1;
switch (criteria.category)
{
case scStatus:
case scPriority:
case scLabel:
case scDate:
specTop -= 2;
break;
}
MoveMyCntl(criteria.ctlSpecifier,right+INSET,specTop,0,0);
}
}
/**********************************************************************
* SearchDidResize - resize this search window
**********************************************************************/
static void SearchDidResize(MyWindowPtr win, Rect *oldContR)
{
SearchHandle sh;
ControlHandle ctl;
short wi,v1,v3,right,left,moreRight;
short winWidth = WindowWi(GetMyWindowWindowPtr(win));
short criteriaHt;
short groupRight;
short i;
TOCHandle tocH;
Rect rCntl;
GetSearchInfo(win,&tocH,&sh);
SetSearchTopMargin(win,sh);
// criteria group box
ctl = (*sh)->ctlCriteriaGroup;
criteriaHt = INSET+(*sh)->criteriaCount*kCriteriaHt+4;
groupRight = winWidth-2*INSET;
MoveMyCntl(ctl,INSET,INSET,groupRight,criteriaHt);
SetControlVisibility(ctl,true,true); // it's disappearing for some reason
// criteria
for(i=0;i<(*sh)->criteriaCount;i++)
ResizeCriterion(win,sh,i);
v1 = INSET+(*sh)->criteriaCount*kCriteriaHt+16;
// "search" button
ctl = (*sh)->ctlSearch;
left = winWidth-ControlWi(ctl)-2*INSET;
MoveMyCntl(ctl,left,v1+2,0,0);
// chasing arrows
left = left - INSET - 16;
MoveMyCntl((*sh)->ctlChaseArrows,left,v1+2,16,16);
// more, fewer buttons, etc
if (HasFeature (featureSearch)) {
ctl = (*sh)->ctlMore;
MoveMyCntl(ctl,INSET,v1,0,0);
GetControlBounds(ctl,&rCntl);
right = moreRight = rCntl.right;
ctl = (*sh)->ctlFewer;
MoveMyCntl(ctl,right+INSET,v1,0,0);
GetControlBounds(ctl,&rCntl);
right = rCntl.right;
ctl = (*sh)->ctlMatch;
MoveMyCntl(ctl,right+INSET,v1+1,0,0);
GetControlBounds(ctl,&rCntl);
right = (*sh)->criteriaCount > 1 ? rCntl.right : moreRight;
ctl = (*sh)->ctlSrchResults;
MoveMyCntl(ctl,right+INSET,v1,0,0);
v3 = v1+28;
}
else
v3 = v1+12;
// mailbox name
wi = winWidth - 2*INSET;
if (wi < 100) wi = 0; // Make it invisible if too narrowl
ctl = (*sh)->ctlMailBox;
MoveMyCntl(ctl,INSET+3,v3,wi,24);
SetControlVisibility(ctl,(*sh)->searchMode,true);
// tabs
MoveMyCntl((*sh)->ctlTabs,-3,v3,winWidth+6,30);
SetControlVisibility((*sh)->ctlTabs,!(*sh)->searchMode,true);
if ((*sh)->mailboxView)
// Mailbox list
SizeMBList(win,sh);
else
BoxDidResize(win,oldContR);
SetPrefLong(PREF_SEARCH_PREVIEW,(*tocH)->previewHi);
}
/**********************************************************************
* SearchClose - close this search window
**********************************************************************/
static Boolean SearchClose(MyWindowPtr win)
{
TOCHandle toc;
SearchHandle sh;
WindowPtr thisWin;
PushGWorld();
SetPort_(GetMyWindowCGrafPtr(win));
StopSearch(win);
GetSearchInfo(win,&toc,&sh);
while ((*sh)->criteriaCount)
RemoveCriteria(win,sh);
ZapHandle((*sh)->hCriteria);
LDRef(sh);
// Save expanded folder list
if (!(*sh)->didSearch)
// Don't save changes. Just dispose of data
(*sh)->expandList.fExpandListChanged = false;
SaveExpandedFolderList(&(*sh)->expandList);
// Remove references to this window from any
// messages opened from this window
for (thisWin=FrontWindow_();thisWin;thisWin=GetNextWindow(thisWin))
if (GetWindowKind(thisWin)==COMP_WIN || GetWindowKind(thisWin)==MESS_WIN)
{
MessHandle messH = WinPtr2MessH(thisWin);
if ((*messH)->openedFromTocH == toc)
(*messH)->openedFromTocH = (*messH)->tocH;
}
ZapSpecList(toc);
ZapHandle(toc);
if ((*sh)->list)
{
LVDispose((*sh)->list);
ZapPtr((*sh)->list);
}
ZapHandle((*sh)->BoxCount);
ZapHandle((*sh)->imapBoxCount);
ZapHandle((*sh)->redoBoxCount);
ZapHandle((*sh)->imapBoxSpecIdx);
ZapHandle(sh);
win->selection = nil;
gSearchWinCount--;
PopGWorld();
return true;
}
/**********************************************************************
* SearchUpdate - update search window
**********************************************************************/
static void SearchUpdate(MyWindowPtr win)
{
CGrafPtr winPort = GetMyWindowCGrafPtr (win);
SearchHandle sh;
GetSearchInfo(win,nil,&sh);
if ((*sh)->mailboxView)
LVDraw((*sh)->list,nil,true,false);
else
BoxUpdate(win);
// these guys tend to get disabled
if (HasFeature (featureMultiplePersonalities))
EnableItem(GetMHandle(PERS_HIER_MENU),0);
EnableItem(GetMHandle(LABEL_HIER_MENU),0);
}
/**********************************************************************
* SearchClick - handle click in search window
**********************************************************************/
static void SearchClick(MyWindowPtr win,EventRecord *event)
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
Point pt;
// PETEHandle pte;
SearchHandle sh;
ControlHandle cntl;
TOCHandle tocH;
Rect rCntl;
GetSearchInfo(win,&tocH,&sh);
pt = event->where;
GlobalToLocal(&pt);
if ((*sh)->ctlFocus && !PtInRect(pt,GetControlBounds((*sh)->ctlFocus,&rCntl)))
{
SetKeyboardFocus(winWP,(*sh)->ctlFocus,kControlFocusNoPart);
(*sh)->ctlFocus = nil;
SearchSetWTitle(win,sh);
}
if (pt.v > win->topMargin)
{
if ((*sh)->mailboxView)
{
RemoveFocus(win);
LVClick((*sh)->list,event);
}
else
BoxClick(win,event);
}
else
{
short i;
if (!win->isActive) {SelectWindow_(winWP);}
BoxListFocus(tocH,false);
(*tocH)->searchFocus = true;
for(i=0;i<(*sh)->criteriaCount;i++)
{
PETEHandle pte;
if ((pte=(*(*sh)->hCriteria)[i].pte) && PtInPETE(pt,pte))
{
StopSearch(win);
PeteEditWFocus(win,pte,peeEvent,(void*)event,nil);
return;
}
}
if (FindControl(pt,winWP,&cntl))
{
// It's a control. If a menu control, make sure it's enabled
MenuHandle mh;
if (mh = GetPopupMenuHandle(cntl))
EnableItem(mh,0);
// ignore hits on the server status column header like in a regular mailbox -jdboyd 03/01/01
if ((cntl==FindControlByRefCon(win,'wide'+blServer)) && PtInControl(pt,cntl));
else HandleControl(pt,win);
}
}
SearchSetControls(win, sh);
}
/************************************************************************
* SearchHasSelection - is there a selection?
************************************************************************/
static Boolean SearchHasSelection(MyWindowPtr win)
{
SearchHandle sh;
long start,stop;
GetSearchInfo(win,nil,&sh);
if (win->pte && !PeteGetTextAndSelection(win->pte,nil,&start,&stop))
return(stop!=start);
else if ((*sh)->mailboxView)
return (*sh)->list && LVCountSelection((*sh)->list) ? true : false;
else
return BoxHasSelection(win);
}
/**********************************************************************
* SearchCursor - adjust the cursor
**********************************************************************/
static void SearchCursor(Point mouse)
{
SearchHandle sh;
MyWindowPtr win=GetWindowMyWindowPtr(FrontWindow_());
GetSearchInfo(win,nil,&sh);
if (!(*sh)->mailboxView)
BoxCursor(mouse);
else if (!PeteCursorList(win->pteList,mouse))
SetMyCursor(arrowCursor);
}
/**********************************************************************
* SearchActivate - activate/deactivate search window
**********************************************************************/
static void SearchActivate(MyWindowPtr win)
{
SearchHandle sh;
GetSearchInfo(win,nil,&sh);
if ((*sh)->mailboxView)
LVActivate((*sh)->list, win->isActive);
else
BoxActivate(win);
}
/**********************************************************************
* SearchScroll - do scrolling for search window
**********************************************************************/
static Boolean SearchScroll(MyWindowPtr win,short h,short v)
{
SearchHandle sh;
GetSearchInfo(win,nil,&sh);
if (!(*sh)->mailboxView)
BoxScroll(win,h,v);
return(True);
}
/**********************************************************************
* SearchApp1 - process scrolling keys
**********************************************************************/
static Boolean SearchApp1(MyWindowPtr win, EventRecord *event)
{
SearchHandle sh;
TOCHandle tocH;
GetSearchInfo(win,&tocH,&sh);
if ((*sh)->mailboxView)
return LVKey((*sh)->list,event);
else if ((*tocH)->previewID && (*tocH)->previewPTE)
{
event->what = keyDown;
PeteEdit(win,(*tocH)->previewPTE,peeEvent,(void*)event);
return true;
}
else
{
// Send to the mailbox list
DoApp1NoPTE(win,event);
return true;
}
}
/**********************************************************************
* SearchKey - process interesting keys
**********************************************************************/
static Boolean SearchKey(MyWindowPtr win, EventRecord *event)
{
SearchHandle sh;
Boolean result;
GetSearchInfo(win,nil,&sh);
result = DoSearchKey(win,event);
SearchSetControls(win, sh);
return result;
}
/**********************************************************************
* SearchKey - process interesting keys
**********************************************************************/
static Boolean DoSearchKey(MyWindowPtr win, EventRecord *event)
{
short key = event->message & 0xff;
SearchHandle sh;
TOCHandle tocH;
ControlHandle cntl;
GetSearchInfo(win,&tocH,&sh);
if (event->modifiers&cmdKey && IsCmdChar(event,'.') || // command-period
(event->message&charCodeMask)==escChar)
{
#ifdef SPEECH_ENABLED
if (CancelSpeech ())
return (false);
#endif
// stop searching
StopSearch(win);
return true;
}
if (key=='\t')
{
// Who would believe this could be so complicated?
typedef enum {
stabOnly=0, // there is only one pte in the search criteria, and it's focussed
stabFrst, // there is more than one pte in the search criteria, and the first one is focussed
stabMddl, // there are more than two pte's in the search criteria, and a middle one is focussed
stabLast, // there is more than one pte in the search criteria, and the last one is focussed
stabNone, // there are no pte's in the search criteria, but the criteria nonetheless have focus
stabSumm, // the focus is in the list of mailbox summaries
stabPrev, // the focus is in the preview pane
stabBxes, // the focus is in the mailboxes list
stabLimit
} SearchSelectTypeEnum;
SearchSelectTypeEnum currentSelect, nextSelect;
static SearchSelectTypeEnum STabStateTable[2][stabLimit] = {
// stabOnly stabFrst stabMddl stabLast stabNone stabSumm stabPrev stabBxes
/* forward */ stabSumm, stabMddl, stabMddl, stabSumm, stabSumm, stabPrev, stabFrst, stabFrst,
/* backwrd */ stabPrev, stabPrev, stabMddl, stabMddl, stabSumm, stabLast, stabSumm, stabLast
};
PETEHandle nextPTE;
PETEHandle origPTE = win->pte;
Boolean backward = (event->modifiers&shiftKey)!=0;
// Figure out where we are
if (win->pte)
{
nextPTE = PeteNext(win->pte);
if (win->pte==(*tocH)->previewPTE) currentSelect = stabPrev;
else if (nextPTE==(*tocH)->previewPTE || nextPTE==nil)
currentSelect = win->pte==win->pteList ? stabOnly : stabLast;
else
currentSelect = win->pte==win->pteList ? stabFrst : stabMddl;
}
else if ((*tocH)->searchFocus)
currentSelect = stabNone;
else if ((*sh)->mailboxView)
currentSelect = stabBxes;
else
currentSelect = stabSumm;
// First crack at where we should go
nextSelect = STabStateTable[backward][currentSelect];
// Fine-tuning
retune:
switch (nextSelect)
{
case stabSumm:
if ((*sh)->mailboxView)
nextSelect = stabBxes; // mailboxes list is showing, go there
break;
case stabPrev:
if ((*sh)->mailboxView)
nextSelect = stabBxes; // mailboxes list is showing, go there
else if (!(*tocH)->previewID)
{
// no preview, go elsewhere
if (backward)
{
nextSelect = stabSumm; // backing up from the top is the summary list
}
else
{
nextSelect = stabFrst; // foward from the summaries is the top preview edit field
goto retune; // but it might not be there, so try again
}
}
break;
case stabFrst:
case stabMddl:
case stabLast:
if (win->pteList==(*tocH)->previewPTE)
nextSelect = stabNone; // if no edit field or only edit is preview, just focus on criteria, not pte at all
break;
}
// Ok, things are pretty simple now
// First, we defocus
switch (currentSelect)
{
case stabOnly:
case stabFrst:
case stabMddl:
case stabLast:
case stabNone:
case stabPrev:
PeteFocus(win,nil,true); // no-op for stabNone, but who cares?
(*tocH)->searchFocus = false; // no-op for stabPrev, but who cares?
break;
case stabSumm:
BoxListFocus(tocH,false);
break;
case stabBxes:
// nothing special here
break;
default:
ASSERT(0); // naughty processor shdn't be here
break;
}
// Then, we focus
switch (nextSelect)
{
case stabOnly:
case stabFrst:
PeteFocus(win,win->pteList,true);
(*tocH)->searchFocus = true;
break;
case stabMddl:
nextPTE = backward ? PetePrevious(win->pteList,origPTE) : PeteNext(origPTE);
PeteFocus(win,nextPTE,true);
(*tocH)->searchFocus = true;
break;
case stabLast:
// this one actually requires some calculation.
for (origPTE=win->pteList;;origPTE=nextPTE)
{
nextPTE = PeteNext(origPTE);
if (nextPTE==nil || nextPTE==(*tocH)->previewPTE) break;
}
PeteFocus(win,origPTE,true);
(*tocH)->searchFocus = true;
break;
case stabNone:
(*tocH)->searchFocus = true;
break;
case stabPrev:
// Sigh. First, we must focus on the list
BoxListFocus(tocH,true);
// Then we send it a tab to focus on the preview
BoxKey(win,event);
break;
case stabSumm:
if (FirstMsgSelected(tocH)==-1) BoxInitialSelection(tocH);
BoxListFocus(tocH,true);
break;
case stabBxes:
// nothing special here
break;
default:
ASSERT(0); // naughty processor shdn't be here
break;
}
if (win->pte && win->pte!=(*tocH)->previewPTE) PeteSelectAll(win,win->pte);
// There, that was easy. (sarcasm)
return true;
}
if (key==returnChar || key==enterChar)
{
if ((*sh)->searchMode)
{
// Stop if we're already searching
StopSearch(win);
return true;
}
// Start search if in mailbox view or if focus not in results pane or not in preview pane or summary list
if ((*sh)->mailboxView || win->pte && (win->pte != (*tocH)->previewPTE) || !(*tocH)->listFocus)
{
StartSearch(win);
return true;
}
}
if (!(event->modifiers&cmdKey) && !GetKeyboardFocus(GetMyWindowWindowPtr(win),&cntl) && cntl)
{
// send key to control
HandleControlKey(cntl,(event->message&keyCodeMask)>>8,event->message&charCodeMask,event->modifiers);
return true;
}
if (win->pte)
{
if ((win->pte != (*tocH)->previewPTE) && !(event->modifiers&cmdKey))
StopSearch(win);
if (!(event->modifiers&cmdKey))
{
if ((win->pte == (*tocH)->previewPTE) && DirtyKey(event->message))
AlertStr(READ_ONLY_ALRT, Stop, nil);
else
{
PeteKey(win,win->pte,event);
SearchSetControls(win, sh);
}
return true;
}
else
return false;
}
if ((*sh)->mailboxView)
return LVKey((*sh)->list,event);
else
return BoxKey(win,event);
}
enum
{
kHelpStrings,
kHelpSearch,
kHelpStop,
kHelpMore,
kHelpFewer,
kHelpMatch,
kHelpSearchResults,
kHelpMailboxesTab,
kHelpResultsTab,
kHelpCategory,
kHelpVerb,
kHelpWhatMenu,
kHelpWhatText,
kHelpMBList,
kHelpCount
};
/**********************************************************************
* SearchHelp - provide help for the search window
**********************************************************************/
static void SearchHelp(MyWindowPtr win,Point mouse)
{
SearchHandle sh;
ControlHandle cntl;
short part;
short helpID = 0;
SearchPtr sp;
Rect r;
GetSearchInfo(win,nil,&sh);
sp = LDRef(sh);
if (part = FindControl(mouse,GetMyWindowWindowPtr(win),&cntl))
{
GetControlBounds(cntl,&r);
if (cntl==sp->ctlSearch)
helpID = sp->searchMode ? kHelpStop : kHelpSearch;
else if (cntl==sp->ctlMore)
helpID = kHelpMore;
else if (cntl==sp->ctlFewer)
helpID = kHelpFewer;
else if (cntl==sp->ctlMatch)
helpID = kHelpMatch;
else if (cntl==sp->ctlSrchResults)
helpID = kHelpSearchResults;
else if (cntl==sp->ctlTabs)
helpID = part == tabMailboxes ? kHelpMailboxesTab : kHelpResultsTab;
else if (GetControlReference(cntl)==kBoxSizeRefCon)
helpID = kHelpCount;
else
{
// see if it's in a criterion
short i;
for(i=0;i<sp->criteriaCount;i++)
{
CriteriaPtr pc=&(*sp->hCriteria)[i];
if (cntl==pc->ctlCategory)
{ helpID = kHelpCategory; break; }
else if (cntl==pc->ctlRelation)
{ helpID = kHelpVerb; break; }
else if (cntl==pc->ctlSpecifier)
{ helpID = kHelpWhatMenu; break; }
else if (cntl==pc->ctlPete)
{ helpID = kHelpWhatText; break; }
}
}
}
if (!helpID)
{
if (sp->mailboxView)
{
if (PtInRect(mouse,&sp->list->bounds))
{
r = sp->list->bounds;
helpID = kHelpMBList;
}
}
else
// let boxhelp handle it
BoxHelp(win, mouse);
}
if (helpID)
MyBalloon(&r,100,0,helpID+SEARCH_HELP_STRN,0,nil);
UL(sh);
}
/**********************************************************************
* SearchMenuSelect - handle a menu selection
**********************************************************************/
static Boolean SearchMenuSelect(MyWindowPtr win, int menu, int item, short modifiers)
{
SearchHandle sh;
GetSearchInfo(win,nil,&sh);
switch (menu)
{
case FILE_MENU:
switch (item)
{
case FILE_SAVE_ITEM:
SearchSave(win,false);
return true;
break;
case FILE_SAVE_AS_ITEM:
SearchSave(win,true);
return true;
break;
}
}
if ((*sh)->mailboxView)
{
if ((*sh)->list)
{
switch (menu)
{
case FILE_MENU:
if (item==FILE_OPENSEL_ITEM)
{
MBListOpen((*sh)->list);
return(True);
}
break;
case EDIT_MENU:
if (!win->pte)
{
switch(item)
{
case EDIT_SELECT_ITEM:
if (LVSelectAll((*sh)->list))
return(true);
break;
case EDIT_COPY_ITEM:
LVCopy((*sh)->list);
return true;
}
}
break;
}
}
}
else
return BoxMenu(win,menu,item,modifiers);
return false;
}
/**********************************************************************
* SearchFind - find in the Search window
**********************************************************************/
static Boolean SearchFind(MyWindowPtr win,PStr what)
{
SearchHandle sh;
GetSearchInfo(win,nil,&sh);
if ((*sh)->mailboxView)
// mailbox view
return FindListView(win,(*sh)->list,what);
else
// results view
return BoxFind(win,what);
}
/**********************************************************************
* IsSearchWindow - is the window a Search window?
**********************************************************************/
Boolean IsSearchWindow(WindowPtr theWindow)
{
TOCHandle tocH;
return (IsKnownWindowMyWindow(theWindow) && GetWindowKind(theWindow)==MBOX_WIN &&
(tocH = (TOCHandle)(GetWindowPrivateData(theWindow))) &&
(*tocH)->virtualTOC &&
(*tocH)->mailbox.virtualMB.type == kSearchMB);
}
/**********************************************************************
* GetSearchWinSpec - get saved spec for search window
**********************************************************************/
Boolean GetSearchWinSpec(WindowPtr winWP,FSSpecPtr spec)
{
SearchHandle sh;
if (IsSearchWindow(winWP))
{
MyWindowPtr win = GetWindowMyWindowPtr (winWP);
GetSearchInfo(win,nil,&sh);
if ((*sh)->saveSpec.vRefNum)
{
*spec = (*sh)->saveSpec;
return true;
}
}
return false;
}
/**********************************************************************
* SearchNewFindString - there's a new Find string
**********************************************************************/
void SearchNewFindStringLo(PStr what,Boolean withPrejuidice)
{
WindowPtr winWP;
for (winWP=FrontWindow();winWP;winWP = GetNextWindow (winWP))
if (IsWindowVisible(winWP) && IsSearchWindow(winWP))
{
SearchHandle sh;
PETEHandle pte;
GetSearchInfo(GetWindowMyWindowPtr (winWP),nil,&sh);
// Replace search string if only one criterion, it has a text field,
// and the field is empty
if ((*sh)->criteriaCount == 1 &&
(pte=(*(*sh)->hCriteria)[0].pte) &&
(withPrejuidice || !PeteLen(pte)))
{
PeteSetString(what,pte);
}
break;
}
}
/**********************************************************************
* SearchSetControls - set status of controls
**********************************************************************/
static Boolean SearchSetControls(MyWindowPtr win, SearchHandle sh)
{
Boolean active = false;
ControlHandle ctl;
// Determine if search button should be dimmed
if ((*sh)->searchMode)
active = true;
{
// search through criteria for something we can search on
short i;
PETEHandle pte;
for(i=0;i<(*sh)->criteriaCount;i++)
{
pte = (*(*sh)->hCriteria)[i].pte;
if (!PeteIsValid(pte) || PeteLen(pte))
{
// If critiera doesn't have a text field or
// there is something in the text field, don't dim
active = true;
break;
}
}
}
ctl = (*sh)->ctlSearch;
if (win->isActive && IsControlActive(ctl) != active)
{
if (active) ActivateControl(ctl);
else DeactivateControl(ctl);
}
win->userSave = (*sh)->saveSpec.vRefNum!=0;
return active;
}
/**********************************************************************
* GetSearchInfo - get search info from window
**********************************************************************/
static void GetSearchInfo(MyWindowPtr win,TOCHandle *ptoc,SearchHandle *psh)
{
TOCHandle toc=(TOCHandle)GetMyWindowPrivateData(win);
SearchHandle sh = (SearchHandle)(*toc)->mailbox.virtualMB.data;
if (ptoc) *ptoc = toc;
if (psh) *psh = sh;
}
/**********************************************************************
* GetSearchTOC - get the TOC of a search window
**********************************************************************/
void GetSearchTOC(MyWindowPtr win,TOCHandle *ptoc)
{
TOCHandle toc=(TOCHandle)GetMyWindowPrivateData(win);
SearchHandle sh = (SearchHandle)(*toc)->mailbox.virtualMB.data;
if (ptoc) *ptoc = toc;
}
/**********************************************************************
* StartSearch - start the search
**********************************************************************/
void StartSearch(MyWindowPtr win)
{
SearchHandle sh;
SearchPtr sp;
TOCHandle toc;
short n;
Str32 s;
ActiveSearchCount++;
Log(LOG_SEARCH,"\pSearch started");
GetSearchInfo(win,&toc,&sh);
SearchSetWTitle(win,sh);
if (!SearchSetControls(win, sh))
// Can't search. Probably no mailboxes selected
return;
sp = LDRef(sh);
// set up search parameters
Zero(sp->curr);
sp->curr.lastBoxSpec = -2;
sp->curr.box = -1;
sp->searchMode = kSearchingLocal;
sp->didSearch = true;
sp->nHits = 0;
sp->bulkData.didBulkSearch = false;
if (!SetupSearchCriteria(win,sp)) return; // setup failed
if (GetSelectedBoxes(sp,toc,true)) return; // aborted
// remove any current hits
if (!(*sh)->searchResults)
{
n=(*toc)->count;
if (n)
UpdateBoxSize(win);
while (n--)
DeleteSum(toc,n);
ZapSpecList(toc);
}
if ((*sh)->mailboxView)
{
// switch back to results view
SetControlValue((*sh)->ctlTabs,tabResults);
SearchButton(win,(*sh)->ctlTabs,0,0);
}
ShowHideTabs(sp->ctlTabs,false);
ShowControl(sp->ctlChaseArrows);
ShowControl(sp->ctlMailBox);
SetControlTitle(sp->ctlSearch,GetRString(s,STOP_BUTTON));
#ifdef VALPHA
sp->nSearched = sp->nTicks = 0;
#endif
UL(sh);
win->isDirty = true;
}
/**********************************************************************
* SetupSearchCriteria - setup search criteria
**********************************************************************/
static Boolean SetupSearchCriteria(MyWindowPtr win,SearchPtr sp)
{
short i;
Str255 s;
Str255 cs;
long junk;
LongDateRec date;
PersHandle pers;
short orderIdx;
if (HasFeature (featureSearch)) {
sp->matchAny = GetControlValue(sp->ctlMatch) == matchAny;
sp->searchResults = GetControlValue(sp->ctlSrchResults) > 0;
}
sp->criteriaFlags = 0;
sp->noBulkSearch = false;
for(i=0;i<sp->criteriaCount;i++)
{
CriteriaInfo criteria=(*sp->hCriteria)[i];
short value;
criteria.specifier = 0;
criteria.relation = GetControlValue(criteria.ctlRelation);
if (criteria.ctlSpecifier)
value = GetControlValue(criteria.ctlSpecifier);
if (HasFeature (featureSearch) || !gCategoryTable[criteria.category-1].proOnly) {
switch (criteria.category)
{
case scAnywhere:
case scHeaders:
case scBody:
case scAttachNames:
case scSummary:
case scTo:
case scFrom:
case scSubject:
case scCC:
case scBCC:
case scAnyRecipient:
// get text
PeteString(s,criteria.pte);
ZapHandle(criteria.searchString);
criteria.searchString = criteria.relation != strRegExp ? NewString(s) : (StringHandle)regcomp(PtoCcpy(cs,s));
criteria.fudge8bit = criteria.relation != strRegExp && MixedHighBits(s+1,*s);
if (!criteria.searchString) return false;
HLock((Handle)criteria.searchString);
sp->noBulkSearch = sp->noBulkSearch || PrefIsSet(PREF_NO_BULK_SEARCH) && AllHighBits(s+1,*s);
// Should we set up the default find string?
if (i==0 && criteria.relation != strDoesNotContain && criteria.relation != strIsNot)
FindEnterSelection(s,false);
// Bug 4262 came from doing this before entering the find string...
if (criteria.relation != strRegExp && AnyHighBits(s+1,*s))
{
RomanToUTF8(s,sizeof(s));
criteria.utf8SearchString = NewString(s);
}
break;
case scStatus:
criteria.specifier = Item2Status(value);
break;
case scPriority:
criteria.specifier = Display2Prior(value);
break;
case scAge:
PeteString(s,criteria.pte);
StringToNum(s,&criteria.misc);
criteria.specifier = value;
criteria.zoneSecs = ZoneSecs();
break;
case scAttachCount:
case scSize:
case scJunkScore:
PeteString(s,criteria.pte);
StringToNum(s,&junk);
criteria.specifier = junk;
break;
case scLabel:
criteria.specifier = Menu2Label(value);
break;
case scDate:
GetControlData(criteria.ctlSpecifier,0,
kControlClockLongDateTag,sizeof(date),(Ptr)&date,&junk);
criteria.date.day = date.ld.day;
criteria.date.month = date.ld.month;
criteria.date.year = date.ld.year;
criteria.zoneSecs = ZoneSecs();
criteria.useSenderZone = !PrefIsSet(PREF_LOCAL_DATE);
break;
case scPersonality:
if (HasFeature (featureMultiplePersonalities))
if (pers = value==1 ? PersList : Index2Pers(value-2))
criteria.specifier = (*pers)->persId;
break;
}
(*sp->hCriteria)[i] = criteria;
sp->criteriaFlags |= gCategoryTable[criteria.category-1].flags;
}
// (jp) 1-18-00 Support for light downgrade
if (gCategoryTable[criteria.category-1].proOnly)
UseFeature (featureSearch);
}
// Set up search order. Search those that require searching through the message last.
orderIdx=0;
for(i=0;i<sp->criteriaCount;i++)
if (!MustSearchBody((*sp->hCriteria)[i].category))
sp->searchOrder[orderIdx++] = i;
for(i=0;i<sp->criteriaCount;i++)
if (MustSearchBody((*sp->hCriteria)[i].category))
sp->searchOrder[orderIdx++] = i;
sp->setup = true;
return true;
}
/**********************************************************************
* MustSearchBody - does this criteria category require searching the message body
**********************************************************************/
static Boolean MustSearchBody(short category)
{
return (gCategoryTable[category-1].flags & kNeedsMsg) != 0;
}
/**********************************************************************
* GetSelectedBoxes - build list of selected mailboxes
**********************************************************************/
static OSErr GetSelectedBoxes(SearchPtr sp,TOCHandle tocH,Boolean bStartIMAPSearch)
{
ViewListPtr pView;
Boolean haveIMAP = false;
OSErr err=noErr;
ZapHandle(sp->BoxCount);
ZapHandle(sp->imapBoxCount);
ZapHandle(sp->redoBoxCount);
if (pView=sp->list)
{
BoxCountHandle saveBoxCount,popBoxCount,imapBoxCount=nil;
short i;
short n;
n=LVCountSelection(pView);
if (!n)
{
// No mailboxes are selected. Select ALL of them.
LVSelectAll(pView);
n=LVCountSelection(pView);
}
saveBoxCount = BoxCount;
popBoxCount = NuHandle(0);
// build our own box count
for(i=1;i<=n;i++)
{
VLNodeInfo itemInfo;
// get next selected item
if (!LVGetItem(pView,i,&itemInfo,true)) break;
BoxCount = popBoxCount;
if (IsIMAPBox(&itemInfo))
{
if (sp->criteriaFlags & kNeedsMsg)
{
// Need to do an IMAP on server search
if (!imapBoxCount)
imapBoxCount=NuHandle(0);
BoxCount = imapBoxCount;
}
haveIMAP = true;
}
if ((itemInfo.nodeID == MAILBOX_MENU || itemInfo.nodeID ==kEudoraFolder) &&
itemInfo.refCon == MailRoot.dirId && itemInfo.isParent && itemInfo.isCollapsed)
{
// Eudora folder
// add entire Eudora folder if collapsed
AddBoxCountItem(MAILBOX_IN_ITEM,MailRoot.vRef,MailRoot.dirId);
AddBoxCountItem(MAILBOX_OUT_ITEM,MailRoot.vRef,MailRoot.dirId);
AddBoxCountItem(MAILBOX_JUNK_ITEM,MailRoot.vRef,MailRoot.dirId);
AddBoxCountItem(MAILBOX_TRASH_ITEM,MailRoot.vRef,MailRoot.dirId);
AddBoxCountMenu(MAILBOX_MENU,MAILBOX_FIRST_USER_ITEM,MailRoot.vRef,MailRoot.dirId,false);
}
else
{
MenuHandle mh;
short menuItem;
short menuID;
short vRef;
long dirId;
if (itemInfo.nodeID==kEudoraFolder) continue; // Ignore Eudora Folder
menuID = itemInfo.nodeID==kIMAPFolder ? MAILBOX_MENU : itemInfo.nodeID;
mh = GetMHandle(menuID);
menuItem = FindItemByName(mh,itemInfo.name);
if (!menuItem) continue; // shouldn't happen
if (itemInfo.isParent)
{
// mailbox folder
if (haveIMAP && itemInfo.iconID == IMAP_MAILBOX_FILE_ICON)
{
// IMAP hybrid mailbox/mailfolder. Need to add it
MenuID2VD(menuID,&vRef,&dirId);
AddBoxCountItem(menuItem,vRef,dirId);
}
if (itemInfo.isCollapsed)
{
// add entire folder contents if collapsed
menuID = SubmenuId(mh,menuItem);
MenuID2VD(menuID,&vRef,&dirId);
AddBoxCountMenu(menuID,(menuID==MAILBOX_MENU) ? 1: MAILBOX_FIRST_USER_ITEM-MAILBOX_BAR1_ITEM,vRef,dirId,false);
}
}
else
{
// mailbox
MenuID2VD(menuID,&vRef,&dirId);
AddBoxCountItem(menuItem,vRef,dirId);
}
}
}
sp->BoxCount = popBoxCount;
sp->imapBoxCount = imapBoxCount;
BoxCount = saveBoxCount;
if (haveIMAP && bStartIMAPSearch)
{
if (sp->criteriaFlags & kNeedsSum && !(sp->criteriaFlags & kNeedsMsg))
{
// Warn user we are only searching local cache for summary items
if (ComposeStdAlert(Caution,IMAP_SEARCH_LOCAL_WARN)==2)
err = userCanceledErr;
}
if ((!sp->searchResults))
{
// start the IMAP searches ...
if (imapBoxCount)
{
// Start IMAP search
IMAPSCHandle searchCriteria;
if (searchCriteria = SetupIMAPSearchCriteria(sp))
{
// IMAPSearch will return false if no searches were actually started.
if (IMAPSearch(tocH,imapBoxCount,searchCriteria,!sp->matchAny))
sp->searchMode |= kSearchingIMAP;
else
err = userCanceledErr;
if (err!=userCanceledErr)
{
ZapHandle(sp->imapBoxSpecIdx);
sp->imapBoxSpecIdx = NuHandleClear(HandleCount(imapBoxCount)*sizeof(short));
}
}
}
}
}
}
return err;
}
/**********************************************************************
* ZapSpecList - zap Spec List handle
**********************************************************************/
static void ZapSpecList(TOCHandle toc)
{
FSSpecHandle specList;
if (specList = (*toc)->mailbox.virtualMB.specList)
{
ZapHandle(specList);
(*toc)->mailbox.virtualMB.specList = nil;
}
(*toc)->mailbox.virtualMB.specListCount = 0;
}
/**********************************************************************
* StopSearch - stop the search
**********************************************************************/
static void StopSearch(MyWindowPtr win)
{
SearchHandle sh;
short i;
Str32 s;
Boolean gotHits;
SearchMarker curr;
CriteriaHandle hCriteria;
TOCHandle tocH;
GetSearchInfo(win,&tocH,&sh);
if (!(*sh)->searchMode) return;
ActiveSearchCount--;
StopBulkSearch(sh);
if ((*sh)->searchMode & kSearchingIMAP)
IMAPAbortSearch(tocH);
curr = (*sh)->curr;
FinishedTOC(&curr);
(*sh)->curr = curr;
SetControlTitle((*sh)->ctlSearch,GetRString(s,SEARCH_BUTTON));
HideControl((*sh)->ctlChaseArrows);
HideControl((*sh)->ctlMailBox);
ShowHideTabs((*sh)->ctlTabs,true);
(*sh)->searchMode = 0;
SetTextControlText((*sh)->ctlMailBox,"",nil);
// unlock search string handles
// dispose of accumulators
hCriteria = (*sh)->hCriteria;
for(i=0;i<(*sh)->criteriaCount;i++)
{
StringHandle h;
if (h = (*hCriteria)[i].searchString)
HUnlock((Handle)h);
SetHits(sh,i,nil); // dispose of bulk search hits
}
gotHits = (*sh)->nHits>0;
if (HasFeature (featureSearch)) {
SetControlVisibility((*sh)->ctlSrchResults,gotHits,true);
if (!gotHits)
SetControlValue((*sh)->ctlSrchResults,0);
}
#if __profile__
// ProfilerDump("\psearch-profile");
// ProfilerClear();
#endif
// log search info
if (LOG_SEARCH&LogLevel)
{
long nSelMB=0;
// search stats
if ((*sh)->BoxCount)
nSelMB = GetHandleSize_((*sh)->BoxCount)/sizeof(BoxCountElem);
#ifdef VALPHA
ComposeLogS(LOG_SEARCH,nil,"\pHits: %d/%d Seconds: %d.%d, Mailboxes: %d/%d",
(*sh)->nHits,(*sh)->nSearched,
(*sh)->nTicks/60,(*sh)->nTicks%60,
nSelMB,GetHandleSize_(BoxCount)/sizeof(BoxCountElem));
#endif
// criteria
for(i=0;i<(*sh)->criteriaCount;i++)
{
CriteriaInfo criteria=(*(*sh)->hCriteria)[i];
Str32 sCategory,sRelation;
Str255 sSpecifier;
GetMenuItemText(GetPopupMenuHandle(criteria.ctlCategory),criteria.category,sCategory);
GetMenuItemText(GetPopupMenuHandle(criteria.ctlRelation),criteria.relation,sRelation);
if (criteria.searchString)
{
PCopy(sSpecifier,*criteria.searchString);
ComposeLogS(LOG_SEARCH,nil,"\p %d: %p %p \"%p\"",i+1,sCategory,sRelation,sSpecifier);
}
else
{
ComposeLogS(LOG_SEARCH,nil,"\p %d: %p %p %d",i+1,sCategory,sRelation,criteria.specifier);
}
}
}
}
/**********************************************************************
* AdjustForCriteriaCount - adjust controls for number of criteria
**********************************************************************/
static void AdjustForCriteriaCount(SearchHandle sh)
{
if (HasFeature (featureSearch)) {
SetControlVisibility((*sh)->ctlFewer,(*sh)->criteriaCount>1,true);
SetControlVisibility((*sh)->ctlMatch,(*sh)->criteriaCount>1,true);
}
}
/**********************************************************************
* SearchButton - hit one of our controls
**********************************************************************/
static void SearchButton(MyWindowPtr win,ControlHandle button,long modifiers,short part)
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
SearchHandle sh;
SearchPtr sp;
TOCHandle toc;
long controlId = -1;
Rect rWin;
GetSearchInfo(win,&toc,&sh);
sp=*sh;
if (button==sp->ctlSearch)
{
if (sp->searchMode)
StopSearch(win);
else
StartSearch(win);
controlId = kctlSearch;
}
else if (button==sp->ctlMore)
{
Rect rNone;
controlId = kctlMore;
if ((*sh)->criteriaCount>=kMaxCriteria)
{
SysBeep(0);
return; // enough criteria
}
StopSearch(win);
AddCriteria(win,sh,false);
SetCriteria:
AdjustForCriteriaCount(sh);
SetRect(&rNone,0,0,0,0);
ClipRect(&rNone);
SetSearchTopMargin(win,sh);
MyWindowDidResize(win,&win->contR);
InfiniteClip(GetMyWindowCGrafPtr(win));
InvalWindowRect(winWP,GetWindowPortBounds(winWP,&rWin));
win->isDirty = true;
SearchSetWTitle(win,sh);
}
else if (button==sp->ctlFewer)
{
controlId = kctlFewer;
if ((*sh)->criteriaCount >= 2)
{
StopSearch(win);
RemoveCriteria(win,sh);
goto SetCriteria;
}
}
else if (button==sp->ctlTabs)
{
// Toggle view of mailboxes
ControlHandle cntl,placard;
short i;
Boolean boxCtlsVisible;
PETEHandle previewPTE;
Rect oldContR = win->contR;
Rect rNone;
controlId = kctlTabs;
SetRect(&rNone,0,0,0,0);
ClipRect(&rNone);
(*sh)->mailboxView = GetControlValue(button)==tabMailboxes;
boxCtlsVisible = !(*sh)->mailboxView;
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 (cntl=FindControlByRefCon(win,kConConProfRefCon))
SetControlVisibility(cntl,boxCtlsVisible,true);
if (previewPTE = (*toc)->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 ((*sh)->mailboxView)
{
(*sh)->saveBotMargin = win->botMargin;
(*sh)->savePreviewHi = (*toc)->previewHi;
SetBotMargin(win,0);
(*toc)->previewHi = -1;
}
else
{
(*toc)->previewHi = (*sh)->savePreviewHi;
SetBotMargin(win,(*sh)->saveBotMargin);
GetSelectedBoxes(*sh,toc,false); // This isn't quite the right place, but it's better than nothing
}
// Make sure to clear pView field if in results
win->pView = (*sh)->mailboxView ? (*sh)->list : 0;
InvalBotMargin(win);
// if ((*sh)->mailboxView && !(*sh)->list)
// InitMailboxList(win,sh);
SetControlVisibility((*(*sh)->list->hList)->vScroll,!boxCtlsVisible,true);
// make sure the mailbox list is active. Certain IMAP dialogs might have deactivated it. -jdboyd 7/26/04
if (!boxCtlsVisible) LVActivate((*sh)->list, win->isActive);
SetSearchTopMargin(win,sh);
MyWindowDidResize(win, &oldContR);
InfiniteClip(GetMyWindowCGrafPtr(win));
InvalWindowRect(winWP,GetWindowPortBounds(winWP,&rWin));
}
else if (button==sp->ctlSrchResults)
{
StopSearch(win);
SetControlValue(button,!GetControlValue(button));
controlId = kctlSrchResults;
}
else if (ClickCriteriaButton(win,sh,button,part))
{
StopSearch(win);
win->isDirty = true;
SearchSetWTitle(win,sh);
controlId = kctlCriteria;
}
else if (!(*sh)->mailboxView)
BoxButton(win,button,modifiers,part);
else if (button==sp->ctlMatch)
controlId = kctlMatch;
if (controlId >= 0)
AuditHit((modifiers&shiftKey)!=0, (modifiers&controlKey)!=0, (modifiers&optionKey)!=0, (modifiers&cmdKey)!=0, false, GetWindowKind(winWP), AUDITCONTROLID(GetWindowKind(winWP),controlId), mouseDown);
}
/************************************************************************
* ClickCriteriaButton - check for click in a criteria button
************************************************************************/
static Boolean ClickCriteriaButton(MyWindowPtr win,SearchHandle sh,ControlHandle cntl,short part)
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
short i;
Rect r;
for(i=0;i<(*sh)->criteriaCount;i++)
{
CriteriaInfo criteria=(*(*sh)->hCriteria)[i];
short top = INSET+6+i*kCriteriaHt;
Boolean resize = false;
if (cntl == criteria.ctlCategory)
{
// change in category means changing relation and specifier
short category = GetControlValue(cntl);
short relation = gCategoryTable[category-1].relation;
short specifier = gCategoryTable[category-1].specifier;
if (relation != gCategoryTable[criteria.category-1].relation)
{
// different relation menu
DisposeControl(criteria.ctlRelation);
criteria.ctlRelation = CreateMenuControl(win,win->topMarginCntl,"\p",relation,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,1,false);
resize = true;
}
if (specifier != gCategoryTable[criteria.category-1].specifier)
{
short defaultSpecifierItem = 0;
DisposeSpecifier(win,sh,&criteria);
switch (specifier)
{
case ssText:
case ssNumber:
CreateTextSpecifier(win,&criteria,specifier!=ssText);
break;
case ssDate:
SetRect(&r,-4000,-4000,kDateWi-4000,kDateHt-4000);
criteria.ctlSpecifier = NewControl(winWP,&r,"\p",true,0,0,0,kControlClockDateProc,0);
//LetsGetSmall(criteria.ctlSpecifier);
break;
case ssAge:
// needs both text and specifier
CreateTextSpecifier(win,&criteria,true);
SetControlValue(criteria.ctlRelation,GetRLong(SEARCH_DF_AGE_REL));
PeteSetString(GetRString(P1,SEARCH_DF_AGE),criteria.pte);
specifier = SRCH_AGE_UNITS_MENU;
defaultSpecifierItem = GetRLong(SEARCH_DF_AGE_SPFY);
break;
default:
defaultSpecifierItem = 1;
break;
}
if (defaultSpecifierItem)
criteria.ctlSpecifier = CreateMenuControl(win,win->topMarginCntl,"\p",specifier,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,defaultSpecifierItem,false);
resize = true;
}
// else if (specifier==ssText)
// {
// // remove any existing text
// PeteDelete(criteria.pte,0,0x7fffffff);
// }
// prefill attachment count
if (category == scAttachCount)
{
SetControlValue(criteria.ctlRelation,GetRLong(SEARCH_DF_ATT_REL));
PeteSetString(GetRString(P1,SEARCH_DF_ATT),criteria.pte);
}
criteria.category = category;
(*(*sh)->hCriteria)[i] = criteria;
if (resize)
ResizeCriterion(win,sh,i);
EmbedSearchCriteriaControls(&criteria,sh);
return true;
}
else if (cntl == criteria.ctlSpecifier)
{
if (criteria.category==scDate)
{
// This control needs focus
SetKeyboardFocus(winWP,cntl,part);
(*sh)->ctlFocus = cntl;
RemoveFocus(win);
}
return true;
}
else if (cntl == criteria.ctlRelation)
// don't worry about this for now
return true;
}
return false;
}
/************************************************************************
* SearchLVCallBack - callback function for List View
************************************************************************/
static long SearchLVCallBack(ViewListPtr pView, VLCallbackMessage message, long data)
{
VLNodeInfo *pInfo;
OSErr err = noErr;
SearchHandle sh;
DEC_STATE(sh);
switch (message)
{
case kLVAddNodeItems:
case kLVExpandCollapseItem:
GetSearchInfo(pView->wPtr,nil,&sh);
L_STATE(sh);
if (message==kLVAddNodeItems)
AddMailboxListItems(pView,data,&(*sh)->expandList);
else
SaveExpandStatus((VLNodeInfo *)data,&(*sh)->expandList);
U_STATE(sh);
break;
case kLVGetParentID:
err = MailboxesLVCallBack(pView, message, data);
break;
case kLVOpenItem:
GetSearchInfo(pView->wPtr,nil,&sh);
MBListOpen((*sh)->list);
break;
case kLVQueryItem:
pInfo = (VLNodeInfo*)data;
switch (pInfo->query)
{
case kQuerySelect:
return true;
case kQueryDrag:
case kQueryRename:
case kQueryDrop:
case kQueryDropParent:
case kQueryDragExpand:
return false;
default:
err = MailboxesLVCallBack(pView, message, data);
}
break;
case kLVGetFlags:
return gLVFlags;
}
return err;
}
/************************************************************************
* InitMailboxList - set up the mailbox list
************************************************************************/
static ViewListPtr InitMailboxList(MyWindowPtr win,Boolean useMBSelection)
{
CGrafPtr winPort = GetMyWindowCGrafPtr (win);
ViewListPtr pList=NuPtr(sizeof(ViewList));
Rect r;
GrafPtr oldPort;
GetPortBounds(winPort,&r);
GetPort(&oldPort);
SetPort_(winPort);
r.top = win->topMargin;
if (pList)
{
LVNew(pList,win,&r,0,SearchLVCallBack,0);
if (useMBSelection)
{
// Select same mailboxes as in Mailboxes window
ViewListPtr pMBList = MBGetList();
short i;
VLNodeInfo data;
for (i=1;i<=LVCountSelection(pMBList);i++)
{
if (LVGetItem(pMBList,i,&data,true))
LVSelect(pList,data.nodeID,data.name,true);
}
}
}
SetPort_(oldPort);
return pList;
}
/**********************************************************************
* SizeMBList - set size of mailbox list
**********************************************************************/
static void SizeMBList(MyWindowPtr win,SearchHandle sh)
{
Rect r;
if ((*sh)->list)
{
CGrafPtr winPort = GetMyWindowCGrafPtr (win);
GetPortBounds(winPort,&r);
r.top = win->topMargin;
r.bottom -= GROW_SIZE;
if (!(*sh)->mailboxView)
OffsetRect(&r,-4000,-4000); // hide the list
LVSize((*sh)->list,&r,nil);
}
}
/************************************************************************
* SearchIdle - do actual search
************************************************************************/
static void SearchIdle(MyWindowPtr win)
{
SearchHandle sh;
TOCHandle toc;
short sumNum;
GetSearchInfo(win,&toc,&sh);
if (!(*sh)->mailboxView)
BoxIdle(win);
SearchSetControls(win, sh);
if (((*sh)->searchMode & kSearchingLocal) && (!(*sh)->redoTicks || ((*sh)->redoTicks <= TickCount())))
ProcessSearch(win,toc,sh);
else if ((*sh)->searchMode & kSearchingIMAP)
ProcessIMAPSearch(win,toc,sh);
if ((*toc)->previewID &&
(sumNum=FirstMsgSelected(toc))>=0 &&
(*toc)->sums[sumNum].offset < 0)
{
// We have an IMAP message in preview pane waiting for download
TOCHandle realTOC;
short realSumNum;
realTOC = GetRealTOC(toc,sumNum,&realSumNum);
UpdateIMAPMailbox(realTOC);
if ((*realTOC)->sums[realSumNum].offset >= 0)
{
// The message has arrived. Show it.
(*toc)->previewID = 0;
// Make sure we don't download it again
(*toc)->sums[sumNum].offset = (*realTOC)->sums[realSumNum].offset;
}
}
if ((*toc)->resort)
{
// Need to resort
(*toc)->resort = kResortWhenever;
RedoTOC(toc);
}
// take care of any incremental IMAP searches
UpdateIncrementalIMAPSearches();
}
/************************************************************************
* Call the idle routines of all search windows
************************************************************************/
void SearchAllIdle(void)
{
WindowPtr winWP;
MyWindowPtr win;
SearchHandle sh;
for (winWP = GetWindowList(); winWP; winWP = GetNextWindow (winWP)) {
win = GetWindowMyWindowPtr (winWP);
if (IsSearchWindow(winWP))
{
GetSearchInfo(win,nil,&sh);
if ((*sh)->searchMode) SearchIdle(win);
}
}
}
/************************************************************************
* ProcessSearch - do actual search
************************************************************************/
static void ProcessSearch(MyWindowPtr win,TOCHandle toc,SearchHandle sh)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
uLong endTicks,maxTicks;
SearchMarker curr;
Boolean stop = false;
TOCHandle aTOC;
Boolean foundTOC = false;
FSSpec spec;
Str255 s;
Str63 name;
GrafPtr oldPort;
Boolean inFront=false,match;
Boolean invalidateBoxSize = false;
#ifdef VALPHA
long startTicks;
#endif
#if __profile__
// ProfilerSetStatus(true);
#endif
#ifdef VALPHA
startTicks = TickCount();
#endif
GetPort(&oldPort);
SetPort_(GetWindowPort(winWP));
if ((*sh)->bulkSearching)
{
DoBulkSearch(sh);
if ((*sh)->bulkSearching)
// bulk search was interrupted again
return;
}
// validate the saved toc, if any
curr = (*sh)->curr;
if (!(*sh)->searchResults && curr.tocH)
{
if (!curr.opened)
{
// make sure our saved TOC is still valid
for (aTOC=TOCList;aTOC;aTOC=(*aTOC)->next)
if (aTOC == curr.tocH) foundTOC = true;
}
else
{
ASSERT(*curr.tocH!=(TOCPtr)-1); // ACK!!! Freed?
foundTOC = *curr.tocH!=(TOCPtr)-1;
}
if (!foundTOC)
{
// this TOC has been disposed of, need to open it again
GetBoxTOC(sh,&curr);
(*sh)->curr = curr;
}
}
if (InBG)
maxTicks = kInBGTicks;
else
{
if (FrontWindow_()==winWP)
{
maxTicks = kFrontWinTicks;
inFront = true;
}
else
maxTicks = kBgdWinTicks;
}
endTicks = TickCount() + maxTicks;
while(TickCount() < endTicks && !EventPending() && (!inFront || !CommandPeriod))
{
MSumType sum;
Boolean found=false;
CheckChaseArrows(sh);
while (!curr.tocH || curr.message >= (*curr.tocH)->count)
{
// Done with this mailbox
if ((*sh)->searchResults)
{
// searching through the results. we're finished
if (curr.tocH) goto done;
curr.tocH = toc;
curr.message = 0;
}
else
{
// do next mailbox
short i;
CriteriaHandle hCriteria = (*sh)->hCriteria;;
FinishedTOC(&curr);
curr.box++;
curr.tocH = 0;
curr.message = 0;
if (curr.box>=GetHandleSize_((*sh)->BoxCount)/sizeof(BoxCountElem))
{
// end of mailboxes!
(*sh)->redoTicks = 0;
if ((*sh)->redoBoxCount)
{
SearchPtr sp;
// There were some mailboxes that were busy. Try them again
ZapHandle((*sh)->BoxCount);
sp = *sh;
sp->BoxCount = sp->redoBoxCount;
sp->redoBoxCount = nil;
Zero(sp->curr);
sp->curr.lastBoxSpec = -2;
sp->curr.box = -1;
(*sh)->redoTicks = TickCount() + 120; // Try again in a couple of seconds
goto dontstop;
}
else
// We're all done
goto done;
}
// reset all hits accumulators
for(i=0;i<(*sh)->criteriaCount;i++)
SetHits(sh,i,nil);
(*sh)->bulkData.didBulkSearch = false;
// If we are searching for a string in a single criterion, do the bulk
// search now. If nothing found, skip this mailbox
if ((*sh)->criteriaCount==1 &&
(*hCriteria)->searchString &&
(*hCriteria)->category != scSummary)
{
PCopy(s,*(*hCriteria)->searchString);
if (GetBoxCountSpec(sh,curr.box,&spec))
{
DisplayMBName(sh,spec.name);
if (SearchBulk(&spec,sh) == opWrErr)
{
// This mailbox is busy right now. We'll come back to it.
if (!(*sh)->redoBoxCount) (*sh)->redoBoxCount = NuHandle(0);
if ((*sh)->redoBoxCount)
{
BoxCountHandle boxList = (*sh)->BoxCount ? (*sh)->BoxCount : BoxCount;
BoxCountElem bce = (*boxList)[curr.box];
PtrPlusHand_(&bce,(*sh)->redoBoxCount,sizeof(bce));
}
break;
}
if ((*hCriteria)->hits.offset==0 && !(*sh)->bulkSearching && !(*sh)->noBulkSearch)
{
// skip this mailbox, nothing found in bulk search
break;
}
}
}
if (GetBoxTOC(sh,&curr))
{
(*sh)->curr = curr;
GetMailboxName((*sh)->curr.tocH,0,name);
DisplayMBName(sh,name);
}
if ((*sh)->bulkSearching)
break; // Interrupted a bulk search.
}
}
if (!curr.tocH)
continue; // Did a bulk search and found nothing
// search summary
sum = (*curr.tocH)->sums[curr.message];
(*sh)->curr = curr;
#ifdef VALPHA
(*sh)->nSearched++;
#endif
match = SearchMessage(win,&sum,sh,&curr,&stop,false);
if (stop)
goto done;
if ((*sh)->bulkSearching)
break; // Interrupted a bulk search.
if ((*sh)->searchResults)
{
if (match)
(*sh)->nHits++;
else
{
// no match on searching results. remove this one.
DeleteSum(curr.tocH,curr.message);
invalidateBoxSize = true;
curr.message--;
}
}
else if (match)
{
// add a message summary to search window toc
if (AddSearchResult(sh,toc,curr.tocH,curr.message,curr.lastBoxSpec != curr.box,-1))
{
stop = true;
goto done; // error
}
curr.lastBoxSpec = curr.box;
invalidateBoxSize = true;
}
curr.message++;
if (stop) goto done;
}
stop = inFront && (CommandPeriod || HasCommandPeriod());
(*sh)->curr = curr;
if (stop)
{
done:
(*sh)->curr = curr;
if ((*sh)->searchMode != kSearchingLocal)
{
// Stop local search, continue with IMAP
(*sh)->searchMode &= ~kSearchingLocal;
SetTextControlText((*sh)->ctlMailBox,GetRString(s,SEARCHING_SERVER),nil);
}
else
// Stop everything
StopSearch(win);
}
dontstop:
if (invalidateBoxSize)
UpdateBoxSize(win);
#if __profile__
// ProfilerSetStatus(false);
#endif
SetPort(oldPort);
#ifdef VALPHA
(*sh)->nTicks += TickCount()-startTicks;
#endif
}
/**********************************************************************
* CopySum - copy a summary
**********************************************************************/
void CopySum(MSumPtr sumFrom, MSumPtr sumTo, short virtualMBIdx)
{
short saveSerialNum = sumTo->serialNum;
Boolean saveSelected = sumTo->selected;
Handle saveCache = sumTo->cache;
*sumTo = *sumFrom;
sumTo->cache = saveCache;
sumTo->mesgErrH = nil;
sumTo->messH = nil;
sumTo->selected = saveSelected;
sumTo->u.virtualMess.virtualMBIdx = virtualMBIdx;
sumTo->u.virtualMess.linkSerialNum = sumFrom->serialNum;
sumTo->serialNum = saveSerialNum;
}
/**********************************************************************
* AddSearchResult - add this search result
**********************************************************************/
static OSErr AddSearchResult(SearchHandle sh, TOCHandle srchTOC, TOCHandle tocH, short sumNum, Boolean addSpec, short specIdx)
{
MSumType sum;
Zero(sum);
if (addSpec)
{
FSSpecHandle specList = (*srchTOC)->mailbox.virtualMB.specList;
FSSpec spec;
if (!specList)
(*srchTOC)->mailbox.virtualMB.specList = specList = NuHandle(0);
Zero(spec);
spec = GetMailboxSpec(tocH,sumNum);
PtrAndHand(&spec,(Handle)specList,sizeof(spec));
(*srchTOC)->mailbox.virtualMB.specListCount++;
}
CopySum(&(*tocH)->sums[sumNum], &sum, specIdx<0 ? (*srchTOC)->mailbox.virtualMB.specListCount-1 : specIdx);
sum.serialNum = (*srchTOC)->nextSerialNum++;
if ((*srchTOC)->count)
{
OSErr err;
if (err = PtrPlusHand_(&sum,srchTOC,sizeof(sum)))
{
WarnUser(MEM_ERR,err);
return err;
}
}
else
(*srchTOC)->sums[0] = sum;
InvalSum(srchTOC,(*srchTOC)->count++);
(*sh)->nHits++;
return noErr;
}
/**********************************************************************
* UpdateBoxSize - redisplay box size (# of messages in search window)
**********************************************************************/
static void UpdateBoxSize(MyWindowPtr win)
{
ControlHandle boxSizeCntl;
Rect rCtl;
if (boxSizeCntl = FindControlByRefCon(win,kBoxSizeRefCon))
{
GetControlBounds(boxSizeCntl,&rCtl);
SetPort_(GetMyWindowCGrafPtr(win)); // someone keeps changing this
InvalWindowRect(GetMyWindowWindowPtr(win),&rCtl);
}
}
/**********************************************************************
* SearchText - search for string in text
**********************************************************************/
static Boolean SearchText(StringPtr value,UPtr pText,long len,long offset,short relation)
{
switch (relation)
{
case strContains:
return SearchStrPtr(value,pText,offset,len,false,false,nil) >= 0;
case strContainsWord:
return SearchStrPtr(value,pText,offset,len,false,true,nil) >= 0;
case strDoesNotContain:
return SearchStrPtr(value,pText,offset,len,false,false,nil) < 0;
case strIs:
return *value==len && SearchStrPtr(value,pText,offset,*value,false,false,nil) >= 0;
case strIsNot:
return *value!=len || SearchStrPtr(value,pText,offset,*value,false,false,nil) < 0;
case strStarts:
return *value<=len && SearchStrPtr(value,pText,offset,*value,false,false,nil) >= 0;
case strEnds:
return *value<=len && SearchStrPtr(value,pText,offset+len-*value,len,false,false,nil) >= 0;
case strRegExp:
return SearchRegExpPtr(value,pText,offset,len) >= 0;
}
return false;
}
/**********************************************************************
* SearchString - search for string in string
**********************************************************************/
static Boolean SearchString(StringPtr value,StringPtr s,short relation)
{
return SearchText(value,s+1,*s,0,relation);
}
/**********************************************************************
* SearchMessage - search this message summary, return true if found
**********************************************************************/
static Boolean SearchMessage(MyWindowPtr win,MSumPtr sumP,SearchHandle sh,SearchMarker *curr,Boolean *stop,Boolean sumOnly)
{
short i;
// MyWindowPtr messWin;
CriteriaInfo criteria;
long value;
DateTimeRec date,currDate;
long secs,sumSecs,result;
Str255 string;
short label;
TOCHandle tocH = curr->tocH;
long mNum = curr->message;
FSSpec spec;
Boolean found;
Handle hText;
PETEHandle tempPte;
long textSize;
Boolean disposeText;
Ptr pText;
long offset;
short hdrID;
Boolean notInBulk;
FindAttData attData;
Str127 searchString;
// shouldn't be here unless the search has already been setup
ASSERT((*sh)->setup);
if (!(*sh)->setup) return false;
for(i=0;i<(*sh)->criteriaCount;i++)
{
criteria=(*(*sh)->hCriteria)[(*sh)->searchOrder[i]];
found = false;
if (criteria.searchString && !**criteria.searchString && criteria.relation!=strRegExp)
// ignore any blank search strings
continue;
if (sumOnly && !(gCategoryTable[criteria.category-1].flags & kNeedsSum))
continue; // Doing summaries only. Did IMAP server search
switch (criteria.category)
{
case scSummary:
// if the summary is utf8, use the utf8 search string
if ((sumP->flags&FLAG_UTF8)!=0 && criteria.utf8SearchString)
PSCopy(searchString,*criteria.utf8SearchString);
else
PSCopy(searchString,*criteria.searchString);
// search subject, from, date, and label in summary info
if (criteria.relation == strDoesNotContain)
// for "does not contain" all must be true for a true result
found = SearchString(searchString,sumP->subj,criteria.relation) &&
SearchString(searchString,sumP->from,criteria.relation) &&
SearchString(searchString,ComputeLocalDate(sumP,string),criteria.relation);
else
found = SearchString(searchString,sumP->subj,criteria.relation) ||
SearchString(searchString,sumP->from,criteria.relation) ||
SearchString(searchString,ComputeLocalDate(sumP,string),criteria.relation);
if (!found && (label = SumColor(sumP)))
{
RGBColor color;
MyGetLabel(label,&color,string);
if (SearchString(*criteria.searchString,string,criteria.relation))
found = true;
}
break;
case scStatus:
value = sumP->state;
goto DoEqual;
case scPriority:
value = sumP->priority;
if (!value) value = Display2Prior(3);
result=ShortCompare(criteria.specifier,value); // for priority to backward compare
goto DoCompare;
case scAttachCount:
if (sumP->flags & FLAG_HAS_ATT)
{
if (criteria.specifier==0)
// Don't need to count them. If compare to zero, any number will do.
value=1;
else
{
value = 0;
CacheMessage(tocH,mNum);
if (hText=(*tocH)->sums[mNum].cache)
{
HNoPurge(hText);
InitAttachmentFinder(&attData,hText,true,tocH,&(*tocH)->sums[mNum]);
while (GetNextAttachment(&attData,&spec))
value++;
}
}
}
else
value = 0;
DoCompareValue:
result=ShortCompare(value,criteria.specifier);
goto DoCompare;
case scLabel:
value = SumColor(sumP);
DoEqual:
found = criteria.specifier == value ? criteria.relation == srIs : criteria.relation == srIsNot;
break;
case scAge:
sumSecs = sumP->seconds + criteria.zoneSecs; // Use local time
SecondsToDate(sumSecs,&date);
GetDateTime(&secs);
GetTime(&currDate);
result = DateTimeDifference(&date,&currDate,secs-sumSecs,criteria.specifier) - criteria.misc;
goto DoCompare;
case scDate:
// Convert GMT seconds to local or sender's time based on preference
SecondsToDate(sumP->seconds+(criteria.useSenderZone?60*sumP->origZone:criteria.zoneSecs),&date);
if (!(result=ShortCompare(date.year,criteria.date.year)))
if (!(result=ShortCompare(date.month,criteria.date.month)))
result=ShortCompare(date.day,criteria.date.day);
DoCompare:
switch (criteria.relation)
{
case srIs:
found = result==0;
break;
case srIsNot:
found = result!=0;
break;
case srGreater:
found = result > 0;
break;
case srLess:
found = result < 0;
break;
}
break;
case scSize:
value = (sumP->length+1023)/1024;
goto DoCompareValue;
case scJunkScore:
value = sumP->spamScore;
goto DoCompareValue;
case scPersonality:
if (HasFeature (featureMultiplePersonalities)) {
value = sumP->popPersId;
goto DoEqual;
}
case scAttachNames:
if (!(sumP->flags & FLAG_HAS_ATT))
// no attachments
break;
// fall thru
case scAnywhere:
case scHeaders:
case scBody:
case scTo:
case scFrom:
case scSubject:
case scCC:
case scBCC:
case scAnyRecipient:
// search message
notInBulk = !(*sh)->searchResults && !(*sh)->noBulkSearch && !MessPreFind(sh,*criteria.searchString,(*sh)->searchOrder[i]);
if ((*sh)->bulkSearching)
return false; // bulk search was interrupted
if (notInBulk)
{
if (criteria.relation == strDoesNotContain)
found = true;
break;
}
if (criteria.category==scAnywhere &&
criteria.relation==strContains &&
!(*sh)->noBulkSearch &&
!criteria.utf8SearchString &&
!(*sh)->searchResults &&
!((*tocH)->sums[mNum].flags & FLAG_RICH) &&
!((*tocH)->sums[mNum].opts & OPT_HTML))
{
// if search anywhere, contains, and not HTML or RICH, we don't need to
// load and search through the message, bulk search is good enough
found = true;
break;
}
// get text of message
tempPte = nil;
{
// need to read in message text
TOCHandle realTOC;
short realSumNum;
realTOC = GetRealTOC(tocH,mNum,&realSumNum);
// if this is an IMAP message, make sure it's been downloaded
if ((*realTOC)->imapTOC) EnsureMsgDownloaded(realTOC,realSumNum,false);
textSize = (*realTOC)->sums[realSumNum].length;
disposeText = true;
CacheMessage(realTOC,realSumNum);
hText = nil;
if ((*realTOC)->sums[realSumNum].cache)
{
HNoPurge((*realTOC)->sums[realSumNum].cache);
{
// If rich or HTML, need text stripped of markup
if (criteria.fudge8bit || (*sh)->noBulkSearch ||
(*sh)->searchResults && (((*realTOC)->sums[realSumNum].flags & FLAG_RICH) ||
((*realTOC)->sums[realSumNum].opts & OPT_HTML+OPT_FLOW+OPT_CHARSET)))
{
if (!PeteCreate(win,&tempPte,0,nil))
{
PeteCalcOff(tempPte);
if (!InsertRich((*realTOC)->sums[realSumNum].cache,0,-1,-1,true,tempPte,nil,0!=((*realTOC)->sums[realSumNum].opts & OPT_DELSP)))
{
PeteGetRawText(tempPte,&hText);
}
}
}
else
{
hText = DupHandle((*realTOC)->sums[realSumNum].cache);
disposeText = true;
}
}
HPurge((*realTOC)->sums[realSumNum].cache);
}
}
if (!hText)
return false;
pText = LDRef(hText);
textSize = GetHandleSize(hText);
offset=0;
switch (criteria.category)
{
// search message
case scAnywhere:
DoSearchMsg:
found = SearchText(*criteria.searchString,pText,textSize,offset,criteria.relation);
break;
case scHeaders:
textSize = BodyOffset(hText);
goto DoSearchMsg;
case scBody:
offset = BodyOffset(hText);
goto DoSearchMsg;
// search in a header
case scTo:
hdrID = TO_HEAD+HEADER_STRN;
DoSearchHdr:
found = SearchHeader(hdrID,hText,textSize,*criteria.searchString,criteria.relation);
break;
case scFrom:
hdrID = FROM_HEAD+HEADER_STRN;
goto DoSearchHdr;
case scSubject:
hdrID = SUBJ_HEAD+HEADER_STRN;
goto DoSearchHdr;
case scCC:
hdrID = CC_HEAD+HEADER_STRN;
goto DoSearchHdr;
case scBCC:
hdrID = BCC_HEAD+HEADER_STRN;
goto DoSearchHdr;
// search any recipient
case scAnyRecipient:
found = SearchHeader(TO_HEAD+HEADER_STRN,hText,textSize,*criteria.searchString,criteria.relation) ||
SearchHeader(CC_HEAD+HEADER_STRN,hText,textSize,*criteria.searchString,criteria.relation) ||
SearchHeader(BCC_HEAD+HEADER_STRN,hText,textSize,*criteria.searchString,criteria.relation);
break;
case scAttachNames:
InitAttachmentFinder(&attData,hText,true,tocH,&(*tocH)->sums[mNum]);
while (GetNextAttachment(&attData,&spec))
{
if (found=SearchString(*criteria.searchString,spec.name,criteria.relation))
break;
}
break;
}
// Dispose of text
if (tempPte)
PeteDispose(win,tempPte);
else if (disposeText)
ZapHandle(hText);
else
UL(hText);
break;
}
if (found)
{
if ((*sh)->matchAny)
return true; // Found one, that's good enough
}
else
{
if (!(*sh)->matchAny)
return false; // This one not found, need to match them all
}
}
return (*sh)->matchAny ? false : true;
}
/**********************************************************************
* DateTimeDifference - return discrete difference between 2 times
**********************************************************************/
long DateTimeDifference(DateTimeRec *date,DateTimeRec *currDate,long seconds,short units)
{
switch(units)
{
case sauMinutes: return seconds/60 + (currDate->minute<date->minute?1:0);
case sauHours: return DateTimeDifference(date,currDate,seconds,sauMinutes)/60 + ((seconds>=0&&currDate->minute<date->minute)?1:0);
case sauDays: return DateTimeDifference(date,currDate,seconds,sauHours)/24 + ((seconds>=0&&currDate->hour<date->hour)?1:0);
case sauWeeks: return DateTimeDifference(date,currDate,seconds,sauDays)/7 + ((seconds>=0&&currDate->dayOfWeek<date->dayOfWeek)?1:0);
case sauMonths: return DateTimeDifference(date,currDate,seconds,sauYears)*12 + (currDate->month - date->month);
case sauYears: return currDate->year - date->year;
}
return 0; /* mtc sez - good as any! */
}
/**********************************************************************
* ShortCompare - compare 2 short, return 0 if equal, -1 if value1 < value2, 1 if value1 2 value2
**********************************************************************/
static short ShortCompare(short value1,short value2)
{
if (value1 == value2) return 0;
if (value1 < value2) return -1;
return 1;
}
/**********************************************************************
* SearchHeader - search a header for the string
**********************************************************************/
static Boolean SearchHeader(short header,Handle text,long size,StringPtr searchString,short relation)
{
Str32 s;
long len = size;
UPtr found;
GetRString(s,header);
if (found = FindHeaderString(*text,s,&len,False))
return SearchText(searchString,found,len,0,relation);
return false;
}
/**********************************************************************
* MessPreFind - do a quick scan to see if a message might contain
* the desired string. We cop out on anything weird and return True.
* The only way we return false is if we read up the message and it does
* not contain the desired string
**********************************************************************/
static Boolean MessPreFind(SearchHandle sh,StringPtr s,short criteriaIdx)
{
FSSpec spec;
Accumulator a;
if ((*sh)->noBulkSearch) return true;
if (!(*sh)->bulkData.didBulkSearch)
{
spec = (*(*sh)->curr.tocH)->mailbox.spec;
SearchBulk(&spec,sh);
if ((*sh)->bulkSearching)
return false; // bulk search was interrupted
}
GetHits(sh,criteriaIdx,&a);
if (a.data)
return(FindInHits(sh,&a));
else
return false;
}
/**********************************************************************
* FindInHits - do a find in the hit database
**********************************************************************/
static Boolean FindInHits(SearchHandle sh,AccuPtr a)
{
long start = (*(*sh)->curr.tocH)->sums[(*sh)->curr.message].offset;
long stop = start + (*(*sh)->curr.tocH)->sums[(*sh)->curr.message].length;
long *hit, *hitEnd;
hitEnd = (long *)(*a->data + a->offset);
for (hit=(long *)*a->data;hit<hitEnd;hit++)
{
if (stop<*hit) return(False); // we're past the message
if (*hit<start) continue; // we're not there yet
return(True); // start <= *hit <= stop; we found it!
}
return(False); // never found
}
/**********************************************************************
* SearchBulk - search a file, but do it fast
* string - the string to search for
* spec - the file to search in
* allOffsets - accumulator (may be nil) that will get all hits
**********************************************************************/
static OSErr SearchBulk(FSSpecPtr spec,SearchHandle sh)
{
BulkSearchInfo bulkData;
OSErr err = fnfErr;
FSSpec localSpec;
// are we just pretending?
if ((*sh)->noBulkSearch)
{
bulkData.didBulkSearch = true;
return noErr;
}
/*
* allocate stuff
*/
Zero(bulkData.buf[0]);
Zero(bulkData.buf[1]);
bulkData.buf[0].buffer = NewIOBHandle(1 K, kIOBufSize);
bulkData.buf[1].buffer = NewIOBHandle(1 K, kIOBufSize);
bulkData.buf[0].pb = NuPtr(sizeof(ParamBlockRec));
bulkData.buf[1].pb = NuPtr(sizeof(ParamBlockRec));
bulkData.buf[0].free = bulkData.buf[1].free = True;
bulkData.fullOffset = -1;
if (bulkData.buf[0].buffer && bulkData.buf[1].buffer)
{
// record buffer sizes
bulkData.buf[0].bSize = GetHandleSize(bulkData.buf[0].buffer);
bulkData.buf[1].bSize = GetHandleSize(bulkData.buf[1].buffer);
// open the file
// open with write access also so nothing else (especially mailbox
// compaction) tries to open and change it while we are searching
if (!(err=AFSpOpenDF(spec,&localSpec,fsRdWrPerm,&bulkData.refN)))
{
short i;
// record initial state
bulkData.spot = 0;
bulkData.bulkSearchCriteriaIdx = 0;
GetEOF(bulkData.refN,&bulkData.len);
bulkData.len -= bulkData.spot;
bulkData.didBulkSearch = true;
// build list of criteria to search
bulkData.nBulkSearchCriteria = 0;
for(i=0;i<(*sh)->criteriaCount;i++)
{
CriteriaInfo criteria=(*(*sh)->hCriteria)[i];
if (gCategoryTable[criteria.category-1].flags & kNeedsMsg)
bulkData.bulkSearchCriteria[bulkData.nBulkSearchCriteria++] = i;
}
(*sh)->bulkSearching = true;
(*sh)->bulkData = bulkData;
DoBulkSearch(sh);
}
else
{
// File error
ZapHandle(bulkData.buf[0].buffer);
ZapHandle(bulkData.buf[1].buffer);
DisposePtr((Ptr)bulkData.buf[0].pb);
DisposePtr((Ptr)bulkData.buf[1].pb);
}
}
return err;
}
/**********************************************************************
* StopBulkSearch - stop bulk searching
**********************************************************************/
static void StopBulkSearch(SearchHandle sh)
{
if (!(*sh)->bulkSearching)
return;
FSClose((*sh)->bulkData.refN);
/*
* dump the buffers
*/
ZapHandle((*sh)->bulkData.buf[0].buffer);
ZapHandle((*sh)->bulkData.buf[1].buffer);
DisposePtr((Ptr)(*sh)->bulkData.buf[0].pb);
DisposePtr((Ptr)(*sh)->bulkData.buf[1].pb);
(*sh)->bulkSearching = false;
}
/**********************************************************************
* DoBulkSearch - do bulk searching
**********************************************************************/
static void DoBulkSearch(SearchHandle sh)
{
BulkSearchInfo bulkData = (*sh)->bulkData;
OSErr err = noErr;
Str255 strings[2];
short curString;
UPtr string;
while ((!err && bulkData.len>0) || !bulkData.buf[0].free || !bulkData.buf[1].free)
{
short i;
if (EventPending())
{
// Need to go away for a while to process this event
(*sh)->bulkData = bulkData;
// SetHits(sh,bulkData.bulkSearchCriteria[bulkData.bulkSearchCriteriaIdx],&hits);
return;
}
// CycleBalls();
CheckChaseArrows(sh);
/*
* fill the buffers if need be
*/
for (i=0;i<sizeof(bulkData.buf)/sizeof(bulkData.buf[0]);i++)
if (bulkData.len && !err && bulkData.buf[i].free && bulkData.len)
{
ParmBlkPtr pb = bulkData.buf[i].pb;
Zero(*pb);
bulkData.buf[i].offset = bulkData.spot;
bulkData.buf[i].size = MIN(bulkData.len,bulkData.buf[i].bSize);
bulkData.spot += bulkData.buf[i].size;
bulkData.len -= bulkData.buf[i].size;
if (SyncRW)
{
// boring synch stuff
err = FSRead(bulkData.refN,&bulkData.buf[i].size,LDRef(bulkData.buf[i].buffer));
UL(bulkData.buf[i].buffer);
pb->ioParam.ioResult = err;
if (!err) bulkData.buf[i].free = False;
}
else
{
// zippy asynch stuff
pb->ioParam.ioRefNum = bulkData.refN;
pb->ioParam.ioBuffer = LDRef(bulkData.buf[i].buffer);
pb->ioParam.ioReqCount = bulkData.buf[i].size;
pb->ioParam.ioResult = inProgress;
pb->ioParam.ioPosMode = fsFromStart;
pb->ioParam.ioPosOffset = bulkData.buf[i].offset;
PBReadAsync(pb);
bulkData.buf[i].pending = True;
bulkData.buf[i].free = False;
}
}
/*
* check to see if any i/o has completed
*/
for (i=0;i<sizeof(bulkData.buf)/sizeof(bulkData.buf[0]);i++)
if (bulkData.buf[i].pending && bulkData.buf[i].pb->ioParam.ioResult!=inProgress)
{
if (!err) err = bulkData.buf[i].pb->ioParam.ioResult;
bulkData.buf[i].pending = False;
UL(bulkData.buf[i].buffer);
}
/*
* do we have a buffer to search?
*/
i = -1;
if (READY(0))
{
// buffer 0 is ready. How about buffer 1?
if (READY(1))
// both ready. take lesser
i = (bulkData.buf[0].offset < bulkData.buf[1].offset) ? 0 : 1;
else
i = 0; // just buffer 0 is ready
}
else if (READY(1)) i = 1; // just buffer 1 is ready
/*
* search
*/
if (i>=0)
{
if (err) bulkData.buf[i].free = True; // backing out
else
{
long tempOffset;
long bufLen = GetHandleSize(bulkData.buf[i].buffer);
Boolean regexp;
for(;bulkData.bulkSearchCriteriaIdx < bulkData.nBulkSearchCriteria;bulkData.bulkSearchCriteriaIdx++)
{
short criteriaIdx = bulkData.bulkSearchCriteria[bulkData.bulkSearchCriteriaIdx];
StringHandle searchString;
Boolean match = false;
Accumulator hits;
// set up for this criterion
GetHits(sh,criteriaIdx,&hits);
if (!hits.data)
{
err = AccuInit(&hits);
SetHits(sh,criteriaIdx,&hits);
}
ASSERT(!err);
if (err) break;
searchString = (*(*sh)->hCriteria)[criteriaIdx].searchString;
regexp = (*(*sh)->hCriteria)[criteriaIdx].relation==strRegExp;
curString = 1; // assume we'll skip the utf8 string
if (!regexp)
{
if (searchString && **searchString)
{
PCopy(strings[1],*searchString);
NormalizeSearchString(strings[1]);
if ((*(*sh)->hCriteria)[criteriaIdx].utf8SearchString && **(*(*sh)->hCriteria)[criteriaIdx].utf8SearchString)
{
PCopy(strings[0],*(*(*sh)->hCriteria)[criteriaIdx].utf8SearchString);
NormalizeSearchString(strings[0]);
curString = 0;
}
}
else
continue;
}
// wrap this in a loop, so we can look for both normal & utf8 hits
do
{
string = strings[curString];
tempOffset = 0;
while (0<=(tempOffset= regexp ? SearchRegExpHandle(*searchString,bulkData.buf[i].buffer,tempOffset) : SearchStrText(string,(*bulkData.buf[i].buffer),bufLen,tempOffset)))
{
Ptr pData;
// string found
bulkData.fullOffset = tempOffset + bulkData.buf[i].offset;
err = AccuAddSortedLong(&hits,bulkData.fullOffset);
tempOffset++;
match=true;
if (err) break;
// for regular expressions, start at the beginning of the next line
if (regexp)
{
for(pData=*bulkData.buf[i].buffer;tempOffset<bufLen;)
if (pData[tempOffset++] == returnChar)
break;
if (tempOffset >= bufLen)
break; // end of buffer
}
}
}
while (++curString<2);
if (match)
SetHits(sh,criteriaIdx,&hits); // save updated hits
}
bulkData.bulkSearchCriteriaIdx = 0;
bulkData.buf[i].free = True;
}
}
}
// Done!
StopBulkSearch(sh);
}
/**********************************************************************
* GetBoxCountSpec - get file spec for mailbox from BoxCount
**********************************************************************/
static Boolean GetBoxCountSpec(SearchHandle sh, short boxNum, FSSpecPtr spec)
{
return GetBoxCountSpecLo((*sh)->BoxCount ? (*sh)->BoxCount : BoxCount,boxNum,spec);
}
/**********************************************************************
* GetBoxCountSpec - get file spec for mailbox from BoxCount
**********************************************************************/
static Boolean GetBoxCountSpecLo(BoxCountHandle boxList, short boxNum, FSSpecPtr spec)
{
short menuId;
if (boxNum>=GetHandleSize_(boxList)/sizeof(BoxCountElem)) return false;
spec->parID = (*boxList)[boxNum].dirId;
spec->vRefNum = (*boxList)[boxNum].vRef;
menuId = (spec->parID==MailRoot.dirId && SameVRef(spec->vRefNum,MailRoot.vRef)) ? MAILBOX_MENU : FindDirLevel(spec->vRefNum,spec->parID);
MailboxMenuFile(menuId,(*boxList)[boxNum].item,spec->name);
if (IsIMAPCacheFolder(spec)) spec->parID = SpecDirId(spec);
return true;
}
/**********************************************************************
* GetBoxTOC - get TOC from BoxCount or my own BoxCount
**********************************************************************/
static TOCHandle GetBoxTOC(SearchHandle sh,SearchMarker *which)
{
FSSpec spec;
which->tocH = nil;
which->opened = false;
if (GetBoxCountSpec(sh,which->box,&spec))
which->tocH = GetSpecTOC(&spec,&which->opened);
return which->tocH;
}
/**********************************************************************
* GetSpecTOC - get TOC from file spec
**********************************************************************/
static TOCHandle GetSpecTOC(FSSpecPtr spec,Boolean *opened)
{
FSSpec newSpec;
TOCHandle tocH = nil;
*opened = false;
if (!(tocH=FindTOC(spec)))
{
// didn't find TOC, need to open it
if (IsAlias(spec,&newSpec) && SameSpec(spec,&newSpec)) return(nil);
if (tocH = CheckTOC(&newSpec))
*opened = true;
}
return tocH;
}
/**********************************************************************
* FinishedTOC - we're done with this TOC, may need to dispose of it
**********************************************************************/
static void FinishedTOC(SearchMarker *which)
{
if (which->tocH && which->opened)
{
if (TOCIsDirty(which->tocH)) WriteTOC(which->tocH);
BoxFClose(which->tocH,true);
ZapHandle(which->tocH);
which->opened = false;
}
which->tocH = nil;
}
/**********************************************************************
* CreateTextSpecifier - add a text specifier
**********************************************************************/
static void CreateTextSpecifier(MyWindowPtr win,CriteriaInfo *pCriteria,Boolean number)
{
PETEDocInitInfo pdi;
PETEHandle previewPTE;
TOCHandle tocH;
DECLARE_UPP(SearchTextDraw,ControlUserPaneDraw);
DefaultPII(win,false,peNoStyledPaste+peClearAllReturns,&pdi);
SetRect(&pdi.inRect,-4000,-4000,-3900,-3900);
if (!PeteCreate(win,&pCriteria->pte,peNoStyledPaste+peClearAllReturns,&pdi))
{
PeteFontAndSize(pCriteria->pte,SmallSysFontID(),SmallSysFontSize());
(*PeteExtra(pCriteria->pte))->frame = True;
if (pCriteria->ctlPete = CreateControl(win,win->topMarginCntl,0,kControlUserPaneProc,false))
{
INIT_UPP(SearchTextDraw,ControlUserPaneDraw);
SetControlData(pCriteria->ctlPete,0,kControlUserPaneDrawProcTag,sizeof(SearchTextDrawUPP),(void*)&SearchTextDrawUPP);
SetControlReference(pCriteria->ctlPete,(long)pCriteria->pte);
}
PeteFocus(win,pCriteria->pte,true);
if (number)
(*PeteExtra(pCriteria->pte))->numberField = true; // Number field
// If there's a preview pane, it can mess up the text field focus order
// Put any preview pane at the end of the list of text fields
GetSearchInfo(win,&tocH,nil);
if (tocH && (previewPTE = (*tocH)->previewPTE))
{
win->pteList = PeteRemove(win->pteList,previewPTE);
win->pteList = PeteLink(win->pteList,previewPTE);
}
}
CleanPII(&pdi);
}
/**********************************************************************
* RemoveFocus - remove current pte focus
**********************************************************************/
static void RemoveFocus(MyWindowPtr win)
{
//SD Why? PeteSelect(win,win->pte,0,0);
PeteFocus(win,nil,true);
}
/**********************************************************************
* AddCriteria - add criteria
**********************************************************************/
static void AddCriteria(MyWindowPtr win,SearchHandle sh,Boolean defaultString)
{
CriteriaInfo criteria;
Str255 what;
short count,
i;
RemoveFocus(win);
Zero(criteria);
// add default search criteria
criteria.ctlCategory = CreateMenuControl(win,win->topMarginCntl,"\p",SRCH_CATEGORY_MENU,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,1,false);
criteria.ctlRelation = CreateMenuControl(win,win->topMarginCntl,"\p",SRCH_TEXT_COMPARE_MENU,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,1,false);
CreateTextSpecifier(win,&criteria,false);
criteria.category = 1;
if (!PtrAndHand(&criteria,(Handle)(*sh)->hCriteria,sizeof(CriteriaInfo)))
{
(*sh)->criteriaCount++;
if (defaultString && GetFindString(what))
PeteSetString(what,criteria.pte);
PeteSelectAll(win,criteria.pte);
}
// (jp) 1-18-00 For light...
count = CountMenuItems (GetPopupMenuHandle (criteria.ctlCategory));
for (i = 1; i <= count; ++i)
if (gCategoryTable[i - 1].proOnly)
SetMenuItemBasedOnFeature (GetPopupMenuHandle(criteria.ctlCategory), i, featureSearch, true);
count = CountMenuItems (GetPopupMenuHandle (criteria.ctlRelation));
for (i = strContains + 1; i <= count; ++i)
SetMenuItemBasedOnFeature (GetPopupMenuHandle(criteria.ctlRelation), i, featureSearch, true);
EmbedSearchCriteriaControls(&criteria,sh);
}
/**********************************************************************
* DisposeSpecifier - dispose of any specifier controls
**********************************************************************/
static void DisposeSpecifier(MyWindowPtr win,SearchHandle sh,CriteriaInfo *pCriteria)
{
if (pCriteria->ctlSpecifier)
{
if ((*sh)->ctlFocus==pCriteria->ctlSpecifier)
(*sh)->ctlFocus = nil;
DisposeControl(pCriteria->ctlSpecifier);
pCriteria->ctlSpecifier = nil;
}
if (pCriteria->ctlPete) { DisposeControl(pCriteria->ctlPete); pCriteria->ctlPete = nil; }
if (pCriteria->pte)
{
Rect r;
PeteRect(pCriteria->pte,&r);
InsetRect(&r,-3,-3);
InvalWindowRect(GetMyWindowWindowPtr(win),&r); // make sure entire pete (including focus ring) is updated
PeteDispose(win,pCriteria->pte);
pCriteria->pte = nil;
}
if (pCriteria->searchString) { DisposeHandle((Handle)pCriteria->searchString); pCriteria->searchString = nil; }
}
/**********************************************************************
* RemoveCriteria - remove criteria
**********************************************************************/
static void RemoveCriteria(MyWindowPtr win,SearchHandle sh)
{
CriteriaInfo criteria=(*(*sh)->hCriteria)[--(*sh)->criteriaCount];
DisposeControl(criteria.ctlCategory);
DisposeControl(criteria.ctlRelation);
DisposeSpecifier(win,sh,&criteria);
AccuZap(criteria.hits);
SetHandleSize((Handle)(*sh)->hCriteria,(*sh)->criteriaCount*sizeof(CriteriaInfo));
}
/**********************************************************************
* SetSearchTopMargin - set top margin for Search window
**********************************************************************/
static void SetSearchTopMargin(MyWindowPtr win,SearchHandle sh)
{
short topMargin = kWinHeaderHt+kCriteriaHt*(*sh)->criteriaCount;
if ((*sh)->mailboxView)
topMargin -= kMBHdrHt;
SetTopMargin(win,topMargin);
}
/**********************************************************************
* CheckChaseArrows - see if we should spin the chase arrows
**********************************************************************/
static void CheckChaseArrows(SearchHandle sh)
{
if (TickCount()-(*sh)->chaseArrowTicks > kChaseArrowTicks)
{
SendControlMessage((*sh)->ctlChaseArrows,kControlMsgIdle,nil);
(*sh)->chaseArrowTicks = TickCount();
}
}
/**********************************************************************
* DisplayMBName - display name of mailbox we are searching
**********************************************************************/
static void DisplayMBName(SearchHandle sh, PStr name)
{
Str255 s;
GetRString(s,SEARCH_SEARCHING);
PCat(s,name);
SetTextControlText((*sh)->ctlMailBox,s,nil);
}
/**********************************************************************
* SearchTransfer - a message has been transferred, update Mailbox display
**********************************************************************/
static short FindMBIdx(TOCHandle tocH,FSSpecPtr toSpec,Boolean allowCreate)
{
FSSpecHandle specList = (*tocH)->mailbox.virtualMB.specList;
FSSpecPtr pSpec;
short i,cnt;
Boolean found=false;
if (!specList) return -1; // no mailboxes, not found
// do we already have this mailbox spec in the list?
pSpec = LDRef(specList);
cnt = (*tocH)->mailbox.virtualMB.specListCount;
for(i=0;i<cnt;i++,pSpec++)
{
if (SameSpec(toSpec,pSpec))
{
found = true;
break;
}
}
UL(specList);
if (!found)
{
if (allowCreate)
{
// not found, add to list
PtrAndHand(toSpec,(Handle)specList,sizeof(FSSpec));
(*tocH)->mailbox.virtualMB.specListCount++;
i = cnt;
}
else
i = -1; // not found
}
return i;
}
unsigned char SrchCharTabStrict[256] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ' ', ' ', 0x0B, 0x0C, ' ', 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7F,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
unsigned char SrchCharTabFudge[256] =
{
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ' ', ' ', 0x0B, 0x0C, ' ', 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '[', '\\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', 0x7F,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};
unsigned char *SrchCharTab = SrchCharTabFudge;
/**********************************************************************
* NormalizeSearchString - convert to lower case, all white space to space
**********************************************************************/
static void NormalizeSearchString(PStr s)
{
short i;
unsigned char *p;
for(i=*s,p=s+1;i;i--,p++)
*p=SrchCharTab[*p];
}
/**********************************************************************
* SearchStrText - search for a string in text
**********************************************************************/
long SearchStrText(PStr s,Ptr text,long length,long offset)
{
UPtr tp1,tp2,sp;
long tn,sn;
long sLen = *s;
if (sLen<1) return -1;
tp1=text+offset;
for(tn=length-sLen-offset+1;tn>0;tn--)
{
if (s[1] != SrchCharTab[*tp1]) goto NoMatch; // Chances are the first char doesn't match
// search from 2nd char
sp=s+2;
tp2=tp1+1;
for(sn=sLen-1;sn;sn--)
{
if (*sp != SrchCharTab[*tp2]) goto NoMatch;
sp++;
tp2++;
}
// match
return tp1-text;
NoMatch:
tp1++;
}
// not found
return -1;
}
/**********************************************************************
* FindHits - return pointer to hits accumulator
**********************************************************************/
static AccuPtr FindHits(SearchHandle sh, short idx)
{
CriteriaHandle hCriteria;
hCriteria = (*sh)->hCriteria;
if (idx>=0 && idx<(*sh)->criteriaCount)
return &(*hCriteria)[idx].hits;
ASSERT(0);
return nil;
}
/**********************************************************************
* GetHits - get hits accumulator
**********************************************************************/
static void GetHits(SearchHandle sh,short idx,AccuPtr a)
{
*a = *FindHits(sh,idx);
}
/**********************************************************************
* SetHits - set hits accumulator
**********************************************************************/
static void SetHits(SearchHandle sh,short idx,AccuPtr a)
{
AccuPtr acc;
if (a)
{
if (acc = FindHits(sh,idx))
*acc = *a;
ASSERT(acc);
}
else
{
// Dispose
Accumulator hits;
GetHits(sh,idx,&hits);
if (hits.data)
{
AccuZap(hits);
*FindHits(sh,idx) = hits;
}
}
}
/**********************************************************************
* SetupIMAPSearchCriteria - set up criteria for IMAP server search
**********************************************************************/
static IMAPSCHandle SetupIMAPSearchCriteria(SearchPtr sp)
{
IMAPSCHandle searchCriteria=NuHandle(0);
short i;
// Add criteria that require a message search
for(i=0;i<sp->criteriaCount;i++)
{
CriteriaInfo criteria=(*sp->hCriteria)[i];
if (gCategoryTable[criteria.category-1].flags & kNeedsMsg)
{
IMAPSCStruct searchInfo;
PCopy(searchInfo.string,*criteria.searchString);
searchInfo.headerCombination = kNone;
searchInfo.headerName = 0;
switch (criteria.category)
{
case scAnywhere: searchInfo.headerCombination = kAnyWhere; break;
case scHeaders: searchInfo.headerCombination = kAllHeaders; break;
case scBody: searchInfo.headerCombination = kBody; break;
case scTo: searchInfo.headerName = TO_HEAD+HEADER_STRN; break;
case scFrom: searchInfo.headerName = FROM_HEAD+HEADER_STRN; break;
case scSubject: searchInfo.headerName = SUBJ_HEAD+HEADER_STRN; break;
case scCC: searchInfo.headerName = CC_HEAD+HEADER_STRN; break;
case scBCC: searchInfo.headerName = BCC_HEAD+HEADER_STRN; break;
case scAnyRecipient: searchInfo.headerCombination = kAnyRecipient; break;
case scAttachNames: searchInfo.headerCombination = kBody; break;
}
PtrAndHand((Ptr)&searchInfo,(Handle)searchCriteria,sizeof(searchInfo));
}
}
return searchCriteria;
}
/**********************************************************************
* ProcessIMAPSearch - process IMAP server search results
**********************************************************************/
static void ProcessIMAPSearch(MyWindowPtr win,TOCHandle srchTOC,SearchHandle sh)
{
Handle toAdd = nil, toUpdate = nil, toDelete = nil, toCopy = nil;
MailboxNodeHandle mbox = nil;
IMAPSResultHandle hResults=nil;
FSSpec spec;
Boolean invalidateBoxSize = false;
CheckChaseArrows(sh);
// Check for results
if (IMAPDelivery(srchTOC,&toAdd,&toUpdate,&toDelete,&toCopy,nil,&hResults,&mbox,nil) && hResults)
{
short count = GetHandleSize_(hResults)/sizeof(IMAPSResultStruct);
IMAPSResultPtr pResult;
for(pResult=LDRef(hResults);count--;pResult++)
{
TOCHandle tocH;
Boolean opened;
if (GetBoxCountSpecLo((*sh)->imapBoxCount,pResult->box,&spec) &&
(tocH = GetSpecTOC(&spec,&opened)))
{
MSumPtr pSum;
short sumNum;
DEC_STATE(tocH);
L_STATE(tocH);
if ((sumNum = FindSumByHash(tocH,pResult->uidHash)) >= 0)
pSum = &(*tocH)->sums[sumNum];
else
pSum = nil;
if (!pSum)
{
// IMAP summary info hasn't been delivered yet. Do delivery and try again
U_STATE(tocH);
UpdateIMAPMailbox(tocH);
L_STATE(tocH);
if ((sumNum = FindSumByHash(tocH,pResult->uidHash)) >= 0)
pSum = &(*tocH)->sums[sumNum];
}
if (pSum)
{
// See if we need to do any searching in the summary
if (((*sh)->criteriaFlags & kNeedsSum) && !(*sh)->matchAny)
{
Boolean stop;
SearchMarker curr;
Zero(curr);
curr.box = pResult->box;
curr.message = sumNum;
curr.tocH = tocH;
if (!SearchMessage(win,pSum,sh,&curr,&stop,true))
pSum=nil; // This one doesn't match
}
}
if (pSum)
{
// Add this to search results!
short specIdx = (*(*sh)->imapBoxSpecIdx)[pResult->box];
if (AddSearchResult(sh,srchTOC,tocH,sumNum,specIdx==0,specIdx-1))
{
StopSearch(win);
U_STATE(tocH);
break;
}
if ((specIdx-1)<0)
// Added new spec
(*(*sh)->imapBoxSpecIdx)[pResult->box] = (*srchTOC)->mailbox.virtualMB.specListCount;
invalidateBoxSize = true;
}
U_STATE(tocH);
}
}
}
if (mbox == SEARCH_WINDOW)
// we're done!
StopSearch(win);
ZapHandle(hResults);
if (invalidateBoxSize)
UpdateBoxSize(win);
}
/**********************************************************************
* FindLinkSummary - find virtual summary that links to this one
**********************************************************************/
static MSumPtr FindLinkSummary(TOCHandle srchTocH, TOCHandle fromTocH, long serialNum, short *realSum, short *pVirtualMBIdx)
{
short i,count;
MSumPtr sum;
if (srchTocH && fromTocH)
{
FSSpec spec = GetMailboxSpec(fromTocH,-1);
short virtualMBIdx = FindMBIdx(srchTocH,&spec,false);
if (virtualMBIdx >= 0)
{
count = (*srchTocH)->count;
for(i=0,sum=(*srchTocH)->sums;i<count;i++,sum++)
if (serialNum==sum->u.virtualMess.linkSerialNum && virtualMBIdx == sum->u.virtualMess.virtualMBIdx)
{
// found it!
*realSum = i;
*pVirtualMBIdx = virtualMBIdx;
return sum;
}
}
}
return nil; // not found!
}
/**********************************************************************
* SearchUpdateSum - this summary has changed. See if we need to update
* summary in search window
**********************************************************************/
void SearchUpdateSum(TOCHandle tocH, short sumNum, TOCHandle fromTocH, long serialNum, Boolean transfer, Boolean nuke)
{
WindowPtr winWP;
MyWindowPtr win;
MSumPtr pSum;
FSSpec spec;
if (!gSearchWinCount) // Don't update from search window
return;
// Find search window(s)
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
if (IsSearchWindow(winWP))
{
SearchHandle sh;
TOCHandle srchTocH;
short srchSum;
short virtualMBIdx;
Boolean redraw = false;
win = GetWindowMyWindowPtr (winWP);
GetSearchInfo(win,&srchTocH,&sh);
if (nuke && !tocH)
{
// Just emptied the trash. Delete any messages that were in the trash.
short trashIdx;
spec.vRefNum = MailRoot.vRef;
spec.parID = MailRoot.dirId;
GetRString(spec.name,TRASH);
trashIdx = FindMBIdx(srchTocH,&spec,false);
if (trashIdx >= 0)
{
short sum;
for(sum=(*srchTocH)->count-1;sum>=0;sum--)
{
if ((*srchTocH)->sums[sum].u.virtualMess.virtualMBIdx==trashIdx)
{
DeleteSum(srchTocH,sum);
redraw = true;
}
}
}
}
else if (pSum = FindLinkSummary(srchTocH, fromTocH, serialNum, &srchSum, &virtualMBIdx))
{
if (nuke)
{
DeleteSum(srchTocH,srchSum);
redraw = true;
}
else
{
if (transfer)
{
// Change MB idx and assign new serialNumber
spec = GetMailboxSpec(tocH,sumNum);
(*srchTocH)->sums[srchSum].u.virtualMess.virtualMBIdx = FindMBIdx(srchTocH,&spec,true);
(*srchTocH)->sums[srchSum].u.virtualMess.linkSerialNum = (*tocH)->sums[sumNum].serialNum;
}
else
// Update summary
CopySum(&(*tocH)->sums[sumNum],pSum,pSum->u.virtualMess.virtualMBIdx);
InvalSum(srchTocH,srchSum);
}
}
else if (!nuke && tocH && !PrefIsSet(PREF_NO_LIVE_SEARCHES))
{
if (!SearchBoxesInclude(sh,fromTocH) && SearchBoxesInclude(sh,tocH))
SearchIncremental(win,tocH,sumNum);
}
if (redraw)
{
InvalContent(win);
UpdateBoxSize(win);
}
}
}
/**********************************************************************
* SearchBoxesInclude - does the search domain include this tocH?
**********************************************************************/
Boolean SearchBoxesInclude(SearchHandle sh,TOCHandle tocH)
{
short count = HandleCount((*sh)->BoxCount);
FSSpec searchSpec;
while (count--)
{
GetBoxCountSpec(sh,count,&searchSpec);
if (SameSpec(&searchSpec,&(*tocH)->mailbox.spec)) return true;
}
return false;
}
/**********************************************************************
* SearchIncremental - add a message to this search, if it matches
**********************************************************************/
Boolean SearchIncremental(MyWindowPtr win,TOCHandle tocH,short sumNum)
{
TOCHandle srchTocH;
SearchHandle sh;
SearchMarker marker;
Boolean oldNoBulk;
Boolean found;
short specIdx;
marker.tocH = tocH;
marker.message = sumNum;
GetSearchInfo(win,&srchTocH,&sh);
ASSERT((*sh)->setup); // avoid searches that haven't been started yet.
if ((*sh)->setup)
{
oldNoBulk = (*sh)->noBulkSearch;
(*sh)->noBulkSearch = true;
found = SearchMessage(win,&(*tocH)->sums[sumNum],sh,&marker,nil,false);
(*sh)->noBulkSearch = oldNoBulk;
if (found)
{
if (!(*srchTocH)->mailbox.virtualMB.specList) (*srchTocH)->mailbox.virtualMB.specList = NuHandle(0);
// this routine will add the spec to the specList if necessary
VERIFY(0<=(specIdx=FindMBIdx(srchTocH,&(*tocH)->mailbox.spec,true)));
// and now we can add it!
AddSearchResult(sh,srchTocH,tocH,sumNum,false,specIdx);
return true;
}
}
return false;
}
/**********************************************************************
* IMAPSearchIncremental - an IMAP mailbox has changed. Update all
* search windows that are currently searching this mailbox.
**********************************************************************/
void IMAPSearchIncremental(MailboxNodeHandle mbox)
{
WindowPtr winWP;
MyWindowPtr win;
SearchHandle sh;
TOCHandle toc;
short i;
FSSpec spec;
long lastSearchedUID = 0;
long firstUid = (*mbox)->searchUID + 1; // where to start the incremental search from
IMAPSCHandle searchCriteria;
for (winWP = GetWindowList(); winWP; winWP = GetNextWindow (winWP))
{
win = GetWindowMyWindowPtr (winWP);
if (IsSearchWindow(winWP))
{
GetSearchInfo(win,&toc,&sh);
if ((*sh)->setup)
{
// make sure we're up to date on the selected mailboxes
if ((*sh)->imapBoxCount == NULL)
GetSelectedBoxes(*sh, toc,false);
if ((*sh)->imapBoxCount)
{
// is this mailbox amongst the mailboxes to search in this search window?
for (i = 0; GetBoxCountSpecLo((*sh)->imapBoxCount, i, &spec); i++)
{
if (SameSpec(&spec, &(*mbox)->mailboxSpec))
{
// re-run the search on this particular mailbox
if (searchCriteria = SetupIMAPSearchCriteria(*sh))
{
// IMAPSearch will return false if no searches were actually started.
if (IMAPSearchMailbox(toc,(*sh)->imapBoxCount,mbox,searchCriteria,!(*sh)->matchAny,firstUid))
(*sh)->searchMode |= kSearchingIMAP;
}
}
}
}
}
}
}
}
/**********************************************************************
* SearchInvalTocBox - this summary needs to be updated. See if any
* summaries in search windows need to also be updated
**********************************************************************/
void SearchInvalTocBox(TOCHandle tocH, short sumNum, short box)
{
WindowPtr winWP;
if (!gSearchWinCount) // No search windows
return;
// Find search window(s)
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
if (IsSearchWindow(winWP))
{
SearchHandle sh;
TOCHandle srchTocH;
short srchSum,virtualMBIdx;
GetSearchInfo(GetWindowMyWindowPtr(winWP),&srchTocH,&sh);
if (FindLinkSummary(srchTocH,tocH,(*tocH)->sums[sumNum].serialNum,&srchSum,&virtualMBIdx))
InvalTocBoxLo(srchTocH,srchSum,box);
}
}
/**********************************************************************
* TellSearchMBRename - notify search windows that mailbox has been renamed
**********************************************************************/
void TellSearchMBRename(FSSpecPtr spec,FSSpecPtr newSpec)
{
WindowPtr winWP;
MyWindowPtr win;
if (!gSearchWinCount)
return;
// Find search window(s)
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
if (IsSearchWindow(winWP))
{
SearchHandle sh;
TOCHandle tocH;
short index;
win = GetWindowMyWindowPtr (winWP);
GetSearchInfo(win,&tocH,&sh);
index = FindMBIdx(tocH,spec,false);
if (index >= 0)
{
// Found it. Replace.
(*(*tocH)->mailbox.virtualMB.specList)[index] = *newSpec;
InvalContent(win); // Update window
}
}
}
/**********************************************************************
* GetTOCFromSearchWin - see if any search windows have opened a TOC not in TOC list
* Called from OpenMailbox. TOC window will be opened and TOC added to TOC list
**********************************************************************/
TOCHandle GetTOCFromSearchWin(FSSpecPtr spec)
{
WindowPtr winWP;
MyWindowPtr win;
if (!gSearchWinCount)
return nil;
// Find search window(s)
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
if (IsSearchWindow(winWP))
{
SearchHandle sh;
TOCHandle tocH;
FSSpec thisSpec;
win = GetWindowMyWindowPtr (winWP);
GetSearchInfo(win,nil,&sh);
if ((tocH=(*sh)->curr.tocH) && (*sh)->curr.opened)
{
ASSERT(*tocH!=(TOCPtr)-1);
if (*tocH==(TOCPtr)-1)
{
// ACK! Fix this!
(*sh)->curr.tocH = nil;
(*sh)->curr.opened = false;
continue;
}
thisSpec = GetMailboxSpec(tocH,-1);
if (SameSpec(&thisSpec,spec))
{
(*sh)->curr.opened = false; // TOC window will be opened and TOC added to TOC list
return tocH;
}
}
}
return nil;
}
/**********************************************************************
* SelectMailbox - select a mailbox in mailbox list
**********************************************************************/
static void SelectMailbox(FSSpecPtr spec,ViewListPtr pView,Boolean folder,Boolean addToSelection)
{
Str63 name;
short menuID;
Str255 s;
Boolean isIMAP;
PCopy(name,spec->name);
if (isIMAP = IsIMAPMailboxFile(spec))
ParentSpec(spec,spec);
menuID = VD2MenuId(spec->vRefNum,spec->parID);
if (folder)
{
if (menuID == MAILBOX_MENU)
{
menuID = kEudoraFolder;
GetRString(name,FILE_ALIAS_EUDORA_FOLDER);
}
else
{
// get mail folder
short item;
MenuHandle mhPar = ParentMailboxMenu(GetMenuHandle(menuID),&item);
menuID = GetMenuID(mhPar);
GetMenuItemText(mhPar,item,name);
if (menuID == MAILBOX_MENU && isIMAP)
menuID = kIMAPFolder;
}
}
// open any folders above
OpenMBFolder(pView,menuID,s);
// now select our item
LVSelect(pView,menuID,name,addToSelection);
}
/**********************************************************************
* SearchSave - save contents of Search window
**********************************************************************/
static void SearchSave(MyWindowPtr win,Boolean saveAs)
{
SearchHandle sh;
short refN,i;
FSSpec spec;
TOCHandle tocH;
short saveResFile = CurResFile();
Str255 s;
OSErr err;
SearchPtr sp;
GetSearchInfo(win,&tocH,&sh);
sp = LDRef(sh);
SetupSearchCriteria(win,sp);
UL(sh);
spec= (*sh)->saveSpec;
if (saveAs || !spec.vRefNum)
{
// User selects file
long newDirId;
FSSpec folderSpec;
DirCreate(Root.vRef,Root.dirId,GetRString(s,SEARCH_FOLDER),&newDirId);
SubFolderSpec(SEARCH_FOLDER,&folderSpec);
spec = folderSpec;
*s = 0;
AddCriteriaText(sh,s); // Get text description of criteria
if (*s > 31) *s = 31; // Truncate to 31 chars
PCopy(spec.name,s);
err = SFPutOpen(&spec,CREATOR,SEARCH_FILE_TYPE,&refN,nil,nil,0,&folderSpec,nil,nil);
if (err)
return;
MyFSClose(refN);
(*sh)->saveSpec = spec;
SetWTitle(GetMyWindowWindowPtr(win),spec.name);
}
FSpKillRFork(&spec);
FSpCreateResFile(&spec,CREATOR,SEARCH_FILE_TYPE,smSystemScript);
if (-1!=(refN=FSpOpenResFile(&spec,fsRdWrPerm)))
{
Handle hSave,hSpecList;
SearchSaveInfo saveInfo;
ViewListPtr pView;
UseResFile(refN);
Zero(saveInfo);
saveInfo.criteriaCount = (*sh)->criteriaCount;
if (HasFeature (featureSearch))
saveInfo.matchMode = GetControlValue((*sh)->ctlMatch);
saveInfo.tabMode = GetControlValue((*sh)->ctlTabs);
if (!PtrToHand(&saveInfo,&hSave,sizeof(saveInfo)))
{
for(i=0;i<saveInfo.criteriaCount;i++)
{
CriteriaInfo criteria=(*(*sh)->hCriteria)[i];
CriteriaSave saveCriteria;
saveCriteria.category = MenuCategory2SavedCategory[criteria.category];
saveCriteria.relation = GetControlValue(criteria.ctlRelation);
saveCriteria.specifier = criteria.ctlSpecifier ? GetControlValue(criteria.ctlSpecifier) : 0;
saveCriteria.misc = criteria.misc;
saveCriteria.date = criteria.date;
*s = 0;
if (criteria.pte)
PeteString(s,criteria.pte);
// No personalities in Light
else if (HasFeature (featureMultiplePersonalities) && criteria.category==scPersonality)
{
if (saveCriteria.specifier > 1)
// Save personality by name in case menu changes
GetMenuItemText(GetPopupMenuHandle(criteria.ctlSpecifier),saveCriteria.specifier,s);
}
if (*s)
{
// Save string in separate resource
StringHandle hString = NewString(s);
AddResource(hString,'STR ',1000+i,"");
saveCriteria.stringID = 1000+i;
}
PtrAndHand(&saveCriteria,hSave,sizeof(saveCriteria));
}
AddResource(hSave,SEARCH_FILE_TYPE,1000,"");
}
// Save aliases to each selected mailbox if not all selected
if ((pView = (*sh)->list) && (LVCountSelection(pView) != (*pView->hList)->dataBounds.bottom))
{
short i;
VLNodeInfo data;
for(i=1;i<=LVCountSelection(pView);i++)
{
AliasHandle alias;
LVGetItem(pView,i,&data,true);
MakeMBSpec(&data,&spec);
NewAliasMinimal(&spec,&alias);
if (alias)
AddResource((Handle)alias,rAliasType,1000+i,"");
}
}
// Save TOC handle
HandToHand((Handle *) &tocH);
AddResource((Handle)tocH,TOC_TYPE,1000,"");
if (hSpecList = (Handle)(*tocH)->mailbox.virtualMB.specList)
{
HandToHand(&hSpecList);
AddResource(hSpecList,kSpecListType,1000,"");
}
// Save window position
SearchPosition(true,win);
// Save sort criteria
Sort2String(s,tocH);
{
StringHandle hString = NewString(s);
AddResource(hString,'STR ',SEARCH_SAVED_SORT_ID,"");
}
CloseResFile(refN);
win->isDirty = false;
BuildSearchMenu();
}
UseResFile(saveResFile);
}
/**********************************************************************
* OpenSearchMenu - open a search file
**********************************************************************/
void OpenSearchMenu(short item)
{
FSSpec spec;
MenuHandle mh = GetMHandle(FIND_HIER_MENU);
SubFolderSpec(SEARCH_FOLDER,&spec);
GetMenuItemText(mh,item,spec.name);
OpenSearchFileAndStart(&spec);
}
/**********************************************************************
* OpenSearchFileAndStart - open a search file, and start the search
**********************************************************************/
MyWindowPtr OpenSearchFileAndStart(FSSpecPtr spec)
{
MyWindowPtr win = OpenSearchFile(spec);
if (win)
{
TOCHandle tocH;
GetSearchInfo(win,&tocH,nil);
#ifdef WE_EVER_GET_CLEVER
if (!(*tocH)->count)
#else
// setup for incremental searching. Until we get clever, that is.
if (!(*tocH)->count || !PrefIsSet(PREF_NO_LIVE_SEARCHES))
#endif
// If no results, start search
StartSearch(win);
}
return win;
}
/**********************************************************************
* OpenSearchFile - open a search file
**********************************************************************/
MyWindowPtr OpenSearchFile(FSSpecPtr spec)
{
WindowPtr winWP;
MyWindowPtr win;
SearchHandle sh;
TOCHandle tocH;
Str255 s;
Rect r;
short saveResFile = CurResFile();
short tabMode = 0;
FSSpec aliasSpec;
// Is search window already open?
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
if (IsSearchWindow(winWP))
{
win = GetWindowMyWindowPtr (winWP);
GetSearchInfo(win,nil,&sh);
if (SameSpec(spec,&(*sh)->saveSpec))
{
// Already open
UserSelectWindow(winWP);
return win;
}
}
if (win = SearchOpen(0))
{
Handle hTOCRes;
short refN;
winWP = GetMyWindowWindowPtr (win);
GetSearchInfo(win,&tocH,&sh);
if (-1!=(refN=FSpOpenResFile(spec,fsRdPerm)))
{
Handle hSave;
short cntAlias;
ViewListPtr pView;
UseResFile(refN);
if (hSave = GetResource(SEARCH_FILE_TYPE,1000))
{
SearchSaveInfo *pSaveInfo = LDRef(hSave);
CriteriaHandle hCriteria = (*sh)->hCriteria;
CriteriaSave *pSaveCriteria;
short count,
item,
i;
while ((*sh)->criteriaCount)
RemoveCriteria(win,sh);
(*sh)->criteriaCount = pSaveInfo->criteriaCount;
if (HasFeature (featureSearch))
SetControlValue((*sh)->ctlMatch,pSaveInfo->matchMode);
tabMode = pSaveInfo->tabMode;
for(i=0,pSaveCriteria=pSaveInfo->criteria;i<pSaveInfo->criteriaCount;i++,pSaveCriteria++)
{
CriteriaInfo criteria;
short specifier;
LongDateRec date;
Zero(criteria);
criteria.category = SafeSavedCategory2MenuCategory(pSaveCriteria->category);
criteria.relation = pSaveCriteria->relation;
criteria.specifier = pSaveCriteria->specifier;
criteria.misc = pSaveCriteria->misc;
criteria.date = pSaveCriteria->date;
criteria.ctlCategory = CreateMenuControl(win,win->topMarginCntl,"\p",SRCH_CATEGORY_MENU,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,criteria.category,false);
criteria.ctlRelation = CreateMenuControl(win,win->topMarginCntl,"\p",gCategoryTable[criteria.category-1].relation,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,criteria.relation,false);
// (jp) 1-18-00 For light...
count = CountMenuItems (GetPopupMenuHandle (criteria.ctlCategory));
for (item = 1; item <= count; ++item)
if (gCategoryTable[item - 1].proOnly)
SetMenuItemBasedOnFeature (GetPopupMenuHandle(criteria.ctlCategory), item, featureSearch, true);
count = CountMenuItems (GetPopupMenuHandle (criteria.ctlRelation));
for (item = strContains + 1; item <= count; ++item)
SetMenuItemBasedOnFeature (GetPopupMenuHandle(criteria.ctlRelation), item, featureSearch, true);
*s = 0;
if (pSaveCriteria->stringID)
GetRStr(s,pSaveCriteria->stringID);
specifier = gCategoryTable[criteria.category-1].specifier;
switch (specifier)
{
case ssText:
case ssAge:
case ssNumber:
CreateTextSpecifier(win,&criteria,specifier!=ssText);
PeteSetString(s,criteria.pte);
if (specifier==ssAge)
criteria.ctlSpecifier = CreateMenuControl(win,win->topMarginCntl,"\p",SRCH_AGE_UNITS_MENU,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,criteria.specifier,false);
break;
case ssDate:
SetRect(&r,-4000,-4000,kDateWi-4000,kDateHt-4000);
criteria.ctlSpecifier = NewControl(winWP,&r,"\p",true,0,0,0,kControlClockDateProc,0);
EmbedControl(criteria.ctlSpecifier,win->topMarginCntl);
Zero(date);
date.ld.day = pSaveCriteria->date.day;
date.ld.month = pSaveCriteria->date.month;
date.ld.year = pSaveCriteria->date.year;
SetControlData(criteria.ctlSpecifier,0,kControlClockLongDateTag,sizeof(date),(Ptr)&date);
break;
case PERS_HIER_MENU:
// No personalities in Light
if (HasFeature (featureMultiplePersonalities) && *s)
criteria.specifier = FindItemByName(GetMenuHandle(PERS_HIER_MENU),s);
// Fall thru
default:
criteria.ctlSpecifier = CreateMenuControl(win,win->topMarginCntl,"\p",specifier,kControlPopupFixedWidthVariant+kControlPopupUseWFontVariant,criteria.specifier,false);
break;
}
PtrAndHand(&criteria,hCriteria,sizeof(criteria));
EmbedSearchCriteriaControls(&criteria,sh);
}
}
// Setup selected mailboxes from alias resource
if (pView = (*sh)->list)
{
if (cntAlias = Count1Resources(rAliasType))
{
short i;
for(i=1;i<=cntAlias;i++)
{
AliasHandle alias;
if (alias = (AliasHandle)GetIndResource(rAliasType,i))
{
if (!SimpleResolveAliasNoUI(alias,&aliasSpec))
SelectMailbox(&aliasSpec,pView,false,i>1);
ReleaseResource((Handle)alias);
}
}
}
else
{
// Select all
Cell c;
c.h = 0;
for(c.v=0;c.v<(*pView->hList)->dataBounds.bottom;c.v++)
LSetSelect(true, c, pView->hList);
LVUpdateSelection(pView);
}
}
// Get TOC
if (hTOCRes = GetResource(TOC_TYPE,1000))
{
FSSpecHandle specList,specListRes;
TOCType saveTOC = **tocH;
short sumCount;
short previewHi;
SetHandleSize((Handle)tocH,0);
HandAndHand(hTOCRes,(Handle)tocH);
sumCount = (*tocH)->count;
previewHi = (*tocH)->previewHi;
BMD(&saveTOC,*tocH,sizeof(TOCType)-sizeof(MSumType)); // Only summaries and TOC summary count should change
(*tocH)->count = sumCount;
(*tocH)->previewHi = previewHi;
CleanseTOC(tocH); (*tocH)->virtualTOC = true; // CleanseTOC steps on that. :-(
specList = (*tocH)->mailbox.virtualMB.specList;
if (specListRes = (FSSpecHandle)GetResource(kSpecListType,1000))
{
if (!specList)
{
specList = NuHandle(0);
(*tocH)->mailbox.virtualMB.specList = specList;
}
else
SetHandleSize(specList,0);
HandAndHand(specListRes,specList);
(*tocH)->mailbox.virtualMB.specListCount = HandleCount(specList);
}
else
{
(*tocH)->mailbox.virtualMB.specListCount = 0;
if (specList)
SetHandleSize(specList,0);
}
}
(*sh)->saveSpec = *spec;
SetWTitle(winWP,spec->name);
// restore sort
{
Handle h = Get1Resource('STR ',SEARCH_SAVED_SORT_ID);
if (h)
{
PCopy(s,*h);
ApplySortString(tocH,s);
ReleaseResource(h);
}
}
}
AdjustForCriteriaCount(sh);
if (tabMode && tabMode != GetControlValue((*sh)->ctlTabs))
{
SetControlValue((*sh)->ctlTabs,tabMode);
SearchButton(win,(*sh)->ctlTabs,0,tabMode);
}
ShowMyWindow(winWP);
UserSelectWindow(winWP);
#ifdef WE_EVER_GET_CLEVER
// setup for incremental searching
if (!PrefIsSet(PREF_NO_LIVE_SEARCHES))
{
// This would work fine except that it won't pickup messages added while window was NOT open
// For now, that's not acceptable, so we'll assume the caller will start the search
GetSelectedBoxes(*sh,tocH,false);
SetupSearchCriteria(win,*sh);
}
#endif
if (refN!=-1)
CloseResFile(refN);
}
UseResFile(saveResFile);
return win;
}
/**********************************************************************
* BuildSearchMenu - add saved search windows to menu
**********************************************************************/
void BuildSearchMenu(void)
{
MenuHandle mh = GetMHandle(FIND_HIER_MENU);
Boolean haveDivider = false;
FSSpec folderSpec;
Str255 s;
CInfoPBRec hfi;
short item;
// Remove any existing items
for(item=CountMenuItems(mh);item>=FIND_MENU_LIMIT;item--)
DeleteMenuItem(mh,item);
if (!SubFolderSpec(SEARCH_FOLDER,&folderSpec))
{
hfi.hFileInfo.ioNamePtr = s;
hfi.hFileInfo.ioFDirIndex = 0;
while (!DirIterate(folderSpec.vRefNum,folderSpec.parID,&hfi))
if (!(hfi.hFileInfo.ioFlAttrib&0x10) && (hfi.hFileInfo.ioFlFndrInfo.fdType==SEARCH_FILE_TYPE))
{
if (!haveDivider)
{
AppendMenu(mh,"\p-");
haveDivider = true;
}
MyAppendMenu(mh,s);
}
}
}
/**********************************************************************
* GetMenuText - return text of selected item in menu control
**********************************************************************/
static void GetMenuText(ControlHandle cntl,StringPtr s)
{
GetMenuItemText(GetPopupMenuHandle(cntl),GetControlValue(cntl),s);
}
/**********************************************************************
* GetCriteriaString - return a string representation of a criterion
**********************************************************************/
static void GetCriteriaString(StringPtr s,CriteriaPtr criteria)
{
Str255 sTemp;
GetMenuText(criteria->ctlCategory,s);
GetMenuText(criteria->ctlRelation,sTemp);
PCatC(s,' ');
PCat(s,sTemp);
if (criteria->pte)
{
PeteString(sTemp,criteria->pte);
if (*sTemp + *s >= 250) return; // too big!
PCatC(s,' ');
PCatC(s,'"');
PCat(s,sTemp);
PCatC(s,'"');
}
if (criteria->ctlSpecifier)
{
long junk;
LongDateRec date;
LongDateTime time;
if (criteria->category==scDate)
{
Zero(date);
GetControlData(criteria->ctlSpecifier,0,
kControlClockLongDateTag,sizeof(date),(Ptr)&date,&junk);
LongDateToSeconds(&date,&time);
LongDateString(&time,shortDate,sTemp,nil);
}
else
GetMenuText(criteria->ctlSpecifier,sTemp);
PCatC(s,' ');
PCat(s,sTemp);
}
}
/**********************************************************************
* AddCriteriaText - append a textual representation of criteria
**********************************************************************/
static void AddCriteriaText(SearchHandle sh, StringPtr sText)
{
short i;
Str255 s;
for(i=0;i<(*sh)->criteriaCount;i++)
{
CriteriaInfo criteria=(*(*sh)->hCriteria)[i];
GetCriteriaString(s,&criteria);
if (*sText + *s > 250)
break; // it's getting too long
if (i)
PCat(sText,"\p, ");
PCat(sText,s);
}
}
/**********************************************************************
* SearchSetWTitle - set the title of the search message
**********************************************************************/
static void SearchSetWTitle(MyWindowPtr win,SearchHandle sh)
{
WindowPtr winWP = GetMyWindowWindowPtr(win);
Str255 sTitle,s;
if (!(*sh)->saveSpec.vRefNum) // don't change title of saved windows
{
GetRString(sTitle,SEARCH_SEARCH);
PCat(sTitle,"\p: ");
AddCriteriaText(sh,sTitle);
GetWTitle(winWP,s);
if (!StringSame(s,sTitle)) // don't change title if hasn't changed
SetWTitle_(winWP,sTitle);
}
}
/************************************************************************
* SearchRestorePosition - save/restore window position
************************************************************************/
static Boolean SearchPosition(Boolean save,MyWindowPtr win)
{
Str255 title;
SearchHandle sh;
GetSearchInfo(win,nil,&sh);
if ((*sh)->saveSpec.vRefNum)
// If window has been saved, save by window title
return PositionPrefsTitle(save,win);
// Save/restore based on title "Search"
GetRString(title,SEARCH_SEARCH);
return PositionPrefsByName(save,win,title);
}
/************************************************************************
* SearchMBUpdate - update mailbox list
************************************************************************/
void SearchMBUpdate(void)
{
WindowPtr winWP;
if (!gSearchWinCount)
return;
// Find search window(s)
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
if (IsSearchWindow(winWP))
{
SearchHandle sh;
GetSearchInfo(GetWindowMyWindowPtr (winWP),nil,&sh);
InvalidListView((*sh)->list);
}
}
/************************************************************************
* SearchFixUnread - update mailbox list unread status
************************************************************************/
void SearchFixUnread(MenuHandle mh,short item,Boolean unread)
{
WindowPtr winWP;
if (!gSearchWinCount)
return;
// Find search window(s)
for (winWP=FrontWindow_();winWP;winWP=GetNextWindow(winWP))
if (IsSearchWindow(winWP))
{
SearchHandle sh;
GetSearchInfo(GetWindowMyWindowPtr (winWP),nil,&sh);
MBFixUnreadLo((*sh)->list,mh,item,unread,(*sh)->mailboxView);
}
}
/************************************************************************
* ShowHideTabs - need to clip tab control on Mac OS X since control itself doesn't
************************************************************************/
static void ShowHideTabs(ControlRef ctlTabs,Boolean show)
{
Rect rCtl;
ClipRect(GetControlBounds(ctlTabs,&rCtl));
if (show) ShowControl(ctlTabs);
else HideControl(ctlTabs);
InfiniteClip(GetWindowPort(GetControlOwner(ctlTabs)));
}
/************************************************************************
* EmbedSearchCriteriaControls - make sure search criteria controls are
* imbedded within group control, otherwise they don't respond under OS X
************************************************************************/
static void EmbedSearchCriteriaControls(CriteriaInfo *criteria,SearchHandle sh)
{
ControlHandle ctlCriteriaGroup = (*sh)->ctlCriteriaGroup;
EmbedControl(criteria->ctlCategory,ctlCriteriaGroup);
EmbedControl(criteria->ctlRelation,ctlCriteriaGroup);
if (criteria->ctlSpecifier)
EmbedControl(criteria->ctlSpecifier,ctlCriteriaGroup);
}
/************************************************************************
* SearchViewIsMailbox - are we looking at the mailboxes view of the search
* window?
************************************************************************/
Boolean SearchViewIsMailbox(TOCHandle tocH)
{
SearchHandle sh = nil;
if (!(*tocH)->virtualTOC) return false;
if (!IsSearchWindow(GetMyWindowWindowPtr((*tocH)->win))) return false;
GetSearchInfo((*tocH)->win,nil,&sh);
return sh && (*sh)->mailboxView;
}
#endif