1987 lines
68 KiB
C++
Executable File
1987 lines
68 KiB
C++
Executable File
/* -*- 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/25/96 - Tim Craycroft
|
||
|
||
*/
|
||
|
||
|
||
#pragma mark --- Includes
|
||
|
||
#include "CStandardFlexTable.h"
|
||
|
||
// Mac
|
||
#include <Drag.h>
|
||
|
||
// PowerPlant
|
||
#include <LTableMultiSelector.h>
|
||
#include <LTableArrayStorage.h>
|
||
#include <URegions.h>
|
||
#include <LDropFlag.h>
|
||
|
||
// ANSI
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
|
||
// Netscape Mac Libs
|
||
#include "LFlexTableGeometry.h"
|
||
#include "UGraphicGizmos.h"
|
||
#include "LTableRowSelector.h"
|
||
#include "CTableKeyAttachment.h"
|
||
#include "CContextMenuAttachment.h"
|
||
#include "CTargetFramer.h"
|
||
#include "UMailFolderMenus.h"
|
||
|
||
#include "macutil.h"
|
||
#include "resgui.h"
|
||
#include "secnav.h"
|
||
#include "cstring.h"
|
||
|
||
#include "CInlineEditField.h"
|
||
|
||
#pragma mark --- CTableHeaderListener
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CTableHeaderListener::ListenToMessage(MessageT inMessage, void *ioParam)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (inMessage == LTableHeader::msg_SortedColumnChanged) {
|
||
mTable->ChangeSort((const LTableHeader::SortChange*)ioParam);
|
||
}
|
||
} // CTableHeaderListener::ListenToMessage
|
||
|
||
|
||
#pragma mark --- CTableHeaderListener
|
||
|
||
|
||
//
|
||
// ListenToMessage
|
||
//
|
||
// Respond to changes in the inline editor
|
||
//
|
||
void
|
||
CInlineEditorListener::ListenToMessage ( MessageT inMessage, void * /*ioParam*/ )
|
||
{
|
||
switch ( inMessage ) {
|
||
|
||
case CInlineEditField::msg_InlineEditFieldChanged:
|
||
mTable->InlineEditorTextChanged();
|
||
break;
|
||
|
||
case CInlineEditField::msg_HidingInlineEditField:
|
||
mTable->InlineEditorDone();
|
||
mTable->mRowBeingEdited = LArray::index_Bad;
|
||
break;
|
||
|
||
} // case of which message
|
||
|
||
} // ListenToMessage
|
||
|
||
#pragma mark --- CClickTimer
|
||
|
||
//========================================================================================
|
||
class CClickTimer : public LPeriodical
|
||
//========================================================================================
|
||
{
|
||
public:
|
||
CClickTimer(
|
||
CStandardFlexTable* inTable,
|
||
const STableCell& inCell,
|
||
const SMouseDownEvent& inMouseDown);
|
||
virtual ~CClickTimer();
|
||
void NoteDoubleClick();
|
||
void NoteSingleClick();
|
||
TableIndexT GetClickedRow() { return mCell.row; }
|
||
|
||
protected:
|
||
void ReverseTheHilite();
|
||
virtual void SpendTime(const EventRecord& inMacEvent);
|
||
|
||
// Data:
|
||
protected:
|
||
CStandardFlexTable* mTable;
|
||
STableCell mCell;
|
||
SMouseDownEvent mMouseDown; // Can't be a reference, orig. is on the stack & dies
|
||
}; // class CClickTimer
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CClickTimer::CClickTimer(
|
||
CStandardFlexTable* inTable,
|
||
const STableCell& inCell,
|
||
const SMouseDownEvent& inMouseDown)
|
||
//----------------------------------------------------------------------------------------
|
||
: mTable(inTable)
|
||
, mCell(inCell)
|
||
, mMouseDown(inMouseDown)
|
||
{
|
||
mTable->mClickTimer = this;
|
||
ReverseTheHilite(); // fake a new selection (temporarily).
|
||
StartRepeating();
|
||
} // CClickTimer::CClickTimer()
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CClickTimer::~CClickTimer()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
mTable->mClickTimer = nil;
|
||
mTable->SetHiliteDisabled(false); // make sure it's normal
|
||
} // CClickTimer::~CClickTimer()
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CClickTimer::SpendTime(const EventRecord& inMacEvent)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (inMacEvent.when >= mMouseDown.macEvent.when + LMGetDoubleTime())
|
||
NoteSingleClick();
|
||
} // CClickTimer::SpendTime()
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CClickTimer::NoteDoubleClick()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
ReverseTheHilite(); // Show the selection again
|
||
delete this;
|
||
} // CClickTimer::NoteDoubleClick()
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CClickTimer::NoteSingleClick()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
try
|
||
{
|
||
// ReverseTheHilite(); // Show the selection again
|
||
// The hiliting is now correct. Turn off hiliting to avoid a double flash.
|
||
mTable->SetHiliteDisabled(true);
|
||
mTable->SetFancyDoubleClick(false);
|
||
mTable->ClickSelect(mCell, mMouseDown);
|
||
mTable->SetFancyDoubleClick(true);
|
||
}
|
||
catch (...)
|
||
{
|
||
delete this;
|
||
throw;
|
||
}
|
||
delete this;
|
||
} // CClickTimer::NoteSingleClick()
|
||
|
||
const Boolean ignore = true;
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CClickTimer::ReverseTheHilite()
|
||
// This relies on the implementation of HiliteRow, which ignores the "inActively"
|
||
// parameter.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
STableCell cell = mTable->GetFirstSelectedCell();
|
||
UInt32 total = mTable->GetSelectedRowCount();
|
||
UInt32 count = 0;
|
||
if (total > 0)
|
||
for (; cell.row <= mTable->mRows && count < total; cell.row++)
|
||
{
|
||
if (mTable->CellIsSelected(cell))
|
||
{
|
||
mTable->HiliteRow(cell.row, ignore);
|
||
count++;
|
||
}
|
||
}
|
||
mTable->mDropRow = cell.row; // fake out the icon to draw selected
|
||
mTable->HiliteRow(mCell.row, ignore);
|
||
mTable->mDropRow = LArray::index_Bad;
|
||
} // CClickTimer::ReverseTheHilite
|
||
|
||
#pragma mark --- CStandardFlexTable
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CStandardFlexTable::CStandardFlexTable(LStream *inStream)
|
||
//----------------------------------------------------------------------------------------
|
||
: CSpecialTableView(inStream)
|
||
, LDragAndDrop(GetMacPort(), this)
|
||
, LCommander()
|
||
, LBroadcaster()
|
||
//, CQAPartnerTableMixin(this)
|
||
, mTableHeaderListener(this)
|
||
, mTableHeader(NULL)
|
||
, mSelectionList(NULL)
|
||
, mClickTimer(nil)
|
||
, mFancyDoubleClick(false)
|
||
, mHiliteDisabled(false)
|
||
, mDropRow(LArray::index_Bad)
|
||
, mIsInternalDrop(false)
|
||
, mIsDropBetweenRows(false)
|
||
, mNameEditor(NULL)
|
||
, mRowBeingEdited(0)
|
||
, mInlineListener(this)
|
||
{
|
||
*inStream >> mTableHeaderPaneID;
|
||
*inStream >> mTextTraitsID;
|
||
*inStream >> mClickCountToOpen;
|
||
|
||
SetRefreshAllWhenResized(true);
|
||
} // CStandardFlexTable::CStandardFlexTable
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
CStandardFlexTable::~CStandardFlexTable()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
delete mClickTimer;
|
||
} // CStandardFlexTable::~CStandardFlexTable
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::FinishCreateSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
RGBColor ignore;
|
||
UGraphicGizmos::CalcWindowTingeColors(GetMacPort(), mDropColor, ignore);
|
||
|
||
LView *super = GetSuperView();
|
||
Assert_(super != NULL);
|
||
mTableHeader = (LTableViewHeader*)dynamic_cast<LTableViewHeader*>(super->FindPaneByID(mTableHeaderPaneID));
|
||
Assert_(mTableHeader);
|
||
// This class assumes the header is fully created first (Oh, yukky-poo).
|
||
Boolean HEADER_MUST_BE_IN_RESOURCE_BEFORE_TABLE = mTableHeader->GetTableView() != NULL;
|
||
Assert_(HEADER_MUST_BE_IN_RESOURCE_BEFORE_TABLE);
|
||
|
||
mTableHeader->AddListener(&mTableHeaderListener);
|
||
SetUpTableHelpers();
|
||
|
||
// Add columns to match the header
|
||
|
||
SynchronizeColumnsWithHeader();
|
||
|
||
SetTextTraits(mTextTraitsID);
|
||
|
||
SetRefreshAllWhenResized(true);
|
||
|
||
// If an editor for in-place editing exists, find it. Don't worry if one can't be found.
|
||
mNameEditor = dynamic_cast<CInlineEditField*>(FindPaneByID(paneID_InlineEdit));
|
||
if ( mNameEditor ) {
|
||
mNameEditor->AddListener ( &mInlineListener );
|
||
mNameEditor->SetGrowableBorder(true);
|
||
}
|
||
|
||
} // CStandardFlexTable::FinishCreateSelf
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::SetTextTraits(ResIDT inmTextTraitsID)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// load the text traits and pre-measure them
|
||
StTextState textState;
|
||
|
||
SInt16 rowHeight;
|
||
|
||
mTextTraitsID = inmTextTraitsID;
|
||
UTextTraits::SetPortTextTraits(mTextTraitsID);
|
||
::GetFontInfo(&mTextFontInfo);
|
||
|
||
rowHeight = mTextFontInfo.ascent + mTextFontInfo.descent + 1;
|
||
if (rowHeight < kMinRowHeight) {
|
||
rowHeight = kMinRowHeight;
|
||
}
|
||
|
||
mTableGeometry->SetRowHeight(rowHeight, 0, 0);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::ScrollRowIntoFrame(TableIndexT inRow)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// This avoids excessive erasure done in the base class.
|
||
if (inRow == LArray::index_Bad)
|
||
return;
|
||
STableCell cellToShow(inRow, 1);
|
||
SPoint32 startLocation;
|
||
GetImageLocation(startLocation);
|
||
ScrollCellIntoFrame(cellToShow);
|
||
SPoint32 endLocation;
|
||
GetImageLocation(endLocation);
|
||
if (startLocation.h != endLocation.h || startLocation.v != endLocation.v)
|
||
{
|
||
// 97/08/06. jrm: not entirely sure about whether this will give us
|
||
// a redraw bug, but ScrollCellIntoFrame seems to end up
|
||
// calling UpdatePort(). So I've added DontRefresh() here,
|
||
// and it seems to cut down on flashing.
|
||
DontRefresh();
|
||
}
|
||
} // CStandardFlexTable::ScrollRowIntoFrame
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::SelectRow(TableIndexT inRow)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
STableCell cellToSelect(inRow, 1);
|
||
if (GetSelectedRowCount() == 0)
|
||
ScrollRowIntoFrame(inRow);
|
||
SelectCell(cellToSelect);
|
||
} // CStandardFlexTable::SelectRow
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::ScrollSelectionIntoFrame()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mTableSelector)
|
||
{
|
||
STableCell cellToSelect = mTableSelector->GetFirstSelectedCell();
|
||
if ( cellToSelect.row != LArray::index_Bad )
|
||
ScrollRowIntoFrame(cellToSelect.row);
|
||
else
|
||
ScrollRowIntoFrame(1); // if no selection, scroll to topw
|
||
}
|
||
} // CStandardFlexTable::SelectRow
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::SynchronizeColumnsWithHeader()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Assert_(mTableHeader);
|
||
Int32 deltaColumns = (Int32) mTableHeader->CountVisibleColumns() - (Int32) mCols;
|
||
if ( deltaColumns != 0 ) {
|
||
if ( deltaColumns > 0 ) {
|
||
InsertCols(deltaColumns, mCols, NULL, 0, false);
|
||
} else {
|
||
RemoveCols(-deltaColumns, mCols + deltaColumns + 1, false);
|
||
}
|
||
}
|
||
} // CStandardFlexTable::SynchronizeColumnsFromHeader
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::AddAttachmentFirst(LAttachment* inAttachment, Boolean inOwnsAttachment)
|
||
// nb: AddAttachment adds at end. But this can be called if you want the new attachment
|
||
// to be executed before the existing ones.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
LAttachment* beforeItem = nil;
|
||
if (mAttachments)
|
||
mAttachments->FetchItemAt(LArray::index_First, beforeItem);
|
||
LCommander::AddAttachment(inAttachment, beforeItem, inOwnsAttachment);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::SetUpTableHelpers()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
SetTableGeometry( new LFlexTableGeometry(this, mTableHeader) );
|
||
SetTableSelector( new LTableRowSelector(this));
|
||
// NOTE: There is a dependency between LTableRowSelector and CThreadView
|
||
// because CThreadView::DoSelectThread() needs to select several rows at once.
|
||
|
||
// standard keyboard navigation.
|
||
AddAttachment( new CTableKeyAttachment(this) );
|
||
// We don't need table storage. It's safe to have a null one.
|
||
// It is NOT safe, to call SetTableStorage(NULL), as its
|
||
// implementation in LTableView does not check against NULL.
|
||
// SetTableStorage(NULL);
|
||
Assert_(mTableStorage == NULL);
|
||
} // CStandardFlexTable::SetUpTableHelpers
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::RefreshCellRange(
|
||
const STableCell &inTopLeft,
|
||
const STableCell &inBotRight)
|
||
// Fix a bug in LTableView. If neither cell intersects the frame, there are two
|
||
// cases: union of two cells intersects in empty rect or entire rect. PP always
|
||
// assumed the latter.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// I added this test to prevent updating the entire frame in the case
|
||
// of an empty intersection (a very common case!).
|
||
// Calculate the convex hull of the two cells
|
||
Int32 cell1Left, cell1Top, dummy1, dummy2;
|
||
mTableGeometry->GetImageCellBounds(inTopLeft, cell1Left, cell1Top,
|
||
dummy1, dummy2);
|
||
Int32 cell2Right, cell2Bottom;
|
||
mTableGeometry->GetImageCellBounds(inBotRight, dummy1, dummy2,
|
||
cell2Right, cell2Bottom);
|
||
// Does this big rect intersect the frame? If not, nothing to do.
|
||
if (!ImageRectIntersectsFrame(cell1Left, cell1Top, cell2Right, cell2Bottom))
|
||
return;
|
||
LTableView::RefreshCellRange(inTopLeft, inBotRight);
|
||
} // CStandardFlexTable::RefreshCellRange
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DrawSelf()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
StTextState textState;
|
||
|
||
UTextTraits::SetPortTextTraits(mTextTraitsID);
|
||
ApplyForeAndBackColors();
|
||
LTableView::DrawSelf();
|
||
ExecuteAttachments(CTargetFramer::msg_DrawSelfDone, this);
|
||
} // CStandardFlexTable::DrawSelf
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
TableIndexT CStandardFlexTable::CountExtraRowsControlledByCell(const STableCell& /*inCell*/) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return 0;
|
||
} // CStandardFlexTable::CountExtraRowsControlledByCell
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::ApplyTextStyle(TableIndexT /* inrow */) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
} // CStandardFlexTable::ApplyTextStyle
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::GetMainRowText(
|
||
TableIndexT /*inRow*/,
|
||
char* outText,
|
||
UInt16 inMaxBufferLength) const
|
||
// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
|
||
// Return result indicates if any of the text is visible in the cell
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (outText && inMaxBufferLength > 0)
|
||
*outText = '\0';
|
||
} // CStandardFlexTable::GetMainRowText
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::GetHiliteText(
|
||
TableIndexT inRow,
|
||
char* outText,
|
||
UInt16 inMaxTextLength,
|
||
Rect* ioRect) const
|
||
// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
|
||
// Caller must initialize the rectangle
|
||
// Return result indicates if any of the text is visible in the cell
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
GetMainRowText(inRow, outText, inMaxTextLength);
|
||
if (!*outText)
|
||
return false;
|
||
if (!ioRect)
|
||
return true;
|
||
ioRect->left += kDistanceFromIconToText;
|
||
ioRect->top += 2;
|
||
ioRect->bottom -= 2;
|
||
StColorState penState;
|
||
UTextTraits::SetPortTextTraits(mTextTraitsID);
|
||
ApplyTextStyle(inRow);
|
||
ioRect->right = ioRect->left + ::TextWidth(outText, 0, ::strlen(outText));
|
||
Rect cellRect;
|
||
GetLocalCellRect(STableCell(inRow, GetHiliteColumn()), cellRect);
|
||
if (ioRect->left < cellRect.right)
|
||
{
|
||
if (ioRect->right > cellRect.right)
|
||
ioRect->right = cellRect.right;
|
||
}
|
||
else
|
||
return false;
|
||
return true;
|
||
} // CStandardFlexTable::GetHiliteText
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::GetHiliteTextRect(
|
||
const TableIndexT inRow,
|
||
Rect& outRect) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Default is to return a rect enclosing the entire row. Derived classes may wish
|
||
// to use GetHiliteText instead.
|
||
STableCell cell(inRow, 1);
|
||
if (!GetLocalCellRect(cell, outRect))
|
||
return false;
|
||
cell.col = mCols;
|
||
Rect cellRect;
|
||
if (!GetLocalCellRect(cell, cellRect))
|
||
return false;
|
||
outRect.right = cellRect.right;
|
||
return true;
|
||
} // CStandardFlexTable::GetHiliteTextRect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::GetIconRect(
|
||
const STableCell& inCell,
|
||
const Rect& inLocalRect,
|
||
Rect& outRect) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
GetDropFlagRect(inLocalRect, outRect);
|
||
outRect.left = outRect.right + kIconMargin
|
||
+ kIndentPerLevel * GetNestedLevel(inCell.row);
|
||
outRect.right = outRect.left + kIconWidth;
|
||
return true;
|
||
} // CStandardFlexTable::GetIconRect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::HiliteRow(
|
||
TableIndexT inRow,
|
||
Boolean /*inHilite*/)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mHiliteDisabled)
|
||
return;
|
||
StRegion hiliteRgn;
|
||
if (FocusExposed() && GetRowHiliteRgn(inRow, hiliteRgn))
|
||
{
|
||
StColorPenState saveColorPen; // Preserve color & pen state
|
||
TableIndexT col = GetHiliteColumn();
|
||
if (col)
|
||
{
|
||
Rect r;
|
||
STableCell cell(inRow, col);
|
||
GetLocalCellRect(cell, r);
|
||
DrawIcons(cell, r);
|
||
}
|
||
if (!IsActive())
|
||
{
|
||
// Make a frame. Do it this way so we get the "Invert" color trickery
|
||
// from the toolbox. The previous code (srcXOR/FrameRgn) did not work
|
||
// well when the table was not the active view.
|
||
StRegion tempRgn;
|
||
::CopyRgn(hiliteRgn, tempRgn);
|
||
::InsetRgn(tempRgn, 1, 1);
|
||
::DiffRgn(hiliteRgn, tempRgn, hiliteRgn);
|
||
}
|
||
ApplyForeAndBackColors();
|
||
DoHiliteRgn(hiliteRgn); // can be overridden to do something different
|
||
}
|
||
} // CStandardFlexTable::HiliteRow
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable :: DoHiliteRgn( RgnHandle inHiliteRgn ) const
|
||
// Hilite the given area using the system hilite color. Override this to do
|
||
// something different (like not use the hilite color).
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UDrawingUtils::SetHiliteModeOn(); // Don't use hilite color, except for inline editing.
|
||
::InvertRgn(inHiliteRgn);
|
||
} // CStandardFlexTable :: DoHiliteRgn
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable :: DoHiliteRect( const Rect & inHiliteRect ) const
|
||
// Hilite the given area using the system hilite color. Override this to do
|
||
// something different (like not use the hilite color).
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UDrawingUtils::SetHiliteModeOn(); // Don't use hilite color, except for inline editing.
|
||
::InvertRect(&inHiliteRect);
|
||
} // CStandardFlexTable :: DoHiliteRect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::Click(SMouseDownEvent &inMouseDown)
|
||
// Click the table. Special processing for drag-n-drop stuff.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
LPane *clickedPane = FindSubPaneHitBy(inMouseDown.wherePort.h,
|
||
inMouseDown.wherePort.v);
|
||
if ( clickedPane != nil )
|
||
clickedPane->Click(inMouseDown);
|
||
else
|
||
{
|
||
if ( !inMouseDown.delaySelect ) LCommander::SwitchTarget(this);
|
||
StValueChanger<Boolean> change(inMouseDown.delaySelect, false);
|
||
// For drag-n-drop functionality
|
||
LPane::Click(inMouseDown); // will process click on this View
|
||
}
|
||
} // CStandardFlexTable::Click
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::ClickDropFlag(
|
||
const STableCell &inCell,
|
||
const SMouseDownEvent &inMouseDown)
|
||
// Handle drop-flag ("twistee icon") clicks. Return true if handled.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Boolean isCellExpanded;
|
||
if (CellHasDropFlag(inCell, isCellExpanded))
|
||
{
|
||
Rect cellRect, flagRect;
|
||
GetLocalCellRect(inCell, cellRect);
|
||
GetDropFlagRect(cellRect, flagRect);
|
||
if (::PtInRect(inMouseDown.whereLocal, &flagRect))
|
||
{
|
||
ApplyForeAndBackColors();
|
||
if (FocusDraw() && LDropFlag::TrackClick(flagRect, inMouseDown.whereLocal, isCellExpanded))
|
||
SetCellExpansion(inCell, !isCellExpanded);
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::ClickSelect(
|
||
const STableCell &inCell,
|
||
const SMouseDownEvent &inMouseDown)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Rect cellRect;
|
||
GetLocalCellRect(inCell, cellRect);
|
||
|
||
FocusDraw(); //to be on the safe side
|
||
|
||
// compute the text and icon rectangles and if the click is in them
|
||
Rect textRect, iconRect;
|
||
GetHiliteTextRect ( inCell.row, textRect );
|
||
GetIconRect ( inCell, cellRect, iconRect );
|
||
bool clickIsInIcon = ::PtInRect(inMouseDown.whereLocal, &iconRect);
|
||
bool clickIsInTitle = ::PtInRect(inMouseDown.whereLocal, &textRect);
|
||
|
||
// If the click is outside of the icon/title, or if it is in the title (and in-place editing is on),
|
||
// then temporarily turn off the fancy double-click stuff since we don't want it to happen.
|
||
bool savedFancyDoubleClick = mFancyDoubleClick;
|
||
if ( !clickIsInIcon && !clickIsInTitle )
|
||
mFancyDoubleClick = false; // click was outside icon&title
|
||
if ( clickIsInTitle && mNameEditor )
|
||
mFancyDoubleClick = false; // click was in title and we're doing inplace editing
|
||
|
||
// Fancy double-click behavior. There are two reasons for this new feature of 5.0:
|
||
// <09> Double-clicking a thread in a message thread view (for example) should
|
||
// not select the new item, because then the selected message would load in
|
||
// the message pane of the thread window as well as in the newly-opened message
|
||
// window. The user would prefer to maintain the message displayed in the current
|
||
// window, and open the double-clicked, different message in the separate window.
|
||
// <09> Selecting a new row can be expensive, because it causes the message/folder
|
||
// to load. This loading usually unwanted after a double-click.
|
||
if (mFancyDoubleClick
|
||
&& !CellIsSelected(inCell)
|
||
&& (inMouseDown.macEvent.modifiers & shiftKey) == 0
|
||
&& (inMouseDown.macEvent.modifiers & cmdKey) == 0
|
||
)
|
||
{
|
||
if (GetClickCount() == 1)
|
||
{
|
||
// Single-click. First cancel any previous double-click in progress.
|
||
if (mClickTimer)
|
||
mClickTimer->NoteSingleClick();
|
||
// Set a timer. If the timer times out, it will set the hilite
|
||
// back and call ClickSelect again with fancy double-click set to false.
|
||
mClickTimer = new CClickTimer(this, inCell, inMouseDown);
|
||
}
|
||
else
|
||
{
|
||
// Double-click. Get rid of the timer.
|
||
if (mClickTimer)
|
||
mClickTimer->NoteDoubleClick();
|
||
OpenRow(inCell.row);
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// 0, 1, >=2 rows selected are the values that change command-enabling configurations.
|
||
int selectedRowCountForCommandStatus = GetSelectedRowCount();
|
||
if (selectedRowCountForCommandStatus > 2)
|
||
selectedRowCountForCommandStatus = 2;
|
||
Boolean result = false;
|
||
|
||
//
|
||
// There are two cases here: user clicks in the hilite column and they don't click in the
|
||
// hilite column. If they do, only select if the click is in the icon or the icon text. Any
|
||
// other click w/in that column unselects or start a marquee selection. If they click outside
|
||
// the hilite column, process as normal.
|
||
//
|
||
if ( (inCell.col != GetHiliteColumn()) || clickIsInIcon || clickIsInTitle ) {
|
||
if (LTableView::ClickSelect(inCell, inMouseDown))
|
||
{
|
||
// Handle it ourselves if the popup attachment doesn't want it.
|
||
CContextMenuAttachment::SExecuteParams params;
|
||
params.inMouseDown = &inMouseDown;
|
||
if (ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms))
|
||
{
|
||
// drag ? - don't become target
|
||
if (CellInitiatesDrag(inCell) && ::WaitMouseMoved(inMouseDown.macEvent.where))
|
||
DragSelection(inCell, inMouseDown);
|
||
else
|
||
{
|
||
// become target
|
||
if (!IsTarget())
|
||
SwitchTarget(this);
|
||
|
||
// check if the click is in the title of the icon. If so, and an editor is
|
||
// present and we're not doing a shift-selection extension and it's not a
|
||
// double click then invoke the inline editor. Otherwise process the click normally.
|
||
// (whew!)
|
||
//
|
||
// The reason for not inline editing on a double-click is that it allows the
|
||
// user to double-click anywhere on the item to open it, like in the Finder. Doing
|
||
// a double-click to edit an item doesn't make sense and the user would get
|
||
// confused. <20><><EFBFBD>Double click check doesn't work right now.....will fix later....<2E><><EFBFBD>
|
||
if ( clickIsInTitle && mNameEditor && !(inMouseDown.macEvent.modifiers & shiftKey)
|
||
&& GetClickCount() != 2 )
|
||
DoInlineEditing ( inCell, textRect );
|
||
else {
|
||
// open selection if we've got the right click count
|
||
if (GetClickCount() == mClickCountToOpen)
|
||
OpenSelection();
|
||
}
|
||
}
|
||
result = true;
|
||
}
|
||
}
|
||
} // if click in icon or text
|
||
else {
|
||
// since the click is not in the icon and not in the text, the selection should be
|
||
// cleared before we try to show any context menus. This is possibly faster than
|
||
// calling UnselectAllCells() because it only iterates over the selection, not the whole table.
|
||
static const Rect empty = { 0, 0, 0, 0 };
|
||
UnselectCellsNotInSelectionOutline(empty);
|
||
|
||
CContextMenuAttachment::SExecuteParams params;
|
||
params.inMouseDown = &inMouseDown;
|
||
if (ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms))
|
||
HandleSelectionTracking(inMouseDown);
|
||
}
|
||
|
||
cmd_status:
|
||
mFancyDoubleClick = savedFancyDoubleClick;
|
||
int newSelectedRowCountForCommandStatus = GetSelectedRowCount();
|
||
if (newSelectedRowCountForCommandStatus > 2)
|
||
newSelectedRowCountForCommandStatus = 2;
|
||
if (selectedRowCountForCommandStatus != newSelectedRowCountForCommandStatus)
|
||
SetUpdateCommandStatus(true);
|
||
return result;
|
||
} // CStandardFlexTable::ClickSelect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::ClickSelf(const SMouseDownEvent &inMouseDown)
|
||
// Overridden so that certain cells (e.g. toggled icons) don't select on click and to
|
||
// handle marquee selection.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
STableCell hitCell;
|
||
SPoint32 imagePt;
|
||
|
||
LocalToImagePoint(inMouseDown.whereLocal, imagePt);
|
||
if (GetCellHitBy(imagePt, hitCell))
|
||
{
|
||
// Handle drop-flag clicks first
|
||
if (ClickDropFlag(hitCell, inMouseDown))
|
||
return;
|
||
|
||
// See if the cell selects. If so go ahead and process it (could be a drag or selection
|
||
// outline). If the cell does not select, the cell may still really want the click (like
|
||
// the "marked read message" column in mail/news. If it does, give it the click, otherwise
|
||
// start doing a marquee selection.
|
||
if ( CellSelects(hitCell) ) {
|
||
if ( ClickSelect(hitCell, inMouseDown) )
|
||
ClickCell(hitCell, inMouseDown);
|
||
}
|
||
else {
|
||
if ( CellWantsClick(hitCell) )
|
||
ClickCell(hitCell, inMouseDown);
|
||
else
|
||
HandleSelectionTracking(inMouseDown);
|
||
}
|
||
}
|
||
else {
|
||
// since the click is not in any cell, the selection should be cleared before we try to
|
||
// show the context menus. This is possibly faster than calling UnselectAllCells() because
|
||
// it only iterates over the selection, not the whole table.
|
||
static const Rect empty = { 0, 0, 0, 0 };
|
||
UnselectCellsNotInSelectionOutline(empty);
|
||
|
||
CContextMenuAttachment::SExecuteParams params;
|
||
params.inMouseDown = &inMouseDown;
|
||
if ( ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms) )
|
||
HandleSelectionTracking(inMouseDown);
|
||
}
|
||
|
||
} // CStandardFlexTable::ClickSelf
|
||
|
||
|
||
//
|
||
// DoInlineEditing
|
||
//
|
||
// Perform the necessary setup for inline editing of a row's main text and then show the edit field.
|
||
// This version of the routine is public and does not require any external knowledge of table internals
|
||
// besides the cell that we should edit. Most of the work is done by calling the routine below
|
||
//
|
||
void
|
||
CStandardFlexTable::DoInlineEditing ( const STableCell &inCell )
|
||
{
|
||
Rect textRect;
|
||
GetHiliteTextRect ( inCell.row, textRect );
|
||
DoInlineEditing ( inCell, textRect );
|
||
|
||
} // DoInlineEditing
|
||
|
||
|
||
//
|
||
// DoInlineEditing
|
||
//
|
||
// Perform the necessary setup for inline editing of a row's main text and then show the edit field
|
||
//
|
||
void
|
||
CStandardFlexTable::DoInlineEditing ( const STableCell &inCell, Rect & inTextRect )
|
||
{
|
||
mRowBeingEdited = inCell.row;
|
||
if ( !CanDoInlineEditing() ) { // bail if inline editing is temporarily turned off
|
||
mRowBeingEdited = LArray::index_Bad;
|
||
return;
|
||
}
|
||
|
||
#if 0
|
||
// erase the text rectangle so that when the text field shrinks, you won't see the old name
|
||
// behind it. This is kinda skanky, but we need to make sure we draw the background color
|
||
// correctly.
|
||
//<2F><><EFBFBD>This doesn't work.
|
||
RGBColor backColor;
|
||
::GetCPixel ( inTextRect.right - 1, inTextRect.top - 1, &backColor );
|
||
::RGBBackColor(&backColor);
|
||
::EraseRect ( &inTextRect );
|
||
#endif
|
||
|
||
SPoint32 imagePoint;
|
||
LocalToImagePoint(topLeft(inTextRect), imagePoint);
|
||
imagePoint.h -= 1; imagePoint.v -= 1;
|
||
|
||
LCommander::SwitchTarget(mNameEditor);
|
||
|
||
// Don't broadcast that the selection has changed until we update the edit field
|
||
SetNotifyOnSelectionChange(false);
|
||
SelectCell(STableCell(mRowBeingEdited, GetHiliteColumn()));
|
||
SetNotifyOnSelectionChange(true);
|
||
|
||
char nameString[256];
|
||
GetHiliteText(mRowBeingEdited, nameString, 255, &inTextRect);
|
||
|
||
mNameEditor->UpdateEdit(LStr255(nameString), &imagePoint, nil);
|
||
|
||
SelectionChanged();
|
||
|
||
} // DoInlineEditing
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::CellSelects(const STableCell& /*inCell*/) const
|
||
// Determines if a cell is allowed to select the row.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return true;
|
||
} // CStandardFlexTable::CellSelects
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::HandleSelectionTracking ( const SMouseDownEvent & inMouseDown )
|
||
// Set up everything to start tracking a selection marquee
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// bail if the table specifically doesn't want the tracking.
|
||
if ( !TableDesiresSelectionTracking() )
|
||
return;
|
||
|
||
// Click is outside of any Cell. Unselect everything if the shift key is up.
|
||
if ( !(inMouseDown.macEvent.modifiers & shiftKey) )
|
||
UnselectAllCells();
|
||
|
||
// track the mouse for doing drag selection
|
||
FocusDraw();
|
||
TrackSelection( inMouseDown );
|
||
|
||
} // HandleSelectionTracking
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::TrackSelection( const SMouseDownEvent & inMouseDown )
|
||
// Do the tracking of when the mouse is down and the user wants to do a multiple selection
|
||
// by drawing a selection marquee.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Point originalLoc = inMouseDown.whereLocal;
|
||
|
||
Rect selectionRect = { 0, 0, 0, 0 }, iconHotSpotRect = { 0, 0, 0, 0 };
|
||
Rect cellHotSpotRect = { 0, 0, 0, 0 };
|
||
|
||
Point lastMousePoint = { 0, 0 }, curMousePoint = { 0, 0 }, curOrigin = { 0, 0 };
|
||
|
||
UInt32 currentRow = 0L, currentCol = 0L, maxRows = 0L, maxColumns = 0L;
|
||
|
||
STableCell topLeftCell( 0, 0 );
|
||
STableCell botRightCell( 0, 0 );
|
||
STableCell currentCell( 0, 0 );
|
||
|
||
StColorPenState oldPenState;
|
||
|
||
// mTableView->GetTableSize( maxRows, maxColumns );
|
||
|
||
SetNotifyOnSelectionChange(false);
|
||
while ( ::StillDown() ) {
|
||
|
||
::GetMouse( &curMousePoint );
|
||
|
||
if ( (curMousePoint.v != lastMousePoint.v) || (curMousePoint.h != lastMousePoint.h) ) {
|
||
|
||
currentCell.row = 0;
|
||
currentCell.col = 0;
|
||
|
||
::PenPat( &qd.gray );
|
||
::PenMode( patXor );
|
||
::PenSize( 2, 2 );
|
||
|
||
if ( !::EmptyRect(&selectionRect) )
|
||
::FrameRect( &selectionRect );
|
||
|
||
lastMousePoint = curMousePoint;
|
||
|
||
::Pt2Rect( curMousePoint, originalLoc, &selectionRect );
|
||
|
||
// Don't let the selection rect be empty. Trust me.
|
||
if ( (selectionRect.right - selectionRect.left) == 0 )
|
||
::InsetRect( &selectionRect, -1, 0 );
|
||
if ( (selectionRect.bottom - selectionRect.top) == 0 )
|
||
::InsetRect( &selectionRect, 0, -1 );
|
||
|
||
//
|
||
// Restore the text traits, scroll the table if needed, unselect all the cells
|
||
// that shouldn't be selected, select the ones that should be selected,
|
||
// reset the text traits and *then* finally draw the gray rect.
|
||
//
|
||
|
||
oldPenState.Restore();
|
||
|
||
if ( AutoScrollImage(curMousePoint) )
|
||
FocusDraw();
|
||
|
||
UnselectCellsNotInSelectionOutline(selectionRect);
|
||
|
||
// find what cells are in w/in the rectangle and select them if the rectangle
|
||
// encloses/touches the cell hot spot (usually the icon or icon text).
|
||
FetchIntersectingCells( selectionRect, topLeftCell, botRightCell );
|
||
for ( currentRow = topLeftCell.row; currentRow <= botRightCell.row; currentRow++ ) {
|
||
for ( currentCol = topLeftCell.col; currentCol <= botRightCell.col; currentCol++ ) {
|
||
|
||
currentCell.row = currentRow;
|
||
currentCell.col = currentCol;
|
||
|
||
if ( !CellIsSelected(currentCell) && HitCellHotSpot(currentCell, selectionRect) ) {
|
||
SelectCell( currentCell );
|
||
}
|
||
|
||
} // for each column
|
||
} // for each row
|
||
|
||
oldPenState.Save();
|
||
|
||
::PenPat( &qd.gray );
|
||
::PenMode( patXor );
|
||
::PenSize( 2, 2 );
|
||
::FrameRect( &selectionRect );
|
||
|
||
} // if mouse has moved
|
||
|
||
} // while mouse still down
|
||
|
||
SetNotifyOnSelectionChange(true);
|
||
SelectionChanged();
|
||
|
||
if ( !::EmptyRect(&selectionRect) )
|
||
::FrameRect( &selectionRect );
|
||
|
||
} // TrackSelection
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::UnselectCellsNotInSelectionOutline( const Rect & inSelectionRect )
|
||
// Iterate over existing selection looking for things that are no longer w/in
|
||
// the rectangle and unselect them.
|
||
//
|
||
// IMPORTANT: This routine assumes a selection model that selects the entire row.
|
||
// If another selection model is used (such as one that only selects individual
|
||
// cells), this needs to be rewritten.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
TableIndexT currentRow = LArray::index_Bad;
|
||
while ( GetNextSelectedRow(currentRow) ) {
|
||
|
||
STableCell currentCell;
|
||
currentCell.row = currentRow;
|
||
currentCell.col = GetHiliteColumn();
|
||
if ( !HitCellHotSpot( currentCell, inSelectionRect) ) {
|
||
UnselectCell( currentCell );
|
||
currentRow = currentCell.row;
|
||
}
|
||
|
||
}
|
||
|
||
} // UnselectCellsNotInSelectionOutline
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::HitCellHotSpot( const STableCell &inCell, const Rect &inTotalSelectionRect )
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Boolean result = false;
|
||
TableIndexT hiliteCol = GetHiliteColumn();
|
||
|
||
FocusDraw(); //to be on the safe side
|
||
|
||
if ( hiliteCol ) {
|
||
if ( hiliteCol == inCell.col ) {
|
||
Rect cellRect;
|
||
GetLocalCellRect(inCell, cellRect);
|
||
|
||
Rect textRect, iconRect;
|
||
GetHiliteTextRect ( inCell.row, textRect );
|
||
GetIconRect ( inCell, cellRect, iconRect );
|
||
if ( ::SectRect(&textRect, &inTotalSelectionRect, &textRect) ||
|
||
::SectRect(&iconRect, &inTotalSelectionRect, &iconRect) )
|
||
result = true;
|
||
}
|
||
}
|
||
else {
|
||
Rect cellRect = { 0, 0, 0, 0 };
|
||
GetLocalCellRect( inCell, cellRect );
|
||
result = ::SectRect( &cellRect, &inTotalSelectionRect, &cellRect );
|
||
}
|
||
|
||
return result;
|
||
|
||
} // HitCellHotSpot
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::BeTarget()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
LCommander::BeTarget();
|
||
Activate();
|
||
ExecuteAttachments(CTargetFramer::msg_BecomingTarget, this);
|
||
} // CStandardFlexTable::BeTarget
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DontBeTarget()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (mClickTimer)
|
||
mClickTimer->NoteSingleClick();
|
||
ExecuteAttachments(CTargetFramer::msg_ResigningTarget, this);
|
||
Deactivate();
|
||
LCommander::DontBeTarget();
|
||
} // CStandardFlexTable::DontBeTarget
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
PaneIDT CStandardFlexTable::GetCellDataType(const STableCell &inCell) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return mTableHeader->GetColumnPaneID(inCell.col);
|
||
} // CStandardFlexTable::GetCellDataType
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::GetNextSelectedRow(TableIndexT& inOutAfterRow) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
TableIndexT nRows, nCols;
|
||
GetTableSize(nRows, nCols);
|
||
STableCell cell(inOutAfterRow, nCols);
|
||
if (LTableView::GetNextSelectedCell(cell))
|
||
{
|
||
inOutAfterRow = cell.row;
|
||
return true;
|
||
}
|
||
return false;
|
||
|
||
} // CStandardFlexTable::GetNextSelectedRow
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::GetLastSelectedRow(TableIndexT& inOutBeforeRow) const
|
||
// Currently a little inefficient...
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
STableCell cell(inOutBeforeRow, 1);
|
||
while (cell.row-- > LArray::index_Bad)
|
||
{
|
||
if (CellIsSelected(cell))
|
||
{
|
||
inOutBeforeRow = cell.row;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
} // CStandardFlexTable::GetLastSelectedRow
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::FindCommandStatus(
|
||
CommandT inCommand,
|
||
Boolean &outEnabled,
|
||
Boolean &outUsesMark,
|
||
Char16 &outMark,
|
||
Str255 outName)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
switch (inCommand)
|
||
{
|
||
case cmd_GetInfo:
|
||
outEnabled = GetSelectedRowCount() == 1;
|
||
break;
|
||
// These commands require one or more lines to be selected.
|
||
case cmd_OpenSelection:
|
||
case cmd_OpenSelectionNewWindow:
|
||
case cmd_Clear:
|
||
case cmd_SecurityInfo:
|
||
outEnabled = GetSelectedRowCount() > 0;
|
||
break;
|
||
// Selection not necessary
|
||
case cmd_SelectAll:
|
||
outEnabled = true;
|
||
break;
|
||
default:
|
||
LCommander::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName);
|
||
break;
|
||
}
|
||
} // CStandardFlexTable::FindCommandStatus
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::ObeyCommand(
|
||
CommandT inCommand,
|
||
void *ioParam)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Boolean result = false;
|
||
switch(inCommand)
|
||
{
|
||
case cmd_OpenSelectionNewWindow:
|
||
case cmd_OpenSelection:
|
||
OpenSelection();
|
||
return true;
|
||
case cmd_GetInfo:
|
||
GetInfo();
|
||
return true;
|
||
case cmd_SelectAll:
|
||
SelectAllCells();
|
||
return true;
|
||
case cmd_Clear:
|
||
DeleteSelection();
|
||
return true;
|
||
default:
|
||
result = LCommander::ObeyCommand(inCommand, ioParam);
|
||
break;
|
||
}
|
||
return result;
|
||
} // CStandardFlexTable::ObeyCommand
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::HandleKeyPress( const EventRecord& inKeyEvent)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// We are NOT masking off keyUp events. So let's beware of them!
|
||
if (inKeyEvent.what == keyUp)
|
||
return false;
|
||
Char16 c = inKeyEvent.message & charCodeMask;
|
||
switch (c)
|
||
{
|
||
case char_Return:
|
||
case char_Enter:
|
||
OpenSelection();
|
||
return true;
|
||
case char_Backspace:
|
||
case char_FwdDelete:
|
||
DeleteSelection();
|
||
return true;
|
||
case char_LeftArrow:
|
||
case char_RightArrow:
|
||
if (GetSelectedRowCount() == 1)
|
||
{
|
||
STableCell cell = mTableSelector->GetFirstSelectedCell();
|
||
Boolean isCellExpanded;
|
||
// selected cell will be in column 1, but dropflag may not be.
|
||
for (; IsValidCell(cell); cell.col++)
|
||
{
|
||
if (CellHasDropFlag(cell, isCellExpanded))
|
||
{
|
||
SetCellExpansion(cell, c == char_RightArrow);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
default:
|
||
LCommander::HandleKeyPress(inKeyEvent);
|
||
return true;
|
||
} // switch
|
||
return false;
|
||
} // CStandardFlexTable::HandleKeyPress
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::OpenSelection()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
TableIndexT selectedRow = 0;
|
||
while (GetNextSelectedRow(selectedRow) && !CmdPeriod())
|
||
OpenRow(selectedRow);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::GetInfo()
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Base class does nothing
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::RowCanAcceptDrop(
|
||
DragReference /*inDragRef*/,
|
||
TableIndexT /*inDropRow*/)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return false;
|
||
} // CStandardFlexTable::RowCanAcceptDrop
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::RowCanAcceptDropBetweenAbove(
|
||
DragReference /*inDragRef*/,
|
||
TableIndexT /*inDropRow*/)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return false;
|
||
} // CStandardFlexTable::RowCanAcceptDropBetweenAbove
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
TableIndexT CStandardFlexTable::GetHiliteColumn() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return LArray::index_Bad; // default is to hilite the whole row.
|
||
} // CStandardFlexTable::GetHiliteColumn
|
||
|
||
|
||
//
|
||
// ComputeItemDropAreas
|
||
//
|
||
// When a drag goes over a cell that contains a non-folder, divide the node in half. The top
|
||
// half will represent dropping above the item, the bottom after.
|
||
//
|
||
void
|
||
CStandardFlexTable :: ComputeItemDropAreas ( const Rect & inLocalCellRect, Rect & oTop,
|
||
Rect & oBottom )
|
||
{
|
||
oBottom = oTop = inLocalCellRect;
|
||
uint16 midPt = (oTop.bottom - oTop.top) / 2;
|
||
oTop.bottom = oTop.top + midPt;
|
||
oBottom.top = oTop.bottom + 1;
|
||
|
||
} // ComputeItemDropAreas // ComputeItemDropAreas
|
||
|
||
|
||
//
|
||
// ComputeFolderDropAreas
|
||
//
|
||
// When a drag goes over a cell that contains a folder, divide the cell area into 3 parts. The middle
|
||
// area, which corresponds to a drop on the folder takes up the majority of the cell space with the
|
||
// top and bottom areas (corresponding to drop before and drop after, respectively) taking up the rest
|
||
// at the ends.
|
||
//
|
||
void
|
||
CStandardFlexTable :: ComputeFolderDropAreas ( const Rect & inLocalCellRect, Rect & oTop,
|
||
Rect & oBottom )
|
||
{
|
||
const Uint8 capAreaHeight = 3;
|
||
|
||
oBottom = oTop = inLocalCellRect;
|
||
oTop.bottom = oTop.top + capAreaHeight;
|
||
oBottom.top = oBottom.bottom - capAreaHeight;
|
||
|
||
} // ComputeFolderDropAreas
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::InsideDropArea(DragReference inDragRef)
|
||
// (override from LDragAndDrop)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Point mouseLoc;
|
||
SPoint32 imagePt;
|
||
Rect rowBounds; // only top/bottom are important
|
||
|
||
FocusDraw();
|
||
|
||
::GetDragMouse(inDragRef, &mouseLoc, NULL);
|
||
::GlobalToLocal(&mouseLoc);
|
||
// Auto scroll code -- jrm 97/05/03
|
||
if (AutoScrollImage(mouseLoc))
|
||
{
|
||
Rect frame;
|
||
CalcLocalFrameRect(frame);
|
||
Int32 pt = ::PinRect(&frame, mouseLoc);
|
||
mouseLoc = *(Point*)&pt;
|
||
}
|
||
LocalToImagePoint(mouseLoc, imagePt);
|
||
TableIndexT rowMouseIsOver = mTableGeometry->GetRowHitBy(imagePt);
|
||
GetLocalCellRect ( STableCell(rowMouseIsOver, GetHiliteColumn()), rowBounds );
|
||
|
||
// Find the index of where we want to drop the item and if it is between two rows
|
||
// of if it is on a row (can only happen if row is a container).
|
||
bool newIsDropBetweenRows = false;
|
||
TableIndexT newDropRow = LArray::index_Bad;
|
||
if ( rowMouseIsOver >= 1 )
|
||
{
|
||
// is the current row in the existing table or off the end? If it's off the end, we
|
||
// know we're dropping between rows.
|
||
if ( rowMouseIsOver <= mRows ) {
|
||
// divide row horizontally into several parts (3 if a container, 2 if not) and determine which
|
||
// part of the row the mouse is over. This will determine if we should interpret this as
|
||
// a drop before/after or a drop on.
|
||
if ( RowIsContainer(rowMouseIsOver) ) {
|
||
Rect topArea, bottomArea;
|
||
ComputeFolderDropAreas ( rowBounds, topArea, bottomArea );
|
||
if ( ::PtInRect(mouseLoc, &topArea) ) {
|
||
newDropRow = rowMouseIsOver; // before current row
|
||
newIsDropBetweenRows = true;
|
||
}
|
||
else if ( ::PtInRect(mouseLoc, &bottomArea) ) {
|
||
newDropRow = rowMouseIsOver + 1; // after current row
|
||
newIsDropBetweenRows = true;
|
||
}
|
||
else
|
||
newDropRow = rowMouseIsOver; // hilite folder, don't draw line
|
||
}
|
||
else {
|
||
Rect topArea, bottomArea;
|
||
ComputeItemDropAreas ( rowBounds, topArea, bottomArea );
|
||
if ( ::PtInRect(mouseLoc, &topArea) )
|
||
newDropRow = rowMouseIsOver; // before current row
|
||
else
|
||
newDropRow = rowMouseIsOver + 1; // after current row
|
||
newIsDropBetweenRows = true; // draw line between rows
|
||
}
|
||
}
|
||
else {
|
||
newDropRow = mRows + 1;
|
||
newIsDropBetweenRows = true;
|
||
newDropRow = rowMouseIsOver;
|
||
}
|
||
|
||
// we now know where the drop SHOULD go, now check if it CAN go there.
|
||
newIsDropBetweenRows &= RowCanAcceptDropBetweenAbove(inDragRef, newDropRow);
|
||
if (!newIsDropBetweenRows && !RowCanAcceptDrop(inDragRef, newDropRow))
|
||
newDropRow = LArray::index_Bad;
|
||
}
|
||
|
||
// if things have changed from last time, unhilite the last drop feedback and redraw
|
||
// the new drop feedback.
|
||
if (newDropRow != mDropRow || newIsDropBetweenRows != mIsDropBetweenRows)
|
||
{
|
||
TableIndexT oldDropRow = mDropRow;
|
||
mDropRow = LArray::index_Bad; // so that we'll unhilite
|
||
HiliteDropRow(oldDropRow, mIsDropBetweenRows);
|
||
mIsDropBetweenRows = newIsDropBetweenRows;
|
||
|
||
if (newDropRow > LArray::index_Bad)
|
||
{
|
||
mDropRow = newDropRow; // so that we'll hilite
|
||
HiliteDropRow(newDropRow, mIsDropBetweenRows);
|
||
}
|
||
else
|
||
mDropRow = LArray::index_Bad;
|
||
}
|
||
|
||
} // CStandardFlexTable::InsideDropArea
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::HiliteDropRow(TableIndexT inRow, Boolean inDrawBarAbove)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (inRow == LArray::index_Bad)
|
||
return;
|
||
|
||
STableCell cell(inRow, 1);
|
||
if (mIsInternalDrop && CellIsSelected(cell))
|
||
return; // Don't show hiliting feedback for a drag when over the selection.
|
||
|
||
TableIndexT dropHiliteColumn = GetHiliteColumn();
|
||
if ( dropHiliteColumn != LArray::index_Bad )
|
||
cell.col = dropHiliteColumn;
|
||
else
|
||
cell.col = 1;
|
||
Rect r;
|
||
GetLocalCellRect(cell, r);
|
||
if (inDrawBarAbove || dropHiliteColumn == LArray::index_Bad)
|
||
{
|
||
r.left = 0;
|
||
r.right = mFrameSize.width;
|
||
if (inDrawBarAbove)
|
||
{
|
||
#if 1
|
||
// here's how you draw a bar above.
|
||
r.bottom = r.top + 1;
|
||
r.top -= 1;
|
||
#else
|
||
// here's how you underline below.
|
||
r.top = r.bottom - 1;
|
||
r.bottom += 1;
|
||
#endif
|
||
}
|
||
DoHiliteRect(r);
|
||
}
|
||
else
|
||
{
|
||
DrawIcons(cell, r);
|
||
StRegion hiliteRgn;
|
||
GetRowHiliteRgn(inRow, hiliteRgn);
|
||
::InvertRgn(hiliteRgn);
|
||
}
|
||
} // CStandardFlexTable::HiliteDropRow
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DrawCellContents(
|
||
const STableCell& /*inCell*/,
|
||
const Rect& /*inLocalRect*/)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DrawCell(
|
||
const STableCell& inCell,
|
||
const Rect& inLocalRect)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
// Compute the update rgn's intersection with the cell
|
||
// bounds and bail if they're disjoint.
|
||
RgnHandle updateRgn = GetLocalUpdateRgn();
|
||
if (!updateRgn)
|
||
return;
|
||
Rect updateRect = (**updateRgn).rgnBBox;
|
||
::DisposeRgn(updateRgn);
|
||
|
||
Rect intersection;
|
||
if (!::SectRect(&updateRect, &inLocalRect, &intersection))
|
||
return;
|
||
|
||
// Save clip, and clip to the cell/updateRgn intersection
|
||
StClipRgnState savedClip;
|
||
::ClipRect(&intersection);
|
||
|
||
StColorState penState;
|
||
RGBColor white = { 0xFFFF, 0xFFFF, 0xFFFF };
|
||
::RGBBackColor(&white);
|
||
|
||
// Inset the rectangle on the right for better separation
|
||
// (long text looks bad when truncated).
|
||
Rect localRect = inLocalRect;
|
||
Rect insetRect = inLocalRect;
|
||
insetRect.right--;
|
||
// The rightmost column overlaps the scrollbar. We must not draw on the overlap.
|
||
if (inCell.col == mTableHeader->CountVisibleColumns())
|
||
localRect = insetRect;
|
||
::EraseRect(&inLocalRect);
|
||
|
||
ApplyTextStyle(inCell.row);
|
||
if ( mRowBeingEdited != inCell.row )
|
||
DrawCellContents(inCell, insetRect);
|
||
} // CStandardFlexTable::DrawCell
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::GetRowDragRgn(TableIndexT inRow, RgnHandle ioHiliteRgn) const
|
||
// The drag region is the hilite region plus the icon
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
::SetEmptyRgn(ioHiliteRgn);
|
||
Rect cellRect;
|
||
TableIndexT col = GetHiliteColumn();
|
||
if ( !col )
|
||
col = 1;
|
||
STableCell cell(inRow, col);
|
||
if (!GetLocalCellRect(cell, cellRect))
|
||
return false;
|
||
ResIDT iconID = GetIconID(inRow);
|
||
if (iconID)
|
||
{
|
||
GetIconRect(cell, cellRect, cellRect);
|
||
::IconIDToRgn(ioHiliteRgn, &cellRect, atNone, iconID);
|
||
}
|
||
StRegion cellRgn;
|
||
GetRowHiliteRgn(inRow, cellRgn);
|
||
::UnionRgn(ioHiliteRgn, cellRgn, ioHiliteRgn);
|
||
return true;
|
||
} // CStandardFlexTable::GetRowHiliteRgn
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::GetRowHiliteRgn(TableIndexT inRow, RgnHandle ioHiliteRgn) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
::SetEmptyRgn(ioHiliteRgn);
|
||
Rect cellRect;
|
||
if (!GetHiliteTextRect(inRow, cellRect))
|
||
return false;
|
||
::RectRgn(ioHiliteRgn, &cellRect);
|
||
return true;
|
||
} // CStandardFlexTable::GetRowHiliteRgn
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::GetHiliteRgn(RgnHandle ioHiliteRgn)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
::SetEmptyRgn(ioHiliteRgn);
|
||
Rect visRect;
|
||
GetRevealedRect(visRect); // Check if Table is revealed
|
||
if (!::EmptyRect(&visRect))
|
||
{
|
||
PortToLocalPoint(topLeft(visRect));
|
||
PortToLocalPoint(botRight(visRect));
|
||
|
||
STableCell topLeftCell, botRightCell;
|
||
FetchIntersectingCells(visRect, topLeftCell, botRightCell);
|
||
|
||
StRegion cellRgn;
|
||
STableCell cell(1, GetHiliteColumn()); // Loop thru all cells
|
||
for (cell.row = topLeftCell.row; cell.row <= botRightCell.row; cell.row++)
|
||
{
|
||
if (CellIsSelected(cell))
|
||
{
|
||
if (GetRowHiliteRgn(cell.row, cellRgn))
|
||
::UnionRgn(ioHiliteRgn, cellRgn, ioHiliteRgn);
|
||
}
|
||
}
|
||
}
|
||
} // CStandardFlexTable::GetHiliteRgn
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::EnterDropArea(
|
||
DragReference /*inDragRef*/,
|
||
Boolean /*inDragHasLeftSender*/)
|
||
// CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row
|
||
// scenario
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
} // CStandardFlexTable::EnterDropArea
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::LeaveDropArea(DragReference /*inDragRef*/)
|
||
// CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row
|
||
// scenario
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
FocusDraw();
|
||
if (mDropRow)
|
||
{
|
||
TableIndexT oldDropRow = mDropRow;
|
||
mDropRow = LArray::index_Bad; // so that we'll unhilite
|
||
HiliteDropRow(oldDropRow, mIsDropBetweenRows);
|
||
}
|
||
|
||
// HiliteSelection( IsActive(), true); // Finder does not do this.
|
||
mIsDropBetweenRows = false;
|
||
mIsInternalDrop = false;
|
||
} // CStandardFlexTable::LeaveDropArea
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::PointInDropArea(Point inGlobalPt)
|
||
// Overridden to return true just above and below the view, to allow autoscroll
|
||
// (override from LDragAndDrop)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
#define kSlopPixels 20
|
||
if (!IsEnabled())
|
||
return false;
|
||
Point portPoint = inGlobalPt;
|
||
GlobalToPortPoint(portPoint);
|
||
return ( (portPoint.h >= mFrameLocation.h) &&
|
||
(portPoint.h < mFrameLocation.h + mFrameSize.width) &&
|
||
(portPoint.v >= mFrameLocation.v - kSlopPixels) &&
|
||
(portPoint.v < mFrameLocation.v + mFrameSize.height + kSlopPixels) );
|
||
} // CStandardFlexTable::PointInDropArea
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::ScrollImageBy(
|
||
Int32 inLeftDelta,
|
||
Int32 inTopDelta,
|
||
Boolean inRefresh)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Boolean mayAutoScrollLeavingTurds = inRefresh && IsTarget(); //&& mDropRow;
|
||
// Turds will be left if we call scrollbits in ScrollImageBy
|
||
if (mayAutoScrollLeavingTurds)
|
||
{
|
||
// Notify the CTargetFramer (if any) to erase the frame hilite
|
||
ExecuteAttachments(CTargetFramer::msg_ResigningTarget, this);
|
||
// invalidates the border, and stops the refresh from drawing a new one
|
||
// If we are about to autoscroll and we are calling scrollbits, unhilite
|
||
// the drop row as well
|
||
TableIndexT dropRow = mDropRow;
|
||
mDropRow = LArray::index_Bad; // So that we'll unhilite
|
||
HiliteDropRow(dropRow, mIsDropBetweenRows);
|
||
mDropRow = dropRow; // so that we'll hilite
|
||
}
|
||
Inherited::ScrollImageBy(inLeftDelta, inTopDelta, inRefresh);
|
||
if (mayAutoScrollLeavingTurds && FocusDraw())
|
||
{
|
||
// Turn hiliting back on
|
||
HiliteDropRow(mDropRow, mIsDropBetweenRows);
|
||
// Notify the CTargetFramer to draw the border now.
|
||
ExecuteAttachments(CTargetFramer::msg_BecomingTarget, this); // invert
|
||
}
|
||
} // CStandardFlexTable::ScrollImageBy
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DragSelection(
|
||
const STableCell& inCell,
|
||
const SMouseDownEvent& inMouseDown )
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
#pragma unused(inCell)
|
||
|
||
DragReference dragRef = 0;
|
||
|
||
if (!FocusDraw()) return;
|
||
|
||
try
|
||
{
|
||
StRegion dragRgn;
|
||
OSErr err = ::NewDrag(&dragRef);
|
||
ThrowIfOSErr_(err);
|
||
AddSelectionToDrag(dragRef, dragRgn);
|
||
if (inMouseDown.macEvent.modifiers & optionKey != 0)
|
||
{
|
||
CursHandle copyDragCursor = ::GetCursor(6608); // finder's copy-drag cursor
|
||
if (copyDragCursor != nil)
|
||
::SetCursor(*copyDragCursor);
|
||
}
|
||
(void) ::TrackDrag(dragRef, &(inMouseDown.macEvent), dragRgn);
|
||
}
|
||
catch( ... )
|
||
{
|
||
}
|
||
|
||
if (dragRef != 0)
|
||
::DisposeDrag(dragRef);
|
||
} // CStandardFlexTable::DragSelection
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::AddSelectionToDrag(DragReference inDragRef, RgnHandle inDragRgn)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
StRegion tempRgn;
|
||
|
||
::SetEmptyRgn(inDragRgn);
|
||
|
||
STableCell cell(0, 1);
|
||
while (GetNextSelectedRow(cell.row))
|
||
{
|
||
AddRowDataToDrag(cell.row, inDragRef);
|
||
GetRowDragRgn(cell.row, tempRgn);
|
||
::UnionRgn(tempRgn, inDragRgn, inDragRgn);
|
||
}
|
||
::CopyRgn(inDragRgn, tempRgn);
|
||
::InsetRgn(tempRgn, 1, 1);
|
||
::DiffRgn(inDragRgn, tempRgn, inDragRgn);
|
||
|
||
// Convert the region to global coordinates
|
||
Point p;
|
||
p.h = p.v = 0;
|
||
::LocalToGlobal(&p);
|
||
::OffsetRgn(inDragRgn, p.h, p.v);
|
||
} // CStandardFlexTable::AddSelectionToDrag
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::ChangeSort(const LTableHeader::SortChange* /*inSortChange*/)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
} // CStandardFlexTable::ChangeSort
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::GetDropFlagRect(
|
||
const Rect& inLocalCellRect,
|
||
Rect& outFlagRect ) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
UInt32 vDiff = (inLocalCellRect.bottom - inLocalCellRect.top - 12) >> 1;
|
||
|
||
outFlagRect.top = inLocalCellRect.top + vDiff;
|
||
outFlagRect.bottom = outFlagRect.top + 12;
|
||
|
||
outFlagRect.left = inLocalCellRect.left + 3;
|
||
outFlagRect.right = outFlagRect.left + 16;
|
||
} // CStandardFlexTable::GetDropFlagRect
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
TableIndexT CStandardFlexTable::GetSelectedRowCount() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return ((LTableRowSelector*)mTableSelector)->GetSelectedRowCount();
|
||
// safe cast.
|
||
} // CStandardFlexTable::GetSelectedRowCount
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::SetNotifyOnSelectionChange(Boolean inDoNotify)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (inDoNotify)
|
||
StartBroadcasting();
|
||
else
|
||
StopBroadcasting();
|
||
} // CStandardFlexTable::SetNotifyOnSelectionChange
|
||
|
||
//-----------------------------------
|
||
Boolean CStandardFlexTable::GetNotifyOnSelectionChange()
|
||
//-----------------------------------
|
||
{
|
||
return IsBroadcasting();
|
||
|
||
} // CStandardFlexTable::GetNotifyOnSelectionChange
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
const TableIndexT* CStandardFlexTable::GetUpdatedSelectionList(TableIndexT& outSelectionSize)
|
||
// Returns a ONE-BASED list of TableIndexT, as it should.
|
||
// The result is const because this is not a copy, it is
|
||
// the actual list cached in this object. You probably want
|
||
// to use CMailFlexTable::GetSelection() for message stuff.
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
outSelectionSize = GetSelectedRowCount();
|
||
// if we've got a selection list, it's assumed up to date,
|
||
// so return it. When the selection changes, we just get
|
||
// rid of it (in SelectionChanged()) and set mSelectionList to NULL
|
||
if (mSelectionList)
|
||
return mSelectionList;
|
||
// If there is a selection, allocate the list
|
||
if (outSelectionSize > 0)
|
||
{
|
||
mSelectionList = new TableIndexT[outSelectionSize];
|
||
if (!mSelectionList)
|
||
outSelectionSize = 0;
|
||
}
|
||
if (mSelectionList)
|
||
{
|
||
TableIndexT selectedRow = 0;
|
||
TableIndexT *item = mSelectionList;
|
||
for (int i = 0; i < outSelectionSize; i++)
|
||
{
|
||
Boolean ok = GetNextSelectedRow(selectedRow);
|
||
Assert_(ok);
|
||
if (!ok) break;
|
||
*item++ = selectedRow;
|
||
}
|
||
}
|
||
return mSelectionList;
|
||
} // CStandardFlexTable::GetUpdatedSelectionList
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::IsColumnVisible(PaneIDT inID)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return mTableHeader->IsColumnVisible(inID);
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::SelectionChanged()
|
||
// invalidate any cached selection list
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
delete [] mSelectionList;
|
||
mSelectionList = NULL;
|
||
BroadcastMessage(msg_SelectionChanged, this);
|
||
} // CStandardFlexTable::SelectionChanged
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DrawTextString(
|
||
const char* inText,
|
||
const FontInfo* inFontInfo,
|
||
SInt16 inMargin,
|
||
const Rect& inBounds,
|
||
SInt16 inJustification,
|
||
Boolean inDoTruncate,
|
||
TruncCode inTruncWhere)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Rect r = inBounds;
|
||
|
||
r.left += inMargin;
|
||
r.right -= inMargin;
|
||
|
||
UGraphicGizmos::PlaceTextInRect(inText,
|
||
strlen(inText),
|
||
r,
|
||
inJustification,
|
||
teCenter,
|
||
inFontInfo,
|
||
inDoTruncate,
|
||
inTruncWhere );
|
||
} // CStandardFlexTable::DrawTextString
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DrawIconFamily(
|
||
ResIDT inIconID,
|
||
SInt16 inIconWidth,
|
||
SInt16 inIconHeight,
|
||
IconTransformType inTransform,
|
||
const Rect& inBounds)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
OSErr err;
|
||
Rect iconRect = inBounds;
|
||
|
||
iconRect.right = iconRect.left + inIconWidth;
|
||
iconRect.bottom = iconRect.top + inIconHeight;
|
||
|
||
UGraphicGizmos::CenterRectOnRect(iconRect, inBounds);
|
||
err = ::PlotIconID(&iconRect, atNone, inTransform, inIconID);
|
||
} // CStandardFlexTable::DrawIconFamily
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
SInt16 CStandardFlexTable::DrawIcons(
|
||
const STableCell& inCell,
|
||
const Rect& inLocalRect) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
Rect iconRect;
|
||
GetDropFlagRect(inLocalRect, iconRect);
|
||
// Draw the drop flag if the folder has subfolders
|
||
// UI issue: the following test makes us differ from the Finder: there, all
|
||
// folders have dropflags, whether they have subfolders or not. This diff may confuse
|
||
// users... if so, just comment out the "if" line. - jrm
|
||
Boolean expanded;
|
||
if (CellHasDropFlag(inCell, expanded))
|
||
{
|
||
StColorState state; // LDropFlag::Draw has a bug (clobbers the color).
|
||
LDropFlag::Draw(iconRect, expanded);
|
||
}
|
||
|
||
// Work out the correct transform type, and then call our virtual method so that
|
||
// derived classes can draw the icons they want.
|
||
IconTransformType transformType = kTransformNone;
|
||
if (mClickTimer)
|
||
{
|
||
// A fancy double-click is in progress.
|
||
// Only the fake selection should be hilited
|
||
if (inCell.row == mClickTimer->GetClickedRow())
|
||
transformType = kTransformSelected;
|
||
}
|
||
else
|
||
{
|
||
if (inCell.row == mDropRow)
|
||
{
|
||
// Drop-target always hilited
|
||
transformType = kTransformSelected;
|
||
}
|
||
else if (IsActive() && CellIsSelected(inCell))
|
||
{
|
||
// Only the real selection is hilited (and only in the active pane)
|
||
transformType = kTransformSelected;
|
||
}
|
||
}
|
||
GetIconRect(inCell, inLocalRect, iconRect);
|
||
DrawIconsSelf(inCell, transformType, iconRect);
|
||
return iconRect.right;
|
||
} // CStandardFlexTable::DrawIcons
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::DrawIconsSelf(
|
||
const STableCell& inCell,
|
||
IconTransformType inTransformType,
|
||
const Rect& inIconRect) const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
ResIDT iconID = GetIconID(inCell.row);
|
||
if (iconID)
|
||
DrawIconFamily(iconID, 16, 16, inTransformType, inIconRect);
|
||
} // CStandardFlexTable::DrawIconsSelf
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Boolean CStandardFlexTable::IsSortedBackwards() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
return mTableHeader->IsSortedBackwards();
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
PaneIDT CStandardFlexTable::GetSortedColumn() const
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
PaneIDT result;
|
||
mTableHeader->GetSortedColumn(result);
|
||
return result;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::SetRightmostVisibleColumn(UInt16 inLastDesiredColumn)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
mTableHeader->SetRightmostVisibleColumn(inLastDesiredColumn);
|
||
} // CStandardFlexTable::SetRightmostVisibleColumn
|
||
|
||
#pragma mark -
|
||
#if defined(QAP_BUILD)
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::QapGetListInfo(PQAPLISTINFO pInfo)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (pInfo == nil)
|
||
return;
|
||
|
||
// get table size
|
||
TableIndexT outRows, outCols;
|
||
GetTableSize(outRows, outCols);
|
||
|
||
// get range of cells that are within the frame
|
||
Rect frame;
|
||
CalcLocalFrameRect(frame);
|
||
STableCell topLeftCell, botRightCell;
|
||
FetchIntersectingCells(frame, topLeftCell, botRightCell);
|
||
|
||
// fetch vertical scrollbar Macintosh control
|
||
ControlHandle macVScroll = NULL;
|
||
LScroller *myScroller = dynamic_cast<LScroller *>(GetSuperView());
|
||
if (myScroller != NULL)
|
||
{
|
||
if (myScroller->GetVScrollbar() != NULL)
|
||
macVScroll = myScroller->GetVScrollbar()->GetMacControl();
|
||
}
|
||
|
||
pInfo->itemCount = (short)outRows;
|
||
pInfo->topIndex = topLeftCell.row;
|
||
pInfo->itemHeight = GetRowHeight(0);
|
||
pInfo->visibleCount = botRightCell.row - topLeftCell.row + 1;
|
||
pInfo->vScroll = macVScroll;
|
||
pInfo->isMultiSel = true;
|
||
pInfo->isExtendSel = true;
|
||
pInfo->hasText = true;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
Ptr CStandardFlexTable::QapAddCellToBuf(Ptr pBuf, Ptr pLimit, const STableCell& sTblCell)
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
char str[cMaxMailFolderNameLength + 1];
|
||
short len = 0;
|
||
|
||
GetQapRowText(sTblCell.row, str, sizeof(str));
|
||
len = strlen(str) + 1;
|
||
|
||
if (pBuf + sizeof(short) + len >= pLimit)
|
||
return NULL;
|
||
|
||
*(unsigned short *)pBuf = sTblCell.row - 1;
|
||
if (CellIsSelected(sTblCell))
|
||
*(unsigned short *)pBuf |= 0x8000;
|
||
|
||
pBuf += sizeof(short);
|
||
|
||
strcpy(pBuf, str);
|
||
pBuf += len;
|
||
|
||
return pBuf;
|
||
}
|
||
|
||
//----------------------------------------------------------------------------------------
|
||
void CStandardFlexTable::GetQapRowText(
|
||
TableIndexT /*inRow*/,
|
||
char* outText,
|
||
UInt16 inMaxBufferLength) const
|
||
// Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
|
||
// Return result indicates if any of the text is visible in the cell
|
||
//----------------------------------------------------------------------------------------
|
||
{
|
||
if (outText && inMaxBufferLength > 0)
|
||
*outText = '\0';
|
||
} // CStandardFlexTable::GetQapRowText
|
||
|
||
#endif //QAP_BUILD
|