/* Copyright (c) 2017, Computer History Museum All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "mbwin.h" #include "listview.h" #define FILE_NUM 23 /* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */ /********************************************************************** * handling the mailbox window **********************************************************************/ // First few items in list enum { kItemEudoraFolder=1,kItemInBox,kItemOutBox,kItemJunkBox,kItemTrash,kItemMailBoxes }; // ViewListIcon - Icon constants for list view enum { kFolderIcon=kGenericFolderIconResource, kTrashBoxIcon=kTrashIconResource, kMailBoxIcon=MAILBOX_ONLY_ICON, kIMAPMailboxIcon=IMAP_MAILBOX_FILE_ICON, kIMAPPolledMailboxIcon=RESYNC_ICON, kIMAPPolledFolderIcon=IMAP_POLLED_MAILBOX_FILE_ICON }; #define dashChar ((unsigned char)'-') Boolean gfMessageDrag,gfBoxDrag; // define this to include the Resync button. Remove this all eventually if it's not missed. //#define SHOW_RESYNC_BUTTON #pragma segment MboxWin /************************************************************************ * the structure for the mailbox window ************************************************************************/ typedef enum { NewMBIndex, NewFolderIndex, RemoveIndex, #ifdef SHOW_RESYNC_BUTTON ResyncIndex, #endif OptionsMenuIndex, ControlCount } ControlEnum; typedef struct { ViewList list; MyWindowPtr win; ControlHandle controls[ControlCount]; Boolean dontTickle; Boolean inited; Boolean IMAPButtonsVisible; #ifdef SHOW_RESYNC_BUTTON Boolean resyncEnabled; Boolean imapMailboxSelected; #endif } MBType; MBType MB; #define Win MB.win #define DontTickle MB.dontTickle #define Controls MB.controls #ifdef SHOW_RESYNC_BUTTON #define cntMBControls 5 #else #define cntMBControls 4 #endif #define CtlRemove MB.controls[RemoveIndex] #define CtlNewMB MB.controls[NewMBIndex] #define CtlNewFolder MB.controls[NewFolderIndex] #ifdef SHOW_RESYNC_BUTTON #define CtlResync MB.controls[ResyncIndex] #endif #define CtlOptionsMenu MB.controls[OptionsMenuIndex] /************************************************************************ * Structure for remembering which folders are expanded. * Saved as a resource in the settings file ************************************************************************/ struct ExpandListRec { short count; long dirID[]; }; typedef struct ExpandListRec ExpandListRec; ExpandInfo gExpandList; typedef struct { short cmd; short mark; Style style; } MenuProperties; /************************************************************************ * prototypes ************************************************************************/ Boolean MBMenu(MyWindowPtr win, int menu, int item, short modifiers); void MBDidResize(MyWindowPtr win, Rect *oldContR); void MBZoomSize(MyWindowPtr win,Rect *zoom); void MBUpdate(MyWindowPtr win); Boolean MBClose(MyWindowPtr win); short MBFill(void); short MBFillSide(MenuHandle mh); void MBClick(MyWindowPtr win,EventRecord *event); void MBCursor(Point mouse); void MBActivate(MyWindowPtr win); void MBSetControls(void); void MBHit(short which, Boolean optionPressed); static void MBIdle(MyWindowPtr win); static short WhichTree(VLNodeInfo *pInfo); static void EnableIMAPButtons(MyWindowPtr win); static Boolean MBKey(MyWindowPtr win, EventRecord *event); void MBListOpen(ViewListPtr pView); void MBListClose(short whichSide); static void MBMenuToFile(short menuID, short *pVRef, long *pDirID); static void MBMove(VLNodeInfo *pTargetInfo); static void MBRemove(void); static OSErr MBDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag); short FindItemBySubmenu(MenuHandle mh, short subID); short MBRefill(UPtr andSelect); static void DoNewMailbox(Boolean fFolder); Boolean DoRemoveBox(MenuHandle mh,UPtr name,Boolean multiple,Boolean *andShutUp,Boolean *needRefill); static short DeleteMailFolder(MenuHandle mh,short item); void DoMoveMailbox(UPtr selectName,short si); Boolean SameSelected(void); Boolean MBGetData(VLNodeInfo *data,short item); void MBHelp(MyWindowPtr win,Point mouse); // void MBFixIndices(void); static void SetExpandListSize(ExpandInfoPtr pExpList); static void GetMenuItemProperties(MenuHandle hMenu,short item,MenuProperties *pFormat); static void AddMBMenuItem(MenuHandle hMenu,StringPtr sName,MenuProperties *pFormat); static void MBGrow(MyWindowPtr win,Point *newSize); static Boolean MBFind(MyWindowPtr win,PStr what); static Boolean IsSpecialBox(ViewListPtr pView, VLNodeInfo *pInfo); Boolean IsIMAPTrashBox(VLNodeInfo *data); OSErr MBTellOthersRename(FSSpecPtr spec,FSSpecPtr newSpec,Boolean folder,Boolean will,Boolean dontWarn); OSErr GetSelectedMailboxSpecs(Handle *mailboxes, Boolean optionPressed); OSErr GetSelectedChildMailboxSpecs(Accumulator *b, MailboxNodeHandle toAdd, Boolean childrenToo); void EnableOptionsMenu(MyWindowPtr win, Boolean optionPressed); char IMAPOptionsCheckMarkChar(MailboxNeedsEnum needs); long IMAPMailboxIcon(VLNodeInfo *info, short menuID); void MBRefreshIMAPMailboxes(Boolean optionPressed); OSErr MBResyncOrExpungeIMAPMailboxes(Boolean optionPressed, TaskKindEnum task); void MBMarkIMAPMailboxes(Boolean on, MailboxNeedsEnum needs); void MBCompactMailboxes(void); /************************************************************************ * OpenMBWin - open the mailbox window ************************************************************************/ void OpenMBWin(void) { WindowPtr WinWP = nil; if (SelectOpenWazoo(MB_WIN)) return; // Already opened in a wazoo if (!MB.inited) { short err=0; Rect r; if (!(Win=GetNewMyWindow(MBWIN_WIND,nil,nil,BehindModal,False,False,MB_WIN))) {err=MemError(); goto fail;} WinWP = GetMyWindowWindowPtr (Win); SetWinMinSize(Win,-1,-1); SetPort_(GetMyWindowCGrafPtr(Win)); ConfigFontSetup(Win); MySetThemeWindowBackground(Win,kThemeListViewBackgroundBrush,False); /* * controls */ if (!(CtlNewMB = NewIconButton(MB_NEWMB_CNTL,WinWP)) || !(CtlNewFolder = NewIconButton(MB_NEWFOLDER_CNTL,WinWP)) || !(CtlRemove = NewIconButton(MB_REMOVE_CNTL,WinWP)) || #ifdef SHOW_RESYNC_BUTTON !(CtlResync = NewIconButton(MB_RESYNC_CNTL,WinWP)) || #endif !(CtlOptionsMenu = NewIconButtonMenu(MAILBOX_OPTIONS_MENU,REFRESH_ICON,WinWP))) goto fail; #ifdef SHOW_RESYNC_BUTTON MB.resyncEnabled = true; #endif MB.IMAPButtonsVisible = true; MBIdle(Win); /* * list */ gExpandList.resID = kMBExpandID; SetRect(&r,0,0,20,20); if (LVNew(&MB.list,Win,&r,0,MailboxesLVCallBack,kMBDragType)) {err=MemError(); goto fail;} // if (MBFill()) goto fail; Win->didResize = MBDidResize; Win->close = MBClose; Win->update = MBUpdate; Win->position = PositionPrefsTitle; Win->click = MBClick; Win->bgClick = MBClick; Win->dontControl = True; Win->cursor = MBCursor; Win->activate = MBActivate; Win->help = MBHelp; Win->menu = MBMenu; Win->key = MBKey; Win->app1 = MBKey; Win->drag = MBDragHandler; Win->zoomSize = MBZoomSize; Win->grow = MBGrow; Win->find = MBFind; Win->idle = MBIdle; Win->showsSponsorAd = true; ShowMyWindow(WinWP); MyWindowDidResize(Win,&Win->contR); MB.inited = true; return; fail: if (WinWP) CloseMyWindow(WinWP); if (err) WarnUser(COULDNT_MBOX,err); return; } else WinWP = GetMyWindowWindowPtr (Win); UserSelectWindow(WinWP); } #pragma segment MboxWin /************************************************************************ * MBDidResize - resize the MB window ************************************************************************/ void MBDidResize(MyWindowPtr win, Rect *oldContR) { #define kListInset 10 #pragma unused(oldContR) Rect r; short htAdjustment; // buttons PositionBevelButtons(win,cntMBControls,MB.controls,kListInset,Win->contR.bottom - INSET - kHtCtl,kHtCtl,RectWi(win->contR)); // list SetRect(&r,kListInset,win->topMargin+kListInset,Win->contR.right-kListInset,Win->contR.bottom - 2*INSET - kHtCtl); if (win->sponsorAdExists && r.bottom >= win->sponsorAdRect.top - kSponsorBorderMargin) r.bottom = win->sponsorAdRect.top - kSponsorBorderMargin; LVSize(&MB.list,&r,&htAdjustment); /* * redraw */ MBSetControls(); InvalContent(win); } /************************************************************************ * MBOpenFolder - open and selected the folder indicated by the menu ID ************************************************************************/ void MBOpenFolder(Handle hStringList,Boolean IsIMAP) { StringPtr pString,sSelect; VLNodeID id; short selectID = 0; OpenMBWin(); // Make sure the MB window is open and in front // Expand each folder id = IsIMAP ? kIMAPFolder: kEudoraFolder; for(pString = LDRef(hStringList);*pString;pString += *pString+1) { LVExpand(&MB.list,id,pString,true); sSelect = pString; selectID = id; id = id==kEudoraFolder ? MAILBOX_MENU : MBGetFolderMenuID(id==kIMAPFolder?MAILBOX_MENU:id,pString); } if (selectID) LVSelect(&MB.list,selectID,sSelect,false); } /************************************************************************ * MBZoomSize - zoom to only the maximum size of list ************************************************************************/ void MBZoomSize(MyWindowPtr win,Rect *zoom) { short zoomHi = zoom->bottom-zoom->top; short zoomWi = zoom->right-zoom->left; short hi, wi; LVMaxSize(&MB.list, &wi, &hi); wi += 2*kListInset; hi += 2*kListInset + INSET + ControlHi(CtlNewMB); wi = MIN(wi,zoomWi); wi = MAX(wi,win->minSize.h); hi = MIN(hi,zoomHi); hi = MAX(hi,win->minSize.v); zoom->right = zoom->left+wi; zoom->bottom = zoom->top+hi; } /************************************************************************ * MBGrow - adjust grow size ************************************************************************/ static void MBGrow(MyWindowPtr win,Point *newSize) { WindowPtr winWP = GetMyWindowWindowPtr (win); Rect r,rWin; short htControl = ControlHi(CtlNewMB); short bottomMargin,sponsorMargin; // Get list position bottomMargin = INSET*2 + htControl; if (win->sponsorAdExists) { GetWindowPortBounds(winWP,&rWin); sponsorMargin = rWin.bottom - win->sponsorAdRect.top + kSponsorBorderMargin; if (sponsorMargin > bottomMargin) bottomMargin = sponsorMargin; } SetRect(&r,kListInset,win->topMargin+kListInset,newSize->h-kListInset,newSize->v - bottomMargin); LVCalcSize(&MB.list,&r); // Calculate new window height newSize->v = r.bottom + bottomMargin; } #pragma segment Main /************************************************************************ * MBClose - close the window ************************************************************************/ Boolean MBClose(MyWindowPtr win) { #pragma unused(win) if (MB.inited) { // Save expanded folder list SaveExpandedFolderList(&gExpandList); // Dispose of list LVDispose(&MB.list); MB.inited = false; } return(True); } #pragma segment MboxWin /************************************************************************ * MBUpdate - draw the window ************************************************************************/ void MBUpdate(MyWindowPtr win) { CGrafPtr winPort = GetMyWindowCGrafPtr (win); Rect r; r = MB.list.bounds; DrawThemeListBoxFrame(&r,kThemeStateActive); LVDraw(&MB.list, MyGetPortVisibleRegion(winPort), true, false); } /************************************************************************ * MBActivate - activate the window ************************************************************************/ void MBActivate(MyWindowPtr win) { #pragma unused(win) LVActivate(&MB.list, Win->isActive); } /********************************************************************** * MBFind - find in the Mailboxes window **********************************************************************/ static Boolean MBFind(MyWindowPtr win,PStr what) { return FindListView(win,&MB.list,what); } /************************************************************************ * MBRefill - refill current menus ************************************************************************/ short MBRefill(UPtr andSelect) { short err; /* If andSelect specified, find and select it. Else If there was a selection before, select it */ err = MBFillSide(GetMHandle(MAILBOX_MENU)); return(err); } /************************************************************************ * MBFill - fill the windows ************************************************************************/ short MBFill(void) { short err; MenuHandle mh=GetMHandle(MAILBOX_MENU); if (err=MBFillSide(mh)) return(err); return(noErr); } /************************************************************************ * MBFillSide - fill the list from a menu ************************************************************************/ short MBFillSide(MenuHandle mh) { InvalidListView(&MB.list); return noErr; } /************************************************************************ * MBKey - key stroke ************************************************************************/ static Boolean MBKey(MyWindowPtr win, EventRecord *event) { #pragma unused(win) short key = (event->message & 0xff); if (LVKey(&MB.list,event)) { MBSetControls(); return true; } return false; } /************************************************************************ * MBClick - click in mailboxes window ************************************************************************/ void MBClick(MyWindowPtr win,EventRecord *event) { //#pragma unused(win) WindowPtr winWP = GetMyWindowWindowPtr (win); Rect r; short i; Point pt; SetPort(GetMyWindowCGrafPtr(win)); if (!LVClick(&MB.list,event)) { pt = event->where; GlobalToLocal(&pt); if (!win->isActive) { SelectWindow_(winWP); UpdateMyWindow(winWP); // Have to update manually since no events are processed } for (i=0;imodifiers&optionKey)!=0)); if (!ControlIsGrey(Controls[i]) && TrackControl(Controls[i],pt,(void *)(-1))) MBHit(i,(event->modifiers&optionKey) != 0); AuditHit((event->modifiers&shiftKey)!=0, (event->modifiers&controlKey)!=0, (event->modifiers&optionKey)!=0, (event->modifiers&cmdKey)!=0, false, GetWindowKind(winWP), AUDITCONTROLID(GetWindowKind(winWP),i), event->what); break; } } } MBSetControls(); } /************************************************************************ * MBCursor - set the cursor properly for the mailboxes window ************************************************************************/ void MBCursor(Point mouse) { if (!PeteCursorList(Win->pteList,mouse)) SetMyCursor(arrowCursor); } /************************************************************************ * MBSetControls - enable or disable the controls, based on current situation ************************************************************************/ void MBSetControls(void) { Boolean fSelect,fRemove; Boolean fAdd = true; Boolean isIMAPBox; short i; VLNodeInfo data; fSelect = LVCountSelection(&MB.list)!=0; Win->hasSelection = fSelect; // Determine if "Remove" button needs to be greyed if (fRemove = fSelect) { // Make sure that selection doesn't include any non-removeable items short i; VLNodeInfo data; for (i=1;i<=LVCountSelection(&MB.list);i++) { MBGetData(&data,i); if ((IsSpecialBox(&MB.list,&data)) || data.nodeID == kIMAPFolder // Can't delete IMAP personalities || (IsIMAPBox(&data) && !CanModifyMailboxTrees())) // Can't delete IMAP boxes while the tree is in use { // Here's an item that can't be removed fRemove = false; break; } } } SetGreyControl(CtlRemove,!fRemove); // Determine if the "Add" buttons need to be greyed. // see what's selected #ifdef SHOW_RESYNC_BUTTON MB.imapMailboxSelected = false; #endif for (i=1;i<=LVCountSelection(&MB.list);i++) { MBGetData(&data,i); isIMAPBox = IsIMAPBox(&data); if (isIMAPBox) { // Can't add mailboxes when the tree is in use if (!CanModifyMailboxTrees()) { // Here's an item that can't be removed fAdd = false; break; } #ifdef SHOW_RESYNC_BUTTON // but we can enable the resync control MB.imapMailboxSelected = true; #endif } } // properly activate Refresh and Resync buttons EnableIMAPButtons(Win); SetGreyControl(CtlNewMB,!fAdd); SetGreyControl(CtlNewFolder,!fAdd); } /************************************************************************ * MBHit - a control item was hit ************************************************************************/ void MBHit(short which, Boolean optionPressed) { Boolean resyncPlease = false; SInt16 value=0; MenuHandle menu = nil; short markChar=0; switch (which) { case RemoveIndex: MBRemove(); break; case NewMBIndex: DoNewMailbox(false); break; case NewFolderIndex: DoNewMailbox(true); break; case OptionsMenuIndex: if (!GetControlData(CtlOptionsMenu,1,kControlBevelButtonMenuValueTag,sizeof(SInt16),(Ptr)&value,nil) && !GetControlData(CtlOptionsMenu,1,kControlBevelButtonMenuHandleTag,sizeof(menu),(Ptr)&menu,nil) && menu) { switch(value) { case OPTIONS_RESYNCSUB_ITEM: optionPressed = true; case OPTIONS_RESYNC_ITEM: resyncPlease = true; break; case OPTIONS_AUTOSYNC_ITEM: GetItemMark(menu,value,&markChar); MBMarkIMAPMailboxes((markChar==0), kNeedsPoll); break; case OPTIONS_REFRESH_ITEM: MBRefreshIMAPMailboxes(optionPressed); break; case OPTIONS_SHOWDELETED_ITEM: GetItemMark(menu,value,&markChar); MBMarkIMAPMailboxes((markChar==0), kShowDeleted); break; case OPTIONS_EXPUNGE_ITEM: MBResyncOrExpungeIMAPMailboxes(false, IMAPMultExpungeTask); break; case OPTIONS_COMPACT_ITEM: MBCompactMailboxes(); break; default: break; } } // fall through if resync was requested through the menu if (!resyncPlease) break; #ifdef SHOW_RESYNC_BUTTON case ResyncIndex: #endif MBResyncOrExpungeIMAPMailboxes(optionPressed, IMAPMultResyncTask); break; } } /************************************************************************ * GetSelectedMailboxSpecs - build a list of selected mailboxes to process. ************************************************************************/ OSErr GetSelectedMailboxSpecs(Handle *mailboxes, Boolean optionPressed) { OSErr err = noErr; Accumulator boxes; short count, i; VLNodeInfo data; FSSpec spec; MailboxNodeHandle mbox; PersHandle selectedPers = nil, pers; *mailboxes = NULL; if (count = LVCountSelection(&MB.list)) { err = AccuInit(&boxes); if (err == noErr) { // collect all selected mailboxes for (i=1;i<=count;i++) { // get mailbox spec MBGetData(&data,i); MakeMBSpec(&data,&spec); // option-refresh on an IMAP account refreshes all mailboxes. if (optionPressed && (data.nodeID == kIMAPFolder)) { selectedPers = FindPersByName(spec.name); GetSelectedChildMailboxSpecs(&boxes, (*selectedPers)->mailboxTree, true); } // is this an IMAP mailbox? if (IsIMAPBox(&data)) { spec.parID = SpecDirId(&spec); LocateNodeBySpecInAllPersTrees(&spec, &mbox, &pers); // skip this mailbox if t belongs to the last IMAP account we added to the list. if (pers != selectedPers) { if (mbox) { GetSelectedChildMailboxSpecs(&boxes, mbox, optionPressed); } } } } AccuTrim(&boxes); *mailboxes = boxes.data; } } // else // nothing was selected. Return nothing. return (err); } /************************************************************************ * GetSelectedChildMailboxSpecs - add the mailbox and the children, if * appropriate, to the list. ************************************************************************/ OSErr GetSelectedChildMailboxSpecs(Accumulator *b, MailboxNodeHandle toAdd, Boolean childrenToo) { OSErr err = noErr; FSSpec spec; MailboxNodeHandle scan; // check parameters if (toAdd && b) { // add this mailbox if it's really a mailbox if ((*toAdd)->mailboxName) { spec = ((*toAdd)->mailboxSpec); err = AccuAddPtr(b, &spec, sizeof(FSSpec)); // add the children if we ought if ((err == noErr) && childrenToo) { if (scan = (*toAdd)->childList) { while (scan && (err == noErr)) { err = GetSelectedChildMailboxSpecs(b, scan, true); scan = (*scan)->next; } } } } else if (childrenToo) { // This is an IMAP account. Add all of the mailboxes. scan = (*toAdd)->next; while (scan && (err == noErr)) { err = GetSelectedChildMailboxSpecs(b, scan, true); scan = (*scan)->next; } } } return (err); } /************************************************************************ * MBListOpen - open some stuff from a list handle ************************************************************************/ void MBListOpen(ViewListPtr pView) { short i; VLNodeInfo data; FSSpec spec; for (i=1;i<=LVCountSelection(pView);i++) { LVGetItem(pView,i,&data,true); if (!data.isParent || data.iconID == kIMAPMailboxIcon || IsIMAPTrashBox(&data)) { MakeMBSpec(&data,&spec); (void) GetMailbox(&spec,True); } } } /************************************************************************ * MBFixUnread - Update and "unread" status of a mailbox ************************************************************************/ void MBFixUnread(MenuHandle mh,short item,Boolean unread) { if (MB.inited) MBFixUnreadLo(&MB.list,mh,item,unread,GetWindowKind(GetMyWindowWindowPtr(MB.win)) == MB_WIN); SearchFixUnread(mh,item,unread); MBDrawerFixUnread(mh,item,unread); } /************************************************************************ * MBFixUnread - Update and "unread" status of a mailbox ************************************************************************/ void MBFixUnreadLo(ViewListPtr pView,MenuHandle mh,short item,Boolean unread,Boolean draw) { Str63 name; Style theStyle = unread ? UnreadStyle : 0; short menuID; Boolean mailboxMenu = GetMenuID(mh) == MAILBOX_MENU; if (mailboxMenu && !item) { // Update "Eudora Folder" GetMBDirName(Root.vRef,Root.dirId,name); // Name of Mail Folder LVUpdateStyle(pView,kEudoraFolder,name,theStyle,draw); } else { MyGetItem(mh,item,name); if (mailboxMenu && IMAPExists() && (menuID = SubmenuId(mh,item))) { short vRef; long dirID; MBMenuToFile(menuID,&vRef,&dirID); if (IsIMAPVD(vRef,dirID)) { LVUpdateStyle(pView,kIMAPFolder,name,theStyle,draw); return; } } LVUpdateStyle(pView,GetMenuID(mh),name,theStyle,draw); } } /************************************************************************ * DoNewMailbox - make a new mailbox or mailbox folder ************************************************************************/ static void DoNewMailbox(Boolean folder) { FSSpec spec; short vRefNum; long dirID; VLNodeInfo data; short menuID; Boolean imapSuccess = false; MenuHandle mh; short level,subMenuID; Str63 name; // Determine which folder to put it in vRefNum = MailRoot.vRef; // Assume main mailboxes folder dirID = MailRoot.dirId; menuID = MAILBOX_MENU; if (MBGetData(&data,1)) { // if the currently select node is an IMAP personality, make sure we have a mailbox tree if (data.nodeID == kIMAPFolder) { PersHandle oldPers = CurPers; // the personality is collapsed. Do nothing. if (data.isCollapsed) return; if (CurPers = FindPersByName(data.name)) { if (PrefIsSet(PREF_IS_IMAP)) if (!MailboxTreeGood(CurPers)) if (CreateLocalCache() != noErr) { CurPers = oldPers; return; } } CurPers = oldPers; } // Put it in parent of first selected item or self if a folder MBMenuToFile(data.nodeID, &vRefNum, &dirID); menuID = data.nodeID; if ((!data.isParent || !data.isCollapsed) && IsIMAPBox(&data)) { PersHandle pers = nil; MailboxNodeHandle node = nil; FSMakeFSSpec(vRefNum,dirID,data.name,&spec); spec.parID = SpecDirId(&spec); LocateNodeBySpecInAllPersTrees(&spec, &node, &pers); if (node && pers && CanHaveChildren(node)) { dirID = spec.parID; menuID = MBGetFolderMenuID(data.nodeID,data.name); } } else { if (data.isParent && !data.isCollapsed) { FSMakeFSSpec(vRefNum,dirID,data.name,&spec); dirID = SpecDirId(&spec); if (!dirID) dirID = spec.parID; menuID = MBGetFolderMenuID(data.nodeID,data.name); } } } // Warn user if we will be more than 5 levels deep subMenuID = menuID; mh=GetMenuHandle(subMenuID); for(level=0;mh;level++) mh=ParentMailboxMenu(mh,&subMenuID); if (level>5 && !PrefIsSet(PREF_NO_MAILBOX_LEVEL_WARNING) && !Mom(CREATE,0,PREF_NO_MAILBOX_LEVEL_WARNING,R_FMT,MAILBOX_LEVEL_WARNING)) return; // too deep // Make a unique "untitled" name MakeUniqueUntitledSpec (vRefNum, dirID, folder ? UNTITLED_FOLDER : UNTITLED_MAILBOX, &spec); // try to add this mailbox as an IMAP mailbox first. if (IMAPAddMailbox(&spec, folder, &imapSuccess, false)) { if (imapSuccess) { MBTickle(nil,nil); menuID = MBGetFolderMenuID(data.nodeID,data.name); LVRename(&MB.list,menuID,spec.name,false,false); // Allow user to rename the untitled mailbox/folder } } else { PSCopy(name,spec.name); // BadMailboxName steps on the name if it's a folder if (!BadMailboxName(&spec,folder)) // Creates the mailbox/folder { if (!folder) { AddBoxHigh(&spec); } else { BuildBoxMenus(); MBTickle(nil,nil); } LVRename(&MB.list,menuID,name,false,false); // Allow user to rename the untitled mailbox/folder } } } /************************************************************************ * MBRemove - delete selected mailbox(es) ************************************************************************/ static void MBRemove(void) { short i; Boolean needRefill=false; Boolean andShutUp=false; Boolean fFolder=false; short n; for (i=1;i<=(n=LVCountSelection(&MB.list));i++) { VLNodeInfo data; MBGetData(&data,i); if ((data.nodeID != kIMAPFolder) && (!IsSpecialBox(&MB.list,&data))) { if (!DoRemoveBox(GetMHandle(data.nodeID),data.name,i31-*GetRString(suffix,TOC_SUFFIX)) { TooLong(newName); goto Done; } if (MBGetData(&data,1) && *newName && !EqualString(newName,data.name,true,true)) { MBMenuToFile(data.nodeID, &vRef, &dirID); FSMakeFSSpec(vRef,dirID,data.name,&spec); SimpleMakeFSSpec(vRef,dirID,newName,&newSpec); IsAlias(&spec,&origSpec); isFolder=FSpIsItAFolder(&origSpec); if (!isFolder && (!(tocH=TOCBySpec(&spec)) || (*tocH)->which)) { err = WarnUser(MAYNT_RENAME_BOX,0); goto Done; } // complain if the mailbox name has some inappropriate characters if (BadMailboxNameChars(&newSpec)) goto Done; // rename the IMAP mailbox if (IsIMAPCacheFolder(&spec)) { MBTellOthersRename(&spec,&newSpec,True,True,false); IMAPRenameMailbox(&spec, newSpec.name); MBTellOthersRename(&spec,&newSpec,True,False,false); goto Done; } // if (!StringSame(spec.name,newName)) // HDelete(spec.vRefNum,spec.parID,newName); if (isFolder) { MBTellOthersRename(&spec,&newSpec,True,True,false); if (err = HRename(spec.vRefNum,spec.parID,spec.name,newName)) { (FileSystemError(RENAMING_BOX,spec.name,err)); goto Done; } BuildBoxMenus(); MBTickle(nil,nil); MBTellOthersRename(&spec,&newSpec,True,False,false); } else { MBTellOthersRename(&spec,&newSpec,False,True,false); if (err = RenameMailbox(&spec,newName,isFolder)) goto Done; if (tocH) { TOCSetDirty(tocH,true); PCopy((*tocH)->mailbox.spec.name,newName); SetWTitle_(GetMyWindowWindowPtr((*tocH)->win),newName); } DontTickle = True; RemoveBoxHigh(&spec); DontTickle = False; AddBoxHigh(&newSpec); MBTellOthersRename(&spec,&newSpec,False,False,false); TellSearchMBRename(&spec,&newSpec); } } Done: if (err) InvalContent(Win); // Clean up window } /************************************************************************ * ParentMailboxMenu - find the menu enclosing this one ************************************************************************/ MenuHandle ParentMailboxMenu(MenuHandle mh,short *itemPtr) { short myID, curID; curID = myID = GetMenuID(mh); while (--curID && (mh = GetMHandle(curID))) { if (*itemPtr = FindItemBySubmenu(mh,myID)) return(mh); } mh = GetMHandle(MAILBOX_MENU); if (*itemPtr = FindItemBySubmenu(mh,myID)) return(mh); return(nil); } /************************************************************************ * FindItemBySubmenu - find a menu item with a given submenu ************************************************************************/ short FindItemBySubmenu(MenuHandle mh, short subID) { short item, newID; for (item=CountMenuItems(mh);item;item--) { if (HasSubmenu(mh,item)) { newID = SubmenuId(mh,item); if (newID==subID) return(item); } } return(0); } /************************************************************************ * DoRemoveBox - interact with the user before removing a mailbox ************************************************************************/ Boolean DoRemoveBox(MenuHandle mh,UPtr name,Boolean multiple,Boolean *andShutUp,Boolean *needRefill) { Boolean isFolder=False; Boolean isEmpty; short clicked; FSSpec spec, newSpec; CInfoPBRec hfi; FInfo info; short res; Boolean trashChain=False; Boolean justTrash=False; Boolean isIMAPBox = false; GetTransferParams(GetMenuID(mh),FindItemByName(mh,name),&spec,nil); FSpGetFInfo(&spec,&info); isFolder = FSpIsItAFolder(&spec); isIMAPBox = IsIMAPMailboxFile(&spec); /* * is it an alias? */ if (!isFolder && info.fdFlags & kIsAlias) { if (!IsAlias(&spec,&newSpec)) { /* * ok, here we have an alias that points nowhere. just trash it. */ isEmpty = True; trashChain = False; justTrash = True; } else { res = AlertStr(ALIAS_OR_REAL_ALRT,Note,spec.name); if (res==aorCancel) return(False); trashChain = (res==aorBoth); if (trashChain) isFolder = info.fdType==kContainerFolderAliasType; else justTrash = isEmpty = True; } } if (!justTrash) { if (!isFolder) { /* * is the box empty? */ if (AFSpGetHFileInfo(&spec,&hfi)) isEmpty = True; else isEmpty = hfi.hFileInfo.ioFlLgLen==0; } else isEmpty = FolderFileCount(&spec)<=0; // if this is an IMAP mailbox, make sure this mailbox really is empty if (isEmpty && isIMAPBox) isEmpty = IsIMAPMailboxEmpty(&spec); } if (!justTrash && !*andShutUp) { clicked = AlertStr(isEmpty? (multiple ? DELETE_EMPTY_ALRT : ALRTStringsOnlyStrn+DELETE_EMPTY_SINGLE_ASTR) : (multiple ? (isIMAPBox?DELETE_NON_EMPTY_IMAP_ASTR+ALRTStringsOnlyStrn:DELETE_NON_EMPTY_ALRT) : ALRTStringsOnlyStrn+(isIMAPBox?DELETE_NON_EMPTY_SINGLE_IMAP_ASTR:DELETE_NON_EMPTY_SINGLE_ASTR)), Caution,name); if (clicked==DELETE_REMOVE_ALL) *andShutUp = True; else if (clicked!=DELETE_REMOVE_IT) return(False); } // if this is an IMAP folder, then do the IMAP thing if (isIMAPBox) { Boolean result = false; // close any child mailboxes ... IMAPCloseChildMailboxes(&spec); DontTickle = True; result = IMAPDeleteMailbox(&spec); DontTickle = False; *needRefill = True; return (result); } if (isFolder) { short item = FindItemByName(mh,name); if (DeleteMailFolder(mh,item)) return(False); *needRefill = True; } else { RemoveMailbox(&spec,trashChain); DontTickle = True; RemoveBoxHigh(&spec); DontTickle = False; *needRefill = True; } return(True); } /************************************************************************ * DeleteMailFolder - move a mail folder to the trash ************************************************************************/ static short DeleteMailFolder(MenuHandle mh,short item) { short err=0; FSSpec spec, realSpec; GetTransferParams(GetMenuID(mh),item,&spec,nil); if (IsAlias(&spec,&realSpec)) FSpTrash(&realSpec); // Was an alias, move real folder to trash, then alias err = FSpTrash(&spec); return(err); } /************************************************************************ * MBMove - move selected mailbox(es) to target ************************************************************************/ static void MBMove(VLNodeInfo *pToInfo) { short i,n; short toVRef; long toDirID; Boolean fChanged = false; Boolean fFolder = false; Boolean fMovedFolder; FSSpec spec, newSpec; OSErr moveErr=nil; short fromVRef,flushVRef=0; long fromDirID; VLNodeInfo data,folderData; Boolean ignoreFilters = false; Boolean dontWarn = false; if (pToInfo->nodeID==kEudoraFolder) { // Eudora Folder toVRef = MailRoot.vRef; toDirID = MailRoot.dirId; } else { MBMenuToFile(pToInfo->nodeID, &toVRef, &toDirID); FSMakeFSSpec(toVRef,toDirID,pToInfo->name,&spec); IsAlias(&spec,&spec); toDirID = SpecDirId(&spec); toVRef = spec.vRefNum; // In case it was alias to another volume } n = LVCountSelection(&MB.list); fMovedFolder = false; for (i=1;i<=n && !moveErr;i++) { MBGetData(&data,i); if (IsSpecialBox(&MB.list,&data)) // Can't move this one continue; MBMenuToFile(data.nodeID, &fromVRef, &fromDirID); // If the mailbox to be moved is an IMAP box, allow it to be moved on the same server only if (IsIMAPBox(&data)) { FSSpec fromSpec, toSpec; SimpleMakeFSSpec(fromVRef, fromDirID, data.name, &fromSpec); SimpleMakeFSSpec(toVRef, toDirID, pToInfo->name, &toSpec); MBTellOthersRename(&spec,&newSpec,true,true,false); if (!IMAPMoveMailbox(&fromSpec, &toSpec, i==n, i>n, &dontWarn)) return; MBTellOthersRename(&spec,&newSpec,true,false,dontWarn); continue; } // we don't allow moves of local mailboxes *to* an IMAP server if (pToInfo->nodeID == kIMAPFolder) { WarnUser(MOVE_MAILBOX,0); return; } // and we don't allow moves of local mailboxes to an IMAP folder if (IsIMAPBox(pToInfo)) { WarnUser(MOVE_MAILBOX,0); return; } if (toVRef != fromVRef) { WarnUser(CANT_VOL_MOVE,0); return; } if (fMovedFolder && LVDescendant(&MB.list,&data,&folderData)) // We have already moved an ancestor of this file. Don't // try to move it again continue; if (fMovedFolder = data.isParent) { // Moved a folder folderData = data; } if (LVDescendant(&MB.list,&data,pToInfo)==1) // Don't try to move a child into its parent--it's already there continue; // Move file or directory if (data.isParent) fFolder = true; MBTellOthersRename(&spec,&newSpec,fFolder,true,false); if (!fFolder) { DontTickle = true; FSMakeFSSpec(fromVRef,fromDirID,data.name,&spec); RemoveBoxHigh(&spec); DontTickle = false; } moveErr=HMove(fromVRef,fromDirID,data.name,toDirID,nil); flushVRef = fromVRef; if (!fFolder) { // If there was an error, we need to add the mailbox back in to its original location DontTickle = true; if (!moveErr) FSMakeFSSpec(fromVRef,toDirID,data.name,&spec); AddBoxHigh(&spec); DontTickle = false; } if (moveErr) { FileSystemError(MOVE_MAILBOX,data.name,moveErr); } else { // If file, move TOC also if (!data.isParent) { Str15 suffix; short err; TOCHandle tocH; FSSpec spec; PCat(data.name,GetRString(suffix,TOC_SUFFIX)); if ((err=HMove(fromVRef,fromDirID,data.name,toDirID,nil)) && err!=fnfErr && err!=paramErr && err!=bdNamErr) {FileSystemError(MOVE_MAILBOX,data.name,err); return;} *data.name -= *suffix; FSMakeFSSpec(fromVRef,fromDirID,data.name,&spec); if (tocH=FindTOC(&spec)) (*tocH)->mailbox.spec.parID = toDirID; } fChanged = true; SimpleMakeFSSpec(fromVRef,fromDirID,data.name,&spec); SimpleMakeFSSpec(toVRef,toDirID,data.name,&newSpec); if (!ignoreFilters) { if (MBTellOthersRename(&spec,&newSpec,fFolder,false,dontWarn)==userCanceledErr) ignoreFilters = true; dontWarn = true; // one warning is enough } TellSearchMBRename(&spec,&newSpec); } } if (flushVRef) // Flush the volume so the directory is correct for // rebuilding the mailbox tree FlushVol(nil,flushVRef); if (fFolder) BuildBoxMenus(); if (fChanged) MBTickle(nil,nil); } Boolean MBGetData(VLNodeInfo *data,short selectedItem) { short junk=64; return LVGetItem(&MB.list,selectedItem,data,true); } void MBTickle(UPtr fromSelect,UPtr select) { if (MB.inited && !DontTickle) { // MBFixIndices(); (void) MBRefill(select); } SearchMBUpdate(); MBDrawerReload(); } /************************************************************************ * MBTellOthersRename - let the rest of the app know what we're up to ************************************************************************/ OSErr MBTellOthersRename(FSSpecPtr spec,FSSpecPtr newSpec,Boolean folder,Boolean will,Boolean dontWarn) { if (MBRenameStack) { short n = (*MBRenameStack)->elCount; while (n--) { MBRenameProcPtr proc; OSErr err; StackItem(&proc,n,MBRenameStack); err = (*proc)(spec,newSpec,folder,will,dontWarn); if (err) return err; } } return noErr; } #pragma segment Balloon /************************************************************************ * MBHelp - provide help for the mailbox window ************************************************************************/ void MBHelp(MyWindowPtr win,Point mouse) { #pragma unused(win) if (PtInRect(mouse,&MB.list.bounds)) MyBalloon(&MB.list.bounds,100,0,MBWIN_HELP_STRN+1,0,nil); else ShowControlHelp(mouse,MBWIN_HELP_STRN+2,CtlNewMB,CtlNewFolder,CtlRemove,CtlOptionsMenu, #ifdef SHOW_RESYNC_BUTTON CtlResync, #endif nil); } #pragma segment MboxWin /************************************************************************ * MBMenu - menu choice in the mailbox window ************************************************************************/ Boolean MBMenu(MyWindowPtr win, int menu, int item, short modifiers) { #pragma unused(win,modifiers) switch (menu) { case FILE_MENU: switch(item) { case FILE_OPENSEL_ITEM: MBListOpen(&MB.list); return(True); break; } break; case EDIT_MENU: switch(item) { case EDIT_SELECT_ITEM: if (LVSelectAll(&MB.list)) { MBSetControls(); return(true); } break; case EDIT_COPY_ITEM: LVCopy(&MB.list); return true; } break; } return(False); } /************************************************************************ * MBMenuToFile - convert a menu id into a file reference ************************************************************************/ void MBMenuToFile(short menuID, short *pVRef, long *pDirID) { if (menuID==kIMAPFolder) { *pVRef = IMAPMailRoot.vRef; *pDirID = IMAPMailRoot.dirId; } else MenuID2VD(menuID==kEudoraFolder?MAILBOX_MENU:menuID,pVRef,pDirID); } /********************************************************************** * MBDragHandler - handle drags * * Can do internal dragging of mailboxes or external dragging and dropping * of messages **********************************************************************/ static OSErr MBDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag) { #pragma unused(win) OSErr err = noErr; gfMessageDrag = DragIsInteresting(drag,MESS_FLAVOR,TOC_FLAVOR,nil); // Dragging messages gfBoxDrag = LVDragFlavor() == kMBDragType; // Dragging mailboxes if (!gfMessageDrag && !gfBoxDrag) return(dragNotAcceptedErr); // Nothing here we want switch (which) { case kDragTrackingEnterWindow: case kDragTrackingLeaveWindow: case kDragTrackingInWindow: err = LVDrag(&MB.list,which,drag); break; case 0xfff: // Drop if (gfBoxDrag) // Mailbox drag err = LVDrag(&MB.list,which,drag); else { // Message drag VLNodeInfo targetInfo; if (LVDrop(&MB.list,&targetInfo)) err = MBDragMessages(drag, &targetInfo); else return dragNotAcceptedErr; } break; } return err; } /********************************************************************** * MBDragMessages - handle message drags **********************************************************************/ OSErr MBDragMessages(DragReference drag, VLNodeInfo *targetInfo) { FSSpec spec,toSpec; TOCHandle tocH; short sumNum; OSErr err; UHandle data=nil; // Get spec of mailbox dropped on MakeMBSpec(targetInfo,&spec); if (!(err=MyGetDragItemData(drag,1,MESS_FLAVOR,(void*)&data))) { tocH = (***(MessHandle**)data)->tocH; sumNum = (***(MessHandle**)data)->sumNum; } else if (!(err=MyGetDragItemData(drag,1,TOC_FLAVOR,(void*)&data))) { tocH = **(TOCHandle**)data; sumNum = -1; } ZapHandle(data); // Can't move to own mailbox toSpec = GetMailboxSpec(tocH,-1); if (toSpec.vRefNum==spec.vRefNum && toSpec.parID==spec.parID && StringSame(toSpec.name,spec.name)) // Dragging to own mailbox return dragNotAcceptedErr; if (!err) { if (sumNum==-1) MoveSelectedMessages(tocH,&spec,(DragOrMods(drag)&optionKey)!=0); else { // if (!(DragOrMods(drag)&optionKey)) EzOpen(tocH,sumNum,0,DragOrMods(drag),True,True); if (!(DragOrMods(drag)&optionKey)) { #ifndef clarenceBug821 #ifdef TWO AddXfUndo(tocH,TOCBySpec(&spec),sumNum); #endif #endif EzOpen(tocH,sumNum,0,DragOrMods(drag),True,True); } MoveMessage(tocH,sumNum,&spec,(DragOrMods(drag)&optionKey)!=0); } } return(err); } /************************************************************************ * MBGetExpList - return pointer to folder expansion list data ************************************************************************/ ExpandInfoPtr MBGetExpList(void) { return &gExpandList; } /************************************************************************ * MBGetList - return pointer to mailbox list ************************************************************************/ ViewListPtr MBGetList(void) { return &MB.list; } /************************************************************************ * FindExpandDirID - Find the indicated dirID in the expanded folder list ************************************************************************/ long *FindExpandDirID(long dirID, ExpandInfoPtr pExpList) { short i; long *pDirID; if (!pExpList->hExpandList) { // Get expanded folder list if (pExpList->resID && (pExpList->hExpandList = Get1Resource(kExpandListType,pExpList->resID))) DetachResource(pExpList->hExpandList); else // Need to create a new one pExpList->hExpandList = NewHandleClear(2); pExpList->fExpandListChanged = false; } // Find the dirID in the list for (i=0,pDirID=(*pExpList->hExpandList)->dirID;i<(*pExpList->hExpandList)->count;i++,pDirID++) { if (*pDirID == dirID) return pDirID; } return nil; } /************************************************************************ * SaveExpandStatus - save status of expanded folders ************************************************************************/ void SaveExpandStatus(VLNodeInfo *data,ExpandInfoPtr pExpList) { long *pDirID; pDirID = FindExpandDirID(data->refCon,pExpList); if (data->isCollapsed) { // Remove from list if (pDirID) { (*pExpList->hExpandList)->count--; BMD(pDirID+1,pDirID,sizeof(long)*((*pExpList->hExpandList)->count-(pDirID-(*pExpList->hExpandList)->dirID))); SetExpandListSize(pExpList); pExpList->fExpandListChanged = true; } } else { // Add to list if (!pDirID) { if (!PtrPlusHand_(&data->refCon,(Handle)pExpList->hExpandList,sizeof(long))) { (*pExpList->hExpandList)->count++; pExpList->fExpandListChanged = true; } } } } /************************************************************************ * SetExpandListSize - set size based on count ************************************************************************/ static void SetExpandListSize(ExpandInfoPtr pExpList) { SetHandleSize((Handle)pExpList->hExpandList,sizeof(short)+sizeof(long)*(*pExpList->hExpandList)->count); } /************************************************************************ * MBGetFolderMenuID - get the menu ID for this folder ************************************************************************/ short MBGetFolderMenuID(short theID,StringPtr name) { short item,id; MenuHandle mh; id = 0; if (theID==kEudoraFolder || theID==kIMAPFolder) theID = MAILBOX_MENU; if (mh = GetMenuHandle(theID)) { item = FindItemByName(mh,name); id = SubmenuId(mh,item); } return(id); } /************************************************************************ * SaveExpandedFolderList - save expanded folder list. Remove any folders * that no longer exist ************************************************************************/ void SaveExpandedFolderList(ExpandInfoPtr pExpList) { if (pExpList->fExpandListChanged && pExpList->resID) { short from,to; OSErr err; // Remove any directories from the list that no longer exist for(from=0,to=0;from<(*pExpList->hExpandList)->count;from++) { CInfoPBRec hfi; long dirID; hfi.dirInfo.ioCompletion = nil; hfi.dirInfo.ioNamePtr = nil; hfi.dirInfo.ioVRefNum=MailRoot.vRef; hfi.dirInfo.ioDrDirID = dirID = (*pExpList->hExpandList)->dirID[from]; hfi.dirInfo.ioFDirIndex=-1; if (!PBGetCatInfo((CInfoPBPtr)&hfi,false)) { // This one still exists. Use it (*pExpList->hExpandList)->dirID[to++] = dirID; } } if (from != to) { // Looks like we deleted one or more directory ID's (*pExpList->hExpandList)->count = to; SetExpandListSize(pExpList); } ZapSettingsResource(kExpandListType,pExpList->resID); AddMyResource_((Handle)pExpList->hExpandList,kExpandListType,pExpList->resID,""); if (!(err=ResError())) MyUpdateResFile(SettingsRefN); DetachResource((Handle)pExpList->hExpandList); } ZapHandle((Handle)pExpList->hExpandList); } /************************************************************************ * AddMailboxListItems - add mailboxes to list ************************************************************************/ void AddMailboxListItems(ViewListPtr pView, short nodeId,ExpandInfoPtr pExpList) { short i,n; Point c; short menuID; VLNodeInfo info; Str255 sTemp; MenuHandle mh; Boolean haveIMAP; Boolean doingTopLvlIMAP=false; haveIMAP = IMAPExists(); mh = GetMenuHandle(nodeId==0 || nodeId == kEudoraFolder ? MAILBOX_MENU : nodeId); n = CountMenuItems(mh); c.h = 0; SetPort_(GetMyWindowCGrafPtr(pView->wPtr)); menuID = GetMenuID(mh); i = (menuID==MAILBOX_MENU) ? 1: MAILBOX_FIRST_USER_ITEM-MAILBOX_BAR1_ITEM; if (nodeId==0) { // First level. Add Eudora folder if (haveIMAP) { info.useLevelZero = false; info.isParent = true; } else { info.useLevelZero = true; info.isParent = false; } info.iconID = APP_ICON; info.nodeID = kEudoraFolder; info.isCollapsed = !FindExpandDirID(MailRoot.dirId,pExpList); info.style = 0; if (haveIMAP) { // Any unread in the local mailboxes? short count = CountMenuItems(mh); short item; for(item=1;item<=count;item++) { Style style; GetMenuItemText(mh, item, sTemp); if (!IsIMAPCacheName(sTemp)) { GetItemStyle(mh,item,&style); if (style & UnreadStyle) { info.style = UnreadStyle; break; } } } } info.refCon = MailRoot.dirId; GetMBDirName(Root.vRef,Root.dirId,info.name); // Name of Mail Folder LVAdd(pView, &info); // If we have IMAP folders, add them at this level. // Otherwise add all the POP stuff at this level if (haveIMAP) { for (i=MAILBOX_FIRST_USER_ITEM;i<=n;i++) { MyGetItem(mh,i,info.name); if (StringSame(info.name,"\p-")) break; // stop at menu divider } i++; doingTopLvlIMAP = true; } } info.useLevelZero = false; for (;i<=n;i++) { short mailBoxIcon; Style style; mailBoxIcon = kMailBoxIcon; if (menuID==MAILBOX_MENU) { switch (i) { case MAILBOX_IN_ITEM: // In box mailBoxIcon = IN_MB_ICON; break; case MAILBOX_OUT_ITEM: // Out box mailBoxIcon = OUT_MB_ICON; break; case MAILBOX_JUNK_ITEM: // Junk box //mailBoxIcon = JUNK_MB_ICON; break; case MAILBOX_TRASH_ITEM: // Trash mailBoxIcon = kTrashBoxIcon; break; case MAILBOX_BAR1_ITEM: i = MAILBOX_FIRST_USER_ITEM; break; } } if (i <= n) { short vRef; long dirID; MyGetItem(mh,i,info.name); if (menuID==MAILBOX_MENU && haveIMAP && StringSame(info.name,"\p-")) break; // stop at menu divider info.nodeID = menuID; if (info.isParent = HasSubmenu(mh,i)) { short thisMenuID; thisMenuID = SubmenuId(mh,i); MBMenuToFile(thisMenuID, &vRef, &dirID); info.iconID = kFolderIcon; info.isCollapsed = info.isParent && !FindExpandDirID(dirID,pExpList); info.refCon = dirID; } else { info.iconID = mailBoxIcon; info.isCollapsed = false; } if (doingTopLvlIMAP) { info.nodeID = kIMAPFolder; info.iconID = APP_ICON; } else if (IsIMAPBox(&info)) info.iconID = IMAPMailboxIcon(&info, menuID); if (info.iconID == kMailBoxIcon && !PrefIsSet(PREF_NO_CUSTOM_MB_ICONS)) { Handle resH; OSType junk; // See if we have a custom icon SetResLoad(false); resH = GetNamedResource('ICN#',info.name); SetResLoad(true); if (resH) { short resFile = HomeResFile(resH); // Don't use icons from Eudora application or help file if (resFile != AppResFile && resFile != HelpResFile) GetResInfo(resH,&info.iconID,&junk,sTemp); } } GetItemStyle(mh,i,&style); info.style = style&UnreadStyle ? UnreadStyle : 0; LVAdd(pView, &info); } } } /************************************************************************ * IMAPMailboxIcon - which icon should be used for this IMAP mailbox? ************************************************************************/ long IMAPMailboxIcon(VLNodeInfo *info, short menuID) { long id = kMailBoxIcon; short vRef; long dirID; FSSpec mailboxSpec; MailboxNodeHandle node = nil; PersHandle pers = nil; Str255 sTemp; MBMenuToFile(menuID,&vRef,&dirID); if (IsIMAPVD(vRef,dirID)) { SimpleMakeFSSpec(vRef, dirID, info->name, &mailboxSpec); mailboxSpec.parID = SpecDirId(&mailboxSpec); LocateNodeBySpecInAllPersTrees(&mailboxSpec, &node, &pers); if (node) { if (StringSame(info->name,GetRString(sTemp,IMAP_INBOX_NAME)) && !((*node)->childList)) // if this is the IMAP Inbox, and it can't have any children, use "In" icon id = IN_MB_ICON; else if (((*node)->attributes&LATT_TRASH) != 0) // if this is the trash mailbox, use the trash icon id = kTrashBoxIcon; else if (DoesIMAPMailboxNeed(node,kNeedsPoll)) // a polled mailbox { if ((*node)->childList) id = kIMAPPolledFolderIcon; // with children else id = kIMAPPolledMailboxIcon; // without } else if (((*node)->attributes & LATT_NOSELECT)) // a strict folder id = kFolderIcon; else if ((*node)->childList) // a regular IMAP mailbox folder id = kIMAPMailboxIcon; } } return (id); } /************************************************************************ * WhichTree - which tree (POP, IMAP account) does this file/folder belong to ************************************************************************/ static short WhichTree(VLNodeInfo *pInfo) { MailboxNodeHandle node; PersHandle pers; FSSpec spec; if (IMAPExists()) { MakeMBSpec(pInfo,&spec); if (IsIMAPBox(pInfo)) { spec.parID = SpecDirId(&spec); LocateNodeBySpecInAllPersTrees(&spec, &node, &pers); return (long)pers; } } // POP mailbox return -1; } /************************************************************************ * MailboxesLVCallBack - callback function for List View ************************************************************************/ long MailboxesLVCallBack(ViewListPtr pView, VLCallbackMessage message, long data) { OSErr err = noErr; SendDragDataInfo *pSendData; MBDragData dragData; VLNodeInfo *pInfo; MenuHandle mh; short nodeID; switch (message) { case kLVAddNodeItems: AddMailboxListItems(pView,data,&gExpandList); break; case kLVGetParentID: if (((VLNodeInfo *)data)->rowNum==1) return kEudoraFolder; nodeID = ((VLNodeInfo *)data)->nodeID; if (nodeID == kEudoraFolder || nodeID == kIMAPFolder) nodeID = MAILBOX_MENU; return MBGetFolderMenuID(nodeID,((VLNodeInfo *)data)->name); //break; case kLVOpenItem: MBListOpen(&MB.list); break; case kLVMoveItem: MBMove((VLNodeInfo*)data); break; case kLVDeleteItem: MBRemove(); break; case kLVRenameItem: MBRename((StringPtr)data); break; case kLVQueryItem: pInfo = ( VLNodeInfo *)data; switch (pInfo->query) { case kQuerySelect: return true;; case kQueryDrag: { // make sure we're not dragging items from different trees // must be all POP or same IMAP tree long thisTree = WhichTree(pInfo); if (!pView->dragGroup) pView->dragGroup = thisTree; // this is the first item we've seen else if (pView->dragGroup != thisTree) return false; // drag to different tree } // fall through to common rename code case kQueryRename: if ((((VLNodeInfo*)data)->nodeID == kIMAPFolder) || (IsIMAPBox(pInfo) && !CanModifyMailboxTrees())) return (false); return (!IsSpecialBox(pView,pInfo)); case kQueryDrop: case kQueryDropParent: // make sure we're not dragging items from different trees // must be all POP or same IMAP tree if (gfBoxDrag && pView->dragGroup != WhichTree(pInfo)) return false; // drag to different tree // IMAP drop cases if (IsIMAPBox(pInfo)) { IMAPMailboxAttributes att; FSSpec toSpec; // can't drop if we can't currently modify mailbox trees if (gfBoxDrag && !CanModifyMailboxTrees()) return (false); MBMenuToFile(pInfo->nodeID, &(toSpec.vRefNum), &(toSpec.parID)); PCopy(toSpec.name, pInfo->name); toSpec.parID = SpecDirId(&toSpec); // can't drop if the destination can't have messages if (MailboxAttributes(&toSpec, &att)) { if ((gfMessageDrag && att.noSelect) || // Will MB accept messages? gfBoxDrag && att.noInferiors) // Will MB accept children? return false; else return true; } } if (pInfo->query==kQueryDrop) { if (pInfo->isParent || pInfo->rowNum == kItemEudoraFolder) // Can accept mail box drags return gfBoxDrag; else // Can accept message drags return gfMessageDrag; } else // message==kQueryDropParent return gfBoxDrag; break; case kQueryDragExpand: // if we're dragging to a different tree, don't auto-expand // folders if (gfBoxDrag && pView->dragGroup != WhichTree(pInfo)) return false; // drag to different tree return true; case kQueryDCOpens: // If IMAP mailbox hybrid, double-click opens the mailbox // instead of expanding/collapsing the folder return pInfo->iconID == kIMAPMailboxIcon || IsIMAPTrashBox(pInfo) || (!pInfo->isParent && pInfo->nodeID != kEudoraFolder); } break; case kLVCopyItem: break; case kLVExpandCollapseItem: if (((VLNodeInfo *)data)->rowNum==1 && (MainEvent.modifiers & optionKey)) { FinderOpen(&SettingsSpec,true,false); } else SaveExpandStatus((VLNodeInfo *)data,&gExpandList); break; case kLVSendDragData: pSendData = (SendDragDataInfo *)data; PCopy(dragData.name,pSendData->info->name); dragData.menuID = pSendData->info->nodeID; if (mh = GetMenuHandle(dragData.menuID)) dragData.menuItem = FindItemByName(mh,dragData.name); MakeMBSpec(pSendData->info,&dragData.spec); err = SetDragItemFlavorData(pSendData->drag, pSendData->itemRef, pSendData->flavor,&dragData, sizeof(dragData), 0L); break; } return err; } /************************************************************************ * MakeMBSpec - make a file spec for a mailbox file ************************************************************************/ void MakeMBSpec(VLNodeInfo *pData,FSSpec *pSpec) { short vRef; long dirID; Str32 name; MBMenuToFile(pData->nodeID, &vRef, &dirID); PCopy(name,pData->name); if (pData->nodeID == MAILBOX_MENU) switch(pData->rowNum) { // Filename may be different than name in list if localized case kItemInBox: GetRString(name,IN); break; case kItemOutBox: GetRString(name,OUT); break; case kItemJunkBox: GetRString(name,JUNK); break; case kItemTrash: GetRString(name,TRASH); break; } FSMakeFSSpec(vRef,dirID,name,pSpec); } /************************************************************************ * IsIMAPBox - does this entry refer to an IMAP mailbox? ************************************************************************/ Boolean IsIMAPBox(VLNodeInfo *pData) { short vRef; long dirID; if (pData->nodeID == kIMAPFolder) return true; MBMenuToFile(pData->nodeID, &vRef, &dirID); return IsIMAPVD(vRef,dirID); } /********************************************************************** * OpenMBFolder - open folders above this one in the mailbox list **********************************************************************/ void OpenMBFolder(ViewListPtr pView,short menuID,StringPtr s) { MenuHandle mh; if (menuID>0 && (mh = GetMenuHandle(menuID))) { short item; if (menuID == MAILBOX_MENU) { if (IMAPExists()) { // Open Eudora Folder Str32 s; GetDirName(nil,Root.vRef,Root.dirId,s); LVExpand(pView,kEudoraFolder,s,true); } } else { MenuHandle mhPar = ParentMailboxMenu(mh,&item); if (IMAPExists() && GetMenuID(mhPar) == MAILBOX_MENU) { short vRef; long dirID; MBMenuToFile(GetMenuID(mh), &vRef, &dirID); if (IsIMAPVD(vRef,dirID)) { // This is an IMAP account folder GetMenuTitle(mh,s); LVExpand(pView,kIMAPFolder,s,true); return; } } OpenMBFolder(pView,GetMenuID(mhPar),s); MyGetItem(mhPar,item,s); LVExpand(pView,GetMenuID(mhPar),s,true); } } } /********************************************************************** * MBIdle - MB idle function **********************************************************************/ static void MBIdle(MyWindowPtr win) { EnableIMAPButtons(win); } /********************************************************************** * MBIdle - see if we need to hide/show the IMAP refresh button **********************************************************************/ static void EnableIMAPButtons(MyWindowPtr win) { Boolean haveIMAP = IMAPExists(); MenuHandle mh = nil; if (MB.IMAPButtonsVisible != haveIMAP) { SetControlVisibility(CtlOptionsMenu,haveIMAP,true); #ifdef SHOW_RESYNC_BUTTON SetControlVisibility(CtlResync,haveIMAP,true); #endif MB.IMAPButtonsVisible = haveIMAP; } #ifdef SHOW_RESYNC_BUTTON if (MB.IMAPButtonsVisible) { // also grey out the Resync control if there's nothing selected if ( MB.imapMailboxSelected != MB.resyncEnabled) { MB.resyncEnabled = MB.imapMailboxSelected; SetGreyControl(CtlResync, !MB.imapMailboxSelected); } } #endif } /********************************************************************** * EnableOptionsMenu - properly enable and disable items in the options * menu based on what's selected in the mailboxes window **********************************************************************/ void EnableOptionsMenu(MyWindowPtr win, Boolean optionPressed) { Boolean enabled; MenuHandle mh = nil; short count,i; VLNodeInfo data; Boolean imapMailboxSelected = false; Boolean imapPersSelected = false; Boolean popMailboxSelected = false; if (MB.IMAPButtonsVisible) { // what is visible? if (count = LVCountSelection(&MB.list)) { for (i=1;i<=count;i++) { // get mailbox spec MBGetData(&data,i); if (data.nodeID == kIMAPFolder) imapPersSelected = true; else if (IsIMAPBox(&data)) imapMailboxSelected = true; else if (data.nodeID == kEudoraFolder) ; else if (data.iconID == kFolderIcon) ; else popMailboxSelected = true; } } // properly enable the menu items if (!GetControlData(CtlOptionsMenu,kControlMenuPart,kControlBevelButtonMenuHandleTag,sizeof(mh),(Ptr)&mh,nil) && mh) { // prevent the user from doing a refresh if there's currently a background thread running. enabled = CanModifyMailboxTrees() && (imapMailboxSelected || imapPersSelected || (CurrentModifiers()&optionKey)); EnableIf(mh, OPTIONS_REFRESH_ITEM, enabled); // properly enable all IMAP-mailbox-specifc items while we're at it enabled = imapMailboxSelected; EnableIf(mh, OPTIONS_RESYNC_ITEM, enabled); EnableIf(mh, OPTIONS_RESYNCSUB_ITEM, enabled); EnableIf(mh, OPTIONS_AUTOSYNC_ITEM, enabled); EnableIf(mh, OPTIONS_SHOWDELETED_ITEM, enabled); EnableIf(mh, OPTIONS_EXPUNGE_ITEM, enabled); // set the check marks appropriately SetItemMark (mh, OPTIONS_AUTOSYNC_ITEM, IMAPOptionsCheckMarkChar(kNeedsPoll)); SetItemMark (mh, OPTIONS_SHOWDELETED_ITEM, IMAPOptionsCheckMarkChar(kShowDeleted)); // Compact menu item applies to POP mailboxes, too EnableIf(mh, OPTIONS_COMPACT_ITEM, (popMailboxSelected || imapMailboxSelected)); } } } /********************************************************************** * IsSpecialBox - see if this is a special POP box (in, out, trash, Eudora folder) **********************************************************************/ static Boolean IsSpecialBox(ViewListPtr pView, VLNodeInfo *pInfo) { VLNodeInfo efInfo; if (pInfo->rowNum >= kItemMailBoxes) return false; // Need to get status of Eudora folder LVGetItem(pView,1,&efInfo,false); if (efInfo.isParent && efInfo.isCollapsed) return pInfo->rowNum <= 1; else return (pInfo->rowNum < kItemMailBoxes); } /********************************************************************** * MBFindInCollapsed - search a collapsed folder **********************************************************************/ Boolean MBFindInCollapsed(MyWindowPtr win,ViewListPtr pView,PStr what,short menuID) { CGrafPtr winPort; MenuHandle mh; short item,count,id; Str255 s,name; if (mh = GetMenuHandle(menuID)) { count = CountMenuItems(mh); for (item=1;item<=count;item++) { if (id = SubmenuId(mh,item)) { // has submenu. recurse if (MBFindInCollapsed(win,pView,what,id)) return true; } MyGetItem(mh,item,name); if (FindStrStr(what,name)>=0) { // found // open collapsed folder(s) and select item OpenMBFolder(pView,menuID,s); LVSelect(pView,menuID,name,false); winPort = GetMyWindowCGrafPtr (win); LVDraw(pView,MyGetPortVisibleRegion(winPort),true,false); return true; } } } return false; } /********************************************************************** * IsIMAPTrashBox - is this an IMAP trash mailbox? **********************************************************************/ Boolean IsIMAPTrashBox(VLNodeInfo *data) { return (data->iconID == kTrashBoxIcon && IsIMAPBox(data)); } /************************************************************************ * IMAPOptionsCheckMarkChar - return the appropriate character to display * next to an IMAP mailboxes options menu item ************************************************************************/ char IMAPOptionsCheckMarkChar(MailboxNeedsEnum needs) { char c = 0; short count, i, needsMark; VLNodeInfo data; FSSpec spec; MailboxNodeHandle mbox; PersHandle pers; if (count = LVCountSelection(&MB.list)) { // count the number of mailboxes that need to be marked needsMark = 0; for (i=1;i<=count;i++) { // get mailbox spec MBGetData(&data,i); MakeMBSpec(&data,&spec); // is this an IMAP mailbox? if (IsIMAPBox(&data)) { spec.parID = SpecDirId(&spec); LocateNodeBySpecInAllPersTrees(&spec, &mbox, &pers); if (pers && mbox && (DoesIMAPMailboxNeed(mbox,needs) || ((needs == kNeedsPoll) && (mbox == LocateInboxForPers(pers))))) needsMark++; } } // return the proper char if (needsMark) { if (count == needsMark) c = checkMark; else c = dashChar; } } return (c); } /************************************************************************ * MBRefreshIMAPMailboxes - refresh IMAP mailbox trees. That is, fetch * the mailbox list from the server and adjust cache mailboxes as needed ************************************************************************/ void MBRefreshIMAPMailboxes(Boolean optionPressed) { Boolean refresh = false; short count, i; VLNodeInfo data; FSSpec spec; PersHandle pers = nil; MailboxNodeHandle node = nil; // is there anything selected that can be refreshed? if (!optionPressed && (count = LVCountSelection(&MB.list))) { for (i=1;i<=count;i++) { // get mailbox spec MBGetData(&data,i); MakeMBSpec(&data,&spec); // who does this mailbox belong to? if (data.nodeID == kIMAPFolder) { // an IMAP personality heading is selected. Which personality does it refer to? pers = FindPersByName(spec.name); } else { // a mailbox is selected. Who does it belong to? spec.parID = SpecDirId(&spec); LocateNodeBySpecInAllPersTrees(&spec, &node, &pers); } // if it belongs to an IMAP personality, we'll want to refresh this personality if (pers) { (*pers)->imapRefresh = 1; refresh = true; } } // now go refresh if (refresh) IMAPRefreshPersCaches(); } else if (optionPressed) { // Shift was pressed. Refresh ALL the IMAP personalities IMAPRefreshAllCaches(); } } /************************************************************************ * MBResyncOrExpungeIMAPMailboxes - process the selected IMAP mailboxes. ************************************************************************/ OSErr MBResyncOrExpungeIMAPMailboxes(Boolean optionPressed, TaskKindEnum task) { OSErr err; Handle mailboxes; // Build a handle of all of the selected mailboxes err = GetSelectedMailboxSpecs(&mailboxes, optionPressed); if ((err == noErr) && mailboxes) { // process the mailboxs err = IMAPProcessMailboxes(mailboxes, task); } if (err != noErr) { WarnUser(MEM_ERR, err); ZapHandle(mailboxes); } return (err); } /************************************************************************ * MBMarkIMAPMailboxes - mark selected mailboxes ************************************************************************/ void MBMarkIMAPMailboxes(Boolean on, MailboxNeedsEnum needs) { short count, i; VLNodeInfo data; FSSpec spec; PersHandle pers = nil; MailboxNodeHandle node = nil; if (count = LVCountSelection(&MB.list)) { for (i=1;i<=count;i++) { // get mailbox spec MBGetData(&data,i); MakeMBSpec(&data,&spec); if (data.nodeID != kIMAPFolder) { // a mailbox is selected. Who does it belong to? spec.parID = SpecDirId(&spec); LocateNodeBySpecInAllPersTrees(&spec, &node, &pers); if (node) { // is this not the inbox? if ((needs != kNeedsPoll) || (node != LocateInboxForPers(pers))) { // set the property SetIMAPMailboxNeeds(node, needs, on); // are we hiding or showing deleted messages? if (needs == kShowDeleted) HideDeletedMessages(node, true, on); // update the cache mailbox now WriteIMAPMailboxInfo(&spec, node); } } } } // update the list MBTickle(nil,nil); } } /************************************************************************ * MBCompactMailboxes - compact selected mailboxes ************************************************************************/ void MBCompactMailboxes(void) { short count, i; VLNodeInfo data; FSSpec spec; PersHandle pers = nil; MailboxNodeHandle node = nil; // properly set autocheck property if (count = LVCountSelection(&MB.list)) { for (i=1;(i<=count) && !CommandPeriod;i++) { // get mailbox spec MBGetData(&data,i); MakeMBSpec(&data,&spec); // compact the mailbox if ((data.nodeID != kIMAPFolder) && (data.nodeID != kEudoraFolder)) { // is this an IMAP mailbox? if (IsIMAPBox(&data)) spec.parID = SpecDirId(&spec); // is this a POP folder? else if (FSpIsItAFolder(&spec)) continue; CompactMailbox(&spec,false); } } // update the list MBTickle(nil,nil); } }