// ================================================================================= // CDDListBox.h ©1995 J. Rodden, DD/MF & Associates. All rights reserved // // Based on: // CDDxList ©1995 Malcolm Pradhan. All rights reserved // ================================================================================= #include "CDDListBox.h" #include #include #include #include #include const short kCellLineHeight = 2; // --------------------------------------------------------------------------------- // € CreateFromStream // --------------------------------------------------------------------------------- CDDListBox* CDDListBox::CreateFromStream( LStream *inStream ) { return (new CDDListBox(inStream)); } // --------------------------------------------------------------------------------- // € CDDListBox // --------------------------------------------------------------------------------- CDDListBox::CDDListBox() : CBetterListBox(), CDragAndDrop(UQDGlobals::GetCurrentPort(), this) { } // --------------------------------------------------------------------------------- // € CDDListBox(LStream*) // --------------------------------------------------------------------------------- CDDListBox::CDDListBox( LStream *inStream ) : CBetterListBox(inStream), CDragAndDrop(UQDGlobals::GetCurrentPort(), this) { } // --------------------------------------------------------------------------------- // € FinishCreateSelf // --------------------------------------------------------------------------------- // This code could have gone in the constructor for CDDListBox, but then // derived class's ldef initialization code wouldn't get called. void CDDListBox::FinishCreateSelf() { CBetterListBox::FinishCreateSelf(); mAcceptsInternalDrags = true; mDrawInsertionLine = true; mInsertDrop = -1; } // --------------------------------------------------------------------------------- // € ClickInCell // --------------------------------------------------------------------------------- Boolean CDDListBox::ClickInCell(const SMouseDownEvent &inMouseDown, Rect& inRect, Cell& inCell) { SelectOneCell(inCell); return ClickIsDragEvent( inMouseDown, &inRect); } // ================================================================================= // Drag and Drop // ================================================================================= // --------------------------------------------------------------------------------- // € AddFlavors // --------------------------------------------------------------------------------- // Add flavored items to the DragTask. void CDDListBox::AddFlavors(DragReference inDragRef) { listBoxFlavor theFlavorData; theFlavorData.vSenderID = GetPaneID(); if ( GetLastSelectedCell(theFlavorData.vCell) ) { ::AddDragItemFlavor( inDragRef, kItemRef, listBoxFlavorType, &theFlavorData, sizeof(listBoxFlavor), flavorSenderOnly); } } // --------------------------------------------------------------------------------- // € SetLocalFrame // --------------------------------------------------------------------------------- // Setup local frame (in global coords) for reference by drawing routines void CDDListBox::SetLocalFrame (void) { mLocalFrame = (**mMacListH).rView; LocalToPortPoint(topLeft (mLocalFrame)); LocalToPortPoint(botRight (mLocalFrame)); PortToGlobalPoint(topLeft (mLocalFrame)); PortToGlobalPoint(botRight (mLocalFrame)); } // --------------------------------------------------------------------------------- // € ItemIsAcceptable // --------------------------------------------------------------------------------- Boolean CDDListBox::ItemIsAcceptable(DragReference inDragRef, ItemReference inItemRef) { // ItemIsAcceptable will be called whenever the Drag Manager wants to know if // the item the user is currently dragging contains any information that we // can accept. // // In our case, the only thing we'll accept are mFlavorAccepted items. FlavorFlags theFlags; if ( mAcceptsInternalDrags && ::GetFlavorFlags(inDragRef, inItemRef, listBoxFlavorType, &theFlags) == noErr ) return true; return CDragAndDrop::ItemIsAcceptable( inDragRef, inItemRef); } // --------------------------------------------------------------------------------- // € LeaveDropArea // --------------------------------------------------------------------------------- void CDDListBox::LeaveDropArea (DragReference inDragRef) { CDragAndDrop::LeaveDropArea(inDragRef); CleanInsertionLine(); } // --------------------------------------------------------------------------------- // € HiliteDropArea // --------------------------------------------------------------------------------- // Hilite a DropArea to indicate that it can accept the current Drag // // For a DragAndDrop, the drop area is the Frame of its associated // Pane inset by one pixel to account for the border which usually // surrounds a Drop-capable Pane. void CDDListBox::HiliteDropArea (DragReference inDragRef) { Rect dropRect; RgnHandle dropRgn = ::NewRgn(); if (mHighlightDrag) { FocusDraw(); dropRect = (**mMacListH).rView; ::RectRgn(dropRgn, &dropRect); ::ShowDragHilite(inDragRef, dropRgn, true); ::DisposeRgn(dropRgn); } } // --------------------------------------------------------------------------------- // € InsideDropArea // --------------------------------------------------------------------------------- // Track the mouse and show the insertion point of the item if it were dropped. // Scroll the list if the mouse is close to the bottom or top of the list. void CDDListBox::InsideDropArea ( DragReference inDragRef, Point& theMouseLocation, Point& thePinnedLocation) { Cell theCell, oldCell = {0, 0}; short cellHeight; short cellWidth; short scrollD; short visibleTop; short visibleLeft; Rect scrollBounds; Boolean hiliteFlag = mHighlightDrag; visibleTop = (**mMacListH).visible.top; visibleLeft = (**mMacListH).visible.left; cellHeight = (**mMacListH).cellSize.v; cellWidth = (**mMacListH).cellSize.h; theCell.h = 0; theCell.v = ((theMouseLocation.v - (**mMacListH).rView.top + 6) / cellHeight) + visibleTop; scrollBounds = (**mMacListH).rView; scrollBounds.top += 8; scrollBounds.bottom -= 8; if (theCell.v > (**mMacListH).dataBounds.bottom) theCell.v = (**mMacListH).dataBounds.bottom; if (theCell.v < 0) theCell.v = 0; if (theCell.v != mInsertDrop) { if (mInsertDrop >= 0) { oldCell.v = mInsertDrop; InvertCellEdge(oldCell); } InvertCellEdge(theCell); mInsertDrop = theCell.v; } // scroll if we're not already at the bottom if (theMouseLocation.v > scrollBounds.bottom) { if ((**mMacListH).visible.bottom != (**mMacListH).dataBounds.bottom) { mInsertDrop = theCell.v; CleanInsertionLine(); scrollD = -cellHeight; if (hiliteFlag) ::DragPreScroll(inDragRef, 0, scrollD); ::LScroll (0, 1, mMacListH); if (hiliteFlag) ::DragPostScroll(inDragRef); } } // scroll if we're not already at the top else if (theMouseLocation.v < scrollBounds.top) { if ((**mMacListH).visible.top != 0) { mInsertDrop = theCell.v; CleanInsertionLine (); scrollD = cellHeight; if (hiliteFlag) ::DragPreScroll (inDragRef, 0, scrollD); ::LScroll (0, -1, mMacListH); if (hiliteFlag) ::DragPostScroll (inDragRef); } } } // --------------------------------------------------------------------------------- // € ReceiveDragItem // --------------------------------------------------------------------------------- // The user has dropped something in this view. void CDDListBox::ReceiveDragItem( DragReference inDragRef, ItemReference inItemRef, Boolean inCopyData, // Should we copy the data? Boolean inFromFinder, // Data came from the Finder Rect& inItemBounds) // In Local coordinates { short rowNum = mInsertDrop; // Determine where to insert data ListBoxDragCleanup(); FlavorFlags theFlags; // First deal with moves within the list if ( ::GetFlavorFlags(inDragRef, inItemRef, listBoxFlavorType, &theFlags) == noErr ) { Size theDataSize; listBoxFlavor theFlavorData; ::GetFlavorDataSize(inDragRef, inItemRef, listBoxFlavorType, &theDataSize); ThrowIf_(theDataSize != sizeof(listBoxFlavor)); // sanity check ::GetFlavorData(inDragRef, inItemRef, listBoxFlavorType, &theFlavorData, &theDataSize, 0L); if ( theFlavorData.vSenderID == GetPaneID() && // Did data come from this pane? theFlavorData.vCell.v != rowNum && // Are we actually moving the data? theFlavorData.vCell.v != (rowNum - 1) ) { ::LSetSelect( false, theFlavorData.vCell, mMacListH); ::LSetDrawingMode( false, mMacListH); // First add the new data rowNum = ::LAddRow( 1, rowNum, mMacListH); Cell sourceCell = { theFlavorData.vCell.v, 0}; Cell targetCell = { rowNum, 0}; sourceCell.v += ( rowNum < theFlavorData.vCell.v ) ? 1 : 0 ; CopyCell( sourceCell, targetCell); // Now check if we should delete the source if ( !inCopyData ) { DeleteRow(sourceCell.v); targetCell.v -= ( rowNum < sourceCell.v ) ? 0 : 1 ; } ::LSetSelect( true, targetCell, mMacListH); ::LSetDrawingMode( true, mMacListH); // refresh the updated part of the list Rect viewRect = (**mMacListH).rView; short cellH = (**mMacListH).cellSize.v; short firstRow = (rowNum > sourceCell.v) ? sourceCell.v : rowNum; short lastRow = (rowNum < sourceCell.v) ? sourceCell.v : rowNum + 1; viewRect.top += (firstRow - (**mMacListH).visible.top) * cellH; if (viewRect.top < viewRect.bottom) { short bottom = (lastRow - firstRow) * cellH + viewRect.top; viewRect.bottom = ( viewRect.bottom < bottom ) ? viewRect.bottom : bottom + kCellLineHeight; InvalRect(&viewRect); } } } else { CDragAndDrop::ReceiveDragItem( inDragRef, inItemRef, inCopyData, inFromFinder, inItemBounds); } } // --------------------------------------------------------------------------------- // € CopyOrMoveDragData // --------------------------------------------------------------------------------- void CDDListBox::CopyOrMoveDragData( void* inDragData, Size inDataSize, Boolean inCopyData, Boolean inFromFinder, Rect& inItemBounds) { Cell theCell = { mInsertDrop, 0}; ListBoxDragCleanup(); // Add the new data to the list ::LSetDrawingMode( false, mMacListH); AddRowElement( inDragData, inDataSize, theCell); ::LSetDrawingMode( true, mMacListH); // Refresh updated part of the list Rect viewRect = (**mMacListH).rView; viewRect.top += (theCell.v - (**mMacListH).visible.top) * (**mMacListH).cellSize.v; if (viewRect.top < viewRect.bottom) ::InvalRect(&viewRect); } // --------------------------------------------------------------------------------- // € CopyCell // --------------------------------------------------------------------------------- // You MUST override this routine in order to do internal list dragging void CDDListBox::CopyCell( Cell inSrcCell, Cell inDestCell) { /* * MyDataType theData; * short theSize = sizeof(MyDataType); * * ::LGetCell( &theData, &theSize, inSrcCell, mMacListH); * * ::LSetCell( &theData, theSize, inDestCell, mMacListH); */ } // --------------------------------------------------------------------------------- // € ListBoxDragCleanup // --------------------------------------------------------------------------------- // The user has dropped something in this view. void CDDListBox::ListBoxDragCleanup() { CleanInsertionLine(); mHighlightDrag = true; Cell selectedCell; // Drag may have upset selection graphics if ( GetLastSelectedCell(selectedCell) ) ::LDraw( selectedCell, mMacListH); } // --------------------------------------------------------------------------------- // € InvertCellEdge // --------------------------------------------------------------------------------- // Inverts a line above the cell to show where the dragged item would be // inserted if dropped. Takes care not to over-draw the pane's hilite. void CDDListBox::InvertCellEdge (Cell theCell) { if (mDrawInsertionLine) { Rect topRect; ::LRect (&topRect, theCell, mMacListH); if (topRect.top <= (**mMacListH).rView.top + 1) topRect.top = (**mMacListH).rView.top + 2; if (topRect.top + 1 >= (**mMacListH).rView.bottom - 2) topRect.top = (**mMacListH).rView.bottom - 4; topRect.bottom = topRect.top + kCellLineHeight; if (mHighlightDrag) { topRect.left += 2; topRect.right -= 2; } FocusDraw(); LMSetHiliteMode( LMGetHiliteMode() & ~(1 << hiliteBit) ); ::InvertRect (&topRect); } } // --------------------------------------------------------------------------------- // € CleanInsertionLine // --------------------------------------------------------------------------------- // Removes the insertion line and resets insertdrop. void CDDListBox::CleanInsertionLine (void) { Cell theCell = { 0, 0}; if (mInsertDrop >= 0) { theCell.v = mInsertDrop; InvertCellEdge(theCell); mInsertDrop = -1; } }