eudora-mac/listview.c

1 line
99 KiB
C
Executable File
Raw Permalink Blame History

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
/**********************************************************************
* Implements a hierarchical list similar to Finder's list view
**********************************************************************/
#include "listview.h"
#define FILE_NUM 83
// Don't include auto bring-to-front feature. There is a bug when returning the window
// to it's previous position--it sometimes gets lost. This bug is not reproduceable and
// I can't determine what's causing it. ALB 1/24/97
// Well, let's try it again! ALB 7/11/97
#define AUTOFRONT true
#pragma segment ListView
static short gAddCell,gAddLevel;
static OSType gDragFlavor;
#define ArrowLeft pView->details.arrowLeft
#define IconTop pView->details.iconTop
#define IconLeft pView->details.iconLeft
#define IconLevelWidth pView->details.iconLevelWidth
#define TextBottom pView->details.textBottom
#define RowHt pView->details.rowHt
#define NameAddMargin pView->details.nameAddMargin
#define MaxNameWidth pView->details.maxNameWidth
#define KeyNavTicks pView->details.keyNavTicks
#define COLLAPSED_ICON 3060
#define EXPANDED_ICON 3061
// Drag info needed for list clickloop
typedef struct
{
ViewListPtr pView;
EventRecord *event;
Point pt;
short clickLoopCount;
Boolean dragHilited;
DragReference drag;
} DragInfo;
// Prototypes
static void AdjustPeteSize(ViewListPtr pView,Rect *rErase);
static void BringWinToFront(ViewListPtr pView);
static long CallBack(ViewListPtr pView,short message, long data);
static void CheckFillList(ViewListPtr pView);
static void CollapseDragExpanded(ViewListPtr pView,short descendent,DragReference drag);
static void CopyCellInfo(VLNodeInfo *pInfo,CellRec *pCellData,short rowNum);
static void CopyNodeInfo(CellRec *pCellData,VLNodeInfo *pInfo);
static short CountChildren(ViewListPtr pView,short item);
static void DelayTicks(long tickCount);
static void DisposeDragRgn(RgnHandle rgn, GWorldPtr gworld, RgnHandle hDragRgn);
static void DoDraw(ViewListPtr pView);
static void DontDraw(ViewListPtr pView);
static void DragHilite(ViewListPtr pView,short row,Boolean hilite);
static void DragSelect(ViewListPtr pView,Point initPt);
static void DrawItem(ViewListPtr pView,short row,Rect *pRect,CellRec *pCellData,Boolean select,Boolean eraseName);
static void DrawPete(ViewListPtr pView);
static void DrawRow(ViewListPtr pView, short row,Boolean hilite);
static CellRec *FindByName(ViewListPtr pView,VLNodeID id,StringPtr sName,short *pRow);
static CellRec *FindByNodeID(ViewListPtr pView,VLNodeID id,short *pRow);
static CellRec *FindCellData(ViewListPtr pView,short item);
static void ClipMax(void);
static void DrawList(ViewListPtr pView,Rect *pRect);
static void FinishRename(ViewListPtr pView,Boolean fRename);
static short GetCellData(ViewListPtr pView,short item,CellRec *pCellData);
static void AdjustSubsequentCells (ListHandle hList, long followingThisOffset, long delta);
static void GetCellRects(ViewListPtr pView,CellRec *pCellData,Rect *pRect,Rect *pTriangle,Rect *pIcon,Rect *pName,Point *pText);
static Boolean GetDragRgn(ViewListPtr pView, RgnHandle *rgn, GWorldPtr *gworld, RgnHandle *hDrawRgn,DragReference drag,short row);
static short GetParent(ViewListPtr pView, short rowNum);
static void InitRename(ViewListPtr pView,Rect *pRect,UPtr name,short rowNum);
static Boolean IsDragExpanded(short rowNum);
static short InWhichCell(Point pt,ListHandle hList);
static void KeyNavigate(ViewListPtr pView,StringPtr sKeyNavigation);
static Boolean ListDrag(ViewListPtr pView,EventRecord *event,Point pt);
static pascal Boolean MyListClickLoop(void);
static void PlotTriangle(Rect *pRect,short index);
static void PlotTheIcon(short iconID, Rect *pRect, Boolean selected,IconAlignmentType align);
static Boolean QueryItem(ViewListPtr pView,short query,short item);
#if AUTOFRONT
static void RestoreToLayer(ViewListPtr pView, Boolean fUpdate);
#endif
static Boolean SelectOneItem(ViewListPtr pView,short row);
static void SelectItem(ViewListPtr pView,short item,Boolean fSelect,Boolean dontUpdate);
static OSErr SetCellData(ViewListPtr pView,short item,CellRec *pCellData);
static void SetCollapseStatus(ViewListPtr pView,CellRec *pCellData,short rowNum,Boolean fCollapse);
static pascal OSErr ListViewSend(FlavorType flavor, void *dragSendRefCon, ItemReference theItemRef, DragReference drag);
static void SetWaitExpandRow(ViewListPtr pView,short rowNum);
static void ShowListDragHilite(ViewListPtr pView, DragReference drag);
static void SizePete(ViewListPtr pView, Rect *pRect);
static Boolean TrackTriangle(Rect *prTriangle,Boolean collapsed);
static void UpdateSelection(ViewListPtr pView);
static SignedByte LockCells(ViewListPtr pView);
static SignedByte LockUserHandle(ViewListPtr pView);
static void RestoreCells(ViewListPtr pView,SignedByte state);
static void RestoreUserHandle(ViewListPtr pView,SignedByte state);
static void CheckAutoSelect(ViewListPtr pView,short row);
static Boolean DrawRowCallback(ViewListPtr pView,short item,Rect *pRect,CellRec *pCellData,Boolean select,Boolean eraseName);
static Boolean FillBlankCallback(ViewListPtr pView);
static Boolean GetCellRectsCallBack(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Rect *iconRect, Rect *nameRect);
static Boolean InterestingClickCallback(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Point pt);
// Globals
static Boolean gDragFromMe,gDontDragHilite;
static DragInfo gDragInfo;
static short gDragExpandLevel;
static short gWaitExpandRow; // This row is a folder about to auto expand
static Boolean gDidDrag; // Did we do a drag on the click?
#if AUTOFRONT
static WindowPtr gwSendBehind;
#endif
static Boolean gAutoSelect;
static Handle ghDrawExpandedRows; // RowNum (1-based) of drag-expanded row at each level
static Boolean gOpenAfterRename;
/**********************************************************************
* LVNew - Open a new list view, use standard list view sizes.
**********************************************************************/
OSErr LVNew(ViewListPtr pView,MyWindowPtr wPtr,Rect *pBounds,VLNodeID rootNodeID,
ViewListCallBackPtr pCallBack,OSType dragFlavor)
{
DrawDetailsStruct details;
// Use the standard sizes for this list view ...
LVInitDetails(&details);
return (LVNewWithDetails(pView,wPtr,pBounds,rootNodeID,pCallBack,dragFlavor,&details));
}
/**********************************************************************
* LVNewWithDetails - Open a new list view
**********************************************************************/
OSErr LVNewWithDetails(ViewListPtr pView,MyWindowPtr wPtr,Rect *pBounds,
VLNodeID rootNodeID,ViewListCallBackPtr pCallBack,OSType dragFlavor,
DrawDetailsPtr details)
{
Rect rDataBounds,rBounds;
Cell cSize;
ListHandle hList;
DECLARE_UPP(ListViewListDef,ListDef);
INIT_UPP(ListViewListDef,ListDef);
WriteZero(pView,sizeof(ViewList));
pView->wPtr = wPtr;
pView->bounds = rBounds = *pBounds;
rBounds.right -= GROW_SIZE; // Don't include scroll bar in parameter to LNew
pView->rootNodeID = rootNodeID;
pView->pCallBack = pCallBack;
pView->details = *details;
SetRect(&rDataBounds,0,0,1,0);
pView->dragFlavor = dragFlavor;
pView->font = SmallSysFontID();
pView->fontSize = SmallSysFontSize();
pView->listFocus = kFocusNone;
cSize.v = RowHt;
cSize.h = 0;
if (pView->hList = hList = CreateNewList(ListViewListDefUPP,LISTVIEW_LDEF,pBounds,&rDataBounds,cSize,GetMyWindowWindowPtr(wPtr),False,False,False,True))
{
(*hList)->indent.v = 4;
(*hList)->refCon = (long)pView;
pView->hSelectList = NuHTempOK(0);
pView->flags = CallBack(pView,kLVGetFlags,0);
if (pView->flags & kfSupportsMondoBigList)
(*hList)->userHandle = NuHTempOK (0);
if (pView->flags & kfSingleSelection)
(*hList)->selFlags |= lOnlyOne;
wPtr->pView = pView;
// Add the first level
InvalidListView(pView);
}
return hList == 0;
}
/**********************************************************************
* LVInitDetails - initialize details fields
**********************************************************************/
void LVInitDetails(DrawDetailsPtr details)
{
Zero(*details);
details->arrowLeft = 3;
details->iconTop = 1;
details->iconLeft = details->arrowLeft; // First icon level
details->iconLevelWidth = 20;
details->textBottom = 5;
details->rowHt = 18;
details->nameAddMargin = 2; // Add to name width when drawing
details->maxNameWidth = 160; // Approximate maximum name width
details->keyNavTicks = 60; // Delay accepted between navigation keys
}
/**********************************************************************
* LVDispose - Dispose of list view
**********************************************************************/
void LVDispose(ViewListPtr pView)
{
if (pView->pte)
FinishRename(pView,true);
if (pView->hList)
{
ZapHandle ((*pView->hList)->userHandle);
LDispose(pView->hList);
pView->hList = nil;
}
if (pView->hSelectList)
{
ZapHandle(pView->hSelectList);
pView->hSelectList = 0;
pView->selectCount = 0;
}
}
/**********************************************************************
* LVAdd - Add a list view item
* <09><> This should only be called in response to a kLVAddNodeItems request <20><>
**********************************************************************/
OSErr LVAdd(ViewListPtr pView, VLNodeInfo *pInfo)
{
CellRec cellData;
OSErr theError;
short theRow;
theError = noErr;
// Add the list item
if (!(pView->flags & kfManualRowAddsExpected))
DontDraw(pView);
if (pView->flags & kfManualRowAddsExpected)
theRow = gAddCell;
else
theRow = LAddRow(1,gAddCell,pView->hList);
// Save the cell data
cellData.iconID = pInfo->iconID;
cellData.nodeID = pInfo->nodeID;
cellData.misc.fParent = pInfo->isParent;
cellData.misc.fCollapsed = pInfo->isCollapsed;
cellData.misc.fHaveChildren = false;
cellData.misc.level = pInfo->useLevelZero ? 0 : gAddLevel;
cellData.misc.style = pInfo->style;
cellData.refCon = pInfo->refCon;
PCopy(cellData.name,pInfo->name);
theError = SetCellData(pView,theRow,&cellData);
gAddCell++;
if (gAutoSelect)
SelectItem(pView,theRow+1,true,false);
if (!(pView->flags & kfManualRowAddsExpected))
DoDraw(pView);
return (theError);
}
/**********************************************************************
* InvalidListView - Regenerate the list
**********************************************************************/
void InvalidListView(ViewListPtr pView)
{
short saveTop;
ListHandle hList = pView->hList;
GrafPtr savePort;
// Rect rDraw;
GetPort(&savePort);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
gAddCell = 0;
gAddLevel = 1;
saveTop = (*hList)->visible.top;
DontDraw(pView);
LDelRow(REAL_BIG,0,hList);
if (pView->flags & kfSupportsMondoBigList)
SetHandleBig ((*hList)->userHandle, 0);
gAutoSelect = pView->flags&kfAutoSelectAll != 0;
CallBack(pView,kLVAddNodeItems,pView->rootNodeID);
CheckFillList(pView);
gAutoSelect=false;
if (saveTop)
{
LScroll(0, saveTop, hList);
}
UpdateSelection(pView);
InvalWindowRect(GetMyWindowWindowPtr(pView->wPtr),&pView->bounds);
SetPort(savePort);
DoDraw(pView);
}
/**********************************************************************
* AdjustPeteSize - Adjust the size of the edit region based on the size
* of the string and the width of the window
**********************************************************************/
static void AdjustPeteSize(ViewListPtr pView,Rect *rErase)
{
Str63 s;
Rect r,rCell;
SAVE_STUFF;
SET_COLORS;
PeteStringLo(s,sizeof(s),pView->pte);
if (*s>31) *s=31;
PeteRect(pView->pte,&r);
r.right = r.left + StringWidth(s) + 8;
SizePete(pView,&r);
if (rErase)
{
rErase->left = r.right+1;
EraseRect(rErase);
}
else
{
Cell1Rect(pView->hList,pView->renameRow,&rCell);
rCell.left = r.right+1;
EraseRect(&rCell); // Erase if Pete shrinks
}
DrawPete(pView);
REST_STUFF;
}
/**********************************************************************
* LVKey - Key for list view
**********************************************************************/
Boolean LVKey(ViewListPtr pView,EventRecord *event)
{
Boolean fResult = true;
Boolean fUpdate = false;
short key = (event->message & 0xff);
ListHandle hList = pView->hList;
Str63 s;
short row;
static Str32 sKeyNavigation;
static unsigned long lastKeyTicks;
unsigned long ticks = 0;
if (pView->pte)
{
// Renaming a file
Boolean isDirtyKey;
long start,stop;
if (event->modifiers & cmdKey)
{
long select;
if ((select=MyMenuKey(event)) && ((select >> 16) & 0xffff)==EDIT_MENU)
{
// May have done paste or undo which
// require resizing the edit field
DoMenu(GetMyWindowWindowPtr(pView->wPtr),select,event->modifiers);
AdjustPeteSize(pView,nil);
return true;
}
return (false);
}
switch (key)
{
case returnChar:
case enterChar:
case tabChar:
FinishRename(pView,true);
break;
case escChar:
FinishRename(pView,false);
break;
default:
PeteStringLo(s,33,pView->pte);
isDirtyKey = DirtyKey(event->message);
// Don't try to insert another character if the name is
// already at 31 characters.
PeteGetTextAndSelection(pView->pte,nil,&start,&stop);
if (*s >= 31 &&
isDirtyKey &&
start == stop &&
key != backSpace &&
key != delChar)
{
// Oops, file name too long
}
else
{
Rect rTemp,rErase;
if (isDirtyKey)
{
// Make sure field is wide enough to insert a wide character
PeteRect(pView->pte,&rTemp);
SetRect(&rErase,rTemp.left,rTemp.top-1,rTemp.right+1,rTemp.bottom+1);
rTemp.right += 12;
SizePete(pView,&rTemp);
}
PeteEdit(pView->wPtr,pView->pte,peeEvent,event);
if (isDirtyKey)
{
PeteRect(pView->pte,&rTemp);
AdjustPeteSize(pView,&rErase);
rTemp.right += 20;
ValidWindowRect(GetMyWindowWindowPtr(pView->wPtr),&rTemp);
}
}
break;
}
return true;
}
if (ListApp1(event,hList)) // Check for page commands
fUpdate = true;
else
{
SelectedListHandle hSelectList;
SelectedListPtr pSelectList;
short i,count,bottom;
CellRec cellData;
Boolean fCollapse;
hSelectList = pView->hSelectList;
pSelectList = *hSelectList;
count = pView->selectCount;
bottom = (*hList)->dataBounds.bottom;
if (event->modifiers & cmdKey)
{
// Command key down
switch (key)
{
case leftArrowChar:
case rightArrowChar:
// Expand or collapse selected items
fCollapse = key==leftArrowChar;
for(i=0;i<pView->selectCount;i++)
{
row = (*hSelectList)[i].row;
GetCellData(pView,row,&cellData);
if (cellData.misc.fParent && (cellData.misc.fCollapsed != fCollapse))
{
SetCollapseStatus(pView,&cellData,row+1,fCollapse);
CheckFillList(pView);
UpdateSelection(pView);
}
}
break;
case upArrowChar:
row = count ? pSelectList[0].row - 1 : 0;
for (; row >= 0; --row)
{
GetCellData(pView,row,&cellData);
if (cellData.misc.fParent) {
SelectOneItem(pView,row);
break;
}
}
break;
case downArrowChar:
row = count ? pSelectList[count-1].row + 1 : 0;
for (; row < bottom; ++row)
{
GetCellData(pView,row,&cellData);
if (cellData.misc.fParent) {
SelectOneItem(pView,row);
break;
}
}
break;
default:
fResult = false;
break;
}
}
else
{
switch (key)
{
case upArrowChar:
row = count ? pSelectList[0].row : 1;
if (row >= 1)
row--;
SetSelection:
SelectOneItem(pView,row);;
break;
case downArrowChar:
row = count ? pSelectList[count-1].row : bottom-1;
if (row < bottom-1)
row++;
goto SetSelection;
case returnChar:
case enterChar:
CallBack(pView,kLVOpenItem,0);
break;
case backSpace:
case delChar:
CallBack(pView,kLVDeleteItem,dataDeleteFromKeyboard);
break;
default:
// Do key navigation
if (key >= ' ')
{
ticks = TickCount();
if (ticks > lastKeyTicks+KeyNavTicks)
// Start over
*sKeyNavigation = 0;
if (*sKeyNavigation < 31)
{
sKeyNavigation[++*sKeyNavigation] = key;
KeyNavigate(pView,sKeyNavigation);
}
}
else
fResult = false;
break;
}
}
}
lastKeyTicks = ticks;
if (fResult)
UpdateSelection(pView);
return fResult;
}
/**********************************************************************
* LVClick - Click on list view
**********************************************************************/
Boolean LVClick(ViewListPtr pView,EventRecord *event)
{
Boolean fDblClick;
short rowNum;
ListHandle hList;
Point pt;
Boolean fResult = false;
GrafPtr savePort;
ListFocusType newFocus = pView->listFocus;
SAVE_STUFF;
SET_COLORS;
GetPort(&savePort);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
pt = event->where;
GlobalToLocal(&pt);
gDidDrag = false;
if (pView->pte && PtInPETE(pt,pView->pte))
{
PeteEdit(pView->wPtr,pView->pte,peeEvent,event);
fResult = true;
}
else
{
ControlRef vScroll;
Rect rScroll;
if (pView->pte)
FinishRename(pView,true);
hList = pView->hList;
vScroll = (*hList)->vScroll;
GetControlBounds(vScroll,&rScroll);
if (PtInRect(pt,&rScroll))
{
// Click in scroll bar
BringWinToFront(pView);
MyLClick(pt,event->modifiers,pView->hList);
UpdateSelection(pView);
fResult = true;
}
else if (PtInRect(pt,&pView->bounds))
{
rowNum = InWhichCell(pt,hList);
newFocus = kFocusList;
if (rowNum)
{
Point ptName;
CellRec cellData;
Rect rTriangle,rIcon,rName,rCell;
Cell1Rect(hList,rowNum,&rCell);
GetCellData(pView,rowNum-1,&cellData);
GetCellRects(pView,&cellData,&rCell,&rTriangle,&rIcon,&rName,&ptName);
// Check for click in triangle
if (cellData.misc.fParent &&
PtInRect(pt, &rTriangle))
{
if (TrackTriangle(&rTriangle,cellData.misc.fCollapsed))
{
// Toggle triangle
SetCollapseStatus(pView,&cellData,rowNum,!cellData.misc.fCollapsed);
}
}
// Check for click in icon or name
else if (((*pView->details.InterestingClickCallback) && (InterestingClickCallback(pView, &cellData, &rCell, pt)) || PtInRect(pt,&rIcon) || PtInRect(pt,&rName)) && QueryItem(pView,kQuerySelect,rowNum))
{
ListClickLoopUPP saveClickLoop;
DECLARE_UPP(MyListClickLoop,ListClickLoop);
INIT_UPP(MyListClickLoop,ListClickLoop);
// Set up a clickloop to check for drags. Don't leave cliploop function installed
// because we don't want it when doing LClick for the scroll bar
saveClickLoop = (*hList)->lClickLoop;
(*hList)->lClickLoop = MyListClickLoopUPP;
// Set up data needed for clickloop since cliploop receives no parameters
gDragInfo.pView = pView;
gDragInfo.event = event;
gDragInfo.pt = pt;
gDragInfo.clickLoopCount = 0;
gDragInfo.dragHilited = true;
gDragInfo.drag = nil;
fDblClick = MyLClick(pt,event->modifiers,pView->hList);
(*hList)->lClickLoop = saveClickLoop;
if (!gDidDrag)
{
Cell lastCell = LLastClick(hList);
CheckAutoSelect(pView,lastCell.v);
}
UpdateSelection(pView);
if (!gDidDrag)
{
BringWinToFront(pView);
if (fDblClick)
{
// Double click.
if (QueryItem(pView,kQueryDCOpens,rowNum))
// Open the selected item(s)
CallBack(pView,kLVOpenItem,0);
else if (cellData.misc.fParent)
// Folder. Toggle collapse status
SetCollapseStatus(pView,&cellData,rowNum,!cellData.misc.fCollapsed);
}
else if (PtInRect(pt,&rName) && pView->selectCount==1 && QueryItem(pView,kQueryRename,rowNum))
{
// Rename this item
long startTicks;
Boolean fAbortRename = false;
// Let's wait around for the time of a double-click to see if the
// user is going to double click
for (startTicks = TickCount();TickCount() < startTicks + GetDblTime();)
{
EventRecord theEvent;
if (EventAvail(mDownMask+keyDownMask, &theEvent))
{
// Mouse or key event. We don't want to rename
fAbortRename = true;
break;
}
}
if (!fAbortRename)
{
InitRename(pView,&rName,cellData.name,rowNum);
newFocus = kFocusEdit;
}
}
}
}
else
{
// Click on nothing.
if (!(event->modifiers&(shiftKey+cmdKey)))
{
// Select nothing if no shift or command key key
SelectOneItem(pView,-1);
}
BringWinToFront(pView);
// Check for drag selection
if (!(pView->flags & kfSingleSelection))
if (MyWaitMouseMoved(event->where,True))
{
DragSelect(pView,pt);
}
}
}
else
BringWinToFront(pView);
CheckFillList(pView);
fResult = true;
}
}
LVChangeFocus (pView, newFocus);
SetPort(savePort);
REST_STUFF;
return fResult;
}
/**********************************************************************
* LVDraw - Draw list view
**********************************************************************/
void LVDraw(ViewListPtr pView, RgnHandle hRgn, Boolean checkList, Boolean fDrawFrame)
{
WindowPtr pViewWPtrWP = GetMyWindowWindowPtr (pView->wPtr);
Rect r;
// Make sure we have the data on all the cells currently being displayed
if (checkList)
CheckFillList(pView);
// Draw the list
LUpdate(hRgn ? hRgn : MyGetPortVisibleRegion(GetWindowPort(pViewWPtrWP)),pView->hList);
DrawPete(pView);
// Draw frame
if (fDrawFrame)
{
r = pView->bounds;
InsetRect(&r,-1,-1);
FrameRect(&r);
}
LVDrawFocus (pView);
// Fill the unused portion of the list with something
if (pView->details.FillBlankCallback) FillBlankCallback(pView);
}
/**********************************************************************
* LVActivate - Activate list view
**********************************************************************/
void LVActivate(ViewListPtr pView, Boolean activate)
{
#pragma unused(activate)
if (pView->pte)
FinishRename(pView,true);
LActivate(activate,pView->hList);
LVDrawFocus (pView);
}
/**********************************************************************
* LVSize - Size list view
**********************************************************************/
void LVSize(ViewListPtr pView,Rect *pRect,short *pHeightAdjustment)
{
WindowPtr pViewWPtrWP = GetMyWindowWindowPtr (pView->wPtr);
Point cSize;
Rect r,rPort;
ControlHandle cntl;
if (pView->pte) FinishRename(pView,true);
r = *pRect; // May get modified. Make a copy.
cSize.v = RowHt;
cSize.h = r.right-r.left;
// Make height a multiple of the cell height
if (pHeightAdjustment)
{
r.bottom = r.top + ((r.bottom-r.top)/RowHt)*RowHt;
*pHeightAdjustment = r.bottom - pRect->bottom;
}
pView->bounds = r;
r.right -= GROW_SIZE; // Don't include scroll bar in this measurement
InsetRect(&(*pView->hList)->rView,2,2);
ResizeList(pView->hList,&r,cSize);
cntl = (*pView->hList)->vScroll;
GetControlBounds(cntl,&r);
GetPortBounds(GetWindowPort(pViewWPtrWP),&rPort);
if (r.bottom > rPort.bottom-14)
{
// scroll bar is covering up grow box. move it up
r.bottom = rPort.bottom-14;
MySetCntlRect(cntl,&r);
}
if (pView->pte)
AdjustPeteSize(pView,nil);
}
/**********************************************************************
* LVCalcSize - Calculate the adjust size the list will be
**********************************************************************/
void LVCalcSize(ViewListPtr pView, Rect *r)
{
#pragma unused(pView)
// Make height a multiple of the cell height
r->bottom = r->top + ((r->bottom-r->top+RowHt/2)/RowHt)*RowHt;
}
/**********************************************************************
* FindByName - find an item by nodeID and name, row is 1-based
**********************************************************************/
static CellRec *FindByName(ViewListPtr pView,VLNodeID id,StringPtr sName,short *pRow)
{
short i;
ListHandle hList = pView->hList;
CellRec *pCellData = nil;
Boolean found = false;
// StringSame may move memory for international languages, so 'pCellData'
// might be pointing to nowheresville when we return. We'll need to make
// sure the cells (and userhandle) are locked.
SignedByte cellsState = LockCells(pView);
SignedByte userHandleState = LockUserHandle(pView);
for(i=0;i<(*hList)->dataBounds.bottom && !found;i++)
{
pCellData = FindCellData(pView,i);
if (id==pCellData->nodeID && StringSame(sName,pCellData->name))
{
*pRow = i+1;
found = true;
}
}
RestoreCells(pView,cellsState);
RestoreUserHandle(pView,userHandleState);
return (found ? pCellData : nil);
}
/**********************************************************************
* FindByNodeID - find an item by nodeID only, row is 1-based
**********************************************************************/
static CellRec *FindByNodeID(ViewListPtr pView,VLNodeID id,short *pRow)
{
short i;
ListHandle hList = pView->hList;
for(i=0;i<(*hList)->dataBounds.bottom;i++)
{
CellRec *pCellData;
pCellData = FindCellData(pView,i);
if (id==pCellData->nodeID)
{
*pRow = i+1;
return pCellData;
}
}
return nil;
}
/**********************************************************************
* LVRename - allow renaming of an item
**********************************************************************/
void LVRename(ViewListPtr pView,VLNodeID id,StringPtr sName,Boolean openAfterRename,Boolean hahaOnlyKidding)
{
CellRec *pCellData,cellData;
short row;
SignedByte cellsState = LockCells(pView);
SignedByte userHandleState = LockUserHandle(pView);
if (pCellData = FindByName(pView,id,sName,&row))
{
Rect rCell,rTriangle,rIcon,rName;
Point ptName;
SelectOneItem(pView,row-1);
LAutoScroll(pView->hList);
Cell1Rect(pView->hList,row,&rCell);
if (rCell.bottom > pView->bounds.bottom)
{
LScroll(0,1,pView->hList);
Cell1Rect(pView->hList,row,&rCell);
}
if (!hahaOnlyKidding) {
cellData = *pCellData; // make a copy that can't move
GetCellRects(pView,&cellData,&rCell,&rTriangle,&rIcon,&rName,&ptName);
InitRename(pView,&rName,sName,row);
}
gOpenAfterRename = openAfterRename;
}
RestoreCells(pView,cellsState);
RestoreUserHandle(pView,userHandleState);
}
/**********************************************************************
* LVSelect - select a list view item
**********************************************************************/
Boolean LVSelect(ViewListPtr pView,VLNodeID id,StringPtr sName,Boolean addToSelection)
{
short row;
Boolean result = false;
if (FindByName(pView,id,sName,&row))
{
if (addToSelection)
SelectItem(pView,row,true,true);
else
result = SelectOneItem(pView,row-1);
UpdateSelection(pView);
}
return result; // Indicate if we selected anything
}
/**********************************************************************
* LVUnselect - unselect a list view item (should be combined with LVSelect,
* but this is less code impact FTTB.
**********************************************************************/
Boolean LVUnselect(ViewListPtr pView,VLNodeID id,StringPtr sName,Boolean removeFromSelection)
{
short row;
Boolean result = false;
if (FindByName(pView,id,sName,&row))
{
if (removeFromSelection)
SelectItem(pView,row,false,true);
else
result = SelectOneItem(pView,-1);
UpdateSelection(pView);
}
return result; // Indicate if we selected anything
}
/**********************************************************************
* LVExpand - expand/collapse a list view item
**********************************************************************/
void LVExpand(ViewListPtr pView,VLNodeID id,StringPtr sName,Boolean expand)
{
short row;
CellRec *pCellData;
if (pCellData = FindByName(pView,id,sName,&row))
{
DataHandle cellsHandle = (*pView->hList)->cells;
Handle userHandle = (*pView->hList)->userHandle;
DEC_STATE(cellsHandle)
DEC_STATE(userHandle)
L_STATE(cellsHandle);
if (pView->flags & kfSupportsMondoBigList)
L_STATE(userHandle);
SetCollapseStatus(pView,pCellData,row,!expand);
U_STATE(cellsHandle);
if (pView->flags & kfSupportsMondoBigList)
U_STATE(userHandle);
if (expand)
{
// DontDraw(pView);
CheckFillList(pView);
// DoDraw(pView);
}
}
}
/**********************************************************************
* LVUpdateStyle - update style for an item
**********************************************************************/
void LVUpdateStyle(ViewListPtr pView,VLNodeID id,StringPtr sName,Style theStyle,Boolean fDraw)
{
short row;
ListHandle hList = pView->hList;
CellRec *pCellData;
if (pCellData = FindByName(pView,id,sName,&row))
{
// Found it. Don't redraw unless it's different
if (pCellData->misc.style != theStyle)
{
Cell c;
pCellData->misc.style = theStyle;
c.h=0; c.v=row-1;
if (fDraw)
{
GrafPtr savePort;
GetPort(&savePort);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
LDraw(c, hList);
SetPort(savePort);
}
}
}
}
/**********************************************************************
* LVCountSelection - return number of items selected
**********************************************************************/
short LVCountSelection(ViewListPtr pView)
{
return pView->selectCount;
}
/**********************************************************************
* GetListViewSelection - return the cell data for the indicated item
* item is 1-based from the list or from the selection
**********************************************************************/
Boolean LVGetItem(ViewListPtr pView,short item,VLNodeInfo *pInfo,Boolean fFromSelection)
{
Boolean fResult;
CellRec *pCellData;
short row;
fResult = false;
if (fFromSelection)
{
// Get a selected item
if (item > 0 && item <= pView->selectCount && pView->hSelectList)
row = (*pView->hSelectList)[item-1].row;
else
// Invalid item
return false;
}
else
{
// Get the specified item
if (item > 0 && item <= (*pView->hList)->dataBounds.bottom)
row = item-1;
else
// Invalid item
return false;
}
if (pCellData = FindCellData(pView,row))
{
CopyCellInfo(pInfo,pCellData,(*pView->hSelectList)[item-1].row+1);
return true;
}
return false;
}
Boolean LVGetItemWithNodeID(ViewListPtr pView,VLNodeID nodeID,VLNodeInfo *pInfo)
{
CellRec *pCellData;
short row;
if (pCellData = FindByNodeID (pView, nodeID, &row)) {
CopyCellInfo (pInfo, pCellData,row);
return true;
}
return (false);
}
Boolean LVSetItemWithNodeID(ViewListPtr pView,VLNodeID nodeID,VLNodeInfo *pInfo, Boolean update)
{
CellRec *pCellData;
Rect cellRect;
short row;
if (pCellData = FindByNodeID (pView, nodeID, &row)) {
CopyNodeInfo (pCellData, pInfo);
if (update)
InvalWindowRect(GetMyWindowWindowPtr(pView->wPtr),Cell1Rect (pView->hList, row, &cellRect));
return true;
}
return (false);
}
#if AUTOFRONT
/************************************************************************
* RestoreToLayer - restore the window to its proper layer if it was brought
* to the front.
************************************************************************/
static void RestoreToLayer(ViewListPtr pView, Boolean fUpdate)
{
WindowPtr pViewWPtrWP = GetMyWindowWindowPtr(pView->wPtr);
if (gwSendBehind)
{
// Send the window back where it came from
// Make sure the window we need to go behind is still in the window list. It
// make have closed by now.
WindowPtr theWindow;
for (theWindow = FrontWindow (); theWindow; theWindow = GetNextWindow (theWindow))
{
if (gwSendBehind == theWindow)
{
SendBehind(pViewWPtrWP,gwSendBehind);
if (fUpdate)
{
EventRecord theEvent;
while (CheckUpdate(&theEvent) && theEvent.what == updateEvt)
{
UpdateMyWindow((WindowPtr)theEvent.message); // Have to update manually since no events are processed
}
}
break;
}
}
}
}
#endif
/************************************************************************
* LVDrag - called by drag tracking and drag drop handler
************************************************************************/
OSErr LVDrag(ViewListPtr pView,DragTrackingMessage which,DragReference drag)
{
short rowNum;
Point pt,pinnedPt;
OSErr result;
static short lastRow,lastExpanded;
static long lastTicks,LastScrollTicks;
static short lastScroll;
#if AUTOFRONT
static long enterTicks;
#endif
short expandingRow,scroll;
Boolean fTimesUp;
ListHandle hList;
short dragExpandTicks = GetRLong(DRAG_EXPAND_TICKS);
result = noErr;
hList = pView->hList;
switch (which)
{
case kDragTrackingEnterWindow:
if (pView->pte) FinishRename(pView,true);
pView->dragHiliteRow = 0;
lastScroll = 0;
gWaitExpandRow = 0;
// SelectOneItem(pView,-1); // Remove any selections
if (!gDontDragHilite)
{
ShowListDragHilite(pView, drag);
}
#if AUTOFRONT
enterTicks = TickCount();
gwSendBehind = nil;
#endif
if (!gDragFromMe)
SelectOneItem(pView,-1); // Get rid of any selection if dragging from outside
break;
case kDragTrackingLeaveWindow:
DragHilite(pView,pView->dragHiliteRow,false);
pView->dragHiliteRow = 0;
SetWaitExpandRow(pView,0);
if (!gDontDragHilite)
HideDragHilite(drag);
CollapseDragExpanded(pView,0,nil);
gDontDragHilite = false; // Make sure we hilite next time coming into this window
#if AUTOFRONT
RestoreToLayer(pView, true);
#endif
break;
case kDragTrackingInWindow:
{
Point ptLocal;
Rect rContent;
GetDragMouse(drag, &pt, &pinnedPt);
GlobalToLocal(&pt);
do
{
rowNum = InWhichCell(pt,hList);
if (lastRow != rowNum)
{
lastTicks = TickCount();
lastRow = rowNum;
}
// Check for drag collapse
if (rowNum)
CollapseDragExpanded(pView,rowNum,drag);
if (rowNum)
{
CellRec *pCellData = nil;
SignedByte cellsState = LockCells(pView);
SignedByte userHandleState = LockUserHandle(pView);
Boolean canDragExpand,
fParent;
// Don't hilite unless over icon, name or triangle
if (pCellData = FindCellData(pView,rowNum-1))
{
Rect rCell,rTriangle,rIcon,rName;
Point ptName;
Cell1Rect(hList,rowNum,&rCell);
GetCellRects(pView,pCellData,&rCell,&rTriangle,&rIcon,&rName,&ptName);
if (!(PtInRect(pt,&rTriangle) || PtInRect(pt,&rIcon) || PtInRect(pt,&rName)))
rowNum = 0;
}
// Can't drag onto something that's already selected if we're dragging from ourself
if (rowNum && gDragFromMe && Cell1Selected(rowNum,hList))
rowNum = 0;
expandingRow = gWaitExpandRow;
// If it's a folder, see if we can do drag expand
fParent = pCellData->misc.fParent;
canDragExpand = rowNum && fParent && pCellData->misc.fCollapsed && !IsDragExpanded(rowNum) && QueryItem(pView,kQueryDragExpand,rowNum);
if (canDragExpand)
{
if (TickCount() < lastTicks+dragExpandTicks) // Short delay before expanding
{
// Don't expand yet. Rotate the triangle 45 degrees.
expandingRow = rowNum;
GetMouse(&pt);
}
else
{
// Time to expand it
if (!ghDrawExpandedRows)
ghDrawExpandedRows = NuHandle(0);
if (ghDrawExpandedRows)
{
HideDragHilite(drag);
SetCollapseStatus(pView,pCellData,rowNum,false);
PtrAndHand(&rowNum,ghDrawExpandedRows,sizeof(rowNum));
// The userhandle MUST be unlocked prior to calling CheckFillList during a drag
// expand because we'll be adding cells
if (pView->flags & kfSupportsMondoBigList)
RestoreUserHandle (pView, userHandleState);
CheckFillList(pView);
ShowListDragHilite(pView, drag);
}
// rowNum = 0;
pView->dragHiliteRow = 0;
expandingRow = 0;
}
}
RestoreCells(pView,cellsState);
RestoreUserHandle(pView,userHandleState);
if (expandingRow && expandingRow != rowNum)
expandingRow = 0;
SetWaitExpandRow(pView,expandingRow);
// See if we can drag onto this
if (rowNum && !QueryItem(pView,kQueryDrop,rowNum))
{
// Can't drop on this. Can we drop on the parent?
if (!fParent && QueryItem(pView,kQueryDropParent,rowNum))
{
// Drop on the parent
rowNum = GetParent(pView, rowNum);
}
else
// Can't drop on this one
rowNum = 0;
}
}
if (pView->dragHiliteRow != rowNum)
{
DragHilite(pView,pView->dragHiliteRow,false);
DragHilite(pView,rowNum,true);
pView->dragHiliteRow = rowNum;
}
// See if we need to auto scroll
fTimesUp = TickCount() - LastScrollTicks >= 12;
scroll = 0;
if (((pt.v < pView->bounds.top && fTimesUp) || pt.v < pView->bounds.top-16) &&
(*hList)->visible.top) // Scroll only if we're not already at the top
scroll = -1;
else if (((pt.v > pView->bounds.bottom && fTimesUp) || pt.v > pView->bounds.bottom+16) &&
(*hList)->visible.bottom < (*hList)->dataBounds.bottom) // Scroll only if we're not already at the bottom
scroll = 1;
if (scroll)
{
if (lastScroll && lastScroll != scroll)
{
// There appears to be a bug in DragPostScroll if you
// switch directions on the scroll. Hiding and showing
// the drag hilite clears up the problem
HideDragHilite(drag);
LScroll(0,scroll,hList);
ShowListDragHilite(pView, drag);
}
else
{
DragPreScroll(drag, 0, -RowHt*scroll);
LScroll(0,scroll,hList);
DragPostScroll(drag);
}
LastScrollTicks = TickCount();
lastScroll = scroll;
}
// The dragTrackingInWindow message is sent only while the mouse is moving.
// To support auto scrolling, drag expand, we need to keep looping here as long as the mouse
// is inside the window and not in the list
GetMouse(&ptLocal);
GetWindowBounds(GetMyWindowWindowPtr(pView->wPtr),kWindowContentRgn,&rContent);
} while (EqualPt(ptLocal,pt) &&
StillDown());
#if AUTOFRONT
// Check if we need to bring the mailboxes window to the front
if (!gwSendBehind && (TickCount() - enterTicks >= 60) && RunType==Debugging)
{
WindowPtr pViewWPtrWP = GetMyWindowWindowPtr (pView->wPtr);
gwSendBehind = nil;
if ((FrontWindow()) != pViewWPtrWP)
{
// Find the window in front of us
for (gwSendBehind = FrontWindow (); gwSendBehind; gwSendBehind = GetNextWindow (gwSendBehind))
{
if (GetNextWindow(gwSendBehind) == pViewWPtrWP)
{
HideDragHilite(drag);
BringToFront(pViewWPtrWP);
UpdateMyWindow(pViewWPtrWP); // Have to update manually since no events are processed
ShowListDragHilite(pView, drag);
break;
}
}
}
enterTicks = positiveInfinity;
}
#endif
}
break;
case 0xfff:
// Drop
#if AUTOFRONT
RestoreToLayer(pView, false);
#endif
if (pView->dragHiliteRow || (pView->flags & kfDropRoot))
{
CellRec *pCellData;
VLNodeInfo info;
if (pView->dragHiliteRow)
{
if (pCellData = FindCellData(pView,pView->dragHiliteRow-1))
CopyCellInfo(&info,pCellData,pView->dragHiliteRow);
else
goto DropFail;
}
else
{
// Drag to root level
Zero(info);
info.nodeID = pView->rootNodeID;
info.isParent = true;
}
if (gDragFromMe)
{
// Internal drag/drop
short modifiers,mouseDownModifiers,mouseUpModifiers;
GetDragModifiers(drag, &modifiers, &mouseDownModifiers, &mouseUpModifiers);
CallBack(pView,(modifiers|mouseDownModifiers|mouseUpModifiers)&optionKey ? kLVCopyItem : kLVMoveItem,(long)&info);
}
else
{
// Drop from external source
SendDragDataInfo dragInfo;
dragInfo.drag = drag;
dragInfo.info = &info;
CallBack(pView,kLVDragDrop,(long)&dragInfo);
}
DropFail:
if (pView->dragHiliteRow)
{
DragHilite(pView,pView->dragHiliteRow,false);
pView->dragHiliteRow = 0;
}
}
else
result = dragNotAcceptedErr;
CollapseDragExpanded(pView,0,nil);
SetWaitExpandRow(pView,0);
break;
}
return result;
}
/************************************************************************
* LVDrop - finish up drag and return item dropped on
************************************************************************/
Boolean LVDrop(ViewListPtr pView,VLNodeInfo *pInfo)
{
#if AUTOFRONT
RestoreToLayer(pView, false);
#endif
if (pView->dragHiliteRow)
{
CellRec *pCellData;
if (pCellData = FindCellData(pView,pView->dragHiliteRow-1))
{
CopyCellInfo(pInfo,pCellData,pView->dragHiliteRow);
}
DragHilite(pView,pView->dragHiliteRow,false);
pView->dragHiliteRow = 0;
return true;
}
return false;
}
/************************************************************************
* LVDescendant - see if "check" is a descendent of "in".
* Return 0 if not or generation if it is
************************************************************************/
short LVDescendant(ViewListPtr pView,VLNodeInfo *checkInfo,VLNodeInfo *inInfo)
{
CellRec *pInCellData;
short row;
if (pInCellData = FindByName(pView,inInfo->nodeID,inInfo->name,&row))
{
short level = pInCellData->misc.level;
ListHandle hList = pView->hList;
short i;
for(i=row;i<(*hList)->dataBounds.bottom;i++)
{
CellRec *pCellData;
if (pCellData = FindCellData(pView,i))
{
short descendLevel;
descendLevel = pCellData->misc.level - level;
if (descendLevel <= 0)
return 0; // Not a descendent
if (pCellData->nodeID == checkInfo->nodeID && StringSame(pCellData->name,checkInfo->name))
return descendLevel;
}
}
}
return 0;
}
/************************************************************************
* LVMaxSize - get the maximum size of the list
************************************************************************/
void LVMaxSize(ViewListPtr pView, short *pWd, short *pHt)
{
*pWd = pView->maxNameWidth + GROW_SIZE;
*pHt = (*pView->hList)->cellSize.v * (*pView->hList)->dataBounds.bottom;
}
/************************************************************************
* LVDragFlavor - return the drag flavor if we are dragging
************************************************************************/
OSType LVDragFlavor(void)
{
return gDragFlavor;
}
/**********************************************************************
* LVIsSelected - check if a row is in the list of selected items (row is 1-based)
**********************************************************************/
Boolean LVIsSelected(ViewListPtr pView,short row)
{
SelectedListPtr pSelectedList;
short i;
for(i=0,pSelectedList=*pView->hSelectList;i<pView->selectCount;i++,pSelectedList++)
if (pSelectedList->row == row - 1)
return (true);
return false;
}
/**********************************************************************
* LVSelectAll - select all items in list (at least all those that allow selection)
**********************************************************************/
Boolean LVSelectAll(ViewListPtr pView)
{
short row;
short bottom = (*pView->hList)->dataBounds.bottom;
if (pView->pte) return false; // select all while renaming selects all text in item
for(row=1;row<=bottom;row++)
SelectItem(pView,row,true,true);
UpdateSelection(pView);
return true;
}
/***************************************************************
*
* To add an LDEF do:
* - add "data 'LDEF'" in Common.r
* - add enum to LDEFEnum in MyRes.h
* - add doInitDEF and extern declaration in "ends.c"
*
***************************************************************/
/************************************************************************
* list definition proc
************************************************************************/
pascal void ListViewListDef(short message, Boolean select, Rect *pRect, Cell cell,
short dataOffset, short dataLen, ListHandle hList)
{
ViewListPtr pView;
CellRec cellData;
WindowPtr theWindow = GetWindowFromPort(GetQDGlobalsThePort());
switch (message)
{
case lDrawMsg:
case lHiliteMsg:
if (dataLen)
{
SAVE_STUFF;
SET_COLORS;
pView = (ViewListPtr) (*hList)->refCon;
if (pView->flags & kfSupportsMondoBigList) {
long userHandleOffset;
short len;
BlockMoveData (*(*hList)->cells + dataOffset, &userHandleOffset, dataLen);
len = offsetof (CellRec, name) + 1; // Includes the length byte of the name
BlockMoveData (*(*hList)->userHandle + userHandleOffset, &cellData, len);
BlockMoveData (*(*hList)->userHandle + userHandleOffset + len, &cellData.name[1], cellData.name[0]);
}
else
BlockMoveData(*(*hList)->cells + dataOffset,&cellData,dataLen);
EraseRect(pRect);
DrawItem((ViewListPtr)(*hList)->refCon,cell.v+1,pRect,&cellData,select,false);
REST_STUFF;
}
break;
}
}
/**********************************************************************
* PlotTheIcon - Plot a color icon
**********************************************************************/
static void PlotTheIcon(short iconID, Rect *pRect,Boolean hilite,IconAlignmentType align)
{
short realSettingsRef = CurResFile();
Boolean plotted = false;
typedef struct IconTypeMap { short id; OSType type; } IconTypeMap;
static IconTypeMap iconTable[] =
{
kGenericFolderIconResource,kGenericFolderIcon,
kTrashIconResource,kTrashIcon,
0,0
};
static Boolean checkedIconServices, haveIconServices;
IconTypeMap *pType;
SInt32 gestaltResult;
if (!checkedIconServices)
haveIconServices = !Gestalt(gestaltIconUtilitiesAttr,&gestaltResult) && (gestaltResult&gestaltIconUtilitiesHasIconServices) && GetIconRefFromFile;
if (iconID < 0 && haveIconServices)
{
for(pType=iconTable;pType->type;pType++)
{
if (pType->id == iconID)
{
IconRef iconRef;
if (!GetIconRef(kOnSystemDisk,kSystemIconsCreator,pType->type,&iconRef))
{
PlotIconRef(pRect,align,hilite ? ttSelected : ttNone,kIconServicesNormalUsageFlag,iconRef);
ReleaseIconRef(iconRef);
plotted = true;
}
break;
}
}
}
if (!plotted)
{
UseResFile(GetMainGlobalSettingsRefN());
PlotIconID(pRect,align,hilite ? ttSelected : ttNone,iconID);
UseResFile(realSettingsRef);
}
}
/**********************************************************************
* CallBack - Send a message back to the caller
**********************************************************************/
static long CallBack(ViewListPtr pView,short message, long data)
{
return (*pView->pCallBack)(pView,message,data);
}
/**********************************************************************
* DrawRowCallback - Draw a single row via the callback. Returns true
* if no more drawing is required.
**********************************************************************/
static Boolean DrawRowCallback(ViewListPtr pView,short item,Rect *pRect,CellRec *pCellData,Boolean select,Boolean eraseName)
{
return (*pView->details.DrawRowCallback)(pView,item,pRect,pCellData,select,eraseName);
}
/**********************************************************************
* FillBlankCallback - Draw a single row via the callback. Returns true
* if no more drawing is required.
**********************************************************************/
static Boolean FillBlankCallback(ViewListPtr pView)
{
return (*pView->details.FillBlankCallback)(pView);
}
/**********************************************************************
* GetCellRectsCallBack - Draw a single row via the callback. Returns true
* if no more drawing is required.
**********************************************************************/
static Boolean GetCellRectsCallBack(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Rect *iconRect, Rect *nameRect)
{
return (*pView->details.GetCellRectsCallBack)(pView, cellData, cellRect, iconRect, nameRect);
}
/**********************************************************************
* InterestingClickCallback - see if a point is interesting
**********************************************************************/
static Boolean InterestingClickCallback(ViewListPtr pView, CellRec *cellData, Rect *cellRect, Point pt)
{
return (*pView->details.InterestingClickCallback)(pView, cellData, cellRect, pt);
}
/**********************************************************************
* DrawItem - Draw a list item, item is 1-based
**********************************************************************/
static void DrawItem(ViewListPtr pView,short item,Rect *pRect,CellRec *pCellData,Boolean select,Boolean eraseName)
{
Rect rTriangle,rIcon,rName;
Point ptText;
Boolean hasColor = IsColorWin(GetMyWindowWindowPtr(pView->wPtr));
Boolean didCallback;
if (item && item <= (*pView->hList)->dataBounds.bottom)
{
SAVE_STUFF;
SET_COLORS;
GetCellRects(pView,pCellData,pRect,&rTriangle,&rIcon,&rName,&ptText);
// Do the drawing via the callback, if one is provided.
didCallback = *pView->details.DrawRowCallback && DrawRowCallback(pView,item,pRect,pCellData,select,eraseName);
// Draw triangle even for callback drawing
if (pCellData->misc.fParent)
{
// Parent, draw triangle (expanded, collapsed, or in transition
EraseRect(&rTriangle);
PlotTheIcon(item==gWaitExpandRow ? TRIANGLE_TRANSITION_ICON : (pCellData->misc.fCollapsed ? COLLAPSED_ICON : EXPANDED_ICON),&rTriangle,false,atNone);
}
// Do the drawing via the callback, if one is provided.
if (!didCallback)
{
// Plot the icon, if any
if (pCellData->iconID)
{
OffsetRect(&rIcon,0,-1);
PlotTheIcon(pCellData->iconID,&rIcon,select,atNone + atHorizontalCenter);
}
// Draw the name (if not renaming)
if (pView->renameRow != item)
{
MoveTo(ptText.h,ptText.v);
InsetRect(&rName,-NameAddMargin,0);
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);
}
}
REST_STUFF;
}
}
/**********************************************************************
* GetCellData - Get data for cell,item is 0-based
**********************************************************************/
static short GetCellData(ViewListPtr pView,short item,CellRec *pCellData)
{
Size len;
long offset;
short dataLen;
Cell c;
c.h = 0; c.v = item;
if (pView->flags & kfSupportsMondoBigList) {
dataLen = sizeof (offset);
LGetCell (&offset, &dataLen, c, pView->hList);
if (dataLen) {
len = offsetof (CellRec, name) + 1; // Includes the length byte of the name
BlockMoveData (*(*pView->hList)->userHandle + offset, pCellData, len);
BlockMoveData (*(*pView->hList)->userHandle + offset + len, &pCellData->name[1], pCellData->name[0]);
dataLen = len + pCellData->name[0];
}
}
else {
dataLen = sizeof(CellRec);
LGetCell((Ptr)pCellData, &dataLen, c, pView->hList);
}
return dataLen;
}
/**********************************************************************
* FindCellData - Find data for cell, item is zero-based
**********************************************************************/
static CellRec *FindCellData(ViewListPtr pView,short item)
{
Cell c;
long cellOffset;
short offset,len;
c.h = 0; c.v = item;
if (pView->flags & kfSupportsMondoBigList) {
if (item >= (*pView->hList)->dataBounds.bottom || item < 0)
return (nil);
len = sizeof (cellOffset);
LGetCell (&cellOffset, &len, c, pView->hList);
if (len>0)
return (CellRec *)(*(*pView->hList)->userHandle + cellOffset);
else
return nil;
}
else {
LGetCellDataLocation(&offset, &len, c, pView->hList);
if (len>0)
return (CellRec *)(*(*pView->hList)->cells + offset);
else
return nil;
}
}
/**********************************************************************
* SetCellData - Set data for cell, item is 0-based
**********************************************************************/
static OSErr SetCellData(ViewListPtr pView,short item,CellRec *pCellData)
{
CellRec existingCell;
Cell c;
long offset;
short dataLen;
short len;
OSErr theError;
c.h = 0; c.v = item;
theError = noErr;
if (pView->flags & kfSupportsMondoBigList) {
len = offsetof(CellRec,name)+pCellData->name[0]+1;
// Is the item within the bounds of the list?
if (item < (*pView->hList)->dataBounds.bottom) {
dataLen = sizeof (offset);
LGetCell (&offset, &dataLen, c, pView->hList);
// If the cell containing the offset into the userHandle is zero,
// append the cell data onto the end of the big list and set the
// list cell to point to this new record.
if (!dataLen) {
offset = GetHandleSize ((*pView->hList)->userHandle);
theError = PtrPlusHand (pCellData, (*pView->hList)->userHandle, len);
if (!theError)
LSetCell (&offset, sizeof (offset), c, pView->hList);
}
// For existing items, we'll have to size the existing record to the new
// cell data, then adjust any indices that have moved
else {
dataLen = GetCellData (pView, item, &existingCell);
Munger ((*pView->hList)->userHandle, offset, nil, dataLen, pCellData, len);
theError = MemError ();
if (!theError && len != dataLen)
AdjustSubsequentCells (pView->hList, offset, len - dataLen);
}
}
}
else {
len = (offsetof(CellRec,name)+pCellData->name[0]+1+1)&-2;
LSetCell((Ptr)pCellData,len,c,pView->hList);
}
return (theError);
}
/**********************************************************************
* AdjustSubsequentCells - When using really big lists we have to recalculate
* offsets stored in the list's 'cells' array
* whenever we remove cells or change the size of
* any one cell. Since the data is stored out of
* order we have to pass through the entire list
* adjusting only those items that originally fell
* after the cell offset that was changed.
**********************************************************************/
static void AdjustSubsequentCells (ListHandle hList, long followingThisOffset, long delta)
{
Cell c;
long dataOffset;
short dataLen;
c.h = 0;
dataLen = sizeof (dataOffset);
for (c.v = 0; c.v < (*hList)->dataBounds.bottom; ++c.v) {
LGetCell (&dataOffset, &dataLen, c, hList);
if (dataOffset > followingThisOffset) {
dataOffset += delta;
LSetCell (&dataOffset, sizeof (dataOffset), c, hList);
}
}
}
/**********************************************************************
* LVUpdateSelection - update the selection data
**********************************************************************/
void LVUpdateSelection(ViewListPtr pView)
{
UpdateSelection(pView);
}
/**********************************************************************
* UpdateSelection - update the selection data
**********************************************************************/
static void UpdateSelection(ViewListPtr pView)
{
SelectedListHandle hSelectList,
oldList;
CellRec cellData;
short count = 0,
oldCount;
if (pView->flags & kfSupportsSelectCallbacks) {
oldCount = pView->selectCount;
oldList = pView->hSelectList;
MyHandToHand (&oldList);
}
if (hSelectList = pView->hSelectList)
{
Point c;
c.h = 0;
c.v = 0;
while(LGetSelect(True,&c,pView->hList))
{
if (count >= pView->selectCount)
{
// Need to expand handle size
SetHandleSize((Handle)hSelectList,(count+1)*sizeof(SelectedListRec));
if (MemError())
return;
}
GetCellData (pView, c.v, &cellData);
(*hSelectList)[count].nodeID = cellData.nodeID;
(*hSelectList)[count++].row = c.v++;
}
}
pView->selectCount = count;
SetHandleSize((Handle)hSelectList,(count)*sizeof(SelectedListRec));
// Compare the old list to the new list, things missing from the new list will be send back
// to the caller in an unselect callback, while things missing from the old list will be sent
// to the caller in a select callback.
if (pView->flags & kfSupportsSelectCallbacks) {
SelectedListPtr pItem,
oldItem;
short i,
j;
// First, scan the old list for items no longer selected in the new list
LDRef (oldList);
LDRef (hSelectList);
for (i = 0, oldItem = *oldList; i < oldCount; i++) {
for (j = 0, pItem = *hSelectList; j < count && (pItem->nodeID != oldItem->nodeID || pItem->row != oldItem->row); j++)
++pItem;
if (j == count)
CallBack (pView, kLVUnselectItem, (long) oldItem->nodeID);
++oldItem;
}
// Next, scan the new list for items not in the old list
for (i = 0, pItem = *hSelectList; i < count; i++) {
for (j = 0, oldItem = *oldList; j < oldCount && (pItem->nodeID != oldItem->nodeID || pItem->row != oldItem->row); j++)
++oldItem;
if (j == oldCount)
CallBack (pView, kLVSelectItem, (long) pItem->nodeID);
++pItem;
}
UL (hSelectList);
ZapHandle (oldList);
}
}
/**********************************************************************
* CheckFillList - see if we need to add more sub entries to the list
**********************************************************************/
static void CheckFillList(ViewListPtr pView)
{
short item;
ListHandle hList;
Boolean fUpdate,fCalcNameWidth;
Rect rUpdate;
// Make sure we have the data on all the cells currently being displayed
hList = pView->hList;
fUpdate = false;
pView->maxLevel = 0;
// If we've got a lot of names, don't check their width. Just use MaxNameWidth
fCalcNameWidth = (*hList)->dataBounds.bottom < 250;
pView->maxNameWidth = 0;
for (item = (*hList)->dataBounds.top;item < (*hList)->dataBounds.bottom; item++)
{
CellRec cellData;
if (GetCellData(pView,item,&cellData))
{
if (cellData.misc.fParent &&
!cellData.misc.fCollapsed &&
!cellData.misc.fHaveChildren)
{
// Add children for this parent
VLNodeID nodeID;
VLNodeInfo nodeInfo;
Boolean saveAutoSelect;
CopyCellInfo(&nodeInfo,&cellData,item+1);
nodeID = CallBack(pView,kLVGetParentID,(long)&nodeInfo);
gAddCell = item+1;
gAddLevel = cellData.misc.level+1;
if (gAddLevel > pView->maxLevel)
pView->maxLevel = gAddLevel;
saveAutoSelect = gAutoSelect;
gAutoSelect = pView->flags&kfAutoSelectChild && LVIsSelected(pView,item+1);
CallBack(pView,kLVAddNodeItems,nodeID);
gAutoSelect = saveAutoSelect;
cellData.misc.fHaveChildren = true;
SetCellData(pView,item,&cellData); // Save updated info
if (!fUpdate && CountChildren(pView,item))
{
// Update from this row on down
Cell1Rect(hList,item+1,&rUpdate);
rUpdate.bottom = pView->bounds.bottom;
fUpdate = true;
}
}
if (cellData.misc.level > pView->maxLevel)
pView->maxLevel = cellData.misc.level;
if (fCalcNameWidth)
{
// Check max name width
short wd;
wd = IconLeft + (pView->maxLevel+1)*IconLevelWidth + StringWidth(cellData.name) + 3;
if (wd > pView->maxNameWidth)
pView->maxNameWidth = wd;
}
}
}
if (!fCalcNameWidth)
pView->maxNameWidth = IconLeft + (pView->maxLevel+1)*IconLevelWidth + MaxNameWidth + 3;
if (fUpdate)
{
UpdateSelection(pView);
DrawList(pView,&rUpdate);
}
}
/**********************************************************************
* GetCellRects - get triangle, icon, and name rects
**********************************************************************/
static void GetCellRects(ViewListPtr pView,CellRec *pCellData,Rect *pRect,Rect *pTriangle,Rect *pIcon,Rect *pName,Point *pText)
{
short offset;
Point pt;
FontInfo fInfo;
SAVE_STUFF;
// Triangle
if (pCellData->misc.fParent && pCellData->misc.level)
SetRect(pTriangle,pRect->left+ArrowLeft,pRect->top+IconTop,pRect->left+ArrowLeft+16,pRect->top+IconTop+16);
else
SetRect(pTriangle,0,0,0,0);
// Icon
offset = IconLeft + pCellData->misc.level*IconLevelWidth;
if (pCellData->iconID)
{
SetRect(pIcon,pRect->left+offset,pRect->top+IconTop,pRect->left+offset+16,pRect->top+IconTop+16);
offset += IconLevelWidth;
}
else
SetRect(pIcon,0,0,0,0);
// Name
TextFont(pView->font);
TextSize(pView->fontSize);
TextFace(pCellData->misc.style);
pt.h = pRect->left + offset;
pt.v = pRect->top+(*pView->hList)->cellSize.v-TextBottom;
*pText = pt;
GetFontInfo(&fInfo);
SetRect(pName,pt.h,pt.v-fInfo.ascent-fInfo.leading,pt.h + StringWidth(pCellData->name) /* + 2*CharWidth(' ') */,pt.v+fInfo.descent);
REST_STUFF;
}
/**********************************************************************
* SelectOneItem - Select a single cell, row is 0-based
**********************************************************************/
static Boolean SelectOneItem(ViewListPtr pView,short row)
{
if (!QueryItem(pView,kQuerySelect,row+1))
row = -1; // Can't select this one
SelectSingleCell(row,pView->hList);
CheckAutoSelect(pView,row);
UpdateSelection(pView);
return row != -1; // return false if nothing selected
}
/**********************************************************************
* PlotTriangle - plot a small B&W icon
**********************************************************************/
static void PlotTriangle(Rect *pRect,short index)
{
Handle hSICN;
EraseRect(pRect);
if (hSICN = GetResource('SICN',TRIANGLE_SICN))
PlotSICN(pRect, (SICNHand)hSICN,index);
}
/**********************************************************************
* TrackTriangle - track mouse over triangle
**********************************************************************/
static Boolean TrackTriangle(Rect *prTriangle,Boolean collapsed)
{
Boolean fInside=true;
Boolean fLastInside=false;
while(WaitMouseUp())
{
Point pt;
GetMouse(&pt);
fInside = PtInRect(pt,prTriangle);
if (fInside != fLastInside)
{
PlotTriangle(prTriangle,fInside ? (collapsed ? kRightSICN : kDownSICN) :
(collapsed ? kRightOutlineSICN : kDownOutlineSICN));
fLastInside = fInside;
}
}
if (fInside)
{
// Do intermediate triangles
PlotTriangle(prTriangle,kDiagonalSICN);
DelayTicks(2);
PlotTriangle(prTriangle,collapsed ? kDownSICN : kRightSICN);
DelayTicks(2);
}
// Plot the final color icon
EraseRect(prTriangle);
PlotTheIcon(fInside ? (collapsed ? EXPANDED_ICON : COLLAPSED_ICON) :
(collapsed ? COLLAPSED_ICON : EXPANDED_ICON),prTriangle,false,atNone);
return fInside;
}
/**********************************************************************
* DelayTicks - delay a few ticks
**********************************************************************/
static void DelayTicks(long tickCount)
{
long ticks;
ticks = TickCount()+tickCount;
while (TickCount() <= tickCount);
}
/**********************************************************************
* SetCollapseStatus - expand or collapse a folder, rownum is 1-based
**********************************************************************/
static void SetCollapseStatus(ViewListPtr pView,CellRec *pCellData,short rowNum,Boolean fCollapse)
{
Boolean fUpdateOnDown=false;
VLNodeInfo info;
if (pCellData->misc.fCollapsed = fCollapse)
{
// Collapsing
short numChildren;
if (numChildren = CountChildren(pView,rowNum-1))
{
// DontDraw(pView);
// We'll need to remove these records from our private userhandle
if (pView->flags & kfSupportsMondoBigList) {
CellRec existingCell;
Cell c;
long firstOffset,
lastOffset,
bytesToRemove;
short dataLen;
char dummy;
c.h = 0;
c.v = rowNum;
dataLen = sizeof (firstOffset);
LGetCell (&firstOffset, &dataLen, c, pView->hList);
c.v = rowNum + numChildren - 1;
LGetCell (&lastOffset, &dataLen, c, pView->hList);
// We need to remove all the data between the first and last offset, plus the length of the last item
if (dataLen) {
bytesToRemove = lastOffset - firstOffset + GetCellData (pView, c.v, &existingCell);
Munger ((*pView->hList)->userHandle, firstOffset, nil, bytesToRemove, &dummy, 0);
AdjustSubsequentCells (pView->hList, lastOffset, -bytesToRemove);
}
}
LDelRow(numChildren, rowNum, pView->hList);
// DoDraw(pView);
fUpdateOnDown=true;
}
pCellData->misc.fHaveChildren = false;
}
// DontDraw(pView);
SetCellData(pView,rowNum-1,pCellData);
// Need to refresh the parent ourselves when using big lists since we bypass the List Manager
if (pCellData->misc.fCollapsed && (pView->flags & kfSupportsMondoBigList))
DrawRow (pView, rowNum, LVIsSelected (pView, rowNum));
// DoDraw(pView);
// Update from this row on down
#if 0
if (fCollapse)
{
Cell1Rect(pView->hList,rowNum,&rUpdate);
if (fUpdateOnDown)
{
rUpdate.bottom = pView->bounds.bottom;
UpdateSelection(pView);
}
DrawList(pView,&rUpdate);
}
#endif
UpdateSelection(pView);
// Notify caller
CopyCellInfo(&info,pCellData,rowNum);
CallBack(pView,kLVExpandCollapseItem,(long)&info);
}
/**********************************************************************
* ListViewSend - provide promised drag flavor
**********************************************************************/
static pascal OSErr ListViewSend(FlavorType flavor, void *dragSendRefCon, ItemReference item, DragReference drag)
{
OSErr err=cantGetFlavorErr;
ViewListPtr pView = (ViewListPtr)dragSendRefCon;
SendDragDataInfo sendData;
if (flavor==pView->dragFlavor || pView->dragFlavor == kLVDoOwnDragAdd)
{
VLNodeInfo info;
CellRec *pCellData;
short row;
sendData.drag = drag;
sendData.itemRef = item;
sendData.flavor = flavor;
sendData.info = &info;
row = (*pView->hSelectList)[item-1].row;
if (pCellData = FindCellData(pView,row))
{
CopyCellInfo(&info,pCellData,row+1);
if (!CallBack(pView,kLVSendDragData,(long)&sendData))
err = noErr;
}
}
return(err);
}
/**********************************************************************
* ListDrag - check for drag and then handle it if it is a drag
**********************************************************************/
static Boolean ListDrag(ViewListPtr pView,EventRecord *event,Point pt)
{
ListHandle hList = pView->hList;
short s = InWhichCell(pt,hList);
DragReference drag;
RgnHandle rgn,hDrawRgn;
GWorldPtr gworld;
OSErr err=noErr;
long item;
DECLARE_UPP(ListViewSend,DragSendData);
pView->dragGroup = 0;
for(item=1;item<=pView->selectCount;item++)
{
// For every selected item, see if there is one we can't drag
if (!QueryItem(pView,kQueryDrag,(*pView->hSelectList)[item-1].row+1))
// Can't drag this item
return false;
}
if (!(err=MyNewDrag(pView->wPtr,&drag)))
{
if (!Cell1Selected(s,hList))
{
// This one's not selected. Select it.
SelectOneItem(pView,s-1);
}
if (!(err=FinderDragVoodoo(drag))) // bad finder. bad.
if (pView->dragFlavor)
{
CellRec *pCellData;
// Promise data for each item
for(item=1;item<=pView->selectCount;item++)
if ((pCellData = FindCellData(pView,(*pView->hSelectList)[item-1].row)) && (!pCellData->misc.fParent || pView->dragFlavor == kLVDoOwnDragAdd))
{
if (pView->dragFlavor == kLVDoOwnDragAdd)
{
// The caller wants to add the items itself
SendDragDataInfo dragInfo;
VLNodeInfo nodeInfo;
dragInfo.drag = drag;
dragInfo.itemRef = item;
CopyCellInfo(&nodeInfo,pCellData,(*pView->hSelectList)[item-1].row+1);
dragInfo.info = &nodeInfo;
CallBack(pView,kLVAddDragItem,(long)&dragInfo);
}
else
AddDragItemFlavor(drag, item, pView->dragFlavor, nil, 0L, flavorSenderOnly|flavorNotSaved);
}
INIT_UPP(ListViewSend,DragSendData);
SetDragSendProc(drag,ListViewSendUPP, pView);
}
// build the region
if (GetDragRgn(pView,&rgn,&gworld,&hDrawRgn,drag,s))
{
gDragFlavor = pView->dragFlavor;
gDragFromMe = gDontDragHilite = true;
MyTrackDrag(drag,event,rgn);
gDragFromMe = gDontDragHilite = false;
gDragFlavor = nil;
DisposeDragRgn(rgn,gworld,hDrawRgn);
if (DragTargetWasTrash(drag))
CallBack(pView,kLVDeleteItem,dataDeleteFromDrag);
gDidDrag = true;
}
MyDisposeDrag(drag);
return(True);
cleanup:
MyDisposeDrag(drag);
if (err) WarnUser(COULDNT_DRAG,err);
}
return(False);
}
/**********************************************************************
* CopyCellInfo - copy cell info into rec for caller
**********************************************************************/
static void CopyCellInfo(VLNodeInfo *pInfo,CellRec *pCellData,short rowNum)
{
pInfo->iconID = pCellData->iconID;
pInfo->nodeID = pCellData->nodeID;
pInfo->isParent = pCellData->misc.fParent;
pInfo->isCollapsed = pCellData->misc.fCollapsed;
pInfo->style = pCellData->misc.style;
pInfo->refCon = pCellData->refCon;
pInfo->rowNum = rowNum;
PCopy(pInfo->name,pCellData->name);
}
/**********************************************************************
* CopyNodeInfo - copy node info into a cell for caller
**********************************************************************/
static void CopyNodeInfo(CellRec *pCellData,VLNodeInfo *pInfo)
{
pCellData->iconID = pInfo->iconID;
pCellData->nodeID = pInfo->nodeID;
pCellData->misc.fParent = pInfo->isParent;
pCellData->misc.fCollapsed = pInfo->isCollapsed ;
pCellData->misc.style = pInfo->style;
pCellData->refCon = pInfo->refCon;
PCopy(pCellData->name,pInfo->name);
}
/**********************************************************************
* CountChildren - return the number of children this folder has
**********************************************************************/
static short CountChildren(ViewListPtr pView,short item)
{
CellRec *pCellData;
short children=0;
if ((pCellData = FindCellData(pView,item)) && pCellData->misc.fParent)
{
short level,i;
level = pCellData->misc.level;
for(i=item+1;pCellData = FindCellData(pView,i);i++)
{
if (level<pCellData->misc.level)
children++;
else
break;
}
}
return children;
}
/**********************************************************************
* InitRename - setup for renaming
**********************************************************************/
static void InitRename(ViewListPtr pView,Rect *pRect,UPtr name,short rowNum)
{
PETEHandle pte;
OSErr err;
PETEDocInitInfo pdi;
short saveRight;
GrafPtr savePort;
PETEDefaultFontEntry defaultFont;
GetPort(&savePort);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
pRect->left -= 4;
pRect->top += 1;
pRect->bottom += 1;
pRect->right += 4;
saveRight = pRect->right;
DefaultPII(pView->wPtr,false,0,&pdi);
pdi.inRect = *pRect;
pdi.docWidth = 4000;
//pdi.inParaInfo.startMargin = 2;
if (err=PeteCreate(pView->wPtr,&pte,peClearAllReturns|peNoStyledPaste,&pdi)) {WarnUser(PETE_ERR,err); return;}
CleanPII(&pdi);
Zero(defaultFont);
defaultFont.pdFont = SmallSysFontID();
defaultFont.pdSize = SmallSysFontSize();
err = PETESetDefaultFont(PETE,pte,&defaultFont);
ASSERT(!err);
pView->pte = pte;
SizePete(pView,pRect);
PeteSetString(name,pte);
PETEMarkDocDirty (PETE, pte, false);
PeteSelectAll(pView->wPtr,pte);
PeteFocus(pView->wPtr,pte,true);
// Erase any leftover if we resized
if (saveRight != pRect->right)
{
Rect rErase;
SAVE_STUFF;
SET_COLORS;
SetRect(&rErase, pRect->right, pRect->top-1, pView->bounds.right - GROW_SIZE, pRect->bottom);
EraseRect(&rErase);
REST_STUFF;
}
DrawPete(pView);
pView->renameRow = rowNum;
SetPort(savePort);
}
/**********************************************************************
* FinishRename - finish renaming
**********************************************************************/
static void FinishRename(ViewListPtr pView,Boolean fRename)
{
Str255 s;
Rect rCell;
GrafPtr savePort;
VLNodeInfo info;
VLNodeID id;
GetPort(&savePort);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
PeteStringLo(s,33,pView->pte); // To get a 31-char string, we need to pass 33
PeteDispose(pView->wPtr,pView->pte);
pView->pte = nil;
if (LVGetItem(pView,1,&info,true))
id = info.nodeID;
if (fRename)
{
// Go ahead and rename the item
CallBack(pView,kLVRenameItem,(long)s);
}
Cell1Rect(pView->hList,pView->renameRow,&rCell);
InvalWindowRect(GetMyWindowWindowPtr(pView->wPtr),&rCell);
pView->renameRow = 0;
if (fRename)
{
LVSelect(pView,id,s,false);
LAutoScroll(pView->hList);
if (gOpenAfterRename)
CallBack(pView,kLVOpenItem,0); // Open the renamed file
}
SetPort(savePort);
gOpenAfterRename = false;
LVChangeFocus (pView, kFocusList);
}
/**********************************************************************
* DontDraw - Don't draw list
**********************************************************************/
static void DontDraw(ViewListPtr pView)
{
if (0==pView->listStatus++)
LSetDrawingMode(false,pView->hList);
}
/**********************************************************************
* DoDraw - Do draw list
**********************************************************************/
static void DoDraw(ViewListPtr pView)
{
if (pView->listStatus==0 || --pView->listStatus==0)
LSetDrawingMode(true,pView->hList);
}
/**********************************************************************
* QueryItem - query the caller about a specific item (item is 1-based)
**********************************************************************/
static Boolean QueryItem(ViewListPtr pView,short query,short item)
{
VLNodeInfo info;
CellRec *pCellData;
long result = 0;
if (pCellData = FindCellData(pView,item-1))
{
CopyCellInfo(&info,pCellData,item);
info.query = query;
result = CallBack(pView,kLVQueryItem,(long)&info);
}
return result;
}
/**********************************************************************
* KeyNavigate - navigate through list by keys
**********************************************************************/
static void KeyNavigate(ViewListPtr pView,StringPtr sKeyNavigation)
{
ListHandle hList = pView->hList;
short i,row;
Str32 s;
Boolean fCloseMatch=false;
*s = 0;
row = 0;
for(i=0;i<(*hList)->dataBounds.bottom;i++)
{
CellRec *pCellData;
if (pCellData = FindCellData(pView,i))
{
short compare1,compare2;
unsigned char saveLen;
saveLen = *pCellData->name;
if (*sKeyNavigation < saveLen)
*pCellData->name = *sKeyNavigation; // Make the list name the same length as the search name
compare1 = RelString(pCellData->name,sKeyNavigation, false, true);
*pCellData->name = saveLen;
compare2 = RelString(pCellData->name, s, false, true);
if (compare1==0)
{
if (saveLen==*sKeyNavigation)
{
// Found exact match
row=i;
break;
}
else if (!fCloseMatch || compare2 < 0) // No match so far or closer one
{
fCloseMatch = true;
goto BestMatch;
}
}
else if (!fCloseMatch &&
compare1>0 &&
((*s==0) || compare2<0))
// Greater than or equal to the search item. If it's less than the one we have, use it
{
BestMatch:
PCopy(s,pCellData->name);
row = i;
}
}
}
// Only change selection if it's not the right one
if (pView->selectCount != 1 || (**pView->hSelectList).row!=row)
SelectOneItem(pView,row);
}
/**********************************************************************
* GreyRect - do EOR of rect
**********************************************************************/
static void GreyRect(Rect *pRect)
{
Pattern grayPat;
PenMode(patXor);
PenPat(GetQDGlobalsGray(&grayPat));
FrameRect(pRect);
PenPat(GetQDGlobalsBlack(&grayPat));
PenMode(patCopy);
}
/**********************************************************************
* DragSelect - drag selection box around items
**********************************************************************/
static void DragSelect(ViewListPtr pView,Point initPt)
{
Rect r,lastRect;
ListHandle hList;
pView->dragSelect = true;
SetRect(&lastRect,initPt.h,initPt.v,initPt.h,initPt.v);
r = pView->bounds;
// r.right -= GROW_SIZE; /* I don't know why this was here. ALB 8/4/00 */
ClipRect(&r);
GreyRect(&lastRect);
hList = pView->hList;
while (WaitMouseUp())
{
Point pt;
short scroll;
GetMouse(&pt);
if (pt.h < initPt.h)
{ r.left = pt.h; r.right = initPt.h; }
else
{ r.left = initPt.h; r.right = pt.h; }
if (pt.v < initPt.v)
{ r.top = pt.v; r.bottom = initPt.v; }
else
{ r.top = initPt.v; r.bottom = pt.v; }
if(!EqualRect(&r,&lastRect))
{
Boolean fChangeSelection;
short row;
CellRec *pCellData;
GreyRect(&lastRect);
GreyRect(&r);
// For every item, check to see if it is within the rect
fChangeSelection = false;
for (row=0;row<(*hList)->dataBounds.bottom;row++)
{
Rect rCell,rTemp;
Cell1Rect(hList,row+1,&rCell);
// This cell is interesting only if it is within the current rect or with the last one
if (SectRect(&rCell,&r,&rTemp) || SectRect(&rCell,&lastRect,&rTemp))
{
if (pCellData = FindCellData(pView,row))
{
Rect rTriangle,rIcon,rName;
Point ptName;
Boolean fSelect,fLastSelect;
// Figure out the name and icon rects to make the drag region
if (*pView->details.GetCellRectsCallBack) GetCellRectsCallBack(pView,pCellData,&rCell,&rIcon,&rName);
else GetCellRects(pView,pCellData,&rCell,&rTriangle,&rIcon,&rName,&ptName);
fSelect = SectRect(&r,&rTriangle,&rTemp) || SectRect(&r,&rIcon,&rTemp) || SectRect(&r,&rName,&rTemp);
fLastSelect = SectRect(&lastRect,&rTriangle,&rTemp) || SectRect(&lastRect,&rIcon,&rTemp) || SectRect(&lastRect,&rName,&rTemp);
if (fSelect == !fLastSelect)
{
// Need to change selection
Cell c;
c.h = 0; c.v = row;
GreyRect(&r); // To avoid screen fragments just hide selection rectangle
SelectItem(pView,row+1,!LVIsSelected(pView,row+1),false);
GreyRect(&r);
fChangeSelection = true;
}
}
}
}
if (fChangeSelection)
UpdateSelection(pView);
lastRect=r;
}
// See if we need to scroll
scroll = 0;
if (pt.v < pView->bounds.top)
scroll = -1;
else if (pt.v > pView->bounds.bottom)
scroll = 1;
if ((scroll<0 && (*hList)->visible.top) || (scroll>0 &&
(*hList)->visible.bottom < (*hList)->dataBounds.bottom))
{
short top;
top = (*hList)->visible.top; // Save top
GreyRect(&lastRect);
LScroll(0,scroll,pView->hList);
initPt.v += (top-(*hList)->visible.top)*RowHt;
OffsetRect(&lastRect,0,-scroll*(*pView->hList)->cellSize.v);
GreyRect(&lastRect);
}
}
GreyRect(&lastRect);
ClipMax();
pView->dragSelect = false;
}
/**********************************************************************
* LVDragSelectInProgress - Chance for the client to find out if a drag
* selection is in progress.
**********************************************************************/
Boolean LVDragSelectInProgress (ViewListPtr pView)
{
return (pView->dragSelect);
}
/**********************************************************************
* SelectItem - select (or unselect) an item, item is 1-based
**********************************************************************/
static void SelectItem(ViewListPtr pView,short item,Boolean fSelect,Boolean dontUpdate)
{
Cell c;
if (fSelect && !QueryItem(pView,kQuerySelect,item))
return; // Can't select this one
c.h = 0; c.v = item-1;
LSetSelect(fSelect, c, pView->hList);
CheckAutoSelect(pView,item-1);
if (!dontUpdate)
UpdateSelection(pView);
}
/**********************************************************************
* DragHilite - Hilite (or unhilite) a list item (1-based). This DOESN'T select.
**********************************************************************/
static void DragHilite(ViewListPtr pView,short row,Boolean hilite)
{
if (!row)
return; // Not within list
if (!hilite && Cell1Selected(row,pView->hList))
// Is selected. Don't unhilite.
return;
DrawRow(pView,row,hilite);
}
/************************************************************************
* DrawRow - draw the specified row (1-based)
************************************************************************/
static void DrawRow(ViewListPtr pView, short row, Boolean hilite)
{
ListHandle hList = pView->hList;
Rect rCell,rClip,r;
CellRec cellData;
if (row)
{
Cell1Rect(hList,row,&rCell);
if (SectRect(&rCell, &pView->bounds, &r)) // Make sure we're inside the list
{
GetCellData(pView,row-1,&cellData);
rClip = rCell;
rClip.right -= 2; // Avoid drag hilight on right
ClipRect(&rClip);
DrawItem(pView,row,&rCell,&cellData,hilite,true);
ClipMax();
}
}
}
/************************************************************************
* list click loop
************************************************************************/
static pascal Boolean MyListClickLoop(void)
{
Point mouse;
// Don't check for drag the very first time because we haven't returned a true value yet
// so LClick hasn't selected our item yet
if (gDragInfo.clickLoopCount++)
{
if (gDragInfo.clickLoopCount==2)
{
// LClick has updated the selection.
UpdateSelection(gDragInfo.pView);
}
GetMouse(&mouse);
// (jp) 12-11-00 Added 'StillDown' to the if statement so that we don't mistakenly think a
// drag is underway when, in fact, 'UpdateSelection' has merely taken a long
// time to complete on a single click.
if (StillDown () && abs(mouse.h - gDragInfo.pt.h) + abs(mouse.v - gDragInfo.pt.v) > 3)
// Moved, do drag
if (ListDrag(gDragInfo.pView,gDragInfo.event,gDragInfo.pt))
// Did drag, abort LClick
return false;
}
// Check for drag outside of list area
if (gDragInfo.drag && gDontDragHilite)
{
GetMouse(&mouse);
if (PtInRect(mouse,&gDragInfo.pView->bounds))
{
// Make sure draghilite is visible
if (!gDragInfo.dragHilited)
{
ShowListDragHilite(gDragInfo.pView, gDragInfo.drag);
gDragInfo.dragHilited = true;
}
}
else
{
// Make sure draghilite is hidden
if (gDragInfo.dragHilited)
{
HideDragHilite(gDragInfo.drag);
gDragInfo.dragHilited = false;
}
}
}
return true;
}
/************************************************************************
* DrawList - draw all or part of the list
************************************************************************/
static void DrawList(ViewListPtr pView,Rect *pRect)
{
RgnHandle hRgn;
ListHandle hList=pView->hList;
if (hRgn = NewRgn())
{
RectRgn(hRgn, pRect);
LUpdate(hRgn,hList);
DisposeRgn(hRgn);
// Erase any blank rows at the bottom
if ((*hList)->visible.bottom > (*hList)->dataBounds.bottom)
{
Rect rErase;
GrafPtr savePort;
SAVE_STUFF;
SET_COLORS;
GetPort(&savePort);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
Cell1Rect(hList,(*hList)->dataBounds.bottom+1,&rErase);
rErase.bottom = pView->bounds.bottom;
EraseRect(&rErase);
SetPort(savePort);
REST_STUFF;
}
}
}
/************************************************************************
* IsDragExpanded - is the indicated item already drag-expanded
************************************************************************/
static Boolean IsDragExpanded(short rowNum)
{
short i;
if (ghDrawExpandedRows)
{
short *pRow;
pRow = (short *)*ghDrawExpandedRows;
for(i = (GetHandleSize(ghDrawExpandedRows)/sizeof(short))-1;i>=0;i--)
{
if (pRow[i] == rowNum)
return true;
}
}
return false;
}
/************************************************************************
* CollapseDragExpanded - collapse any folders that were drag expanded
* that do not included the indicated descendent (1-based)
************************************************************************/
static void CollapseDragExpanded(ViewListPtr pView,short theRow,DragReference drag)
{
short i;
if (ghDrawExpandedRows)
{
short *pRow;
short count;
HLock(ghDrawExpandedRows);
pRow = (short *)*ghDrawExpandedRows;
count = (GetHandleSize(ghDrawExpandedRows)/sizeof(short));
for(i = count;i>0;i--)
{
short checkRow;
checkRow = pRow[i-1];
if (theRow < checkRow)
{
SignedByte cellsState = LockCells(pView);
SignedByte userHandleState = LockUserHandle(pView);
if (drag)
HideDragHilite(drag);
SetCollapseStatus(pView,FindCellData(pView,checkRow-1),checkRow,true);
RestoreCells(pView,cellsState);
RestoreUserHandle(pView,userHandleState);
if (drag)
ShowListDragHilite(pView, drag);
}
else
break;
}
HUnlock(ghDrawExpandedRows);
if (i<=0 || theRow==0)
{
// Collapsed them all
DisposeHandle(ghDrawExpandedRows);
ghDrawExpandedRows = 0;
}
else if (i < count)
{
SetHandleSize(ghDrawExpandedRows,i*sizeof(short));
}
}
}
/************************************************************************
* InWhichCell - in what cell (1-based) is the point?
************************************************************************/
static short InWhichCell(Point pt,ListHandle hList)
{
short row;
if (row=InWhich1Cell(pt,hList))
{
if (row > (*hList)->dataBounds.bottom)
row = 0;
}
return row;
}
/************************************************************************
* ClipMax - set clipping to maximum
************************************************************************/
static void ClipMax(void)
{
Rect rFull;
SetRect(&rFull,-32766,-32766,32766,32766);
ClipRect(&rFull);
}
/************************************************************************
* ShowListDragHilite - Show the hilite for a drag
************************************************************************/
static void ShowListDragHilite(ViewListPtr pView, DragReference drag)
{
Rect r;
if (!gDragFromMe)
{
r = pView->bounds;
r.right -= GROW_SIZE;
// InsetRect(&r,-1,-1);
ShowDragRectHilite(drag, &r, true);
}
}
/************************************************************************
* SetWaitExpandRow - set the row with the intermediate triangle to the indicated row number
************************************************************************/
static void SetWaitExpandRow(ViewListPtr pView,short rowNum)
{
if (gWaitExpandRow != rowNum)
{
short saveRow;
saveRow = gWaitExpandRow;
gWaitExpandRow = rowNum;
DrawRow(pView,saveRow,false);
DrawRow(pView,gWaitExpandRow,false);
}
}
/************************************************************************
* DrawPete - draw and frame the edit field
************************************************************************/
static void DrawPete(ViewListPtr pView)
{
Rect r;
PETEHandle pte;
if (pte = pView->pte)
if (PeteIsValid(pte))
{
PeteRect(pte,&r);
// if (r.right >= pView->bounds.right-GROW_SIZE)
// {
// Need to clip
// r.right = pView->bounds.right-GROW_SIZE-1;
// ClipRect(&r);
// }
PeteUpdate(pte);
InsetRect(&r,-1,-1);
FrameRect(&r);
}
}
/************************************************************************
* SizePete - resize edit field
************************************************************************/
static void SizePete(ViewListPtr pView, Rect *pRect)
{
short rtMax;
rtMax = pView->bounds.right - GROW_SIZE - 4;
if (pRect->right > rtMax)
pRect->right = rtMax;
if (pView->pte)
PETESizeDoc(PETE,pView->pte,RectWi(*pRect),RectHi(*pRect));
}
/************************************************************************
* GetParent - find the parent of the item (rowNum is 1-based)
************************************************************************/
static short GetParent(ViewListPtr pView, short rowNum)
{
CellRec *pCellData;
short parent=0;
if (rowNum>1 && (pCellData = FindCellData(pView,rowNum-1)))
{
short level,i;
level = pCellData->misc.level;
for(i=rowNum-1;i && (pCellData = FindCellData(pView,i-1));i--)
{
if (level>pCellData->misc.level)
{
parent = i;
break;
}
}
}
return parent;
}
/**********************************************************************
* DisposeDragRgn - dispose of drag region
**********************************************************************/
static void DisposeDragRgn(RgnHandle rgn, GWorldPtr gworld, RgnHandle hDrawRgn)
{
if (rgn)
DisposeRgn(rgn);
if (gworld)
DisposeGWorld(gworld);
if (hDrawRgn)
DisposeRgn(hDrawRgn);
}
/**********************************************************************
* BringWinToFront - bring our window to the front
**********************************************************************/
static void BringWinToFront(ViewListPtr pView)
{
MyWindowPtr pViewWPtr = pView->wPtr;
if (!pViewWPtr->isActive)
{
WindowPtr pViewWPtrWP = GetMyWindowWindowPtr (pViewWPtr);
SelectWindow_(pViewWPtrWP);
UpdateMyWindow(pViewWPtrWP); // Have to update manually since no events are processed
}
}
/**********************************************************************
* GetDragRgn - make a drag region out of the selected cells (clickRow is 1-based)
**********************************************************************/
static Boolean GetDragRgn(ViewListPtr pView, RgnHandle *phRgn, GWorldPtr *pgworld, RgnHandle *phDrawRgn,DragReference drag,short clickRow)
{
Boolean result = false;
RgnHandle hRgn = nil;
RgnHandle hDrawRgn = nil;
GWorldPtr gworld = nil;
ListHandle hList = pView->hList;
if (hRgn = NewRgn())
{
short i;
RgnHandle hTempRgn;
if (hTempRgn = NewRgn())
{
for (i=1;i<=pView->selectCount;i++)
{
CellRec cellData;
short row;
row = (*pView->hSelectList)[i-1].row;
if (GetCellData(pView,row,&cellData))
{
Rect rCell,rTriangle,rIcon,rName;
Point ptName;
Boolean fTranslucent = false;
Cell1Rect(hList,row+1,&rCell);
// Figure out the name and icon rects to make the drag region
if (*pView->details.GetCellRectsCallBack)
{
// do the drag region callback if there is one.
GetCellRectsCallBack(pView,&cellData,&rCell,&rIcon,&rName);
}
else
{
GetCellRects(pView,&cellData,&rCell,&rTriangle,&rIcon,&rName,&ptName);
InsetRect(&rName,-NameAddMargin,0);
}
// Get region of icon
if (IconIDToRgn(hTempRgn, &rIcon, atNone + atHorizontalCenter, cellData.iconID))
// Icon unailable, just use icon rect
RectRgn(hTempRgn,&rIcon);
if (row == clickRow-1)
{
// See if we can do translucent dragging on this one
long response;
OSErr err;
// check if the Drag Manager supports image dragging<6E>
if (!Gestalt(gestaltDragMgrAttr, &response) &&
(response & (1L << gestaltDragMgrHasImageSupport)))
{
// allocate a GWorld to hold the image; it is
// okay if the pixels are in the app heap or
// in temp memory
WindowPtr pViewWPtrWP = GetMyWindowWindowPtr (pView->wPtr);
CGrafPtr savePort;
GDHandle saveDevice;
Rect imageRect;
Point offsetPt;
GetGWorld(&savePort,&saveDevice);
UnionRect(&rIcon, &rName, &imageRect);
if (err = NewGWorld(&gworld,8,&imageRect,nil,nil,useTempMem))
err = NewGWorld(&gworld,8,&imageRect,nil,nil,0);
if (!err)
{
PixMapHandle imagePixMap;
Rect rGW;
imagePixMap = GetGWorldPixMap(gworld);
// draw the item into the GWorld
SetGWorld(gworld, nil);
LockPixels(imagePixMap);
EraseRect(GetPortBounds(gworld,&rGW));
TextFont(GetPortTextFont(GetWindowPort(pViewWPtrWP)));
TextSize(GetPortTextSize(GetWindowPort(pViewWPtrWP)));
DrawItem(pView,clickRow,&rCell,&cellData,true,true);
UnlockPixels(imagePixMap);
SetGWorld(savePort, saveDevice);
// We've already got the icon region. Just add the name region
if (hDrawRgn = NewRgn())
{
RectRgn(hDrawRgn,&rName); // Name region
UnionRgn(hDrawRgn,hTempRgn,hDrawRgn);
// RectRgn(hDrawRgn,&rIcon);
// attach the image to the drag
SetPt(&offsetPt,0,0);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
LocalToGlobal(&offsetPt);
if (!SetDragImage (drag, imagePixMap, hDrawRgn, offsetPt, kDragDarkTranslucency+kDragRegionAndImage))
fTranslucent = true;
}
}
SetGWorld(savePort, saveDevice);
}
}
if (!fTranslucent)
{
// Add regions
UnionRgn(hRgn,hTempRgn,hRgn); // Icon region
RectRgn(hTempRgn,&rName); // Name region
UnionRgn(hRgn,hTempRgn,hRgn);
}
}
}
DisposeRgn(hTempRgn);
OutlineRgn(hRgn,1);
GlobalizeRgn(hRgn);
result = true;
}
}
*phRgn = hRgn;
*pgworld = gworld;
*phDrawRgn = hDrawRgn;
return result;
}
/**********************************************************************
* LockCells - lock cells handle in ListRec
**********************************************************************/
static SignedByte LockCells(ViewListPtr pView)
{
Handle hCells = (*pView->hList)->cells;
SignedByte cellsState = HGetState(hCells);
HLock(hCells);
return cellsState;
}
/**********************************************************************
* LockUserHandle - lock the big list data handle in our pView
**********************************************************************/
static SignedByte LockUserHandle(ViewListPtr pView)
{
Handle userHandle = (*pView->hList)->userHandle;
SignedByte userHandleState = HGetState (userHandle);
HLock(userHandle);
return userHandleState;
}
/**********************************************************************
* RestoreCells - restore cells handle state
**********************************************************************/
static void RestoreCells(ViewListPtr pView,SignedByte state)
{
HSetState((*pView->hList)->cells,state);
}
/**********************************************************************
* RestoreUserHandle - restore the big list data handle state
**********************************************************************/
static void RestoreUserHandle(ViewListPtr pView,SignedByte state)
{
HSetState((*pView->hList)->userHandle,state);
}
/**********************************************************************
* CheckAutoSelect - see if we need to auto select some children, row is 0-based
**********************************************************************/
static void CheckAutoSelect(ViewListPtr pView,short row)
{
CellRec cellData,*pCellData;
CellRec nextCellData;
if (pView->flags&kfAutoSelectChild &&
GetCellData(pView,row,&cellData) &&
((cellData.misc.fParent && cellData.misc.fHaveChildren) || // current cell has natural children
(cellData.misc.level==0 && GetCellData(pView,row+1,&nextCellData) && nextCellData.misc.level==1))) // current cell has adopted children
{
// auto select children
Boolean select = Cell1Selected(row+1,pView->hList);
ListHandle hList = pView->hList;
for(row++;row<(*hList)->dataBounds.bottom;row++)
{
if ((pCellData=FindCellData(pView,row)) && pCellData->misc.level > cellData.misc.level)
SelectItem(pView,row+1,select,false);
else
break; // done
}
}
}
/**********************************************************************
* ScrollTopListView - scroll the list to the top.
**********************************************************************/
void ScrollTopListView(ViewListPtr pView)
{
ListHandle hList = pView->hList;
GrafPtr savePort;
// this seems strange and scary to me ...
(*hList)->visible.bottom -= (*hList)->visible.top;
(*hList)->visible.top = 0;
// redraw the list
UpdateSelection(pView);
GetPort(&savePort);
SetPort(GetMyWindowCGrafPtr(pView->wPtr));
InvalWindowRect(GetMyWindowWindowPtr(pView->wPtr),&pView->bounds);
SetPort(savePort);
DoDraw(pView);
}
/**********************************************************************
* LVScroll - scroll the list up or down
**********************************************************************/
void LVScroll(ViewListPtr pView,SInt32 lines)
{
LScroll(0,-lines,pView->hList);
}
/**********************************************************************
* LVCopy - copy selected item text to clipboard
**********************************************************************/
void LVCopy(ViewListPtr pView)
{
OSErr err = noErr;
Str255 s;
if (pView->pte)
{
// Renaming an item. Copy it's name
err = PeteEdit(pView->wPtr,pView->pte,peeCopy,&MainEvent);
}
else
{
Handle copyText = NuHandle(0);
if (!copyText) err = MemError();
else
{
short i;
VLNodeInfo data;
for (i=1;i<=pView->selectCount;i++)
{
LVGetItem(pView,i,&data,true);
PCopy(s,data.name);
if (i<pView->selectCount) PCatC(s,'\015');
if (err = PtrPlusHand_(s+1,copyText,*s)) break;
}
if (!err)
{
ScrapRef scrap;
ClearCurrentScrap();
GetCurrentScrap(&scrap);
err = PutScrapFlavor(scrap,'TEXT',kScrapFlavorMaskNone,GetHandleSize(copyText),LDRef(copyText));
}
ZapHandle(copyText);
}
}
if (err) WarnUser(COPY_FAILED,err);
}
/**********************************************************************
* LVChangeFocus - Change the focus
**********************************************************************/
void LVChangeFocus(ViewListPtr pView, ListFocusType newFocus)
{
if (pView->flags & kfListSupportsFocus)
if (pView->listFocus != newFocus) {
pView->listFocus = newFocus;
LVDrawFocus (pView);
}
}
/**********************************************************************
* LVFocus - Give (or remove) the listview focus
**********************************************************************/
Boolean LVSetKeyboardFocus(ViewListPtr pView, Boolean getFocus)
{
LVChangeFocus (pView, getFocus ? kFocusList : kFocusNone);
return (true);
}
/**********************************************************************
* LVAdvanceKeyboardFocus - Silly, we always give up the focus
**********************************************************************/
Boolean LVAdvanceKeyboardFocus (ViewListPtr pView)
{
return (true);
}
/**********************************************************************
* LVReverseKeyboardFocus - Silly, we always give up the focus
**********************************************************************/
Boolean LVReverseKeyboardFocus (ViewListPtr pView)
{
return (true);
}
/**********************************************************************
* LVDrawFocus - Draw the focus
**********************************************************************/
void LVDrawFocus(ViewListPtr pView)
{
Boolean hasFocus;
if (pView->flags & kfListSupportsFocus) {
hasFocus = pView->wPtr->isActive && pView->listFocus == kFocusList;
DrawThemeFocusRect (&pView->bounds, hasFocus);
// Have to redraw the frame on an erase because Inside Mac tells us to
if (!hasFocus)
DrawThemeListBoxFrame (&pView->bounds, kThemeStateActive);
}
}
/**********************************************************************
* LVGetLastAddedRow - Get the number of the last added row -- 1 based
**********************************************************************/
short LVGetNextRowToAdd (void)
{
return (gAddCell);
}