/* 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)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;icriteriaCount;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;icriteriaCount;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;icriteriaCount;i++) if (!MustSearchBody((*sp->hCriteria)[i].category)) sp->searchOrder[orderIdx++] = i; for(i=0;icriteriaCount;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;ivBar,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->minuteminute?1:0); case sauHours: return DateTimeDifference(date,currDate,seconds,sauMinutes)/60 + ((seconds>=0&&currDate->minuteminute)?1:0); case sauDays: return DateTimeDifference(date,currDate,seconds,sauHours)/24 + ((seconds>=0&&currDate->hourhour)?1:0); case sauWeeks: return DateTimeDifference(date,currDate,seconds,sauDays)/7 + ((seconds>=0&&currDate->dayOfWeekdayOfWeek)?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;hitnoBulkSearch) { 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;iioParam.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;iioParam.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) 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;imailbox.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;icriteriaCount;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;iu.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;ihCriteria)[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;icriteriaCount;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