home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / mac / UserInterface / Tables / LTableHeader.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  41.7 KB  |  1,415 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.     
  21.     Created 3/18/96 - Tim Craycroft (tj, x3672)
  22.  
  23.     To Do:
  24.     
  25. */
  26.  
  27.  
  28. // PowerPlant
  29. #include <URegions.h>
  30.  
  31. // Mac Lib
  32. #include "UserInterface_Defines.h"
  33. #include "UGraphicGizmos.h"
  34. #include "LTableHeader.h"
  35. #include "CSimpleDividedView.h"
  36.  
  37. #include <math.h>
  38.  
  39. // Flags
  40.  
  41. typedef UInt16 HeaderFlagsT;
  42. static const HeaderFlagsT cFlagHeaderSortedBackwards = 0x0001;
  43.  
  44. // Move me somewhere useful !!!
  45. static void    LocalToGlobalRect(Rect& r);
  46.  
  47. //======================================
  48. class StCurrentPort
  49. //======================================
  50. {
  51. public:
  52.     StCurrentPort(GrafPtr inPort)     { mPort = UQDGlobals::GetCurrentPort(); SetPort((GrafPtr)inPort); }
  53.     ~StCurrentPort()                 { SetPort(mPort); }
  54.  
  55. private:
  56.     GrafPtr    mPort;
  57. };
  58.  
  59. //-----------------------------------
  60. LTableHeader::LTableHeader( LStream *inStream )
  61.     : LView(inStream), LBroadcaster()
  62. //-----------------------------------
  63. {
  64.     ResIDT theBevelTraitsID;
  65.     
  66.     mColumnData = NULL;
  67.     mColumnCount = mLastVisibleColumn = 0;
  68.     
  69.     *inStream >> mHeaderFlags;
  70.         
  71.     *inStream >> theBevelTraitsID;
  72.     UGraphicGizmos::LoadBevelTraits(theBevelTraitsID, mSortedBevel);
  73.     *inStream >> theBevelTraitsID;
  74.     UGraphicGizmos::LoadBevelTraits(theBevelTraitsID, mUnsortedBevel);
  75.     
  76.     *inStream >> mReverseModifiers;
  77.     
  78.     *inStream >> mColumnListResID;
  79.     
  80.     ResizeImageTo(mFrameSize.width, mFrameSize.height, false);
  81. }
  82.     
  83. //-----------------------------------
  84. LTableHeader::~LTableHeader()
  85. //-----------------------------------
  86. {
  87.     if (mColumnData != NULL) ::DisposeHandle( (Handle) mColumnData);
  88.     mColumnData = NULL;
  89. }
  90.  
  91. //-----------------------------------
  92. void LTableHeader::FinishCreateSelf()
  93. //-----------------------------------
  94. {
  95.     // Load up the column data    
  96.     Handle columnData = ::GetResource('Cols', mColumnListResID);
  97.     ThrowIfNULL_(columnData);
  98.     ::DetachResource((Handle)columnData);
  99.  
  100.     // LHandleStream will dispose of columnData
  101.     LHandleStream streamMe(columnData);    
  102.     ReadColumnState(&streamMe, false);
  103.  
  104.     // position the column header panes
  105.     LView::FinishCreateSelf();
  106.     PositionColumnHeaders(true);        
  107. }
  108.  
  109. //-----------------------------------
  110. void LTableHeader::DrawSelf()
  111. //    Draw each column, draw any space to the right of the rightmost
  112. //    column, and draw the column-hiding widget, if necessary.
  113. //-----------------------------------
  114. {
  115.     UInt16    headerWidth = GetHeaderWidth();
  116.     if (headerWidth == 0)
  117.         return;
  118.     
  119.     StColorPenState::Normalize();
  120.                 
  121.     // Draw bottom frame line
  122.     
  123.     Rect r;
  124.     CalcLocalFrameRect(r);
  125.     ::MoveTo(r.left, r.bottom - 1);
  126.     ::LineTo(r.right - 1, r.bottom - 1);
  127.                 
  128.     // Draw the column backgrounds and frames, carrying r.right
  129.     // out of the loop so we know where the rightmost column ends
  130.     
  131.     RgnHandle updateRgn = GetLocalUpdateRgn();
  132.     for (int i = 1; i <= mLastVisibleColumn; i++)
  133.     {
  134.         Rect        sect;
  135.     
  136.         LocateColumn(i, r);
  137.         SColumnData* cData = GetColumnData(i);
  138.         
  139.         // Checking against the update rgn is necessary because we sometimes
  140.         // call Draw(), with a valid rgn to draw only particular columns
  141.         // in order to reduce flicker.
  142.  
  143.         // If we didn't do this check we would end up erasing other columns,
  144.         // as Draw would not redraw their header subpanes
  145.  
  146.         if ( updateRgn == NULL || ::SectRect(&r, &((**updateRgn).rgnBBox), §))
  147.         {
  148.             Boolean isSortedColumn = cData->GetPaneID() == GetColumnPaneID(mSortedColumn);
  149.             DrawColumnBackground(r, isSortedColumn);
  150.             if (isSortedColumn && cData->HasSortIcon())
  151.             {
  152.                 // Draw the little icon
  153.                 const Int16 kSortIconWidth = 14; // which allows 1 pixel each side.
  154.                 ResIDT iconID = mSortedBackwards ? 14504 : 14505;
  155.                 Rect iconFrame = r;
  156.                 iconFrame.left = iconFrame.right - kSortIconWidth;
  157.                 ::PlotIconID(&iconFrame, kAlignAbsoluteCenter, kTransformNone, iconID);
  158.             }
  159.         }
  160.     }
  161.     if (updateRgn)
  162.         ::DisposeRgn(updateRgn);
  163.  
  164.     // If a fixed-width column is rightmost, 
  165.     // we may have some space to fill in between
  166.     // it and the right edge of the view    
  167.     if (headerWidth > r.right )
  168.     {
  169.         r.left = r.right;
  170.         r.right = headerWidth;
  171.         
  172.         DrawColumnBackground(r, false);
  173.     }
  174.     CalcLocalFrameRect(r);
  175.     r.left = r.right - kColumnHidingWidgetWidth;
  176.     DrawColumnBackground(r, false);
  177.  
  178.     // Draw the column hiding widget
  179.     if (CanHideColumns())
  180.     {
  181.         SInt16    iconID;
  182.         r.top -= 1; // Hate this, but the position had to be adjusted. - jrm.
  183.         r.bottom -= 1; // Hate this, but the position had to be adjusted. - jrm.
  184.         r.left -= 1;        
  185.         if (mLastVisibleColumn <= 1) 
  186.             if (mColumnCount == 1)
  187.                 iconID = kColumnHiderDisabledIcon;
  188.             else
  189.                 iconID = kColumnHiderHideDisabledIcon;
  190.         else
  191.             if (mLastVisibleColumn == mColumnCount)
  192.                 iconID = kColumnHiderShowDisabledIcon;
  193.             else    
  194.                 iconID = kColumnHiderEnabledIcon;    
  195.         ::PlotIconID(&r, atNone, ttNone, iconID);
  196.     }    
  197. } // LTableHeader::DrawSelf
  198.  
  199. //-----------------------------------
  200. void LTableHeader::DrawColumnBackground(
  201.     const Rect     & inWhere,
  202.     Boolean          inSortColumn)
  203. //    Bevel and fill the column header rectangle. 
  204. //-----------------------------------
  205. {
  206.     Rect r = inWhere;
  207.     r.right -= 1;    // For frame borders
  208.     r.bottom -= 1;
  209.     const SBevelColorDesc& colors = (inSortColumn) ? mSortedBevel : mUnsortedBevel;
  210.      
  211.     UGraphicGizmos::BevelRect(r, 1, colors.topBevelColor, colors.bottomBevelColor);
  212.  
  213.     ::PmForeColor(colors.fillColor);
  214.     ::InsetRect(&r,1,1);
  215.     ::PaintRect(&r);
  216.     ::InsetRect(&r,-1,-1);
  217.     
  218.     ::ForeColor(blackColor);
  219.     ::MoveTo(r.right, r.top);
  220.     ::LineTo(r.right, r.bottom - 1);
  221. }                                    
  222.  
  223. //-----------------------------------
  224. void LTableHeader::AdjustCursor(Point inPortPt,
  225.                             const EventRecord &inMacEvent)
  226. //    Switch the cursor if it's over a column resize area. 
  227. //    
  228. //    We only give subviews a chance to change the cursor
  229. //    if we're not over a resize area.
  230. //-----------------------------------
  231. {
  232. #pragma unused(inMacEvent)
  233.     
  234.     Point        localPt = inPortPt;
  235.     ColumnIndexT        column;
  236.     
  237.     PortToLocalPoint(localPt);
  238.     if (IsInHorizontalSizeArea(localPt, column))
  239.         ::SetCursor(*GetCursor(kHorizontalResizeCursor));
  240.     else
  241.         LView::AdjustCursor(inPortPt, inMacEvent);
  242. } // LTableHeader::AdjustCursor
  243.  
  244. //-----------------------------------
  245. Boolean LTableHeader::IsInHorizontalSizeArea(
  246.     Point             inLocalPt, 
  247.     ColumnIndexT&     outLeftColumn)
  248. //    Returns true if the given local pt is in a column-resize area. 
  249. //    Also returns the index of the column to the left of that area
  250. //    (the column to be resized).
  251. //-----------------------------------
  252. {
  253.     
  254.     SInt16 left = inLocalPt.h + 2;
  255.     SInt16 right = inLocalPt.h - 2;
  256.     
  257.     // Start at the division between the first and second columns
  258.     // Go up to the division between the last visible and the following one,
  259.     // if there is one.
  260.     SInt16 lastColumnToCheck = mLastVisibleColumn;
  261.     if (mColumnCount > mLastVisibleColumn)
  262.         lastColumnToCheck++;
  263.     SColumnData* cRightNeigbor = *mColumnData + 1;
  264.     for (outLeftColumn = 1;
  265.         outLeftColumn < lastColumnToCheck;
  266.         outLeftColumn++,cRightNeigbor++)
  267.     {
  268.         SInt16 columnRightEdge = cRightNeigbor->columnPosition;
  269.         if (left     >= columnRightEdge &&
  270.             right     <= columnRightEdge)
  271.         {
  272.             // OK, it's a column boundary.
  273.             return (cRightNeigbor - 1)->CanResize();
  274.         }
  275.     }
  276.     outLeftColumn = 0xFFFF;    
  277.     return false;
  278. } // LTableHeader::IsInHorizontalSizeArea
  279.  
  280. //-----------------------------------
  281. void LTableHeader::Click(SMouseDownEvent &inEvent)
  282. //    Handles all clicks in the view.  We never pass the click
  283. //    down to subviews. 
  284. //    
  285. //    Handles changing sort column, resizing columns, moving 
  286. //    columns, and showing/hiding columns.
  287. //-----------------------------------
  288. {
  289.     // LPane::Click ususally does this...
  290.     PortToLocalPoint(inEvent.whereLocal);
  291.     
  292.     // Handle column resize
  293.     ColumnIndexT column;
  294.     if ( IsInHorizontalSizeArea(inEvent.whereLocal, column))
  295.     {
  296.         TrackColumnResize(inEvent, column);
  297.     }
  298.     else
  299.     {
  300.         // Handle clicks in the column hiding widget
  301.         if (CanHideColumns() && (inEvent.whereLocal.h > GetHeaderWidth())) 
  302.         {
  303.             if (mColumnCount == 1 && mLastVisibleColumn == 1)
  304.                 return; // no hiding/showing possible.
  305.             // The right arrow hides the rightmost column and the left arrow
  306.             // shows it.
  307.             enum { leftSide, rightSide } side
  308.                 = inEvent.whereLocal.h >= (mFrameSize.width - (kColumnHidingWidgetWidth/2))
  309.                 ? rightSide
  310.                 : leftSide;
  311.             if (mLastVisibleColumn > 1 && side == rightSide)
  312.                 ShowHideRightmostColumn(false); // hide
  313.             else if (mLastVisibleColumn < mColumnCount && side == leftSide)
  314.                 ShowHideRightmostColumn(true); // show.
  315.         }
  316.         else
  317.         {
  318.             // Click in column header, either drag it,  
  319.             // or set it to be the sort column
  320.             
  321.             column = FindColumn(inEvent.whereLocal);
  322.             if (column != 0)
  323.             {
  324.                 if (mLastVisibleColumn > 1 && ::WaitMouseMoved(inEvent.macEvent.where))
  325.                     TrackColumnDrag(inEvent, column);
  326.                 else
  327.                 {
  328.                     Boolean sortReverse = false;
  329.                     if (mReverseModifiers != 0)
  330.                         sortReverse = (inEvent.macEvent.modifiers & mReverseModifiers) != 0;
  331.                     else
  332.                     {
  333.                         // Toggle mode if click again on sorted column.
  334.                         sortReverse = CycleSortDirection ( column );
  335.                     }
  336.                     SetSortedColumn(column, sortReverse);
  337.                 }
  338.             }
  339.         }
  340.     }
  341. } // LTableHeader::Click
  342.  
  343.  
  344. //
  345. // CycleSortDirection
  346. //
  347. // If the user clicks in the header of the already sorted column, they want to reverse
  348. // the direction of the sort. This can be extended to become a tri-state switch that "unsorts"
  349. // the column (revert to natural order) after doing forward/reverse sort, but this routine 
  350. // doesn't do that.
  351. // 
  352. bool
  353. LTableHeader :: CycleSortDirection ( ColumnIndexT & ioColumn )
  354. {
  355.     return (ioColumn == mSortedColumn) ? !mSortedBackwards : mSortedBackwards;
  356.  
  357. } // ToggleSortedColumn
  358.  
  359.  
  360. //-----------------------------------
  361. void LTableHeader::TrackColumnDrag(
  362.     const            SMouseDownEvent& inEvent,
  363.     ColumnIndexT        inColumn)
  364. //    Tracks the dragging of column inColumn, moving it,
  365. //    and redrawing as necessary.
  366. //-----------------------------------
  367. {
  368.     Point        dropPoint;    
  369.     StRegion    dragRgn;
  370.     Rect        dragRect;
  371.     Rect        dragBounds;
  372.     Rect        dragSlop;
  373.     
  374.     if (!FocusDraw()) return;
  375.     
  376.     // Create the rgn we're dragging
  377.     //
  378.     // This is virtualize so subclasses can make a 
  379.     // more interesting rectangle...
  380.     //
  381.     ComputeColumnDragRect(inColumn, dragRect);
  382.     ::RectRgn(dragRgn, &dragRect);
  383.     
  384.     // Compute the bounds and slop of the drag
  385.     CalcLocalFrameRect(dragBounds);
  386.     LocalToGlobalRect(dragBounds);    
  387.     dragSlop = dragBounds;
  388.     ::InsetRect(&dragSlop, -5, -5);
  389.     
  390.     // drag me
  391.     {
  392.         StCurrentPort    portState(LMGetWMgrPort());
  393.         const Rect         wideopen = { 0xFFFF, 0xFFFF, 0x7FFF, 0x7FFF };
  394.         
  395.         ClipRect(&wideopen);
  396.         *(long*)(&dropPoint) = ::DragGrayRgn(
  397.                                     dragRgn, 
  398.                                     inEvent.macEvent.where, 
  399.                                     &dragBounds,
  400.                                     &dragSlop, 
  401.                                     kVerticalConstraint,
  402.                                     NULL);
  403.     
  404.     }
  405.     
  406.     if ((dropPoint.h != (SInt16) 0x8000 || dropPoint.v != (SInt16) 0x8000)    &&
  407.         (dropPoint.h != 0 || dropPoint.v != 0))
  408.     {
  409.         ColumnIndexT    dropColumn;        
  410.     
  411.         // find the column over which we finished the drag
  412.         dropPoint.h += inEvent.whereLocal.h;
  413.         dropPoint.v += inEvent.whereLocal.v;
  414.         
  415.         dropColumn = FindColumn(dropPoint);
  416.         
  417.         // move the column we were dragging
  418.         if (dropColumn != 0 && inColumn != dropColumn) {
  419.             MoveColumn(inColumn, dropColumn);    
  420.         }
  421.     }
  422. } // LTableHeader::TrackColumnDrag
  423.  
  424. //-----------------------------------
  425. void LTableHeader::TrackColumnResize(
  426.     const SMouseDownEvent    &inEvent,
  427.     ColumnIndexT            inLeftColumn    )
  428. //    Tracks resizing of a column, redrawing as necessary.
  429. //-----------------------------------
  430. {
  431.     Rect            dragBoundsRect;
  432.     Rect            slopRect;
  433.     StRegion        dragRgn;
  434.     Rect            dragRect;
  435.         
  436.     CheckVisible(inLeftColumn);
  437.  
  438.     if (!FocusDraw()) return;
  439.     
  440.     
  441.     // Compute the actual region we're dragging
  442.     //
  443.     // This is virtualized so subclasses can reflect the size-change
  444.     // in some other view's space (like down a table column)
  445.     
  446.     SColumnData* cData = GetColumnData(inLeftColumn);
  447.     ComputeResizeDragRect(inLeftColumn, dragRect);
  448.     ::RectRgn(dragRgn, &dragRect);    
  449.     
  450.     // Compute drag bounds and slop
  451.     CalcLocalFrameRect(dragBoundsRect);
  452.     dragBoundsRect.left = (inLeftColumn != 1) ? cData->columnPosition + 4 : 4;
  453.     
  454.     LocalToGlobalRect(dragBoundsRect);
  455.     slopRect = dragBoundsRect;
  456.     InsetRect(&slopRect, -5, -5);
  457.     
  458.     // drag me
  459.     Point dragDiff;    
  460.     {
  461.         StCurrentPort portState(LMGetWMgrPort());
  462.         const Rect wideopen = { 0xFFFF, 0xFFFF, 0x7FFF, 0x7FFF };
  463.         
  464.         ::ClipRect(&wideopen);
  465.  
  466.         // Track the drag, then resize the column if it was a valid drag
  467.         *(long *)&dragDiff = ::DragGrayRgn(
  468.                                 dragRgn, 
  469.                                 inEvent.macEvent.where, 
  470.                                 &dragBoundsRect,
  471.                                 &slopRect, 
  472.                                 kVerticalConstraint,
  473.                                 NULL);
  474.     }
  475.     if ((dragDiff.h != (SInt16) 0x8000 || dragDiff.v != (SInt16) 0x8000)    &&
  476.         (dragDiff.h != 0     || dragDiff.v != 0 ))
  477.     {
  478.         if (cData->CanResizeBy(dragDiff.h))
  479.             ResizeColumn(inLeftColumn, dragDiff.h);
  480.     }
  481. } // LTableHeader::TrackColumnResize
  482.  
  483. //-----------------------------------
  484. void LTableHeader::ResizeColumn(
  485.     ColumnIndexT    inLeftColumn,
  486.     SInt16            inLeftColumnDelta    )
  487. //    Resize inLeftColumn by inLeftColumnDelta.    
  488. //    Columns to the right of inLeftColumn are resized
  489. //    proportionally.
  490. //-----------------------------------
  491. {
  492.     if (inLeftColumnDelta == 0)
  493.         return;
  494.  
  495.     SColumnData* thisColData = GetColumnData(inLeftColumn);
  496.     
  497.     thisColData->columnWidth += inLeftColumnDelta;
  498.     SColumnData* nextColData = thisColData + 1;
  499.     int i = inLeftColumn + 1;
  500.     Boolean oneColumnCanResize = false;
  501.     for (; i <= mLastVisibleColumn; i++, nextColData++)
  502.         if (nextColData->CanResize())
  503.         {
  504.             oneColumnCanResize = true;
  505.             break;
  506.         }
  507.  
  508.     SInt16 newSpace = GetHeaderWidth() - (thisColData->columnPosition + thisColData->columnWidth);
  509.     SInt16 oldSpace = newSpace + inLeftColumnDelta;
  510.  
  511.     // Use proportional distribution if there's a resizable column on the right, AND either
  512.     // we're shrinking the column or there's enough slack in the columns to the right.
  513.     Boolean useProportionalRedistribution = false;
  514.     if (oneColumnCanResize)
  515.     {
  516.         if (inLeftColumnDelta < 0 && inLeftColumn != mLastVisibleColumn)
  517.             useProportionalRedistribution = true;
  518.         else if (inLeftColumnDelta > 0
  519.                 && newSpace >= GetMinWidthOfRange(inLeftColumn + 1, mLastVisibleColumn))
  520.             useProportionalRedistribution = true;
  521.     }
  522.  
  523.     if (useProportionalRedistribution)
  524.     {
  525.         RedistributeSpace(inLeftColumn + 1, mLastVisibleColumn, newSpace, oldSpace);
  526.         ComputeColumnPositions();
  527.         PositionColumnHeaders(false);
  528.         RedrawColumns(inLeftColumn, mLastVisibleColumn);
  529.         return;
  530.     }
  531.     else if (inLeftColumnDelta > 0)
  532.     {
  533.         // This is the case when we resized the column to the rightmost edge, or so
  534.         // far that there is not enough space for all the columns between
  535.         // the resized column and the edge.  To redistribute the space would reduce the sizes
  536.         // of all the resizable columns in this range to zero.  This would be bad (and was
  537.         // bug number 62743).
  538.         
  539.         // Move columns offscreen one at a time until there's enough room for the remainder.
  540.         while (mLastVisibleColumn > inLeftColumn
  541.                 && newSpace < GetWidthOfRange(inLeftColumn + 1, mLastVisibleColumn))
  542.         {
  543.             mLastVisibleColumn--;
  544.         }
  545.     }
  546.     else if (inLeftColumnDelta < 0)
  547.     {
  548.         // Move as many columns onscreen as will fit
  549.         while (mLastVisibleColumn + 1 <= mColumnCount
  550.                 && GetWidthOfRange(inLeftColumn + 1, mLastVisibleColumn + 1) <= newSpace)
  551.         {
  552.             mLastVisibleColumn++;
  553.         }
  554.     }
  555.     // Finally,  expand this column to the right just enough to fit all the
  556.     // columns that we showed or left showing, and then  compute all the positions.
  557.     UInt16 widthsToRight = GetWidthOfRange(inLeftColumn + 1, mLastVisibleColumn);
  558.     thisColData->columnWidth = GetHeaderWidth() - widthsToRight - thisColData->columnPosition;
  559.     ComputeColumnPositions();
  560.     PositionColumnHeaders(false);
  561.     RedrawColumns(inLeftColumn, mLastVisibleColumn);
  562. } // LTableHeader::ResizeColumn
  563.  
  564. //-----------------------------------
  565. UInt16 LTableHeader::GetMinWidthOfRange(
  566.     ColumnIndexT    inFromColumn,
  567.     ColumnIndexT    inToColumn    ) const
  568. //-----------------------------------
  569. {
  570.     Assert_(inFromColumn > 0);
  571.     Assert_(inToColumn > 0);
  572.     if (inToColumn > mLastVisibleColumn)
  573.         inToColumn = mLastVisibleColumn;
  574.     if (inFromColumn > inToColumn)
  575.         return 0;
  576.  
  577.     SColumnData*    cData = &(*mColumnData)[inFromColumn - 1];
  578.     if (inToColumn > mLastVisibleColumn)
  579.         inToColumn = mLastVisibleColumn;
  580.     
  581.     SInt16 minWidth = 0;
  582.     for (int i = inFromColumn; i <= inToColumn; i++, cData++)
  583.     {
  584.         if (cData->CanResize())
  585.             minWidth += SColumnData::kMinWidth;
  586.         else
  587.             minWidth += cData->columnWidth;
  588.     }
  589.     return minWidth;
  590. } // LTableHeader::GetMinWidthOfRange
  591.  
  592. //-----------------------------------
  593. UInt16 LTableHeader::GetWidthOfRange(
  594.     ColumnIndexT    inFromColumn,
  595.     ColumnIndexT    inToColumn    ) const
  596. //-----------------------------------
  597. {
  598.     Assert_(inFromColumn > 0);
  599.     Assert_(inToColumn > 0);
  600.     if (inToColumn > mLastVisibleColumn)
  601.         inToColumn = mLastVisibleColumn;
  602.     if (inFromColumn > inToColumn)
  603.         return 0;
  604.  
  605.     SColumnData*    cData = &(*mColumnData)[inFromColumn - 1];
  606.     if (inToColumn > mLastVisibleColumn)
  607.         inToColumn = mLastVisibleColumn;
  608.     
  609.     SInt16 width = 0;
  610.     for (int i = inFromColumn; i <= inToColumn; i++, cData++)
  611.         width += cData->columnWidth;
  612.     return width;
  613. } // LTableHeader::GetMinWidthOfRange
  614.  
  615. //-----------------------------------
  616. void LTableHeader::SetRightmostVisibleColumn(ColumnIndexT inLastDesiredColumn)
  617. //-----------------------------------
  618. {
  619.     CheckLegal(inLastDesiredColumn);
  620.     ColumnIndexT currentLastVisible = mLastVisibleColumn;
  621.     while (mLastVisibleColumn < inLastDesiredColumn)
  622.     {
  623.         ShowHideRightmostColumn(true);
  624.         if (mLastVisibleColumn == currentLastVisible)
  625.             return; // prevent infinite loop
  626.         currentLastVisible = mLastVisibleColumn;
  627.     }
  628.     while (mLastVisibleColumn > inLastDesiredColumn)
  629.     {
  630.         ShowHideRightmostColumn(false);
  631.         if (mLastVisibleColumn == currentLastVisible)
  632.             return; // prevent infinite loop
  633.         currentLastVisible = mLastVisibleColumn;
  634.     }
  635. } // LTableHeader::SetRightmostVisibleColumn
  636.  
  637. //-----------------------------------
  638. void LTableHeader::ShowHideRightmostColumn(Boolean inShow)
  639. //    Show or hide the rightmost column.    
  640. //    mLastVisibleColumn is the rightmost visible column.
  641. //-----------------------------------
  642. {
  643.     Assert_((inShow && mLastVisibleColumn < mColumnCount) ||
  644.             (!inShow && mLastVisibleColumn > 1));
  645.                     
  646.     ColumnIndexT savedLastVisibleColumn = mLastVisibleColumn;
  647.     Boolean ok;
  648.     
  649.     UInt16 headerWidth = GetHeaderWidth();
  650. #ifdef DEBUG
  651.     SColumnData*    cData = GetColumnData(1);
  652.     SInt16 width = 0;
  653.     for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
  654.         width += cData->columnWidth;
  655.     Assert_(width == headerWidth);
  656. #endif // DEBUG
  657.     UInt16 newWidth, oldWidth;
  658.     UInt16 colWidth;
  659.     if (inShow)
  660.     {
  661.         mLastVisibleColumn++;
  662.         SColumnData* column = GetColumnData(mLastVisibleColumn);
  663.         if (column->columnWidth < SColumnData::kMinWidth)
  664.             column->columnWidth = SColumnData::kMinWidth;
  665.         colWidth = column->columnWidth;
  666.         if (colWidth < headerWidth / 3)
  667.         {
  668.             // Move new column in at full size, shrinking existing columns to make room
  669.             oldWidth = headerWidth;
  670.             newWidth = headerWidth - colWidth;
  671.             ok = RedistributeSpace(1, mLastVisibleColumn - 1, newWidth, oldWidth);
  672.         }
  673.         else
  674.         {
  675.             // Move them all in proportionally.
  676.             oldWidth = headerWidth + colWidth;
  677.             newWidth = headerWidth;
  678.             ok = RedistributeSpace(1, mLastVisibleColumn, newWidth, oldWidth);
  679.         }
  680.     }
  681.     else
  682.     {
  683.         newWidth = headerWidth;
  684.         colWidth = GetColumnWidth(mLastVisibleColumn);    // col being hidden
  685.         mLastVisibleColumn--;
  686.         oldWidth = headerWidth - colWidth;
  687.         ok = RedistributeSpace(1, mLastVisibleColumn, newWidth, oldWidth);
  688.     }
  689.     if (!ok)
  690.     {
  691.         // It didn't work.  Restore the original visible column count
  692.         // So undo!  There are probably no resizable columns to allow the change.
  693.         mLastVisibleColumn = savedLastVisibleColumn;
  694.         return;
  695.     }
  696.     ComputeColumnPositions();
  697.     PositionColumnHeaders(false);
  698.     RedrawColumns(1, mLastVisibleColumn);
  699. } // LTableHeader::ShowHideRightmostColumn
  700.  
  701. //-----------------------------------
  702. UInt16 LTableHeader::SumOfVisibleResizableWidths() const
  703. //----------------------------------- 
  704. {
  705.     UInt16 result = 0;
  706.     SColumnData*    cData = *mColumnData;
  707.     // Using weighted proportions.  Set d = the total of the proportion
  708.     // values for the VISIBLE columns
  709.     for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
  710.     {
  711.         if (cData->CanResize())
  712.             result += cData->columnWidth;
  713.     }
  714.     return result;
  715. } // LTableHeader::SumOfVisibleResizableWidths
  716.  
  717. //-----------------------------------
  718. Boolean LTableHeader::RedistributeSpace(
  719.     ColumnIndexT    inFromColumn,
  720.     ColumnIndexT    inToColumn,
  721.     SInt16    inNewSpace,
  722.     SInt16    inOldSpace)
  723. //    Given old and new amounts of horizontal space, this resizes a range of columns
  724. //  such that they each have the same percentage of space in the new space as they
  725. //    did in the old space.     
  726. //    
  727. //    Fixed-width columns are not resized, of course.
  728. //    
  729. //    Assumes all resizable column widths are in weighted proportion.
  730. //    Converts all these widths to absolute pixel values in such a way that the
  731. //    "visible" columns take up the full image size.  The proportions are maintained, so
  732. //    there's no need to convert to percentages: there's nothing magic about the number
  733. //    100.
  734. //    
  735. //    If the last visible column does not reach the right edge exactly, the last visible
  736. //    resizable column will be extended or shrunk to make it do so. (this is largely
  737. //    due to round-off errors in our arithmetic).    
  738. //-----------------------------------
  739. {
  740.     CheckVisible(inFromColumn);
  741.     CheckVisible(inToColumn);
  742.     Assert_(inFromColumn <= inToColumn);
  743.     Assert_(inOldSpace > 0);
  744.     if (inOldSpace <= 0 || inNewSpace <= 0)
  745.         return false;
  746.  
  747.     SInt16 fixedWidth = GetFixedWidthInRange(inFromColumn, inToColumn);
  748.  
  749.     // Loop through columns, resizing them in proportion.  This keeps the proportional
  750.     // sizes equal.  While looping, note the last resizable column in the range, which will
  751.     // be used for adjustments.  Also tally the new resizable total width in the range.
  752.     SColumnData* cData = GetColumnData(inFromColumn);
  753.     UInt16 sumOfWidths = 0;                                // Tally to be used after loop completes
  754.     float newSpaceFloat = inNewSpace - fixedWidth;        // Take invariant conversion out of the loop
  755.     float oldSpaceFloat = inOldSpace - fixedWidth;        // ...
  756.     for (int i = inFromColumn; i <= inToColumn; i++, cData++)
  757.     {
  758.         if (cData->CanResize())
  759.         {
  760.             // Resize this column to take the same proportional amount of space, rounding
  761.             // as appropriate
  762.             SInt16 newWidth = (newSpaceFloat * cData->columnWidth + 0.5) / oldSpaceFloat;
  763.             if (newWidth >= SColumnData::kMinWidth || newWidth > cData->columnWidth)
  764.                 cData->columnWidth = newWidth;
  765.         }
  766.         // For columns in range (only), add to tally.
  767.         sumOfWidths += cData->columnWidth;
  768.     }
  769.     // Adjust the resizable columns to remove any rounding error.  We used to add this to the
  770.     // last column, but that would make it get bigger, and bigger, and bigger,...
  771.     // So now, go around to each resizable column in rotation and make it eat some of the
  772.     // error, until it all fits exactly.
  773.     Int16 roundingError = inNewSpace - sumOfWidths;
  774.     if (roundingError == 0)
  775.         return true; // perfick!
  776.     if (sumOfWidths == inOldSpace)
  777.         return false; // Oh, diddums.  No resizable columns
  778.     // OK, if we get this far, then some columns were able to resize
  779.     // (sumOfWidths != inOldSpace), and there's merely a rounding error to adjust.
  780.     int signedUnit = roundingError < 0 ? -1 : +1;
  781. #ifdef DEBUG
  782.     int maxRound =  (inToColumn - inFromColumn + 2) / 2;
  783.     Assert_(roundingError * signedUnit <= maxRound);
  784. #endif
  785.     static int rovingIndex = 0;
  786.     const Int16 initialError = roundingError;
  787.     const int initialIndex = rovingIndex;
  788.     while (roundingError)
  789.     {
  790.         rovingIndex++;
  791.         if (rovingIndex > inToColumn)
  792.             rovingIndex = inFromColumn;
  793.         if (initialIndex == rovingIndex && initialError == roundingError)
  794.         {
  795.             Assert_(false); // not making progress!  No resizable columns?
  796.             return false;
  797.         }
  798.         cData = GetColumnData(rovingIndex);
  799.         if (cData->CanResizeBy(signedUnit))
  800.         {
  801.             cData->columnWidth += signedUnit;
  802.             roundingError -= signedUnit;
  803.         }
  804.     }
  805.     return true;
  806. } // LTableHeader::RedistributeSpace
  807.                                     
  808. //-----------------------------------
  809. SInt16 LTableHeader::GetFixedWidthInRange(
  810.     ColumnIndexT    inFromColumn,
  811.     ColumnIndexT    inToColumn    )
  812. //    Returns the horizontal space taken by all fixed-width
  813. //    columns in a given column range (inclusive).
  814. //-----------------------------------
  815. {
  816.     CheckVisible(inFromColumn);
  817.     CheckVisible(inToColumn);
  818.     Assert_(inFromColumn <= inToColumn);
  819.  
  820.     SColumnData*    cData = GetColumnData(inFromColumn);
  821.     SInt16 fixedWidth = 0;
  822.     if (inToColumn > mLastVisibleColumn)
  823.         inToColumn = mLastVisibleColumn;
  824.     
  825.     for (int i = inFromColumn; i <= inToColumn; i++, cData++)
  826.     {
  827.         if (!cData->CanResize())
  828.             fixedWidth += cData->columnWidth;
  829.     }
  830.     return fixedWidth;
  831. } // LTableHeader::GetFixedWidthInRange
  832.  
  833. //-----------------------------------
  834. UInt16 LTableHeader::GetHeaderWidth() const
  835. //    Returns the width of all the visible columns,
  836. //    not including the column hiding widget.
  837. //-----------------------------------
  838. {
  839.     Int16 result = mImageSize.width - kColumnHidingWidgetWidth;
  840.     return result < 0 ? 0 : (UInt16)result;
  841. }
  842.                         
  843. //-----------------------------------
  844. void LTableHeader::MoveColumn(ColumnIndexT inColumn, ColumnIndexT inMoveTo)
  845. //    If inColumn > inMoveTo, moves inColumn BEFORE inMoveTo.
  846. //    If inColumn < inMoveTo, moves inColumn AFTER inMoveTo.
  847. //-----------------------------------
  848. {
  849.     SColumnData    *    cData         = *mColumnData,
  850.                 *    shiftFrom, 
  851.                 *    shiftTo;
  852.     SColumnData        swap;
  853.     Boolean            adjustedSort = false;
  854.     
  855.     // save off the data for the column we're moving
  856.     ::BlockMoveData(&(cData[inColumn-1]), &swap, sizeof(SColumnData));
  857.     
  858.     // Handle case where we're moving the sorted column        
  859.     if (mSortedColumn == inColumn)
  860.     {
  861.         adjustedSort     = true;
  862.         mSortedColumn     = inMoveTo;
  863.     }
  864.     
  865.     //
  866.     // Figure out which direction we're moving the column
  867.     // and how we need to shift the data in the columndata array
  868.     //
  869.     SInt32 delta = inColumn - inMoveTo;
  870.     if (delta > 0)
  871.     {
  872.         shiftFrom    = &(cData[inMoveTo-1]);
  873.         shiftTo        = shiftFrom + 1;
  874.         
  875.         if (!adjustedSort && mSortedColumn >= inMoveTo && mSortedColumn < inColumn)
  876.             mSortedColumn += 1;
  877.     }
  878.     else
  879.     {
  880.         delta         = -delta;
  881.         shiftTo     = &(cData[inColumn-1]);
  882.         shiftFrom    = shiftTo + 1;
  883.         
  884.         if (!adjustedSort && mSortedColumn > inColumn && mSortedColumn <=inMoveTo)
  885.             mSortedColumn -= 1;
  886.     }
  887.     
  888.     // Shift the data in the columnData array and then copy in the data
  889.     // of the column we're moving
  890.     ::BlockMoveData(shiftFrom, shiftTo, (delta*sizeof(SColumnData)));
  891.     ::BlockMoveData(&swap, &(cData[inMoveTo-1]), sizeof(SColumnData));
  892.     
  893.     // resposition everything
  894.     ComputeColumnPositions();
  895.     PositionColumnHeaders(false);
  896.     
  897.     
  898.     if (inColumn > inMoveTo)
  899.         RedrawColumns(inMoveTo, inColumn);
  900.     else
  901.         RedrawColumns(inColumn, inMoveTo);
  902. } // LTableHeader::MoveColumn
  903.     
  904. //-----------------------------------
  905. void LTableHeader::ComputeColumnPositions()
  906. //    Computes the horizontal position of each column, assuming 
  907. //    each column's width is valid in pixels.
  908. //    
  909. //    If the rightmost visible column doesn't fill the column area, 
  910. //    it will be extended to do so, UNLESS it is a fixed-width column.
  911. //-----------------------------------
  912. {
  913.     UInt16 headerWidth = GetHeaderWidth();
  914.     if (headerWidth == 0)
  915.         return;
  916.     // Compute each visible column's position
  917.     SColumnData* cData = *mColumnData;
  918.     SInt16 accumulator = 0;
  919.     for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
  920.     {
  921.         cData->columnPosition = accumulator;
  922.         accumulator += cData->columnWidth;
  923.     }
  924.     
  925.     // If the last column is resizable, expand or shrink it
  926.     // as necessary to reach the edge of the header
  927.     cData--;
  928.     if (cData->CanResize())
  929.     {
  930.         if ( (cData->columnPosition + cData->columnWidth) != headerWidth)
  931.             cData->columnWidth = headerWidth - cData->columnPosition;
  932.     }
  933. } // LTableHeader::ComputeColumnPositions
  934.  
  935. //-----------------------------------
  936. void LTableHeader::ComputeResizeDragRect(ColumnIndexT        inLeftColumn,
  937.                                         Rect    &    outDragRect        )
  938. //    Computes rectangle to be dragged when resizing a column.
  939. //-----------------------------------
  940. {
  941.     outDragRect.top     =     0;
  942.     outDragRect.bottom    =    mFrameSize.height + 100;
  943.     outDragRect.left    =    GetColumnPosition(inLeftColumn+1) - 1;
  944.     outDragRect.right    =    outDragRect.left + 1;
  945.     
  946.     LocalToGlobalRect(outDragRect);
  947. }                                        
  948.  
  949. //-----------------------------------
  950. void LTableHeader::ComputeColumnDragRect(
  951.     ColumnIndexT        inColumn,
  952.     Rect            &outDragRect)
  953. //-----------------------------------
  954. {
  955.     LocateColumn(inColumn, outDragRect);
  956.     outDragRect.bottom += 100;    
  957.     LocalToGlobalRect(outDragRect);
  958. }                                        
  959.  
  960. //-----------------------------------
  961. void LTableHeader::SetSortedColumn(
  962.     ColumnIndexT     inColumn, 
  963.     Boolean            inReverse,
  964.     Boolean            inRefresh    )
  965. //-----------------------------------
  966. {
  967.     if (!CanColumnSort(inColumn))
  968.         return;
  969.     if (inColumn == mSortedColumn && inReverse == mSortedBackwards)
  970.         return;
  971.     SInt16 oldSorted = mSortedColumn;
  972.     mSortedColumn = inColumn;
  973.     mSortedBackwards = inReverse;
  974.     if (inRefresh && FocusDraw())
  975.     {    
  976.         
  977.         
  978.         if (oldSorted != 0) {
  979.             RedrawColumns(oldSorted, oldSorted);
  980.         }
  981.         
  982.         if (mSortedColumn != 0) {
  983.             RedrawColumns(mSortedColumn, mSortedColumn);
  984.         }
  985.     }
  986.     // Tell any listeners that the sort column changed
  987.     LTableHeader::SortChange    changeRecord;
  988.     
  989.     changeRecord.sortColumnID     = GetColumnPaneID(mSortedColumn);
  990.     changeRecord.sortColumn        = mSortedColumn;
  991.     changeRecord.reverseSort    = mSortedBackwards;    
  992.     BroadcastMessage(msg_SortedColumnChanged, &changeRecord);
  993. }
  994.  
  995. //-----------------------------------
  996. void LTableHeader::SetWithoutSort(
  997.     ColumnIndexT     inColumn, 
  998.     Boolean            inReverse,
  999.     Boolean            inRefresh    )
  1000. //-----------------------------------
  1001. {
  1002.     if (!CanColumnSort(inColumn))
  1003.         return;
  1004.     if (inColumn == mSortedColumn && inReverse == mSortedBackwards)
  1005.         return;
  1006.     SInt16 oldSorted = mSortedColumn;
  1007.     mSortedColumn = inColumn;
  1008.     mSortedBackwards = inReverse;
  1009.     if( inRefresh )
  1010.         Refresh();    
  1011. }
  1012.  
  1013. //-----------------------------------
  1014. void LTableHeader::SimulateClick(PaneIDT inID)
  1015. //-----------------------------------
  1016. {
  1017.     PaneIDT result; 
  1018.     
  1019.     ColumnIndexT index = GetSortedColumn(result);
  1020.     
  1021.     if ( (result == inID) || !IsEnabled() || !IsColumnVisible(inID)) return;
  1022.     
  1023.     SetSortedColumn(ColumnFromID(inID), IsSortedBackwards(), true);
  1024. }
  1025.  
  1026. //-----------------------------------
  1027. void LTableHeader::SetSortOrder(Boolean inReverse)
  1028. //-----------------------------------
  1029. {
  1030.     PaneIDT result; 
  1031.     
  1032.     ColumnIndexT index = GetSortedColumn(result);
  1033.     
  1034.     if ( !result || !IsEnabled() || !IsColumnVisible(result)) return;
  1035.     
  1036.     SetSortedColumn(index, inReverse, true);
  1037. }
  1038.  
  1039. //-----------------------------------
  1040. void LTableHeader::RedrawColumns(ColumnIndexT inFrom, ColumnIndexT inTo)
  1041. //    Immediately redraws the given column range.
  1042. //    We don't do update events so we can avoid the flicker
  1043. //    of update rgn being erased to white.
  1044. //-----------------------------------
  1045. {
  1046.     UInt16 headerWidth = GetHeaderWidth();
  1047.     
  1048.     // Compute the top-left of the refresh area
  1049.     Rect r;
  1050.     LocateColumn(inFrom, r);
  1051.     LocalToPortPoint(topLeft(r));
  1052.     
  1053.     // Get the bottom right of the refresh area
  1054.     if (inFrom == inTo)
  1055.     {
  1056.         LocalToPortPoint(botRight(r));
  1057.     }
  1058.     else
  1059.     {
  1060.         Rect right;
  1061.         
  1062.         if (inTo <= mLastVisibleColumn)
  1063.         {
  1064.             LocateColumn(inTo, right);
  1065.             LocalToPortPoint(botRight(right));    
  1066.         }
  1067.         else
  1068.         {
  1069.             right.right = headerWidth;
  1070.             LocalToPortPoint(botRight(right));
  1071.         }
  1072.         
  1073.         r.bottom = right.bottom;
  1074.         r.right  = right.right;
  1075.     }
  1076.  
  1077.     // Redraw
  1078.     StRegion rgn;
  1079.     ::RectRgn(rgn, &r);    
  1080.     Draw(rgn);
  1081. }
  1082.  
  1083. //-----------------------------------
  1084. void LTableHeader::LocateColumn( ColumnIndexT inColumn,
  1085.                              Rect & outWhere) const
  1086. //    Computes the bounding rect of the given column 
  1087. //    in local coordinates.
  1088. //-----------------------------------
  1089. {
  1090.     outWhere.top     = 0;
  1091.     outWhere.bottom = mFrameSize.height;
  1092.     
  1093.     outWhere.left     = GetColumnPosition(inColumn);
  1094.     outWhere.right    = outWhere.left + GetColumnWidth(inColumn);
  1095. }
  1096.  
  1097. //-----------------------------------
  1098.  LTableHeader::ColumnIndexT LTableHeader::FindColumn( Point inLocalPoint ) const
  1099. //    Given a local point, returns the index of the
  1100. //    column containing the point.  Returns 0 for no
  1101. //    column.
  1102. //-----------------------------------
  1103. {
  1104.     int    i;
  1105.  
  1106.     if (inLocalPoint.h >= 0 && inLocalPoint.h <= GetHeaderWidth())
  1107.     {
  1108.         for (i = mLastVisibleColumn; i > 0; i--)
  1109.         {
  1110.             if (inLocalPoint.h >= GetColumnPosition(i))
  1111.                 return i;
  1112.         }
  1113.     }
  1114.     return 0;
  1115. }
  1116.  
  1117. //-----------------------------------
  1118. void LTableHeader::InvalColumn( ColumnIndexT inColumn)
  1119. //    Invalidates a column header area.
  1120. //-----------------------------------
  1121. {
  1122.     if (FocusDraw())
  1123.     {
  1124.         Rect    r;
  1125.         
  1126.         LocateColumn(inColumn, r);
  1127.         LocalToPortPoint(topLeft(r));
  1128.         LocalToPortPoint(botRight(r));
  1129.         InvalPortRect(&r);
  1130.     }
  1131. }
  1132.  
  1133. //-----------------------------------
  1134. void LTableHeader::PositionColumnHeaders(Boolean inRefresh)
  1135. //    Positions all the column header panes to be aligned
  1136. //    with the current column positions.
  1137. //-----------------------------------
  1138. {
  1139.     if (GetHeaderWidth() == 0)
  1140.         return;
  1141.     int i;
  1142.     for (i = 1; i <= mLastVisibleColumn; i++)
  1143.     {
  1144.         PositionOneColumnHeader(i, inRefresh);
  1145.         LPane* headerPane = GetColumnPane(i);
  1146.         if (headerPane)
  1147.         {
  1148.             if (GetColumnData(i)->CanDisplayTitle())
  1149.                 headerPane->Show();
  1150.             else
  1151.                 headerPane->Hide();
  1152.             if (!inRefresh)
  1153.                 headerPane->DontRefresh(true);
  1154.         }
  1155.     }
  1156.     for ( ; i <= mColumnCount; i++)
  1157.     {
  1158.         LPane* headerPane = GetColumnPane(i);
  1159.         if (headerPane)
  1160.         {
  1161.             headerPane->Hide();
  1162.             if (!inRefresh)
  1163.                 headerPane->DontRefresh(true);
  1164.         }
  1165.     }
  1166. } // LTableHeader::PositionColumnHeaders
  1167.  
  1168. //-----------------------------------
  1169. void LTableHeader::PositionOneColumnHeader( ColumnIndexT inColumn, Boolean inRefresh)
  1170. //    Positions a column header pane to be aligned with the its 
  1171. //    column position.
  1172. //    
  1173. //    The column header pane is sized to the width of the column. 
  1174. //    Its vertical position is not changed.
  1175. //-----------------------------------
  1176. {
  1177.     Rect columnBounds;
  1178.     LocateColumn(inColumn, columnBounds);
  1179.     LPane* headerPane = GetColumnPane(inColumn);
  1180.     
  1181.     // Position the pane horizontally within the column
  1182.     if (headerPane)
  1183.     {
  1184.         SDimension16    paneSize;                
  1185.         headerPane->GetFrameSize(paneSize);
  1186.  
  1187.         SPoint32 paneLocation;
  1188.         headerPane->GetFrameLocation(paneLocation);
  1189.  
  1190.         Int32 horizDelta
  1191.             = (columnBounds.left + kColumnMargin) - paneLocation.h + mFrameLocation.h;
  1192.         Int32 paneWidth    = columnBounds.right - (columnBounds.left + (kColumnMargin * 2));
  1193.                 
  1194.         headerPane->ResizeFrameTo(paneWidth, paneSize.height, inRefresh);    
  1195.         headerPane->MoveBy( horizDelta, 0, inRefresh);                                        
  1196.     }    
  1197. } // LTableHeader::PositionOneColumnHeader
  1198.  
  1199. //-----------------------------------
  1200. PaneIDT LTableHeader::GetColumnPaneID(ColumnIndexT inColumn) const
  1201. //    Returns the Pane id of the given column's header-pane.    
  1202. //    Returns 0 if there is no header-pane.
  1203. //-----------------------------------
  1204. {
  1205.     CheckLegalHigh(inColumn);
  1206.     if (inColumn != 0)
  1207.         return (GetColumnData(inColumn)->GetPaneID());
  1208.     return 0;
  1209. }
  1210.  
  1211. //-----------------------------------
  1212. LPane *    LTableHeader::GetColumnPane(ColumnIndexT inColumn)
  1213. //-----------------------------------
  1214. {
  1215.     LView    *super = GetSuperView();
  1216.     
  1217.     CheckLegal(inColumn);
  1218.     Assert_(super != NULL);
  1219.     
  1220.     PaneIDT id = GetColumnPaneID(inColumn);
  1221.     
  1222.     if (id != 0)
  1223.         return super->FindPaneByID(id);
  1224.     return NULL;
  1225. }
  1226.  
  1227. //-----------------------------------
  1228. SInt16 LTableHeader::GetColumnWidth(ColumnIndexT inColumn) const
  1229. //-----------------------------------
  1230. {
  1231.     CheckLegal(inColumn);
  1232.     // widths are always in absolute when this is called.
  1233.     return GetColumnData(inColumn)->columnWidth;
  1234. }
  1235.  
  1236. //-----------------------------------
  1237. SInt16 LTableHeader::GetColumnPosition(ColumnIndexT inColumn) const
  1238. //-----------------------------------
  1239. {
  1240.     CheckLegal(inColumn);
  1241.     return ( GetColumnData(inColumn)->columnPosition);
  1242. }
  1243.  
  1244. //-----------------------------------
  1245. Boolean    LTableHeader::CanColumnResize(ColumnIndexT inColumn) const
  1246. //-----------------------------------
  1247. {
  1248.     CheckLegal(inColumn);
  1249.  
  1250.     // check all of the columns to the right to see if they can be resized
  1251.     // if they can't be resized we should NOT be able to resize this column
  1252.     // example:  | resizCol1 | resizCol2 | fixedCol3 |
  1253.     // if we are working off:            ^
  1254.     // we shouldn't be able to resize (shrink) here because there is nowhere
  1255.     // for any extra space to go and we don't erase where column was.
  1256.     // note that inColumn is 1-based but mColumnData (within loop) is 0-based
  1257.     
  1258.     SColumnData* cData = GetColumnData(CountVisibleColumns());
  1259.     Boolean canFurtherRightResize = false;
  1260.     for (int i = CountVisibleColumns(); i > inColumn; i--,cData--)
  1261.     {
  1262.         if (cData->CanResize())
  1263.         {
  1264.             canFurtherRightResize = true;
  1265.             break;
  1266.         }
  1267.     }
  1268.     return canFurtherRightResize && GetColumnData(inColumn)->CanResize();
  1269. } // LTableHeader::CanColumnResize
  1270.  
  1271. //-----------------------------------
  1272. Boolean    LTableHeader::CanColumnSort(ColumnIndexT inColumn) const
  1273. //-----------------------------------
  1274. {
  1275.     CheckLegal(inColumn);
  1276.     return GetColumnData(inColumn)->CanSort();
  1277. }
  1278.  
  1279. //-----------------------------------
  1280. LTableHeader::ColumnIndexT LTableHeader::GetSortedColumn(PaneIDT &outHeaderPane) const
  1281. //-----------------------------------
  1282.     outHeaderPane = GetColumnPaneID(mSortedColumn);
  1283.     return mSortedColumn; 
  1284. }
  1285.  
  1286. //-----------------------------------
  1287. LTableHeader::ColumnIndexT LTableHeader::ColumnFromID(PaneIDT inPaneID) const
  1288. //-----------------------------------
  1289.     SColumnData* c = *mColumnData;
  1290.     for (int i = 1; i <= mColumnCount; i++,c++)
  1291.         if (c->paneID == inPaneID)
  1292.             return i;
  1293.     return 0;
  1294. }
  1295.  
  1296. //-----------------------------------
  1297. Boolean LTableHeader::CanHideColumns() const
  1298. //-----------------------------------
  1299. {
  1300.      return mHeaderFlags & kHeaderCanHideColumns == kHeaderCanHideColumns;
  1301. }
  1302.  
  1303. //-----------------------------------
  1304. void LTableHeader::WriteColumnState( LStream * inStream)    
  1305. //    Writes out the current column state with column
  1306. //    widths stored as percentages (except for fixed-width)
  1307. //    columns.
  1308. //-----------------------------------
  1309. {
  1310.     Assert_(mColumnData != NULL);
  1311.     
  1312.     HeaderFlagsT flags = 0;
  1313.     
  1314.     if ( mSortedBackwards ) flags |= cFlagHeaderSortedBackwards;
  1315.     
  1316.     *inStream << mColumnCount;
  1317.     *inStream << mLastVisibleColumn;
  1318.     *inStream << mSortedColumn;
  1319.     *inStream << (HeaderFlagsT) flags;            // pad
  1320.     
  1321.     UInt32    dataSize = mColumnCount * sizeof(SColumnData);
  1322.     
  1323.     StHandleLocker locker((Handle)mColumnData);
  1324.     
  1325.     // write out the widths
  1326.     inStream->PutBytes(*mColumnData, dataSize);
  1327.  
  1328. } // LTableHeader::WriteColumnState
  1329.  
  1330. //-----------------------------------
  1331. void LTableHeader::ReadColumnState( LStream * inStream, Boolean inMoveHeaders)        
  1332. //    Reads in column state for the header, converts the column widths
  1333. //    to absolute pixel values, computes column positions, and optionally
  1334. //    positions the column header panes.    
  1335. //-----------------------------------
  1336. {
  1337.     *inStream >> mColumnCount;
  1338.     *inStream >> mLastVisibleColumn;
  1339.     *inStream >> mSortedColumn;
  1340.     HeaderFlagsT flags;
  1341.     *inStream >> flags;
  1342.     mSortedBackwards = (flags & cFlagHeaderSortedBackwards) != 0;
  1343.  
  1344. //    Assert_(mColumnCount > 0);
  1345.     // Columns for RDF views are taken from the RDF database as opposed to
  1346.     // stored in a resource. Therefore allow ReadColumnState to handle case
  1347.     // where there are no columns specified in 'Cols' resource
  1348.     if (mColumnCount > 0)
  1349.     {
  1350.         CheckLegal(mLastVisibleColumn);
  1351.         CheckLegalHigh(mSortedColumn);
  1352.  
  1353.         UInt32    dataSize = mColumnCount * sizeof(SColumnData);
  1354.     
  1355.         if (mColumnData != NULL) ::DisposeHandle((Handle)mColumnData);
  1356.         mColumnData = (SColumnData **) ::NewHandle(dataSize);
  1357.         ThrowIfNULL_(mColumnData);
  1358.  
  1359.         StHandleLocker locker((Handle)mColumnData);
  1360.         inStream->GetBytes(*mColumnData, dataSize);
  1361.  
  1362.         ConvertWidthsToAbsolute();
  1363.         ComputeColumnPositions();
  1364.         
  1365.         if (inMoveHeaders)
  1366.             PositionColumnHeaders(true);
  1367.     }
  1368.         
  1369.     Refresh();
  1370. } // LTableHeader::ReadColumnState
  1371.  
  1372. //-----------------------------------
  1373. void LTableHeader::ConvertWidthsToAbsolute()
  1374. //-----------------------------------
  1375. {    
  1376.     SColumnData*    cData = *mColumnData;
  1377.     UInt16 oldSpace = 0;
  1378.     for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
  1379.         oldSpace += cData->columnWidth;
  1380.     RedistributeSpace(1, mLastVisibleColumn, GetHeaderWidth(), oldSpace);
  1381. } //  LTableHeader::ConvertWidthsToAbsolute
  1382.  
  1383. //-----------------------------------
  1384. void LTableHeader::ResizeFrameBy(
  1385.     Int16    inWidthDelta,
  1386.     Int16    inHeightDelta,
  1387.     Boolean    inRefresh)
  1388. //    When the frame is resized, we resize our image to match,
  1389. //    first converting to percentages so that we can resize
  1390. //    our columns proportionally.
  1391. //-----------------------------------
  1392. {
  1393.     ResizeImageBy(    inWidthDelta, 
  1394.                     inHeightDelta, 
  1395.                     inRefresh    );
  1396.     if ( mColumnCount != 0 )
  1397.     {                                    
  1398.         // Move the columns and column headers
  1399.         ConvertWidthsToAbsolute();
  1400.         ComputeColumnPositions();
  1401.         PositionColumnHeaders();
  1402.     }    
  1403.     LView::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh);
  1404. }                                
  1405.  
  1406. //-----------------------------------
  1407. static void LocalToGlobalRect(Rect& r)
  1408. //-----------------------------------
  1409. {
  1410.     ::LocalToGlobal((Point *) &(r.top));
  1411.     ::LocalToGlobal((Point *) &(r.bottom));
  1412. }
  1413.