/* 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 "linkwin.h" #define FILE_NUM 126 /* Copyright (c) 1999 by QUALCOMM Incorporated */ #pragma segment LinkWin #define NUM_COLUMNS 3 #define THUMB_COLUMN 0 #define NAME_COLUMN 1 #define DATE_COLUMN 2 #define MINI_BANNER_WIDTH 70 // Link History window controls enum { kctlColumns1=0, kctlColumns2, kctlColumns3, kctlView, kctlRemove }; typedef struct { ViewList list; MyWindowPtr win; ControlHandle ctlColumns[NUM_COLUMNS],ctlView,ctlRemove; Boolean inited; Boolean needsSort; short columnLefts[NUM_COLUMNS]; LinkSortTypeEnum sort; } WinData; static WinData gWin; short gColumnHeaderControls[NUM_COLUMNS] = {LINK_THUMB_COL_CNTL,LINK_NAME_COL_CNTL,LINK_DATE_COL_CNTL}; /************************************************************************ * prototypes ************************************************************************/ // Link window static void DoDidResize(MyWindowPtr win, Rect *oldContR); static void DoZoomSize(MyWindowPtr win,Rect *zoom); static void DoUpdate(MyWindowPtr win); static Boolean DoClose(MyWindowPtr win); static void DoClick(MyWindowPtr win,EventRecord *event); static void DoCursor(Point mouse); static void DoActivate(MyWindowPtr win); static Boolean DoKey(MyWindowPtr win, EventRecord *event); static OSErr DoDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag); static void DoShowHelp(MyWindowPtr win,Point mouse); static Boolean DoMenuSelect(MyWindowPtr win, int menu, int item, short modifiers); static long ViewListCallBack(ViewListPtr pView, VLCallbackMessage message, long data); static Boolean GetListData(VLNodeInfo *data,short selectedItem); static void SetControls(void); static void DoGrow(MyWindowPtr win,Point *newSize); static Boolean LinkFind(MyWindowPtr win,PStr what); static Boolean LinkDrawRowCallBack(ViewListPtr pView,short item,Rect *pRect,CellRec *pCellData,Boolean select,Boolean eraseName); static Boolean LinkFillBlankCallback(ViewListPtr pView); static void OpenSelectedLinks(void); static void DeleteSelectedLinks(void); static void GetCellRectsForLHWin(ViewListPtr pView,CellRec *pCellData,Rect *pRect,Rect *pIcon,Rect *pName,Rect *pDate,Point *pNamePt,Point *pDatePt); static Boolean LinkAddColumnHeaders(void); static void LinkPositionColumnHeaders(MyWindowPtr win,short count,ControlHandle *pCtlList,short left,short right,short top,short height); static short ColumnRight(short column, short max); static void DoColumnClick(MyWindowPtr win, EventRecord *event, ControlHandle ctl); static Boolean LinkGetCellRectsCallBack(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Rect *iconRect, Rect *nameRect); static Boolean LinkInterestingClickCallBack(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Point pt); static void SetLinkWinBackgroundColor(void); static LinkSortTypeEnum LinkSavedSortOrder(void); static void LinkSaveSortOrder(LinkSortTypeEnum sort); // Offline Link Dialog typedef enum { kOfflineLinkDlogNow = 1, kOfflineLinkDlogCancel = 2, kOfflineLinkDlogBookmark = 3, kOfflineLinkDlogRemind = 4, kOfflineLinkDlogRemember = 5, kOfflineLinkDlogRemindText = 8, kOfflineLinkDlogText = 10 } OfflineLinkEnum; enum { kRemindDlogNow = 1, kRemindDlogLater, kRemindDlogForget }; #define VaildOfflineActionPref(p) ((p == kOfflineLinkDlogNow) || (p == kOfflineLinkDlogBookmark) || (p == kOfflineLinkDlogRemind)) OfflineLinkEnum DoOfflineLinkDialog(void); void DoRemindNag(void); OSErr RemindDlogInit (DialogPtr theDialog, long refcon); Boolean RemindDlogHit (EventRecord *event, DialogPtr theDialog, short itemHit, long dialogRefcon); void RemindSortLinkWin(void); Boolean RemindUserNow(void); Boolean CanRemindUser(void); /************************************************************************ * OpenLinkWin - open the link window ************************************************************************/ void OpenLinkWin(void) { DrawDetailsStruct details; WindowPtr gWinWinPtr; if (!HasFeature (featureLink)) return; UseFeature (featureLink); if (SelectOpenWazoo(LINK_WIN)) return; // Already opened in a wazoo if (!gWin.inited) { short err=0; Rect r; if (GenHistoriesList()!=noErr) goto fail; // age the links if we haven't in a while ... AgeLinks(); if (!(gWin.win=GetNewMyWindow(LINK_WIND,nil,nil,BehindModal,false,false,LINK_WIN))) {err=MemError(); goto fail;} gWinWinPtr = GetMyWindowWindowPtr (gWin.win); SetWinMinSize(gWin.win,320,12*FontLead); SetPort_(GetMyWindowCGrafPtr(gWin.win)); ConfigFontSetup(gWin.win); MySetThemeWindowBackground(gWin.win,kThemeListViewBackgroundBrush,False); /* * list */ SetRect(&r,0,0,20,20); details.arrowLeft = 2; details.iconTop = 2; details.iconLeft = details.arrowLeft; // First icon level details.iconLevelWidth = 0; details.textBottom = 5; details.rowHt = 36; details.nameAddMargin = 5; // Add to name width when drawing details.maxNameWidth = 225; // Approximate maximum name width details.keyNavTicks = 60; // Delay accepted between navigation keys // callbacks for list drawing details.DrawRowCallback = LinkDrawRowCallBack; details.FillBlankCallback = LinkFillBlankCallback; details.GetCellRectsCallBack = LinkGetCellRectsCallBack; details.InterestingClickCallback = LinkInterestingClickCallBack; gWin.sort = LinkSavedSortOrder(); gWin.needsSort = true; if (LVNewWithDetails(&gWin.list,gWin.win,&r,1,ViewListCallBack,flavorTypeText,&details)) {err=MemError(); goto fail;} // controls if (!(gWin.ctlView = NewIconButton(LINK_NEW_CNTL,gWinWinPtr)) || !(gWin.ctlRemove = NewIconButton(LINK_REMOVE_CNTL,gWinWinPtr)) || !(LinkAddColumnHeaders())) goto fail; // columns. Do this in a more flexible way someday gWin.columnLefts[THUMB_COLUMN] = 0; gWin.columnLefts[NAME_COLUMN] = MINI_BANNER_WIDTH+2; gWin.columnLefts[DATE_COLUMN] = gWin.columnLefts[NAME_COLUMN] + details.maxNameWidth; gWin.win->didResize = DoDidResize; gWin.win->close = DoClose; gWin.win->update = DoUpdate; gWin.win->position = PositionPrefsTitle; gWin.win->click = DoClick; gWin.win->bgClick = DoClick; gWin.win->dontControl = true; gWin.win->cursor = DoCursor; gWin.win->activate = DoActivate; gWin.win->help = DoShowHelp; gWin.win->menu = DoMenuSelect; gWin.win->key = DoKey; gWin.win->app1 = DoKey; gWin.win->drag = DoDragHandler; gWin.win->zoomSize = DoZoomSize; gWin.win->grow = DoGrow; gWin.win->idle = nil; gWin.win->find = LinkFind; gWin.win->showsSponsorAd = true; MyWindowDidResize(gWin.win,&gWin.win->contR); ShowMyWindow(gWinWinPtr); gWin.inited = true; return; fail: if (gWin.win) CloseMyWindow(GetMyWindowWindowPtr(gWin.win)); if (err) WarnUser(COULDNT_WIN,err); return; } UserSelectWindow(GetMyWindowWindowPtr(gWin.win)); } /************************************************************************ * LinkAddColumnHeaders - get the column header controls for the LH win ************************************************************************/ static Boolean LinkAddColumnHeaders(void) { WindowPtr gWinWinPtr = GetMyWindowWindowPtr (gWin.win); Boolean result = true; short i; for (i = 0; result && (i < NUM_COLUMNS); i++) { if (!(gWin.ctlColumns[i] = NewIconButton(gColumnHeaderControls[i],gWinWinPtr))) result = false; } return (result); } /************************************************************************ * DoDidResize - resize the window ************************************************************************/ static void DoDidResize(MyWindowPtr win, Rect *oldContR) { #define kListInset 10 #pragma unused(oldContR) ControlHandle ctlList[NUM_COLUMNS]; Rect r; short htAdjustment; short column; short columnHeaderHeight = GROW_SIZE+5; // column header controls for (column = 0; column < NUM_COLUMNS; column++) ctlList[column] = gWin.ctlColumns[column]; LinkPositionColumnHeaders(win,NUM_COLUMNS,ctlList,kListInset,gWin.win->contR.right-kListInset,win->topMargin+kListInset,columnHeaderHeight); // buttons ctlList[0] = gWin.ctlView; ctlList[1] = gWin.ctlRemove; PositionBevelButtons(win,2,ctlList,kListInset,gWin.win->contR.bottom-INSET-kHtCtl,kHtCtl,RectWi(win->contR)); // list SetRect(&r,kListInset,win->topMargin+kListInset+columnHeaderHeight,gWin.win->contR.right-kListInset,gWin.win->contR.bottom - 2*INSET - kHtCtl); if (gWin.win->sponsorAdExists && r.bottom >= gWin.win->sponsorAdRect.top - kSponsorBorderMargin) r.bottom = gWin.win->sponsorAdRect.top - kSponsorBorderMargin; LVSize(&gWin.list,&r,&htAdjustment); // enable/disble controls SetControls(); // redraw InvalContent(win); } /************************************************************************ * LinkPositionColumnHeaders - position and size the column headers ************************************************************************/ static void LinkPositionColumnHeaders(MyWindowPtr win,short count,ControlHandle *pCtlList,short left,short right,short top,short height) { short width[64]; short i; short max = right - left + (count - 1); // rightmost part of last column header, relative to the list. short columnLeft = left; // Get width of each column header for (i=0;ibottom-zoom->top; short zoomWi = zoom->right-zoom->left; short hi, wi; LVMaxSize(&gWin.list, &wi, &hi); wi += 2*kListInset; hi += 2*kListInset + INSET + kHtCtl; 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; } /************************************************************************ * DoGrow - adjust grow size ************************************************************************/ static void DoGrow(MyWindowPtr win,Point *newSize) { WindowPtr winWP = GetMyWindowWindowPtr (win); Rect r; short htControl = ControlHi(gWin.ctlView); short bottomMargin,sponsorMargin; // Get list position bottomMargin = INSET*2 + htControl; if (win->sponsorAdExists) { Rect rPort; GetPortBounds(GetWindowPort(winWP),&rPort); sponsorMargin = rPort.bottom - win->sponsorAdRect.top + kSponsorBorderMargin; if (sponsorMargin > bottomMargin) bottomMargin = sponsorMargin; } SetRect(&r,kListInset,win->topMargin+kListInset,newSize->h-kListInset,newSize->v - bottomMargin); LVCalcSize(&gWin.list,&r); // Calculate new window height newSize->v = r.bottom + bottomMargin; } /************************************************************************ * DoClose - close the window ************************************************************************/ static Boolean DoClose(MyWindowPtr win) { #pragma unused(win) if (gWin.inited) { // Dispose of list LVDispose(&gWin.list); gWin.inited = false; // clean up the history list, but don't throw it away. ZapHistoriesList(false); // Take care of the icon cache as well ZapPVICache(); } return(True); } /************************************************************************ * DoUpdate - draw the window ************************************************************************/ static void DoUpdate(MyWindowPtr win) { CGrafPtr winPort = GetMyWindowCGrafPtr (win); Rect r,rCntl0,rCntlLast; // Tweak the link history window colors SetLinkWinBackgroundColor(); // draw the list r = gWin.list.bounds; DrawThemeListBoxFrame(&r,kThemeStateActive); LVDraw(&gWin.list,MyGetPortVisibleRegion(winPort), true, false); // Draw a rect around the column controls GetControlBounds(gWin.ctlColumns[0],&rCntl0); GetControlBounds(gWin.ctlColumns[NUM_COLUMNS-1],&rCntlLast); SetRect(&r,rCntl0.left-1,rCntl0.top-1,rCntlLast.right,rCntl0.bottom); FrameRect(&r); } /************************************************************************ * DoActivate - activate the window ************************************************************************/ static void DoActivate(MyWindowPtr win) { #pragma unused(win) LVActivate(&gWin.list, gWin.win->isActive); SetControls(); } /************************************************************************ * DoKey - key stroke ************************************************************************/ static Boolean DoKey(MyWindowPtr win, EventRecord *event) { #pragma unused(win) short key = (event->message & 0xff); if (LVKey(&gWin.list,event)) { SetControls(); return true; } return false; } /********************************************************************** * LinkFind - find in the window **********************************************************************/ static Boolean LinkFind(MyWindowPtr win,PStr what) { return FindListView(win,&gWin.list,what); } /************************************************************************ * DoClick - click in window ************************************************************************/ void DoClick(MyWindowPtr win,EventRecord *event) { WindowPtr winWP = GetMyWindowWindowPtr (win); Point pt; ControlHandle hCtl; SetPort(GetMyWindowCGrafPtr(win)); if (!LVClick(&gWin.list,event)) { pt = event->where; GlobalToLocal(&pt); if (!win->isActive) { SelectWindow_(winWP); UpdateMyWindow(winWP); // Have to update manually since no events are processed } if (FindControl(pt, winWP, &hCtl)) { if (TrackControl(hCtl,pt,(void *)(-1))) { if (hCtl == gWin.ctlView) { OpenSelectedLinks(); AuditHit((event->modifiers&shiftKey)!=0, (event->modifiers&controlKey)!=0, (event->modifiers&optionKey)!=0, (event->modifiers&cmdKey)!=0, false, GetWindowKind(winWP), AUDITCONTROLID(GetWindowKind(winWP),kctlView), event->what); } else if (hCtl == gWin.ctlRemove) { DeleteSelectedLinks(); AuditHit((event->modifiers&shiftKey)!=0, (event->modifiers&controlKey)!=0, (event->modifiers&optionKey)!=0, (event->modifiers&cmdKey)!=0, false, GetWindowKind(winWP), AUDITCONTROLID(GetWindowKind(winWP),kctlRemove), event->what); } else DoColumnClick(win, event, hCtl); } } } SetControls(); } /************************************************************************ * DoColumnClick - Handle a click on a column header, if indeed it is one ************************************************************************/ static void DoColumnClick(MyWindowPtr win, EventRecord *event, ControlHandle ctl) { WindowPtr winWP = GetMyWindowWindowPtr (win); short column = 0; LinkSortTypeEnum oldSort = gWin.sort; LinkSortTypeEnum newSort; while (column < NUM_COLUMNS) { if (ctl == gWin.ctlColumns[column]) { newSort = column; if (event->modifiers&optionKey) newSort |= kLinkReverseSort; if (gWin.sort != newSort) { gWin.needsSort = true; gWin.sort = newSort; LinkSaveSortOrder(gWin.sort); LinkTickle(); } AuditHit((event->modifiers&shiftKey)!=0, (event->modifiers&controlKey)!=0, (event->modifiers&optionKey)!=0, (event->modifiers&cmdKey)!=0, false, GetWindowKind(winWP), AUDITCONTROLID(GetWindowKind(winWP),column), mouseDown); break; } column++; } } /************************************************************************ * NotifyLinkWin - notify the link window that we've been up to no good ************************************************************************/ void NotifyLinkWin(void) { if (!HasFeature (featureLink)) return; if (gWin.inited) InvalidListView(&gWin.list); // Regenerate list } /************************************************************************ * DoCursor - set the cursor properly for the window ************************************************************************/ static void DoCursor(Point mouse) { if (!PeteCursorList(gWin.win->pteList,mouse)) SetMyCursor(arrowCursor); } /************************************************************************ * DoShowHelp - provide help for the window ************************************************************************/ static void DoShowHelp(MyWindowPtr win,Point mouse) { if (PtInRect(mouse,&gWin.list.bounds)) MyBalloon(&gWin.list.bounds,100,0,LINK_HELP_STRN+1,0,nil); else ShowControlHelp(mouse,LINK_HELP_STRN+2,gWin.ctlView,gWin.ctlRemove,gWin.ctlColumns[THUMB_COLUMN],gWin.ctlColumns[NAME_COLUMN],gWin.ctlColumns[DATE_COLUMN],nil); } /************************************************************************ * GetListData - get data for selected item ************************************************************************/ static Boolean GetListData(VLNodeInfo *data,short selectedItem) { return LVGetItem(&gWin.list,selectedItem,data,true); } /************************************************************************ * DoMenuSelect - menu choice in the window ************************************************************************/ static Boolean DoMenuSelect(MyWindowPtr win, int menu, int item, short modifiers) { #pragma unused(win,modifiers) switch (menu) { case FILE_MENU: switch(item) { case FILE_OPENSEL_ITEM: // this should open the selected link // MBListOpen(); return(True); break; } break; case EDIT_MENU: if (item==EDIT_SELECT_ITEM) { if (LVSelectAll(&gWin.list)) { SetControls(); return(true); } } break; } return(False); } /********************************************************************** * DoDragHandler - handle drags **********************************************************************/ static OSErr DoDragHandler(MyWindowPtr win,DragTrackingMessage which,DragReference drag) { #pragma unused(win) OSErr err = noErr; //#warning don't forget to handle drags from link history window! if (!DragIsInteresting(drag,MESS_FLAVOR,TOC_FLAVOR,nil)) return(dragNotAcceptedErr); // Nothing here we want switch (which) { case kDragTrackingEnterWindow: case kDragTrackingLeaveWindow: case kDragTrackingInWindow: err = LVDrag(&gWin.list,which,drag); break; case 0xfff: break; } return err; } /************************************************************************ * LinkFillBlankCallback - callback function for List View. Fill unused * parts of the list. ************************************************************************/ static Boolean LinkFillBlankCallback(ViewListPtr pView) { ListHandle hList=pView->hList; Rect rErase; GrafPtr savePort; short column; Rect columnRect; LinkSortTypeEnum sortedColumn = gWin.sort&kLinkSortTypeMask; if ((*hList)->visible.bottom > (*hList)->dataBounds.bottom) { SAVE_STUFF; SET_COLORS; GetPort(&savePort); SetPort(GetMyWindowCGrafPtr(pView->wPtr)); Cell1Rect(hList,(*hList)->dataBounds.bottom+1,&rErase); rErase.bottom = pView->bounds.bottom; if (UseListColors) { for (column = 0; column < NUM_COLUMNS; column++) { // what is the rect describing this column? SetRect(&columnRect,rErase.left+gWin.columnLefts[column],rErase.top,rErase.left+ColumnRight(column,rErase.right),rErase.bottom); // color this column SetThemeBackground(sortedColumn==column ? kThemeBrushListViewSortColumnBackground:kThemeBrushListViewBackground,RectDepth(&columnRect),true); EraseRect(&columnRect); } } SetPort(savePort); REST_STUFF; } return true; /* mtc sez - this value is never checked 11-18-03 */ } /************************************************************************ * ColumnRight - return the right side of a column ************************************************************************/ static short ColumnRight(short column, short max) { short right = 0; if ((column>=0) && (columnwPtr)); Str255 dateStr; short column; Rect columnRect; Handle theIcon; LinkSortTypeEnum sortedColumn = gWin.sort&kLinkSortTypeMask; if (item && item <= (*pView->hList)->dataBounds.bottom) { SAVE_STUFF; // // Figure out where things are in this row // GetCellRectsForLHWin(pView,pCellData,pRect,&rIcon,&rName,&rDate,&ptName,&ptDate); // // Draw the list background color and seperator lines for this row // if (UseListColors) { for (column = 0; column < NUM_COLUMNS; column++) { // what is the rect describing this column? SetRect(&columnRect,pRect->left+gWin.columnLefts[column],pRect->top,pRect->left+ColumnRight(column,pRect->right),pRect->top + (*pView->hList)->cellSize.v - 1); // color this column if (sortedColumn==column || ((sortedColumn == sSpecialRemind) && (IsMarkedRemind(pCellData->nodeID)))) SetThemeBackground(kThemeBrushListViewSortColumnBackground,RectDepth(&columnRect),true); else SetThemeBackground(kThemeBrushListViewBackground,RectDepth(&columnRect),true); EraseRect(&columnRect); } SET_COLORS; } // // Draw the contents of this item // SET_COLORS; // Plot the icon if (pCellData->iconID) { switch (pCellData->iconID) { case kCustomIconResource: theIcon = GetLHPreviewIcon(pCellData->nodeID); if (theIcon) { PlotIconSuite(&rIcon, atNone + atHorizontalCenter, select ? ttSelected : ttNone, theIcon); HPurge(theIcon); // This icon can be purged when needed. break; } else { // this ad does not have a preview for some reason pCellData->iconID = HTTP_LINK_TYPE_ICON; // fall through and plot the http icon } default: OffsetRect(&rIcon,0,-1); PlotIconID(&rIcon,atNone + atHorizontalCenter,select ? ttSelected : ttNone,pCellData->iconID); break; } } // Draw the name MoveTo(ptName.h,ptName.v); if (eraseName) EraseRect(&rName); if (pCellData->misc.style) TextFace(pCellData->misc.style); TextFont(pView->font); TextSize(pView->fontSize); DrawString(pCellData->name); if (pCellData->misc.style) TextFace(0); if (select) InvertRect(&rName); // Draw the date if (GetDateString(pCellData->nodeID,dateStr)) { MoveTo(ptDate.h,ptDate.v); if (eraseName) EraseRect(&rDate); if (pCellData->misc.style) TextFace(pCellData->misc.style); TextFont(pView->font); TextSize(pView->fontSize); DrawString(dateStr); if (pCellData->misc.style) TextFace(0); } REST_STUFF; } return (result); } /********************************************************************** * GetCellRects - get triangle, icon, and name rects **********************************************************************/ static void GetCellRectsForLHWin(ViewListPtr pView,CellRec *pCellData,Rect *pRect,Rect *pIcon,Rect *pName,Rect *pDate,Point *pNamePt,Point *pDatePt) { short offset; Point pt; FontInfo fInfo; short column; SAVE_STUFF; for (column = 0; column < NUM_COLUMNS; column++) { switch (column) { case THUMB_COLUMN: // Thumb or Icon if (pCellData->iconID) { // This entry has an icon. Return rect of icon centered in thumb column. if (column < NUM_COLUMNS - 1) { // center of column, minus half of an icon offset = (gWin.columnLefts[column+1] - gWin.columnLefts[column])/2 - 16; } else { // half an icon away from the leftmost part of the column offset = gWin.columnLefts[column] + 48; } SetRect(pIcon,pRect->left+offset,pRect->top+(pView->details.iconTop),pRect->left+offset+32,pRect->top+(pView->details.iconTop)+32); } else { // This entry has a thumbnail. SetRect(pIcon,pRect->left+(pView->details.arrowLeft),pRect->top+(pView->details.iconTop),pRect->left+(pView->details.arrowLeft)+62,pRect->top+(pView->details.iconTop)+32); } break; case NAME_COLUMN: case DATE_COLUMN: offset = gWin.columnLefts[column] + (pView->details.nameAddMargin); TextFont(pView->font); TextSize(pView->fontSize); pt.h = pRect->left + offset; pt.v = pRect->top+((*pView->hList)->cellSize.v/2)+(pView->fontSize)/2; // center the text vertically GetFontInfo(&fInfo); if (column==NAME_COLUMN) { *pNamePt = pt; SetRect(pName,pt.h,pt.v-fInfo.ascent-fInfo.leading,pt.h + StringWidth(pCellData->name),pt.v+fInfo.descent); } else if (column==DATE_COLUMN) { *pDatePt = pt; SetRect(pDate,pt.h,pt.v-fInfo.ascent-fInfo.leading,pt.h + StringWidth(pCellData->name),pt.v+fInfo.descent); } break; default: break; } } REST_STUFF; } /************************************************************************ * ViewListCallBack - callback function for List View ************************************************************************/ static long ViewListCallBack(ViewListPtr pView, VLCallbackMessage message, long data) { VLNodeInfo *pInfo; OSErr err = noErr; Handle hUrl; SendDragDataInfo *pSendData; switch (message) { case kLVAddNodeItems: AddAllHistoryItems(pView,gWin.needsSort,gWin.sort); // Add History items to the list. Sort if necessary. break; case kLVOpenItem: OpenSelectedLinks(); break; case kLVDeleteItem: DeleteSelectedLinks(); break; case kLVRenameItem: break; case kLVQueryItem: pInfo = ( VLNodeInfo *)data; switch (pInfo->query) { case kQuerySelect: case kQueryDrag: case kQueryDCOpens: return true; case kQueryDrop: case kQueryRename: return false; } break; case kLVSendDragData: pSendData = (SendDragDataInfo*)data; hUrl = GetLinkURL(pSendData->info); HLock(hUrl); err = SetDragItemFlavorData(pSendData->drag, pSendData->itemRef, pSendData->flavor, *hUrl, InlineGetHandleSize((Handle)hUrl), 0L); UL(hUrl); break; } return err; } /************************************************************************ * OpenSelectedLinks - open all selected links ************************************************************************/ static void OpenSelectedLinks(void) { short i; VLNodeInfo info; for (i=1;i<=LVCountSelection(&gWin.list);i++) { GetListData(&info,i); OpenHistoryEntry(&info); } } /************************************************************************ * DeleteSelectedLinks - delete all selected links ************************************************************************/ static void DeleteSelectedLinks(void) { short i; VLNodeInfo info; for (i=1;i<=LVCountSelection(&gWin.list);i++) { GetListData(&info,i); DeleteHistoryEntry(&info); } // Save all history files that need it SaveAllHistoryFiles(); // Tell the Link Window to redraw itself. LinkTickle(); } /************************************************************************ * LinkTickle - tickle the link window ************************************************************************/ void LinkTickle(void) { if (gWin.inited) { InvalidListView(&gWin.list); UpdateMyWindow(GetMyWindowWindowPtr(gWin.win)); } } /************************************************************************ * LinkInterestingClickCallBack - return true if this point is interesting ************************************************************************/ static Boolean LinkInterestingClickCallBack(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Point pt) { Boolean result = false; Rect rIcon, rName, rDate; Point ptName, ptDate; GetCellRectsForLHWin(pView,cellData,cellRect,&rIcon,&rName,&rDate,&ptName,&ptDate); if (PtInRect(pt,&rIcon) || PtInRect(pt,&rName) || PtInRect(pt,&rDate)) result = true; return (result); } /********************************************************************** * GetCellData - Get data for cell,item is 0-based **********************************************************************/ static short GetCellData(ViewListPtr pView,short item,CellRec *pCellData) { short dataLen; Cell c; dataLen = sizeof(CellRec); c.h = 0; c.v = item; LGetCell((Ptr)pCellData, &dataLen, c, pView->hList); return dataLen; } /************************************************************************ * LinkGetCellRectsCallBack - return true if this we should drag ************************************************************************/ static Boolean LinkGetCellRectsCallBack(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Rect *iconRect, Rect *nameRect) { Rect rDate; Point ptName, ptDate; GetCellRectsForLHWin(pView,cellData,cellRect,iconRect,nameRect,&rDate,&ptName,&ptDate); return (true); } /************************************************************************ * SetLinkWinBackgroundColor - set the background color of the link win ************************************************************************/ static void SetLinkWinBackgroundColor(void) { if (UseListColors) { // Using finder list color scheme. Punt on background color. gWin.win->backColor.red = gWin.win->backColor.green = gWin.win->backColor.blue = 0xffff; } else { // Using BACK_COLOR. GetRColor(&gWin.win->backColor,BACK_COLOR); } } /************************************************************************ * LinkHasCustomIcons - return if we should draw preview icons ************************************************************************/ Boolean LinkHasCustomIcons(void) { long flags = GetRLong(LINK_HISTORY_UI_FLAGS); return ((flags&kLinkHasNoCustomIcons)==0); } /************************************************************************ * LinkSavedSortOrder - return sort order saved from last time ************************************************************************/ static LinkSortTypeEnum LinkSavedSortOrder(void) { long flags = GetRLong(LINK_HISTORY_UI_FLAGS); if (flags & kLinkHasNoCustomIcons) flags &= ~kLinkHasNoCustomIcons; return (flags); } /************************************************************************ * LinkSaveSortOrder - remember the sort order ************************************************************************/ static void LinkSaveSortOrder(LinkSortTypeEnum sort) { long newFlags = sort; if (!LinkHasCustomIcons()) newFlags |= kLinkHasNoCustomIcons; SetPrefLong(LINK_HISTORY_UI_FLAGS,newFlags); } /************************************************************************ * PreventOfflineLink - don't follow this link if we're offline ************************************************************************/ Boolean PreventOfflineLink(OSErr *err, Boolean justDoIt) { Boolean preventConnection = false; Boolean goingToFail = false; long offlineAction = 0; *err = noErr; // Only makes sense to do this if the Link Window is present if (!HasFeature(featureLink)) return (false); if (!justDoIt) { // don't follow this link if we're offline goingToFail = Offline; // this link will fail if we have to dial if (!goingToFail) { if (!CanCheckPPPState()) return (false); // if we can't tell if we're offline, pretend we're online goingToFail = TCPWillDial(true); } } // if the machine is running OT, we know for sure if the link is going to fail or not now. if (goingToFail || justDoIt) { // did the user tell us what to do last time? If not, ask again. offlineAction = GetRLong(PREF_OFFLINE_LINK_ACTION); if (!VaildOfflineActionPref(offlineAction) || PrefIsSet(PREF_LINK_OFFLINE_WARN)) offlineAction = DoOfflineLinkDialog(); // if the user cancels the dialog, prevent the connection. if (offlineAction == kOfflineLinkDlogCancel) { // we won't do anything *err = olaCancel; // prevent the connection ... preventConnection = true; } // connect now if (offlineAction == kOfflineLinkDlogNow) { // connect to the internet right now ... *err = OTConnectForLink(); if (*err != noErr) preventConnection = true; } // bookmark if (offlineAction == kOfflineLinkDlogBookmark) { // we will mark the link in the Link History window ... *err = oldaBookmark; // and not connect right now ... preventConnection = true; } // remind later if (offlineAction == kOfflineLinkDlogRemind) { // we will remember this link ... *err = oldaRemind; // and not connect right now ... preventConnection = true; } } // else // no need to prevent the connection. return (preventConnection); } #ifdef NOT_NEEDED /************************************************************************ * PingAdServer - can we see the ad server? * * Do this before launching an ad or web link. This will either cause * the OS to dial, which will happen anyway when the web browser comes * up, or produce some sort of error. * * If there's an error, we know we're not online, and we can display the * Offline Link Dialog. ************************************************************************/ Boolean PingAdServer(void) { Boolean result = false; TransStream stream = nil; extern char PlayListURL[]; // currently in adwin.c Str255 proto,host,query; unsigned char *p; long port = 80; //default http: port OSErr err = NewTransStream(&stream); Boolean oldPref = PrefIsSet(PREF_IGNORE_PPP); //#warning Ping the Adserver, whatever we're supposed to if (err == noErr) { // extract the host from the playlist url if ((err=ParseURL(PlayListURL,proto,host,query))==noErr) { if (host[0]) { // null terminate host ... host[MIN(host[0]+1,255)] = 0; // figure out the server and port from the hostname ... p = PIndex(host, ':'); if (p) { host[0] = p - host - 1; // host now contains the hostname only; p++; if (port = atoi(p)); // port is whatever's left over else port = 80; // or the default http: port } // try to connect to the host or something. See if it's there. SetPref(PREF_IGNORE_PPP,YesStr); err = ConnectTrans(stream, host, port, true, GetRLong(OFFLINE_LINK_OPEN_TIMEOUT)); SetPref(PREF_IGNORE_PPP,oldPref ? YesStr : NoStr); // That's all we wanted. Cleanup. if (err == noErr) { DestroyTrans(stream); result = true; } ZapTransStream(&stream); } } } return (result); } #endif //NOT_NEEDED /************************************************************************ * DoOfflineLinkDialog - Ask the user what to do with a link that can't * be followed right now. ************************************************************************/ OfflineLinkEnum DoOfflineLinkDialog(void) { MyWindowPtr offlineLinkDlogWin; DialogPtr offlineLinkDlog; extern ModalFilterUPP DlgFilterUPP; short itemHit; Boolean done; short itemType; Handle itemH; Rect rect; Boolean remember; SAVE_PORT; //get the Offline Link dialog SetDialogFont (SmallSysFontID()); offlineLinkDlogWin = GetNewMyDialog(OFFLINE_LINK_DLOG,nil,nil,InFront); SetDialogFont(0); offlineLinkDlog = GetMyWindowDialogPtr (offlineLinkDlogWin); if (!offlineLinkDlog) { WarnUser(GENERAL,MemError()); return(kOfflineLinkDlogCancel); } //fix the controls SetPort(GetDialogPort(offlineLinkDlog)); ConfigFontSetup(offlineLinkDlogWin); // ReplaceAllControls(offlineLinkDlog); // let's leave the checkbox alone ... // uncheck the Remember checkbox remember = false; GetDialogItem(offlineLinkDlog, kOfflineLinkDlogRemember, &itemType, &itemH, &rect); SetDItemState(offlineLinkDlog,kOfflineLinkDlogRemember,remember); // hide the Remind stuff if we're too uninformed if (!CanRemindUser()) { HideDialogItem(offlineLinkDlog,kOfflineLinkDlogRemind); HideDialogItem(offlineLinkDlog,kOfflineLinkDlogRemindText); } //Do the dialog SetMyCursor(arrowCursor); StartMovableModal(offlineLinkDlog); ShowWindow(GetMyWindowWindowPtr (offlineLinkDlogWin)); done = false; do { MovableModalDialog(offlineLinkDlog,DlgFilterUPP,&itemHit); switch(itemHit) { case kOfflineLinkDlogRemember: remember = !remember; GetDialogItem(offlineLinkDlog, kOfflineLinkDlogRemember, &itemType, &itemH, &rect); SetDItemState(offlineLinkDlog,kOfflineLinkDlogRemember,remember); break; case kOfflineLinkDlogNow: case kOfflineLinkDlogBookmark: case kOfflineLinkDlogRemind: case kOfflineLinkDlogCancel: done = true; break; default: break; } } while (!done); // Set the remember pref properly if we were supposed to ... if (remember && VaildOfflineActionPref(itemHit)) { SetPrefLong(PREF_OFFLINE_LINK_ACTION,itemHit); SetPref(PREF_LINK_OFFLINE_WARN, NoStr); } else { SetPrefLong(PREF_OFFLINE_LINK_ACTION,0); SetPref(PREF_LINK_OFFLINE_WARN, YesStr); } EndMovableModal(offlineLinkDlog); DisposDialog_(offlineLinkDlog); REST_PORT; return (itemHit); } /************************************************************************ * RemindNag - Nag the user about links remembered in the Link Window ************************************************************************/ void RemindNag(void) { // Are there any links to remind the user about? if (gNeedRemind) { // nothing to do if the Link Window isn't around. if (!HasFeature(featureLink)) return; if (!CanCheckPPPState()) return; // see if we're online. if (RemindUserNow()) { // Nag the user DoRemindNag(); // don't need to do remind again anytime soon. gNeedRemind = false; } } } /************************************************************************ * DoRemindNag - display the remind nag ************************************************************************/ void DoRemindNag(void) { extern ModalFilterUPP DlgFilterUPP; Nag(NAG_LINK_REMIND_DLOG, RemindDlogInit, RemindDlogHit, nil, false, 0); } /************************************************************************ * RemindDlogInit - init the Remind nag ************************************************************************/ OSErr RemindDlogInit (DialogPtr theDialog, long refcon) { OSErr theError = noErr; ConfigFontSetup(GetDialogMyWindowPtr(theDialog)); return (theError); } /************************************************************************ * RemindDlogHit - handle clicks in the remind dialog ************************************************************************/ Boolean RemindDlogHit (EventRecord *event, DialogPtr theDialog, short itemHit, long dialogRefcon) { switch (itemHit) { // remind the user about the sites NOW case kRemindDlogNow: CloseMyWindow (GetDialogWindow(theDialog)); RemindSortLinkWin(); UnRemindLinks(false); break; // make the dialog go away, but don't forget about the sites case kRemindDlogLater: CloseMyWindow (GetDialogWindow(theDialog)); break; // forget about the sites completely case kRemindDlogForget: UnRemindLinks(true); CloseMyWindow (GetDialogWindow(theDialog)); LinkTickle(); break; } return (true); } /************************************************************************ * RemindSortLinkWin - sort the link window so the Remind links filter * to the top. ************************************************************************/ void RemindSortLinkWin(void) { OpenLinkWin(); if (gWin.inited) { gWin.needsSort = true; gWin.sort = sSpecialRemind; LinkSaveSortOrder(gWin.sort); ScrollTopListView(&(gWin.list)); SetControls(); LinkTickle(); } } /************************************************************************ * RemindUserNow - are we online enough to remind the user about links? * This is a lot like AutoCheckOK(). ************************************************************************/ Boolean RemindUserNow(void) { uLong hourBits; DateTimeRec dtr; // if we're offline, forget about it if (Offline) return(False); // if we're on batteries, and we're not supposed to check while on batteries, don't fire up the network for this either. // go ahead and remind the user if the network is up, even if the machine is on batteries. -jdboyd 01/22/01 // if (PrefIsSet(PREF_NO_BATT_CHECK) && OnBatteries()) return(False); // if PPP is down, forget it. if (PPPDown()) return(False); // are we outside the range of time we're allowed to check? Don't remind the user now, either hourBits = GetPrefLong(PREF_CHECK_HOURS); if (0!=(hourBits&kHourUseMask) || 0!=(hourBits&kHourNeverWeekendMask)) { SecondsToDate(LocalDateTime(),&dtr); if (dtr.dayOfWeek==1||dtr.dayOfWeek==7) // weekends { if (hourBits&kHourAlwaysWeekendMask) return(True); if (hourBits&kHourNeverWeekendMask) return(False); } if (0!=(hourBits&kHourUseMask)) // non-weekends, or weekend same as weekday { return 0!=(hourBits&(1L<