home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / C / BC_502 / OWLSRC.PAK / TABCTRL.CPP < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-06  |  44.0 KB  |  1,773 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows
  3. // Copyright (c) 1995, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   10.22  $
  6. //
  7. // Implementation of classes TTabItem and TTabControl.
  8. //----------------------------------------------------------------------------
  9. #include <owl/pch.h>
  10. #if !defined(OWL_TABCTRL_H)
  11. # include <owl/tabctrl.h>
  12. #endif
  13. #if !defined(OWL_UIHELPER_H)
  14. # include <owl/uihelper.h>
  15. #endif
  16. #if !defined(OWL_DC_H)
  17. # include <owl/dc.h>
  18. #endif
  19. #if !defined(OWL_GDIOBJEC_H)
  20. # include <owl/gdiobjec.h>
  21. #endif
  22.  
  23. OWL_DIAGINFO;
  24. DIAG_DECLARE_GROUP(OwlCommCtrl);        // CommonCtrl Diagnostic group
  25.  
  26. //
  27. // Constants used in OWL's implementation of Tab Controls
  28. //
  29. const int InitSize     = 5;         // Initial size of Tab Collection
  30. const int InitDelta    = 5;         // Inc. when expanding collection
  31. const int HorzPad      = 6;         // Pad on each side of tab label
  32. const int VertPad      = 4;         // Pad above/below each tab label
  33. const int HorzSelInc   = 1;         // Inc. for selected tab
  34. const int VertSelInc   = 1;         // Inc. for selected tab
  35. const int HorzMargin   = 3;         // Margin on left/right of control
  36. const int VertMargin   = 3;         // Margin above control
  37. const int ClientMargin = 2;         // Inner margin of tab's client area
  38. const int ID_Updown    = 0x100;     // Ctl. identifier of scroller;
  39.  
  40. //
  41. // Constructor for a Tab Item
  42. //
  43. // The item is initialized with the state of an actual tab in
  44. // a created tab control.
  45. //
  46. TTabItem::TTabItem(const TTabControl& ctl, int index, uint msk,
  47.                    int buffLen, char far* buffer)
  48. {
  49.   PRECONDITION(ctl.GetHandle());
  50.  
  51.   // When retrieving text, a buffer and length must be supplied
  52.   //
  53.   PRECONDITION(!(msk & TCIF_TEXT) || (buffLen && buffer));
  54.  
  55.   mask = msk;
  56.   if (buffer && buffLen)
  57.     SetLabel(buffer, buffLen);
  58.  
  59.   ctl.GetItem(index, *this);
  60. }
  61.  
  62. //
  63. // Constructor for a Tab Item
  64. //
  65. // The tab item's label field is initialized to the specified
  66. // buffer and extra parameter set to the 'param' parameter.
  67. //
  68. TTabItem::TTabItem(const char far* str, int buffLen, TParam2 param)
  69. {
  70.   mask = 0;
  71.   iImage = -1;
  72.  
  73.   SetParam(param);
  74.   SetLabel(str, buffLen);
  75. }
  76.  
  77. //
  78. // Constructor for a Tab Item
  79. //
  80. // The tab item is initialized with the IMAGELIST index and extra
  81. // parameter specified.
  82. //
  83. TTabItem::TTabItem(int imageIndex, TParam2 param)
  84. {
  85.   mask = 0;
  86.   pszText = 0;
  87.   cchTextMax = 0;
  88.  
  89.   SetParam(param);
  90.   SetIcon(imageIndex);
  91. }
  92.  
  93. //
  94. // Constructor for a Tab Item
  95. //
  96. // The tab item is initialized with the IMAGELIST index and label
  97. // specified..
  98. //
  99. TTabItem::TTabItem(int imageIndex, const char far* str)
  100. {
  101.   mask = 0;
  102.   SetIcon(imageIndex);
  103.   SetLabel(str);
  104. }
  105.  
  106. //
  107. // Initializes the structure member representing the tab's text
  108. //
  109. void
  110. TTabItem::SetLabel(const char far* str, int len)
  111. {
  112.   pszText = CONST_CAST(char far*, str);
  113.   cchTextMax = len ? len : strlen(str);
  114.   mask |= TCIF_TEXT;
  115. }
  116.  
  117. //----------------------------------------------------------------------------
  118. DEFINE_RESPONSE_TABLE1(TTabControl, TControl)
  119.   EV_WM_HSCROLL,
  120.   EV_WM_VSCROLL,
  121. #if !defined(OWL_NATIVECTRL_ALWAYS)
  122.   EV_WM_SIZE,
  123.   EV_WM_LBUTTONDOWN,
  124.   EV_WM_SETFOCUS,
  125.   EV_WM_KILLFOCUS,
  126.   EV_WM_GETDLGCODE,
  127.   EV_WM_KEYDOWN,
  128. #endif  // OWL_NATIVECTRL_ALWAYS
  129. END_RESPONSE_TABLE;
  130.  
  131.  
  132. //
  133. // constructor for TTabControl
  134. //
  135. // initializes its data fields using parameters passed and default values
  136. //
  137. // by default, a TabControl associated with the TTabControl will:
  138. //   - be visible upon creation
  139. //   - display only one row of tabs.
  140. //
  141. TTabControl::TTabControl(TWindow*   parent,
  142.                          int        id,
  143.                          int x, int y, int w, int h,
  144.                          TModule*   module)
  145. :
  146.   TControl(parent, id, 0, x, y, w, h, module)
  147. #if !defined(OWL_NATIVECTRL_ALWAYS)
  148.   ,Updown(0), NormalFont(0), SelectFont(0), TabList(0)
  149. #endif
  150. {
  151.   Attr.Style |= (TCS_SINGLELINE|TCS_TABS|WS_CLIPCHILDREN|WS_CLIPSIBLINGS);
  152.  
  153.   // By default we'll use the native implementation if it's available
  154.   //
  155.   NativeUse = TCommCtrl::IsAvailable() ? nuAlways : nuNever;
  156.  
  157. #if !defined(OWL_NATIVECTRL_ALWAYS)
  158.   // When running in an environment where the system does not provide
  159.   // Common Controls we need to initialize the variables used for
  160.   // emulating a tabcontrol...
  161.   //
  162.   if (!TCommCtrl::IsAvailable())
  163.     InitCtl();
  164. #else
  165.   // When OWL is built with the NATIVECTRL_ALWAYS option, the
  166.   // Common Control library MUST be available....
  167.   //
  168.   CHECK(TCommCtrl::IsAvailable());
  169. #endif
  170. }
  171.  
  172. //
  173. // Constructor for a TTabControl object associated with a Tab Control
  174. // specified in a dialog resource.
  175. //
  176. TTabControl::TTabControl(TWindow*   parent,
  177.                          int        resourceId,
  178.                          TModule*   module)
  179. :
  180.   TControl(parent, resourceId, module)
  181. {
  182.   // By default we'll use the native implementation if it's available
  183.   // NOTE: Since this control is really originating from a dialog
  184.   //       resource, we cannot ascertain the classname used by the
  185.   //       resource designer before having access to the HWND.
  186.   //       Hence, we'll assume that if we're running on a system
  187.   //       with native common control support, the dialog resource
  188.   //       specified the native class name.
  189.   //
  190.   NativeUse = TCommCtrl::IsAvailable() ? nuAlways : nuNever;
  191.  
  192. #if !defined(OWL_NATIVECTRL_ALWAYS)
  193.   // When running in an environment where the system does not provide
  194.   // Common Controls we need to initialize the variables used for
  195.   // emulating a tabcontrol...
  196.   //
  197.   if (!TCommCtrl::IsAvailable())
  198.     InitCtl();
  199. #else
  200.   // When OWL is built with the NATIVECTRL_ALWAYS option, the
  201.   // Common Control library MUST be available....
  202.   //
  203.   CHECK(TCommCtrl::IsAvailable());
  204. #endif
  205. }
  206.  
  207. //
  208. // Constructor for a TTabControl object to be associated with 
  209. // an already created Tab Control.
  210. //
  211. TTabControl::TTabControl(HWND hwnd) : TControl(hwnd)
  212. {
  213.   PRECONDITION(hwnd && ::IsWindow(hwnd));
  214.  
  215.   // Here we'll assume if the Common Control Library is available, the
  216.   // window passed is being backed by the CommonControl Library (i.e.
  217.   // OWL does not need to provide the underlying implementation for
  218.   // tab controls.
  219. }
  220.  
  221. //
  222. // Cleans up if underlying Tab support was provided by ObjectWindows
  223. //
  224. TTabControl::~TTabControl()
  225. {
  226. #if !defined(OWL_NATIVECTRL_ALWAYS)
  227.   // When running in an environment where the system does not provide
  228.   // Common Controls we need to cleanup the variables used for
  229.   // emulating a tabcontrol...
  230.   //
  231.   if (!TCommCtrl::IsAvailable())
  232.     CleanupCtl();
  233. #endif
  234. }
  235.  
  236. //
  237. // Returns the class name of the underlying control associated with
  238. // the TTabControl object.
  239. //
  240. char far*
  241. TTabControl::GetClassName()
  242. {
  243. #if defined(OWL_NATIVECTRL_ALWAYS)
  244.   // When OWL is built with the NATIVECTRL_ALWAYS option, the
  245.   // Common Control library MUST be available....
  246.   //
  247.   PRECONDITION(TCommCtrl::IsAvailable());
  248.   NativeUse = TNativeUse(NativeUse | nuUsing);
  249.   return WC_TABCONTROL;
  250.  
  251. #else
  252.   // Update flags (??)
  253.   //
  254.   if (TCommCtrl::IsAvailable())
  255.     NativeUse = TNativeUse(NativeUse | nuUsing);
  256.   else
  257.     NativeUse = TNativeUse(NativeUse & ~nuUsing);
  258.  
  259.   // For debugging purposes only
  260.   //
  261. #if defined(__OWL_NO_COMMCTRL)
  262.  
  263.   // CommCtrl is always available under the new Shell - If IsAvailable()
  264.   // returns false, we must be running a test/debugging run. Return a
  265.   // non-system name to avoid conflicts
  266.   //
  267.   if (!TCommCtrl::IsAvailable() && TSystem::Has3dUI())
  268.     return "OWL_Tab";
  269. #endif
  270.  
  271.   // Return same name irrespective of whether OWL or Common Control Library
  272.   // provides underlying implementation. This eases the usage of the control
  273.   // in a dialog resource.
  274.   //
  275.   return WC_TABCONTROL;
  276. #endif
  277. }
  278.  
  279. //
  280. // Transfer is not implemented in TTabControl given that each item
  281. // interacts with settings outside of the TC_ITEM members. [eg. the 
  282. // image index points to the ImageList].
  283. //
  284. uint
  285. TTabControl::Transfer(void* /*buffer*/, TTransferDirection /*direction*/)
  286. {
  287.   TRACEX(OwlCommCtrl, 0, "TTabControl::Transfer is not implemented");
  288.   return 0;
  289. }
  290.  
  291. #if defined(OWL_NATIVECTRL_ALWAYS)
  292. //
  293. // Keep TWindow from rerouting this message - it must be left as is 
  294. // for the tab control as it may originate from the control's spin.
  295. //
  296. void
  297. TTabControl::EvVScroll(uint, uint, THandle)
  298. {
  299.   DefaultProcessing();
  300. }
  301.  
  302. //
  303. // Keep TWindow from rerouting this message - it must be left as is 
  304. // for the tab control as it may originate from the control's spin.
  305. //
  306. void
  307. TTabControl::EvHScroll(uint, uint, THandle)
  308. {
  309.   DefaultProcessing();
  310. }
  311.  
  312. #else   
  313.  
  314. //
  315. // Handler for WM_VSCROLL messages
  316. //
  317. void
  318. TTabControl::EvVScroll(uint scrollCode, uint /*pos*/, THandle /*hWndCtl*/)
  319. {
  320.   if (TCommCtrl::IsAvailable()) {
  321.     DefaultProcessing();
  322.   } 
  323.   else {
  324.     if (scrollCode == SB_LINEDOWN) {
  325.       if (GetFirstVisibleTab() > 0)
  326.         SetFirstVisibleTab(GetFirstVisibleTab()-1);
  327.     }
  328.     else if (scrollCode == SB_LINEUP) {
  329.       if (!IsVisible(GetCount()-1) || IsClipped(GetCount()-1))
  330.         SetFirstVisibleTab(GetFirstVisibleTab()+1);
  331.     }
  332.   }
  333. }
  334.  
  335. //
  336. // Handler for WM_VSCROLL messages
  337. //
  338. void
  339. TTabControl::EvHScroll(uint, uint, THandle)
  340. {
  341.   DefaultProcessing();
  342. }
  343. #endif  
  344.  
  345.  
  346. #if !defined(OWL_NATIVECTRL_ALWAYS)
  347.  
  348. //
  349. // By default, GetWindowClass is only called if the class is not
  350. // already registered - i.e. If GetClassInfo on the classname failed.
  351. // Therefore, when we use the Common Control Library, the following
  352. // method is never invoked.
  353. //
  354. void
  355. TTabControl::GetWindowClass(WNDCLASS& wndClass)
  356. {
  357.   TControl::GetWindowClass(wndClass);
  358.   wndClass.hbrBackground = HBRUSH(COLOR_BTNFACE + 1);
  359.   wndClass.style = CS_VREDRAW|CS_HREDRAW;
  360. }
  361.  
  362. //
  363. // SetupWindow: Initializes internal state variables when 
  364. // ObjectWindows provides the underlying support for tab controls.
  365. //
  366. void
  367. TTabControl::SetupWindow()
  368. {
  369.   TControl::SetupWindow();
  370.  
  371.   if (!TCommCtrl::IsAvailable()) {
  372.     // Setup Fonts used by control
  373.     //
  374.     SetupFont();
  375.   }
  376. }
  377.  
  378. //
  379. // If the 'clientInWindowOut' parameter is false, this method calculates
  380. // the display area of a tab control's display from a window rectangle
  381. // specified by the 'rect' parameter. Otherwise, the method calculates
  382. // the window rectangle that would correspond to display area specified
  383. // by the 'rect' parameter. The 'rect' parameter receives the newly computed
  384. // rectangle.
  385. //
  386. void
  387. TTabControl::AdjustRect(bool clientInWindowOut, TRect& rect)
  388. {
  389.   if (TCommCtrl::IsAvailable())
  390.     SendMessage(TCM_ADJUSTRECT, TParam1(clientInWindowOut), TParam2(&rect));
  391.   else {
  392.  
  393.     // NOTE: The following logic is simplified by letting windows provide 
  394.     //       the delta between WindowRect and ClientRect and
  395.     //       then offseting the passed rectangle accordingly...
  396.     //       If the tab is at (0, 0, 0, 0) [for example, if it's
  397.     //       minimized], this logic will fail. 
  398.     //      
  399.     //       A better approach [but more involved] would be to 
  400.     //       use AdjustWindowRectEx and the system metrics information
  401.     //       to perform the computation.
  402.  
  403.     // Retrieve current rectangles of tab to have the deltas
  404.     // between the window rect and the client area...
  405.     //
  406.     TRect clientArea, winRect;
  407.     GetClientArea(clientArea, true);
  408.     GetWindowRect(winRect);
  409.  
  410.     // Make the window-rect (0, 0)-based so it may be used 
  411.     // as a delta...
  412.     //
  413.     ::MapWindowPoints(HWND_DESKTOP, *this, (LPPOINT)&winRect, 2);
  414.  
  415.     if (clientInWindowOut) {
  416.       // 'rect' specifies the clientarea, we give back the 
  417.       // Windowrect...
  418.       //
  419.       rect.left  -= (clientArea.left - winRect.left);
  420.       rect.right += (winRect.right - clientArea.right);
  421.       rect.top   -= (clientArea.top - winRect.top);
  422.       rect.bottom+= (winRect.bottom - clientArea.bottom);
  423.     }
  424.     else {
  425.       // 'rect' specifies the WindowRect, we give back the 
  426.       // clientArea...
  427.       //
  428.       rect.left  += (clientArea.left - winRect.left);
  429.       rect.right -= (winRect.right - clientArea.right);
  430.       rect.top   += (clientArea.top - winRect.top);
  431.       rect.bottom-= (winRect.bottom - clientArea.bottom);
  432.     }
  433.   }
  434. }
  435.  
  436. //
  437. // Adds a new tab described by the 'item' parameter to the tab control.
  438. // The return value is the index of the new tab or -1 in case of error.
  439. //
  440. int
  441. TTabControl::Add(const TTabItem& item)
  442. {
  443.   return Insert(item, GetCount());
  444. }
  445.  
  446. //
  447. // Adds a new tab with the 'tabText' caption to the tab control
  448. // Returns the index of the new tab, if successful or -1 otherwise.
  449. //
  450. int 
  451. TTabControl::Add(const char far* tabText)
  452. {
  453.   return Insert(tabText, GetCount());
  454. }
  455.  
  456. //
  457. // Inserts a new tab described by the 'item' parameter to the tab
  458. // control at the position specified by the 'index' parameter.
  459. // The return value is the index of the new tab or -1 in case of error.
  460. //
  461. int
  462. TTabControl::Insert(const TTabItem& item, int index)
  463. {
  464.   if (TCommCtrl::IsAvailable())
  465.     return (int)SendMessage(TCM_INSERTITEM, index,
  466.                             TParam2((const TC_ITEM far*)(&item)));
  467.   else  {
  468.     // Insert the helper object
  469.     //
  470.     TabList->InsertAt(index, new TTabEntryInternal(item));
  471.  
  472.     // Set desired size of tab
  473.     //
  474.     SetTabSize(index);
  475.  
  476.     // Invalidate the whole client on first tab to allow the border
  477.     // to be redrawn...
  478.     // NOTE: We'll opt to select the first item added... Is this
  479.     //       acceptable or should we allow the control to have 
  480.     //       items where none is selected until User-action??
  481.     //
  482.     if (GetCount() == 1)  {
  483.       SetSel(0);
  484.       Invalidate();
  485.     }
  486.  
  487.     // Allow control to (re)layout the various tabItem locations
  488.     // based on their indices..
  489.     //
  490.     SetTabRects(FirstVisibleTab);
  491.  
  492.     // Return the index of the tab
  493.     //
  494.     return index;
  495.   }
  496. }
  497.  
  498. //
  499. // Inserts a new tab with the caption 'tabText' at the 
  500. // specified 'index'. Returns the index of the new tab, if successful.
  501. //
  502. int
  503. TTabControl::Insert(const char far* tabText, int index)
  504. {
  505.   return Insert(TTabItem(tabText), index);
  506. }
  507.  
  508. //
  509. // Removes the item at the position specified by the 'index' parameter.
  510. // Returns 'true' if successful or 'false' otherwise.
  511. //
  512. bool
  513. TTabControl::Delete(int index)
  514. {
  515.   if (TCommCtrl::IsAvailable()) {
  516.     return SendMessage(TCM_DELETEITEM, index) != 0;
  517.   }
  518.   else {
  519.  
  520.     // Validate/free item at index...
  521.     //
  522.     if (index < GetCount()) {
  523.  
  524.       // Keep flag if we'll need to redraw
  525.       //
  526.       bool needRedraw = IsVisible(index);
  527.  
  528.       // Invalidate the leaving tab
  529.       //
  530.       if (needRedraw)
  531.         InvalidateTabRect(index);
  532.  
  533.       // Free the item structure
  534.       //
  535.       TabList->FreeAt(index);
  536.  
  537.       // Layout tabs or invalidate if we just lost the last tab
  538.       //
  539.       if (GetCount() == 0) { 
  540.         FirstVisibleTab = 0;
  541.         SelectedTab = -1;
  542.         Invalidate();
  543.       }
  544.       else {
  545.         if (FirstVisibleTab == GetCount())
  546.           FirstVisibleTab--;
  547.  
  548.         if (needRedraw) 
  549.           SetTabRects(FirstVisibleTab);
  550.       }
  551.  
  552.       return true;
  553.     }
  554.   }
  555.   return false;
  556. }
  557.  
  558. //
  559. // Removes all items from the tab control. Returns 'true' if successful or
  560. // 'false' otherwise.
  561. //
  562. bool
  563. TTabControl::DeleteAll()
  564. {
  565.   if (TCommCtrl::IsAvailable())
  566.     return SendMessage(TCM_DELETEALLITEMS) != 0;
  567.   else {
  568.     while(GetCount())
  569.       Delete(0);
  570.   }
  571.   return true;
  572. }
  573.  
  574. //
  575. // Returns the number of tab items in the tab control.
  576. //
  577. int
  578. TTabControl::GetCount() const
  579. {
  580.   if (TCommCtrl::IsAvailable())
  581.     return (int)CONST_CAST(TTabControl*, this)->SendMessage(TCM_GETITEMCOUNT);
  582.   else
  583.     return TabList->GetCount();
  584. }
  585.  
  586. //
  587. // Retrieves the current number of rows in the tab control.
  588. // NOTE: Only tabs with the TCS_MULTILINE style can have multiple rows.
  589. //
  590. int
  591. TTabControl::GetRowCount() const
  592. {
  593.   if (TCommCtrl::IsAvailable())
  594.     return (int)CONST_CAST(TTabControl*, this)->SendMessage(TCM_GETROWCOUNT);
  595.   else
  596.     return NumRows;
  597. }
  598.  
  599. //
  600. // Returns the index of the currently selected tab item in the tab control.
  601. // Returns -1 if no tab is selected.
  602. //
  603. int
  604. TTabControl::GetSel() const
  605. {
  606.   if (TCommCtrl::IsAvailable())
  607.     return (int)CONST_CAST(TTabControl*, this)->SendMessage(TCM_GETCURSEL);
  608.   else
  609.     return SelectedTab;
  610. }
  611.  
  612. //
  613. // Selects the tab item at the position specified by the 'index' parameter.
  614. // The return value is the index of the previously selected tab item if
  615. // successful or -1 otherwise.
  616. // NOTE: A tab control does not send TCN_SELCHANGING or TCN_SELCHANGE 
  617. //       notifications when a tab item is selected via this method.
  618. //
  619. int
  620. TTabControl::SetSel(int index)
  621. {
  622.   if (TCommCtrl::IsAvailable())
  623.     return (int)SendMessage(TCM_SETCURSEL, index);
  624.   else {
  625.     PRECONDITION(index < GetCount());
  626.     PRECONDITION(index >= 0);
  627.  
  628.     int prevSel = GetSel();
  629.     if (index != prevSel) {
  630.  
  631.       // Invalidate area occupied by previously selected item
  632.       //
  633.       if (prevSel >= 0 && IsVisible(prevSel))
  634.         InvalidateTabRect(prevSel);
  635.  
  636.       // Update selected index
  637.       //
  638.       SelectedTab = index;
  639.  
  640.       // Invalidate area occupied by new selection
  641.       //
  642.       if (index >= 0 && IsVisible(index))
  643.         InvalidateTabRect(index);
  644.     }
  645.     return prevSel;
  646.   }
  647. }
  648.  
  649. //
  650. // Retrieves information about the tab at the position specified by
  651. // the 'index' parameter. Returns true if successful or false otherwise.
  652. // NOTE: The 'mask' member of the 'item' structure specifies which
  653. //       attributes of the tab to return. When spefying TCIF_TEXT, item's
  654. //       pszText member must point to a valid buffer and cchTextMax must
  655. //       specify the size of the buffer.
  656. //
  657. bool
  658. TTabControl::GetItem(int index, TTabItem& item) const
  659. {
  660.   if (TCommCtrl::IsAvailable())
  661.     return CONST_CAST(TTabControl*, this)->SendMessage(TCM_GETITEM, index,
  662.                                            TParam2((TC_ITEM far*)&item)) != 0;
  663.   else {
  664.  
  665.     // Validate index
  666.     //
  667.     if (index < GetCount()) {
  668.  
  669.       TTabEntryInternal& tabInfo = (*TabList)[index];
  670.  
  671.       if (item.mask & TCIF_TEXT)
  672.         strncpy(item.pszText, tabInfo.pszText, item.cchTextMax);
  673.  
  674.       if (item.mask & TCIF_IMAGE)
  675.         item.iImage = tabInfo.iImage;
  676.  
  677.       if (item.mask & TCIF_PARAM)
  678.         item.lParam = tabInfo.lParam ;
  679.  
  680.       return true;
  681.     }
  682.   }
  683.   return false;
  684. }
  685.  
  686. //
  687. // Retrieves the bounding rectangle of a tab within a tab control.
  688. // Returns true if successful or false otherwise. 
  689. // NOTE: 'rect' receives the position in viewport coordinates.
  690. //
  691. bool
  692. TTabControl::GetItemRect(int index, TRect& rect) const
  693. {
  694.   if (TCommCtrl::IsAvailable())
  695.     return CONST_CAST(TTabControl*, this)->SendMessage(TCM_GETITEMRECT,
  696.                                          index, TParam2((LPRECT)&rect)) != 0;
  697.   else {
  698.     if (index < GetCount()) {
  699.       rect = (*TabList)[index].Rect;
  700.       return true;
  701.     }
  702.   }
  703.   return false;
  704. }
  705.  
  706. //
  707. // Sets some or all of a tab's attributes. The 'mask' member of the
  708. // 'item' parameter specifies which attributes to set.
  709. // Returns true if successful or false otherwise.
  710. //
  711. bool
  712. TTabControl::SetItem(int index, const TTabItem& item)
  713. {
  714.   if (TCommCtrl::IsAvailable())
  715.     return SendMessage(TCM_SETITEM, index,
  716.                        TParam2((const TC_ITEM far*)&item)) != 0;
  717.   else {
  718.     if (index < GetCount()) {
  719.  
  720.       // Retrieve reference to our helper object
  721.       //
  722.       TTabEntryInternal& tabInfo = (*TabList)[index];
  723.  
  724.       if (item.mask & TCIF_TEXT) {
  725.         delete[] tabInfo.pszText;
  726.         tabInfo.pszText = strnewdup(item.pszText);
  727.       }
  728.  
  729.       if (item.mask & TCIF_IMAGE)
  730.         tabInfo.iImage = item.iImage;
  731.  
  732.       if (item.mask & TCIF_PARAM)
  733.         tabInfo.lParam = item.lParam;
  734.  
  735.       // Force redraw if necessary
  736.       //
  737.       if (IsVisible(index) && (item.mask & (TCIF_TEXT|TCIF_IMAGE))) {
  738.         InvalidateTabRect(index);
  739.         SetTabRects(FirstVisibleTab);
  740.       }
  741.       return true;
  742.     }
  743.   }
  744.   return false;
  745. }
  746.  
  747. //
  748. // Sets the number of bytes per tab reserved for application-defined
  749. // data in a tab control. Returns true if successful or false otherwise.
  750. // NOTE: This method should be invoked only when the tab control does not
  751. //       contain any tabs.
  752. //
  753. bool
  754. TTabControl::SetItemExtra(int extra)
  755. {
  756.   if (TCommCtrl::IsAvailable())
  757.     return SendMessage(TCM_SETITEMEXTRA, extra) != 0;
  758.   else
  759.     // Not necessary when OWL provides underlying implementation of tabs
  760.     // since C++ provides a safer way to add extra data: derivation!
  761.     //
  762.     return false;
  763. }
  764.  
  765. //
  766. // Sets the size (width/height) of tabs in a fixed-width or owner-draw
  767. // tab control. Returns a TSize object containing the old width and height.
  768. //
  769. TSize
  770. TTabControl::SetItemSize(const TSize& size)
  771. {
  772.   if (TCommCtrl::IsAvailable())
  773.     return TSize(SendMessage(TCM_SETITEMSIZE, 0, MkParam2(size.cx, size.cy)));
  774.   else {
  775.     TSize prevSize = TabSize;
  776.     TabSize = size;
  777.     return prevSize;
  778.   }
  779. }
  780.  
  781. //
  782. // Sets the amount of space around each tab's icon and label in a tab 
  783. // control. 
  784. //
  785. void
  786. TTabControl::SetPadding(const TSize& size)
  787. {
  788.   if (TCommCtrl::IsAvailable())
  789.     SendMessage(TCM_SETPADDING, 0, MkParam2(size.cx, size.cy));
  790.   else {
  791.     Padding = size;
  792.   }
  793. }
  794.  
  795. //
  796. // Retrieves the ImageList associated with a tab control. Returns 0 if
  797. // unsuccessful.
  798. //
  799. HIMAGELIST
  800. TTabControl::GetImageList() const
  801. {
  802.   if (TCommCtrl::IsAvailable())
  803.     return (HIMAGELIST)
  804.             CONST_CAST(TTabControl*, this)->SendMessage(TCM_GETIMAGELIST);
  805.   else
  806.     TRACEX(OwlCommCtrl, 0, "TTabControl::GetImageList requires system "\
  807.                            "implementation of tab controls");
  808.     return 0;
  809. }
  810.  
  811. //
  812. // Assigns an imagelist to the tab control. Returns the handle of the
  813. // previous imagelist or 0 if there is no previous image list.
  814. //
  815. HIMAGELIST
  816. TTabControl::SetImageList(HIMAGELIST himl)
  817. {
  818.   if (TCommCtrl::IsAvailable())
  819.     return (HIMAGELIST)SendMessage(TCM_SETIMAGELIST, 0, TParam2(himl));
  820.   else
  821.     TRACEX(OwlCommCtrl, 0, "TTabControl::SetImageList requires system "\
  822.                            "implementation of tab controls");
  823.     return 0;
  824. }
  825.  
  826. //
  827. // Removes the image at the position specified by 'index' from the
  828. // imagelist associated with the tab control.
  829. // NOTE: The tab automatically updates each tab's image index so each
  830. //       tab remains associated with the same image it had been.
  831. //
  832. void
  833. TTabControl::RemoveImage(int index)
  834. {
  835.   if (TCommCtrl::IsAvailable())
  836.     SendMessage(TCM_REMOVEIMAGE, index);
  837.   else {
  838.     TRACEX(OwlCommCtrl, 0, "TTabControl::RemoveImage requires system "\
  839.                            "implementation of tab controls");
  840.   }
  841. }
  842.  
  843. //
  844. // Retrieves the handle of the tooltip control associated with the 
  845. // tab control. Returns 0 if unsuccessful.
  846. //
  847. TWindow::THandle
  848. TTabControl::GetToolTips() const
  849. {
  850.   if (TCommCtrl::IsAvailable()) {
  851.     return THandle(CONST_CAST(TTabControl*,this)->SendMessage(TCM_GETTOOLTIPS));
  852.   }
  853.   else {
  854.     TRACEX(OwlCommCtrl, 0, "TTabControl::GetToolTips requires system "\
  855.                            "implementation of tab controls");
  856.     return 0;
  857.   }
  858. }
  859.  
  860. //
  861. // Assigns a tooltip control to the tab control. 
  862. //
  863. void
  864. TTabControl::SetToolTips(THandle toolTip)
  865. {
  866.   if (TCommCtrl::IsAvailable())
  867.     SendMessage(TCM_SETTOOLTIPS, TParam1(toolTip));
  868.   else {
  869.     TRACEX(OwlCommCtrl, 0, "TTabControl::SetToolTips requires system "\
  870.                            "implementation of tab controls");
  871.   }
  872. }
  873.  
  874. //
  875. // Determines the index of the tab which is at the location 
  876. // specified in the 'pt' member of the 'htInfo' parameter.
  877. // Returns -1 if no tab is at the specified position.
  878. //
  879. int
  880. TTabControl::HitTest(TTabHitTestInfo& htInfo)
  881. {
  882.   if (TCommCtrl::IsAvailable()) {
  883.     return (int)SendMessage(TCM_HITTEST, 0,
  884.                               TParam2((TC_HITTESTINFO far*)&htInfo));
  885.   }
  886.   else {
  887.     TRACEX(OwlCommCtrl, 0, "TTabControl::HitTest requires system "\
  888.                            "implementation of tab controls");
  889.     return 0;
  890.   }
  891. }
  892.  
  893. //----------------------------------------------------------------------------
  894.  
  895. //
  896. // Constructor of a TabEntry object
  897. //
  898. TTabEntryInternal::TTabEntryInternal(const TC_ITEM& tbItem)
  899. :
  900.  TTabItem(tbItem),
  901.  Rect(0, 0, 0, 0),
  902.  Size(0, 0),
  903.  Row(0)
  904. {
  905.   // Make our own copy of the text if specified
  906.   //
  907.   if (mask & TCIF_TEXT && pszText)
  908.     pszText = strnewdup(pszText);
  909.   else
  910.     pszText = 0;
  911. }
  912.  
  913. //
  914. // Inflates a Tab's rectangle when the latter is selected
  915. //
  916. inline void
  917. AdjustSelRect(TRect& rect) {
  918.   rect.Inflate(HorzSelInc, 0);
  919.   rect.top -= VertSelInc;
  920. }
  921.  
  922. //
  923. // Determines if two rectangles meet
  924. // NOTE: This is different from TRect::Touches which reponds
  925. //       negatively when 'right == left' or 'top == bottom'.
  926. //
  927. inline bool
  928. RectsMeet(const TRect& rc1, const TRect rc2) {
  929.   return (rc1.right >= rc2.left && rc1.left <= rc2.right &&
  930.           rc1.bottom >= rc2.top && rc1.top <= rc2.bottom) ? true : false;
  931. }
  932.  
  933. //
  934. // Initialize variables when ObjectWindows provides underlying
  935. // support for tab control...
  936. //
  937. void
  938. TTabControl::InitCtl()
  939. {
  940.   // Initialize internal structures when using OWL's implementation
  941.   //
  942.   SelectedTab= -1;
  943.   FirstVisibleTab = 0;
  944.   NumRows = 1;
  945.   Padding = TSize(1, 1);
  946.   TabSize = TSize(0, 0);
  947.   TabList = new TCollection<TTabEntryInternal>(InitSize, InitDelta);
  948.   Updown = new TUpDown(this, ID_Updown, 0, 0, 0, 0, this);
  949.   Updown->ModifyStyle(UDS_WRAP, UDS_HORZ);
  950. }
  951.  
  952. //
  953. // Cleanup internal structures when ObjectWindows provides underlying
  954. // implementation for tab control.
  955. //
  956. void
  957. TTabControl::CleanupCtl()
  958. {
  959.   delete NormalFont;
  960.   delete SelectFont;
  961.   delete TabList;
  962. }
  963.  
  964.  
  965. //
  966. // Paint's a tab item
  967. //
  968. void
  969. TTabControl::PaintTab(TDC& dc, const TRect& tbRect, 
  970.                       const char far* txt, bool clip)
  971. {
  972.   // Grab a few pens
  973.   //
  974.   TPen hiPen(TColor::Sys3dHilight);
  975.   TPen txtPen(TColor::SysBtnText);
  976.   TPen drkPen(TColor::Sys3dShadow);
  977.  
  978.   //
  979.   // Draw highlights for top tabs
  980.   //
  981.   dc.SelectObject(hiPen);
  982.   dc.MoveTo(tbRect.left,    tbRect.bottom);
  983.   dc.LineTo(tbRect.left,    tbRect.top+2);
  984.   dc.LineTo(tbRect.left+2,  tbRect.top);
  985.   dc.LineTo(tbRect.right-1, tbRect.top);
  986.   dc.RestorePen();
  987.  
  988.   if (clip) {
  989.     // Draw a 'torn' edge
  990.     //
  991.     dc.SelectObject(drkPen);
  992.     int x;
  993.     for (int i = 1, y = tbRect.top; y < tbRect.bottom; y += 3, i++) {
  994.       x = ((i%4) - 2*((i%4)/3))-1;
  995.       dc.MoveTo(tbRect.right + x, y);
  996.       dc.LineTo(tbRect.right + x, min(y+3, int(tbRect.bottom)));
  997.     }
  998.     dc.RestorePen();
  999.   }
  1000.   else {
  1001.     dc.SelectObject(drkPen);
  1002.     dc.MoveTo(tbRect.right-1, tbRect.bottom);
  1003.     dc.LineTo(tbRect.right-1, tbRect.top);
  1004.     dc.RestorePen();
  1005.  
  1006.     dc.SelectObject(txtPen);
  1007.     dc.MoveTo(tbRect.right, tbRect.bottom);
  1008.     dc.LineTo(tbRect.right, tbRect.top+1);
  1009.     dc.RestorePen();
  1010.   }
  1011.  
  1012.   // Display the label
  1013.   //
  1014.   TRect txtRect = tbRect;
  1015.   txtRect.left += HorzPad;      
  1016.   dc.DrawText(txt, -1, txtRect, DT_LEFT|DT_SINGLELINE|DT_VCENTER);
  1017. }
  1018.  
  1019. //
  1020. // Paint tab item's border
  1021. //
  1022. void
  1023. TTabControl::PaintTabBorder(TDC& dc, const TRect& tbClient, const TRect* selTab)
  1024. {
  1025.   // Grab a few pens
  1026.   //
  1027.   TPen hiPen(TColor::Sys3dHilight);
  1028.   TPen drkPen(TColor::Sys3dShadow);
  1029.  
  1030.   // Draw highlights
  1031.   //
  1032.   dc.SelectObject(hiPen);
  1033.   dc.MoveTo(tbClient.left,  tbClient.bottom);
  1034.   dc.LineTo(tbClient.left,  tbClient.top);
  1035.  
  1036.   // Skip the selected tab (if there's one)
  1037.   //
  1038.   if (selTab) {
  1039.     dc.LineTo(selTab->left,  tbClient.top);
  1040.     dc.MoveTo(selTab->right, tbClient.top);
  1041.   }
  1042.  
  1043.   dc.LineTo(tbClient.right, tbClient.top);
  1044.   dc.RestorePen();
  1045.  
  1046.   // Draw Shadow on top of tabs
  1047.   //
  1048.   dc.SelectObject(drkPen);
  1049.   dc.LineTo(tbClient.right, tbClient.bottom);
  1050.   dc.LineTo(tbClient.left, tbClient.bottom);
  1051.   dc.RestorePen();
  1052. }
  1053.  
  1054. //
  1055. // WM_PAINT handler... paints individual tab items
  1056. //
  1057. void
  1058. TTabControl::Paint(TDC& dc, bool /*erase*/, TRect& rect)
  1059. {
  1060.   // Retrieve client area of tab control
  1061.   //
  1062.   TRect tbClient;
  1063.   GetClientArea(tbClient);
  1064.  
  1065.   // Retrieve rectangle of selected tab
  1066.   //
  1067.   TRect selRect;
  1068.   if (GetSel() >= 0)
  1069.     GetSelTabArea(selRect);
  1070.  
  1071.   // Paint if there are any tab entries
  1072.   //
  1073.   if (TabList->GetCount()) {
  1074.  
  1075.     // Setup the DC for text output
  1076.     //
  1077.     dc.SelectObject(*NormalFont);
  1078.     dc.SetBkMode(TRANSPARENT);
  1079.  
  1080.     // Iterate through the tabs
  1081.     //
  1082.     for (int i=0; i<TabList->GetCount(); i++) {
  1083.       const TRect& tbRect = (*TabList)[i].Rect;
  1084.  
  1085.       // Only paint if necessary
  1086.       //
  1087.       if (RectsMeet(rect, tbRect)) {
  1088.         const TTabEntryInternal& tab= (*TabList)[i];
  1089.  
  1090.         // Skip the selection: it's drawn last
  1091.         //
  1092.         if (i == GetSel())
  1093.           continue;
  1094.  
  1095.         PaintTab(dc, tbRect, tab.pszText, IsClipped(i));
  1096.       }
  1097.     }
  1098.     dc.RestoreObjects();
  1099.  
  1100.  
  1101.     // Handle the Selected Tab
  1102.     //
  1103.     if (GetSel() >= 0) {
  1104.  
  1105.       // Draw only if necessary
  1106.       //
  1107.       if (RectsMeet(rect, selRect)) {
  1108.  
  1109.         // Check if it's clipped
  1110.         //
  1111.         bool selIsClipped = IsClipped(GetSel());
  1112.  
  1113.         // Pick bold font and paint it
  1114.         //
  1115.         dc.SelectObject(*SelectFont);
  1116.         const TTabEntryInternal& tab= (*TabList)[GetSel()];
  1117.         PaintTab(dc, selRect, tab.pszText, selIsClipped);
  1118.  
  1119.         // Draw Focus rect
  1120.         //
  1121.         if (GetFocus() == *this)
  1122.           DrawFocusRect(dc, true);
  1123.  
  1124.         dc.RestoreObjects();
  1125.       }
  1126.     }
  1127.   }
  1128.  
  1129.   // Draw a border around the client area
  1130.   //
  1131.   if (GetSel() >= 0)
  1132.     PaintTabBorder(dc, tbClient, &selRect);
  1133.   else
  1134.     PaintTabBorder(dc, tbClient);
  1135. }
  1136.  
  1137.  
  1138. //
  1139. // Draws a focus rectangle around the selected tab entry.
  1140. //
  1141. void
  1142. TTabControl::DrawFocusRect(TDC& dc, bool /*inFocus*/)
  1143. {
  1144.   if (GetCount() > 0 && GetSel() >= 0) {
  1145.     TRect tbRect = (*TabList)[GetSel()].Rect;
  1146.     tbRect.Inflate(-HorzSelInc, -VertSelInc);
  1147.     dc.DrawFocusRect(tbRect);
  1148.   }
  1149. }
  1150.  
  1151. //
  1152. // Handles WM_SIZE by repositions individual tab items.
  1153. //
  1154. void
  1155. TTabControl::EvSize(uint sizeType, TSize& size)
  1156. {
  1157.   if (TCommCtrl::IsAvailable()) {
  1158.     DefaultProcessing();
  1159.   }
  1160.   else {
  1161.     TControl::EvSize(sizeType, size);
  1162.  
  1163.     // Layout tabs, if we've got any
  1164.     //
  1165.     if (sizeType != SIZE_MAXHIDE && sizeType != SIZE_MINIMIZED) 
  1166.       if (GetCount())
  1167.         SetTabRects(FirstVisibleTab);
  1168.   }
  1169. }
  1170.  
  1171. //
  1172. // Handles WM_LBUTTONDOWN: Check for tab selection...
  1173. //
  1174. void
  1175. TTabControl::EvLButtonDown(uint /*modKeys*/, TPoint& point)
  1176. {
  1177.   if (TCommCtrl::IsAvailable()) {
  1178.     DefaultProcessing();
  1179.   }
  1180.   else {
  1181.  
  1182.     // Select the tab hit - if any
  1183.     //
  1184.     int hitIndex = TabFromPoint(point);
  1185.     if (hitIndex != -1) 
  1186.       if (hitIndex != GetSel()) 
  1187.         NotifyAndSelect(hitIndex, true);
  1188.  
  1189.     // Grab focus
  1190.     //
  1191.     if (!(GetStyle() & TCS_FOCUSNEVER))
  1192.       SetFocus();
  1193.   }
  1194. }
  1195.  
  1196. //
  1197. // Handles WM_SETFOCUS: Draw focus to identify selected tab
  1198. //
  1199. void
  1200. TTabControl::EvSetFocus(THandle /*hWndLostFocus*/)
  1201. {
  1202.   if (TCommCtrl::IsAvailable()) {
  1203.     DefaultProcessing();
  1204.   }
  1205.   else {
  1206.     DrawFocusRect(TClientDC(*this), true);
  1207.   }
  1208. }
  1209.  
  1210. //
  1211. // Handles WM_KILLFOCUS: Remove focus around selected tab
  1212. //
  1213. void
  1214. TTabControl::EvKillFocus(THandle hWndGetFocus)
  1215. {
  1216.   TControl::EvKillFocus(hWndGetFocus);
  1217.  
  1218.   if (!TCommCtrl::IsAvailable()) {
  1219.     DrawFocusRect(TClientDC(*this), false);
  1220.   }
  1221. }
  1222.  
  1223. //
  1224. // Handles WM_GETDLGCODE: Request arrow keys
  1225. //
  1226. uint
  1227. TTabControl::EvGetDlgCode(MSG far* msg)
  1228. {
  1229.   if (TCommCtrl::IsAvailable()) {
  1230.     return TControl::EvGetDlgCode(msg);
  1231.   }
  1232.   else {
  1233.     return TControl::EvGetDlgCode(msg) | DLGC_WANTARROWS;
  1234.   }
  1235. }
  1236.  
  1237. //
  1238. // Handles WM_KEYDOWN: Allow arrow keys to move through tab items
  1239. //
  1240. void
  1241. TTabControl::EvKeyDown(uint key, uint /*repeatCount*/, uint flags)
  1242. {
  1243.   if (TCommCtrl::IsAvailable()) {
  1244.     DefaultProcessing();
  1245.   }
  1246.   else {
  1247.     TTabKeyDown tbKydn(*this, Attr.Id, TCN_KEYDOWN, uint16(key), flags);
  1248.     SendNotification(::GetParent(*this), Attr.Id, tbKydn);
  1249.  
  1250.     // Select next or previous tab on Right and Left arrow respectively
  1251.     //
  1252.     if (key == VK_RIGHT) {
  1253.       if (GetSel() < GetCount()-1)
  1254.         NotifyAndSelect(GetSel()+1, true);
  1255.     }
  1256.     else if (key == VK_LEFT) {
  1257.       if (GetSel() > 0)
  1258.         NotifyAndSelect(GetSel()-1, true);
  1259.     }
  1260.   }
  1261. }
  1262.  
  1263. //
  1264. // Assigns a size to a tab. If both width or height are zero, the size is
  1265. // determined from the size of the text and/or image of the tab item.
  1266. //
  1267. void
  1268. TTabControl::SetTabSize(int index, int width, int height)
  1269. {
  1270.   TTabEntryInternal& tab = (*TabList)[index];
  1271.  
  1272.   // Store height of tab
  1273.   //
  1274.   tab.Size.cy = height ? height : TabSize.cy + VertPad*2;
  1275.  
  1276.   // Store width of tab
  1277.   //
  1278.   if (width)
  1279.     tab.Size.cx = width;
  1280.   else {
  1281.     if ((GetStyle() & TCS_FIXEDWIDTH) && TabSize.cx != 0) 
  1282.       tab.Size.cx = TabSize.cx;      
  1283.     else {
  1284.  
  1285.       // Retrieve width and height of text
  1286.       //
  1287.       if (tab.pszText) {
  1288.  
  1289.         // Use screen DC & selected font
  1290.         //
  1291.         TScreenDC dc;
  1292.         dc.SelectObject(*SelectFont);
  1293.  
  1294.         // Compute size of label
  1295.         //
  1296.         TSize txtSize;
  1297.         dc.GetTextExtent(tab.pszText, strlen(tab.pszText), txtSize);
  1298.         dc.RestoreFont();
  1299.  
  1300.         tab.Size.cx = width ? width : txtSize.cx + HorzPad*2;
  1301.       }
  1302.     }
  1303.   }
  1304. }
  1305.  
  1306. //
  1307. // Invalidates the rectangle occupied by the tab at the specified index
  1308. //
  1309. void
  1310. TTabControl::InvalidateTabRect(int index)
  1311. {
  1312.   PRECONDITION(IsVisible(index));
  1313.   TRect tbRect = (*TabList)[index].Rect;
  1314.   if (index == GetSel()) {
  1315.     AdjustSelRect(tbRect);
  1316.  
  1317.     // Lower bottom to allow upper border of client area to 
  1318.     // be redrawn...
  1319.     //
  1320.     tbRect.bottom++;
  1321.   }
  1322.   // Extend right end to catch dark shadow
  1323.   //
  1324.   tbRect.right++;
  1325.  
  1326.   // Extend further to cover torn edge
  1327.   //
  1328.   if (IsClipped(index))
  1329.     tbRect.right++;
  1330.  
  1331.   // Invalidate
  1332.   //
  1333.   InvalidateRect(tbRect, true);
  1334. }
  1335.  
  1336. //
  1337. // Accessor for index of the first visible tab item
  1338. //
  1339. int
  1340. TTabControl::GetFirstVisibleTab() const
  1341. {
  1342.   return FirstVisibleTab;
  1343. }
  1344.  
  1345. //
  1346. // Sets the index of the first visible tab
  1347. //
  1348. void
  1349. TTabControl::SetFirstVisibleTab(int index)
  1350. {
  1351.   PRECONDITION(GetCount());
  1352.   PRECONDITION(index < GetCount());
  1353.   PRECONDITION(index >= 0);
  1354.  
  1355.   if (index != FirstVisibleTab) {
  1356.     FirstVisibleTab = index;
  1357.     SetTabRects(FirstVisibleTab);
  1358.  
  1359.     // Update scoller's range
  1360.     //
  1361.     UpdateScrollRange();
  1362.   }
  1363. }
  1364.  
  1365. //
  1366. // Updates the rectangle occupied by the tab item at the position
  1367. // identified by the 'index' parameter.
  1368. //
  1369. void
  1370. TTabControl::SetTabRect(int index, TRect* newRect)
  1371. {
  1372.   TRect& tbRect = ((*TabList)[index]).Rect;
  1373.   if (!tbRect.IsNull())
  1374.     InvalidateTabRect(index);
  1375.  
  1376.   if (!newRect)
  1377.     tbRect.SetNull();
  1378.   else {
  1379.     tbRect = *newRect;
  1380.     if (!tbRect.IsNull())
  1381.       InvalidateTabRect(index);
  1382.   }
  1383. }
  1384.  
  1385. //
  1386. // Updates the size/locations of tab items.
  1387. //
  1388. void
  1389. TTabControl::SetTabRects(int firstTab)
  1390. {
  1391.   // First hide the tabs preceeding the first one
  1392.   //
  1393.   for (int i=0; i<firstTab; i++)
  1394.     SetTabRect(i, 0);
  1395.  
  1396.   // Layout each item's location
  1397.   //
  1398.   SetTabRectsSingle(firstTab);
  1399. }
  1400.  
  1401. //
  1402. // Updates the locations/size of tabs in a single row..
  1403. //
  1404. void
  1405. TTabControl::SetTabRectsSingle(int firstTab)
  1406. {
  1407.   // Retrieve the area where tabs can hang out
  1408.   //
  1409.   TRect tabArea;
  1410.   GetTabsArea(tabArea);
  1411.  
  1412.   // Loop vars to keep track of upper left corner assignments
  1413.   //
  1414.   int x = tabArea.left;
  1415.   int y = tabArea.top;
  1416.  
  1417.   TRect newRect;
  1418.  
  1419.   // Assume scrollers are off unless we have tabs off to the left
  1420.   //
  1421.   int index;
  1422.   int count = GetCount();
  1423.   bool showScroller = firstTab ? true : false;
  1424.  
  1425.   // Check if the width of tabs exceeds the width of the control -
  1426.   // which would imply that we need scrollers
  1427.   //
  1428.   if (!showScroller) {
  1429.     for (index=firstTab; index<count; index++) {
  1430.       TTabEntryInternal& tab= (*TabList)[index];
  1431.       x += (tab.Size.cx + Padding.cx);
  1432.       if (x > tabArea.right) {
  1433.         showScroller = true;
  1434.         break;
  1435.       }
  1436.     }
  1437.   }
  1438.  
  1439.   // If we'll need scrollers, shrink the tab-area to accomodate
  1440.   //
  1441.   TRect scrollRect(0, 0, 0, 0);    
  1442.   if (showScroller) {
  1443.     GetScrollerArea(scrollRect);
  1444.     tabArea.right = scrollRect.left-HorzMargin;
  1445.   }
  1446.  
  1447.   // Iterate through tab, positioning each one
  1448.   //
  1449.   x = tabArea.left;
  1450.   for (index=firstTab; index<count; index++) {
  1451.     TTabEntryInternal& tab= (*TabList)[index];
  1452.     TRect& tbRect = tab.Rect;
  1453.  
  1454.     // Allocate space within the current row using
  1455.     // the tab's desired width
  1456.     //
  1457.     int width = tab.Size.cx;
  1458.     int height= tab.Size.cy;
  1459.  
  1460.     // Each tab's individual height should match the global TabSize
  1461.     //
  1462.     CHECK(height == TabSize.cy + VertPad*2);
  1463.  
  1464.     // Break if we're going off the viewport to the right
  1465.     //
  1466.     if (x >= tabArea.right) 
  1467.       break;
  1468.  
  1469.     // Compute new rectangle
  1470.     //
  1471.     newRect.Set(x, y, min(x+width, int(tabArea.right-HorzMargin)), y+height);
  1472.  
  1473.     // Update tab's rectangle
  1474.     //
  1475.     if (tbRect != newRect)
  1476.       SetTabRect(index, &newRect);
  1477.  
  1478.     // Set left side of next tab using some padding
  1479.     //
  1480.     x += (width + Padding.cx);
  1481.   }
  1482.  
  1483.   // Make any remaining tabs to the right invisible
  1484.   //
  1485.   while(index < count)
  1486.     SetTabRect(index++, 0);
  1487.  
  1488.   // Show/Hide the scrollers & update the scroll range/pos
  1489.   //
  1490.   Updown->SetWindowPos(HWND_TOP, scrollRect,
  1491.                        showScroller ? SWP_NOACTIVATE|SWP_SHOWWINDOW :
  1492.                                       SWP_HIDEWINDOW);
  1493.   if (showScroller)
  1494.     UpdateScrollRange();
  1495. }
  1496.  
  1497.  
  1498.  
  1499. //
  1500. // Select a tab and send the appropriate notifications
  1501. //
  1502. void        
  1503. TTabControl::NotifyAndSelect(int index, bool bringInView)
  1504. {
  1505.   PRECONDITION(index >= 0);
  1506.   PRECONDITION(index < GetCount());
  1507.  
  1508.   // First notify that we're about to change selection
  1509.   //
  1510.   TNotify not(*this, Attr.Id, TCN_SELCHANGING);
  1511.   if (!SendNotification(::GetParent(*this), Attr.Id, not)) {
  1512.  
  1513.     // If notification was not vetoed, proceed...
  1514.     //
  1515.     SetSel(index);
  1516.  
  1517.     // Notify of selection change
  1518.     //
  1519.     not.code = TCN_SELCHANGE;
  1520.     SendNotification(::GetParent(*this), Attr.Id, not);
  1521.   }
  1522.  
  1523.   // Bring tab into view if necessary
  1524.   //
  1525.   if (bringInView) {
  1526.     if (!IsVisible(index) || IsClipped(index)) {
  1527.  
  1528.       // If index it hidden on the left side, make it the first tab
  1529.       //
  1530.       if (index < GetFirstVisibleTab())
  1531.         SetFirstVisibleTab(index);
  1532.       else {
  1533.  
  1534.         // If the selected index is too much to the right, scroll
  1535.         // things left until it comes in view
  1536.         //
  1537.         while ((index > GetFirstVisibleTab()) && 
  1538.                (!IsVisible(index) || IsClipped(index)))
  1539.           SetFirstVisibleTab(GetFirstVisibleTab()+1);          
  1540.       }
  1541.     }
  1542.   }
  1543. }
  1544.  
  1545. //
  1546. // Responds to requests to scroll tabs
  1547. //
  1548. void
  1549. TTabControl::UpdateScrollRange()
  1550. {
  1551.   int left = 0;
  1552.   int cnt = GetCount();
  1553.  
  1554.   // Find index of leftmost item
  1555.   // NOTE: Leftmost item is never clipped on the left side
  1556.   //
  1557.   while (left < cnt && !IsVisible(left))
  1558.     left++;
  1559.  
  1560.   // Find how many items not fully are hanging on the right
  1561.   //      
  1562.   int noShowRight = 0;
  1563.   for (int i = cnt-1; i >=0; i--)
  1564.     if (!IsVisible(i) || IsClipped(i))
  1565.       noShowRight++;
  1566.    
  1567.   // Now we want the scroller to allow us to go left 'left' times and go
  1568.   // right 'noShowRight' times.
  1569.   //      
  1570.   Updown->SetRange(0, noShowRight);
  1571.   Updown->SetPos(left);
  1572. }
  1573.  
  1574. //
  1575. // Update the Bold and Normal font used by the
  1576. // TabControl when displaying Tab labels
  1577. //
  1578. void
  1579. TTabControl::SetupFont(HFONT font)
  1580. {
  1581.   if (font == 0) {
  1582.     // Try to get our parent's font
  1583.     //
  1584.     font = Parent->GetWindowFont();
  1585.  
  1586.     // Use the default UI font
  1587.     //
  1588.     if (!font) {
  1589.       LOGFONT lf;
  1590.       TDefaultGUIFont().GetObject(lf);
  1591.       font = CreateFontIndirect(&lf);
  1592.     }
  1593.   }
  1594.  
  1595.   // Create Normal and Bold versions of the font for the
  1596.   // TabControl's use.
  1597.   //
  1598.   if (font) {
  1599.     LOGFONT lf;
  1600.     if (::GetObject(font, sizeof lf, &lf)) {
  1601.  
  1602.       lf.lfWeight = FW_NORMAL;
  1603.       SetupNormalFont(new TFont(&lf));
  1604.  
  1605.       lf.lfWeight = FW_BOLD;
  1606.       SetupSelectFont(new TFont(&lf));
  1607.     }
  1608.  
  1609.     // Update tab height as it must a least match the font's height
  1610.     //
  1611.     CHECK(SelectFont);
  1612.     TScreenDC dc;
  1613.     dc.SelectObject(*SelectFont);
  1614.     static char smplTxt[] = "M|#_^";
  1615.     dc.GetTextExtent(smplTxt, sizeof smplTxt, TabSize);
  1616.     dc.RestoreFont();
  1617.   }
  1618. }
  1619.  
  1620. //
  1621. // Updates the font used for non-selected tabs
  1622. //
  1623. void        
  1624. TTabControl::SetupNormalFont(TFont* normalFont)
  1625. {
  1626.   PRECONDITION(!normalFont || normalFont->IsGDIObject());
  1627.   delete NormalFont;
  1628.   NormalFont = normalFont;
  1629. }
  1630.  
  1631. //
  1632. // Updates the font used for the selected tab
  1633. //
  1634. void        
  1635. TTabControl::SetupSelectFont(TFont* selectFont)
  1636. {
  1637.   PRECONDITION(!selectFont || selectFont->IsGDIObject());
  1638.   delete SelectFont;
  1639.   SelectFont = selectFont;
  1640. }
  1641.  
  1642. //
  1643. // Compute the index of the tab at the specified location.
  1644. // Returns -1 if unsuccessful.
  1645. //
  1646. int
  1647. TTabControl::TabFromPoint(const TPoint& pt) const
  1648. {
  1649.   for (int i=0; i<TabList->GetCount(); i++) {
  1650.     TRect& tbRect = (*TabList)[i].Rect;
  1651.     if (tbRect.Contains(pt))
  1652.       return i;
  1653.   }
  1654.   return -1;
  1655. }
  1656.  
  1657. //
  1658. // Retrieves the desired location of the scrollers within the tab
  1659. // control - Assumes we're in 'SingleRow' mode.
  1660. // Must *NOT* be called when there are not tabs in the tab controls
  1661. //
  1662. void
  1663. TTabControl::GetScrollerArea(TRect& rect)
  1664. {
  1665.   PRECONDITION(GetCount() > 0);
  1666.   PRECONDITION(!(GetStyle() & TCS_MULTILINE));
  1667.  
  1668.   // Retrieve area tabs hang in
  1669.   //
  1670.   GetTabsArea(rect);
  1671.  
  1672.   // Shrink area to almost match the tabs height with a 
  1673.   // width twice as big
  1674.   //
  1675.   rect.Inflate(0, -1);
  1676.   rect.left = rect.right - rect.Height()*2;
  1677. }
  1678.  
  1679. //
  1680. // Retrieves the area of the selected tab item.
  1681. //
  1682. void
  1683. TTabControl::GetSelTabArea(TRect& rect)
  1684. {
  1685.   PRECONDITION(GetSel() >= 0);
  1686.   PRECONDITION(GetSel() < GetCount());
  1687.  
  1688.   rect = (*TabList)[GetSel()].Rect;
  1689.   AdjustSelRect(rect);
  1690. }
  1691.  
  1692. //
  1693. // Retrieves the rectangle within which tabs lie. If there are no tabs in
  1694. // the control, then only the left, top and right sides are valid as the
  1695. // bottom requires tabs to be computed
  1696. //
  1697. void
  1698. TTabControl::GetTabsArea(TRect& rect)
  1699. {
  1700.   // First retrieve left, top and right borders
  1701.   //
  1702.   GetClientRect(rect);
  1703.   rect.Inflate(-(HorzMargin+HorzSelInc), -VertMargin);
  1704.  
  1705.   // Adjust bottom if there are tabs in the control
  1706.   //
  1707.   if (GetCount() > 0)
  1708.     rect.bottom = rect.top + 
  1709.                  (GetRowCount()*(TabSize.cy + VertPad*2 + Padding.cy));
  1710. }
  1711.  
  1712. //
  1713. // Retrieves the rectangle outside the area of tabs
  1714. //
  1715. // ClientArea = ClientRect with following adjustments
  1716. //  (a) left  += HorMargin;
  1717. //  (b) right -= HorMargin;
  1718. //  (c) top   += (GetRowCount()*(TabSize.cy + VertPad*2 + Padding.cy))-1
  1719. //  (d) bottom-= VertMargin;
  1720. //
  1721. // The Client area described above is the outer client area - i.e. the
  1722. // area around which we draw a raised rectangle. This rectangle is
  1723. // further shrunk by 'ClientMargin' on all four sizes when computing
  1724. // the inner rectangle, the area where a page would be displayed 
  1725. // for example.
  1726. //
  1727. void
  1728. TTabControl::GetClientArea(TRect& rect, bool innerRect)
  1729. {
  1730.   // Retrieve client area and adjust left, right and bottom sides
  1731.   //
  1732.   GetClientRect(rect);
  1733.   rect.Inflate(-HorzMargin, -VertMargin);
  1734.  
  1735.   // If there are tabs, adjust the top accordingly
  1736.   //
  1737.   if (GetCount() > 0) {
  1738.  
  1739.     // Adjust top to skip row(s) of tabs
  1740.     //
  1741.     rect.top += (GetRowCount()*(TabSize.cy + VertPad*2 + Padding.cy))-1;
  1742.     if (innerRect) 
  1743.       rect.Inflate(-ClientMargin, -ClientMargin);
  1744.   }
  1745. }
  1746.  
  1747. //
  1748. // Indicates whether the tab at the specified position is visible.
  1749. //
  1750. bool
  1751. TTabControl::IsVisible(int index)
  1752. {
  1753.   PRECONDITION(index < GetCount());
  1754.   TTabEntryInternal& tab= (*TabList)[index];
  1755.   return tab.Rect.IsNull() ? false : true;
  1756. }
  1757.  
  1758. //
  1759. // Indicates whether the tab the the specified position is clipped.
  1760. // NOTE: Does not check if the tab is visible...
  1761. //       Will be inaccurate for hidden tab items!
  1762. //
  1763. bool
  1764. TTabControl::IsClipped(int index)
  1765. {
  1766.   PRECONDITION(index < GetCount());
  1767.   TTabEntryInternal& tab= (*TabList)[index];
  1768.   return tab.Size.cx > tab.Rect.Width();
  1769. }
  1770.  
  1771.  
  1772. #endif  // !OWL_NATIVECTRL_ALWAYS
  1773.