Torque3D Documentation / _generateds / guiWindowCtrl.cpp

guiWindowCtrl.cpp

Engine/source/gui/containers/guiWindowCtrl.cpp

More...

Public Functions

ConsoleDocClass(GuiWindowCtrl , "@brief A window with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> title bar and an optional set of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">buttons.\n\n</a>" "The <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a> class implements windows that can be freely placed within the <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> window. Additionally, " "the windows can be resized and maximized/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">minimized.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a>(MyWindow)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " text=\"My Window\"; // The text that is displayed on the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "   resizeWidth = true; // Allow horizontal resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" "   resizeHeight = true; // Allow vertical resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" "   canClose = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> close button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "   canMinimize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> minimize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "   canMaximize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> maximize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup GuiContainers" )
DefineEngineMethod(GuiWindowCtrl , attachTo , void , (GuiWindowCtrl *window) , "" )
DefineEngineMethod(GuiWindowCtrl , selectWindow , void , () , "Bring the window <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the front." )
DefineEngineMethod(GuiWindowCtrl , setCollapseGroup , void , (bool state) , "Set the window's collapsing state." )
DefineEngineMethod(GuiWindowCtrl , toggleCollapseGroup , void , () , "Toggle the window collapsing." )
DefineEngineStaticMethod(GuiWindowCtrl , attach , void , (GuiWindowCtrl *bottomWindow, GuiWindowCtrl *topWindow) , "Attach @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> @topWindow so that @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow moves along with @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> topWindow when it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dragged.\n\n</a>" "@param bottomWindow \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param topWindow " )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onClose , void , () , () , "Called when the close button has been pressed." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onCollapse , void , () , () , "Called when the window is collapsed by clicking its title bar." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onMaximize , void , () , () , "Called when the window has been maximized." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onMinimize , void , () , () , "Called when the window has been minimized." )
IMPLEMENT_CALLBACK(GuiWindowCtrl , onRestore , void , () , () , "Called when the window is restored from minimized, maximized , or collapsed state." )

Detailed Description

Public Functions

ConsoleDocClass(GuiWindowCtrl , "@brief A window with <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> title bar and an optional set of <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">buttons.\n\n</a>" "The <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a> class implements windows that can be freely placed within the <a href="/coding/file/editortool_8cpp/#editortool_8cpp_1a4cb041169a32ea3d4cacadbb955e06b4">render</a> window. Additionally, " "the windows can be resized and maximized/<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">minimized.\n\n</a>" " @<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">tsexample\n</a>" "<a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> <a href="/coding/class/classguiwindowctrl/">GuiWindowCtrl</a>(MyWindow)\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "{\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" " text=\"My Window\"; // The text that is displayed on the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "   resizeWidth = true; // Allow horizontal resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" "   resizeHeight = true; // Allow vertical resizing by user via <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">mouse.\n</a>" "   canClose = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> close button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "   canMinimize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> minimize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "   canMaximize = true; // Display <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> maximize button in the title <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">bar.\n</a>" "};\<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">endtsexample\n\n</a>" "@ingroup GuiContainers" )

DefineEngineMethod(GuiWindowCtrl , attachTo , void , (GuiWindowCtrl *window) , "" )

DefineEngineMethod(GuiWindowCtrl , selectWindow , void , () , "Bring the window <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the front." )

DefineEngineMethod(GuiWindowCtrl , setCollapseGroup , void , (bool state) , "Set the window's collapsing state." )

DefineEngineMethod(GuiWindowCtrl , toggleCollapseGroup , void , () , "Toggle the window collapsing." )

DefineEngineStaticMethod(GuiWindowCtrl , attach , void , (GuiWindowCtrl *bottomWindow, GuiWindowCtrl *topWindow) , "Attach @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> @topWindow so that @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> bottomWindow moves along with @<a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> topWindow when it is <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">dragged.\n\n</a>" "@param bottomWindow \<a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">n</a>" "@param topWindow " )

IMPLEMENT_CALLBACK(GuiWindowCtrl , onClose , void , () , () , "Called when the close button has been pressed." )

IMPLEMENT_CALLBACK(GuiWindowCtrl , onCollapse , void , () , () , "Called when the window is collapsed by clicking its title bar." )

IMPLEMENT_CALLBACK(GuiWindowCtrl , onMaximize , void , () , () , "Called when the window has been maximized." )

IMPLEMENT_CALLBACK(GuiWindowCtrl , onMinimize , void , () , () , "Called when the window has been minimized." )

IMPLEMENT_CALLBACK(GuiWindowCtrl , onRestore , void , () , () , "Called when the window is restored from minimized, maximized , or collapsed state." )

IMPLEMENT_CONOBJECT(GuiWindowCtrl )

   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 "platform/platform.h"
  25#include "gui/containers/guiWindowCtrl.h"
  26
  27#include "console/consoleTypes.h"
  28#include "console/console.h"
  29#include "console/engineAPI.h"
  30#include "gui/core/guiCanvas.h"
  31#include "gui/core/guiDefaultControlRender.h"
  32#include "gfx/gfxDevice.h"
  33#include "gfx/gfxDrawUtil.h"
  34#include "gui/containers/guiRolloutCtrl.h"
  35#include "gui/editor/guiMenuBar.h"
  36
  37IMPLEMENT_CONOBJECT( GuiWindowCtrl );
  38
  39ConsoleDocClass( GuiWindowCtrl,
  40   "@brief A window with a title bar and an optional set of buttons.\n\n"
  41
  42   "The GuiWindowCtrl class implements windows that can be freely placed within the render window.  Additionally, "
  43   "the windows can be resized and maximized/minimized.\n\n"
  44   
  45   "@tsexample\n"
  46   "new GuiWindowCtrl( MyWindow )\n"
  47   "{\n"
  48   "   text = \"My Window\"; // The text that is displayed on the title bar.\n"
  49   "   resizeWidth = true; // Allow horizontal resizing by user via mouse.\n"
  50   "   resizeHeight = true; // Allow vertical resizing by user via mouse.\n"
  51   "   canClose = true; // Display a close button in the title bar.\n"
  52   "   canMinimize = true; // Display a minimize button in the title bar.\n"
  53   "   canMaximize = true; // Display a maximize button in the title bar.\n"
  54   "};\n"
  55   "@endtsexample\n\n"
  56   
  57   "@ingroup GuiContainers"
  58);
  59
  60IMPLEMENT_CALLBACK( GuiWindowCtrl, onClose, void, (), (),
  61   "Called when the close button has been pressed." );
  62IMPLEMENT_CALLBACK( GuiWindowCtrl, onMinimize, void, (), (),
  63   "Called when the window has been minimized." );
  64IMPLEMENT_CALLBACK( GuiWindowCtrl, onMaximize, void, (), (),
  65   "Called when the window has been maximized." );
  66IMPLEMENT_CALLBACK( GuiWindowCtrl, onCollapse, void, (), (),
  67   "Called when the window is collapsed by clicking its title bar." );
  68IMPLEMENT_CALLBACK( GuiWindowCtrl, onRestore, void, (), (),
  69   "Called when the window is restored from minimized, maximized, or collapsed state." );
  70
  71//-----------------------------------------------------------------------------
  72
  73GuiWindowCtrl::GuiWindowCtrl()
  74   :  mResizeWidth(true),
  75      mResizeEdge(edgeNone),
  76      mResizeHeight(true),
  77      mCanMove(true),
  78      mResizeMargin(2.f),
  79      mCanClose(true),
  80      mCanMinimize(true),
  81      mCanMaximize(true),
  82      mCanCollapse(false),
  83      mCanDock(false),
  84      mEdgeSnap(true),
  85      mCollapseGroup(-1),
  86      mCollapseGroupNum(-1),
  87      mIsCollapsed(false),
  88      mIsMouseResizing(false)
  89{
  90   // mTitleHeight will change in instanciation most likely...
  91   mTitleHeight = 24;
  92
  93   mIsContainer = true;
  94
  95   mCloseCommand = StringTable->EmptyString();
  96
  97   mMinimized = false;
  98   mMaximized = false;
  99   mMouseMovingWin = false;
 100   mMouseResizeWidth = false;
 101   mMouseResizeHeight = false;
 102   mMinimizeIndex = -1;
 103   mTabIndex = -1;
 104   mBitmapBounds = NULL;
 105   setExtent(100, 200);
 106
 107   RectI closeRect(80, 2, 16, 16);
 108   mCloseButton = closeRect;
 109   closeRect.point.x -= 18;
 110   mMaximizeButton = closeRect;
 111   closeRect.point.x -= 18;
 112   mMinimizeButton = closeRect;
 113
 114   // Other defaults
 115   mActive = true;
 116   mCloseButtonPressed = false;
 117   mMaximizeButtonPressed = false;
 118   mMinimizeButtonPressed = false;
 119
 120   mRepositionWindow = false;
 121   mResizeWindow = false;
 122   mSnapSignal = false;
 123   mPreCollapsedYExtent = 200;
 124   mPreCollapsedYMinExtent = 176;
 125   mText = "New Window";
 126}
 127
 128//-----------------------------------------------------------------------------
 129
 130void GuiWindowCtrl::initPersistFields()
 131{
 132   addGroup( "Window" );
 133   
 134      addField( "text",              TypeRealString,   Offset( mText, GuiWindowCtrl ),
 135         "Text label to display in titlebar." );
 136      addField( "resizeWidth",       TypeBool,         Offset( mResizeWidth, GuiWindowCtrl ),
 137         "Whether the window can be resized horizontally." );
 138      addField( "resizeHeight",      TypeBool,         Offset( mResizeHeight, GuiWindowCtrl ),
 139         "Whether the window can be resized vertically." );
 140      addField( "canMove",           TypeBool,         Offset( mCanMove, GuiWindowCtrl ),
 141         "Whether the window can be moved by dragging its titlebar." );
 142      addField( "canClose",          TypeBool,         Offset( mCanClose, GuiWindowCtrl ),
 143         "Whether the window has a close button." );
 144      addField( "canMinimize",       TypeBool,         Offset( mCanMinimize, GuiWindowCtrl ),
 145         "Whether the window has a minimize button." );
 146      addField( "canMaximize",       TypeBool,         Offset( mCanMaximize, GuiWindowCtrl ),
 147         "Whether the window has a maximize button." );
 148      addField( "canCollapse",       TypeBool,         Offset( mCanCollapse, GuiWindowCtrl ),
 149         "Whether the window can be collapsed by clicking its title bar." );
 150      addField( "closeCommand",      TypeString,       Offset( mCloseCommand, GuiWindowCtrl ),
 151         "Script code to execute when the window is closed." );
 152      addField( "edgeSnap",          TypeBool,         Offset( mEdgeSnap,GuiWindowCtrl ),
 153         "If true, the window will snap to the edges of other windows when moved close to them." );
 154         
 155   endGroup( "Window" );
 156
 157   Parent::initPersistFields();
 158}
 159
 160//=============================================================================
 161//    Collapsing.
 162//=============================================================================
 163// MARK: ---- Collapsing ----
 164
 165//-----------------------------------------------------------------------------
 166
 167void GuiWindowCtrl::moveFromCollapseGroup()
 168{
 169   GuiControl *parent = getParent();
 170   if( !parent )
 171      return;
 172
 173   S32 groupVec = mCollapseGroup;
 174   S32 vecPos = mCollapseGroupNum;
 175   S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1;
 176
 177   CollapseGroupNumVec collapseGroupNumVec;
 178
 179   if( groupVecCount > vecPos )
 180   {
 181      if (vecPos == 1)
 182      {
 183         CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
 184         for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
 185         {
 186            if((*iter)->mCollapseGroupNum >= vecPos)
 187               collapseGroupNumVec.push_back((*iter));
 188         }
 189
 190         parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
 191         parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
 192         parent->mCollapseGroupVec[groupVec].erase(U32(0));
 193         parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1);
 194         parent->mCollapseGroupVec.erase(groupVec);
 195         if(groupVec > 0)
 196            parent->mCollapseGroupVec.setSize(groupVec);
 197
 198         parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
 199      }
 200      else
 201      {
 202         // Iterate through the group i was once in, gather myself and the controls below me and store them in an array
 203         CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
 204         for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
 205         {
 206            if((*iter)->mCollapseGroupNum >= vecPos)
 207               collapseGroupNumVec.push_back((*iter));
 208         }
 209         
 210         // Iterate through the newly created array; delete my references in the old group, create a new group and organize me accord.
 211         S32 assignWindowNumber = 0;
 212         CollapseGroupNumVec::iterator iter2 = collapseGroupNumVec.begin();
 213         for(; iter2 != collapseGroupNumVec.end(); iter2++ )
 214         {
 215            parent->mCollapseGroupVec[groupVec].pop_back();
 216            parent->mCollapseGroupVec[groupVec].setSize(groupVecCount);
 217            (*iter2)->mCollapseGroup = (parent->mCollapseGroupVec.size());
 218            (*iter2)->mCollapseGroupNum = assignWindowNumber;
 219            
 220            assignWindowNumber++;
 221            groupVecCount--;
 222         }
 223
 224         parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
 225      }
 226   }
 227   else
 228   {
 229      parent->mCollapseGroupVec[groupVec].erase(mCollapseGroupNum);
 230      parent->mCollapseGroupVec[groupVec].setSize(groupVecCount);
 231      mCollapseGroup = -1;
 232      mCollapseGroupNum = -1;
 233
 234      if(groupVecCount <= 1)
 235      {
 236         parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
 237         parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
 238         parent->mCollapseGroupVec[groupVec].erase(U32(0));
 239         parent->mCollapseGroupVec[groupVec].setSize(groupVecCount - 1);
 240         parent->mCollapseGroupVec.erase(groupVec);   
 241      }
 242   }
 243   
 244   // Re id collapse groups
 245   refreshCollapseGroups();
 246}
 247
 248//-----------------------------------------------------------------------------
 249
 250void GuiWindowCtrl::moveToCollapseGroup(GuiWindowCtrl* hitWindow, bool orientation )
 251{
 252   // Orientation 0 - window in question is being connected to top of another window
 253   // Orientation 1 - window in question is being connected to bottom of another window
 254
 255   GuiControl *parent = getParent();
 256   if( !parent )
 257      return;
 258   
 259   S32 groupVec = mCollapseGroup;
 260   S32 attatchedGroupVec = hitWindow->mCollapseGroup;
 261   S32 vecPos = mCollapseGroupNum;
 262   
 263   if(mCollapseGroup == attatchedGroupVec && vecPos != -1)
 264      return;
 265
 266   CollapseGroupNumVec collapseGroupNumVec;
 267
 268   // Window colliding with is not in a collapse group
 269   if(hitWindow->mCollapseGroup < 0) 
 270   {
 271      // We(the collider) are in a group of windows
 272      if(mCollapseGroup >= 0) 
 273      {
 274         S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1;
 275            
 276         // Copy pointer window data in my array, and store in a temp array
 277         CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
 278         for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
 279         {
 280            if((*iter)->mCollapseGroupNum >= vecPos)
 281            {
 282               collapseGroupNumVec.push_back((*iter));
 283               groupVecCount--;
 284            }
 285         }
 286
 287         // Kill my old array group and erase its footprints
 288         if( vecPos <= 1 || groupVecCount == 0 )
 289         {
 290            // Isn't covered in the renumbering of groups, so we it here
 291            parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
 292            parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
 293
 294            parent->mCollapseGroupVec[groupVec].clear();
 295            parent->mCollapseGroupVec[groupVec].setSize(U32(0));
 296            parent->mCollapseGroupVec.erase(groupVec);
 297
 298            if(groupVec > 0)
 299               parent->mCollapseGroupVec.setSize(groupVec);
 300         }
 301
 302         // Push the collided window
 303         if(orientation == 0)
 304            collapseGroupNumVec.push_back(hitWindow);
 305         else
 306            collapseGroupNumVec.push_front(hitWindow);
 307
 308         // Push the temp array in the guiControl array holder
 309         parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
 310      }
 311      else
 312      {
 313         if(orientation == 0)
 314         {
 315            collapseGroupNumVec.push_front(hitWindow);
 316            collapseGroupNumVec.push_front(this);
 317         }
 318         else
 319         {
 320            collapseGroupNumVec.push_front(this);
 321            collapseGroupNumVec.push_front(hitWindow);
 322         }
 323
 324         parent->mCollapseGroupVec.push_back( collapseGroupNumVec );
 325      }
 326   }
 327   else // Window colliding with *IS* in a collapse group
 328   {
 329   
 330      if(mCollapseGroup >= 0)
 331      {
 332         S32 groupVecCount = parent->mCollapseGroupVec[groupVec].size() - 1;
 333            
 334         CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[groupVec].begin();
 335         for(; iter != parent->mCollapseGroupVec[groupVec].end(); iter++ )
 336         {
 337            if((*iter)->mCollapseGroupNum >= vecPos)
 338            {
 339               // Push back the pointer window controls to the collided array
 340               parent->mCollapseGroupVec[attatchedGroupVec].push_back((*iter));
 341               groupVecCount--;
 342            }
 343         }
 344
 345         if( vecPos <= 1 || groupVecCount == 0 )
 346         {
 347            // Isn't covered in the renumbering of groups, so we it here
 348            parent->mCollapseGroupVec[groupVec].first()->mCollapseGroup = -1;
 349            parent->mCollapseGroupVec[groupVec].first()->mCollapseGroupNum = -1;
 350
 351            parent->mCollapseGroupVec[groupVec].clear();
 352            parent->mCollapseGroupVec[groupVec].setSize(U32(0));
 353            parent->mCollapseGroupVec.erase(groupVec);
 354
 355            if(groupVec > 0)
 356               parent->mCollapseGroupVec.setSize(groupVec);
 357         }
 358         
 359         // Since we killed my old array group, run in case the guiControl array moved me down a notch
 360         if(attatchedGroupVec > groupVec )
 361            attatchedGroupVec--;
 362
 363      }
 364      else
 365      {
 366         groupVec = hitWindow->mCollapseGroup;
 367         
 368         if(orientation == 0)
 369            parent->mCollapseGroupVec[groupVec].push_front(this);
 370         else
 371            parent->mCollapseGroupVec[groupVec].push_back(this);
 372      }
 373   }
 374   
 375   // Re id collapse groups
 376   refreshCollapseGroups();
 377
 378   // Select the current window and its collapse group
 379   selectWindow();
 380}
 381
 382//-----------------------------------------------------------------------------
 383
 384void GuiWindowCtrl::refreshCollapseGroups()
 385{
 386   GuiControl *parent = getParent();
 387   if( !parent )
 388      return;
 389   
 390   CollapseGroupNumVec  collapseGroupNumVec;
 391
 392   // iterate through the collided array, renumbering the windows pointers
 393   S32 assignGroupNum = 0;
 394   CollapseGroupVec::iterator iter = parent->mCollapseGroupVec.begin();
 395   for(; iter != parent->mCollapseGroupVec.end(); iter++ )
 396   {
 397      S32 assignWindowNumber = 0;
 398      CollapseGroupNumVec::iterator iter2 = parent->mCollapseGroupVec[assignGroupNum].begin();
 399      for(; iter2 != parent->mCollapseGroupVec[assignGroupNum].end(); iter2++ )
 400      {
 401         (*iter2)->mCollapseGroup = assignGroupNum;
 402         (*iter2)->mCollapseGroupNum = assignWindowNumber;
 403         assignWindowNumber++;
 404      }
 405            
 406      assignGroupNum++;
 407   }
 408}
 409
 410//-----------------------------------------------------------------------------
 411
 412void GuiWindowCtrl::moveWithCollapseGroup(Point2I windowPosition)
 413{
 414   GuiControl *parent = getParent();
 415   if( !parent )
 416      return;
 417
 418   Point2I newChildPosition(0, 0);
 419   S32 addedPosition = getExtent().y;
 420
 421   // Iterate through windows below this. Move them according to my position.
 422   CollapseGroupNumVec collapseGroupNumVec;
 423   CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
 424   for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
 425   {
 426      if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
 427      {
 428         newChildPosition.x = windowPosition.x;
 429         newChildPosition.y = windowPosition.y + addedPosition;
 430
 431         (*iter)->resize(newChildPosition, (*iter)->getExtent());
 432         addedPosition += (*iter)->getExtent().y;
 433      }
 434   }
 435}
 436
 437//-----------------------------------------------------------------------------
 438
 439void GuiWindowCtrl::setCollapseGroup(bool state)
 440{
 441   GuiControl *parent = getParent();
 442   if( !parent )
 443      return;
 444
 445   if( mIsCollapsed != state )
 446   {
 447      mIsCollapsed = state;
 448      handleCollapseGroup();
 449   }
 450}
 451
 452//-----------------------------------------------------------------------------
 453
 454void GuiWindowCtrl::toggleCollapseGroup()
 455{
 456   GuiControl *parent = getParent();
 457   if( !parent )
 458      return;
 459
 460   mIsCollapsed = !mIsCollapsed;
 461   handleCollapseGroup();
 462}
 463
 464//-----------------------------------------------------------------------------
 465
 466void GuiWindowCtrl::handleCollapseGroup()
 467{
 468   GuiControl *parent = getParent();
 469   if( !parent )
 470      return;
 471
 472   CollapseGroupNumVec  collapseGroupNumVec;
 473
 474   if( mIsCollapsed ) // minimize window up to its header bar
 475   {
 476      //save settings 
 477      mPreCollapsedYExtent = getExtent().y;
 478      mPreCollapsedYMinExtent = getMinExtent().y;
 479
 480      //create settings for collapsed window to abide by
 481      mResizeHeight = false;
 482      setMinExtent( Point2I( getMinExtent().x, 24 ) );
 483
 484      iterator i;
 485      for(i = begin(); i != end(); i++)
 486      {
 487         GuiControl *ctrl = static_cast<GuiControl *>(*i);
 488         ctrl->setVisible(false);
 489         ctrl->mCanResize = false;
 490      }
 491
 492      resize( getPosition(), Point2I( getExtent().x, 24 ) );
 493
 494      if(mCollapseGroup >= 0)
 495      {
 496         S32 moveChildYBy = (mPreCollapsedYExtent - 24);
 497
 498         CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
 499         for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
 500         {
 501            if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
 502            {
 503               Point2I newChildPosition =  (*iter)->getPosition();
 504               newChildPosition.y -= moveChildYBy;
 505               (*iter)->resize(newChildPosition, (*iter)->getExtent());
 506            }
 507         }
 508      }
 509      
 510      onCollapse_callback();
 511   }
 512   else // maximize the window to its previous position
 513   {
 514      //create and load settings
 515      mResizeHeight = true;
 516      setMinExtent( Point2I( getMinExtent().x, mPreCollapsedYMinExtent ) );
 517      
 518      resize( getPosition(), Point2I( getExtent().x, mPreCollapsedYExtent ) );
 519      
 520      iterator i;
 521      for(i = begin(); i != end(); i++)
 522      {
 523         GuiControl *ctrl = static_cast<GuiControl *>(*i);
 524         ctrl->setVisible(true);
 525         ctrl->mCanResize = true;
 526      }
 527
 528      if(mCollapseGroup >= 0)
 529      {
 530         S32 moveChildYBy = (mPreCollapsedYExtent - 24);
 531
 532         CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
 533         for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
 534         {
 535            if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
 536            {
 537               Point2I newChildPosition =  (*iter)->getPosition();
 538               newChildPosition.y += moveChildYBy;             
 539               (*iter)->resize(newChildPosition, (*iter)->getExtent());
 540            }
 541         }
 542      }
 543      
 544      onRestore_callback();
 545   }
 546}
 547
 548//-----------------------------------------------------------------------------
 549
 550bool GuiWindowCtrl::resizeCollapseGroup(bool resizeX, bool resizeY, Point2I resizePos, Point2I resizeExtent)
 551{
 552   GuiControl *parent = getParent();
 553   if( !parent )
 554      return false;
 555
 556   CollapseGroupNumVec  collapseGroupNumVec;
 557
 558   bool canResize = true;
 559   CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
 560   for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
 561   {
 562      if((*iter) == this)
 563         continue; 
 564      
 565      Point2I newChildPosition = (*iter)->getPosition();
 566      Point2I newChildExtent = (*iter)->getExtent();
 567
 568      if( resizeX == true )
 569      {
 570         newChildPosition.x -= resizePos.x;
 571         newChildExtent.x -= resizeExtent.x;
 572         
 573      }
 574      if( resizeY == true )
 575      {
 576         if((*iter)->mCollapseGroupNum > mCollapseGroupNum)
 577         {
 578            newChildPosition.y -= resizeExtent.y;
 579            newChildPosition.y -= resizePos.y;
 580         }
 581         else if((*iter)->mCollapseGroupNum == mCollapseGroupNum - 1)
 582            newChildExtent.y -= resizePos.y;
 583      }
 584
 585      // check is done for normal extent of windows. if false, check again in case its just giving false
 586      // due to being a collapsed window. if your truly being forced passed your extent, return false
 587      if( !(*iter)->mIsCollapsed && newChildExtent.y >= (*iter)->getMinExtent().y )
 588         (*iter)->resize( newChildPosition, newChildExtent);
 589      else
 590      {
 591         if( (*iter)->mIsCollapsed )
 592            (*iter)->resize( newChildPosition, newChildExtent);
 593         else
 594            canResize = false;
 595      }
 596   }
 597   return canResize;
 598}
 599
 600//-----------------------------------------------------------------------------
 601// Mouse Methods
 602//-----------------------------------------------------------------------------
 603S32 GuiWindowCtrl::findHitEdges( const Point2I &globalPoint )
 604{
 605   // No Edges
 606   S32 edgeMask = edgeNone;
 607
 608   GuiControl *parent = getParent();
 609   if( !parent )
 610      return edgeMask;
 611
 612   RectI bounds( getGlobalBounds() );
 613
 614   // Create an EdgeRectI structure that has four edges
 615   // Left/Right/Top/Bottom
 616   // Each Edge structure has a hit operation that will take
 617   // another edge and test for a hit on the edge with a margin
 618   // specified by the .margin scalar
 619   EdgeRectI edges = EdgeRectI(bounds, mResizeMargin);
 620
 621   // Get Cursor Edges
 622   Edge cursorVertEdge = Edge( globalPoint, Point2F( 1.f, 0.f ) );
 623   Edge cursorHorzEdge = Edge( globalPoint, Point2F( 0.f, 1.f ) );
 624
 625   if( edges.left.hit( cursorVertEdge ) )
 626      edgeMask |= edgeLeft;
 627   else if( edges.right.hit( cursorVertEdge ) )
 628      edgeMask |= edgeRight;
 629
 630   if( edges.bottom.hit( cursorHorzEdge ) )
 631      edgeMask |= edgeBottom;
 632   else if( edges.top.hit( cursorHorzEdge ) )
 633   {
 634      // Only the top window in a collapse group can be extended from the top
 635      if( mCanCollapse && mCollapseGroup >= 0 )
 636      {
 637         if( parent->mCollapseGroupVec[mCollapseGroup].first() !=  this )
 638            return edgeMask;
 639      }
 640
 641      edgeMask |= edgeTop;
 642   }
 643
 644   // Return the resulting mask
 645   return edgeMask;
 646}
 647
 648void GuiWindowCtrl::getSnappableWindows( Vector<GuiWindowCtrl*> &windowOutVector, bool canCollapse )
 649{
 650   GuiControl *parent = getParent();
 651   if( !parent )
 652      return;
 653
 654   S32 parentSize = parent->size();
 655   for( S32 i = 0; i < parentSize; i++ )
 656   {
 657      GuiWindowCtrl *childWindow = dynamic_cast<GuiWindowCtrl*>(parent->at(i));
 658      if( !childWindow || !childWindow->isVisible() || childWindow == this || childWindow->mEdgeSnap == false)
 659         continue;
 660      
 661      if( canCollapse && !childWindow->mCanCollapse )
 662         continue;
 663
 664      windowOutVector.push_back(childWindow);
 665   }
 666
 667}
 668
 669//=============================================================================
 670//    Events.
 671//=============================================================================
 672// MARK: ---- Events ----
 673
 674//-----------------------------------------------------------------------------
 675
 676bool GuiWindowCtrl::onWake()
 677{
 678   if (! Parent::onWake())
 679      return false;
 680
 681   //get the texture for the close, minimize, and maximize buttons
 682   mTextureObject = mProfile->mTextureObject;
 683   bool result = mProfile->constructBitmapArray() >= NumBitmaps;
 684   if( !result )
 685   {
 686      Con::errorf( "GuiWindowCtrl::onWake - failed to create bitmap array from profile bitmap." );
 687      return false;
 688   }
 689
 690   mBitmapBounds = mProfile->mBitmapArrayRects.address();
 691   S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y;
 692
 693   mTitleHeight = buttonHeight + 4;
 694
 695   //set the button coords
 696   positionButtons();
 697
 698   //set the tab index
 699   mTabIndex = -1;
 700   GuiControl *parent = getParent();
 701   if (parent && mFirstResponder)
 702   {
 703      mTabIndex = 0;
 704
 705      //count the number of windows preceeding this one
 706      iterator i;
 707      for (i = parent->begin(); i != parent->end(); i++)
 708      {
 709         GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
 710         if (ctrl)
 711         {
 712            if (ctrl == this) break;
 713            else if (ctrl->mFirstResponder) mTabIndex++;
 714         }
 715      }
 716   }
 717
 718   return true;
 719}
 720
 721//-----------------------------------------------------------------------------
 722
 723void GuiWindowCtrl::onSleep()
 724{
 725   mTextureObject = NULL;
 726   mMousePosition = Point2I(0,0);
 727   Parent::onSleep();
 728}
 729
 730//-----------------------------------------------------------------------------
 731
 732void GuiWindowCtrl::onMouseDown(const GuiEvent &event)
 733{
 734   setUpdate();
 735
 736   mOrigBounds = getBounds();
 737
 738   mMouseDownPosition = event.mousePoint;
 739   Point2I localPoint = globalToLocalCoord(event.mousePoint);
 740
 741   // Select this window - move it to the front, and set the first responder
 742   selectWindow();
 743
 744   mMouseMovingWin = false;
 745
 746   S32 hitEdges = findHitEdges( event.mousePoint );
 747
 748   mResizeEdge = edgeNone;
 749
 750   // Set flag by default so we only clear it
 751   // if we don't match either edge
 752   mMouseResizeHeight = true;
 753
 754   // Check Bottom/Top edges (Mutually Exclusive)
 755   if( mResizeHeight && hitEdges & edgeBottom )
 756      mResizeEdge |= edgeBottom;
 757   else if( mResizeHeight && hitEdges & edgeTop )
 758      mResizeEdge |= edgeTop;
 759   else
 760      mMouseResizeHeight = false;
 761
 762   // Set flag by default so we only clear it
 763   // if we don't match either edge
 764   mMouseResizeWidth = true;
 765
 766   // Check Left/Right edges (Mutually Exclusive)
 767   if( mResizeWidth && hitEdges & edgeLeft )
 768      mResizeEdge |= edgeLeft;
 769   else if( mResizeWidth && hitEdges & edgeRight )
 770      mResizeEdge |= edgeRight;
 771   else
 772      mMouseResizeWidth = false;
 773
 774
 775   // If we clicked within the title bar
 776   if ( !(mResizeEdge & ( edgeTop | edgeLeft | edgeRight ) ) && localPoint.y < mTitleHeight)
 777   {
 778      if (mCanClose && mCloseButton.pointInRect(localPoint))
 779      {
 780         mCloseButtonPressed = mCanClose;
 781      }
 782      else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
 783      {
 784         mMaximizeButtonPressed = mCanMaximize;
 785      }
 786      else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
 787      {
 788         mMinimizeButtonPressed = mCanMinimize;
 789      }
 790      else // We clicked anywhere else within the title
 791      {
 792         S32 docking = getDocking();
 793         if( docking == Docking::dockInvalid || docking == Docking::dockNone )
 794            mMouseMovingWin = mCanMove;
 795      
 796         mMouseResizeWidth = false;
 797         mMouseResizeHeight = false;
 798      }
 799   }
 800
 801
 802   if (mMouseMovingWin || mResizeEdge != edgeNone ||
 803         mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed)
 804   {
 805      mouseLock();
 806   }
 807   else
 808   {
 809
 810      GuiControl *ctrl = findHitControl(localPoint);
 811      if (ctrl && ctrl != this)
 812         ctrl->onMouseDown(event);
 813   }
 814}
 815
 816//-----------------------------------------------------------------------------
 817
 818void GuiWindowCtrl::onMouseDragged(const GuiEvent &event)
 819{
 820   GuiControl *parent = getParent();
 821   GuiCanvas *root = getRoot();
 822   if ( !root ) 
 823      return;
 824
 825   mMousePosition = globalToLocalCoord(event.mousePoint);
 826   Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
 827
 828   Point2I newPosition = getPosition();
 829   Point2I newExtent = getExtent();
 830   bool resizeX = false;
 831   bool resizeY = false;
 832   mResizeWindow = false;
 833   mRepositionWindow = false;
 834
 835   if (mMouseMovingWin && parent)
 836   {
 837      if( parent != root )
 838      {
 839         newPosition.x = mOrigBounds.point.x + deltaMousePosition.x;
 840         newPosition.y = getMax(0, mOrigBounds.point.y + deltaMousePosition.y );
 841         mRepositionWindow = true;
 842      }
 843      else
 844      {
 845         newPosition.x = getMax(0, getMin(parent->getWidth() - getWidth(), mOrigBounds.point.x + deltaMousePosition.x));
 846         newPosition.y = getMax(0, getMin(parent->getHeight() - getHeight(), mOrigBounds.point.y + deltaMousePosition.y));
 847      }
 848
 849      // Check snapping to other windows
 850      if( mEdgeSnap )
 851      {
 852         RectI bounds = getGlobalBounds();
 853         bounds.point = mOrigBounds.point + deltaMousePosition;
 854         EdgeRectI edges = EdgeRectI( bounds, mResizeMargin );
 855         
 856         // Create a global-space rectangle that covers the snapping
 857         // zone of this window. Double the space in which snapping occurs
 858         // for top and bottom.
 859         RectI snapZone = bounds;
 860         snapZone.point.x -= SnapDistance;
 861         snapZone.point.y -= SnapDistance;
 862         snapZone.extent.x += SnapDistance + SnapDistance;
 863         snapZone.extent.y += SnapDistance + SnapDistance;
 864
 865         //check if we need to offset because of the menubar
 866         U32 menuBarHeight = 0;
 867         GuiCanvas* guiCanvas = getRoot();
 868         if (guiCanvas)
 869         {
 870            GuiMenuBar* menuBar = dynamic_cast<GuiMenuBar*>(guiCanvas->getMenuBar());
 871            if (menuBar)
 872            {
 873               menuBarHeight = menuBar->getHeight();
 874            }
 875         }
 876         
 877         // Build valid snap and window vectors to compare against
 878         Vector< GuiWindowCtrl*> windowList;
 879         getSnappableWindows( windowList );
 880
 881         for( S32 i =0; i < windowList.size(); i++ )
 882         {            
 883            // Make sure the window is both horizontally and vertically
 884            // within the snap zone for this window.
 885            RectI windowBounds = windowList[i]->getGlobalBounds();
 886            //offset position by menubar height
 887            windowBounds.point.y -= menuBarHeight;
 888
 889            if( !snapZone.overlaps(windowBounds) )
 890               continue;
 891            
 892            // Build edges for snap detection
 893            EdgeRectI snapRect(windowBounds, mResizeMargin );
 894
 895            if( snapRect.right.position.x <= edges.left.position.x + SnapDistance &&
 896               snapRect.right.position.x >= edges.left.position.x - SnapDistance )
 897            {
 898               newPosition.x = snapRect.right.position.x;
 899            }           
 900            else if( snapRect.left.position.x <= edges.right.position.x + SnapDistance &&
 901               snapRect.left.position.x >= edges.right.position.x - SnapDistance )
 902            {
 903               newPosition.x = snapRect.left.position.x - bounds.extent.x;
 904            }
 905            else if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance &&
 906                  snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance )
 907            {
 908               // Ensure that we're not snapping to the middle of collapse groups
 909               if( (windowList[i]->mCanCollapse && windowList[i]->mCollapseGroup >= 0) ||
 910                     (mCanCollapse && mCollapseGroup >= 0) )
 911                     continue;
 912               newPosition.x = snapRect.left.position.x;
 913               newPosition.y = snapRect.top.position.y - bounds.extent.y;
 914            }
 915            else if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance &&
 916                  snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance)
 917            {
 918               // Ensure that we're not snapping to the middle of collapse groups
 919               // We are not in a group, or we are not in the same group
 920               if( mCollapseGroup == -1 || ( mCollapseGroup >= 0 && mCollapseGroup != windowList[i]->mCollapseGroup ) )
 921               {
 922                  // If the window checked is in a group, if its anything but the last, its n/a
 923                  if( windowList[i]->mCollapseGroup >= 0 && 
 924                        windowList[i] != parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() )
 925                     continue;
 926               }
 927               else  // We are in the same group, we can't obviously be [0]
 928               {
 929                  // If we are [-1/0] we have a serious problem. Also, we only allow connection to the window directly above us
 930                  if( mCollapseGroupNum <= 0 || 
 931                        windowList[i] != parent->mCollapseGroupVec[mCollapseGroup][mCollapseGroupNum-1] )
 932                     continue;
 933               }
 934
 935               newPosition.x = snapRect.left.position.x;
 936               newPosition.y = snapRect.bottom.position.y;
 937            }
 938         }
 939      }
 940   }
 941   else if(mCloseButtonPressed || mMaximizeButtonPressed || mMinimizeButtonPressed)
 942   {
 943      setUpdate();
 944      return;
 945   }
 946   else
 947   {
 948      if( ( !mMouseResizeHeight && !mMouseResizeWidth ) || !parent )
 949         return;
 950
 951      mResizeWindow = true;
 952      if( mResizeEdge & edgeBottom )
 953      {
 954         newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y + deltaMousePosition.y);
 955         resizeY = true;
 956      }
 957      else if ( mResizeEdge & edgeTop )
 958      {
 959         newExtent.y = getMin(parent->getHeight(), mOrigBounds.extent.y - deltaMousePosition.y);
 960         if ( newExtent.y >= mMinExtent.y )
 961         {
 962            // Standard reposition as we're not travelling into the min extent range
 963            newPosition.y = mOrigBounds.point.y + deltaMousePosition.y;
 964         }
 965         else
 966         {
 967            // We're into the min extent, so adjust the position up to the min extent
 968            // so the window doesn't appear to jump
 969            newPosition.y = mOrigBounds.point.y + (mOrigBounds.extent.y - mMinExtent.y);
 970         }
 971         resizeY = true;
 972      }
 973      
 974      if( mResizeEdge & edgeRight )
 975      {
 976         newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x + deltaMousePosition.x);
 977         resizeX = true;
 978      }
 979      else if( mResizeEdge & edgeLeft )
 980      {
 981         newExtent.x = getMin(parent->getWidth(), mOrigBounds.extent.x - deltaMousePosition.x);
 982         if ( newExtent.x >= mMinExtent.x )
 983         {
 984            // Standard reposition as we're not travelling into the min extent range
 985            newPosition.x = mOrigBounds.point.x + deltaMousePosition.x;
 986         }
 987         else
 988         {
 989            // We're into the min extent, so adjust the position up to the min extent
 990            // so the window doesn't appear to jump
 991            newPosition.x = mOrigBounds.point.x + (mOrigBounds.extent.x - mMinExtent.x);
 992         }
 993         resizeX = true;
 994      }
 995   }
 996
 997   // Resize this
 998   Point2I pos = parent->localToGlobalCoord(getPosition());
 999   root->addUpdateRegion(pos, getExtent());
1000
1001   if(mCanCollapse && mCollapseGroup >= 0 && mRepositionWindow == true)
1002      moveWithCollapseGroup(newPosition);
1003
1004   if(mCanCollapse && mCollapseGroup >= 0 && mResizeWindow == true )
1005   {  
1006      // Resize the window if allowed
1007      if( newExtent.y >= getMinExtent().y && newExtent.x >= getMinExtent().x)
1008      {
1009         mIsMouseResizing = true;
1010         if( resizeCollapseGroup( resizeX, resizeY, (getPosition() - newPosition), (getExtent() - newExtent) ) )
1011            resize(newPosition, newExtent);
1012      }
1013   }
1014   else // Normal window sizing functionality
1015      resize(newPosition, newExtent);
1016}
1017
1018//-----------------------------------------------------------------------------
1019
1020void GuiWindowCtrl::onMouseUp(const GuiEvent &event)
1021{
1022   bool closing = mCloseButtonPressed;
1023   bool maximizing = mMaximizeButtonPressed;
1024   bool minimizing = mMinimizeButtonPressed;
1025
1026   mCloseButtonPressed = false;
1027   mMaximizeButtonPressed = false;
1028   mMinimizeButtonPressed = false;
1029
1030   TORQUE_UNUSED(event);
1031   mouseUnlock();
1032
1033   mMouseMovingWin = false;
1034   mMouseResizeWidth = false;
1035   mMouseResizeHeight = false;
1036
1037   GuiControl *parent = getParent();
1038   if (! parent)
1039      return;
1040
1041   if( mIsMouseResizing )
1042   {
1043      mIsMouseResizing = false;
1044      return;
1045   }
1046
1047   Point2I localPoint = globalToLocalCoord(event.mousePoint);
1048   if (closing && mCloseButton.pointInRect(localPoint))
1049   {
1050      // Here is where were going to put our other if statement
1051      // if the window closes, and there were only 2 windows in the array, then just delete the array and default there params
1052      // if not, delete the window from the array, default its params, and renumber the windows
1053      
1054      if( engineAPI::gUseConsoleInterop )
1055         evaluate( mCloseCommand );
1056      onClose_callback();
1057   }
1058   else if (maximizing && mMaximizeButton.pointInRect(localPoint))
1059   {
1060      if (mMaximized)
1061      {
1062         // Resize to the previous position and extent, bounded by the parent
1063         resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)),
1064                        getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))),
1065                        mStandardBounds.extent);
1066         // Set the flag
1067         mMaximized = false;
1068         
1069         onRestore_callback();
1070      }
1071      else
1072      {
1073         // Only save the position if we're not minimized
1074         if (! mMinimized)
1075            mStandardBounds = getBounds();
1076         else
1077            mMinimized = false;
1078
1079         // Resize to fit the parent
1080         resize(Point2I(0, 0), parent->getExtent());
1081
1082         // Set the flag
1083         mMaximized = true;
1084         
1085         onMaximize_callback();
1086      }
1087   }
1088   else if (minimizing && mMinimizeButton.pointInRect(localPoint))
1089   {
1090      if (mMinimized)
1091      {
1092         // Resize to the previous position and extent, bounded by the parent
1093         resize(Point2I(getMax(0, getMin(parent->getWidth() - mStandardBounds.extent.x, mStandardBounds.point.x)),
1094                        getMax(0, getMin(parent->getHeight() - mStandardBounds.extent.y, mStandardBounds.point.y))),
1095                        mStandardBounds.extent);
1096         // Set the flag
1097         mMinimized = false;
1098         
1099         onRestore_callback();
1100      }
1101      else
1102      {
1103         if (parent->getWidth() < 100 || parent->getHeight() < mTitleHeight + 3)
1104            return;
1105
1106         // Only save the position if we're not maximized
1107         if (! mMaximized)
1108         {
1109            mStandardBounds = getBounds();
1110         }
1111         else
1112         {
1113            mMaximized = false;
1114         }
1115
1116         // First find the lowest unused minimized index up to 32 minimized windows
1117         U32 indexMask = 0;
1118         iterator i;
1119         S32 count = 0;
1120         for (i = parent->begin(); i != parent->end() && count < 32; i++)
1121         {
1122            count++;
1123            S32 index;
1124            GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
1125            if (ctrl && ctrl->isMinimized(index))
1126            {
1127               indexMask |= (1 << index);
1128            }
1129         }
1130
1131         // Now find the first unused bit
1132         for (count = 0; count < 32; count++)
1133         {
1134            if (! (indexMask & (1 << count))) break;
1135         }
1136
1137         // If we have more than 32 minimized windows, use the first position
1138         count = getMax(0, count);
1139
1140         // This algorithm assumes all window have the same title height, and will minimize to 98 pix
1141         Point2I newExtent(98, mTitleHeight);
1142
1143         // First, how many can fit across
1144         S32 numAcross = getMax(1, (parent->getWidth() / newExtent.x + 2));
1145
1146         // Find the new "mini position"
1147         Point2I newPosition;
1148         newPosition.x = (count % numAcross) * (newExtent.x + 2) + 2;
1149         newPosition.y = parent->getHeight() - (((count / numAcross) + 1) * (newExtent.y + 2)) - 2;
1150
1151         // Find the minimized position and extent
1152         resize(newPosition, newExtent);
1153
1154         // Set the index so other windows will not try to minimize to the same location
1155         mMinimizeIndex = count;
1156
1157         // Set the flag
1158         mMinimized = true;
1159         
1160         onMinimize_callback();
1161      }
1162   }
1163   else if ( !(mResizeEdge & edgeTop) && localPoint.y < mTitleHeight && event.mousePoint == mMouseDownPosition)
1164   {
1165      if (mCanClose && mCloseButton.pointInRect(localPoint))
1166         return;
1167      else if (mCanMaximize && mMaximizeButton.pointInRect(localPoint))
1168         return;
1169      else if (mCanMinimize && mMinimizeButton.pointInRect(localPoint))
1170         return;
1171      else if( mCanCollapse ) // If we clicked anywhere else on the title bar
1172         toggleCollapseGroup();
1173   }
1174   else if( mEdgeSnap && mCanCollapse )
1175   {
1176      // Create storage pointer
1177      GuiWindowCtrl* hitWindow = NULL;
1178      S32 snapType = -1;
1179
1180      Point2I deltaMousePosition = event.mousePoint - mMouseDownPosition;
1181      Point2I newExtent = getExtent();
1182
1183      RectI bounds = getGlobalBounds();
1184      bounds.point = mOrigBounds.point + deltaMousePosition;
1185      EdgeRectI edges = EdgeRectI( bounds, mResizeMargin );
1186      
1187      RectI snapZone = bounds;
1188      snapZone.point.x -= SnapDistance;
1189      snapZone.point.y -= SnapDistance;
1190      snapZone.extent.x += SnapDistance + SnapDistance;
1191      snapZone.extent.y += SnapDistance + SnapDistance;
1192
1193      Vector<EdgeRectI> snapList;
1194      Vector<GuiWindowCtrl*> windowList;
1195      
1196      getSnappableWindows( windowList, true );
1197      
1198      for( S32 i =0; i < windowList.size(); i++ )
1199      {
1200         if( !snapZone.overlaps( windowList[i]->getGlobalBounds() ) )
1201            continue;
1202
1203         // Build edges for snap detection
1204         EdgeRectI snapRect( windowList[i]->getGlobalBounds(), mResizeMargin );
1205         
1206         if( windowList[i]->mCollapseGroupNum == -1 ) 
1207         {
1208            // BECOMES "PARENT"
1209            if( snapRect.top.position.y <= edges.bottom.position.y + SnapDistance + SnapDistance &&
1210                  snapRect.top.position.y >= edges.bottom.position.y - SnapDistance - SnapDistance )
1211            {
1212               hitWindow = windowList[i];
1213               snapType = 0;
1214               newExtent.x = snapRect.right.position.x - snapRect.left.position.x;
1215               break;
1216            }
1217         }
1218         
1219         if( (windowList[i]->mCollapseGroupNum == -1) || (windowList[i]->mCollapseGroupNum == mCollapseGroupNum - 1) ||
1220               (!parent->mCollapseGroupVec.empty() && parent->mCollapseGroupVec[windowList[i]->mCollapseGroup].last() ==  windowList[i]) )
1221         {
1222            // BECOMES "CHILD"
1223            if( snapRect.bottom.position.y <= edges.top.position.y + SnapDistance + SnapDistance &&
1224                  snapRect.bottom.position.y >= edges.top.position.y - SnapDistance - SnapDistance)
1225            {
1226               hitWindow = windowList[i];
1227               snapType = 1;
1228               newExtent.x = snapRect.right.position.x - snapRect.left.position.x;
1229               break;
1230            }
1231         }
1232      }
1233      
1234      // We're either moving out of a collapse group or moving to another one
1235      // Not valid for windows not previously in a group
1236      if( mCollapseGroup >= 0 && 
1237        (snapType == -1 || (hitWindow && snapType >= 0 && mCollapseGroup != hitWindow->mCollapseGroup)))
1238         moveFromCollapseGroup();
1239      
1240      // No window to connect to
1241      if( !hitWindow )
1242         return;
1243
1244      moveToCollapseGroup( hitWindow, snapType );
1245      resize( getPosition(), newExtent );
1246   }
1247}
1248
1249//-----------------------------------------------------------------------------
1250
1251void GuiWindowCtrl::onMouseMove(const GuiEvent &event)
1252{
1253   mMousePosition = globalToLocalCoord(event.mousePoint);
1254}
1255
1256bool GuiWindowCtrl::onKeyDown(const GuiEvent &event)
1257{
1258   // If this control is a dead end, kill the event
1259   if ((! mVisible) || (! mActive) || (! mAwake)) return true;
1260
1261   if ((event.keyCode == KEY_TAB) && (event.modifier & SI_PRIMARY_CTRL))
1262   {
1263      // Find the next sibling window, and select it
1264      GuiControl *parent = getParent();
1265      if (parent)
1266      {
1267         GuiWindowCtrl *firstWindow = NULL;
1268         iterator i;
1269         for (i = parent->begin(); i != parent->end(); i++)
1270         {
1271            GuiWindowCtrl *ctrl = dynamic_cast<GuiWindowCtrl *>(*i);
1272            if (ctrl && ctrl->getTabIndex() == mTabIndex + 1)
1273            {
1274               ctrl->selectWindow();
1275               return true;
1276            }
1277            else if (ctrl && ctrl->getTabIndex() == 0)
1278            {
1279               firstWindow = ctrl;
1280            }
1281         }
1282         // Recycle from the beginning
1283         if (firstWindow != this)
1284         {
1285            firstWindow->selectWindow();
1286            return true;
1287         }
1288      }
1289   }
1290
1291   return Parent::onKeyDown(event);
1292}
1293
1294//-----------------------------------------------------------------------------
1295
1296void GuiWindowCtrl::onRender(Point2I offset, const RectI &updateRect)
1297{
1298   if( !mProfile || mProfile->mFont == NULL || mProfile->mBitmapArrayRects.size() < NumBitmaps )
1299      return Parent::onRender( offset, updateRect );
1300
1301   // Draw the outline
1302   RectI winRect;
1303   winRect.point = offset;
1304   winRect.extent = getExtent();
1305   GuiCanvas *root = getRoot();
1306   GuiControl *firstResponder = root ? root->getFirstResponder() : NULL;
1307
1308   bool isKey = (!firstResponder || controlIsChild(firstResponder));
1309
1310   U32 topBase = isKey ? BorderTopLeftKey : BorderTopLeftNoKey;
1311   winRect.point.x += mBitmapBounds[BorderLeft].extent.x;
1312   winRect.point.y += mBitmapBounds[topBase + 2].extent.y;
1313
1314   winRect.extent.x -= mBitmapBounds[BorderLeft].extent.x + mBitmapBounds[BorderRight].extent.x;
1315   winRect.extent.y -= mBitmapBounds[topBase + 2].extent.y + mBitmapBounds[BorderBottom].extent.y;
1316   
1317   winRect.extent.x += 1;
1318
1319   GFXDrawUtil* drawUtil = GFX->getDrawUtil();
1320
1321   drawUtil->drawRectFill(winRect, mProfile->mFillColor);
1322
1323   drawUtil->clearBitmapModulation();
1324   drawUtil->drawBitmapSR(mTextureObject, offset, mBitmapBounds[topBase]);
1325   drawUtil->drawBitmapSR(mTextureObject, Point2I(offset.x + getWidth() - mBitmapBounds[topBase+1].extent.x, offset.y),
1326                   mBitmapBounds[topBase + 1]);
1327
1328   RectI destRect;
1329   destRect.point.x = offset.x + mBitmapBounds[topBase].extent.x;
1330   destRect.point.y = offset.y;
1331   destRect.extent.x = getWidth() - mBitmapBounds[topBase].extent.x - mBitmapBounds[topBase + 1].extent.x;
1332   destRect.extent.y = mBitmapBounds[topBase + 2].extent.y;
1333   RectI stretchRect = mBitmapBounds[topBase + 2];
1334   stretchRect.inset(1,0);
1335   drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
1336
1337   destRect.point.x = offset.x;
1338   destRect.point.y = offset.y + mBitmapBounds[topBase].extent.y;
1339   destRect.extent.x = mBitmapBounds[BorderLeft].extent.x;
1340   destRect.extent.y = getHeight() - mBitmapBounds[topBase].extent.y - mBitmapBounds[BorderBottomLeft].extent.y;
1341   stretchRect = mBitmapBounds[BorderLeft];
1342   stretchRect.inset(0,1);
1343   drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
1344
1345   destRect.point.x = offset.x + getWidth() - mBitmapBounds[BorderRight].extent.x;
1346   destRect.extent.x = mBitmapBounds[BorderRight].extent.x;
1347   destRect.point.y = offset.y + mBitmapBounds[topBase + 1].extent.y;
1348   destRect.extent.y = getHeight() - mBitmapBounds[topBase + 1].extent.y - mBitmapBounds[BorderBottomRight].extent.y;
1349
1350   stretchRect = mBitmapBounds[BorderRight];
1351   stretchRect.inset(0,1);
1352   drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
1353
1354   drawUtil->drawBitmapSR(mTextureObject, offset + Point2I(0, getHeight() - mBitmapBounds[BorderBottomLeft].extent.y), mBitmapBounds[BorderBottomLeft]);
1355   drawUtil->drawBitmapSR(mTextureObject, offset + getExtent() - mBitmapBounds[BorderBottomRight].extent, mBitmapBounds[BorderBottomRight]);
1356
1357   destRect.point.x = offset.x + mBitmapBounds[BorderBottomLeft].extent.x;
1358   destRect.extent.x = getWidth() - mBitmapBounds[BorderBottomLeft].extent.x - mBitmapBounds[BorderBottomRight].extent.x;
1359
1360   destRect.point.y = offset.y + getHeight() - mBitmapBounds[BorderBottom].extent.y;
1361   destRect.extent.y = mBitmapBounds[BorderBottom].extent.y;
1362   stretchRect = mBitmapBounds[BorderBottom];
1363   stretchRect.inset(1,0);
1364
1365   drawUtil->drawBitmapStretchSR(mTextureObject, destRect, stretchRect);
1366
1367   // Draw the title
1368   // dhc addition: copied/modded from renderJustifiedText, since we enforce a
1369   // different color usage here. NOTE: it currently CAN overdraw the controls
1370   // if mis-positioned or 'scrunched' in a small width.
1371   drawUtil->setBitmapModulation(mProfile->mFontColor);
1372   S32 textWidth = mProfile->mFont->getStrWidth((const UTF8 *)mText);
1373   Point2I start(0,0);
1374
1375   // Align the horizontal
1376   if ( mProfile->mAlignment == GuiControlProfile::RightJustify )
1377      start.set( winRect.extent.x - textWidth, 0 );
1378   else if ( mProfile->mAlignment == GuiControlProfile::CenterJustify )
1379      start.set( ( winRect.extent.x - textWidth) / 2, 0 );
1380   else // GuiControlProfile::LeftJustify or garbage... ;)
1381      start.set( 0, 0 );
1382   // If the text is longer then the box size, (it'll get clipped) so force Left Justify
1383   if( textWidth > winRect.extent.x ) start.set( 0, 0 );
1384   // center the vertical
1385//   start.y = ( winRect.extent.y - ( font->getHeight() - 2 ) ) / 2;
1386   drawUtil->drawText( mProfile->mFont, start + offset + mProfile->mTextOffset, mText );
1387
1388   // Deal with rendering the titlebar controls
1389   AssertFatal(root, "Unable to get the root GuiCanvas.");
1390
1391   // Draw the close button
1392   Point2I tempUL;
1393   Point2I tempLR;
1394   S32 bmp = BmpStates * BmpClose;
1395
1396   if( mCanClose ) {
1397      if( mCloseButton.pointInRect( mMousePosition ) )
1398      {
1399         if( mCloseButtonPressed )
1400            bmp += BmpDown;
1401         else
1402            bmp += BmpHilite;
1403      }
1404
1405      drawUtil->clearBitmapModulation();
1406      drawUtil->drawBitmapSR(mTextureObject, offset + mCloseButton.point, mBitmapBounds[bmp]);
1407   }
1408
1409   // Draw the maximize button
1410   if( mMaximized )
1411      bmp = BmpStates * BmpNormal;
1412   else
1413      bmp = BmpStates * BmpMaximize;
1414
1415   if( mCanMaximize ) {
1416      if( mMaximizeButton.pointInRect( mMousePosition ) )
1417      {
1418         if( mMaximizeButtonPressed )
1419            bmp += BmpDown;
1420         else
1421            bmp += BmpHilite;
1422      }
1423
1424      drawUtil->clearBitmapModulation();
1425      drawUtil->drawBitmapSR( mTextureObject, offset + mMaximizeButton.point, mBitmapBounds[bmp] );
1426   }
1427
1428   // Draw the minimize button
1429   if( mMinimized )
1430      bmp = BmpStates * BmpNormal;
1431   else
1432      bmp = BmpStates * BmpMinimize;
1433
1434   if( mCanMinimize ) {
1435      if( mMinimizeButton.pointInRect( mMousePosition ) )
1436      {
1437         if( mMinimizeButtonPressed )
1438            bmp += BmpDown;
1439         else
1440            bmp += BmpHilite;
1441      }
1442
1443      drawUtil->clearBitmapModulation();
1444      drawUtil->drawBitmapSR( mTextureObject, offset + mMinimizeButton.point, mBitmapBounds[bmp] );
1445   }
1446
1447   if( !mMinimized )
1448   {
1449      // Render the children
1450      renderChildControls( offset, updateRect );
1451   }
1452}
1453
1454//=============================================================================
1455//    Misc.
1456//=============================================================================
1457// MARK: ---- Misc ----
1458
1459//-----------------------------------------------------------------------------
1460
1461const RectI GuiWindowCtrl::getClientRect()
1462{
1463   if( !mProfile || mProfile->mBitmapArrayRects.size() < NumBitmaps )
1464      return Parent::getClientRect();
1465      
1466   if( !mBitmapBounds )
1467      mBitmapBounds = mProfile->mBitmapArrayRects.address();
1468
1469   RectI winRect;
1470   winRect.point.x = mBitmapBounds[BorderLeft].extent.x;
1471   winRect.point.y = mBitmapBounds[BorderTopKey].extent.y;
1472
1473   winRect.extent.y = getHeight() - ( winRect.point.y  + mBitmapBounds[BorderBottom].extent.y );
1474   winRect.extent.x = getWidth() - ( winRect.point.x  + mBitmapBounds[BorderRight].extent.x );
1475
1476   // Finally, inset it by padding
1477   // Inset by padding.  margin is specified for all t/b/l/r but 
1478   // uses only pointx pointy uniformly on both ends. This should be fixed. - JDD
1479   // winRect.inset( mSizingOptions.mPadding.point.x, mSizingOptions.mPadding.point.y );
1480
1481   return winRect;
1482}
1483
1484//-----------------------------------------------------------------------------
1485
1486bool GuiWindowCtrl::isMinimized(S32 &index)
1487{
1488   index = mMinimizeIndex;
1489   return mMinimized && mVisible;
1490}
1491
1492//-----------------------------------------------------------------------------
1493
1494void GuiWindowCtrl::positionButtons(void)
1495{
1496   if( !mBitmapBounds || !mAwake )
1497      return;
1498
1499   S32 buttonWidth = mBitmapBounds[BmpStates * BmpClose].extent.x;
1500   S32 buttonHeight = mBitmapBounds[BmpStates * BmpClose].extent.y;
1501   Point2I mainOff = mProfile->mTextOffset;
1502
1503   // Until a pref, if alignment is LEFT, put buttons RIGHT justified.
1504   // ELSE, put buttons LEFT justified.
1505   S32 closeLeft = mainOff.x, closeTop = mainOff.y, closeOff = buttonWidth + 2;
1506   if ( mProfile->mAlignment == GuiControlProfile::LeftJustify )
1507   {
1508      closeOff = -closeOff;
1509      closeLeft = getWidth() - buttonWidth - mainOff.x;
1510   }
1511   RectI closeRect(closeLeft, closeTop, buttonHeight, buttonWidth);
1512   mCloseButton = closeRect;
1513
1514   // Always put Minimize on left side of Maximize.
1515   closeRect.point.x += closeOff;
1516   if (closeOff>0)
1517   {
1518      mMinimizeButton = closeRect;
1519      closeRect.point.x += closeOff;
1520      mMaximizeButton = closeRect;
1521   }
1522   else
1523   {
1524      mMaximizeButton = closeRect;
1525      closeRect.point.x += closeOff;
1526      mMinimizeButton = closeRect;
1527   }
1528}
1529
1530//-----------------------------------------------------------------------------
1531
1532void GuiWindowCtrl::setCloseCommand(const char *newCmd)
1533{
1534   if (newCmd)
1535      mCloseCommand = StringTable->insert(newCmd);
1536   else
1537      mCloseCommand = StringTable->EmptyString();
1538}
1539
1540//-----------------------------------------------------------------------------
1541
1542GuiControl* GuiWindowCtrl::findHitControl(const Point2I &pt, S32 initialLayer)
1543{
1544   if (! mMinimized)
1545      return Parent::findHitControl(pt, initialLayer);
1546   else
1547      return this;
1548}
1549
1550//-----------------------------------------------------------------------------
1551
1552bool GuiWindowCtrl::resize(const Point2I &newPosition, const Point2I &newExtent)
1553{
1554   if( !Parent::resize(newPosition, newExtent) )
1555      return false;
1556
1557   // Set the button coords
1558   positionButtons();
1559
1560   return true;
1561}
1562
1563//-----------------------------------------------------------------------------
1564
1565GuiControl *GuiWindowCtrl::findNextTabable(GuiControl *curResponder, bool firstCall)
1566{
1567   // Set the global if this is the first call (directly from the canvas)
1568   if (firstCall)
1569   {
1570      GuiControl::smCurResponder = NULL;
1571   }
1572
1573   // If the window does not already contain the first responder, return false
1574   // ie.  Can't tab into or out of a window
1575   if (! controlIsChild(curResponder))
1576   {
1577      return NULL;
1578   }
1579
1580   // Loop through, checking each child to see if it is the one that follows the firstResponder
1581   GuiControl *tabCtrl = NULL;
1582   iterator i;
1583   for (i = begin(); i != end(); i++)
1584   {
1585      GuiControl *ctrl = static_cast<GuiControl *>(*i);
1586      tabCtrl = ctrl->findNextTabable(curResponder, false);
1587      if (tabCtrl) break;
1588   }
1589
1590   // To ensure the tab cycles within the current window...
1591   if (! tabCtrl)
1592   {
1593      tabCtrl = findFirstTabable();
1594   }
1595
1596   mFirstResponder = tabCtrl;
1597   return tabCtrl;
1598}
1599
1600//-----------------------------------------------------------------------------
1601
1602GuiControl *GuiWindowCtrl::findPrevTabable(GuiControl *curResponder, bool firstCall)
1603{
1604   if (firstCall)
1605   {
1606      GuiControl::smPrevResponder = NULL;
1607   }
1608
1609   // If the window does not already contain the first responder, return false
1610   // ie.  Can't tab into or out of a window
1611   if (! controlIsChild(curResponder))
1612   {
1613      return NULL;
1614   }
1615
1616   // Loop through, checking each child to see if it is the one that follows the firstResponder
1617   GuiControl *tabCtrl = NULL;
1618   iterator i;
1619   for (i = begin(); i != end(); i++)
1620   {
1621      GuiControl *ctrl = static_cast<GuiControl *>(*i);
1622      tabCtrl = ctrl->findPrevTabable(curResponder, false);
1623      if (tabCtrl) break;
1624   }
1625
1626   // To ensure the tab cycles within the current window...
1627   if (! tabCtrl)
1628   {
1629      tabCtrl = findLastTabable();
1630   }
1631
1632   mFirstResponder = tabCtrl;
1633   return tabCtrl;
1634}
1635
1636//-----------------------------------------------------------------------------
1637
1638void GuiWindowCtrl::selectWindow(void)
1639{
1640   // First make sure this window is the front most of its siblings
1641   GuiControl *parent = getParent();
1642   if (parent && *parent->end() != this )
1643   {
1644      // Valid collapse groups have to be selected together
1645      if( mCanCollapse && mCollapseGroup >= 0 )
1646      {
1647         CollapseGroupNumVec::iterator iter = parent->mCollapseGroupVec[mCollapseGroup].begin();
1648         for(; iter != parent->mCollapseGroupVec[mCollapseGroup].end(); iter++ )
1649         {
1650            parent->pushObjectToBack( (*iter) );
1651         }
1652      }
1653      else
1654      {
1655         parent->pushObjectToBack( this );
1656      }
1657   }
1658
1659   // Also set the first responder to be the one within this window
1660   setFirstResponder(mFirstResponder);
1661}
1662
1663//-----------------------------------------------------------------------------
1664
1665void GuiWindowCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
1666{
1667   GuiCanvas *pRoot = getRoot();
1668   if( !pRoot )
1669      return;
1670   PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow();
1671   AssertFatal(pWindow != NULL,"GuiControl without owning platform window!  This should not be possible.");
1672   PlatformCursorController *pController = pWindow->getCursorController();
1673   AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");
1674
1675   S32 desiredCursor = PlatformCursorController::curArrow;
1676   S32 hitEdges = findHitEdges( lastGuiEvent.mousePoint );
1677
1678   if( hitEdges & edgeBottom && hitEdges & edgeLeft && mResizeHeight )
1679      desiredCursor = PlatformCursorController::curResizeNESW;
1680   else if( hitEdges & edgeBottom && hitEdges & edgeRight && mResizeHeight  )
1681      desiredCursor = PlatformCursorController::curResizeNWSE;
1682   else if( hitEdges & edgeBottom && mResizeHeight )
1683      desiredCursor = PlatformCursorController::curResizeHorz;
1684   else if( hitEdges & edgeTop && hitEdges & edgeLeft && mResizeHeight )
1685      desiredCursor = PlatformCursorController::curResizeNWSE;
1686   else if( hitEdges & edgeTop && hitEdges & edgeRight && mResizeHeight )
1687      desiredCursor = PlatformCursorController::curResizeNESW;
1688   else if( hitEdges & edgeTop && mResizeHeight )
1689      desiredCursor = PlatformCursorController::curResizeHorz;
1690   else if ( hitEdges & edgeLeft && mResizeWidth )
1691      desiredCursor = PlatformCursorController::curResizeVert;
1692   else if( hitEdges & edgeRight && mResizeWidth )
1693      desiredCursor = PlatformCursorController::curResizeVert;
1694   else
1695      desiredCursor = PlatformCursorController::curArrow;
1696
1697   // Bail if we're already at the desired cursor
1698   if(pRoot->mCursorChanged == desiredCursor )
1699      return;
1700
1701   // Now change the cursor shape
1702   pController->popCursor();
1703   pController->pushCursor(desiredCursor);
1704   pRoot->mCursorChanged = desiredCursor;
1705}
1706
1707//-----------------------------------------------------------------------------
1708
1709void GuiWindowCtrl::parentResized(const RectI &oldParentRect, const RectI &newParentRect)
1710{
1711   if(!mCanResize)
1712      return;
1713
1714   GuiControl *parent = getParent();
1715   if( !parent )
1716      return;
1717
1718   // Bail if were not sized both by windowrelative
1719   if( mHorizSizing != horizResizeWindowRelative || mHorizSizing != vertResizeWindowRelative )
1720      return Parent::parentResized( oldParentRect, newParentRect );
1721
1722   Point2I newPosition = getPosition();
1723   Point2I newExtent = getExtent();
1724         
1725   bool doCollapse = false;
1726
1727   S32 deltaX = newParentRect.extent.x - oldParentRect.extent.x;
1728   S32 deltaY = newParentRect.extent.y - oldParentRect.extent.y;// + mProfile->mYPositionOffset;
1729
1730   if( newPosition.x > ( oldParentRect.extent.x / 2 ) )
1731      newPosition.x = newPosition.x + deltaX;
1732
1733   if (oldParentRect.extent.y != 0)
1734   {
1735      // Only if were apart of a group
1736      if ( mCanCollapse && mCollapseGroup >= 0 )
1737      {
1738         // Setup parsing mechanisms
1739         CollapseGroupNumVec collapseGroupNumVec;
1740         
1741         // Lets grab the information we need (this should probably be already stored on each individual window object)
1742         S32 groupNum = mCollapseGroup;
1743         S32 groupMax = parent->mCollapseGroupVec[ groupNum ].size() - 1;
1744
1745         // Set up vars that we're going to be using
1746         S32 groupPos = 0;
1747         S32 groupExtent = 0;
1748         S32 tempGroupExtent = 0;
1749
1750         // Set up the vector/iterator combo we need
1751         collapseGroupNumVec = parent->mCollapseGroupVec[ groupNum ];
1752         CollapseGroupNumVec::iterator iter = collapseGroupNumVec.begin();
1753
1754         // Grab some more information we need later ( this info should also be located on each ind. window object )
1755         for( ; iter != collapseGroupNumVec.end(); iter++ )
1756         {
1757            if((*iter)->getCollapseGroupNum() == 0)
1758            {
1759               groupPos = (*iter)->getPosition().y;
1760            }
1761
1762            groupExtent += (*iter)->getExtent().y;
1763         }
1764         
1765         // Use the information we just gatherered; only enter this block if we need to
1766         tempGroupExtent = groupPos + groupExtent;
1767         if( tempGroupExtent > ( newParentRect.extent.y / 2 ) )
1768         {
1769            // Lets size the collapse group
1770            S32 windowParser = groupMax;
1771            bool secondLoop = false;
1772            while( tempGroupExtent >= newParentRect.extent.y )
1773            {
1774               
1775               if( windowParser == -1)
1776               {
1777                  if( !secondLoop )
1778                  {
1779                     secondLoop = true;
1780                     windowParser = groupMax;
1781                  }
1782                  else
1783                     break;
1784               }
1785
1786               GuiWindowCtrl *tempWindow = collapseGroupNumVec[windowParser];
1787               if(tempWindow->mIsCollapsed)
1788               {
1789                  windowParser--;
1790                  continue;
1791               }
1792               
1793               // Resizing block for the loop... if we can get away with just resizing the bottom window do it before
1794               // resizing the whole block. else, go through the group and start setting min extents. if that doesnt work
1795               // on the second go around, start collpsing the windows.
1796               if( tempGroupExtent - ( tempWindow->getExtent().y - tempWindow->getMinExtent().y ) <= newParentRect.extent.y )
1797               {
1798                  if( this == tempWindow )
1799                     newExtent.y = newExtent.y - ( tempGroupExtent - newParentRect.extent.y );
1800                     
1801                  tempGroupExtent = tempGroupExtent - newParentRect.extent.y;
1802               }
1803               else
1804               {
1805                  if( secondLoop )
1806                  {
1807                     tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y + 32);
1808
1809                     if( this == tempWindow )
1810                        doCollapse = true;
1811                  }
1812                  else
1813                  {
1814                     tempGroupExtent = tempGroupExtent - (tempWindow->getExtent().y - tempWindow->getMinExtent().y);
1815                     if( this == tempWindow )
1816                        newExtent.y = tempWindow->getMinExtent().y;
1817                  }
1818               }
1819               windowParser--;
1820            }
1821         }
1822      }
1823      else if( newPosition.y > ( oldParentRect.extent.y / 2 ) )
1824      {
1825         newPosition.y = newPosition.y + deltaY - 1;
1826      }
1827   }
1828
1829   if( newExtent.x >= getMinExtent().x && newExtent.y >= getMinExtent().y )
1830   {
1831      // If we are already outside the reach of the main window, lets not place ourselves
1832      // further out; but if were trying to improve visibility, go for it
1833      // note: small tolerance (4) added to keep guis that are very slightly outside
1834      // the main window (like all of the editor windows!) from appearing to 'undock'
1835      // when the resolution is changed.
1836      if( (newPosition.x + newExtent.x) > newParentRect.extent.x + 4 )
1837      {
1838         if( (newPosition.x + newExtent.x) > (getPosition().x + getExtent().x) )
1839         { 
1840            newPosition.x = getPosition().x;
1841            newExtent.x = getExtent().x;
1842         }
1843      }
1844      if( (newPosition.y + newExtent.y) > newParentRect.extent.y + 4)
1845      {
1846         if( (newPosition.y + newExtent.y) > (getPosition().y + getExtent().y) )
1847         {
1848            newPosition.y = getPosition().y;
1849            newExtent.y = getExtent().y;
1850         }
1851      }
1852
1853      // Only for collpasing groups, if were not, then do it like normal windows
1854      if( mCanCollapse && mCollapseGroup >= 0 )
1855      {  
1856         bool resizeMe = false;
1857         
1858         // Only the group window should control positioning
1859         if( mCollapseGroupNum == 0 )
1860         {
1861            resizeMe = resizeCollapseGroup( true, true, (getPosition() - newPosition), (getExtent() - newExtent) );
1862            if(resizeMe == true)
1863               resize(newPosition, newExtent);
1864         }
1865         else if( getExtent() != newExtent)
1866         {
1867            resizeMe = resizeCollapseGroup( false, true, (getPosition() - newPosition), (getExtent() - newExtent) );
1868            if(resizeMe == true)
1869               resize(getPosition(), newExtent);
1870         }
1871      }
1872      else
1873      {
1874         resize(newPosition, newExtent);
1875      }
1876   }
1877
1878   if( mCanCollapse && !mIsCollapsed && doCollapse )
1879      toggleCollapseGroup();
1880
1881   // If docking is invalid on this control, then bail out here
1882   if( getDocking() & Docking::dockInvalid || getDocking() & Docking::dockNone)
1883      return;
1884
1885   // Update Self
1886   RectI oldThisRect = getBounds();
1887   anchorControl( this, Point2I( deltaX, deltaY ) );
1888   RectI newThisRect = getBounds();
1889
1890   // Update Deltas to pass on to children
1891   deltaX = newThisRect.extent.x - oldThisRect.extent.x;
1892   deltaY = newThisRect.extent.y - oldThisRect.extent.y;
1893
1894   // Iterate over all children and update their anchors
1895   iterator nI = begin();
1896   for( ; nI != end(); nI++ )
1897   {
1898      // Sanity
1899      GuiControl *control = dynamic_cast<GuiControl*>( (*nI) );
1900      if( control )
1901         control->parentResized( oldThisRect, newThisRect );
1902   }
1903}
1904
1905//=============================================================================
1906//    Console Methods.
1907//=============================================================================
1908// MARK: ---- Console Methods ----
1909
1910//-----------------------------------------------------------------------------
1911
1912DefineEngineMethod( GuiWindowCtrl, selectWindow, void, (),,
1913   "Bring the window to the front." )
1914{
1915   object->selectWindow();
1916}
1917
1918//-----------------------------------------------------------------------------
1919
1920DefineEngineMethod( GuiWindowCtrl, setCollapseGroup, void, ( bool state ),,
1921   "Set the window's collapsing state." )
1922{
1923   object->setCollapseGroup(state);
1924}
1925
1926//-----------------------------------------------------------------------------
1927
1928DefineEngineMethod( GuiWindowCtrl, toggleCollapseGroup, void, (),,
1929   "Toggle the window collapsing." )
1930{
1931   object->toggleCollapseGroup();
1932}
1933
1934//-----------------------------------------------------------------------------
1935
1936DefineEngineMethod( GuiWindowCtrl, attachTo, void, ( GuiWindowCtrl* window ),,
1937   "" )
1938{
1939   object->moveToCollapseGroup( window, 1 );
1940}
1941
1942//-----------------------------------------------------------------------------
1943
1944DefineEngineStaticMethod( GuiWindowCtrl, attach, void, ( GuiWindowCtrl* bottomWindow, GuiWindowCtrl* topWindow ),,
1945   "Attach @a bottomWindow to @topWindow so that @a bottomWindow moves along with @a topWindow when it is dragged.\n\n"
1946   "@param bottomWindow \n"
1947   "@param topWindow " )
1948{   
1949   if(bottomWindow == NULL || topWindow == NULL)
1950   {
1951      Con::warnf("Warning: AttachWindows - could not find windows");
1952      return;
1953   }
1954
1955   bottomWindow->moveToCollapseGroup( topWindow, 1 );
1956}
1957