Torque3D Documentation / _generateds / guiTabBookCtrl.cpp

guiTabBookCtrl.cpp

Engine/source/gui/containers/guiTabBookCtrl.cpp

More...

Public Functions

ConsoleDocClass(GuiTabBookCtrl , "@brief A container \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Create \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@note Only GuiTabPageCtrls must be added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> GuiTabBookCtrls. If an object of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different " "class is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the control, it will be reassigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> either the active page or the " "tab book 's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">parent.\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiTabPageCtrl\n</a>" " @ingroup GuiContainers" )
DefineEngineMethod(GuiTabBookCtrl , addPage , void , (const char *title) , ("") , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param title Title text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab page header." )
DefineEngineMethod(GuiTabBookCtrl , getSelectedPage , S32 , () , "Get the index of the currently selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@return Index of the selected tab page or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no tab page is selected." )
DefineEngineMethod(GuiTabBookCtrl , selectPage , void , (S32 index) , "Set the selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@param index Index of the tab page." )
IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabRightClick , void , (const String &text, U32 index) , (text, index) , "Called when the user right-clicks on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tab page <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">header.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )
IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabSelected , void , (const String &text, U32 index) , (text, index) , "Called when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )
ImplementEnumType(GuiTabPosition , "Where the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> should put the tab headers <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> selecting individual <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pages.\n\n</a>" "@ingroup GuiContainers" )

Detailed Description

Public Functions

ConsoleDocClass(GuiTabBookCtrl , "@brief A container \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n\n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "// Create \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@note Only GuiTabPageCtrls must be added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> GuiTabBookCtrls. If an object of <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> different " "class is added <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the control, it will be reassigned <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> either the active page or the " "tab book 's <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">parent.\n\n</a>" " @see <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">GuiTabPageCtrl\n</a>" " @ingroup GuiContainers" )

DefineEngineMethod(GuiTabBookCtrl , addPage , void , (const char *title) , ("") , "Add <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">control.\n\n</a>" "@param title Title text <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab page header." )

DefineEngineMethod(GuiTabBookCtrl , getSelectedPage , S32 , () , "Get the index of the currently selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@return Index of the selected tab page or -1 <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> no tab page is selected." )

DefineEngineMethod(GuiTabBookCtrl , selectPage , void , (S32 index) , "Set the selected tab <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">page.\n\n</a>" "@param index Index of the tab page." )

IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabRightClick , void , (const String &text, U32 index) , (text, index) , "Called when the user right-clicks on <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> tab page <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">header.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )

IMPLEMENT_CALLBACK(GuiTabBookCtrl , onTabSelected , void , (const String &text, U32 index) , (text, index) , "Called when <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> tab page is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n\n</a>" "@param text Text of the page header <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> the tab that is being <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">selected.\n</a>" "@param index Index of the tab page being selected." )

IMPLEMENT_CONOBJECT(GuiTabBookCtrl )

ImplementEnumType(GuiTabPosition , "Where the <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> should put the tab headers <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> selecting individual <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">pages.\n\n</a>" "@ingroup GuiContainers" )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "gui/containers/guiTabBookCtrl.h"
 25#include "console/engineAPI.h"
 26#include "gui/core/guiCanvas.h"
 27#include "gui/editor/guiEditCtrl.h"
 28#include "gui/controls/guiPopUpCtrl.h"
 29#include "gui/core/guiDefaultControlRender.h"
 30#include "gfx/gfxDrawUtil.h"
 31
 32
 33IMPLEMENT_CONOBJECT( GuiTabBookCtrl );
 34
 35ConsoleDocClass( GuiTabBookCtrl,
 36   "@brief A container \n\n"
 37   
 38   "@tsexample\n"
 39   "// Create \n"
 40   "@endtsexample\n\n"
 41   
 42   "@note Only GuiTabPageCtrls must be added to GuiTabBookCtrls.  If an object of a different "
 43      "class is added to the control, it will be reassigned to either the active page or the "
 44      "tab book's parent.\n\n"
 45      
 46   "@see GuiTabPageCtrl\n"
 47   
 48   "@ingroup GuiContainers"
 49);
 50
 51ImplementEnumType( GuiTabPosition,
 52   "Where the control should put the tab headers for selecting individual pages.\n\n"
 53   "@ingroup GuiContainers" )
 54   { GuiTabBookCtrl::AlignTop,   "Top",      "Tab headers on top edge." },
 55   { GuiTabBookCtrl::AlignBottom,"Bottom",   "Tab headers on bottom edge." }
 56EndImplementEnumType;
 57
 58IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabSelected, void, ( const String& text, U32 index ), ( text, index ),
 59   "Called when a new tab page is selected.\n\n"
 60   "@param text Text of the page header for the tab that is being selected.\n"
 61   "@param index Index of the tab page being selected." );
 62IMPLEMENT_CALLBACK( GuiTabBookCtrl, onTabRightClick, void, ( const String& text, U32 index ), ( text, index ),
 63   "Called when the user right-clicks on a tab page header.\n\n"
 64   "@param text Text of the page header for the tab that is being selected.\n"
 65   "@param index Index of the tab page being selected." );
 66
 67
 68//-----------------------------------------------------------------------------
 69
 70GuiTabBookCtrl::GuiTabBookCtrl()
 71{
 72   VECTOR_SET_ASSOCIATION( mPages );
 73   
 74   mTabHeight = 24;
 75   mTabPosition = AlignTop;
 76   mActivePage = NULL;
 77   mHoverTab = NULL;
 78   mHasTexture = false;
 79   mBitmapBounds = NULL;
 80   setExtent( 400, 300 );
 81   mPageRect = RectI(0,0,0,0);
 82   mTabRect = RectI(0,0,0,0);
 83   mFrontTabPadding = 0;
 84
 85   mPages.reserve(12);
 86   mTabMargin = 7;
 87   mMinTabWidth = 64;
 88   mIsContainer = true;
 89   mSelectedPageNum = -1;
 90   mDefaultPageNum = -1;
 91
 92   mAllowReorder = false;
 93   mDraggingTab  = false;
 94   mDraggingTabRect = false;
 95   mIsFirstWake = true;
 96}
 97
 98//-----------------------------------------------------------------------------
 99
100void GuiTabBookCtrl::initPersistFields()
101{
102   addGroup( "TabBook" );
103   
104      addField( "tabPosition",     TYPEID< TabPosition >(), Offset( mTabPosition,    GuiTabBookCtrl ),
105         "Where to place the tab page headers." );
106      addField( "tabMargin",       TypeS32,  Offset( mTabMargin,      GuiTabBookCtrl ),
107         "Spacing to put between individual tab page headers." );
108      addField( "minTabWidth",     TypeS32,  Offset( mMinTabWidth,    GuiTabBookCtrl ),
109         "Minimum width allocated to a tab page header." );
110      addField( "tabHeight",       TypeS32,  Offset( mTabHeight,      GuiTabBookCtrl ),
111         "Height of tab page headers." );
112      addField( "allowReorder",    TypeBool, Offset( mAllowReorder,   GuiTabBookCtrl ),
113         "Whether reordering tabs with the mouse is allowed." );
114      addField( "defaultPage",     TypeS32,  Offset( mDefaultPageNum, GuiTabBookCtrl ),
115         "Index of page to select on first onWake() call (-1 to disable)." );
116
117      addProtectedField( "selectedPage", TypeS32, Offset( mSelectedPageNum, GuiTabBookCtrl ),
118         &_setSelectedPage, &defaultProtectedGetFn,
119         "Index of currently selected page." );
120
121      addField( "frontTabPadding", TypeS32, Offset( mFrontTabPadding, GuiTabBookCtrl ),
122         "X offset of first tab page header." );
123
124   endGroup( "TabBook" );
125
126   Parent::initPersistFields();
127}
128
129//-----------------------------------------------------------------------------
130
131void GuiTabBookCtrl::onChildRemoved( GuiControl* child )
132{
133   for (S32 i = 0; i < mPages.size(); i++ )
134   {
135      GuiTabPageCtrl* tab = mPages[i].Page;
136      if( tab == child )
137      {
138         mPages.erase( i );
139         break;
140      }
141   }
142
143   // Calculate Page Information
144   calculatePageTabs();
145
146   // Active Index.
147   mSelectedPageNum = getMin( mSelectedPageNum, mPages.size() - 1 );
148
149   if ( mSelectedPageNum != -1 )
150   {
151       // Select Page.
152       selectPage( mSelectedPageNum );
153   }
154}
155
156//-----------------------------------------------------------------------------
157
158void GuiTabBookCtrl::onChildAdded( GuiControl *child )
159{
160   GuiTabPageCtrl *page = dynamic_cast<GuiTabPageCtrl*>(child);
161   if( !page )
162   {
163      Con::warnf("GuiTabBookCtrl::onChildAdded - attempting to add NON GuiTabPageCtrl as child page");
164      SimObject *simObj = reinterpret_cast<SimObject*>(child);
165      removeObject( simObj );
166      if( mActivePage )
167      {
168         mActivePage->addObject( simObj );
169      }
170      else
171      {
172         Con::warnf("GuiTabBookCtrl::onChildAdded - unable to find active page to reassign ownership of new child control to, placing on parent");
173         GuiControl *rent = getParent();
174         if( rent )
175            rent->addObject( simObj );
176      }
177      return;
178   }
179
180   TabHeaderInfo newPage;
181
182   newPage.Page      = page;
183   newPage.TabRow    = -1;
184   newPage.TabColumn = -1;
185
186   mPages.push_back( newPage );
187
188   // Calculate Page Information
189   calculatePageTabs();
190
191   if( page->getFitBook() )
192      fitPage( page );
193
194   // Select this Page
195   selectPage( page );
196}
197
198//-----------------------------------------------------------------------------
199
200bool GuiTabBookCtrl::reOrder(SimObject* obj, SimObject* target)
201{
202   if ( !Parent::reOrder(obj, target) )
203      return false;
204
205   // Store the Selected Page.
206   GuiTabPageCtrl *selectedPage = NULL;
207   if ( mSelectedPageNum != -1 )
208      selectedPage = mPages[mSelectedPageNum].Page;
209
210   // Determine the Target Page Index.
211   S32 targetIndex = -1;
212   for( S32 i = 0; i < mPages.size(); i++ )
213   {
214      const TabHeaderInfo &info = mPages[i];
215      if ( info.Page == target )
216      {
217         targetIndex = i;
218         break;
219      }
220   }
221
222   if ( targetIndex == -1 )
223   {
224       return false;
225   }
226
227   for( S32 i = 0; i < mPages.size(); i++ )
228   {
229      const TabHeaderInfo &info = mPages[i];
230      if ( info.Page == obj )
231      {
232         // Store Info.
233         TabHeaderInfo objPage = info;
234
235         // Remove.
236         mPages.erase( i );
237         // Insert.
238         mPages.insert( targetIndex, objPage );
239
240         break;
241      }
242   }
243
244   // Update Tabs.
245   calculatePageTabs();
246
247   // Reselect Page.
248   selectPage( selectedPage );
249
250   return true;
251}
252
253//-----------------------------------------------------------------------------
254
255bool GuiTabBookCtrl::acceptsAsChild( SimObject* object ) const
256{
257   // Only accept tab pages.
258   return ( dynamic_cast< GuiTabPageCtrl* >( object ) != NULL );
259}
260
261//-----------------------------------------------------------------------------
262
263bool GuiTabBookCtrl::onWake()
264{
265   if (! Parent::onWake())
266      return false;
267
268   mHasTexture = mProfile->constructBitmapArray() > 0;
269   if( mHasTexture )
270   {
271      mBitmapBounds = mProfile->mBitmapArrayRects.address();
272      mTabHeight = mBitmapBounds[TabSelected].extent.y;
273   }
274
275   calculatePageTabs();
276   
277   if( mIsFirstWake )
278   {
279      // Awaken all pages, visible or not.  We need to do this so
280      // any pages that make use of a language table for their label
281      // are correctly initialized.
282      for ( U32 i = 0; i < mPages.size(); ++i)
283      {
284         if ( !mPages[i].Page->isAwake() )
285         {
286            mPages[i].Page->awaken();
287         }
288      }
289
290      if( mDefaultPageNum >= 0 && mDefaultPageNum < mPages.size() )
291         selectPage( mDefaultPageNum );
292
293      mIsFirstWake = false;
294   }
295
296   return true;
297}
298
299//-----------------------------------------------------------------------------
300
301void GuiTabBookCtrl::addNewPage( const char* text )
302{
303   GuiTabPageCtrl* page = new GuiTabPageCtrl();
304   
305   if( text )
306      page->setText( text );
307   
308   page->registerObject();
309   addObject( page );
310}
311
312//-----------------------------------------------------------------------------
313
314bool GuiTabBookCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
315{
316   bool result = Parent::resize( newPosition, newExtent );
317
318   calculatePageTabs();
319   
320   return result;
321}
322
323//-----------------------------------------------------------------------------
324
325void GuiTabBookCtrl::childResized(GuiControl *child)
326{
327   Parent::childResized( child );
328
329   //child->resize( mPageRect.point, mPageRect.extent );
330}
331
332//-----------------------------------------------------------------------------
333
334void GuiTabBookCtrl::onMouseDown(const GuiEvent &event)
335{
336   mDraggingTab = false;
337   mDraggingTabRect = false;
338   Point2I localMouse = globalToLocalCoord( event.mousePoint );
339   if( mTabRect.pointInRect( localMouse ) )
340   {
341      GuiTabPageCtrl *tab = findHitTab( localMouse );
342      if( tab != NULL )
343      {
344         selectPage( tab );
345         mDraggingTab = mAllowReorder;
346      }
347      else
348      {
349         mDraggingTabRect = true;
350      }
351   }
352}
353
354//-----------------------------------------------------------------------------
355
356void GuiTabBookCtrl::onMouseUp(const GuiEvent &event)
357{
358   Parent::onMouseUp( event );
359
360   mDraggingTab = false;
361   mDraggingTabRect = false;
362}
363
364//-----------------------------------------------------------------------------
365
366void GuiTabBookCtrl::onMouseDragged(const GuiEvent &event)
367{
368   Parent::onMouseDragged( event );
369
370   if ( !mDraggingTab )
371       return;
372
373   GuiTabPageCtrl *selectedPage = NULL;
374   if ( mSelectedPageNum != -1 )
375      selectedPage = mPages[mSelectedPageNum].Page;
376
377   if ( !selectedPage )
378       return;
379
380   Point2I localMouse = globalToLocalCoord( event.mousePoint );
381   if( mTabRect.pointInRect( localMouse ) )
382   {
383      GuiTabPageCtrl *tab = findHitTab( localMouse );
384      if( tab != NULL && tab != selectedPage )
385      {
386         S32 targetIndex = -1;
387         for( S32 i = 0; i < mPages.size(); i++ )
388         {
389            if( mPages[i].Page == tab )
390            {
391               targetIndex = i;
392               break;
393            }
394         }
395
396         if ( targetIndex > mSelectedPageNum )
397         {
398            reOrder( tab, selectedPage );
399         }
400         else
401         {
402            reOrder( selectedPage, tab );
403         }
404      }
405   }
406}
407
408//-----------------------------------------------------------------------------
409
410void GuiTabBookCtrl::onMouseMove(const GuiEvent &event)
411{
412   Point2I localMouse = globalToLocalCoord( event.mousePoint );
413   if( mTabRect.pointInRect( localMouse ) )
414   {
415      GuiTabPageCtrl *tab = findHitTab( localMouse );
416      if( tab != NULL && mHoverTab != tab )
417         mHoverTab = tab;
418      else if ( !tab )
419         mHoverTab = NULL;
420   }
421   Parent::onMouseMove( event );
422}
423
424//-----------------------------------------------------------------------------
425
426void GuiTabBookCtrl::onMouseLeave( const GuiEvent &event )
427{
428   Parent::onMouseLeave( event );
429   
430   mHoverTab = NULL;
431}
432
433//-----------------------------------------------------------------------------
434
435bool GuiTabBookCtrl::onMouseDownEditor(const GuiEvent &event, Point2I offset)
436{
437   bool handled = false;
438   Point2I localMouse = globalToLocalCoord( event.mousePoint );
439
440   if( mTabRect.pointInRect( localMouse ) )
441   {
442      GuiTabPageCtrl *tab = findHitTab( localMouse );
443      if( tab != NULL )
444      {
445         selectPage( tab );
446         handled = true;
447      }
448   }
449
450#ifdef TORQUE_TOOLS
451   // This shouldn't be called if it's not design time, but check just incase
452   if ( GuiControl::smDesignTime )
453   {
454      // If we clicked in the editor and our addset is the tab book
455      // ctrl, select the child ctrl so we can edit it's properties
456      GuiEditCtrl* edit = GuiControl::smEditorHandle;
457      if( edit  && ( edit->getAddSet() == this ) && mActivePage != NULL )
458         edit->select( mActivePage );
459   }
460#endif
461
462   // Return whether we handled this or not.
463   return handled;
464
465}
466
467//-----------------------------------------------------------------------------
468
469void GuiTabBookCtrl::onRightMouseUp( const GuiEvent& event )
470{
471   Point2I localMouse = globalToLocalCoord( event.mousePoint );
472   if( mTabRect.pointInRect( localMouse ) )
473   {
474      GuiTabPageCtrl* tab = findHitTab( localMouse );
475      if( tab )
476         onTabRightClick_callback( tab->getText(), getPageNum( tab ) );
477   }
478}
479
480//-----------------------------------------------------------------------------
481
482void GuiTabBookCtrl::onRender(Point2I offset, const RectI &updateRect)
483{
484   RectI tabRect = mTabRect;
485   tabRect.point += offset;
486   RectI pageRect = mPageRect;
487   pageRect.point += offset;
488
489   // We're so nice we'll store the old modulation before we clear it for our rendering! :)
490   ColorI oldModulation;
491   GFX->getDrawUtil()->getBitmapModulation( &oldModulation );
492
493   // Wipe it out
494   GFX->getDrawUtil()->clearBitmapModulation();
495
496   Parent::onRender(offset, updateRect);
497
498   // Clip to tab area
499   RectI savedClipRect = GFX->getClipRect();
500   RectI clippedTabRect = tabRect;
501   clippedTabRect.intersect( savedClipRect );
502   GFX->setClipRect( clippedTabRect );
503
504   // Render our tabs
505   renderTabs( offset, tabRect );
506
507   // Restore Rect.
508   GFX->setClipRect( savedClipRect );
509
510   // Restore old modulation
511   GFX->getDrawUtil()->setBitmapModulation( oldModulation );
512}
513
514//-----------------------------------------------------------------------------
515
516void GuiTabBookCtrl::renderTabs( const Point2I &offset, const RectI &tabRect )
517{
518   // If the tab size is zero, don't render tabs,
519   //  assuming it's a tab-less book
520   if( mPages.empty() || mTabHeight <= 0 )
521      return;
522
523   for( S32 i = 0; i < mPages.size(); i++ )
524   {
525      const TabHeaderInfo &currentTabInfo = mPages[i];
526      RectI tabBounds = mPages[i].TabRect;
527      tabBounds.point += offset;
528      GuiTabPageCtrl *tab = mPages[i].Page;
529      if( tab != NULL )
530         renderTab( tabBounds, tab );
531
532      // If we're on the last tab, draw the nice end piece
533      if( i + 1 == mPages.size() )
534      {
535         Point2I tabEndPoint = Point2I(currentTabInfo.TabRect.point.x + currentTabInfo.TabRect.extent.x + offset.x, currentTabInfo.TabRect.point.y + offset.y);
536         Point2I tabEndExtent = Point2I((tabRect.point.x + tabRect.extent.x) - tabEndPoint.x, currentTabInfo.TabRect.extent.y);
537         RectI tabEndRect = RectI(tabEndPoint,tabEndExtent);
538
539         GFX->setClipRect( tabEndRect );
540
541         // As it turns out the last tab can be outside the viewport in which
542         // case trying to render causes a DX assert. Could be better if 
543         // setClipRect returned a bool.
544         if ( GFX->getViewport().isValidRect() )
545            renderFixedBitmapBordersFilled( tabEndRect, TabEnds + 1, mProfile );
546      }
547   }
548}
549
550//-----------------------------------------------------------------------------
551
552void GuiTabBookCtrl::renderTab(const RectI& tabRect, GuiTabPageCtrl *tab)
553{
554   StringTableEntry text = tab->getText();
555   ColorI oldColor;
556
557   GFX->getDrawUtil()->getBitmapModulation( &oldColor );
558
559   // Is this a skinned control?
560   if( mHasTexture && mProfile->mBitmapArrayRects.size() >= 9 )
561   {
562      S32 indexMultiplier = 1;
563      switch( mTabPosition )
564      {
565      case AlignTop:
566      case AlignBottom:
567         
568         if ( mActivePage == tab )
569            indexMultiplier += TabSelected;
570         else if( mHoverTab == tab )
571            indexMultiplier += TabHover;
572         else
573            indexMultiplier += TabNormal;
574         break;
575      } 
576
577      renderFixedBitmapBordersFilled( tabRect, indexMultiplier, mProfile );
578   }
579   else
580   {
581      // If this isn't a skinned control or the bitmap is simply missing, handle it WELL
582      if ( mActivePage == tab )
583         GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColor);
584      else if( mHoverTab == tab )
585         GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorHL);
586      else
587         GFX->getDrawUtil()->drawRectFill(tabRect, mProfile->mFillColorNA);
588
589   }
590
591
592   GFX->getDrawUtil()->setBitmapModulation(mProfile->mFontColor);
593
594   switch( mTabPosition )
595   {
596   case AlignTop:
597   case AlignBottom:
598      renderJustifiedText( tabRect.point, tabRect.extent, text);
599   break;
600   }
601
602   GFX->getDrawUtil()->setBitmapModulation( oldColor);
603
604}
605
606//-----------------------------------------------------------------------------
607
608void GuiTabBookCtrl::setUpdate()
609{
610   Parent::setUpdate();
611
612   setUpdateRegion(Point2I(0,0), getExtent());
613
614   calculatePageTabs();
615}
616
617//-----------------------------------------------------------------------------
618
619S32 GuiTabBookCtrl::calculatePageTabWidth( GuiTabPageCtrl *page )
620{
621   if( !page )
622      return mMinTabWidth;
623
624   const char* text = page->getText();
625
626   if( !text || dStrlen(text) == 0 || mProfile == NULL || mProfile->mFont == NULL )
627      return mMinTabWidth;
628
629   GFont *font = mProfile->mFont;
630
631   return font->getStrNWidth( text, dStrlen(text) );
632
633}
634
635//-----------------------------------------------------------------------------
636
637const RectI GuiTabBookCtrl::getClientRect()
638{
639
640   if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps )
641      return Parent::getClientRect();
642
643   return mPageRect;
644}
645
646//-----------------------------------------------------------------------------
647
648void GuiTabBookCtrl::calculatePageTabs()
649{
650   // Short Circuit.
651   //
652   // If the tab size is zero, don't render tabs,
653   //  assuming it's a tab-less book
654   if( mPages.empty() || mTabHeight <= 0 )
655   {
656      mPageRect.point.x = 0;
657      mPageRect.point.y = 0;
658      mPageRect.extent.x = getWidth();
659      mPageRect.extent.y = getHeight();
660      return;
661   }
662
663   S32 currRow    = 0;
664   S32 currColumn = 0;
665   S32 currX      = mFrontTabPadding;
666   S32 maxWidth   = 0;
667
668   for( S32 i = 0; i < mPages.size(); i++ )
669   {
670      // Fetch Tab Width
671      S32 tabWidth = calculatePageTabWidth( mPages[i].Page ) + ( mTabMargin * 2 );
672      tabWidth = getMax( tabWidth, mMinTabWidth );
673      TabHeaderInfo &info = mPages[i];
674      switch( mTabPosition )
675      {
676      case AlignTop:
677      case AlignBottom:
678         // If we're going to go outside our bounds
679         // with this tab move it down a row
680         if( currX + tabWidth > getWidth() )
681         {
682            // Calculate and Advance State.
683            maxWidth = getMax( tabWidth, maxWidth );
684            balanceRow( currRow, currX );
685            info.TabRow = ++currRow;
686            // Reset Necessaries
687            info.TabColumn = currColumn = maxWidth = currX = 0;
688         }
689         else
690         {
691            info.TabRow = currRow;
692            info.TabColumn = currColumn++;
693         }
694
695         // Calculate Tabs Bounding Rect
696         info.TabRect.point.x  = currX;
697         info.TabRect.extent.x = tabWidth;
698         info.TabRect.extent.y = mTabHeight;
699
700         // Adjust Y Point based on alignment
701         if( mTabPosition == AlignTop )
702            info.TabRect.point.y  = ( info.TabRow * mTabHeight );
703         else 
704            info.TabRect.point.y  = getHeight() - ( ( 1 + info.TabRow ) * mTabHeight );
705
706         currX += tabWidth;
707         break;
708      };
709   }
710
711   currRow++;
712   currColumn++;
713
714   Point2I localPoint = getExtent();
715
716   // Calculate 
717   switch( mTabPosition )
718   {
719   case AlignTop:
720
721      localPoint.y -= getTop();
722
723      mTabRect.point.x = 0;
724      mTabRect.extent.x = localPoint.x;
725      mTabRect.point.y = 0;
726      mTabRect.extent.y = currRow * mTabHeight;
727
728      mPageRect.point.x = 0;
729      mPageRect.point.y = mTabRect.extent.y;
730      mPageRect.extent.x = mTabRect.extent.x;
731      mPageRect.extent.y = getHeight() - mTabRect.extent.y;
732
733      break;
734   case AlignBottom:
735      mTabRect.point.x = 0;
736      mTabRect.extent.x = localPoint.x;
737      mTabRect.extent.y = currRow * mTabHeight;
738      mTabRect.point.y = getHeight() - mTabRect.extent.y;
739
740      mPageRect.point.x = 0;
741      mPageRect.point.y = 0;
742      mPageRect.extent.x = mTabRect.extent.x;
743      mPageRect.extent.y = localPoint.y - mTabRect.extent.y;
744
745      break;
746   }
747}
748
749//-----------------------------------------------------------------------------
750
751void GuiTabBookCtrl::balanceRow( S32 row, S32 totalTabWidth )
752{
753   // Short Circuit.
754   //
755   // If the tab size is zero, don't render tabs,
756   //  and assume it's a tab-less tab-book - JDD
757   if( mPages.empty() || mTabHeight <= 0 )
758      return;
759
760   Vector<TabHeaderInfo*> rowTemp;
761   rowTemp.clear();
762
763   for( S32 i = 0; i < mPages.size(); i++ )
764   {
765      TabHeaderInfo &info = mPages[i];
766
767      if(info.TabRow == row )
768         rowTemp.push_back( &mPages[i] );
769   }
770
771   if( rowTemp.empty() )
772      return;
773
774   // Balance the tabs across the remaining space
775   S32 spaceToDivide = getWidth() - totalTabWidth;
776   S32 pointDelta    = 0;
777   for( S32 i = 0; i < rowTemp.size(); i++ )
778   {
779      TabHeaderInfo &info = *rowTemp[i];
780      S32 extraSpace = (S32)spaceToDivide / ( rowTemp.size() );
781      info.TabRect.extent.x += extraSpace;
782      info.TabRect.point.x += pointDelta;
783      pointDelta += extraSpace;
784   }
785}
786
787//-----------------------------------------------------------------------------
788
789GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( const GuiEvent &event )
790{
791   return findHitTab( event.mousePoint );
792}
793
794//-----------------------------------------------------------------------------
795
796GuiTabPageCtrl *GuiTabBookCtrl::findHitTab( Point2I hitPoint )
797{
798   // Short Circuit.
799   //
800   // If the tab size is zero, don't render tabs,
801   //  and assume it's a tab-less tab-book - JDD
802   if( mPages.empty() || mTabHeight <= 0 )
803      return NULL;
804
805   for( S32 i = 0; i < mPages.size(); i++ )
806   {
807      if( mPages[i].TabRect.pointInRect( hitPoint ) )
808         return mPages[i].Page;
809   }
810   return NULL;
811}
812
813//-----------------------------------------------------------------------------
814
815void GuiTabBookCtrl::selectPage( S32 index )
816{
817   if( mPages.empty() || index < 0 )
818      return;
819
820   if( mPages.size() <= index )
821      index = mPages.size() - 1;
822
823   // Select the page
824   selectPage( mPages[ index ].Page );
825}
826
827//-----------------------------------------------------------------------------
828
829void GuiTabBookCtrl::selectPage( GuiTabPageCtrl *page )
830{
831   // Return if already selected.
832   if( mSelectedPageNum >= 0 && mSelectedPageNum < mPages.size() && mPages[ mSelectedPageNum ].Page == page )
833      return;
834      
835   mSelectedPageNum = -1;
836
837   Vector<TabHeaderInfo>::iterator i = mPages.begin();
838   for( S32 index = 0; i != mPages.end() ; i++, index++ )
839   {
840      GuiTabPageCtrl *tab = (*i).Page;
841      if( page == tab )
842      {
843         mActivePage = tab;
844         tab->setVisible( true );
845
846         mSelectedPageNum = index;
847         
848         // Notify User
849         onTabSelected_callback( tab->getText(), index );
850      }
851      else
852         tab->setVisible( false );
853   }
854   setUpdateLayout( updateSelf );
855}
856
857//-----------------------------------------------------------------------------
858
859bool GuiTabBookCtrl::_setSelectedPage( void *object, const char *index, const char *data )
860{
861   GuiTabBookCtrl* book = reinterpret_cast< GuiTabBookCtrl* >( object );
862   book->selectPage( dAtoi( data ) );
863   return false;
864}
865//-----------------------------------------------------------------------------
866
867bool GuiTabBookCtrl::onKeyDown(const GuiEvent &event)
868{
869   // Tab      = Next Page
870   // Ctrl-Tab = Previous Page
871   if( 0 && event.keyCode == KEY_TAB )
872   {
873      if( event.modifier & SI_PRIMARY_CTRL )
874         selectPrevPage();
875      else 
876         selectNextPage();
877
878      return true;
879   }
880
881   return Parent::onKeyDown( event );
882}
883
884//-----------------------------------------------------------------------------
885
886void GuiTabBookCtrl::selectNextPage()
887{
888   if( mPages.empty() )
889      return;
890
891   if( mActivePage == NULL )
892      mActivePage = mPages[0].Page;
893
894   S32 nI = 0;
895   for( ; nI < mPages.size(); nI++ )
896   {
897      GuiTabPageCtrl *tab = mPages[ nI ].Page;
898      if( tab == mActivePage )
899      {
900         if( nI == ( mPages.size() - 1 ) )
901            selectPage( 0 );
902         else if ( nI + 1 <= ( mPages.size() - 1 ) ) 
903            selectPage( nI + 1 );
904         else
905            selectPage( 0 );
906         return;
907      }
908   }
909}
910
911//-----------------------------------------------------------------------------
912
913void GuiTabBookCtrl::selectPrevPage()
914{
915   if( mPages.empty() )
916      return;
917
918   if( mActivePage == NULL )
919      mActivePage = mPages[0].Page;
920
921   S32 nI = 0;
922   for( ; nI < mPages.size(); nI++ )
923   {
924      GuiTabPageCtrl *tab = mPages[ nI ].Page;
925      if( tab == mActivePage )
926      {
927         if( nI == 0 )
928            selectPage( mPages.size() - 1 );
929         else
930            selectPage( nI - 1 );
931         return;
932      }
933   }
934}
935
936//-----------------------------------------------------------------------------
937
938void GuiTabBookCtrl::fitPage( GuiTabPageCtrl* page )
939{
940   page->resize( mPageRect.point, mPageRect.extent );
941}
942
943//-----------------------------------------------------------------------------
944
945S32 GuiTabBookCtrl::getPageNum( GuiTabPageCtrl* page ) const
946{
947   const U32 numPages = mPages.size();
948   for( U32 i = 0; i < numPages; ++ i )
949      if( mPages[ i ].Page == page )
950         return i;
951         
952   return -1;
953}
954
955//=============================================================================
956//    API.
957//=============================================================================
958// MARK: ---- API ----
959
960//-----------------------------------------------------------------------------
961
962DefineEngineMethod( GuiTabBookCtrl, addPage, void, ( const char* title ), ( "" ),
963   "Add a new tab page to the control.\n\n"
964   "@param title Title text for the tab page header." )
965{
966   object->addNewPage( title );
967}
968
969//-----------------------------------------------------------------------------
970
971DefineEngineMethod( GuiTabBookCtrl, selectPage, void, ( S32 index ),,
972   "Set the selected tab page.\n\n"
973   "@param index Index of the tab page." )
974{
975   object->selectPage( index );
976}
977
978//-----------------------------------------------------------------------------
979
980DefineEngineMethod( GuiTabBookCtrl, getSelectedPage, S32, (),,
981   "Get the index of the currently selected tab page.\n\n"
982   "@return Index of the selected tab page or -1 if no tab page is selected." )
983{
984   return object->getSelectedPageNum();
985}
986