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

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /*
  20.     
  21.     Created 3/25/96 - Tim Craycroft
  22.     
  23. */
  24.  
  25.  
  26. #pragma mark --- Includes
  27.  
  28. #include "CStandardFlexTable.h"
  29.  
  30. // Mac
  31. #include <Drag.h>
  32.  
  33. // PowerPlant
  34. #include <LTableMultiSelector.h>
  35. #include <LTableArrayStorage.h>
  36. #include <URegions.h>
  37. #include <LDropFlag.h>
  38.  
  39. // ANSI
  40. #include <string.h>
  41. #include <stdio.h>
  42.  
  43. // Netscape Mac Libs
  44. #include "LFlexTableGeometry.h"
  45. #include "UGraphicGizmos.h"
  46. #include "LTableRowSelector.h"
  47. #include "CTableKeyAttachment.h"
  48. #include "CContextMenuAttachment.h"
  49. #include "CTargetFramer.h"
  50. #include "UMailFolderMenus.h"
  51.  
  52. #include "macutil.h"
  53. #include "resgui.h"
  54. #include "secnav.h"
  55. #include "cstring.h"
  56.  
  57. #include "CInlineEditField.h"
  58.  
  59. #pragma mark --- CTableHeaderListener
  60.  
  61. //----------------------------------------------------------------------------------------
  62. void CTableHeaderListener::ListenToMessage(MessageT inMessage, void *ioParam)
  63. //----------------------------------------------------------------------------------------
  64. {
  65.     if (inMessage == LTableHeader::msg_SortedColumnChanged) {
  66.         mTable->ChangeSort((const LTableHeader::SortChange*)ioParam);
  67.     }
  68. } // CTableHeaderListener::ListenToMessage
  69.  
  70.  
  71. #pragma mark --- CTableHeaderListener
  72.  
  73.  
  74. //
  75. // ListenToMessage
  76. //
  77. // Respond to changes in the inline editor
  78. //
  79. void 
  80. CInlineEditorListener::ListenToMessage ( MessageT inMessage, void * /*ioParam*/ )
  81. {
  82.     switch ( inMessage ) {
  83.     
  84.         case CInlineEditField::msg_InlineEditFieldChanged:
  85.             mTable->InlineEditorTextChanged();
  86.             break;
  87.                 
  88.         case CInlineEditField::msg_HidingInlineEditField:
  89.             mTable->InlineEditorDone();
  90.             mTable->mRowBeingEdited = LArray::index_Bad;
  91.             break;
  92.  
  93.     } // case of which message
  94.     
  95. } // ListenToMessage
  96.  
  97. #pragma mark --- CClickTimer
  98.  
  99. //========================================================================================
  100. class CClickTimer : public LPeriodical
  101. //========================================================================================
  102. {
  103. public:
  104.                     CClickTimer(
  105.                         CStandardFlexTable* inTable,
  106.                         const STableCell& inCell,
  107.                         const SMouseDownEvent& inMouseDown);
  108.     virtual            ~CClickTimer();
  109.     void            NoteDoubleClick();
  110.     void            NoteSingleClick();
  111.     TableIndexT        GetClickedRow() { return mCell.row; }
  112.  
  113. protected:    
  114.     void            ReverseTheHilite();
  115.     virtual void    SpendTime(const EventRecord& inMacEvent);
  116.  
  117. // Data:
  118. protected:
  119.     CStandardFlexTable* mTable;
  120.     STableCell            mCell;
  121.     SMouseDownEvent        mMouseDown; // Can't be a reference, orig. is on the stack & dies
  122. }; // class CClickTimer
  123.  
  124. //----------------------------------------------------------------------------------------
  125. CClickTimer::CClickTimer(
  126.     CStandardFlexTable* inTable,
  127.     const STableCell& inCell,
  128.     const SMouseDownEvent& inMouseDown)
  129. //----------------------------------------------------------------------------------------
  130. :    mTable(inTable)
  131. ,    mCell(inCell)
  132. ,    mMouseDown(inMouseDown)
  133. {
  134.     mTable->mClickTimer = this;
  135.     ReverseTheHilite(); // fake a new selection (temporarily).
  136.     StartRepeating();
  137. } //  CClickTimer::CClickTimer()
  138.  
  139. //----------------------------------------------------------------------------------------
  140. CClickTimer::~CClickTimer()
  141. //----------------------------------------------------------------------------------------
  142. {
  143.     mTable->mClickTimer = nil;
  144.     mTable->SetHiliteDisabled(false); // make sure it's normal
  145. } //  CClickTimer::~CClickTimer()
  146.  
  147. //----------------------------------------------------------------------------------------
  148. void CClickTimer::SpendTime(const EventRecord& inMacEvent)
  149. //----------------------------------------------------------------------------------------
  150. {
  151.     if (inMacEvent.when >= mMouseDown.macEvent.when + LMGetDoubleTime())
  152.         NoteSingleClick();
  153. } // CClickTimer::SpendTime()
  154.  
  155. //----------------------------------------------------------------------------------------
  156. void CClickTimer::NoteDoubleClick()
  157. //----------------------------------------------------------------------------------------
  158. {
  159.     ReverseTheHilite(); // Show the selection again
  160.     delete this;
  161. } // CClickTimer::NoteDoubleClick()
  162.  
  163. //----------------------------------------------------------------------------------------
  164. void CClickTimer::NoteSingleClick()
  165. //----------------------------------------------------------------------------------------
  166. {
  167.     try
  168.     {
  169.         //    ReverseTheHilite(); // Show the selection again
  170.         // The hiliting is now correct. Turn off hiliting to avoid a double flash.
  171.         mTable->SetHiliteDisabled(true);
  172.         mTable->SetFancyDoubleClick(false);
  173.         mTable->ClickSelect(mCell, mMouseDown);
  174.         mTable->SetFancyDoubleClick(true);
  175.     }
  176.     catch (...)
  177.     {
  178.         delete this;
  179.         throw;
  180.     }
  181.     delete this;
  182. } // CClickTimer::NoteSingleClick()
  183.  
  184. const Boolean ignore = true;
  185.  
  186. //----------------------------------------------------------------------------------------
  187. void CClickTimer::ReverseTheHilite()
  188. // This relies on the implementation of HiliteRow, which ignores the "inActively"
  189. // parameter.
  190. //----------------------------------------------------------------------------------------
  191. {
  192.     STableCell cell = mTable->GetFirstSelectedCell();
  193.     UInt32 total = mTable->GetSelectedRowCount();
  194.     UInt32 count = 0;
  195.     if (total > 0)
  196.         for (; cell.row <= mTable->mRows && count < total; cell.row++)
  197.         {
  198.             if (mTable->CellIsSelected(cell))
  199.             {
  200.                 mTable->HiliteRow(cell.row, ignore);
  201.                 count++;
  202.             }
  203.         }
  204.     mTable->mDropRow = cell.row; // fake out the icon to draw selected
  205.     mTable->HiliteRow(mCell.row, ignore);
  206.     mTable->mDropRow = LArray::index_Bad;
  207. } // CClickTimer::ReverseTheHilite
  208.  
  209. #pragma mark --- CStandardFlexTable
  210.  
  211. //----------------------------------------------------------------------------------------
  212. CStandardFlexTable::CStandardFlexTable(LStream *inStream)
  213. //----------------------------------------------------------------------------------------
  214. :    CSpecialTableView(inStream)
  215. ,    LDragAndDrop(GetMacPort(), this)
  216. ,    LCommander()
  217. ,    LBroadcaster()
  218. //,    CQAPartnerTableMixin(this)
  219. ,    mTableHeaderListener(this)
  220. ,    mTableHeader(NULL)
  221. ,    mSelectionList(NULL)
  222. ,    mClickTimer(nil)
  223. ,    mFancyDoubleClick(false)
  224. ,    mHiliteDisabled(false)
  225. ,    mDropRow(LArray::index_Bad)
  226. ,    mIsInternalDrop(false)
  227. ,    mIsDropBetweenRows(false)
  228. ,    mNameEditor(NULL)
  229. ,    mRowBeingEdited(0)
  230. ,    mInlineListener(this)
  231. {
  232.     *inStream >> mTableHeaderPaneID;
  233.     *inStream >> mTextTraitsID;
  234.     *inStream >> mClickCountToOpen;
  235.     
  236.     SetRefreshAllWhenResized(true);
  237. } // CStandardFlexTable::CStandardFlexTable
  238.  
  239. //----------------------------------------------------------------------------------------
  240. CStandardFlexTable::~CStandardFlexTable()
  241. //----------------------------------------------------------------------------------------
  242. {
  243.     delete mClickTimer;
  244. } // CStandardFlexTable::~CStandardFlexTable
  245.  
  246. //----------------------------------------------------------------------------------------
  247. void CStandardFlexTable::FinishCreateSelf()
  248. //----------------------------------------------------------------------------------------
  249. {
  250.     RGBColor ignore;
  251.     UGraphicGizmos::CalcWindowTingeColors(GetMacPort(), mDropColor, ignore);
  252.  
  253.     LView *super = GetSuperView();        
  254.     Assert_(super != NULL);
  255.     mTableHeader = (LTableViewHeader*)dynamic_cast<LTableViewHeader*>(super->FindPaneByID(mTableHeaderPaneID));
  256.     Assert_(mTableHeader);
  257.     // This class assumes the header is fully created first (Oh, yukky-poo).
  258.     Boolean HEADER_MUST_BE_IN_RESOURCE_BEFORE_TABLE = mTableHeader->GetTableView() != NULL;
  259.     Assert_(HEADER_MUST_BE_IN_RESOURCE_BEFORE_TABLE);
  260.     
  261.     mTableHeader->AddListener(&mTableHeaderListener);    
  262.     SetUpTableHelpers();
  263.     
  264.     // Add columns to match the header
  265.     
  266.     SynchronizeColumnsWithHeader();
  267.  
  268.     SetTextTraits(mTextTraitsID);
  269.  
  270.     SetRefreshAllWhenResized(true);
  271.     
  272.     // If an editor for in-place editing exists, find it. Don't worry if one can't be found.
  273.     mNameEditor = dynamic_cast<CInlineEditField*>(FindPaneByID(paneID_InlineEdit));
  274.     if ( mNameEditor ) {
  275.         mNameEditor->AddListener ( &mInlineListener );
  276.         mNameEditor->SetGrowableBorder(true);
  277.     }
  278.     
  279. } // CStandardFlexTable::FinishCreateSelf
  280.  
  281. //----------------------------------------------------------------------------------------
  282. void CStandardFlexTable::SetTextTraits(ResIDT inmTextTraitsID)
  283. //----------------------------------------------------------------------------------------
  284. {
  285.     // load the text traits and pre-measure them
  286.     StTextState    textState;
  287.     
  288.     SInt16        rowHeight;
  289.     
  290.     mTextTraitsID = inmTextTraitsID;
  291.     UTextTraits::SetPortTextTraits(mTextTraitsID);
  292.     ::GetFontInfo(&mTextFontInfo);
  293.     
  294.     rowHeight = mTextFontInfo.ascent + mTextFontInfo.descent + 1;
  295.     if (rowHeight < kMinRowHeight) { 
  296.         rowHeight = kMinRowHeight;
  297.     }
  298.         
  299.     mTableGeometry->SetRowHeight(rowHeight, 0, 0);
  300. }
  301.  
  302. //----------------------------------------------------------------------------------------
  303. void CStandardFlexTable::ScrollRowIntoFrame(TableIndexT inRow)
  304. //----------------------------------------------------------------------------------------
  305. {
  306.     // This avoids excessive erasure done in the base class.
  307.     if (inRow == LArray::index_Bad)
  308.         return;
  309.     STableCell cellToShow(inRow, 1);
  310.     SPoint32 startLocation;
  311.     GetImageLocation(startLocation);
  312.     ScrollCellIntoFrame(cellToShow);
  313.     SPoint32 endLocation;
  314.     GetImageLocation(endLocation);
  315.     if (startLocation.h != endLocation.h || startLocation.v != endLocation.v)
  316.     {
  317.         // 97/08/06.  jrm: not entirely sure about whether this will give us
  318.         //                a redraw bug, but ScrollCellIntoFrame seems to end up
  319.         //                calling UpdatePort().  So I've added DontRefresh() here,
  320.         //                and it seems to cut down on flashing.
  321.         DontRefresh();
  322.     }
  323. } // CStandardFlexTable::ScrollRowIntoFrame
  324.  
  325. //----------------------------------------------------------------------------------------
  326. void CStandardFlexTable::SelectRow(TableIndexT inRow)
  327. //----------------------------------------------------------------------------------------
  328. {
  329.     STableCell cellToSelect(inRow, 1);
  330.     if (GetSelectedRowCount() == 0)
  331.         ScrollRowIntoFrame(inRow);
  332.     SelectCell(cellToSelect);
  333. } // CStandardFlexTable::SelectRow
  334.  
  335. //----------------------------------------------------------------------------------------
  336. void CStandardFlexTable::ScrollSelectionIntoFrame()
  337. //----------------------------------------------------------------------------------------
  338. {
  339.     if (mTableSelector)
  340.     {
  341.         STableCell cellToSelect = mTableSelector->GetFirstSelectedCell();
  342.         if ( cellToSelect.row != LArray::index_Bad )
  343.             ScrollRowIntoFrame(cellToSelect.row);
  344.         else
  345.             ScrollRowIntoFrame(1);                // if no selection, scroll to topw
  346.     }
  347. } // CStandardFlexTable::SelectRow
  348.  
  349. //----------------------------------------------------------------------------------------
  350. void CStandardFlexTable::SynchronizeColumnsWithHeader()
  351. //----------------------------------------------------------------------------------------
  352. {
  353.     Assert_(mTableHeader);
  354.     Int32 deltaColumns = (Int32) mTableHeader->CountVisibleColumns() - (Int32) mCols;
  355.     if ( deltaColumns != 0 ) {
  356.         if ( deltaColumns > 0 ) {
  357.             InsertCols(deltaColumns, mCols, NULL, 0, false);
  358.         } else {
  359.             RemoveCols(-deltaColumns, mCols + deltaColumns + 1, false);
  360.         }
  361.     }
  362. } // CStandardFlexTable::SynchronizeColumnsFromHeader
  363.  
  364. //----------------------------------------------------------------------------------------
  365. void CStandardFlexTable::AddAttachmentFirst(LAttachment* inAttachment, Boolean inOwnsAttachment)
  366. // nb: AddAttachment adds at end.  But this can be called if you want the new attachment
  367. // to be executed before the existing ones.
  368. //----------------------------------------------------------------------------------------
  369. {
  370.     LAttachment* beforeItem = nil;
  371.     if (mAttachments)
  372.         mAttachments->FetchItemAt(LArray::index_First, beforeItem);
  373.     LCommander::AddAttachment(inAttachment, beforeItem, inOwnsAttachment);
  374. }
  375.  
  376. //----------------------------------------------------------------------------------------
  377. void CStandardFlexTable::SetUpTableHelpers()
  378. //----------------------------------------------------------------------------------------
  379. {
  380.     SetTableGeometry( new LFlexTableGeometry(this, mTableHeader) );
  381.     SetTableSelector( new LTableRowSelector(this));
  382.                     // NOTE: There is a dependency between LTableRowSelector and CThreadView
  383.                     // because CThreadView::DoSelectThread() needs to select several rows at once.
  384.  
  385.     // standard keyboard navigation.
  386.     AddAttachment( new CTableKeyAttachment(this) );
  387.     // We don't need table storage. It's safe to have a null one.
  388.     // It is NOT safe, to call SetTableStorage(NULL), as its 
  389.     // implementation in LTableView does not check against NULL.    
  390.     //    SetTableStorage(NULL);
  391.     Assert_(mTableStorage == NULL);
  392. } // CStandardFlexTable::SetUpTableHelpers
  393.  
  394. //----------------------------------------------------------------------------------------
  395. void CStandardFlexTable::RefreshCellRange(
  396.     const STableCell    &inTopLeft,
  397.     const STableCell    &inBotRight)
  398. // Fix a bug in LTableView.  If neither cell intersects the frame, there are two
  399. // cases: union of two cells intersects in empty rect or entire rect.  PP always
  400. // assumed the latter.
  401. //----------------------------------------------------------------------------------------
  402. {
  403.     // I added this test to prevent updating the entire frame in the case
  404.     // of an empty intersection (a very common case!).
  405.     // Calculate the convex hull of the two cells
  406.     Int32    cell1Left, cell1Top, dummy1, dummy2;
  407.     mTableGeometry->GetImageCellBounds(inTopLeft, cell1Left, cell1Top,
  408.                             dummy1, dummy2);
  409.     Int32    cell2Right, cell2Bottom;
  410.     mTableGeometry->GetImageCellBounds(inBotRight, dummy1, dummy2,
  411.                             cell2Right, cell2Bottom);
  412.     // Does this big rect intersect the frame?  If not, nothing to do.
  413.     if (!ImageRectIntersectsFrame(cell1Left, cell1Top, cell2Right, cell2Bottom))
  414.         return;
  415.     LTableView::RefreshCellRange(inTopLeft, inBotRight);
  416. } // CStandardFlexTable::RefreshCellRange
  417.  
  418. //----------------------------------------------------------------------------------------
  419. void CStandardFlexTable::DrawSelf()
  420. //----------------------------------------------------------------------------------------
  421. {
  422.     StTextState    textState;
  423.             
  424.     UTextTraits::SetPortTextTraits(mTextTraitsID);
  425.     ApplyForeAndBackColors();
  426.     LTableView::DrawSelf();
  427.     ExecuteAttachments(CTargetFramer::msg_DrawSelfDone, this);
  428. } // CStandardFlexTable::DrawSelf
  429.  
  430. //----------------------------------------------------------------------------------------
  431. TableIndexT CStandardFlexTable::CountExtraRowsControlledByCell(const STableCell& /*inCell*/) const
  432. //----------------------------------------------------------------------------------------
  433. {
  434.     return 0;
  435. } // CStandardFlexTable::CountExtraRowsControlledByCell
  436.  
  437. //----------------------------------------------------------------------------------------
  438. void CStandardFlexTable::ApplyTextStyle(TableIndexT /* inrow */) const
  439. //----------------------------------------------------------------------------------------
  440. {
  441. } // CStandardFlexTable::ApplyTextStyle
  442.  
  443. //----------------------------------------------------------------------------------------
  444. void CStandardFlexTable::GetMainRowText(
  445.     TableIndexT            /*inRow*/,
  446.     char*                outText,
  447.     UInt16                inMaxBufferLength) const
  448. // Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
  449. // Return result indicates if any of the text is visible in the cell
  450. //----------------------------------------------------------------------------------------
  451. {    
  452.     if (outText && inMaxBufferLength > 0)
  453.         *outText = '\0';
  454. } // CStandardFlexTable::GetMainRowText
  455.  
  456. //----------------------------------------------------------------------------------------
  457. Boolean CStandardFlexTable::GetHiliteText(
  458.     TableIndexT            inRow,
  459.     char*                outText,
  460.     UInt16                inMaxTextLength,
  461.     Rect*                ioRect) const
  462. // Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
  463. // Caller must initialize the rectangle
  464. // Return result indicates if any of the text is visible in the cell
  465. //----------------------------------------------------------------------------------------
  466. {    
  467.     GetMainRowText(inRow, outText, inMaxTextLength);
  468.     if (!*outText)
  469.         return false;
  470.     if (!ioRect)
  471.         return true;
  472.     ioRect->left += kDistanceFromIconToText;
  473.     ioRect->top += 2;
  474.     ioRect->bottom -= 2;
  475.     StColorState penState;
  476.     UTextTraits::SetPortTextTraits(mTextTraitsID);
  477.     ApplyTextStyle(inRow);
  478.     ioRect->right = ioRect->left + ::TextWidth(outText, 0, ::strlen(outText));
  479.     Rect cellRect;
  480.     GetLocalCellRect(STableCell(inRow, GetHiliteColumn()), cellRect);
  481.     if (ioRect->left < cellRect.right)
  482.     {
  483.         if (ioRect->right > cellRect.right)
  484.             ioRect->right = cellRect.right;
  485.     }
  486.     else
  487.         return false;
  488.     return true;
  489. } // CStandardFlexTable::GetHiliteText
  490.  
  491. //----------------------------------------------------------------------------------------
  492. Boolean CStandardFlexTable::GetHiliteTextRect(
  493.     const TableIndexT    inRow,
  494.     Rect&                outRect) const
  495. //----------------------------------------------------------------------------------------
  496. {
  497.     // Default is to return a rect enclosing the entire row.  Derived classes may wish
  498.     // to use GetHiliteText instead.
  499.     STableCell cell(inRow, 1);
  500.     if (!GetLocalCellRect(cell, outRect))
  501.         return false;
  502.     cell.col = mCols;
  503.     Rect cellRect;
  504.     if (!GetLocalCellRect(cell, cellRect))
  505.         return false;
  506.     outRect.right = cellRect.right;
  507.     return true;
  508. } // CStandardFlexTable::GetHiliteTextRect
  509.  
  510. //----------------------------------------------------------------------------------------
  511. Boolean CStandardFlexTable::GetIconRect(
  512.     const STableCell&    inCell,
  513.     const Rect&            inLocalRect,
  514.     Rect&                outRect) const
  515. //----------------------------------------------------------------------------------------
  516. {
  517.     GetDropFlagRect(inLocalRect, outRect);
  518.     outRect.left = outRect.right + kIconMargin
  519.                     + kIndentPerLevel * GetNestedLevel(inCell.row);
  520.     outRect.right = outRect.left + kIconWidth;
  521.     return true;
  522. } // CStandardFlexTable::GetIconRect
  523.  
  524. //----------------------------------------------------------------------------------------
  525. void CStandardFlexTable::HiliteRow(
  526.     TableIndexT    inRow,
  527.     Boolean                /*inHilite*/)
  528. //----------------------------------------------------------------------------------------
  529. {
  530.     if (mHiliteDisabled)
  531.         return;
  532.     StRegion hiliteRgn;
  533.     if (FocusExposed() && GetRowHiliteRgn(inRow, hiliteRgn))
  534.     {
  535.         StColorPenState saveColorPen;   // Preserve color & pen state
  536.         TableIndexT col = GetHiliteColumn();
  537.         if (col)
  538.         {
  539.             Rect r;
  540.             STableCell cell(inRow, col);
  541.             GetLocalCellRect(cell, r);
  542.             DrawIcons(cell, r);
  543.         }
  544.         if (!IsActive())
  545.         {
  546.             // Make a frame.  Do it this way so we get the "Invert" color trickery
  547.             // from the toolbox.  The previous code (srcXOR/FrameRgn) did not work
  548.             // well when the table was not the active view.
  549.             StRegion tempRgn;
  550.             ::CopyRgn(hiliteRgn, tempRgn);
  551.             ::InsetRgn(tempRgn, 1, 1);
  552.             ::DiffRgn(hiliteRgn, tempRgn, hiliteRgn);
  553.         }
  554.         ApplyForeAndBackColors();
  555.         DoHiliteRgn(hiliteRgn);        // can be overridden to do something different
  556.     }
  557. } // CStandardFlexTable::HiliteRow
  558.  
  559. //----------------------------------------------------------------------------------------
  560. void CStandardFlexTable :: DoHiliteRgn( RgnHandle inHiliteRgn ) const
  561. // Hilite the given area using the system hilite color. Override this to do 
  562. // something different (like not use the hilite color).
  563. //----------------------------------------------------------------------------------------
  564. {
  565.     UDrawingUtils::SetHiliteModeOn(); // Don't use hilite color, except for inline editing.
  566.     ::InvertRgn(inHiliteRgn);
  567. } // CStandardFlexTable :: DoHiliteRgn
  568.  
  569. //----------------------------------------------------------------------------------------
  570. void CStandardFlexTable :: DoHiliteRect( const Rect & inHiliteRect ) const
  571. // Hilite the given area using the system hilite color. Override this to do 
  572. // something different (like not use the hilite color).
  573. //----------------------------------------------------------------------------------------
  574. {
  575.     UDrawingUtils::SetHiliteModeOn(); // Don't use hilite color, except for inline editing.
  576.     ::InvertRect(&inHiliteRect);
  577. } // CStandardFlexTable :: DoHiliteRect
  578.  
  579. //----------------------------------------------------------------------------------------
  580. void CStandardFlexTable::Click(SMouseDownEvent &inMouseDown)
  581. //    Click the table. Special processing for drag-n-drop stuff.
  582. //----------------------------------------------------------------------------------------
  583. {
  584.     LPane *clickedPane = FindSubPaneHitBy(inMouseDown.wherePort.h,
  585.                                           inMouseDown.wherePort.v);
  586.     if ( clickedPane != nil )
  587.         clickedPane->Click(inMouseDown);
  588.     else
  589.     {
  590.         if ( !inMouseDown.delaySelect ) LCommander::SwitchTarget(this);
  591.         StValueChanger<Boolean> change(inMouseDown.delaySelect, false);
  592.             // For drag-n-drop functionality
  593.         LPane::Click(inMouseDown);    //   will process click on this View
  594.     }
  595. } // CStandardFlexTable::Click
  596.  
  597. //----------------------------------------------------------------------------------------
  598. Boolean    CStandardFlexTable::ClickDropFlag(
  599.     const STableCell        &inCell,
  600.     const SMouseDownEvent    &inMouseDown)
  601. // Handle drop-flag ("twistee icon") clicks. Return true if handled.
  602. //----------------------------------------------------------------------------------------
  603. {
  604.     Boolean isCellExpanded;
  605.     if (CellHasDropFlag(inCell, isCellExpanded))
  606.     {
  607.         Rect cellRect, flagRect;        
  608.         GetLocalCellRect(inCell, cellRect);
  609.         GetDropFlagRect(cellRect, flagRect);        
  610.         if (::PtInRect(inMouseDown.whereLocal, &flagRect))
  611.         {
  612.             ApplyForeAndBackColors();
  613.             if (FocusDraw() && LDropFlag::TrackClick(flagRect, inMouseDown.whereLocal, isCellExpanded))
  614.                 SetCellExpansion(inCell, !isCellExpanded);
  615.             return true;
  616.         }
  617.     }    
  618.     return false;
  619. }
  620.  
  621. //----------------------------------------------------------------------------------------
  622. Boolean    CStandardFlexTable::ClickSelect(
  623.     const STableCell        &inCell,
  624.     const SMouseDownEvent    &inMouseDown)
  625. //----------------------------------------------------------------------------------------
  626. {
  627.     Rect cellRect;
  628.     GetLocalCellRect(inCell, cellRect);
  629.     
  630.     FocusDraw();    //to be on the safe side
  631.     
  632.     // compute the text and icon rectangles and if the click is in them
  633.     Rect textRect, iconRect;
  634.     GetHiliteTextRect ( inCell.row, textRect );
  635.     GetIconRect ( inCell, cellRect, iconRect );
  636.     bool clickIsInIcon = ::PtInRect(inMouseDown.whereLocal, &iconRect);
  637.     bool clickIsInTitle = ::PtInRect(inMouseDown.whereLocal, &textRect);
  638.     
  639.     // If the click is outside of the icon/title, or if it is in the title (and in-place editing is on),
  640.     // then temporarily turn off the fancy double-click stuff since we don't want it to happen.
  641.     bool savedFancyDoubleClick = mFancyDoubleClick;
  642.     if ( !clickIsInIcon && !clickIsInTitle )
  643.         mFancyDoubleClick = false;                // click was outside icon&title
  644.     if ( clickIsInTitle && mNameEditor )
  645.         mFancyDoubleClick = false;                // click was in title and we're doing inplace editing            
  646.  
  647.     // Fancy double-click behavior.  There are two reasons for this new feature of 5.0:
  648.     //        Ñ    Double-clicking a thread in a message thread view (for example) should
  649.     //        not select the new item, because then the selected message would load in
  650.     //        the message pane of the thread window as well as in the newly-opened message
  651.     //        window.  The user would prefer to maintain the message displayed in the current
  652.     //        window, and open the double-clicked, different message in the separate window.
  653.     //        Ñ    Selecting a new row can be expensive, because it causes the message/folder
  654.     //        to load.  This loading usually unwanted after a double-click.
  655.     if (mFancyDoubleClick
  656.         && !CellIsSelected(inCell)
  657.         && (inMouseDown.macEvent.modifiers & shiftKey) == 0
  658.         && (inMouseDown.macEvent.modifiers & cmdKey) == 0
  659.         )
  660.     {
  661.         if (GetClickCount() == 1)
  662.         {
  663.             // Single-click. First cancel any previous double-click in progress.
  664.             if (mClickTimer)
  665.                 mClickTimer->NoteSingleClick();
  666.             // Set a timer.  If the timer times out, it will set the hilite
  667.             // back and call ClickSelect again with fancy double-click set to false.
  668.             mClickTimer = new CClickTimer(this, inCell, inMouseDown);
  669.         }
  670.         else
  671.         {
  672.             // Double-click.  Get rid of the timer.
  673.             if (mClickTimer)
  674.                 mClickTimer->NoteDoubleClick();
  675.             OpenRow(inCell.row);
  676.         }
  677.         return false;
  678.     }
  679.     
  680.     // 0, 1, >=2 rows selected are the values that change command-enabling configurations.
  681.     int selectedRowCountForCommandStatus = GetSelectedRowCount();
  682.     if (selectedRowCountForCommandStatus > 2)
  683.         selectedRowCountForCommandStatus = 2;
  684.     Boolean result = false;
  685.     
  686.     //
  687.     // There are two cases here: user clicks in the hilite column and they don't click in the
  688.     // hilite column. If they do, only select if the click is in the icon or the icon text. Any
  689.     // other click w/in that column unselects or start a marquee selection. If they click outside
  690.     // the hilite column, process as normal.
  691.     //
  692.     if ( (inCell.col != GetHiliteColumn()) || clickIsInIcon || clickIsInTitle ) {
  693.         if (LTableView::ClickSelect(inCell, inMouseDown))
  694.         {
  695.             // Handle it ourselves if the popup attachment doesn't want it.
  696.             CContextMenuAttachment::SExecuteParams params;
  697.             params.inMouseDown = &inMouseDown;
  698.             if (ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms))
  699.             {
  700.                 // drag ? - don't become target
  701.                 if (CellInitiatesDrag(inCell) && ::WaitMouseMoved(inMouseDown.macEvent.where))
  702.                     DragSelection(inCell, inMouseDown);
  703.                 else
  704.                 {
  705.                     // become target
  706.                     if (!IsTarget())
  707.                         SwitchTarget(this);
  708.  
  709.                     // check if the click is in the title of the icon. If so, and an editor is
  710.                     // present and we're not doing a shift-selection extension and it's not a 
  711.                     // double click then invoke the inline editor. Otherwise process the click normally.
  712.                     // (whew!)
  713.                     //
  714.                     // The reason for not inline editing on a double-click is that it allows the 
  715.                     // user to double-click anywhere on the item to open it, like in the Finder. Doing
  716.                     // a double-click to edit an item doesn't make sense and the user would get
  717.                     // confused. ÑÑÑDouble click check doesn't work right now.....will fix later....ÑÑÑ
  718.                     if ( clickIsInTitle && mNameEditor && !(inMouseDown.macEvent.modifiers & shiftKey)
  719.                             && GetClickCount() != 2 )
  720.                         DoInlineEditing ( inCell, textRect );
  721.                     else {                
  722.                         // open selection if we've got the right click count
  723.                         if (GetClickCount() == mClickCountToOpen)
  724.                             OpenSelection();
  725.                     }
  726.                 }
  727.                 result = true;
  728.             }        
  729.         }
  730.     } // if click in icon or text
  731.     else {
  732.         // since the click is not in the icon and not in the text, the selection should be
  733.         // cleared before we try to show any context menus. This is possibly faster than
  734.         // calling UnselectAllCells() because it only iterates over the selection, not the whole table.
  735.         static const Rect empty = { 0, 0, 0, 0 };
  736.         UnselectCellsNotInSelectionOutline(empty);
  737.         
  738.         CContextMenuAttachment::SExecuteParams params;
  739.         params.inMouseDown = &inMouseDown;
  740.         if (ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms))
  741.             HandleSelectionTracking(inMouseDown);
  742.     }
  743.     
  744. cmd_status:
  745.     mFancyDoubleClick = savedFancyDoubleClick;
  746.     int newSelectedRowCountForCommandStatus = GetSelectedRowCount();
  747.     if (newSelectedRowCountForCommandStatus > 2)
  748.         newSelectedRowCountForCommandStatus = 2;
  749.     if (selectedRowCountForCommandStatus != newSelectedRowCountForCommandStatus)
  750.         SetUpdateCommandStatus(true);
  751.     return result;
  752. } // CStandardFlexTable::ClickSelect
  753.  
  754. //----------------------------------------------------------------------------------------
  755. void CStandardFlexTable::ClickSelf(const SMouseDownEvent &inMouseDown)
  756. // Overridden so that certain cells (e.g. toggled icons) don't select on click and to
  757. // handle marquee selection.
  758. //----------------------------------------------------------------------------------------
  759. {
  760.     STableCell    hitCell;
  761.     SPoint32    imagePt;
  762.     
  763.     LocalToImagePoint(inMouseDown.whereLocal, imagePt);
  764.     if (GetCellHitBy(imagePt, hitCell))
  765.     {
  766.         // Handle drop-flag clicks first
  767.         if (ClickDropFlag(hitCell, inMouseDown))
  768.             return;
  769.  
  770.         // See if the cell selects. If so go ahead and process it (could be a drag or selection 
  771.         // outline). If the cell does not select, the cell may still really want the click (like
  772.         // the "marked read message" column in mail/news. If it does, give it the click, otherwise
  773.         // start doing a marquee selection.
  774.         if ( CellSelects(hitCell) ) {
  775.             if ( ClickSelect(hitCell, inMouseDown) )
  776.                 ClickCell(hitCell, inMouseDown);
  777.         }
  778.         else {
  779.             if ( CellWantsClick(hitCell) )
  780.                 ClickCell(hitCell, inMouseDown);
  781.             else
  782.                 HandleSelectionTracking(inMouseDown);
  783.         }
  784.     }
  785.     else {
  786.         // since the click is not in any cell, the selection should be cleared before we try to 
  787.         // show the context menus. This is possibly faster than  calling UnselectAllCells() because 
  788.         // it only iterates over the selection, not the whole table.
  789.         static const Rect empty = { 0, 0, 0, 0 };
  790.         UnselectCellsNotInSelectionOutline(empty);
  791.  
  792.         CContextMenuAttachment::SExecuteParams params;
  793.         params.inMouseDown = &inMouseDown;
  794.         if ( ExecuteAttachments(CContextMenuAttachment::msg_ContextMenu, (void*)¶ms) )
  795.             HandleSelectionTracking(inMouseDown);
  796.     }
  797.             
  798. } // CStandardFlexTable::ClickSelf
  799.  
  800.  
  801. //
  802. // DoInlineEditing
  803. //
  804. // Perform the necessary setup for inline editing of a row's main text and then show the edit field.
  805. // This version of the routine is public and does not require any external knowledge of table internals
  806. // besides the cell that we should edit. Most of the work is done by calling the routine below
  807. //
  808. void
  809. CStandardFlexTable::DoInlineEditing ( const STableCell &inCell )
  810. {
  811.     Rect textRect;
  812.     GetHiliteTextRect ( inCell.row, textRect );
  813.     DoInlineEditing ( inCell, textRect );
  814.     
  815. } // DoInlineEditing
  816.  
  817.  
  818. //
  819. // DoInlineEditing
  820. //
  821. // Perform the necessary setup for inline editing of a row's main text and then show the edit field
  822. //
  823. void
  824. CStandardFlexTable::DoInlineEditing ( const STableCell &inCell, Rect & inTextRect )
  825. {
  826.     mRowBeingEdited = inCell.row;
  827.     if ( !CanDoInlineEditing() ) {                // bail if inline editing is temporarily turned off
  828.         mRowBeingEdited = LArray::index_Bad;
  829.         return;
  830.     }
  831.     
  832. #if 0
  833.     // erase the text rectangle so that when the text field shrinks, you won't see the old name
  834.     // behind it. This is kinda skanky, but we need to make sure we draw the background color
  835.     // correctly.
  836.     //ÑÑÑThis doesn't work.
  837.     RGBColor backColor; 
  838.     ::GetCPixel ( inTextRect.right - 1, inTextRect.top - 1, &backColor );
  839.     ::RGBBackColor(&backColor);
  840.     ::EraseRect ( &inTextRect );
  841. #endif
  842.  
  843.     SPoint32 imagePoint;
  844.     LocalToImagePoint(topLeft(inTextRect), imagePoint);
  845.     imagePoint.h -= 1; imagePoint.v -= 1;
  846.     
  847.     LCommander::SwitchTarget(mNameEditor);
  848.     
  849.     // Don't broadcast that the selection has changed until we update the edit field
  850.     SetNotifyOnSelectionChange(false);
  851.     SelectCell(STableCell(mRowBeingEdited, GetHiliteColumn()));
  852.     SetNotifyOnSelectionChange(true);
  853.     
  854.     char nameString[256];
  855.     GetHiliteText(mRowBeingEdited, nameString, 255, &inTextRect);
  856.     
  857.     mNameEditor->UpdateEdit(LStr255(nameString), &imagePoint, nil);
  858.     
  859.     SelectionChanged();
  860.     
  861. } // DoInlineEditing
  862.  
  863.  
  864. //----------------------------------------------------------------------------------------
  865. Boolean CStandardFlexTable::CellSelects(const STableCell& /*inCell*/) const
  866. // Determines if a cell is allowed to select the row.
  867. //----------------------------------------------------------------------------------------
  868. {
  869.     return true;
  870. } // CStandardFlexTable::CellSelects
  871.  
  872.  
  873. //----------------------------------------------------------------------------------------
  874. void CStandardFlexTable::HandleSelectionTracking ( const SMouseDownEvent & inMouseDown )
  875. // Set up everything to start tracking a selection marquee
  876. //----------------------------------------------------------------------------------------
  877. {
  878.     // bail if the table specifically doesn't want the tracking.
  879.     if ( !TableDesiresSelectionTracking() )
  880.         return;
  881.         
  882.     // Click is outside of any Cell. Unselect everything if the shift key is up.
  883.     if ( !(inMouseDown.macEvent.modifiers & shiftKey) )
  884.         UnselectAllCells();
  885.     
  886.     // track the mouse for doing drag selection
  887.     FocusDraw();
  888.     TrackSelection( inMouseDown );
  889.     
  890. } // HandleSelectionTracking
  891.  
  892. //----------------------------------------------------------------------------------------
  893. void CStandardFlexTable::TrackSelection( const SMouseDownEvent & inMouseDown )
  894. // Do the tracking of when the mouse is down and the user wants to do a multiple selection
  895. // by drawing a selection marquee.
  896. //----------------------------------------------------------------------------------------
  897. {
  898.     Point originalLoc = inMouseDown.whereLocal;
  899.  
  900.     Rect selectionRect = { 0, 0, 0, 0 }, iconHotSpotRect = { 0, 0, 0, 0 };
  901.     Rect cellHotSpotRect = { 0, 0, 0, 0 };
  902.  
  903.     Point lastMousePoint = { 0, 0 }, curMousePoint = { 0, 0 }, curOrigin = { 0, 0 };
  904.  
  905.     UInt32    currentRow = 0L, currentCol = 0L, maxRows = 0L, maxColumns = 0L;
  906.     
  907.     STableCell    topLeftCell( 0, 0 );
  908.     STableCell    botRightCell( 0, 0 );
  909.     STableCell    currentCell( 0, 0 );
  910.     
  911.     StColorPenState oldPenState;
  912.     
  913.     // mTableView->GetTableSize( maxRows, maxColumns );
  914.                                                                 
  915.     SetNotifyOnSelectionChange(false);
  916.     while ( ::StillDown() ) {        
  917.  
  918.         ::GetMouse( &curMousePoint );
  919.         
  920.         if ( (curMousePoint.v != lastMousePoint.v) || (curMousePoint.h != lastMousePoint.h) ) {
  921.  
  922.             currentCell.row = 0;
  923.             currentCell.col = 0;
  924.             
  925.             ::PenPat( &qd.gray );
  926.             ::PenMode( patXor );
  927.             ::PenSize( 2, 2 );
  928.  
  929.             if ( !::EmptyRect(&selectionRect) )
  930.                 ::FrameRect( &selectionRect );
  931.             
  932.             lastMousePoint = curMousePoint;
  933.             
  934.             ::Pt2Rect( curMousePoint, originalLoc, &selectionRect );    
  935.  
  936.             // Don't let the selection rect be empty. Trust me.
  937.             if ( (selectionRect.right - selectionRect.left) == 0 )
  938.                 ::InsetRect( &selectionRect, -1, 0 );
  939.             if ( (selectionRect.bottom - selectionRect.top) == 0 )
  940.                 ::InsetRect( &selectionRect, 0, -1 );
  941.             
  942.             //
  943.             // Restore the text traits, scroll the table if needed, unselect all the cells 
  944.             // that shouldn't be selected, select the ones that should be selected, 
  945.             // reset the text traits and *then* finally draw the gray rect.
  946.             //
  947.             
  948.             oldPenState.Restore();
  949.  
  950.             if ( AutoScrollImage(curMousePoint) ) 
  951.                 FocusDraw();
  952.         
  953.             UnselectCellsNotInSelectionOutline(selectionRect);
  954.                         
  955.             // find what cells are in w/in the rectangle and select them if the rectangle
  956.             // encloses/touches the cell hot spot (usually the icon or icon text).
  957.             FetchIntersectingCells( selectionRect, topLeftCell, botRightCell );                                
  958.             for ( currentRow = topLeftCell.row; currentRow <= botRightCell.row; currentRow++ ) {
  959.                 for ( currentCol = topLeftCell.col; currentCol <= botRightCell.col; currentCol++ ) {
  960.                     
  961.                     currentCell.row = currentRow;
  962.                     currentCell.col = currentCol;
  963.                     
  964.                     if ( !CellIsSelected(currentCell) && HitCellHotSpot(currentCell, selectionRect) ) {
  965.                         SelectCell( currentCell );
  966.                     }
  967.                     
  968.                 } // for each column
  969.             } // for each row
  970.             
  971.             oldPenState.Save();
  972.  
  973.             ::PenPat( &qd.gray );
  974.             ::PenMode( patXor );
  975.             ::PenSize( 2, 2 );
  976.             ::FrameRect( &selectionRect );
  977.  
  978.         } // if mouse has moved
  979.         
  980.     } // while mouse still down
  981.     
  982.     SetNotifyOnSelectionChange(true);
  983.     SelectionChanged();
  984.  
  985.     if ( !::EmptyRect(&selectionRect) )
  986.         ::FrameRect( &selectionRect );
  987.         
  988. } // TrackSelection
  989.  
  990. //----------------------------------------------------------------------------------------
  991. void CStandardFlexTable::UnselectCellsNotInSelectionOutline( const Rect & inSelectionRect )
  992. // Iterate over existing selection looking for things that are no longer w/in
  993. // the rectangle and unselect them. 
  994. //
  995. // IMPORTANT: This routine assumes a selection model that selects the entire row.
  996. // If another selection model is used (such as one that only selects individual
  997. // cells), this needs to be rewritten.
  998. //----------------------------------------------------------------------------------------
  999. {
  1000.     TableIndexT currentRow = LArray::index_Bad;
  1001.     while ( GetNextSelectedRow(currentRow) ) {
  1002.  
  1003.         STableCell currentCell;
  1004.         currentCell.row = currentRow;
  1005.         currentCell.col = GetHiliteColumn();
  1006.         if ( !HitCellHotSpot( currentCell, inSelectionRect) ) {
  1007.             UnselectCell( currentCell );
  1008.             currentRow = currentCell.row;
  1009.         }
  1010.         
  1011.     }
  1012.  
  1013. } // UnselectCellsNotInSelectionOutline
  1014.  
  1015.  
  1016. //----------------------------------------------------------------------------------------
  1017. Boolean CStandardFlexTable::HitCellHotSpot( const STableCell &inCell, const Rect &inTotalSelectionRect )
  1018. //----------------------------------------------------------------------------------------
  1019. {
  1020.     Boolean result = false;
  1021.     TableIndexT hiliteCol = GetHiliteColumn();
  1022.     
  1023.     FocusDraw();    //to be on the safe side
  1024.     
  1025.     if ( hiliteCol ) {
  1026.         if ( hiliteCol == inCell.col ) {
  1027.             Rect cellRect;
  1028.             GetLocalCellRect(inCell, cellRect);
  1029.             
  1030.             Rect textRect, iconRect;
  1031.             GetHiliteTextRect ( inCell.row, textRect );
  1032.             GetIconRect ( inCell, cellRect, iconRect );
  1033.              if ( ::SectRect(&textRect, &inTotalSelectionRect, &textRect) ||
  1034.                      ::SectRect(&iconRect, &inTotalSelectionRect, &iconRect) )
  1035.                 result = true;
  1036.         }
  1037.     }
  1038.     else {
  1039.         Rect    cellRect    = { 0, 0, 0, 0 };            
  1040.         GetLocalCellRect( inCell, cellRect );                    
  1041.         result = ::SectRect( &cellRect, &inTotalSelectionRect, &cellRect );        
  1042.     }
  1043.     
  1044.     return result;
  1045.  
  1046. } // HitCellHotSpot
  1047.  
  1048.  
  1049. //----------------------------------------------------------------------------------------
  1050. void CStandardFlexTable::BeTarget() 
  1051. //----------------------------------------------------------------------------------------
  1052. {
  1053.     LCommander::BeTarget();
  1054.     Activate();
  1055.     ExecuteAttachments(CTargetFramer::msg_BecomingTarget, this);
  1056. } // CStandardFlexTable::BeTarget
  1057.  
  1058. //----------------------------------------------------------------------------------------
  1059. void CStandardFlexTable::DontBeTarget() 
  1060. //----------------------------------------------------------------------------------------
  1061. {
  1062.     if (mClickTimer)
  1063.         mClickTimer->NoteSingleClick();
  1064.     ExecuteAttachments(CTargetFramer::msg_ResigningTarget, this);
  1065.     Deactivate();
  1066.     LCommander::DontBeTarget();
  1067. } // CStandardFlexTable::DontBeTarget
  1068.  
  1069. //----------------------------------------------------------------------------------------
  1070. PaneIDT CStandardFlexTable::GetCellDataType(const STableCell &inCell) const
  1071. //----------------------------------------------------------------------------------------
  1072. {
  1073.     return mTableHeader->GetColumnPaneID(inCell.col);
  1074. } // CStandardFlexTable::GetCellDataType
  1075.  
  1076. //----------------------------------------------------------------------------------------
  1077. Boolean CStandardFlexTable::GetNextSelectedRow(TableIndexT& inOutAfterRow) const
  1078. //----------------------------------------------------------------------------------------
  1079. {
  1080.     TableIndexT    nRows, nCols;
  1081.     GetTableSize(nRows, nCols);
  1082.     STableCell cell(inOutAfterRow, nCols);
  1083.     if (LTableView::GetNextSelectedCell(cell))
  1084.     {
  1085.         inOutAfterRow = cell.row;
  1086.         return true;
  1087.     }
  1088.     return false;
  1089.     
  1090. } // CStandardFlexTable::GetNextSelectedRow
  1091.  
  1092. //----------------------------------------------------------------------------------------
  1093. Boolean CStandardFlexTable::GetLastSelectedRow(TableIndexT& inOutBeforeRow) const
  1094. // Currently a little inefficient...
  1095. //----------------------------------------------------------------------------------------
  1096. {
  1097.     STableCell cell(inOutBeforeRow, 1);
  1098.     while (cell.row-- > LArray::index_Bad)
  1099.     {
  1100.         if (CellIsSelected(cell))
  1101.         {
  1102.             inOutBeforeRow = cell.row;
  1103.             return true;
  1104.         }
  1105.     }
  1106.     return false;
  1107. } // CStandardFlexTable::GetLastSelectedRow
  1108.  
  1109. //----------------------------------------------------------------------------------------
  1110. void CStandardFlexTable::FindCommandStatus(
  1111.     CommandT            inCommand,
  1112.     Boolean                &outEnabled,
  1113.     Boolean                &outUsesMark,
  1114.     Char16                &outMark,
  1115.     Str255                outName)
  1116. //----------------------------------------------------------------------------------------
  1117. {
  1118.     switch (inCommand)
  1119.     {
  1120.         case cmd_GetInfo:
  1121.             outEnabled = GetSelectedRowCount() == 1;
  1122.             break;
  1123.         // These commands require one or more lines to be selected.
  1124.         case cmd_OpenSelection:
  1125.         case cmd_OpenSelectionNewWindow:
  1126.         case cmd_Clear:
  1127.         case cmd_SecurityInfo:
  1128.             outEnabled = GetSelectedRowCount() > 0;
  1129.             break;
  1130.         // Selection not necessary
  1131.         case cmd_SelectAll:
  1132.             outEnabled = true;
  1133.             break;
  1134.         default:
  1135.             LCommander::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName);
  1136.             break;
  1137.     }
  1138. } // CStandardFlexTable::FindCommandStatus
  1139.  
  1140. //----------------------------------------------------------------------------------------
  1141. Boolean CStandardFlexTable::ObeyCommand(
  1142.     CommandT    inCommand,
  1143.     void        *ioParam)
  1144. //----------------------------------------------------------------------------------------
  1145. {
  1146.     Boolean    result = false;
  1147.     switch(inCommand)
  1148.     {
  1149.         case cmd_OpenSelectionNewWindow:
  1150.         case cmd_OpenSelection:
  1151.             OpenSelection();
  1152.             return true;
  1153.         case cmd_GetInfo:
  1154.             GetInfo();
  1155.             return true;
  1156.         case cmd_SelectAll:
  1157.             SelectAllCells();
  1158.             return true;
  1159.         case cmd_Clear:
  1160.             DeleteSelection();
  1161.             return true;
  1162.         default:
  1163.             result = LCommander::ObeyCommand(inCommand, ioParam);
  1164.             break;
  1165.     }
  1166.     return result;
  1167. } // CStandardFlexTable::ObeyCommand
  1168.  
  1169.  
  1170. //----------------------------------------------------------------------------------------
  1171. Boolean CStandardFlexTable::HandleKeyPress(    const EventRecord& inKeyEvent)
  1172. //----------------------------------------------------------------------------------------
  1173. {
  1174.     // We are NOT masking off keyUp events.  So let's beware of them!
  1175.     if (inKeyEvent.what == keyUp)
  1176.         return false;
  1177.     Char16    c = inKeyEvent.message & charCodeMask;
  1178.     switch (c)
  1179.     {
  1180.     case char_Return:
  1181.     case char_Enter:
  1182.         OpenSelection();
  1183.         return true;
  1184.     case char_Backspace:
  1185.     case char_FwdDelete:
  1186.         DeleteSelection();
  1187.         return true;
  1188.     case char_LeftArrow:
  1189.     case char_RightArrow:
  1190.         if (GetSelectedRowCount() == 1)
  1191.         {
  1192.             STableCell cell = mTableSelector->GetFirstSelectedCell();
  1193.             Boolean isCellExpanded;
  1194.             // selected cell will be in column 1, but dropflag may not be.
  1195.             for (; IsValidCell(cell); cell.col++)
  1196.             {
  1197.                 if (CellHasDropFlag(cell, isCellExpanded))
  1198.                 {
  1199.                     SetCellExpansion(cell, c == char_RightArrow);
  1200.                     break;
  1201.                 }
  1202.             }
  1203.         }
  1204.     default:
  1205.         LCommander::HandleKeyPress(inKeyEvent);
  1206.         return true;
  1207.     } // switch
  1208.     return false;
  1209. } // CStandardFlexTable::HandleKeyPress
  1210.  
  1211. //----------------------------------------------------------------------------------------
  1212. void CStandardFlexTable::OpenSelection()
  1213. //----------------------------------------------------------------------------------------
  1214. {
  1215.     TableIndexT selectedRow = 0;
  1216.     while (GetNextSelectedRow(selectedRow) && !CmdPeriod())
  1217.         OpenRow(selectedRow);
  1218. }
  1219.  
  1220. //----------------------------------------------------------------------------------------
  1221. void CStandardFlexTable::GetInfo()
  1222. //----------------------------------------------------------------------------------------
  1223. {
  1224.     // Base class does nothing
  1225. }
  1226.  
  1227. //----------------------------------------------------------------------------------------
  1228. Boolean CStandardFlexTable::RowCanAcceptDrop(
  1229.     DragReference    /*inDragRef*/,
  1230.     TableIndexT        /*inDropRow*/)
  1231. //----------------------------------------------------------------------------------------
  1232. {
  1233.     return false;
  1234. } // CStandardFlexTable::RowCanAcceptDrop
  1235.  
  1236. //----------------------------------------------------------------------------------------
  1237. Boolean CStandardFlexTable::RowCanAcceptDropBetweenAbove(
  1238.     DragReference    /*inDragRef*/,
  1239.     TableIndexT        /*inDropRow*/)
  1240. //----------------------------------------------------------------------------------------
  1241. {
  1242.     return false;
  1243. } // CStandardFlexTable::RowCanAcceptDropBetweenAbove
  1244.  
  1245. //----------------------------------------------------------------------------------------
  1246. TableIndexT CStandardFlexTable::GetHiliteColumn() const
  1247. //----------------------------------------------------------------------------------------
  1248. {
  1249.     return LArray::index_Bad; // default is to hilite the whole row.
  1250. } // CStandardFlexTable::GetHiliteColumn
  1251.  
  1252.  
  1253. //
  1254. // ComputeItemDropAreas
  1255. //
  1256. // When a drag goes over a cell that contains a non-folder, divide the node in half. The top
  1257. // half will represent dropping above the item, the bottom after.
  1258. //
  1259. void
  1260. CStandardFlexTable :: ComputeItemDropAreas ( const Rect & inLocalCellRect, Rect & oTop, 
  1261.                                                 Rect & oBottom )
  1262. {
  1263.     oBottom = oTop = inLocalCellRect;
  1264.     uint16 midPt = (oTop.bottom - oTop.top) / 2;
  1265.     oTop.bottom = oTop.top + midPt;
  1266.     oBottom.top = oTop.bottom + 1;
  1267.     
  1268. } // ComputeItemDropAreas // ComputeItemDropAreas
  1269.  
  1270.  
  1271. //
  1272. // ComputeFolderDropAreas
  1273. //
  1274. // When a drag goes over a cell that contains a folder, divide the cell area into 3 parts. The middle
  1275. // area, which corresponds to a drop on the folder takes up the majority of the cell space with the
  1276. // top and bottom areas (corresponding to drop before and drop after, respectively) taking up the rest
  1277. // at the ends.
  1278. //
  1279. void
  1280. CStandardFlexTable :: ComputeFolderDropAreas ( const Rect & inLocalCellRect, Rect & oTop, 
  1281.                                                 Rect & oBottom )
  1282. {
  1283.     const Uint8 capAreaHeight = 3;
  1284.         
  1285.     oBottom = oTop = inLocalCellRect;
  1286.     oTop.bottom = oTop.top + capAreaHeight;
  1287.     oBottom.top = oBottom.bottom - capAreaHeight;
  1288.     
  1289. } // ComputeFolderDropAreas
  1290.  
  1291.  
  1292. //----------------------------------------------------------------------------------------
  1293. void CStandardFlexTable::InsideDropArea(DragReference inDragRef)
  1294. // (override from LDragAndDrop)
  1295. //----------------------------------------------------------------------------------------
  1296. {
  1297.     Point            mouseLoc;
  1298.     SPoint32        imagePt;
  1299.     Rect            rowBounds;                    // only top/bottom are important
  1300.     
  1301.     FocusDraw();
  1302.     
  1303.     ::GetDragMouse(inDragRef, &mouseLoc, NULL);
  1304.     ::GlobalToLocal(&mouseLoc);
  1305.     // Auto scroll code -- jrm 97/05/03
  1306.     if (AutoScrollImage(mouseLoc))
  1307.     {
  1308.         Rect frame;
  1309.         CalcLocalFrameRect(frame);
  1310.         Int32 pt = ::PinRect(&frame, mouseLoc);
  1311.         mouseLoc = *(Point*)&pt;
  1312.     }
  1313.     LocalToImagePoint(mouseLoc, imagePt);
  1314.     TableIndexT rowMouseIsOver = mTableGeometry->GetRowHitBy(imagePt);
  1315.     GetLocalCellRect ( STableCell(rowMouseIsOver, GetHiliteColumn()), rowBounds );
  1316.  
  1317.     // Find the index of where we want to drop the item and if it is between two rows
  1318.     // of if it is on a row (can only happen if row is a container).
  1319.     bool newIsDropBetweenRows = false;
  1320.     TableIndexT newDropRow = LArray::index_Bad;
  1321.     if ( rowMouseIsOver >= 1 )
  1322.     {
  1323.         // is the current row in the existing table or off the end? If it's off the end, we
  1324.         // know we're dropping between rows.
  1325.         if ( rowMouseIsOver <= mRows ) {
  1326.             // divide row horizontally into several parts (3 if a container, 2 if not) and determine which
  1327.             // part of the row the mouse is over. This will determine if we should interpret this as
  1328.             // a drop before/after or a drop on.
  1329.             if ( RowIsContainer(rowMouseIsOver) ) {
  1330.                 Rect topArea, bottomArea;
  1331.                 ComputeFolderDropAreas ( rowBounds, topArea, bottomArea );
  1332.                 if ( ::PtInRect(mouseLoc, &topArea) ) {
  1333.                     newDropRow = rowMouseIsOver;            // before current row
  1334.                     newIsDropBetweenRows = true;
  1335.                 }
  1336.                 else if ( ::PtInRect(mouseLoc, &bottomArea) ) {
  1337.                     newDropRow = rowMouseIsOver + 1;        // after current row
  1338.                     newIsDropBetweenRows = true;
  1339.                 }
  1340.                 else
  1341.                     newDropRow = rowMouseIsOver;            // hilite folder, don't draw line
  1342.             }
  1343.             else {
  1344.                 Rect topArea, bottomArea;
  1345.                 ComputeItemDropAreas ( rowBounds, topArea, bottomArea );
  1346.                 if ( ::PtInRect(mouseLoc, &topArea) )
  1347.                     newDropRow = rowMouseIsOver;            // before current row
  1348.                 else
  1349.                     newDropRow = rowMouseIsOver + 1;        // after current row
  1350.                 newIsDropBetweenRows = true;                // draw line between rows
  1351.             }
  1352.         }
  1353.         else {
  1354.             newDropRow = mRows + 1;
  1355.             newIsDropBetweenRows = true;
  1356.             newDropRow = rowMouseIsOver;
  1357.         }
  1358.         
  1359.         // we now know where the drop SHOULD go, now check if it CAN go there.
  1360.         newIsDropBetweenRows &= RowCanAcceptDropBetweenAbove(inDragRef, newDropRow);
  1361.         if (!newIsDropBetweenRows && !RowCanAcceptDrop(inDragRef, newDropRow))
  1362.             newDropRow = LArray::index_Bad;
  1363.     }
  1364.     
  1365.     // if things have changed from last time, unhilite the last drop feedback and redraw
  1366.     // the new drop feedback.
  1367.     if (newDropRow != mDropRow || newIsDropBetweenRows != mIsDropBetweenRows)
  1368.     {                
  1369.         TableIndexT oldDropRow = mDropRow;
  1370.         mDropRow = LArray::index_Bad; // so that we'll unhilite
  1371.         HiliteDropRow(oldDropRow, mIsDropBetweenRows);
  1372.         mIsDropBetweenRows = newIsDropBetweenRows;
  1373.         
  1374.         if (newDropRow > LArray::index_Bad)
  1375.         {
  1376.             mDropRow = newDropRow; // so that we'll hilite
  1377.             HiliteDropRow(newDropRow, mIsDropBetweenRows);
  1378.         }
  1379.         else
  1380.             mDropRow = LArray::index_Bad;
  1381.     }
  1382.     
  1383. } // CStandardFlexTable::InsideDropArea
  1384.  
  1385. //----------------------------------------------------------------------------------------
  1386. void CStandardFlexTable::HiliteDropRow(TableIndexT    inRow, Boolean inDrawBarAbove)
  1387. //----------------------------------------------------------------------------------------
  1388. {
  1389.     if (inRow == LArray::index_Bad)
  1390.         return;
  1391.  
  1392.     STableCell cell(inRow, 1);
  1393.     if (mIsInternalDrop && CellIsSelected(cell))
  1394.         return; // Don't show hiliting feedback for a drag when over the selection.
  1395.  
  1396.     TableIndexT dropHiliteColumn = GetHiliteColumn();
  1397.     if ( dropHiliteColumn != LArray::index_Bad )
  1398.         cell.col = dropHiliteColumn;
  1399.     else
  1400.         cell.col = 1;
  1401.     Rect r;        
  1402.     GetLocalCellRect(cell, r);
  1403.     if (inDrawBarAbove || dropHiliteColumn == LArray::index_Bad)
  1404.     {
  1405.         r.left     = 0;
  1406.         r.right    = mFrameSize.width;
  1407.         if (inDrawBarAbove)
  1408.         {
  1409. #if 1
  1410.             // here's how you draw a bar above.
  1411.             r.bottom = r.top + 1;
  1412.             r.top -= 1;
  1413. #else
  1414.             // here's how you underline below.
  1415.             r.top = r.bottom - 1;
  1416.             r.bottom += 1;
  1417. #endif
  1418.         }
  1419.         DoHiliteRect(r);
  1420.     }
  1421.     else
  1422.     {
  1423.         DrawIcons(cell, r);
  1424.         StRegion hiliteRgn;
  1425.         GetRowHiliteRgn(inRow, hiliteRgn);
  1426.         ::InvertRgn(hiliteRgn);
  1427.     }
  1428. } // CStandardFlexTable::HiliteDropRow
  1429.  
  1430. //----------------------------------------------------------------------------------------
  1431. void CStandardFlexTable::DrawCellContents(
  1432.     const STableCell& /*inCell*/,
  1433.     const Rect& /*inLocalRect*/)
  1434. //----------------------------------------------------------------------------------------
  1435. {
  1436. }
  1437.  
  1438. //----------------------------------------------------------------------------------------
  1439. void CStandardFlexTable::DrawCell(
  1440.     const STableCell& inCell,
  1441.     const Rect& inLocalRect)
  1442. //----------------------------------------------------------------------------------------
  1443. {
  1444.     // Compute the update rgn's intersection with the cell
  1445.     // bounds and bail if they're disjoint.
  1446.     RgnHandle updateRgn = GetLocalUpdateRgn();
  1447.     if (!updateRgn)
  1448.         return;
  1449.     Rect updateRect = (**updateRgn).rgnBBox;
  1450.     ::DisposeRgn(updateRgn);
  1451.     
  1452.     Rect intersection;
  1453.     if (!::SectRect(&updateRect, &inLocalRect, &intersection))
  1454.         return;
  1455.         
  1456.     // Save clip, and clip to the cell/updateRgn intersection
  1457.     StClipRgnState savedClip;
  1458.     ::ClipRect(&intersection);
  1459.  
  1460.     StColorState penState;
  1461.     RGBColor white = { 0xFFFF, 0xFFFF, 0xFFFF };
  1462.     ::RGBBackColor(&white);
  1463.     
  1464.     // Inset the rectangle on the right for better separation
  1465.     // (long text looks bad when truncated).
  1466.     Rect localRect = inLocalRect;
  1467.     Rect insetRect = inLocalRect;
  1468.     insetRect.right--;
  1469.     // The rightmost column overlaps the scrollbar.  We must not draw on the overlap.
  1470.     if (inCell.col == mTableHeader->CountVisibleColumns())
  1471.         localRect = insetRect;
  1472.     ::EraseRect(&inLocalRect);
  1473.  
  1474.     ApplyTextStyle(inCell.row);
  1475.     if ( mRowBeingEdited != inCell.row )
  1476.         DrawCellContents(inCell, insetRect);
  1477. } // CStandardFlexTable::DrawCell
  1478.  
  1479. //----------------------------------------------------------------------------------------
  1480. Boolean CStandardFlexTable::GetRowDragRgn(TableIndexT inRow, RgnHandle ioHiliteRgn) const
  1481. // The drag region is the hilite region plus the icon
  1482. //----------------------------------------------------------------------------------------
  1483. {
  1484.     ::SetEmptyRgn(ioHiliteRgn);
  1485.     Rect cellRect;
  1486.     TableIndexT col = GetHiliteColumn();
  1487.     if ( !col )
  1488.         col = 1;
  1489.     STableCell cell(inRow, col);
  1490.     if (!GetLocalCellRect(cell, cellRect))
  1491.         return false;
  1492.     ResIDT iconID = GetIconID(inRow);
  1493.     if (iconID)
  1494.     {
  1495.         GetIconRect(cell, cellRect, cellRect);
  1496.         ::IconIDToRgn(ioHiliteRgn, &cellRect, atNone, iconID);
  1497.     }
  1498.     StRegion cellRgn;
  1499.     GetRowHiliteRgn(inRow, cellRgn);
  1500.     ::UnionRgn(ioHiliteRgn, cellRgn, ioHiliteRgn);
  1501.     return true;
  1502. } // CStandardFlexTable::GetRowHiliteRgn
  1503.  
  1504. //----------------------------------------------------------------------------------------
  1505. Boolean CStandardFlexTable::GetRowHiliteRgn(TableIndexT inRow, RgnHandle ioHiliteRgn) const
  1506. //----------------------------------------------------------------------------------------
  1507. {
  1508.     ::SetEmptyRgn(ioHiliteRgn);
  1509.     Rect cellRect;
  1510.     if (!GetHiliteTextRect(inRow, cellRect))
  1511.         return false;
  1512.     ::RectRgn(ioHiliteRgn, &cellRect);
  1513.     return true;
  1514. } // CStandardFlexTable::GetRowHiliteRgn
  1515.  
  1516. //----------------------------------------------------------------------------------------
  1517. void CStandardFlexTable::GetHiliteRgn(RgnHandle    ioHiliteRgn)
  1518. //----------------------------------------------------------------------------------------
  1519. {
  1520.     ::SetEmptyRgn(ioHiliteRgn);
  1521.     Rect visRect;
  1522.     GetRevealedRect(visRect);            // Check if Table is revealed
  1523.     if (!::EmptyRect(&visRect))
  1524.     {
  1525.         PortToLocalPoint(topLeft(visRect));
  1526.         PortToLocalPoint(botRight(visRect));
  1527.         
  1528.         STableCell topLeftCell, botRightCell;
  1529.         FetchIntersectingCells(visRect, topLeftCell, botRightCell);
  1530.         
  1531.         StRegion cellRgn;
  1532.         STableCell cell(1, GetHiliteColumn());                // Loop thru all cells
  1533.         for (cell.row = topLeftCell.row; cell.row <= botRightCell.row; cell.row++)
  1534.         {
  1535.             if (CellIsSelected(cell))
  1536.             {
  1537.                 if (GetRowHiliteRgn(cell.row, cellRgn))
  1538.                     ::UnionRgn(ioHiliteRgn, cellRgn, ioHiliteRgn);
  1539.             }
  1540.         }
  1541.     }
  1542. } // CStandardFlexTable::GetHiliteRgn
  1543.  
  1544. //----------------------------------------------------------------------------------------
  1545. void CStandardFlexTable::EnterDropArea(
  1546.     DragReference        /*inDragRef*/,
  1547.     Boolean                /*inDragHasLeftSender*/)
  1548. // CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row
  1549. // scenario
  1550. //----------------------------------------------------------------------------------------
  1551. {
  1552. } // CStandardFlexTable::EnterDropArea
  1553.  
  1554. //----------------------------------------------------------------------------------------
  1555. void CStandardFlexTable::LeaveDropArea(DragReference /*inDragRef*/)
  1556. // CStandardFlexTable overrides the LDropArea base method, because it assumes a drop-on-row
  1557. // scenario
  1558. //----------------------------------------------------------------------------------------
  1559. {
  1560.     FocusDraw();
  1561.     if (mDropRow)
  1562.     {
  1563.         TableIndexT oldDropRow = mDropRow;
  1564.         mDropRow = LArray::index_Bad; // so that we'll unhilite
  1565.         HiliteDropRow(oldDropRow, mIsDropBetweenRows);    
  1566.     }    
  1567.     
  1568. //    HiliteSelection( IsActive(), true); // Finder does not do this.
  1569.     mIsDropBetweenRows = false;
  1570.     mIsInternalDrop = false;
  1571. } // CStandardFlexTable::LeaveDropArea
  1572.  
  1573. //----------------------------------------------------------------------------------------
  1574. Boolean CStandardFlexTable::PointInDropArea(Point inGlobalPt)
  1575. // Overridden to return true just above and below the view, to allow autoscroll
  1576. // (override from LDragAndDrop)
  1577. //----------------------------------------------------------------------------------------
  1578. {
  1579. #define kSlopPixels 20
  1580.     if (!IsEnabled())
  1581.         return false;
  1582.     Point portPoint = inGlobalPt;
  1583.     GlobalToPortPoint(portPoint);
  1584.     return ( (portPoint.h >= mFrameLocation.h)  &&
  1585.              (portPoint.h < mFrameLocation.h + mFrameSize.width)  &&
  1586.              (portPoint.v >= mFrameLocation.v - kSlopPixels)  &&
  1587.              (portPoint.v < mFrameLocation.v + mFrameSize.height + kSlopPixels) );
  1588. } // CStandardFlexTable::PointInDropArea
  1589.  
  1590. //----------------------------------------------------------------------------------------
  1591. void CStandardFlexTable::ScrollImageBy(
  1592.     Int32                inLeftDelta,
  1593.     Int32                inTopDelta,
  1594.     Boolean                inRefresh)
  1595. //----------------------------------------------------------------------------------------
  1596. {
  1597.     Boolean mayAutoScrollLeavingTurds = inRefresh && IsTarget(); //&& mDropRow;
  1598.         // Turds will be left if we call scrollbits in ScrollImageBy
  1599.     if (mayAutoScrollLeavingTurds)
  1600.     {
  1601.         // Notify the CTargetFramer (if any) to erase the frame hilite
  1602.         ExecuteAttachments(CTargetFramer::msg_ResigningTarget, this);
  1603.             // invalidates the border, and stops the refresh from drawing a new one
  1604.         // If we are about to autoscroll and we are calling scrollbits, unhilite
  1605.         // the drop row as well
  1606.         TableIndexT dropRow = mDropRow;
  1607.         mDropRow = LArray::index_Bad; // So that we'll unhilite
  1608.         HiliteDropRow(dropRow, mIsDropBetweenRows);
  1609.         mDropRow = dropRow; // so that we'll hilite
  1610.     }
  1611.     Inherited::ScrollImageBy(inLeftDelta, inTopDelta, inRefresh);
  1612.     if (mayAutoScrollLeavingTurds && FocusDraw())
  1613.     {
  1614.         // Turn hiliting back on
  1615.         HiliteDropRow(mDropRow, mIsDropBetweenRows);
  1616.         // Notify the CTargetFramer to draw the border now.
  1617.         ExecuteAttachments(CTargetFramer::msg_BecomingTarget, this); // invert
  1618.     }
  1619. } // CStandardFlexTable::ScrollImageBy
  1620.  
  1621. //----------------------------------------------------------------------------------------
  1622. void CStandardFlexTable::DragSelection(
  1623.     const STableCell&         inCell, 
  1624.     const SMouseDownEvent&    inMouseDown    )
  1625. //----------------------------------------------------------------------------------------
  1626. {
  1627. #pragma unused(inCell)
  1628.  
  1629.     DragReference dragRef = 0;
  1630.     
  1631.     if (!FocusDraw()) return;
  1632.     
  1633.     try
  1634.     {
  1635.         StRegion dragRgn;
  1636.         OSErr err = ::NewDrag(&dragRef);
  1637.         ThrowIfOSErr_(err);    
  1638.         AddSelectionToDrag(dragRef, dragRgn);    
  1639.         if (inMouseDown.macEvent.modifiers & optionKey != 0)
  1640.         {
  1641.             CursHandle copyDragCursor = ::GetCursor(6608); // finder's copy-drag cursor
  1642.             if (copyDragCursor != nil)
  1643.                 ::SetCursor(*copyDragCursor);
  1644.         }
  1645.         (void) ::TrackDrag(dragRef, &(inMouseDown.macEvent), dragRgn);
  1646.     }
  1647.     catch( ... )
  1648.     {
  1649.     }
  1650.     
  1651.     if (dragRef != 0)
  1652.         ::DisposeDrag(dragRef);
  1653. } // CStandardFlexTable::DragSelection
  1654.  
  1655. //----------------------------------------------------------------------------------------
  1656. void CStandardFlexTable::AddSelectionToDrag(DragReference inDragRef, RgnHandle inDragRgn)
  1657. //----------------------------------------------------------------------------------------
  1658. {
  1659.     StRegion    tempRgn;
  1660.             
  1661.     ::SetEmptyRgn(inDragRgn);
  1662.     
  1663.     STableCell cell(0, 1);
  1664.     while (GetNextSelectedRow(cell.row))
  1665.     {
  1666.         AddRowDataToDrag(cell.row, inDragRef);
  1667.         GetRowDragRgn(cell.row, tempRgn);
  1668.         ::UnionRgn(tempRgn, inDragRgn, inDragRgn);
  1669.     }
  1670.     ::CopyRgn(inDragRgn, tempRgn);    
  1671.     ::InsetRgn(tempRgn, 1, 1);
  1672.     ::DiffRgn(inDragRgn, tempRgn, inDragRgn);
  1673.  
  1674.     // Convert the region to global coordinates
  1675.     Point p;
  1676.     p.h = p.v = 0;
  1677.     ::LocalToGlobal(&p);
  1678.     ::OffsetRgn(inDragRgn, p.h, p.v);
  1679. } // CStandardFlexTable::AddSelectionToDrag
  1680.  
  1681. //----------------------------------------------------------------------------------------
  1682. void CStandardFlexTable::ChangeSort(const LTableHeader::SortChange* /*inSortChange*/)
  1683. //----------------------------------------------------------------------------------------
  1684. {
  1685. } // CStandardFlexTable::ChangeSort
  1686.  
  1687. //----------------------------------------------------------------------------------------
  1688. void CStandardFlexTable::GetDropFlagRect(
  1689.     const Rect&    inLocalCellRect,
  1690.     Rect& outFlagRect    ) const
  1691. //----------------------------------------------------------------------------------------
  1692. {
  1693.     UInt32    vDiff = (inLocalCellRect.bottom - inLocalCellRect.top - 12) >> 1;
  1694.     
  1695.     outFlagRect.top     = inLocalCellRect.top + vDiff;
  1696.     outFlagRect.bottom = outFlagRect.top + 12;
  1697.     
  1698.     outFlagRect.left     = inLocalCellRect.left + 3;
  1699.     outFlagRect.right     = outFlagRect.left + 16;    
  1700. } // CStandardFlexTable::GetDropFlagRect
  1701.  
  1702. //----------------------------------------------------------------------------------------
  1703. TableIndexT CStandardFlexTable::GetSelectedRowCount() const
  1704. //----------------------------------------------------------------------------------------
  1705. {
  1706.     return ((LTableRowSelector*)mTableSelector)->GetSelectedRowCount();
  1707.         // safe cast. 
  1708. } // CStandardFlexTable::GetSelectedRowCount
  1709.  
  1710. //----------------------------------------------------------------------------------------
  1711. void CStandardFlexTable::SetNotifyOnSelectionChange(Boolean inDoNotify)
  1712. //----------------------------------------------------------------------------------------
  1713. {
  1714.     if (inDoNotify)
  1715.         StartBroadcasting();
  1716.     else
  1717.         StopBroadcasting();
  1718. } // CStandardFlexTable::SetNotifyOnSelectionChange
  1719.  
  1720. //-----------------------------------
  1721. Boolean CStandardFlexTable::GetNotifyOnSelectionChange()
  1722. //-----------------------------------
  1723. {
  1724.     return IsBroadcasting();
  1725.     
  1726. } // CStandardFlexTable::GetNotifyOnSelectionChange
  1727.  
  1728. //----------------------------------------------------------------------------------------
  1729. const TableIndexT* CStandardFlexTable::GetUpdatedSelectionList(TableIndexT& outSelectionSize)
  1730.                             // Returns a ONE-BASED list of TableIndexT, as it should.
  1731.                             // The result is const because this is not a copy, it is
  1732.                             // the actual list cached in this object.  You probably want
  1733.                             // to use CMailFlexTable::GetSelection() for message stuff.
  1734. //----------------------------------------------------------------------------------------
  1735. {
  1736.     outSelectionSize = GetSelectedRowCount();
  1737.     // if we've got a selection list, it's assumed up to date,
  1738.     // so return it.  When the selection changes, we just get
  1739.     // rid of it (in SelectionChanged()) and set mSelectionList to NULL
  1740.     if (mSelectionList)
  1741.         return mSelectionList;
  1742.     // If there is a selection, allocate the list
  1743.     if (outSelectionSize > 0) 
  1744.     {
  1745.         mSelectionList = new TableIndexT[outSelectionSize];
  1746.         if (!mSelectionList)
  1747.             outSelectionSize = 0;
  1748.     }
  1749.     if (mSelectionList)
  1750.     {
  1751.         TableIndexT selectedRow = 0;
  1752.         TableIndexT *item = mSelectionList;
  1753.         for (int i = 0; i < outSelectionSize; i++)
  1754.         {
  1755.             Boolean ok = GetNextSelectedRow(selectedRow);
  1756.             Assert_(ok);
  1757.             if (!ok) break;
  1758.             *item++ = selectedRow;
  1759.         }
  1760.     }
  1761.     return mSelectionList;
  1762. } // CStandardFlexTable::GetUpdatedSelectionList
  1763.  
  1764. //----------------------------------------------------------------------------------------
  1765. Boolean CStandardFlexTable::IsColumnVisible(PaneIDT inID)
  1766. //----------------------------------------------------------------------------------------
  1767. {
  1768.     return mTableHeader->IsColumnVisible(inID);
  1769. }
  1770.  
  1771. //----------------------------------------------------------------------------------------
  1772. void CStandardFlexTable::SelectionChanged()
  1773. // invalidate any cached selection list
  1774. //----------------------------------------------------------------------------------------
  1775. {
  1776.      delete [] mSelectionList;
  1777.     mSelectionList = NULL;
  1778.     BroadcastMessage(msg_SelectionChanged, this);
  1779. } // CStandardFlexTable::SelectionChanged
  1780.  
  1781. //----------------------------------------------------------------------------------------
  1782. void CStandardFlexTable::DrawTextString(
  1783.     const char*        inText, 
  1784.     const FontInfo*    inFontInfo,
  1785.     SInt16            inMargin,
  1786.     const Rect&        inBounds,
  1787.     SInt16            inJustification,
  1788.     Boolean            inDoTruncate,
  1789.     TruncCode        inTruncWhere)
  1790. //----------------------------------------------------------------------------------------
  1791. {
  1792.     Rect r = inBounds;
  1793.     
  1794.     r.left  += inMargin;
  1795.     r.right -= inMargin;
  1796.     
  1797.     UGraphicGizmos::PlaceTextInRect(inText, 
  1798.                                     strlen(inText),
  1799.                                     r,
  1800.                                     inJustification,
  1801.                                     teCenter,
  1802.                                     inFontInfo,
  1803.                                     inDoTruncate,
  1804.                                     inTruncWhere );                                
  1805. } // CStandardFlexTable::DrawTextString
  1806.  
  1807. //----------------------------------------------------------------------------------------
  1808. void CStandardFlexTable::DrawIconFamily(
  1809.      ResIDT                inIconID,
  1810.     SInt16                inIconWidth,
  1811.     SInt16                inIconHeight,
  1812.     IconTransformType    inTransform,
  1813.     const Rect&            inBounds)
  1814. //----------------------------------------------------------------------------------------
  1815. {
  1816.     OSErr    err;
  1817.     Rect    iconRect = inBounds;
  1818.     
  1819.     iconRect.right     = iconRect.left + inIconWidth;
  1820.     iconRect.bottom    = iconRect.top  + inIconHeight;
  1821.     
  1822.     UGraphicGizmos::CenterRectOnRect(iconRect, inBounds);    
  1823.     err = ::PlotIconID(&iconRect, atNone, inTransform, inIconID);
  1824. } // CStandardFlexTable::DrawIconFamily
  1825.  
  1826. //----------------------------------------------------------------------------------------
  1827. SInt16 CStandardFlexTable::DrawIcons(
  1828.     const STableCell& inCell,
  1829.     const Rect& inLocalRect) const
  1830. //----------------------------------------------------------------------------------------
  1831. {
  1832.     Rect iconRect;
  1833.     GetDropFlagRect(inLocalRect, iconRect);
  1834.     // Draw the drop flag if the folder has subfolders
  1835.     // UI issue: the following test makes us differ from the Finder: there, all
  1836.     // folders have dropflags, whether they have subfolders or not.  This diff may confuse
  1837.     // users... if so, just comment out the "if" line. - jrm
  1838.     Boolean expanded;
  1839.     if (CellHasDropFlag(inCell, expanded))
  1840.     {
  1841.         StColorState state; // LDropFlag::Draw has a bug (clobbers the color).
  1842.         LDropFlag::Draw(iconRect, expanded);
  1843.     }
  1844.  
  1845.     // Work out the correct transform type, and then call our virtual method so that
  1846.     // derived classes can draw the icons they want.
  1847.     IconTransformType transformType = kTransformNone;
  1848.     if (mClickTimer)
  1849.     {
  1850.         // A fancy double-click is in progress.
  1851.         // Only the fake selection should be hilited
  1852.         if (inCell.row == mClickTimer->GetClickedRow())
  1853.             transformType = kTransformSelected;
  1854.     }
  1855.     else
  1856.     {
  1857.         if (inCell.row == mDropRow)
  1858.         {
  1859.             // Drop-target always hilited
  1860.             transformType = kTransformSelected;
  1861.         }
  1862.         else if (IsActive() && CellIsSelected(inCell))
  1863.         {
  1864.             // Only the real selection is hilited (and only in the active pane)
  1865.             transformType = kTransformSelected;
  1866.         }
  1867.     }
  1868.     GetIconRect(inCell, inLocalRect, iconRect);
  1869.     DrawIconsSelf(inCell, transformType, iconRect);
  1870.     return iconRect.right;
  1871. } // CStandardFlexTable::DrawIcons
  1872.  
  1873. //----------------------------------------------------------------------------------------
  1874. void CStandardFlexTable::DrawIconsSelf(
  1875.     const STableCell& inCell,
  1876.     IconTransformType inTransformType,
  1877.     const Rect& inIconRect) const
  1878. //----------------------------------------------------------------------------------------
  1879. {
  1880.     ResIDT iconID = GetIconID(inCell.row);
  1881.     if (iconID)
  1882.         DrawIconFamily(iconID, 16, 16, inTransformType, inIconRect);
  1883. } // CStandardFlexTable::DrawIconsSelf
  1884.  
  1885. //----------------------------------------------------------------------------------------
  1886. Boolean CStandardFlexTable::IsSortedBackwards() const
  1887. //----------------------------------------------------------------------------------------
  1888. {
  1889.     return mTableHeader->IsSortedBackwards();
  1890. }
  1891.                         
  1892. //----------------------------------------------------------------------------------------
  1893. PaneIDT CStandardFlexTable::GetSortedColumn() const
  1894. //----------------------------------------------------------------------------------------
  1895. {
  1896.     PaneIDT result;
  1897.     mTableHeader->GetSortedColumn(result);
  1898.     return result;
  1899. }
  1900.  
  1901. //----------------------------------------------------------------------------------------
  1902. void CStandardFlexTable::SetRightmostVisibleColumn(UInt16 inLastDesiredColumn)
  1903. //----------------------------------------------------------------------------------------
  1904. {
  1905.     mTableHeader->SetRightmostVisibleColumn(inLastDesiredColumn);
  1906. } // CStandardFlexTable::SetRightmostVisibleColumn
  1907.  
  1908. #pragma mark -
  1909. #if defined(QAP_BUILD)
  1910.  
  1911. //----------------------------------------------------------------------------------------
  1912. void CStandardFlexTable::QapGetListInfo(PQAPLISTINFO pInfo)
  1913. //----------------------------------------------------------------------------------------
  1914. {
  1915.     if (pInfo == nil)
  1916.         return;
  1917.  
  1918.     // get table size
  1919.     TableIndexT outRows, outCols;
  1920.     GetTableSize(outRows, outCols);
  1921.     
  1922.     // get range of cells that are within the frame
  1923.     Rect    frame;
  1924.     CalcLocalFrameRect(frame);
  1925.     STableCell    topLeftCell, botRightCell;
  1926.     FetchIntersectingCells(frame, topLeftCell, botRightCell);
  1927.  
  1928.     // fetch vertical scrollbar Macintosh control
  1929.     ControlHandle macVScroll = NULL;
  1930.     LScroller *myScroller = dynamic_cast<LScroller *>(GetSuperView());
  1931.     if (myScroller != NULL)
  1932.     {
  1933.         if (myScroller->GetVScrollbar() != NULL)
  1934.             macVScroll = myScroller->GetVScrollbar()->GetMacControl();
  1935.     }
  1936.  
  1937.     pInfo->itemCount    = (short)outRows;
  1938.     pInfo->topIndex     = topLeftCell.row;
  1939.     pInfo->itemHeight     = GetRowHeight(0);
  1940.     pInfo->visibleCount = botRightCell.row - topLeftCell.row + 1;
  1941.     pInfo->vScroll         = macVScroll;
  1942.     pInfo->isMultiSel     = true;
  1943.     pInfo->isExtendSel     = true;
  1944.     pInfo->hasText         = true;
  1945. }
  1946.  
  1947.  
  1948. //----------------------------------------------------------------------------------------
  1949. Ptr CStandardFlexTable::QapAddCellToBuf(Ptr pBuf, Ptr pLimit, const STableCell& sTblCell)
  1950. //----------------------------------------------------------------------------------------
  1951. {
  1952.     char     str[cMaxMailFolderNameLength + 1];
  1953.     short    len = 0;
  1954.  
  1955.     GetQapRowText(sTblCell.row, str, sizeof(str));
  1956.     len = strlen(str) + 1;
  1957.  
  1958.     if (pBuf + sizeof(short) + len >= pLimit)
  1959.         return NULL;
  1960.  
  1961.     *(unsigned short *)pBuf = sTblCell.row - 1;
  1962.     if (CellIsSelected(sTblCell))
  1963.         *(unsigned short *)pBuf |= 0x8000;
  1964.  
  1965.     pBuf += sizeof(short);
  1966.  
  1967.     strcpy(pBuf, str);
  1968.     pBuf += len;
  1969.  
  1970.     return pBuf;
  1971. }
  1972.  
  1973. //----------------------------------------------------------------------------------------
  1974. void CStandardFlexTable::GetQapRowText(
  1975.     TableIndexT            /*inRow*/,
  1976.     char*                outText,
  1977.     UInt16                inMaxBufferLength) const
  1978. // Calculate the text and (if ioRect is not passed in as null) a rectangle that fits it.
  1979. // Return result indicates if any of the text is visible in the cell
  1980. //----------------------------------------------------------------------------------------
  1981. {
  1982.     if (outText && inMaxBufferLength > 0)
  1983.         *outText = '\0';
  1984. } // CStandardFlexTable::GetQapRowText
  1985.  
  1986. #endif //QAP_BUILD
  1987.