home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-10-24 | 13.4 KB | 430 lines | [TEXT/MMCC] |
- // ===========================================================================
- // CTwistDownListBox.h ©1994 Jan Bruyndonckx All rights reserved.
- // v1.0 18/6/94
- // Inspired by M.Minow's article in Develop 18
- //
- // A hierarchical listbox subclass.
- // The default listbox contains only string data.
- // ===========================================================================
-
- #include <string.h>
- #include <Palettes.h>
- #include <UDrawingState.h>
- #include "CTwistDownListBox.h"
-
- #ifndef DEBUG_NEW // MetroWerks 'new' leak-finder
- #define NEW new
- #endif
-
- //----------------------------------------------------------------------------
- // The trianges
-
- PolyHandle CTwistDownListBox::sClosedPoly = NULL,
- CTwistDownListBox::sOpenedPoly = NULL,
- CTwistDownListBox::sIntermediatePoly = NULL ;
-
- static void DrawTriangle (PolyHandle aPoly, const Boolean drawFilled,
- const short posV, const short posH) ;
-
- // These macros simplify access to the flag word in the list element.
- #define SetTDFlag(flags, mask) ((flags) |= (mask))
- #define ClearTDFlag(flags, mask) ((flags) &= ~(mask))
- #define InvertTDFlag(flags, mask) ((flags) ^= (mask))
- #define TestTDFlag(flags, mask) (((flags) & (mask)) != 0)
-
- //----------------------------------------------------------------------------
- // Creation methods
-
- CTwistDownListBox* CTwistDownListBox::CreateFromStream(LStream *inStream)
- {
- return (NEW CTwistDownListBox(inStream));
- }
-
- CTwistDownListBox::CTwistDownListBox() : CCustomListBox()
- {
- init () ;
- }
-
- CTwistDownListBox::CTwistDownListBox(const SPaneInfo &inPaneInfo,
- Boolean inHasHorizScroll, Boolean inHasVertScroll,
- Boolean inHasGrow, Boolean inHasFocusBox,
- MessageT inDoubleClickMessage, Int16 inTextTraitsID,
- LCommander *inSuper) :
- CCustomListBox (inPaneInfo, inHasHorizScroll, inHasVertScroll,
- inHasGrow, inHasFocusBox, inDoubleClickMessage, inTextTraitsID,
- inSuper)
- {
- init () ;
- }
-
- CTwistDownListBox::CTwistDownListBox (const CTwistDownListBox &inOriginal) :
- CCustomListBox (inOriginal)
- {
- init () ;
- }
-
- CTwistDownListBox::CTwistDownListBox(LStream *inStream) : CCustomListBox (inStream)
- {
- init () ;
- }
-
- //----------------------------------------------------------------------------
- // Create the triangle buttons, if they didn't exist already
-
- void CTwistDownListBox::init (void)
- {
- // Create the twist down buttons
- // Note that the port and text drawing characteristics must have been set.
-
- short buttonSize;
- short halfSize;
- short intermediateSize;
- FontInfo info;
-
- ::GetFontInfo(&info);
- buttonSize = info.ascent; // The "show sublist" button
- buttonSize &= ~1; // Round down to an even number
- halfSize = buttonSize / 2;
- intermediateSize = (buttonSize * 3) / 4;
-
- // note, we allocate the polygons once (they are static variables) for *all* lists, and
- // we never dispose them
-
- if (sClosedPoly == NULL)
- { sClosedPoly = ::OpenPoly();
- ::MoveTo(halfSize, 0);
- ::LineTo(buttonSize, halfSize);
- ::LineTo(halfSize, buttonSize);
- ::LineTo(halfSize, 0);
- ::ClosePoly();
- }
- if (sOpenedPoly == NULL)
- { sOpenedPoly = ::OpenPoly();
- ::MoveTo(0, halfSize);
- ::LineTo(buttonSize, halfSize);
- ::LineTo(halfSize, buttonSize);
- ::LineTo(0, halfSize);
- ::ClosePoly();
- ::OffsetPoly (sOpenedPoly, 1, -2) ;
- }
- if (sIntermediatePoly == NULL)
- { sIntermediatePoly = ::OpenPoly();
- ::MoveTo(intermediateSize, 0);
- ::LineTo(intermediateSize, intermediateSize);
- ::LineTo(0, intermediateSize);
- ::LineTo(intermediateSize, 0);
- ::ClosePoly();
- }
-
- // Remember the width of the "button" area.
- triangleWidth = (**sOpenedPoly).polyBBox.right
- + kTriangleOutsideGap
- + kTriangleInsideGap;
- }
-
- //----------------------------------------------------------------------------
- // If the data-field contains text (default case), then get/set it
-
- StringPtr CTwistDownListBox::GetDescriptor(Str255 outDescriptor) const
- { Byte buffer[260] ;
- short l = sizeof (buffer) ;
- TwistDownRecPtr twistElement = (TwistDownRecPtr) buffer ;
-
- twistElement->data[0] = '\0' ;
- GetSelection (twistElement, &l) ;
- l -= TwistDownRecSize ;
- ::memcpy (outDescriptor+1, twistElement->data, l) ;
- outDescriptor[0] = l ;
-
- return outDescriptor ;
- }
-
- void CTwistDownListBox::SetDescriptor(ConstStr255Param inDescriptor)
- { Byte buffer[260] ;
- short l ;
- TwistDownRecPtr twistElement = (TwistDownRecPtr) buffer ;
-
- l = *inDescriptor+1 ;
- ::memcpy (twistElement->data, inDescriptor+1, l) ;
- twistElement->indent = 0 ;
- twistElement->flags = 0 ;
- l += TwistDownRecSize ;
- SetSelection (twistElement, l) ;
- }
-
- //----------------------------------------------------------------------------
- // Some info about the mouse location (local coordinates)
-
- void CTwistDownListBox::MouseInfo (Point mousePt, Boolean *inButtonArea, Cell *theCell)
- {
- ::GlobalToLocal (&mousePt) ;
- Rect hitRect = (*mMacListH)->rView ;
- hitRect.right = (*mMacListH)->rView.left + triangleWidth ;
- *inButtonArea = ::PtInRect (mousePt, &hitRect) ; // yes, is it in a button?
-
- short visibleTop = (*mMacListH)->visible.top ;
- short cellHeight = (*mMacListH)->cellSize.v ;
- // get the selected cell
- theCell->v = (mousePt.v - hitRect.top) / cellHeight + visibleTop ;
- theCell->h = 0 ;
- }
-
- //----------------------------------------------------------------------------
- // The user clicked in the list
-
- void CTwistDownListBox::ClickSelf (const SMouseDownEvent &inMouseDown)
- { long l ;
- Cell theCell ;
- Boolean inButtonArea ;
-
- SwitchTarget(this);
- FocusDraw();
-
- // is the mousedown in the twistDown button area?
- MouseInfo (inMouseDown.macEvent.where, &inButtonArea, &theCell) ;
-
- Rect hitRect = (*mMacListH)->rView ;
-
- if (inButtonArea) // yes, is it in a button?
- { short visibleTop = (*mMacListH)->visible.top ;
- short cellHeight = (*mMacListH)->cellSize.v ;
-
- TwistDownRecPtr twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
-
- if ((twist == NULL) || (TestTDFlag(twist->flags, kHasSubList) == 0))
- goto end ;
-
- SetTDFlag (twist->flags, kDrawFilled) ; // Draw the button
- ::LDraw (theCell, mMacListH) ; // Draw the cell anew
- hitRect.top += ((theCell.v - visibleTop) * cellHeight) ; // set to the dimensions of the button
- hitRect.bottom = hitRect.top + cellHeight ;
- Boolean inHitRect = false ;
- if (::StillDown())
- { inHitRect = true ;
- Point mousePt ;
-
- while (::WaitMouseUp())
- { ::GetMouse (&mousePt);
- Boolean newInHitRect = ::PtInRect(mousePt, &hitRect) ;
- if (newInHitRect != inHitRect)
- { twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- InvertTDFlag (twist->flags, kDrawFilled) ;
- ::LDraw (theCell, mMacListH) ;
- inHitRect = newInHitRect ;
- }
- }
- }
-
- twist = (TwistDownRecPtr) GetCellPtr (theCell) ; // the user released the mouse
- if (inHitRect == false)
- { // make very sure the button is cleared
- if (TestTDFlag (twist->flags, kDrawFilled))
- { ClearTDFlag (twist->flags, kDrawFilled) ;
- ::LDraw (theCell, mMacListH) ;
- }
- return ; // and get out…
- }
-
- SetTDFlag (twist->flags, kDrawIntermediate) ; // Draw the animation triangle
- ::LDraw (theCell, mMacListH) ;
- ::Delay (kAnimationDelay, &l) ;
- twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- ClearTDFlag (twist->flags, kDrawIntermediate) ;
-
- ::LSetDrawingMode (false, mMacListH) ; // here send message…
- if (TestTDFlag (twist->flags, kIsOpened)) // …to expand/collapse sublist
- { ClearTDFlag (twist->flags, kIsOpened) ;
- CollapseElement (theCell) ;
- }
- else
- {
- if (GetHandleSize ((*mMacListH)->cells) > 30000)
- { ListLimitReached () ;
- }
- else
- { SetTDFlag (twist->flags, kIsOpened) ;
- ExpandElement (theCell) ;
- }
- }
- ::LSetDrawingMode (true, mMacListH) ; // redraw list again
- twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- ClearTDFlag (twist->flags, kDrawFilled) ;
- ResizeFrameBy (0, 0, true) ; // this works more correctly when collapsing & rows are 'above' the top
- return ;
- }
-
- end: // mousedown was not in a button…
- inherited::ClickSelf (inMouseDown) ;
- }
-
- //---------------------------------------------------------------------------------------
-
- void CTwistDownListBox::FullyExpand (const unsigned short inMaxDepth)
- {
- Cell cell = { 0, 0 } ;
-
- ::LSetDrawingMode (false, mMacListH) ; // don't draw during expand
-
- for (;;)
- { TwistDownRecPtr twist = (TwistDownRecPtr) GetCellPtr (cell) ;
- if (twist == NULL)
- break ;
-
- // element has no sublist or is already opened…
- if ((TestTDFlag(twist->flags, kHasSubList) != 0) && // has a sublist
- (TestTDFlag(twist->flags, kIsOpened) == 0) && // not yet opened
- (twist->indent < inMaxDepth)) // don't go below this level
- { // Expand the element
- SetTDFlag (twist->flags, kIsOpened) ;
- ExpandElement (cell) ;
- }
-
- // advance to next cell position
- if (::LNextCell (true, true, &cell, mMacListH) == false)
- break ;
- }
-
- ::LSetDrawingMode (true, mMacListH) ; // redraw list again
- ResizeFrameBy (0, 0, true) ; // force update
- }
-
- //---------------------------------------------------------------------------------------
- // We reached the 32K limit of the list manager!
- // Override if you want to present a nice alert…
-
- void CTwistDownListBox::ListLimitReached (void)
- {
- SysBeep (1) ;
- SysBeep (1) ;
- #ifdef ASSERTION
- DebugStr("\p32K Reached") ;
- #endif
- }
-
- //----------------------------------------------------------------------------
-
- void CTwistDownListBox::DrawElementSelf (const Rect *lRect,
- const void *lElement,
- const short lDataLen)
-
- // Draw a single list cell on the screen.
- // Checks flags and draws triangular button if needed;
- // calls DrawTwistedElement to draw cell contents.
-
- {
- TwistDownRecPtr twistElement = (TwistDownRecPtr) lElement ;
-
- if (TestTDFlag (twistElement->flags, kHasSubList))
- { PolyHandle aPoly = NULL ;
-
- aPoly = TestTDFlag(twistElement->flags, kIsOpened) ? sOpenedPoly : sClosedPoly ;
- if (TestTDFlag(twistElement->flags, kDrawIntermediate))
- aPoly = sIntermediatePoly ;
- if (aPoly)
- DrawTriangle (aPoly,
- TestTDFlag(twistElement->flags, kDrawFilled),
- lRect->top+1,
- lRect->left+kTriangleOutsideGap) ;
- }
-
- ::MoveTo (lRect->left + triangleWidth + 2 + twistElement->indent*kIndentOffset,
- lRect->top + 10) ;
- DrawTwistedElement (lRect, twistElement, lDataLen) ;
- }
-
- //---------------------------------------------------------------------------------------
- // Since we have only one column of data, resize the cellWidth accordingly
-
- void CTwistDownListBox::ResizeFrameBy (Int16 inWidthDelta, Int16 inHeightDelta, Boolean inRefresh)
- {
- (*mMacListH)->cellSize.h += inWidthDelta ;
- inherited::ResizeFrameBy (inWidthDelta, inHeightDelta, inRefresh) ;
- }
-
- //----------------------------------------------------------------------------
- // Draw the data-field of the element. Default is simple text
-
- void CTwistDownListBox::DrawTwistedElement (const Rect *lRect,
- const TwistDownRecPtr twistElement,
- const short lDataLen)
-
- // Draw contents of a single list element.
- // Default version just draws text; override for other types of data.
-
- {
- #pragma unused (lRect)
- ::DrawText (twistElement->data, 0, lDataLen-TwistDownRecSize) ;
- }
-
- //----------------------------------------------------------------------------
- // User closed a sublist. Remove all sublists hanging from this element.
- // Override in case an element contains a pointer to some allocated memory.
-
- void CTwistDownListBox::CollapseElement (const Cell theCell)
- { short count = 0 ;
- TwistDownRecPtr twist = (TwistDownRecPtr) GetCellPtr (theCell) ;
- short headIndent = twist->indent ;
- Cell cell = theCell ;
-
- for (cell.v++ ; ; cell.v++)
- { twist = (TwistDownRecPtr) GetCellPtr (cell) ;
- if (twist == NULL)
- break ;
- if (twist->indent <= headIndent)
- break ;
- count++ ;
- }
-
- if (count)
- ::LDelRow (count, theCell.v+1, mMacListH) ;
- }
-
- //----------------------------------------------------------------------------
- // You'd better override me!
-
- void CTwistDownListBox::ExpandElement (const Cell theCell)
- {
- #pragma unused (theCell)
- }
-
- //----------------------------------------------------------------------------
-
- static void DrawTriangle (PolyHandle aPoly, const Boolean drawFilled,
- const short posV, const short posH)
- { const short kGrayness = 4 ; // 1 for intermediate gray, 8 for very light gray
-
- ::PenNormal () ;
- ::OffsetPoly (aPoly, posH, posV); // move poly to the right spot
-
- if (drawFilled)
- ::FillPoly(aPoly, &qd.black);
- else
- { RGBColor foreColor, backColor;
-
- ::GetForeColor(&foreColor);
- ::GetBackColor(&backColor);
-
- // This loop sets foreColor to a very light gray.
- for (short i = 0; i < kGrayness; i++)
- if (::GetGray(GetGDevice(), &backColor, &foreColor) == FALSE)
- break;
-
- if ((foreColor.red == 0) &&
- (foreColor.green == 0) &&
- (foreColor.blue == 0))
- ::ErasePoly (aPoly) ;
- else
- { StColorState saveColorState ;
- ::RGBForeColor(&foreColor);
- ::FillPoly(aPoly, &qd.black);
- }
-
- ::FramePoly (aPoly) ;
- }
-
- ::OffsetPoly (aPoly, -posH, -posV);
- }
-
- //----------------------------------------------------------------------------
-
-