home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-04-08 | 41.7 KB | 1,415 lines |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * The contents of this file are subject to the Netscape Public License
- * Version 1.0 (the "NPL"); you may not use this file except in
- * compliance with the NPL. You may obtain a copy of the NPL at
- * http://www.mozilla.org/NPL/
- *
- * Software distributed under the NPL is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
- * for the specific language governing rights and limitations under the
- * NPL.
- *
- * The Initial Developer of this code under the NPL is Netscape
- * Communications Corporation. Portions created by Netscape are
- * Copyright (C) 1998 Netscape Communications Corporation. All Rights
- * Reserved.
- */
-
- /*
-
- Created 3/18/96 - Tim Craycroft (tj, x3672)
-
- To Do:
-
- */
-
-
- // PowerPlant
- #include <URegions.h>
-
- // Mac Lib
- #include "UserInterface_Defines.h"
- #include "UGraphicGizmos.h"
- #include "LTableHeader.h"
- #include "CSimpleDividedView.h"
-
- #include <math.h>
-
- // Flags
-
- typedef UInt16 HeaderFlagsT;
- static const HeaderFlagsT cFlagHeaderSortedBackwards = 0x0001;
-
- // Move me somewhere useful !!!
- static void LocalToGlobalRect(Rect& r);
-
- //======================================
- class StCurrentPort
- //======================================
- {
- public:
- StCurrentPort(GrafPtr inPort) { mPort = UQDGlobals::GetCurrentPort(); SetPort((GrafPtr)inPort); }
- ~StCurrentPort() { SetPort(mPort); }
-
- private:
- GrafPtr mPort;
- };
-
- //-----------------------------------
- LTableHeader::LTableHeader( LStream *inStream )
- : LView(inStream), LBroadcaster()
- //-----------------------------------
- {
- ResIDT theBevelTraitsID;
-
- mColumnData = NULL;
- mColumnCount = mLastVisibleColumn = 0;
-
- *inStream >> mHeaderFlags;
-
- *inStream >> theBevelTraitsID;
- UGraphicGizmos::LoadBevelTraits(theBevelTraitsID, mSortedBevel);
- *inStream >> theBevelTraitsID;
- UGraphicGizmos::LoadBevelTraits(theBevelTraitsID, mUnsortedBevel);
-
- *inStream >> mReverseModifiers;
-
- *inStream >> mColumnListResID;
-
- ResizeImageTo(mFrameSize.width, mFrameSize.height, false);
- }
-
- //-----------------------------------
- LTableHeader::~LTableHeader()
- //-----------------------------------
- {
- if (mColumnData != NULL) ::DisposeHandle( (Handle) mColumnData);
- mColumnData = NULL;
- }
-
- //-----------------------------------
- void LTableHeader::FinishCreateSelf()
- //-----------------------------------
- {
- // Load up the column data
- Handle columnData = ::GetResource('Cols', mColumnListResID);
- ThrowIfNULL_(columnData);
- ::DetachResource((Handle)columnData);
-
- // LHandleStream will dispose of columnData
- LHandleStream streamMe(columnData);
- ReadColumnState(&streamMe, false);
-
- // position the column header panes
- LView::FinishCreateSelf();
- PositionColumnHeaders(true);
- }
-
- //-----------------------------------
- void LTableHeader::DrawSelf()
- // Draw each column, draw any space to the right of the rightmost
- // column, and draw the column-hiding widget, if necessary.
- //-----------------------------------
- {
- UInt16 headerWidth = GetHeaderWidth();
- if (headerWidth == 0)
- return;
-
- StColorPenState::Normalize();
-
- // Draw bottom frame line
-
- Rect r;
- CalcLocalFrameRect(r);
- ::MoveTo(r.left, r.bottom - 1);
- ::LineTo(r.right - 1, r.bottom - 1);
-
- // Draw the column backgrounds and frames, carrying r.right
- // out of the loop so we know where the rightmost column ends
-
- RgnHandle updateRgn = GetLocalUpdateRgn();
- for (int i = 1; i <= mLastVisibleColumn; i++)
- {
- Rect sect;
-
- LocateColumn(i, r);
- SColumnData* cData = GetColumnData(i);
-
- // Checking against the update rgn is necessary because we sometimes
- // call Draw(), with a valid rgn to draw only particular columns
- // in order to reduce flicker.
-
- // If we didn't do this check we would end up erasing other columns,
- // as Draw would not redraw their header subpanes
-
- if ( updateRgn == NULL || ::SectRect(&r, &((**updateRgn).rgnBBox), §))
- {
- Boolean isSortedColumn = cData->GetPaneID() == GetColumnPaneID(mSortedColumn);
- DrawColumnBackground(r, isSortedColumn);
- if (isSortedColumn && cData->HasSortIcon())
- {
- // Draw the little icon
- const Int16 kSortIconWidth = 14; // which allows 1 pixel each side.
- ResIDT iconID = mSortedBackwards ? 14504 : 14505;
- Rect iconFrame = r;
- iconFrame.left = iconFrame.right - kSortIconWidth;
- ::PlotIconID(&iconFrame, kAlignAbsoluteCenter, kTransformNone, iconID);
- }
- }
- }
- if (updateRgn)
- ::DisposeRgn(updateRgn);
-
- // If a fixed-width column is rightmost,
- // we may have some space to fill in between
- // it and the right edge of the view
- if (headerWidth > r.right )
- {
- r.left = r.right;
- r.right = headerWidth;
-
- DrawColumnBackground(r, false);
- }
- CalcLocalFrameRect(r);
- r.left = r.right - kColumnHidingWidgetWidth;
- DrawColumnBackground(r, false);
-
- // Draw the column hiding widget
- if (CanHideColumns())
- {
- SInt16 iconID;
- r.top -= 1; // Hate this, but the position had to be adjusted. - jrm.
- r.bottom -= 1; // Hate this, but the position had to be adjusted. - jrm.
- r.left -= 1;
- if (mLastVisibleColumn <= 1)
- if (mColumnCount == 1)
- iconID = kColumnHiderDisabledIcon;
- else
- iconID = kColumnHiderHideDisabledIcon;
- else
- if (mLastVisibleColumn == mColumnCount)
- iconID = kColumnHiderShowDisabledIcon;
- else
- iconID = kColumnHiderEnabledIcon;
- ::PlotIconID(&r, atNone, ttNone, iconID);
- }
- } // LTableHeader::DrawSelf
-
- //-----------------------------------
- void LTableHeader::DrawColumnBackground(
- const Rect & inWhere,
- Boolean inSortColumn)
- // Bevel and fill the column header rectangle.
- //-----------------------------------
- {
- Rect r = inWhere;
- r.right -= 1; // For frame borders
- r.bottom -= 1;
- const SBevelColorDesc& colors = (inSortColumn) ? mSortedBevel : mUnsortedBevel;
-
- UGraphicGizmos::BevelRect(r, 1, colors.topBevelColor, colors.bottomBevelColor);
-
- ::PmForeColor(colors.fillColor);
- ::InsetRect(&r,1,1);
- ::PaintRect(&r);
- ::InsetRect(&r,-1,-1);
-
- ::ForeColor(blackColor);
- ::MoveTo(r.right, r.top);
- ::LineTo(r.right, r.bottom - 1);
- }
-
- //-----------------------------------
- void LTableHeader::AdjustCursor(Point inPortPt,
- const EventRecord &inMacEvent)
- // Switch the cursor if it's over a column resize area.
- //
- // We only give subviews a chance to change the cursor
- // if we're not over a resize area.
- //-----------------------------------
- {
- #pragma unused(inMacEvent)
-
- Point localPt = inPortPt;
- ColumnIndexT column;
-
- PortToLocalPoint(localPt);
- if (IsInHorizontalSizeArea(localPt, column))
- ::SetCursor(*GetCursor(kHorizontalResizeCursor));
- else
- LView::AdjustCursor(inPortPt, inMacEvent);
- } // LTableHeader::AdjustCursor
-
- //-----------------------------------
- Boolean LTableHeader::IsInHorizontalSizeArea(
- Point inLocalPt,
- ColumnIndexT& outLeftColumn)
- // Returns true if the given local pt is in a column-resize area.
- // Also returns the index of the column to the left of that area
- // (the column to be resized).
- //-----------------------------------
- {
-
- SInt16 left = inLocalPt.h + 2;
- SInt16 right = inLocalPt.h - 2;
-
- // Start at the division between the first and second columns
- // Go up to the division between the last visible and the following one,
- // if there is one.
- SInt16 lastColumnToCheck = mLastVisibleColumn;
- if (mColumnCount > mLastVisibleColumn)
- lastColumnToCheck++;
- SColumnData* cRightNeigbor = *mColumnData + 1;
- for (outLeftColumn = 1;
- outLeftColumn < lastColumnToCheck;
- outLeftColumn++,cRightNeigbor++)
- {
- SInt16 columnRightEdge = cRightNeigbor->columnPosition;
- if (left >= columnRightEdge &&
- right <= columnRightEdge)
- {
- // OK, it's a column boundary.
- return (cRightNeigbor - 1)->CanResize();
- }
- }
- outLeftColumn = 0xFFFF;
- return false;
- } // LTableHeader::IsInHorizontalSizeArea
-
- //-----------------------------------
- void LTableHeader::Click(SMouseDownEvent &inEvent)
- // Handles all clicks in the view. We never pass the click
- // down to subviews.
- //
- // Handles changing sort column, resizing columns, moving
- // columns, and showing/hiding columns.
- //-----------------------------------
- {
- // LPane::Click ususally does this...
- PortToLocalPoint(inEvent.whereLocal);
-
- // Handle column resize
- ColumnIndexT column;
- if ( IsInHorizontalSizeArea(inEvent.whereLocal, column))
- {
- TrackColumnResize(inEvent, column);
- }
- else
- {
- // Handle clicks in the column hiding widget
- if (CanHideColumns() && (inEvent.whereLocal.h > GetHeaderWidth()))
- {
- if (mColumnCount == 1 && mLastVisibleColumn == 1)
- return; // no hiding/showing possible.
- // The right arrow hides the rightmost column and the left arrow
- // shows it.
- enum { leftSide, rightSide } side
- = inEvent.whereLocal.h >= (mFrameSize.width - (kColumnHidingWidgetWidth/2))
- ? rightSide
- : leftSide;
- if (mLastVisibleColumn > 1 && side == rightSide)
- ShowHideRightmostColumn(false); // hide
- else if (mLastVisibleColumn < mColumnCount && side == leftSide)
- ShowHideRightmostColumn(true); // show.
- }
- else
- {
- // Click in column header, either drag it,
- // or set it to be the sort column
-
- column = FindColumn(inEvent.whereLocal);
- if (column != 0)
- {
- if (mLastVisibleColumn > 1 && ::WaitMouseMoved(inEvent.macEvent.where))
- TrackColumnDrag(inEvent, column);
- else
- {
- Boolean sortReverse = false;
- if (mReverseModifiers != 0)
- sortReverse = (inEvent.macEvent.modifiers & mReverseModifiers) != 0;
- else
- {
- // Toggle mode if click again on sorted column.
- sortReverse = CycleSortDirection ( column );
- }
- SetSortedColumn(column, sortReverse);
- }
- }
- }
- }
- } // LTableHeader::Click
-
-
- //
- // CycleSortDirection
- //
- // If the user clicks in the header of the already sorted column, they want to reverse
- // the direction of the sort. This can be extended to become a tri-state switch that "unsorts"
- // the column (revert to natural order) after doing forward/reverse sort, but this routine
- // doesn't do that.
- //
- bool
- LTableHeader :: CycleSortDirection ( ColumnIndexT & ioColumn )
- {
- return (ioColumn == mSortedColumn) ? !mSortedBackwards : mSortedBackwards;
-
- } // ToggleSortedColumn
-
-
- //-----------------------------------
- void LTableHeader::TrackColumnDrag(
- const SMouseDownEvent& inEvent,
- ColumnIndexT inColumn)
- // Tracks the dragging of column inColumn, moving it,
- // and redrawing as necessary.
- //-----------------------------------
- {
- Point dropPoint;
- StRegion dragRgn;
- Rect dragRect;
- Rect dragBounds;
- Rect dragSlop;
-
- if (!FocusDraw()) return;
-
- // Create the rgn we're dragging
- //
- // This is virtualize so subclasses can make a
- // more interesting rectangle...
- //
- ComputeColumnDragRect(inColumn, dragRect);
- ::RectRgn(dragRgn, &dragRect);
-
- // Compute the bounds and slop of the drag
- CalcLocalFrameRect(dragBounds);
- LocalToGlobalRect(dragBounds);
- dragSlop = dragBounds;
- ::InsetRect(&dragSlop, -5, -5);
-
- // drag me
- {
- StCurrentPort portState(LMGetWMgrPort());
- const Rect wideopen = { 0xFFFF, 0xFFFF, 0x7FFF, 0x7FFF };
-
- ClipRect(&wideopen);
- *(long*)(&dropPoint) = ::DragGrayRgn(
- dragRgn,
- inEvent.macEvent.where,
- &dragBounds,
- &dragSlop,
- kVerticalConstraint,
- NULL);
-
- }
-
- if ((dropPoint.h != (SInt16) 0x8000 || dropPoint.v != (SInt16) 0x8000) &&
- (dropPoint.h != 0 || dropPoint.v != 0))
- {
- ColumnIndexT dropColumn;
-
- // find the column over which we finished the drag
- dropPoint.h += inEvent.whereLocal.h;
- dropPoint.v += inEvent.whereLocal.v;
-
- dropColumn = FindColumn(dropPoint);
-
- // move the column we were dragging
- if (dropColumn != 0 && inColumn != dropColumn) {
- MoveColumn(inColumn, dropColumn);
- }
- }
- } // LTableHeader::TrackColumnDrag
-
- //-----------------------------------
- void LTableHeader::TrackColumnResize(
- const SMouseDownEvent &inEvent,
- ColumnIndexT inLeftColumn )
- // Tracks resizing of a column, redrawing as necessary.
- //-----------------------------------
- {
- Rect dragBoundsRect;
- Rect slopRect;
- StRegion dragRgn;
- Rect dragRect;
-
- CheckVisible(inLeftColumn);
-
- if (!FocusDraw()) return;
-
-
- // Compute the actual region we're dragging
- //
- // This is virtualized so subclasses can reflect the size-change
- // in some other view's space (like down a table column)
-
- SColumnData* cData = GetColumnData(inLeftColumn);
- ComputeResizeDragRect(inLeftColumn, dragRect);
- ::RectRgn(dragRgn, &dragRect);
-
- // Compute drag bounds and slop
- CalcLocalFrameRect(dragBoundsRect);
- dragBoundsRect.left = (inLeftColumn != 1) ? cData->columnPosition + 4 : 4;
-
- LocalToGlobalRect(dragBoundsRect);
- slopRect = dragBoundsRect;
- InsetRect(&slopRect, -5, -5);
-
- // drag me
- Point dragDiff;
- {
- StCurrentPort portState(LMGetWMgrPort());
- const Rect wideopen = { 0xFFFF, 0xFFFF, 0x7FFF, 0x7FFF };
-
- ::ClipRect(&wideopen);
-
- // Track the drag, then resize the column if it was a valid drag
- *(long *)&dragDiff = ::DragGrayRgn(
- dragRgn,
- inEvent.macEvent.where,
- &dragBoundsRect,
- &slopRect,
- kVerticalConstraint,
- NULL);
- }
- if ((dragDiff.h != (SInt16) 0x8000 || dragDiff.v != (SInt16) 0x8000) &&
- (dragDiff.h != 0 || dragDiff.v != 0 ))
- {
- if (cData->CanResizeBy(dragDiff.h))
- ResizeColumn(inLeftColumn, dragDiff.h);
- }
- } // LTableHeader::TrackColumnResize
-
- //-----------------------------------
- void LTableHeader::ResizeColumn(
- ColumnIndexT inLeftColumn,
- SInt16 inLeftColumnDelta )
- // Resize inLeftColumn by inLeftColumnDelta.
- // Columns to the right of inLeftColumn are resized
- // proportionally.
- //-----------------------------------
- {
- if (inLeftColumnDelta == 0)
- return;
-
- SColumnData* thisColData = GetColumnData(inLeftColumn);
-
- thisColData->columnWidth += inLeftColumnDelta;
- SColumnData* nextColData = thisColData + 1;
- int i = inLeftColumn + 1;
- Boolean oneColumnCanResize = false;
- for (; i <= mLastVisibleColumn; i++, nextColData++)
- if (nextColData->CanResize())
- {
- oneColumnCanResize = true;
- break;
- }
-
- SInt16 newSpace = GetHeaderWidth() - (thisColData->columnPosition + thisColData->columnWidth);
- SInt16 oldSpace = newSpace + inLeftColumnDelta;
-
- // Use proportional distribution if there's a resizable column on the right, AND either
- // we're shrinking the column or there's enough slack in the columns to the right.
- Boolean useProportionalRedistribution = false;
- if (oneColumnCanResize)
- {
- if (inLeftColumnDelta < 0 && inLeftColumn != mLastVisibleColumn)
- useProportionalRedistribution = true;
- else if (inLeftColumnDelta > 0
- && newSpace >= GetMinWidthOfRange(inLeftColumn + 1, mLastVisibleColumn))
- useProportionalRedistribution = true;
- }
-
- if (useProportionalRedistribution)
- {
- RedistributeSpace(inLeftColumn + 1, mLastVisibleColumn, newSpace, oldSpace);
- ComputeColumnPositions();
- PositionColumnHeaders(false);
- RedrawColumns(inLeftColumn, mLastVisibleColumn);
- return;
- }
- else if (inLeftColumnDelta > 0)
- {
- // This is the case when we resized the column to the rightmost edge, or so
- // far that there is not enough space for all the columns between
- // the resized column and the edge. To redistribute the space would reduce the sizes
- // of all the resizable columns in this range to zero. This would be bad (and was
- // bug number 62743).
-
- // Move columns offscreen one at a time until there's enough room for the remainder.
- while (mLastVisibleColumn > inLeftColumn
- && newSpace < GetWidthOfRange(inLeftColumn + 1, mLastVisibleColumn))
- {
- mLastVisibleColumn--;
- }
- }
- else if (inLeftColumnDelta < 0)
- {
- // Move as many columns onscreen as will fit
- while (mLastVisibleColumn + 1 <= mColumnCount
- && GetWidthOfRange(inLeftColumn + 1, mLastVisibleColumn + 1) <= newSpace)
- {
- mLastVisibleColumn++;
- }
- }
- // Finally, expand this column to the right just enough to fit all the
- // columns that we showed or left showing, and then compute all the positions.
- UInt16 widthsToRight = GetWidthOfRange(inLeftColumn + 1, mLastVisibleColumn);
- thisColData->columnWidth = GetHeaderWidth() - widthsToRight - thisColData->columnPosition;
- ComputeColumnPositions();
- PositionColumnHeaders(false);
- RedrawColumns(inLeftColumn, mLastVisibleColumn);
- } // LTableHeader::ResizeColumn
-
- //-----------------------------------
- UInt16 LTableHeader::GetMinWidthOfRange(
- ColumnIndexT inFromColumn,
- ColumnIndexT inToColumn ) const
- //-----------------------------------
- {
- Assert_(inFromColumn > 0);
- Assert_(inToColumn > 0);
- if (inToColumn > mLastVisibleColumn)
- inToColumn = mLastVisibleColumn;
- if (inFromColumn > inToColumn)
- return 0;
-
- SColumnData* cData = &(*mColumnData)[inFromColumn - 1];
- if (inToColumn > mLastVisibleColumn)
- inToColumn = mLastVisibleColumn;
-
- SInt16 minWidth = 0;
- for (int i = inFromColumn; i <= inToColumn; i++, cData++)
- {
- if (cData->CanResize())
- minWidth += SColumnData::kMinWidth;
- else
- minWidth += cData->columnWidth;
- }
- return minWidth;
- } // LTableHeader::GetMinWidthOfRange
-
- //-----------------------------------
- UInt16 LTableHeader::GetWidthOfRange(
- ColumnIndexT inFromColumn,
- ColumnIndexT inToColumn ) const
- //-----------------------------------
- {
- Assert_(inFromColumn > 0);
- Assert_(inToColumn > 0);
- if (inToColumn > mLastVisibleColumn)
- inToColumn = mLastVisibleColumn;
- if (inFromColumn > inToColumn)
- return 0;
-
- SColumnData* cData = &(*mColumnData)[inFromColumn - 1];
- if (inToColumn > mLastVisibleColumn)
- inToColumn = mLastVisibleColumn;
-
- SInt16 width = 0;
- for (int i = inFromColumn; i <= inToColumn; i++, cData++)
- width += cData->columnWidth;
- return width;
- } // LTableHeader::GetMinWidthOfRange
-
- //-----------------------------------
- void LTableHeader::SetRightmostVisibleColumn(ColumnIndexT inLastDesiredColumn)
- //-----------------------------------
- {
- CheckLegal(inLastDesiredColumn);
- ColumnIndexT currentLastVisible = mLastVisibleColumn;
- while (mLastVisibleColumn < inLastDesiredColumn)
- {
- ShowHideRightmostColumn(true);
- if (mLastVisibleColumn == currentLastVisible)
- return; // prevent infinite loop
- currentLastVisible = mLastVisibleColumn;
- }
- while (mLastVisibleColumn > inLastDesiredColumn)
- {
- ShowHideRightmostColumn(false);
- if (mLastVisibleColumn == currentLastVisible)
- return; // prevent infinite loop
- currentLastVisible = mLastVisibleColumn;
- }
- } // LTableHeader::SetRightmostVisibleColumn
-
- //-----------------------------------
- void LTableHeader::ShowHideRightmostColumn(Boolean inShow)
- // Show or hide the rightmost column.
- // mLastVisibleColumn is the rightmost visible column.
- //-----------------------------------
- {
- Assert_((inShow && mLastVisibleColumn < mColumnCount) ||
- (!inShow && mLastVisibleColumn > 1));
-
- ColumnIndexT savedLastVisibleColumn = mLastVisibleColumn;
- Boolean ok;
-
- UInt16 headerWidth = GetHeaderWidth();
- #ifdef DEBUG
- SColumnData* cData = GetColumnData(1);
- SInt16 width = 0;
- for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
- width += cData->columnWidth;
- Assert_(width == headerWidth);
- #endif // DEBUG
- UInt16 newWidth, oldWidth;
- UInt16 colWidth;
- if (inShow)
- {
- mLastVisibleColumn++;
- SColumnData* column = GetColumnData(mLastVisibleColumn);
- if (column->columnWidth < SColumnData::kMinWidth)
- column->columnWidth = SColumnData::kMinWidth;
- colWidth = column->columnWidth;
- if (colWidth < headerWidth / 3)
- {
- // Move new column in at full size, shrinking existing columns to make room
- oldWidth = headerWidth;
- newWidth = headerWidth - colWidth;
- ok = RedistributeSpace(1, mLastVisibleColumn - 1, newWidth, oldWidth);
- }
- else
- {
- // Move them all in proportionally.
- oldWidth = headerWidth + colWidth;
- newWidth = headerWidth;
- ok = RedistributeSpace(1, mLastVisibleColumn, newWidth, oldWidth);
- }
- }
- else
- {
- newWidth = headerWidth;
- colWidth = GetColumnWidth(mLastVisibleColumn); // col being hidden
- mLastVisibleColumn--;
- oldWidth = headerWidth - colWidth;
- ok = RedistributeSpace(1, mLastVisibleColumn, newWidth, oldWidth);
- }
- if (!ok)
- {
- // It didn't work. Restore the original visible column count
- // So undo! There are probably no resizable columns to allow the change.
- mLastVisibleColumn = savedLastVisibleColumn;
- return;
- }
- ComputeColumnPositions();
- PositionColumnHeaders(false);
- RedrawColumns(1, mLastVisibleColumn);
- } // LTableHeader::ShowHideRightmostColumn
-
- //-----------------------------------
- UInt16 LTableHeader::SumOfVisibleResizableWidths() const
- //-----------------------------------
- {
- UInt16 result = 0;
- SColumnData* cData = *mColumnData;
- // Using weighted proportions. Set d = the total of the proportion
- // values for the VISIBLE columns
- for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
- {
- if (cData->CanResize())
- result += cData->columnWidth;
- }
- return result;
- } // LTableHeader::SumOfVisibleResizableWidths
-
- //-----------------------------------
- Boolean LTableHeader::RedistributeSpace(
- ColumnIndexT inFromColumn,
- ColumnIndexT inToColumn,
- SInt16 inNewSpace,
- SInt16 inOldSpace)
- // Given old and new amounts of horizontal space, this resizes a range of columns
- // such that they each have the same percentage of space in the new space as they
- // did in the old space.
- //
- // Fixed-width columns are not resized, of course.
- //
- // Assumes all resizable column widths are in weighted proportion.
- // Converts all these widths to absolute pixel values in such a way that the
- // "visible" columns take up the full image size. The proportions are maintained, so
- // there's no need to convert to percentages: there's nothing magic about the number
- // 100.
- //
- // If the last visible column does not reach the right edge exactly, the last visible
- // resizable column will be extended or shrunk to make it do so. (this is largely
- // due to round-off errors in our arithmetic).
- //-----------------------------------
- {
- CheckVisible(inFromColumn);
- CheckVisible(inToColumn);
- Assert_(inFromColumn <= inToColumn);
- Assert_(inOldSpace > 0);
- if (inOldSpace <= 0 || inNewSpace <= 0)
- return false;
-
- SInt16 fixedWidth = GetFixedWidthInRange(inFromColumn, inToColumn);
-
- // Loop through columns, resizing them in proportion. This keeps the proportional
- // sizes equal. While looping, note the last resizable column in the range, which will
- // be used for adjustments. Also tally the new resizable total width in the range.
- SColumnData* cData = GetColumnData(inFromColumn);
- UInt16 sumOfWidths = 0; // Tally to be used after loop completes
- float newSpaceFloat = inNewSpace - fixedWidth; // Take invariant conversion out of the loop
- float oldSpaceFloat = inOldSpace - fixedWidth; // ...
- for (int i = inFromColumn; i <= inToColumn; i++, cData++)
- {
- if (cData->CanResize())
- {
- // Resize this column to take the same proportional amount of space, rounding
- // as appropriate
- SInt16 newWidth = (newSpaceFloat * cData->columnWidth + 0.5) / oldSpaceFloat;
- if (newWidth >= SColumnData::kMinWidth || newWidth > cData->columnWidth)
- cData->columnWidth = newWidth;
- }
- // For columns in range (only), add to tally.
- sumOfWidths += cData->columnWidth;
- }
- // Adjust the resizable columns to remove any rounding error. We used to add this to the
- // last column, but that would make it get bigger, and bigger, and bigger,...
- // So now, go around to each resizable column in rotation and make it eat some of the
- // error, until it all fits exactly.
- Int16 roundingError = inNewSpace - sumOfWidths;
- if (roundingError == 0)
- return true; // perfick!
- if (sumOfWidths == inOldSpace)
- return false; // Oh, diddums. No resizable columns
- // OK, if we get this far, then some columns were able to resize
- // (sumOfWidths != inOldSpace), and there's merely a rounding error to adjust.
- int signedUnit = roundingError < 0 ? -1 : +1;
- #ifdef DEBUG
- int maxRound = (inToColumn - inFromColumn + 2) / 2;
- Assert_(roundingError * signedUnit <= maxRound);
- #endif
- static int rovingIndex = 0;
- const Int16 initialError = roundingError;
- const int initialIndex = rovingIndex;
- while (roundingError)
- {
- rovingIndex++;
- if (rovingIndex > inToColumn)
- rovingIndex = inFromColumn;
- if (initialIndex == rovingIndex && initialError == roundingError)
- {
- Assert_(false); // not making progress! No resizable columns?
- return false;
- }
- cData = GetColumnData(rovingIndex);
- if (cData->CanResizeBy(signedUnit))
- {
- cData->columnWidth += signedUnit;
- roundingError -= signedUnit;
- }
- }
- return true;
- } // LTableHeader::RedistributeSpace
-
- //-----------------------------------
- SInt16 LTableHeader::GetFixedWidthInRange(
- ColumnIndexT inFromColumn,
- ColumnIndexT inToColumn )
- // Returns the horizontal space taken by all fixed-width
- // columns in a given column range (inclusive).
- //-----------------------------------
- {
- CheckVisible(inFromColumn);
- CheckVisible(inToColumn);
- Assert_(inFromColumn <= inToColumn);
-
- SColumnData* cData = GetColumnData(inFromColumn);
- SInt16 fixedWidth = 0;
- if (inToColumn > mLastVisibleColumn)
- inToColumn = mLastVisibleColumn;
-
- for (int i = inFromColumn; i <= inToColumn; i++, cData++)
- {
- if (!cData->CanResize())
- fixedWidth += cData->columnWidth;
- }
- return fixedWidth;
- } // LTableHeader::GetFixedWidthInRange
-
- //-----------------------------------
- UInt16 LTableHeader::GetHeaderWidth() const
- // Returns the width of all the visible columns,
- // not including the column hiding widget.
- //-----------------------------------
- {
- Int16 result = mImageSize.width - kColumnHidingWidgetWidth;
- return result < 0 ? 0 : (UInt16)result;
- }
-
- //-----------------------------------
- void LTableHeader::MoveColumn(ColumnIndexT inColumn, ColumnIndexT inMoveTo)
- // If inColumn > inMoveTo, moves inColumn BEFORE inMoveTo.
- // If inColumn < inMoveTo, moves inColumn AFTER inMoveTo.
- //-----------------------------------
- {
- SColumnData * cData = *mColumnData,
- * shiftFrom,
- * shiftTo;
- SColumnData swap;
- Boolean adjustedSort = false;
-
- // save off the data for the column we're moving
- ::BlockMoveData(&(cData[inColumn-1]), &swap, sizeof(SColumnData));
-
- // Handle case where we're moving the sorted column
- if (mSortedColumn == inColumn)
- {
- adjustedSort = true;
- mSortedColumn = inMoveTo;
- }
-
- //
- // Figure out which direction we're moving the column
- // and how we need to shift the data in the columndata array
- //
- SInt32 delta = inColumn - inMoveTo;
- if (delta > 0)
- {
- shiftFrom = &(cData[inMoveTo-1]);
- shiftTo = shiftFrom + 1;
-
- if (!adjustedSort && mSortedColumn >= inMoveTo && mSortedColumn < inColumn)
- mSortedColumn += 1;
- }
- else
- {
- delta = -delta;
- shiftTo = &(cData[inColumn-1]);
- shiftFrom = shiftTo + 1;
-
- if (!adjustedSort && mSortedColumn > inColumn && mSortedColumn <=inMoveTo)
- mSortedColumn -= 1;
- }
-
- // Shift the data in the columnData array and then copy in the data
- // of the column we're moving
- ::BlockMoveData(shiftFrom, shiftTo, (delta*sizeof(SColumnData)));
- ::BlockMoveData(&swap, &(cData[inMoveTo-1]), sizeof(SColumnData));
-
- // resposition everything
- ComputeColumnPositions();
- PositionColumnHeaders(false);
-
-
- if (inColumn > inMoveTo)
- RedrawColumns(inMoveTo, inColumn);
- else
- RedrawColumns(inColumn, inMoveTo);
- } // LTableHeader::MoveColumn
-
- //-----------------------------------
- void LTableHeader::ComputeColumnPositions()
- // Computes the horizontal position of each column, assuming
- // each column's width is valid in pixels.
- //
- // If the rightmost visible column doesn't fill the column area,
- // it will be extended to do so, UNLESS it is a fixed-width column.
- //-----------------------------------
- {
- UInt16 headerWidth = GetHeaderWidth();
- if (headerWidth == 0)
- return;
- // Compute each visible column's position
- SColumnData* cData = *mColumnData;
- SInt16 accumulator = 0;
- for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
- {
- cData->columnPosition = accumulator;
- accumulator += cData->columnWidth;
- }
-
- // If the last column is resizable, expand or shrink it
- // as necessary to reach the edge of the header
- cData--;
- if (cData->CanResize())
- {
- if ( (cData->columnPosition + cData->columnWidth) != headerWidth)
- cData->columnWidth = headerWidth - cData->columnPosition;
- }
- } // LTableHeader::ComputeColumnPositions
-
- //-----------------------------------
- void LTableHeader::ComputeResizeDragRect(ColumnIndexT inLeftColumn,
- Rect & outDragRect )
- // Computes rectangle to be dragged when resizing a column.
- //-----------------------------------
- {
- outDragRect.top = 0;
- outDragRect.bottom = mFrameSize.height + 100;
- outDragRect.left = GetColumnPosition(inLeftColumn+1) - 1;
- outDragRect.right = outDragRect.left + 1;
-
- LocalToGlobalRect(outDragRect);
- }
-
- //-----------------------------------
- void LTableHeader::ComputeColumnDragRect(
- ColumnIndexT inColumn,
- Rect &outDragRect)
- //-----------------------------------
- {
- LocateColumn(inColumn, outDragRect);
- outDragRect.bottom += 100;
- LocalToGlobalRect(outDragRect);
- }
-
- //-----------------------------------
- void LTableHeader::SetSortedColumn(
- ColumnIndexT inColumn,
- Boolean inReverse,
- Boolean inRefresh )
- //-----------------------------------
- {
- if (!CanColumnSort(inColumn))
- return;
- if (inColumn == mSortedColumn && inReverse == mSortedBackwards)
- return;
- SInt16 oldSorted = mSortedColumn;
- mSortedColumn = inColumn;
- mSortedBackwards = inReverse;
- if (inRefresh && FocusDraw())
- {
-
-
- if (oldSorted != 0) {
- RedrawColumns(oldSorted, oldSorted);
- }
-
- if (mSortedColumn != 0) {
- RedrawColumns(mSortedColumn, mSortedColumn);
- }
- }
- // Tell any listeners that the sort column changed
- LTableHeader::SortChange changeRecord;
-
- changeRecord.sortColumnID = GetColumnPaneID(mSortedColumn);
- changeRecord.sortColumn = mSortedColumn;
- changeRecord.reverseSort = mSortedBackwards;
- BroadcastMessage(msg_SortedColumnChanged, &changeRecord);
- }
-
- //-----------------------------------
- void LTableHeader::SetWithoutSort(
- ColumnIndexT inColumn,
- Boolean inReverse,
- Boolean inRefresh )
- //-----------------------------------
- {
- if (!CanColumnSort(inColumn))
- return;
- if (inColumn == mSortedColumn && inReverse == mSortedBackwards)
- return;
- SInt16 oldSorted = mSortedColumn;
- mSortedColumn = inColumn;
- mSortedBackwards = inReverse;
- if( inRefresh )
- Refresh();
- }
-
- //-----------------------------------
- void LTableHeader::SimulateClick(PaneIDT inID)
- //-----------------------------------
- {
- PaneIDT result;
-
- ColumnIndexT index = GetSortedColumn(result);
-
- if ( (result == inID) || !IsEnabled() || !IsColumnVisible(inID)) return;
-
- SetSortedColumn(ColumnFromID(inID), IsSortedBackwards(), true);
- }
-
- //-----------------------------------
- void LTableHeader::SetSortOrder(Boolean inReverse)
- //-----------------------------------
- {
- PaneIDT result;
-
- ColumnIndexT index = GetSortedColumn(result);
-
- if ( !result || !IsEnabled() || !IsColumnVisible(result)) return;
-
- SetSortedColumn(index, inReverse, true);
- }
-
- //-----------------------------------
- void LTableHeader::RedrawColumns(ColumnIndexT inFrom, ColumnIndexT inTo)
- // Immediately redraws the given column range.
- // We don't do update events so we can avoid the flicker
- // of update rgn being erased to white.
- //-----------------------------------
- {
- UInt16 headerWidth = GetHeaderWidth();
-
- // Compute the top-left of the refresh area
- Rect r;
- LocateColumn(inFrom, r);
- LocalToPortPoint(topLeft(r));
-
- // Get the bottom right of the refresh area
- if (inFrom == inTo)
- {
- LocalToPortPoint(botRight(r));
- }
- else
- {
- Rect right;
-
- if (inTo <= mLastVisibleColumn)
- {
- LocateColumn(inTo, right);
- LocalToPortPoint(botRight(right));
- }
- else
- {
- right.right = headerWidth;
- LocalToPortPoint(botRight(right));
- }
-
- r.bottom = right.bottom;
- r.right = right.right;
- }
-
- // Redraw
- StRegion rgn;
- ::RectRgn(rgn, &r);
- Draw(rgn);
- }
-
- //-----------------------------------
- void LTableHeader::LocateColumn( ColumnIndexT inColumn,
- Rect & outWhere) const
- // Computes the bounding rect of the given column
- // in local coordinates.
- //-----------------------------------
- {
- outWhere.top = 0;
- outWhere.bottom = mFrameSize.height;
-
- outWhere.left = GetColumnPosition(inColumn);
- outWhere.right = outWhere.left + GetColumnWidth(inColumn);
- }
-
- //-----------------------------------
- LTableHeader::ColumnIndexT LTableHeader::FindColumn( Point inLocalPoint ) const
- // Given a local point, returns the index of the
- // column containing the point. Returns 0 for no
- // column.
- //-----------------------------------
- {
- int i;
-
- if (inLocalPoint.h >= 0 && inLocalPoint.h <= GetHeaderWidth())
- {
- for (i = mLastVisibleColumn; i > 0; i--)
- {
- if (inLocalPoint.h >= GetColumnPosition(i))
- return i;
- }
- }
- return 0;
- }
-
- //-----------------------------------
- void LTableHeader::InvalColumn( ColumnIndexT inColumn)
- // Invalidates a column header area.
- //-----------------------------------
- {
- if (FocusDraw())
- {
- Rect r;
-
- LocateColumn(inColumn, r);
- LocalToPortPoint(topLeft(r));
- LocalToPortPoint(botRight(r));
- InvalPortRect(&r);
- }
- }
-
- //-----------------------------------
- void LTableHeader::PositionColumnHeaders(Boolean inRefresh)
- // Positions all the column header panes to be aligned
- // with the current column positions.
- //-----------------------------------
- {
- if (GetHeaderWidth() == 0)
- return;
- int i;
- for (i = 1; i <= mLastVisibleColumn; i++)
- {
- PositionOneColumnHeader(i, inRefresh);
- LPane* headerPane = GetColumnPane(i);
- if (headerPane)
- {
- if (GetColumnData(i)->CanDisplayTitle())
- headerPane->Show();
- else
- headerPane->Hide();
- if (!inRefresh)
- headerPane->DontRefresh(true);
- }
- }
- for ( ; i <= mColumnCount; i++)
- {
- LPane* headerPane = GetColumnPane(i);
- if (headerPane)
- {
- headerPane->Hide();
- if (!inRefresh)
- headerPane->DontRefresh(true);
- }
- }
- } // LTableHeader::PositionColumnHeaders
-
- //-----------------------------------
- void LTableHeader::PositionOneColumnHeader( ColumnIndexT inColumn, Boolean inRefresh)
- // Positions a column header pane to be aligned with the its
- // column position.
- //
- // The column header pane is sized to the width of the column.
- // Its vertical position is not changed.
- //-----------------------------------
- {
- Rect columnBounds;
- LocateColumn(inColumn, columnBounds);
- LPane* headerPane = GetColumnPane(inColumn);
-
- // Position the pane horizontally within the column
- if (headerPane)
- {
- SDimension16 paneSize;
- headerPane->GetFrameSize(paneSize);
-
- SPoint32 paneLocation;
- headerPane->GetFrameLocation(paneLocation);
-
- Int32 horizDelta
- = (columnBounds.left + kColumnMargin) - paneLocation.h + mFrameLocation.h;
- Int32 paneWidth = columnBounds.right - (columnBounds.left + (kColumnMargin * 2));
-
- headerPane->ResizeFrameTo(paneWidth, paneSize.height, inRefresh);
- headerPane->MoveBy( horizDelta, 0, inRefresh);
- }
- } // LTableHeader::PositionOneColumnHeader
-
- //-----------------------------------
- PaneIDT LTableHeader::GetColumnPaneID(ColumnIndexT inColumn) const
- // Returns the Pane id of the given column's header-pane.
- // Returns 0 if there is no header-pane.
- //-----------------------------------
- {
- CheckLegalHigh(inColumn);
- if (inColumn != 0)
- return (GetColumnData(inColumn)->GetPaneID());
- return 0;
- }
-
- //-----------------------------------
- LPane * LTableHeader::GetColumnPane(ColumnIndexT inColumn)
- //-----------------------------------
- {
- LView *super = GetSuperView();
-
- CheckLegal(inColumn);
- Assert_(super != NULL);
-
- PaneIDT id = GetColumnPaneID(inColumn);
-
- if (id != 0)
- return super->FindPaneByID(id);
- return NULL;
- }
-
- //-----------------------------------
- SInt16 LTableHeader::GetColumnWidth(ColumnIndexT inColumn) const
- //-----------------------------------
- {
- CheckLegal(inColumn);
- // widths are always in absolute when this is called.
- return GetColumnData(inColumn)->columnWidth;
- }
-
- //-----------------------------------
- SInt16 LTableHeader::GetColumnPosition(ColumnIndexT inColumn) const
- //-----------------------------------
- {
- CheckLegal(inColumn);
- return ( GetColumnData(inColumn)->columnPosition);
- }
-
- //-----------------------------------
- Boolean LTableHeader::CanColumnResize(ColumnIndexT inColumn) const
- //-----------------------------------
- {
- CheckLegal(inColumn);
-
- // check all of the columns to the right to see if they can be resized
- // if they can't be resized we should NOT be able to resize this column
- // example: | resizCol1 | resizCol2 | fixedCol3 |
- // if we are working off: ^
- // we shouldn't be able to resize (shrink) here because there is nowhere
- // for any extra space to go and we don't erase where column was.
- // note that inColumn is 1-based but mColumnData (within loop) is 0-based
-
- SColumnData* cData = GetColumnData(CountVisibleColumns());
- Boolean canFurtherRightResize = false;
- for (int i = CountVisibleColumns(); i > inColumn; i--,cData--)
- {
- if (cData->CanResize())
- {
- canFurtherRightResize = true;
- break;
- }
- }
- return canFurtherRightResize && GetColumnData(inColumn)->CanResize();
- } // LTableHeader::CanColumnResize
-
- //-----------------------------------
- Boolean LTableHeader::CanColumnSort(ColumnIndexT inColumn) const
- //-----------------------------------
- {
- CheckLegal(inColumn);
- return GetColumnData(inColumn)->CanSort();
- }
-
- //-----------------------------------
- LTableHeader::ColumnIndexT LTableHeader::GetSortedColumn(PaneIDT &outHeaderPane) const
- //-----------------------------------
- {
- outHeaderPane = GetColumnPaneID(mSortedColumn);
- return mSortedColumn;
- }
-
- //-----------------------------------
- LTableHeader::ColumnIndexT LTableHeader::ColumnFromID(PaneIDT inPaneID) const
- //-----------------------------------
- {
- SColumnData* c = *mColumnData;
- for (int i = 1; i <= mColumnCount; i++,c++)
- if (c->paneID == inPaneID)
- return i;
- return 0;
- }
-
- //-----------------------------------
- Boolean LTableHeader::CanHideColumns() const
- //-----------------------------------
- {
- return mHeaderFlags & kHeaderCanHideColumns == kHeaderCanHideColumns;
- }
-
- //-----------------------------------
- void LTableHeader::WriteColumnState( LStream * inStream)
- // Writes out the current column state with column
- // widths stored as percentages (except for fixed-width)
- // columns.
- //-----------------------------------
- {
- Assert_(mColumnData != NULL);
-
- HeaderFlagsT flags = 0;
-
- if ( mSortedBackwards ) flags |= cFlagHeaderSortedBackwards;
-
- *inStream << mColumnCount;
- *inStream << mLastVisibleColumn;
- *inStream << mSortedColumn;
- *inStream << (HeaderFlagsT) flags; // pad
-
- UInt32 dataSize = mColumnCount * sizeof(SColumnData);
-
- StHandleLocker locker((Handle)mColumnData);
-
- // write out the widths
- inStream->PutBytes(*mColumnData, dataSize);
-
- } // LTableHeader::WriteColumnState
-
- //-----------------------------------
- void LTableHeader::ReadColumnState( LStream * inStream, Boolean inMoveHeaders)
- // Reads in column state for the header, converts the column widths
- // to absolute pixel values, computes column positions, and optionally
- // positions the column header panes.
- //-----------------------------------
- {
- *inStream >> mColumnCount;
- *inStream >> mLastVisibleColumn;
- *inStream >> mSortedColumn;
- HeaderFlagsT flags;
- *inStream >> flags;
- mSortedBackwards = (flags & cFlagHeaderSortedBackwards) != 0;
-
- // Assert_(mColumnCount > 0);
- // Columns for RDF views are taken from the RDF database as opposed to
- // stored in a resource. Therefore allow ReadColumnState to handle case
- // where there are no columns specified in 'Cols' resource
- if (mColumnCount > 0)
- {
- CheckLegal(mLastVisibleColumn);
- CheckLegalHigh(mSortedColumn);
-
- UInt32 dataSize = mColumnCount * sizeof(SColumnData);
-
- if (mColumnData != NULL) ::DisposeHandle((Handle)mColumnData);
- mColumnData = (SColumnData **) ::NewHandle(dataSize);
- ThrowIfNULL_(mColumnData);
-
- StHandleLocker locker((Handle)mColumnData);
- inStream->GetBytes(*mColumnData, dataSize);
-
- ConvertWidthsToAbsolute();
- ComputeColumnPositions();
-
- if (inMoveHeaders)
- PositionColumnHeaders(true);
- }
-
- Refresh();
- } // LTableHeader::ReadColumnState
-
- //-----------------------------------
- void LTableHeader::ConvertWidthsToAbsolute()
- //-----------------------------------
- {
- SColumnData* cData = *mColumnData;
- UInt16 oldSpace = 0;
- for (int i = 1; i <= mLastVisibleColumn; i++, cData++)
- oldSpace += cData->columnWidth;
- RedistributeSpace(1, mLastVisibleColumn, GetHeaderWidth(), oldSpace);
- } // LTableHeader::ConvertWidthsToAbsolute
-
- //-----------------------------------
- void LTableHeader::ResizeFrameBy(
- Int16 inWidthDelta,
- Int16 inHeightDelta,
- Boolean inRefresh)
- // When the frame is resized, we resize our image to match,
- // first converting to percentages so that we can resize
- // our columns proportionally.
- //-----------------------------------
- {
- ResizeImageBy( inWidthDelta,
- inHeightDelta,
- inRefresh );
- if ( mColumnCount != 0 )
- {
- // Move the columns and column headers
- ConvertWidthsToAbsolute();
- ComputeColumnPositions();
- PositionColumnHeaders();
- }
- LView::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh);
- }
-
- //-----------------------------------
- static void LocalToGlobalRect(Rect& r)
- //-----------------------------------
- {
- ::LocalToGlobal((Point *) &(r.top));
- ::LocalToGlobal((Point *) &(r.bottom));
- }
-