Torque3D Documentation / _generateds / guiEditCtrl.cpp

guiEditCtrl.cpp

Engine/source/gui/editor/guiEditCtrl.cpp

More...

Classes:

Public Variables

Public Functions

ConsoleDocClass(GuiEditCtrl , "@brief Native side of the GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editor.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
ConsoleDocClass(GuiEditorRuler , "@brief Visual representation of markers on top and left sides of GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )
DefineEngineMethod(GuiEditCtrl , addNewCtrl , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , addSelection , void , (S32 id) , "selects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )
DefineEngineMethod(GuiEditCtrl , bringToFront , void , () , "" )
DefineEngineMethod(GuiEditCtrl , clearGuides , void , (S32 axis) , (-1) , "( [ int axis ] ) - Clear all currently set guide lines." )
DefineEngineMethod(GuiEditCtrl , clearSelection , void , () , "Clear selected controls list." )
DefineEngineMethod(GuiEditCtrl , deleteSelection , void , () , "() - Delete the selected controls." )
DefineEngineMethod(GuiEditCtrl , fitIntoParents , void , (bool width, bool height) , (true, true) , "( bool width=true, bool height=true ) - Fit selected controls into their parents." )
DefineEngineMethod(GuiEditCtrl , getContentControl , S32 , () , "() - Return the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> edited inside the GUI editor." )
DefineEngineMethod(GuiEditCtrl , getCurrentAddSet , S32 , () , "Returns the set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> which <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> controls will be added" )
DefineEngineMethod(GuiEditCtrl , getMouseMode , const char * , () , "() - Return the current mouse mode." )
DefineEngineMethod(GuiEditCtrl , getNumSelected , S32 , () , "() - Return the number of controls currently selected." )
DefineEngineMethod(GuiEditCtrl , getSelection , SimSet * , () , "Gets the set of GUI controls currently selected in the editor." )
DefineEngineMethod(GuiEditCtrl , getSelectionGlobalBounds , const char * , () , "() - Returns global bounds of current selection as vector 'x y width height'." )
DefineEngineMethod(GuiEditCtrl , getTrash , SimGroup * , () , "Gets the GUI controls(s) that are currently in the trash." )
DefineEngineMethod(GuiEditCtrl , justify , void , (U32 mode) , "(int <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a>)" )
DefineEngineMethod(GuiEditCtrl , loadSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Load selection from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )
DefineEngineMethod(GuiEditCtrl , moveSelection , void , (S32 dx, S32 dy) , "Move all controls in the selection by (dx,dy) pixels." )
DefineEngineMethod(GuiEditCtrl , pushToBack , void , () , "" )
DefineEngineMethod(GuiEditCtrl , readGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." )
DefineEngineMethod(GuiEditCtrl , removeSelection , void , (S32 id) , "deselects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )
DefineEngineMethod(GuiEditCtrl , saveSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Save selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )
DefineEngineMethod(GuiEditCtrl , select , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , selectAll , void , () , "()" )
DefineEngineMethod(GuiEditCtrl , selectChildren , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select children of currently selected controls." )
DefineEngineMethod(GuiEditCtrl , selectParents , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select parents of currently selected controls." )
DefineEngineMethod(GuiEditCtrl , setContentControl , void , (GuiControl *ctrl) , "( GuiControl ctrl ) - Set the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> edit in the GUI editor." )
DefineEngineMethod(GuiEditCtrl , setCurrentAddSet , void , (GuiControl *addSet) , "(GuiControl ctrl)" )
DefineEngineMethod(GuiEditCtrl , setSnapToGrid , void , (U32 gridsize) , "GuiEditCtrl.setSnapToGrid(gridsize)" )
DefineEngineMethod(GuiEditCtrl , toggle , void , () , "Toggle activation." )
DefineEngineMethod(GuiEditCtrl , writeGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Write the guides <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the given control." )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrl , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrlSet , void , (SimSet *set) , (set) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onClearSelected , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPostApply , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPreApply , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onDelete , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onFitIntoParent , void , (bool width, bool height) , (width, height) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onHierarchyChanged , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onMouseModeChange , void , () , () , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPostEdit , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPostSelectionNudged , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPreEdit , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onPreSelectionNudged , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onRemoveSelected , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionCloned , void , (SimSet *selection) , (selection) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionMoved , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionResized , void , (GuiControl *control) , (control) , "" )
IMPLEMENT_CALLBACK(GuiEditCtrl , onTrashSelection , void , (SimSet *selection) , (selection) , "" )
snapPoint(Point2I point, Point2I delta, Point2I gridSnap)

Detailed Description

Public Variables

GuiControl * control 
 onSelect 
 void 

Public Functions

ConsoleDocClass(GuiEditCtrl , "@brief Native side of the GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">editor.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )

ConsoleDocClass(GuiEditorRuler , "@brief Visual representation of markers on top and left sides of GUI <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">Editor\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )

DefineEngineMethod(GuiEditCtrl , addNewCtrl , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )

DefineEngineMethod(GuiEditCtrl , addSelection , void , (S32 id) , "selects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )

DefineEngineMethod(GuiEditCtrl , bringToFront , void , () , "" )

DefineEngineMethod(GuiEditCtrl , clearGuides , void , (S32 axis) , (-1) , "( [ int axis ] ) - Clear all currently set guide lines." )

DefineEngineMethod(GuiEditCtrl , clearSelection , void , () , "Clear selected controls list." )

DefineEngineMethod(GuiEditCtrl , deleteSelection , void , () , "() - Delete the selected controls." )

DefineEngineMethod(GuiEditCtrl , fitIntoParents , void , (bool width, bool height) , (true, true) , "( bool width=true, bool height=true ) - Fit selected controls into their parents." )

DefineEngineMethod(GuiEditCtrl , getContentControl , S32 , () , "() - Return the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> edited inside the GUI editor." )

DefineEngineMethod(GuiEditCtrl , getCurrentAddSet , S32 , () , "Returns the set <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> which <a href="/coding/file/tmm__on_8h/#tmm__on_8h_1a1ac41480eb2e4aadd52252ee550b630a">new</a> controls will be added" )

DefineEngineMethod(GuiEditCtrl , getMouseMode , const char * , () , "() - Return the current mouse mode." )

DefineEngineMethod(GuiEditCtrl , getNumSelected , S32 , () , "() - Return the number of controls currently selected." )

DefineEngineMethod(GuiEditCtrl , getSelection , SimSet * , () , "Gets the set of GUI controls currently selected in the editor." )

DefineEngineMethod(GuiEditCtrl , getSelectionGlobalBounds , const char * , () , "() - Returns global bounds of current selection as vector 'x y width height'." )

DefineEngineMethod(GuiEditCtrl , getTrash , SimGroup * , () , "Gets the GUI controls(s) that are currently in the trash." )

DefineEngineMethod(GuiEditCtrl , justify , void , (U32 mode) , "(int <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a>)" )

DefineEngineMethod(GuiEditCtrl , loadSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Load selection from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )

DefineEngineMethod(GuiEditCtrl , moveSelection , void , (S32 dx, S32 dy) , "Move all controls in the selection by (dx,dy) pixels." )

DefineEngineMethod(GuiEditCtrl , pushToBack , void , () , "" )

DefineEngineMethod(GuiEditCtrl , readGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." )

DefineEngineMethod(GuiEditCtrl , removeSelection , void , (S32 id) , "deselects <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> control." )

DefineEngineMethod(GuiEditCtrl , saveSelection , void , (const char *filename) , (nullAsType< const char * >()) , "( string fileName=null ) - Save selection <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )

DefineEngineMethod(GuiEditCtrl , select , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )

DefineEngineMethod(GuiEditCtrl , selectAll , void , () , "()" )

DefineEngineMethod(GuiEditCtrl , selectChildren , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select children of currently selected controls." )

DefineEngineMethod(GuiEditCtrl , selectParents , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select parents of currently selected controls." )

DefineEngineMethod(GuiEditCtrl , setContentControl , void , (GuiControl *ctrl) , "( GuiControl ctrl ) - Set the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> edit in the GUI editor." )

DefineEngineMethod(GuiEditCtrl , setCurrentAddSet , void , (GuiControl *addSet) , "(GuiControl ctrl)" )

DefineEngineMethod(GuiEditCtrl , setSnapToGrid , void , (U32 gridsize) , "GuiEditCtrl.setSnapToGrid(gridsize)" )

DefineEngineMethod(GuiEditCtrl , toggle , void , () , "Toggle activation." )

DefineEngineMethod(GuiEditCtrl , writeGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Write the guides <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the given control." )

IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrl , void , (GuiControl *control) , (control) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onAddNewCtrlSet , void , (SimSet *set) , (set) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onAddSelected , void , (GuiControl *control) , (control) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onClearSelected , void , () , () , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPostApply , void , (GuiControl *control) , (control) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onControlInspectPreApply , void , (GuiControl *control) , (control) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onDelete , void , () , () , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onFitIntoParent , void , (bool width, bool height) , (width, height) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onHierarchyChanged , void , () , () , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onMouseModeChange , void , () , () , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onPostEdit , void , (SimSet *selection) , (selection) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onPostSelectionNudged , void , (SimSet *selection) , (selection) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onPreEdit , void , (SimSet *selection) , (selection) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onPreSelectionNudged , void , (SimSet *selection) , (selection) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onRemoveSelected , void , (GuiControl *control) , (control) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionCloned , void , (SimSet *selection) , (selection) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionMoved , void , (GuiControl *control) , (control) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onSelectionResized , void , (GuiControl *control) , (control) , "" )

IMPLEMENT_CALLBACK(GuiEditCtrl , onTrashSelection , void , (SimSet *selection) , (selection) , "" )

IMPLEMENT_CONOBJECT(GuiEditCtrl )

IMPLEMENT_CONOBJECT(GuiEditorRuler )

snapPoint(Point2I point, Point2I delta, Point2I gridSnap)

   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/editor/guiEditCtrl.h"
  26
  27#include "core/frameAllocator.h"
  28#include "core/stream/fileStream.h"
  29#include "core/stream/memStream.h"
  30#include "console/consoleTypes.h"
  31#include "gui/core/guiCanvas.h"
  32#include "gui/containers/guiScrollCtrl.h"
  33#include "core/strings/stringUnit.h"
  34#include "console/engineAPI.h"
  35
  36
  37IMPLEMENT_CONOBJECT( GuiEditCtrl );
  38
  39ConsoleDocClass( GuiEditCtrl,
  40   "@brief Native side of the GUI editor.\n\n"
  41   "Editor use only.\n\n"
  42   "@internal"
  43);
  44
  45IMPLEMENT_CALLBACK( GuiEditCtrl, onHierarchyChanged, void, (), (),
  46   "" );
  47IMPLEMENT_CALLBACK( GuiEditCtrl, onDelete, void, (), (),
  48   "" );
  49IMPLEMENT_CALLBACK( GuiEditCtrl, onPreEdit, void, ( SimSet* selection ), ( selection ),
  50   "" );
  51IMPLEMENT_CALLBACK( GuiEditCtrl, onPostEdit, void, ( SimSet* selection ), ( selection ),
  52   "" );
  53IMPLEMENT_CALLBACK( GuiEditCtrl, onClearSelected, void, (), (),
  54   "" )
  55IMPLEMENT_CALLBACK( GuiEditCtrl, onSelect, void, ( GuiControl* control ), ( control ),
  56   "" );
  57IMPLEMENT_CALLBACK( GuiEditCtrl, onAddSelected, void, ( GuiControl* control ), ( control ),
  58   "" );
  59IMPLEMENT_CALLBACK( GuiEditCtrl, onRemoveSelected, void, ( GuiControl* control ), ( control ),
  60   "" );
  61IMPLEMENT_CALLBACK( GuiEditCtrl, onPreSelectionNudged, void, ( SimSet* selection ), ( selection ),
  62   "" );
  63IMPLEMENT_CALLBACK( GuiEditCtrl, onPostSelectionNudged, void, ( SimSet* selection ), ( selection ),
  64   "" );
  65IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionMoved, void, ( GuiControl* control ), ( control ),
  66   "" );
  67IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionCloned, void, ( SimSet* selection ), ( selection ),
  68   "" );
  69IMPLEMENT_CALLBACK( GuiEditCtrl, onTrashSelection, void, ( SimSet* selection ), ( selection ),
  70   "" );
  71IMPLEMENT_CALLBACK( GuiEditCtrl, onAddNewCtrl, void, ( GuiControl* control ), ( control ),   
  72   "" );
  73IMPLEMENT_CALLBACK( GuiEditCtrl, onAddNewCtrlSet, void, ( SimSet* set ), ( set ),
  74   "" );
  75IMPLEMENT_CALLBACK( GuiEditCtrl, onSelectionResized, void, ( GuiControl* control ), ( control ),
  76   "" );
  77IMPLEMENT_CALLBACK( GuiEditCtrl, onFitIntoParent, void, ( bool width, bool height ), ( width, height ),
  78   "" );
  79IMPLEMENT_CALLBACK( GuiEditCtrl, onMouseModeChange, void, (), (),
  80   "" );
  81IMPLEMENT_CALLBACK( GuiEditCtrl, onControlInspectPreApply, void, ( GuiControl* control ), ( control ),
  82   "" );
  83IMPLEMENT_CALLBACK( GuiEditCtrl, onControlInspectPostApply, void, ( GuiControl* control ), ( control ),
  84   "" );
  85
  86
  87StringTableEntry GuiEditCtrl::smGuidesPropertyName[ 2 ];
  88
  89
  90//-----------------------------------------------------------------------------
  91
  92GuiEditCtrl::GuiEditCtrl()
  93   : mCurrentAddSet( NULL ),
  94     mContentControl( NULL ),
  95     mGridSnap( 0, 0 ),
  96     mDragBeginPoint( -1, -1 ),
  97     mSnapToControls( true ),
  98     mSnapToEdges( true ),
  99     mSnapToGuides( true ),
 100     mSnapToCenters( true ),
 101     mSnapToCanvas( true ),
 102     mDrawBorderLines( true ),
 103     mFullBoxSelection( false ),
 104     mSnapSensitivity( 2 ),
 105     mDrawGuides( true ),
 106     mDragAddSelection(false),
 107     mDragMoveUndo(false)
 108{
 109   VECTOR_SET_ASSOCIATION( mSelectedControls );
 110   VECTOR_SET_ASSOCIATION( mDragBeginPoints );
 111   VECTOR_SET_ASSOCIATION( mSnapHits[ 0 ] );
 112   VECTOR_SET_ASSOCIATION( mSnapHits[ 1 ] );
 113      
 114   mActive = true;
 115   mDotSB = NULL;
 116   
 117   mSnapped[ SnapVertical ] = false;
 118   mSnapped[ SnapHorizontal ] = false;
 119
 120   mDragGuide[ GuideVertical ] = false;
 121   mDragGuide[ GuideHorizontal ] = false;
 122   mDragGuideIndex[0] = 0;
 123   mDragGuideIndex[1] = 1;
 124
 125   std::fill_n(mSnapOffset, 2, 0);
 126   std::fill_n(mSnapEdge, 2, SnapEdgeMin);
 127   
 128   if( !smGuidesPropertyName[ GuideVertical ] )
 129      smGuidesPropertyName[ GuideVertical ] = StringTable->insert( "guidesVertical" );
 130   if( !smGuidesPropertyName[ GuideHorizontal ] )
 131      smGuidesPropertyName[ GuideHorizontal ] = StringTable->insert( "guidesHorizontal" );
 132
 133   mTrash = NULL;
 134   mSelectedSet = NULL;
 135   mMouseDownMode = GuiEditCtrl::Selecting;
 136   mSizingMode = GuiEditCtrl::sizingNone;
 137}
 138
 139//-----------------------------------------------------------------------------
 140
 141void GuiEditCtrl::initPersistFields()
 142{
 143   addGroup( "Snapping" );
 144   addField( "snapToControls",      TypeBool,   Offset( mSnapToControls, GuiEditCtrl ),
 145      "If true, edge and center snapping will work against controls." );
 146   addField( "snapToGuides",        TypeBool,   Offset( mSnapToGuides, GuiEditCtrl ),
 147      "If true, edge and center snapping will work against guides." );
 148   addField( "snapToCanvas",        TypeBool,   Offset( mSnapToCanvas, GuiEditCtrl ),
 149      "If true, edge and center snapping will work against canvas (toplevel control)." );
 150   addField( "snapToEdges",         TypeBool,   Offset( mSnapToEdges, GuiEditCtrl ),
 151      "If true, selection edges will snap into alignment when moved or resized." );
 152   addField( "snapToCenters",       TypeBool,   Offset( mSnapToCenters, GuiEditCtrl ),
 153      "If true, selection centers will snap into alignment when moved or resized." );
 154   addField( "snapSensitivity",     TypeS32,    Offset( mSnapSensitivity, GuiEditCtrl ),
 155      "Distance in pixels that edge and center snapping will work across." );
 156   endGroup( "Snapping" );
 157   
 158   addGroup( "Selection" );
 159   addField( "fullBoxSelection",    TypeBool,   Offset( mFullBoxSelection, GuiEditCtrl ),
 160      "If true, rectangle selection will only select controls fully inside the drag rectangle." );
 161   endGroup( "Selection" );
 162   
 163   addGroup( "Rendering" );
 164   addField( "drawBorderLines",  TypeBool,   Offset( mDrawBorderLines, GuiEditCtrl ),
 165      "If true, lines will be drawn extending along the edges of selected objects." );
 166   addField( "drawGuides", TypeBool, Offset( mDrawGuides, GuiEditCtrl ),
 167      "If true, guides will be included in rendering." );
 168   endGroup( "Rendering" );
 169
 170   Parent::initPersistFields();
 171}
 172
 173//=============================================================================
 174//    Events.
 175//=============================================================================
 176// MARK: ---- Events ----
 177
 178//-----------------------------------------------------------------------------
 179
 180bool GuiEditCtrl::onAdd()
 181{
 182   if( !Parent::onAdd() )
 183      return false;
 184      
 185   mTrash = new SimGroup();
 186   mSelectedSet = new SimSet();
 187      
 188   if( !mTrash->registerObject() )
 189      return false;
 190   if( !mSelectedSet->registerObject() )
 191      return false;
 192
 193   return true;
 194}
 195
 196//-----------------------------------------------------------------------------
 197
 198void GuiEditCtrl::onRemove()
 199{
 200   Parent::onRemove();
 201   
 202   mDotSB = NULL;
 203   
 204   mTrash->deleteObject();
 205   mSelectedSet->deleteObject();
 206   
 207   mTrash = NULL;
 208   mSelectedSet = NULL;
 209}
 210
 211//-----------------------------------------------------------------------------
 212
 213bool GuiEditCtrl::onWake()
 214{
 215   if (! Parent::onWake())
 216      return false;
 217
 218   // Set GUI Controls to DesignTime mode
 219   GuiControl::smDesignTime = true;
 220   GuiControl::smEditorHandle = this;
 221
 222   setEditMode(true);
 223
 224   return true;
 225}
 226
 227//-----------------------------------------------------------------------------
 228
 229void GuiEditCtrl::onSleep()
 230{
 231   // Set GUI Controls to run time mode
 232   GuiControl::smDesignTime = false;
 233   GuiControl::smEditorHandle = NULL;
 234
 235   Parent::onSleep();
 236}
 237
 238//-----------------------------------------------------------------------------
 239
 240bool GuiEditCtrl::onKeyDown(const GuiEvent &event)
 241{
 242   if (! mActive)
 243      return Parent::onKeyDown(event);
 244
 245   if (!(event.modifier & SI_PRIMARY_CTRL))
 246   {
 247      switch(event.keyCode)
 248      {
 249         case KEY_BACKSPACE:
 250         case KEY_DELETE:
 251            deleteSelection();
 252            onDelete_callback();
 253            return true;
 254         default:
 255            break;
 256      }
 257   }
 258   return false;
 259}
 260
 261//-----------------------------------------------------------------------------
 262
 263void GuiEditCtrl::onMouseDown(const GuiEvent &event)
 264{
 265   if (! mActive)
 266   {
 267      Parent::onMouseDown(event);
 268      return;
 269   }
 270   if(!mContentControl)
 271      return;
 272
 273   setFirstResponder();
 274   mouseLock();
 275
 276   mLastMousePos = globalToLocalCoord( event.mousePoint );
 277
 278   // Check whether we've hit a guide.  If so, start a guide drag.
 279   // Don't do this if SHIFT is down.
 280   
 281   if( !( event.modifier & SI_SHIFT ) )
 282   {
 283      for( U32 axis = 0; axis < 2; ++ axis )
 284      {
 285         const S32 guide = findGuide( ( guideAxis ) axis, event.mousePoint, 1 );
 286         if( guide != -1 )
 287         {
 288            setMouseMode( DragGuide );
 289            
 290            mDragGuide[ axis ] = true;
 291            mDragGuideIndex[ axis ] = guide;
 292         }
 293      }
 294            
 295      if( mMouseDownMode == DragGuide )
 296         return;
 297   }
 298   
 299   // Check whether we have hit a sizing knob on any of the currently selected
 300   // controls.
 301   
 302   for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i )
 303   {
 304      GuiControl* ctrl = mSelectedControls[ i ];
 305      
 306      Point2I cext = ctrl->getExtent();
 307      Point2I ctOffset = globalToLocalCoord( ctrl->localToGlobalCoord( Point2I( 0, 0 ) ) );
 308      
 309      RectI box( ctOffset.x, ctOffset.y, cext.x, cext.y );
 310
 311      if( ( mSizingMode = ( GuiEditCtrl::sizingModes ) getSizingHitKnobs( mLastMousePos, box ) ) != 0 )
 312      {
 313         setMouseMode( SizingSelection );
 314         mLastDragPos = event.mousePoint;
 315         
 316         // undo
 317         onPreEdit_callback( getSelectedSet() );
 318         return;
 319      }
 320   }
 321
 322   // Find the control we have hit.
 323   
 324   GuiControl* ctrl = mContentControl->findHitControl( mLastMousePos, getCurrentAddSet()->mLayer );
 325
 326   // Give the control itself the opportunity to handle the event
 327   // to implement custom editing logic.
 328
 329   bool handledEvent = ctrl->onMouseDownEditor( event, localToGlobalCoord( Point2I(0,0) ) );
 330   if( handledEvent == true )
 331   {
 332      // The Control handled the event and requested the edit ctrl
 333      // *NOT* act on it.
 334      return;
 335   }
 336   else if( event.modifier & SI_SHIFT )
 337   {
 338      // Shift is down.  Start rectangle selection in add mode
 339      // no matter what we have hit.
 340      
 341      startDragRectangle( event.mousePoint );
 342      mDragAddSelection = true;
 343   }
 344   else if( selectionContains( ctrl ) )
 345   {
 346      // We hit a selected control.  If the multiselect key is pressed,
 347      // deselect the control.  Otherwise start a drag move.
 348      
 349      if( event.modifier & SI_MULTISELECT )
 350      {
 351         removeSelection( ctrl );
 352
 353         //set the mode
 354         setMouseMode( Selecting );
 355      }
 356      else if( event.modifier & SI_PRIMARY_ALT )
 357      {
 358         // Alt is down.  Start a drag clone.
 359         
 360         startDragClone( event.mousePoint );
 361      }
 362      else
 363      {
 364         startDragMove( event.mousePoint );
 365      }
 366   }
 367   else
 368   {
 369      // We clicked an unselected control.
 370      
 371      if( ctrl == getContentControl() )
 372      {
 373         // Clicked in toplevel control.  Start a rectangle selection.
 374         
 375         startDragRectangle( event.mousePoint );
 376         mDragAddSelection = false;
 377      }
 378      else if( event.modifier & SI_PRIMARY_ALT && ctrl != getContentControl() )
 379      {
 380         // Alt is down.  Start a drag clone.
 381         
 382         clearSelection();
 383         addSelection( ctrl );
 384         
 385         startDragClone( event.mousePoint );
 386      }
 387      else if( event.modifier & SI_MULTISELECT )
 388         addSelection( ctrl );
 389      else
 390      {
 391         // Clicked on child control.  Start move.
 392         
 393         clearSelection();
 394         addSelection( ctrl );
 395         
 396         startDragMove( event.mousePoint );
 397      }
 398   }
 399}
 400
 401//-----------------------------------------------------------------------------
 402
 403void GuiEditCtrl::onMouseUp(const GuiEvent &event)
 404{
 405   if (! mActive || !mContentControl || !getCurrentAddSet() )
 406   {
 407      Parent::onMouseUp(event);
 408      return;
 409   }
 410
 411   //find the control we clicked
 412   GuiControl *ctrl = mContentControl->findHitControl(mLastMousePos, getCurrentAddSet()->mLayer);
 413
 414   bool handledEvent = ctrl->onMouseUpEditor( event, localToGlobalCoord( Point2I(0,0) ) );
 415   if( handledEvent == true )
 416   {
 417      // The Control handled the event and requested the edit ctrl
 418      // *NOT* act on it.  The dude abides.
 419      return;
 420   }
 421
 422   //unlock the mouse
 423   mouseUnlock();
 424
 425   // Reset Drag Axis Alignment Information
 426   mDragBeginPoint.set(-1,-1);
 427   mDragBeginPoints.clear();
 428
 429   mLastMousePos = globalToLocalCoord(event.mousePoint);
 430   if( mMouseDownMode == DragGuide )
 431   {
 432      // Check to see if the mouse has moved off the canvas.  If so,
 433      // remove the guides being dragged.
 434      
 435      for( U32 axis = 0; axis < 2; ++ axis )
 436         if( mDragGuide[ axis ] && !getContentControl()->getGlobalBounds().pointInRect( event.mousePoint ) )
 437            mGuides[ axis ].erase( mDragGuideIndex[ axis ] );
 438   }
 439   else if( mMouseDownMode == DragSelecting )
 440   {
 441      // If not multiselecting, clear the current selection.
 442      
 443      if( !( event.modifier & SI_MULTISELECT ) && !mDragAddSelection )
 444         clearSelection();
 445         
 446      RectI rect;
 447      getDragRect( rect );
 448            
 449      // If the region is somewhere less than at least 2x2, count this as a
 450      // normal, non-rectangular selection. 
 451      
 452      if( rect.extent.x <= 2 && rect.extent.y <= 2 )
 453         addSelectControlAt( rect.point );
 454      else
 455      {
 456         // Use HIT_AddParentHits by default except if ALT is pressed.
 457         // Use HIT_ParentPreventsChildHit if ALT+CTRL is pressed.
 458         
 459         U32 hitFlags = 0;
 460         if( !( event.modifier & SI_PRIMARY_ALT ) )
 461            hitFlags |= HIT_AddParentHits;
 462         if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_CTRL )
 463            hitFlags |= HIT_ParentPreventsChildHit;
 464            
 465         addSelectControlsInRegion( rect, hitFlags );      
 466      }
 467   }
 468   else if( ctrl == getContentControl() && mMouseDownMode == Selecting )
 469      setCurrentAddSet( NULL, true );
 470   
 471   // deliver post edit event if we've been editing
 472   // note: paxorr: this may need to be moved earlier, if the selection has changed.
 473   // undo
 474   if( mMouseDownMode == SizingSelection || ( mMouseDownMode == MovingSelection && mDragMoveUndo ) )
 475      onPostEdit_callback( getSelectedSet() );
 476
 477   //reset the mouse mode
 478   setFirstResponder();
 479   setMouseMode( Selecting );
 480   mSizingMode = sizingNone;
 481   
 482   // Clear snapping state.
 483   
 484   mSnapped[ SnapVertical ] = false;
 485   mSnapped[ SnapHorizontal ] = false;
 486   
 487   mSnapTargets[ SnapVertical ] = NULL;
 488   mSnapTargets[ SnapHorizontal ] = NULL;
 489   
 490   // Clear guide drag state.
 491   
 492   mDragGuide[ GuideVertical ] = false;
 493   mDragGuide[ GuideHorizontal ] = false;
 494}
 495
 496//-----------------------------------------------------------------------------
 497
 498void GuiEditCtrl::onMouseDragged( const GuiEvent &event )
 499{
 500   if( !mActive || !mContentControl || !getCurrentAddSet() )
 501   {
 502      Parent::onMouseDragged(event);
 503      return;
 504   }
 505
 506   Point2I mousePoint = globalToLocalCoord( event.mousePoint );
 507
 508   //find the control we clicked
 509   GuiControl *ctrl = mContentControl->findHitControl( mousePoint, getCurrentAddSet()->mLayer );
 510
 511   bool handledEvent = ctrl->onMouseDraggedEditor( event, localToGlobalCoord( Point2I(0,0) ) );
 512   if( handledEvent == true )
 513   {
 514      // The Control handled the event and requested the edit ctrl
 515      // *NOT* act on it.  The dude abides.
 516      return;
 517   }
 518   
 519   // If we're doing a drag clone, see if we have crossed the move threshold.  If so,
 520   // clone the selection and switch to move mode.
 521   
 522   if( mMouseDownMode == DragClone )
 523   {
 524      // If we haven't yet crossed the mouse delta to actually start the
 525      // clone, check if we have now.
 526      
 527      S32 delta = mAbs( ( mousePoint - mDragBeginPoint ).len() );
 528      if( delta >= 4 )
 529      {
 530         cloneSelection();
 531         mLastMousePos = mDragBeginPoint;
 532         mDragMoveUndo = false;
 533         
 534         setMouseMode( MovingSelection );
 535      }
 536   }
 537   
 538   if( mMouseDownMode == DragGuide )
 539   {
 540      for( U32 axis = 0; axis < 2; ++ axis )
 541         if( mDragGuide[ axis ] )
 542         {
 543            // Set the guide to the coordinate of the mouse cursor
 544            // on the guide's axis.
 545            
 546            Point2I point = event.mousePoint;
 547            point -= localToGlobalCoord( Point2I( 0, 0 ) );
 548            point[ axis ] = mClamp( point[ axis ], 0, getExtent()[ axis ] - 1 );
 549            
 550            mGuides[ axis ][ mDragGuideIndex[ axis ] ] = point[ axis ];
 551         }
 552   }
 553   else if( mMouseDownMode == SizingSelection )
 554   {
 555      // Snap the mouse cursor to grid if active.  Do this on the mouse cursor so that we handle
 556      // incremental drags correctly.
 557      
 558      Point2I dragPoint = event.mousePoint;
 559      snapToGrid(dragPoint);
 560                  
 561      Point2I delta = dragPoint - mLastDragPos;
 562      
 563      // If CTRL is down, apply smart snapping.
 564      
 565      if( event.modifier & SI_CTRL )
 566      {
 567         RectI selectionBounds = getSelectionBounds();
 568         
 569         doSnapping( event, selectionBounds, delta );
 570      }
 571      else
 572      {
 573         mSnapped[ SnapVertical ] = false;
 574         mSnapped[ SnapHorizontal ] = false;
 575      }
 576      
 577      // If ALT is down, do a move instead of a resize on the control
 578      // knob's axis.  Otherwise resize.
 579
 580      if( event.modifier & SI_PRIMARY_ALT )
 581      {
 582         if( !( mSizingMode & sizingLeft ) && !( mSizingMode & sizingRight ) )
 583         {
 584            mSnapped[ SnapVertical ] = false;
 585            delta.x = 0;
 586         }
 587         if( !( mSizingMode & sizingTop ) && !( mSizingMode & sizingBottom ) )
 588         {
 589            mSnapped[ SnapHorizontal ] = false;
 590            delta.y = 0;
 591         }
 592            
 593         moveSelection( delta );
 594      }
 595      else
 596         resizeControlsInSelectionBy( delta, mSizingMode );
 597         
 598      // Remember drag point.
 599      
 600      mLastDragPos = dragPoint;
 601   }
 602   else if (mMouseDownMode == MovingSelection && mSelectedControls.size())
 603   {
 604      Point2I delta = mousePoint - mLastMousePos;
 605      RectI selectionBounds = getSelectionBounds();
 606      
 607      // Apply snaps.
 608      
 609      doSnapping( event, selectionBounds, delta );
 610      
 611      //RDTODO: to me seems to be in need of revision
 612      // Do we want to align this drag to the X and Y axes within a certain threshold?
 613      if( event.modifier & SI_SHIFT && !( event.modifier & SI_PRIMARY_ALT ) )
 614      {
 615         Point2I dragTotalDelta = event.mousePoint - localToGlobalCoord( mDragBeginPoint );
 616         if( dragTotalDelta.y < 10 && dragTotalDelta.y > -10 )
 617         {
 618            for(S32 i = 0; i < mSelectedControls.size(); i++)
 619            {
 620               Point2I selCtrlPos = mSelectedControls[i]->getPosition();
 621               Point2I snapBackPoint( selCtrlPos.x, mDragBeginPoints[i].y);
 622               // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD
 623               if( selCtrlPos.y != mDragBeginPoints[i].y )
 624                  mSelectedControls[i]->setPosition( snapBackPoint );
 625            }
 626            delta.y = 0;
 627         }
 628         if( dragTotalDelta.x < 10 && dragTotalDelta.x > -10 )
 629         {
 630            for(S32 i = 0; i < mSelectedControls.size(); i++)
 631            {
 632               Point2I selCtrlPos = mSelectedControls[i]->getPosition();
 633               Point2I snapBackPoint( mDragBeginPoints[i].x, selCtrlPos.y);
 634               // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD
 635               if( selCtrlPos.x != mDragBeginPoints[i].x )
 636                  mSelectedControls[i]->setPosition( snapBackPoint );
 637            }
 638            delta.x = 0;
 639         }
 640      }
 641
 642      if( delta.x || delta.y )
 643         moveSelection( delta, mDragMoveUndo );
 644
 645      // find the current control under the mouse
 646
 647      canHitSelectedControls( false );
 648      GuiControl *inCtrl = mContentControl->findHitControl(mousePoint, getCurrentAddSet()->mLayer);
 649      canHitSelectedControls( true );
 650
 651      // find the nearest control up the heirarchy from the control the mouse is in
 652      // that is flagged as a container.
 653      while( !inCtrl->mIsContainer )
 654         inCtrl = inCtrl->getParent();
 655         
 656      // if the control under the mouse is not our parent, move the selected controls
 657      // into the new parent.
 658      if(mSelectedControls[0]->getParent() != inCtrl && inCtrl->mIsContainer)
 659      {
 660         moveSelectionToCtrl( inCtrl, mDragMoveUndo );
 661         setCurrentAddSet( inCtrl, false );
 662      }
 663
 664      mLastMousePos += delta;
 665   }
 666   else
 667      mLastMousePos = mousePoint;
 668}
 669
 670//-----------------------------------------------------------------------------
 671
 672void GuiEditCtrl::onRightMouseDown(const GuiEvent &event)
 673{
 674   if (! mActive || !mContentControl)
 675   {
 676      Parent::onRightMouseDown(event);
 677      return;
 678   }
 679   setFirstResponder();
 680
 681   //search for the control hit in any layer below the edit layer
 682   GuiControl *hitCtrl = mContentControl->findHitControl(globalToLocalCoord(event.mousePoint), mLayer - 1);
 683   if (hitCtrl != getCurrentAddSet())
 684   {
 685      setCurrentAddSet( hitCtrl );
 686   }
 687   // select the parent if we right-click on the current add set 
 688   else if( getCurrentAddSet() != mContentControl)
 689   {
 690      setCurrentAddSet( hitCtrl->getParent() );
 691      select(hitCtrl);
 692   }
 693
 694   //Design time mouse events
 695   GuiEvent designEvent = event;
 696   designEvent.mousePoint = mLastMousePos;
 697   hitCtrl->onRightMouseDownEditor( designEvent, localToGlobalCoord( Point2I(0,0) ) );
 698
 699}
 700
 701//=============================================================================
 702//    Rendering.
 703//=============================================================================
 704// MARK: ---- Rendering ----
 705
 706//-----------------------------------------------------------------------------
 707
 708void GuiEditCtrl::onPreRender()
 709{
 710   setUpdate();
 711}
 712
 713//-----------------------------------------------------------------------------
 714
 715void GuiEditCtrl::onRender(Point2I offset, const RectI &updateRect)
 716{   
 717   Point2I ctOffset;
 718   Point2I cext;
 719   bool keyFocused = isFirstResponder();
 720
 721   GFXDrawUtil *drawer = GFX->getDrawUtil();
 722
 723   if (mActive)
 724   {
 725      if( getCurrentAddSet() != getContentControl() )
 726      {
 727         // draw a white frame inset around the current add set.
 728         cext = getCurrentAddSet()->getExtent();
 729         ctOffset = getCurrentAddSet()->localToGlobalCoord(Point2I(0,0));
 730         RectI box(ctOffset.x, ctOffset.y, cext.x, cext.y);
 731
 732         box.inset( -5, -5 );
 733         drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) );
 734         box.inset( 1, 1 );
 735         drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) );
 736         box.inset( 1, 1 );
 737         drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) );
 738         box.inset( 1, 1 );
 739         drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) );
 740         box.inset( 1, 1 );
 741         drawer->drawRect( box, ColorI( 50, 101, 152, 128 ) );
 742      }
 743      Vector<GuiControl *>::iterator i;
 744      bool multisel = mSelectedControls.size() > 1;
 745      for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
 746      {
 747         GuiControl *ctrl = (*i);
 748         cext = ctrl->getExtent();
 749         ctOffset = ctrl->localToGlobalCoord(Point2I(0,0));
 750         RectI box(ctOffset.x,ctOffset.y, cext.x, cext.y);
 751         ColorI nutColor = multisel ? ColorI( 255, 255, 255, 100 ) : ColorI( 0, 0, 0, 100 );
 752         ColorI outlineColor = multisel ? ColorI( 0, 0, 0, 100 ) : ColorI( 255, 255, 255, 100 );
 753         if(!keyFocused)
 754            nutColor.set( 128, 128, 128, 100 );
 755
 756         drawNuts(box, outlineColor, nutColor);
 757      }
 758   }
 759
 760   renderChildControls(offset, updateRect);
 761   
 762   // Draw selection rectangle.
 763   
 764   if( mActive && mMouseDownMode == DragSelecting )
 765   {
 766      RectI b;
 767      getDragRect(b);
 768      b.point += offset;
 769      
 770      // Draw outline.
 771      
 772      drawer->drawRect( b, ColorI( 100, 100, 100, 128 ) );
 773      
 774      // Draw fill.
 775      
 776      b.inset( 1, 1 );
 777      drawer->drawRectFill( b, ColorI( 150, 150, 150, 128 ) );
 778   }
 779
 780   // Draw grid.
 781
 782   if(   mActive &&
 783         ( mMouseDownMode == MovingSelection || mMouseDownMode == SizingSelection ) &&
 784         ( mGridSnap.x || mGridSnap.y ) )
 785   {
 786      cext = getContentControl()->getExtent();
 787      Point2I coff = getContentControl()->localToGlobalCoord(Point2I(0,0));
 788      
 789      // create point-dots
 790      const Point2I& snap = mGridSnap;
 791      U32 maxdot = (U32)(mCeil(cext.x / (F32)snap.x) - 1) * (U32)(mCeil(cext.y / (F32)snap.y) - 1);
 792
 793      if( mDots.isNull() || maxdot != mDots->mNumVerts)
 794      {
 795         mDots.set(GFX, maxdot, GFXBufferTypeStatic);
 796
 797         U32 ndot = 0;
 798         mDots.lock();
 799         for(U32 ix = snap.x; ix < cext.x; ix += snap.x)
 800         { 
 801            for(U32 iy = snap.y; ndot < maxdot && iy < cext.y; iy += snap.y)
 802            {
 803               mDots[ndot].color.set( 50, 50, 254, 100 );
 804               mDots[ndot].point.x = F32(ix + coff.x);
 805               mDots[ndot].point.y = F32(iy + coff.y);
 806               mDots[ndot].point.z = 0.0f;
 807               ndot++;
 808            }
 809         }
 810         mDots.unlock();
 811         AssertFatal(ndot <= maxdot, "dot overflow");
 812         AssertFatal(ndot == maxdot, "dot underflow");
 813      }
 814
 815      if (!mDotSB)
 816      {
 817         GFXStateBlockDesc dotdesc;
 818         dotdesc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha);
 819         dotdesc.setCullMode( GFXCullNone );
 820         mDotSB = GFX->createStateBlock( dotdesc );
 821      }
 822
 823      GFX->setStateBlock(mDotSB);
 824
 825      // draw the points.
 826      GFX->setVertexBuffer( mDots );
 827      GFX->drawPrimitive( GFXPointList, 0, mDots->mNumVerts );
 828   }
 829
 830   // Draw snapping lines.
 831   
 832   if( mActive && getContentControl() )
 833   {      
 834      RectI bounds = getContentControl()->getGlobalBounds();
 835            
 836      // Draw guide lines.
 837
 838      if( mDrawGuides )
 839      {
 840         for( U32 axis = 0; axis < 2; ++ axis )
 841         {
 842            for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i )
 843               drawCrossSection( axis, mGuides[ axis ][ i ] + bounds.point[ axis ],
 844                  bounds, ColorI( 0, 255, 0, 100 ), drawer );
 845         }
 846      }
 847
 848      // Draw smart snap lines.
 849      
 850      for( U32 axis = 0; axis < 2; ++ axis )
 851      {
 852         if( mSnapped[ axis ] )
 853         {
 854            // Draw the snap line.
 855            
 856            drawCrossSection( axis, mSnapOffset[ axis ],
 857               bounds, ColorI( 0, 0, 255, 100 ), drawer );
 858
 859            // Draw a border around the snap target control.
 860            
 861            if( mSnapTargets[ axis ] )
 862            {
 863               RectI snapBounds = mSnapTargets[ axis ]->getGlobalBounds();
 864               drawer->drawRect(snapBounds, ColorI( 128, 128, 128, 128 ) );
 865            }
 866         }
 867      }
 868   }
 869}
 870
 871//-----------------------------------------------------------------------------
 872
 873void GuiEditCtrl::drawNuts(RectI &box, ColorI &outlineColor, ColorI &nutColor)
 874{
 875   GFXDrawUtil *drawer = GFX->getDrawUtil();
 876
 877   S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1;
 878   S32 cx = (lx + rx) >> 1;
 879   S32 ty = box.point.y, by = box.point.y + box.extent.y - 1;
 880   S32 cy = (ty + by) >> 1;
 881
 882   if( mDrawBorderLines )
 883   {
 884      ColorI lineColor( 179, 179, 179, 64 );
 885      ColorI lightLineColor( 128, 128, 128, 26);
 886      
 887      if(lx > 0 && ty > 0)
 888      {
 889         drawer->drawLine(0, ty, lx, ty, lineColor);  // Left edge to top-left corner.
 890         drawer->drawLine(lx, 0, lx, ty, lineColor);  // Top edge to top-left corner.
 891      }
 892
 893      if(lx > 0 && by > 0)
 894         drawer->drawLine(0, by, lx, by, lineColor);  // Left edge to bottom-left corner.
 895
 896      if(rx > 0 && ty > 0)
 897         drawer->drawLine(rx, 0, rx, ty, lineColor);  // Top edge to top-right corner.
 898
 899      Point2I extent = localToGlobalCoord(getExtent());
 900
 901      if(lx < extent.x && by < extent.y)
 902         drawer->drawLine(lx, by, lx, extent.y, lightLineColor);  // Bottom-left corner to bottom edge.
 903      if(rx < extent.x && by < extent.y)
 904      {
 905         drawer->drawLine(rx, by, rx, extent.y, lightLineColor);  // Bottom-right corner to bottom edge.
 906         drawer->drawLine(rx, by, extent.x, by, lightLineColor);  // Bottom-right corner to right edge.
 907      }
 908      if(rx < extent.x && ty < extent.y)
 909         drawer->drawLine(rx, ty, extent.x, ty, lightLineColor);  // Top-right corner to right edge.
 910   }
 911
 912   // Adjust nuts, so they dont straddle the controls.
 913   
 914   lx -= NUT_SIZE + 1;
 915   ty -= NUT_SIZE + 1;
 916   rx += 1;
 917   by += 1;
 918   
 919   // Draw nuts.
 920
 921   drawNut( Point2I( lx - NUT_SIZE, ty - NUT_SIZE ), outlineColor, nutColor ); // Top left
 922   drawNut( Point2I( lx - NUT_SIZE, cy - NUT_SIZE / 2 ), outlineColor, nutColor ); // Mid left
 923   drawNut( Point2I( lx - NUT_SIZE, by ), outlineColor, nutColor ); // Bottom left
 924   drawNut( Point2I( rx, ty - NUT_SIZE ), outlineColor, nutColor ); // Top right
 925   drawNut( Point2I( rx, cy - NUT_SIZE / 2 ), outlineColor, nutColor ); // Mid right
 926   drawNut( Point2I( rx, by ), outlineColor, nutColor ); // Bottom right
 927   drawNut( Point2I( cx - NUT_SIZE / 2, ty - NUT_SIZE ), outlineColor, nutColor ); // Mid top
 928   drawNut( Point2I( cx - NUT_SIZE / 2, by ), outlineColor, nutColor ); // Mid bottom
 929}
 930
 931//-----------------------------------------------------------------------------
 932
 933void GuiEditCtrl::drawNut(const Point2I &nut, ColorI &outlineColor, ColorI &nutColor)
 934{
 935   RectI r( nut.x, nut.y, NUT_SIZE * 2, NUT_SIZE * 2 );
 936   GFX->getDrawUtil()->drawRect( r, outlineColor );
 937   r.inset( 1, 1 );
 938   GFX->getDrawUtil()->drawRectFill( r, nutColor );
 939}
 940
 941//=============================================================================
 942//    Selections.
 943//=============================================================================
 944// MARK: ---- Selections ----
 945
 946//-----------------------------------------------------------------------------
 947
 948void GuiEditCtrl::clearSelection(void)
 949{
 950   mSelectedControls.clear();
 951   onClearSelected_callback();
 952}
 953
 954//-----------------------------------------------------------------------------
 955
 956void GuiEditCtrl::setSelection(GuiControl *ctrl, bool inclusive)
 957{
 958   //sanity check
 959   if( !ctrl )
 960      return;
 961      
 962   if( mSelectedControls.size() == 1 && mSelectedControls[ 0 ] == ctrl )
 963      return;
 964      
 965   if( !inclusive )
 966      clearSelection();
 967
 968   if( mContentControl == ctrl )
 969      setCurrentAddSet( ctrl, false );
 970   else
 971      addSelection( ctrl );
 972}
 973
 974//-----------------------------------------------------------------------------
 975
 976void GuiEditCtrl::addSelection(S32 id)
 977{
 978   GuiControl * ctrl;
 979   if( Sim::findObject( id, ctrl ) )
 980      addSelection( ctrl );
 981}
 982
 983//-----------------------------------------------------------------------------
 984
 985void GuiEditCtrl::addSelection( GuiControl* ctrl )
 986{
 987   // Only add if this isn't the content control and the
 988   // control isn't yet in the selection.
 989   
 990   if( ctrl != getContentControl() && !selectionContains( ctrl ) )
 991   {
 992      mSelectedControls.push_back( ctrl );
 993      
 994      if( mSelectedControls.size() == 1 )
 995      {
 996         // Update the add set.
 997         
 998         if( ctrl->mIsContainer )
 999            setCurrentAddSet( ctrl, false );
1000         else
1001            setCurrentAddSet( ctrl->getParent(), false );
1002            
1003         // Notify script.
1004
1005         onSelect_callback( ctrl );
1006      }
1007      else
1008      {
1009         // Notify script.
1010         
1011         onAddSelected_callback( ctrl );
1012      }
1013   }
1014}
1015
1016//-----------------------------------------------------------------------------
1017
1018void GuiEditCtrl::removeSelection( S32 id )
1019{
1020   GuiControl * ctrl;
1021   if ( Sim::findObject( id, ctrl ) )
1022      removeSelection( ctrl );
1023}
1024
1025//-----------------------------------------------------------------------------
1026
1027void GuiEditCtrl::removeSelection( GuiControl* ctrl )
1028{
1029   if( selectionContains( ctrl ) )
1030   {
1031      Vector< GuiControl* >::iterator i = T3D::find( mSelectedControls.begin(), mSelectedControls.end(), ctrl );
1032      if ( i != mSelectedControls.end() )
1033         mSelectedControls.erase( i );
1034
1035      onRemoveSelected_callback( ctrl );
1036   }
1037}
1038
1039//-----------------------------------------------------------------------------
1040
1041void GuiEditCtrl::canHitSelectedControls( bool state )
1042{
1043   for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i )
1044      mSelectedControls[ i ]->setCanHit( state );
1045}
1046
1047//-----------------------------------------------------------------------------
1048
1049void GuiEditCtrl::moveSelectionToCtrl( GuiControl *newParent, bool callback )
1050{
1051   for( U32 i = 0; i < mSelectedControls.size(); ++ i )
1052   {
1053      GuiControl* ctrl = mSelectedControls[i];
1054      if( ctrl->getParent() == newParent
1055          || ctrl->isLocked()
1056          || selectionContainsParentOf( ctrl ) )
1057         continue;
1058   
1059      Point2I globalpos = ctrl->localToGlobalCoord(Point2I(0,0));
1060      newParent->addObject(ctrl);
1061      Point2I newpos = ctrl->globalToLocalCoord(globalpos) + ctrl->getPosition();
1062      ctrl->setPosition(newpos);
1063   }
1064   
1065   onHierarchyChanged_callback();
1066   
1067   //TODO: undo
1068}
1069
1070//-----------------------------------------------------------------------------
1071
1072static Point2I snapPoint(Point2I point, Point2I delta, Point2I gridSnap)
1073{ 
1074   S32 snap;
1075   if(gridSnap.x && delta.x)
1076   {
1077      snap = point.x % gridSnap.x;
1078      point.x -= snap;
1079      if(delta.x > 0 && snap != 0)
1080         point.x += gridSnap.x;
1081   }
1082   if(gridSnap.y && delta.y)
1083   {
1084      snap = point.y % gridSnap.y;
1085      point.y -= snap;
1086      if(delta.y > 0 && snap != 0)
1087         point.y += gridSnap.y;
1088   }
1089   return point;
1090}
1091
1092void GuiEditCtrl::moveAndSnapSelection( const Point2I &delta, bool callback )
1093{
1094   // move / nudge gets a special callback so that multiple small moves can be
1095   // coalesced into one large undo action.
1096   // undo
1097   
1098   if( callback )
1099      onPreSelectionNudged_callback( getSelectedSet() );
1100
1101   Vector<GuiControl *>::iterator i;
1102   Point2I newPos;
1103   for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1104   {
1105      GuiControl* ctrl = *i;
1106      if( !ctrl->isLocked() && !selectionContainsParentOf( ctrl ) )
1107      {
1108         newPos = ctrl->getPosition() + delta;
1109         newPos = snapPoint( newPos, delta, mGridSnap );
1110         ctrl->setPosition( newPos );
1111      }
1112   }
1113
1114   // undo
1115   if( callback )
1116      onPostSelectionNudged_callback( getSelectedSet() );
1117
1118   // allow script to update the inspector
1119   if( callback && mSelectedControls.size() > 0 )
1120      onSelectionMoved_callback( mSelectedControls[ 0 ] );
1121}
1122
1123//-----------------------------------------------------------------------------
1124
1125void GuiEditCtrl::moveSelection( const Point2I &delta, bool callback )
1126{
1127   Vector<GuiControl *>::iterator i;
1128   for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1129   {
1130      GuiControl* ctrl = *i;
1131      if( !ctrl->isLocked() && !selectionContainsParentOf( ctrl ) )
1132         ctrl->setPosition( ctrl->getPosition() + delta );
1133   }
1134
1135   // allow script to update the inspector
1136   if( callback )
1137      onSelectionMoved_callback( mSelectedControls[ 0 ] );
1138}
1139
1140//-----------------------------------------------------------------------------
1141
1142void GuiEditCtrl::justifySelection( Justification j )
1143{
1144   S32 minX, maxX;
1145   S32 minY, maxY;
1146   S32 extentX, extentY;
1147
1148   if (mSelectedControls.size() < 2)
1149      return;
1150
1151   Vector<GuiControl *>::iterator i = mSelectedControls.begin();
1152   minX = (*i)->getLeft();
1153   maxX = minX + (*i)->getWidth();
1154   minY = (*i)->getTop();
1155   maxY = minY + (*i)->getHeight();
1156   extentX = (*i)->getWidth();
1157   extentY = (*i)->getHeight();
1158   i++;
1159   for(;i != mSelectedControls.end(); i++)
1160   {
1161      minX = getMin(minX, (*i)->getLeft());
1162      maxX = getMax(maxX, (*i)->getLeft() + (*i)->getWidth());
1163      minY = getMin(minY, (*i)->getTop());
1164      maxY = getMax(maxY, (*i)->getTop() + (*i)->getHeight());
1165      extentX += (*i)->getWidth();
1166      extentY += (*i)->getHeight();
1167   }
1168   S32 deltaX = maxX - minX;
1169   S32 deltaY = maxY - minY;
1170   switch(j)
1171   {
1172      case JUSTIFY_LEFT:
1173         for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1174            if( !( *i )->isLocked() )
1175               (*i)->setLeft( minX );
1176         break;
1177      case JUSTIFY_TOP:
1178         for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1179            if( !( *i )->isLocked() )
1180               (*i)->setTop( minY );
1181         break;
1182      case JUSTIFY_RIGHT:
1183         for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1184            if( !( *i )->isLocked() )
1185               (*i)->setLeft( maxX - (*i)->getWidth() + 1 );
1186         break;
1187      case JUSTIFY_BOTTOM:
1188         for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1189            if( !( *i )->isLocked() )
1190               (*i)->setTop( maxY - (*i)->getHeight() + 1 );
1191         break;
1192      case JUSTIFY_CENTER_VERTICAL:
1193         for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1194            if( !( *i )->isLocked() )
1195               (*i)->setLeft( minX + ((deltaX - (*i)->getWidth()) >> 1 ));
1196         break;
1197      case JUSTIFY_CENTER_HORIZONTAL:
1198         for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1199            if( !( *i )->isLocked() )
1200               (*i)->setTop( minY + ((deltaY - (*i)->getHeight()) >> 1 ));
1201         break;
1202      case SPACING_VERTICAL:
1203         {
1204            Vector<GuiControl*> sortedList;
1205            Vector<GuiControl *>::iterator k;
1206            for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1207            {
1208               for(k = sortedList.begin(); k != sortedList.end(); k++)
1209               {
1210                  if ((*i)->getTop() < (*k)->getTop())
1211                     break;
1212               }
1213               sortedList.insert(k, *i);
1214            }
1215            S32 space = (deltaY - extentY) / (mSelectedControls.size() - 1);
1216            S32 curY = minY;
1217            for(k = sortedList.begin(); k != sortedList.end(); k++)
1218            {
1219               if( !( *k )->isLocked() )
1220                  (*k)->setTop( curY );
1221               curY += (*k)->getHeight() + space;
1222            }
1223         }
1224         break;
1225      case SPACING_HORIZONTAL:
1226         {
1227            Vector<GuiControl*> sortedList;
1228            Vector<GuiControl *>::iterator k;
1229            for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1230            {
1231               for(k = sortedList.begin(); k != sortedList.end(); k++)
1232               {
1233                  if ((*i)->getLeft() < (*k)->getLeft())
1234                     break;
1235               }
1236               sortedList.insert(k, *i);
1237            }
1238            S32 space = (deltaX - extentX) / (mSelectedControls.size() - 1);
1239            S32 curX = minX;
1240            for(k = sortedList.begin(); k != sortedList.end(); k++)
1241            {
1242               if( !( *k )->isLocked() )
1243                  (*k)->setLeft( curX );
1244               curX += (*k)->getWidth() + space;
1245            }
1246         }
1247         break;
1248   }
1249}
1250
1251//-----------------------------------------------------------------------------
1252
1253void GuiEditCtrl::cloneSelection()
1254{
1255   Vector< GuiControl*> newSelection;
1256   
1257   // Clone the controls in the current selection.
1258   
1259   const U32 numOldControls = mSelectedControls.size();
1260   for( U32 i = 0; i < numOldControls; ++ i )
1261   {
1262      GuiControl* ctrl = mSelectedControls[ i ];
1263      
1264      // If parent is in selection, too, skip to prevent multiple clones.
1265      
1266      if( ctrl->getParent() && selectionContains( ctrl->getParent() ) )
1267         continue;
1268         
1269      // Clone and add to set.
1270      
1271      GuiControl* clone = dynamic_cast< GuiControl* >( ctrl->deepClone() );
1272      if( clone )
1273         newSelection.push_back( clone );
1274   }
1275   
1276   // Exchange the selection set.
1277   
1278   clearSelection();
1279   const U32 numNewControls = newSelection.size();
1280   for( U32 i = 0; i < numNewControls; ++ i )
1281      addSelection( newSelection[ i ] );
1282      
1283   // Callback for undo.
1284      
1285   onSelectionCloned_callback( getSelectedSet() );
1286}
1287
1288//-----------------------------------------------------------------------------
1289
1290void GuiEditCtrl::deleteSelection()
1291{
1292   // Notify script for undo.
1293   
1294   onTrashSelection_callback( getSelectedSet() );
1295   
1296   // Move all objects in selection to trash.
1297
1298   Vector< GuiControl* >::iterator i;
1299   for( i = mSelectedControls.begin(); i != mSelectedControls.end(); i ++ )
1300   {
1301      if( ( *i ) == getCurrentAddSet() )
1302         setCurrentAddSet( getContentControl(), false );
1303         
1304      mTrash->addObject( *i );
1305   }
1306      
1307   clearSelection();
1308   
1309   // Notify script it needs to update its views.
1310   
1311   onHierarchyChanged_callback();
1312}
1313
1314//-----------------------------------------------------------------------------
1315
1316void GuiEditCtrl::loadSelection( const char* filename )
1317{
1318   // Set redefine behavior to rename.
1319   
1320   const char* oldRedefineBehavior = Con::getVariable( "$Con::redefineBehavior" );
1321   Con::setVariable( "$Con::redefineBehavior", "renameNew" );
1322   
1323   // Exec the file or clipboard contents with the saved selection set.
1324
1325   if( filename )
1326      Con::executef( "exec", filename );
1327   else
1328      Con::evaluate( Platform::getClipboard() );
1329      
1330   SimSet* set;
1331   if( !Sim::findObject( "guiClipboard", set ) )
1332   {
1333      if( filename )
1334         Con::errorf( "GuiEditCtrl::loadSelection() - could not find 'guiClipboard' in '%s'", filename );
1335      else
1336         Con::errorf( "GuiEditCtrl::loadSelection() - could not find 'guiClipboard'" );
1337      return;
1338   }
1339   
1340   // Restore redefine behavior.
1341   
1342   Con::setVariable( "$Con::redefineBehavior", oldRedefineBehavior );
1343   
1344   // Add the objects in the set.
1345
1346   if( set->size() )
1347   {
1348      clearSelection();
1349
1350      GuiControlVector ctrls;
1351      for( U32 i = 0, num = set->size(); i < num; ++ i )
1352      {
1353         GuiControl *ctrl = dynamic_cast< GuiControl* >( ( *set )[ i ] );
1354         if( ctrl )
1355         {
1356            getCurrentAddSet()->addObject( ctrl );
1357            ctrls.push_back( ctrl );
1358         }
1359      }
1360      
1361      // Select all controls.  We need to perform this here rather than in the
1362      // loop above as addSelection() will modify the current add set.
1363      for( U32 i = 0; i < ctrls.size(); ++ i )
1364      {
1365         addSelection( ctrls[i] );
1366      }
1367
1368      // Undo 
1369      onAddNewCtrlSet_callback( getSelectedSet() );
1370
1371      // Notify the script it needs to update its treeview.
1372
1373      onHierarchyChanged_callback();
1374   }
1375   set->deleteObject();
1376}
1377
1378//-----------------------------------------------------------------------------
1379
1380void GuiEditCtrl::saveSelection( const char* filename )
1381{
1382   // If there are no selected objects, then don't save.
1383   
1384   if( mSelectedControls.size() == 0 )
1385      return;
1386      
1387   // Open the stream.
1388
1389   Stream* stream;
1390   if( filename )
1391   {
1392      stream = FileStream::createAndOpen( filename, Torque::FS::File::Write );
1393      if( !stream )
1394      {
1395         Con::errorf( "GuiEditCtrl::saveSelection - could not open '%s' for writing", filename );
1396         return;
1397      }
1398   }
1399   else
1400      stream = new MemStream( 4096 );
1401      
1402   // Create a temporary SimSet.
1403      
1404   SimSet* clipboardSet = new SimSet;
1405   clipboardSet->registerObject();
1406   Sim::getRootGroup()->addObject( clipboardSet, "guiClipboard" );
1407   
1408   // Add the selected controls to the set.
1409
1410   for( Vector< GuiControl* >::iterator i = mSelectedControls.begin();
1411        i != mSelectedControls.end(); ++ i )
1412   {
1413      GuiControl* ctrl = *i;
1414      if( !selectionContainsParentOf( ctrl ) )
1415         clipboardSet->addObject( ctrl );
1416   }
1417   
1418   // Write the SimSet.  Use the IgnoreCanSave to ensure the controls
1419   // get actually written out (also disables the default parent inheritance
1420   // behavior for the flag).
1421
1422   clipboardSet->write( *stream, 0, IgnoreCanSave );
1423   clipboardSet->deleteObject();
1424   
1425   // If we were writing to a memory stream, copy to clipboard
1426   // now.
1427   
1428   if( !filename )
1429   {
1430      MemStream* memStream = static_cast< MemStream* >( stream );
1431      memStream->write( U8( 0 ) );
1432      Platform::setClipboard( ( const char* ) memStream->getBuffer() );
1433   }
1434
1435   delete stream;
1436}
1437
1438//-----------------------------------------------------------------------------
1439
1440void GuiEditCtrl::selectAll()
1441{
1442   GuiControl::iterator i;
1443   
1444   clearSelection();
1445   for(i = getCurrentAddSet()->begin(); i != getCurrentAddSet()->end(); i++)
1446   {
1447      GuiControl *ctrl = dynamic_cast<GuiControl *>(*i);
1448      addSelection( ctrl );
1449   }
1450}
1451
1452//-----------------------------------------------------------------------------
1453
1454void GuiEditCtrl::bringToFront()
1455{
1456   if( getNumSelected() != 1 )
1457      return;
1458
1459   GuiControl* ctrl = mSelectedControls.first();
1460   ctrl->getParent()->pushObjectToBack( ctrl );
1461}
1462
1463//-----------------------------------------------------------------------------
1464
1465void GuiEditCtrl::pushToBack()
1466{
1467   if( getNumSelected() != 1 )
1468      return;
1469
1470   GuiControl* ctrl = mSelectedControls.first();
1471   ctrl->getParent()->bringObjectToFront( ctrl );
1472}
1473
1474//-----------------------------------------------------------------------------
1475
1476RectI GuiEditCtrl::getSelectionBounds() const
1477{
1478   Vector<GuiControl *>::const_iterator i = mSelectedControls.begin();
1479   
1480   Point2I minPos = (*i)->localToGlobalCoord( Point2I( 0, 0 ) );
1481   Point2I maxPos = minPos;
1482   
1483   for(; i != mSelectedControls.end(); i++)
1484   {
1485      Point2I iPos = (**i).localToGlobalCoord( Point2I( 0 , 0 ) );
1486      
1487      minPos.x = getMin( iPos.x, minPos.x );
1488      minPos.y = getMin( iPos.y, minPos.y );
1489         
1490      Point2I iExt = ( **i ).getExtent();
1491      
1492      iPos.x += iExt.x;
1493      iPos.y += iExt.y;
1494
1495      maxPos.x = getMax( iPos.x, maxPos.x );
1496      maxPos.y = getMax( iPos.y, maxPos.y );
1497   }
1498   
1499   minPos = getContentControl()->globalToLocalCoord( minPos );
1500   maxPos = getContentControl()->globalToLocalCoord( maxPos );
1501   
1502   return RectI( minPos.x, minPos.y, ( maxPos.x - minPos.x ), ( maxPos.y - minPos.y ) );
1503}
1504
1505//-----------------------------------------------------------------------------
1506
1507RectI GuiEditCtrl::getSelectionGlobalBounds() const
1508{
1509   Point2I minb( S32_MAX, S32_MAX );
1510   Point2I maxb( S32_MIN, S32_MIN );
1511   
1512   for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i )
1513   {
1514      // Min.
1515
1516      Point2I pos = mSelectedControls[ i ]->localToGlobalCoord( Point2I( 0, 0 ) );
1517      
1518      minb.x = getMin( minb.x, pos.x );
1519      minb.y = getMin( minb.y, pos.y );
1520      
1521      // Max.
1522      
1523      const Point2I extent = mSelectedControls[ i ]->getExtent();
1524      
1525      maxb.x = getMax( maxb.x, pos.x + extent.x );
1526      maxb.y = getMax( maxb.y, pos.y + extent.y );
1527   }
1528   
1529   RectI bounds( minb.x, minb.y, maxb.x - minb.x, maxb.y - minb.y );
1530   return bounds;
1531}
1532
1533//-----------------------------------------------------------------------------
1534
1535bool GuiEditCtrl::selectionContains( GuiControl *ctrl )
1536{
1537   Vector<GuiControl *>::iterator i;
1538   for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1539      if (ctrl == *i) return true;
1540   return false;
1541}
1542
1543//-----------------------------------------------------------------------------
1544
1545bool GuiEditCtrl::selectionContainsParentOf( GuiControl* ctrl )
1546{
1547   GuiControl* parent = ctrl->getParent();
1548   while( parent && parent != getContentControl() )
1549   {
1550      if( selectionContains( parent ) )
1551         return true;
1552         
1553      parent = parent->getParent();
1554   }
1555   
1556   return false;
1557}
1558
1559//-----------------------------------------------------------------------------
1560
1561void GuiEditCtrl::select( GuiControl* ctrl )
1562{
1563   clearSelection();
1564   addSelection( ctrl );
1565}
1566
1567//-----------------------------------------------------------------------------
1568
1569void GuiEditCtrl::updateSelectedSet()
1570{
1571   mSelectedSet->clear();
1572   Vector<GuiControl*>::iterator i;
1573   for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
1574   {
1575      mSelectedSet->addObject(*i);
1576   }
1577}
1578
1579//-----------------------------------------------------------------------------
1580
1581void GuiEditCtrl::addSelectControlsInRegion( const RectI& rect, U32 flags )
1582{
1583   // Do a hit test on the content control.
1584   
1585   canHitSelectedControls( false );
1586   Vector< GuiControl*> hits;
1587   
1588   if( mFullBoxSelection )
1589      flags |= GuiControl::HIT_FullBoxOnly;
1590      
1591   getContentControl()->findHitControls( rect, hits, flags );
1592   canHitSelectedControls( true );
1593   
1594   // Add all controls that got hit.
1595   
1596   for( U32 i = 0, num = hits.size(); i < num; ++ i )
1597      addSelection( hits[ i ] );
1598}
1599
1600//-----------------------------------------------------------------------------
1601
1602void GuiEditCtrl::addSelectControlAt( const Point2I& pos )
1603{
1604   // Run a hit test.
1605   
1606   canHitSelectedControls( false );
1607   GuiControl* hit = getContentControl()->findHitControl( pos );
1608   canHitSelectedControls( true );
1609   
1610   // Add to selection.
1611   
1612   if( hit )
1613      addSelection( hit );
1614}
1615
1616//-----------------------------------------------------------------------------
1617
1618void GuiEditCtrl::resizeControlsInSelectionBy( const Point2I& delta, U32 mode )
1619{
1620   for( U32 i = 0, num = mSelectedControls.size(); i < num; ++ i )
1621   {
1622      GuiControl *ctrl = mSelectedControls[ i ];
1623      if( ctrl->isLocked() )
1624         continue;
1625         
1626      Point2I minExtent    = ctrl->getMinExtent();
1627      Point2I newPosition  = ctrl->getPosition();
1628      Point2I newExtent    = ctrl->getExtent();
1629
1630      if( mSizingMode & sizingLeft )
1631      {
1632         newPosition.x     += delta.x;
1633         newExtent.x       -= delta.x;
1634         
1635         if( newExtent.x < minExtent.x )
1636         {
1637            newPosition.x  -= minExtent.x - newExtent.x;
1638            newExtent.x    = minExtent.x;
1639         }
1640      }
1641      else if( mSizingMode & sizingRight )
1642      {
1643         newExtent.x       += delta.x;
1644
1645         if( newExtent.x < minExtent.x )
1646            newExtent.x    = minExtent.x;
1647      }
1648
1649      if( mSizingMode & sizingTop )
1650      {
1651         newPosition.y     += delta.y;
1652         newExtent.y       -= delta.y;
1653         
1654         if( newExtent.y < minExtent.y )
1655         {
1656            newPosition.y  -= minExtent.y - newExtent.y;
1657            newExtent.y    = minExtent.y;
1658         }
1659      }
1660      else if( mSizingMode & sizingBottom )
1661      {
1662         newExtent.y       += delta.y;
1663         
1664         if( newExtent.y < minExtent.y )
1665            newExtent.y = minExtent.y;
1666      }
1667      
1668      ctrl->resize( newPosition, newExtent );
1669   }
1670   
1671   if( mSelectedControls.size() == 1 )
1672      onSelectionResized_callback( mSelectedControls[ 0 ] );
1673}
1674
1675//-----------------------------------------------------------------------------
1676
1677void GuiEditCtrl::fitIntoParents( bool width, bool height )
1678{
1679   // Record undo.
1680   
1681   onFitIntoParent_callback( width, height );
1682   
1683   // Fit.
1684   
1685   for( U32 i = 0; i < mSelectedControls.size(); ++ i )
1686   {
1687      GuiControl* ctrl = mSelectedControls[ i ];
1688      GuiControl* parent = ctrl->getParent();
1689      
1690      Point2I position = ctrl->getPosition();
1691      if( width )
1692         position.x = 0;
1693      if( height )
1694         position.y = 0;
1695      
1696      Point2I extents = ctrl->getExtent();
1697      if( width )
1698         extents.x = parent->getWidth();
1699      if( height )
1700         extents.y = parent->getHeight();
1701      
1702      ctrl->resize( position, extents );
1703   }
1704}
1705
1706//-----------------------------------------------------------------------------
1707
1708void GuiEditCtrl::selectParents( bool addToSelection )
1709{
1710   Vector< GuiControl*> parents;
1711   
1712   // Collect all parents.
1713   
1714   for( U32 i = 0; i < mSelectedControls.size(); ++ i )
1715   {
1716      GuiControl* ctrl = mSelectedControls[ i ];
1717      if( ctrl != mContentControl && ctrl->getParent() != mContentControl )
1718         parents.push_back( mSelectedControls[ i ]->getParent() );
1719   }
1720   
1721   // If there's no parents to select, don't
1722   // change the selection.
1723   
1724   if( parents.empty() )
1725      return;
1726   
1727   // Blast selection if need be.
1728   
1729   if( !addToSelection )
1730      clearSelection();
1731      
1732   // Add the parents.
1733   
1734   for( U32 i = 0; i < parents.size(); ++ i )
1735      addSelection( parents[ i ] );
1736}
1737
1738//-----------------------------------------------------------------------------
1739
1740void GuiEditCtrl::selectChildren( bool addToSelection )
1741{
1742   Vector< GuiControl*> children;
1743   
1744   // Collect all children.
1745   
1746   for( U32 i = 0; i < mSelectedControls.size(); ++ i )
1747   {
1748      GuiControl* parent = mSelectedControls[ i ];
1749      for( GuiControl::iterator iter = parent->begin(); iter != parent->end(); ++ iter )
1750      {
1751         GuiControl* child = dynamic_cast< GuiControl* >( *iter );
1752         if( child )
1753            children.push_back( child );
1754      }
1755   }
1756   
1757   // If there's no children to select, don't
1758   // change the selection.
1759   
1760   if( children.empty() )
1761      return;
1762
1763   // Blast selection if need be.
1764   
1765   if( !addToSelection )
1766      clearSelection();
1767      
1768   // Add the children.
1769   
1770   for( U32 i = 0; i < children.size(); ++ i )
1771      addSelection( children[ i ] );
1772}
1773
1774//=============================================================================
1775//    Guides.
1776//=============================================================================
1777// MARK: ---- Guides ----
1778
1779//-----------------------------------------------------------------------------
1780
1781void GuiEditCtrl::readGuides( guideAxis axis, GuiControl* ctrl )
1782{
1783   // Read the guide indices from the vector stored on the respective dynamic
1784   // property of the control.
1785   
1786   const char* guideIndices = ctrl->getDataField( smGuidesPropertyName[ axis ], NULL );
1787   if( guideIndices && guideIndices[ 0 ] )
1788   {
1789      U32 index = 0;
1790      while( true )
1791      {
1792         const char* posStr = StringUnit::getUnit( guideIndices, index, " \t" );
1793         if( !posStr[ 0 ] )
1794            break;
1795            
1796         mGuides[ axis ].push_back( dAtoi( posStr ) );
1797         
1798         index ++;
1799      }
1800   }
1801}
1802
1803//-----------------------------------------------------------------------------
1804
1805void GuiEditCtrl::writeGuides( guideAxis axis, GuiControl* ctrl )
1806{
1807   // Store the guide indices of the given axis in a vector on the respective
1808   // dynamic property of the control.
1809   
1810   StringBuilder str;
1811   bool isFirst = true;
1812   for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i )
1813   {
1814      if( !isFirst )
1815         str.append( ' ' );
1816         
1817      char buffer[ 32 ];
1818      dSprintf( buffer, sizeof( buffer ), "%i", mGuides[ axis ][ i ] );
1819      
1820      str.append( buffer );
1821      
1822      isFirst = false;
1823   }
1824   
1825   String value = str.end();
1826   ctrl->setDataField( smGuidesPropertyName[ axis ], NULL, value );
1827}
1828
1829//-----------------------------------------------------------------------------
1830
1831S32 GuiEditCtrl::findGuide( guideAxis axis, const Point2I& point, U32 tolerance )
1832{
1833   const S32 p = ( point - localToGlobalCoord( Point2I( 0, 0 ) ) )[ axis ];
1834   
1835   for( U32 i = 0, num = mGuides[ axis ].size(); i < num; ++ i )
1836   {
1837      const S32 g = mGuides[ axis ][ i ];
1838      if( p >= ( g - tolerance ) &&
1839          p <= ( g + tolerance ) )
1840         return i;
1841   }
1842         
1843   return -1;
1844}
1845
1846//=============================================================================
1847//    Snapping.
1848//=============================================================================
1849// MARK: ---- Snapping ----
1850
1851//-----------------------------------------------------------------------------
1852
1853RectI GuiEditCtrl::getSnapRegion( snappingAxis axis, const Point2I& center ) const
1854{
1855   RectI rootBounds = getContentControl()->getBounds();
1856   
1857   RectI rect;
1858   if( axis == SnapHorizontal )
1859      rect = RectI( rootBounds.point.x,
1860                    center.y - mSnapSensitivity,
1861                    rootBounds.extent.x,
1862                    mSnapSensitivity * 2 );
1863   else // SnapVertical
1864      rect = RectI( center.x - mSnapSensitivity,
1865                    rootBounds.point.y,
1866                    mSnapSensitivity * 2,
1867                    rootBounds.extent.y );
1868   
1869   // Clip against root bounds.
1870   
1871   rect.intersect( rootBounds );
1872      
1873   return rect;
1874}
1875
1876//-----------------------------------------------------------------------------
1877
1878void GuiEditCtrl::registerSnap( snappingAxis axis, const Point2I& mousePoint, const Point2I& point, snappingEdges edge, GuiControl* ctrl )
1879{
1880   bool takeNewSnap = false;
1881   const Point2I globalPoint = getContentControl()->localToGlobalCoord( point );
1882
1883   // If we have no snap yet, just take this one.
1884   
1885   if( !mSnapped[ axis ] )
1886      takeNewSnap = true;
1887   
1888   // Otherwise see if this snap is the better one.
1889   
1890   else
1891   {
1892      // Compare deltas to pointer.
1893      
1894      S32 deltaCurrent = mAbs( mSnapOffset[ axis ] - mousePoint[ axis ] );
1895      S32 deltaNew = mAbs( globalPoint[ axis ] - mousePoint[ axis ] );
1896                              
1897      if( deltaCurrent > deltaNew )
1898         takeNewSnap = true;
1899   }
1900
1901   if( takeNewSnap )
1902   {      
1903      mSnapped[ axis ]     = true;
1904      mSnapOffset[ axis ]  = globalPoint[ axis ];
1905      mSnapEdge[ axis ]    = edge;
1906      mSnapTargets[ axis ] = ctrl;
1907   }
1908}
1909
1910//-----------------------------------------------------------------------------
1911
1912void GuiEditCtrl::findSnaps( snappingAxis axis, const Point2I& mousePoint, const RectI& minRegion, const RectI& midRegion, const RectI& maxRegion )
1913{
1914   // Find controls with edge in either minRegion, midRegion, or maxRegion
1915   // (depending on snap settings).
1916   
1917   for( U32 i = 0, num = mSnapHits[ axis ].size(); i < num; ++ i )
1918   {
1919      GuiControl* ctrl = mSnapHits[ axis ][ i ];
1920      if( ctrl == getContentControl() && !mSnapToCanvas )
1921         continue;
1922         
1923      RectI bounds = ctrl->getGlobalBounds();
1924      bounds.point = getContentControl()->globalToLocalCoord( bounds.point );
1925      
1926      // Compute points on min, mid, and max lines of control.
1927      
1928      Point2I min = bounds.point;
1929      Point2I max = min + bounds.extent - Point2I( 1, 1 );
1930
1931      Point2I mid = min;
1932      mid.x += bounds.extent.x / 2;
1933      mid.y += bounds.extent.y / 2;
1934            
1935      // Test edge snap cases.
1936      
1937      if( mSnapToEdges )
1938      {
1939         // Min to min.
1940         
1941         if( minRegion.pointInRect( min ) )
1942            registerSnap( axis, mousePoint, min, SnapEdgeMin, ctrl );
1943      
1944         // Max to max.
1945         
1946         if( maxRegion.pointInRect( max ) )
1947            registerSnap( axis, mousePoint, max, SnapEdgeMax, ctrl );
1948         
1949         // Min to max.
1950         
1951         if( minRegion.pointInRect( max ) )
1952            registerSnap( axis, mousePoint, max, SnapEdgeMin, ctrl );
1953         
1954         // Max to min.
1955         
1956         if( maxRegion.pointInRect( min ) )
1957            registerSnap( axis, mousePoint, min, SnapEdgeMax, ctrl );
1958      }
1959      
1960      // Test center snap cases.
1961      
1962      if( mSnapToCenters )
1963      {
1964         // Mid to mid.
1965         
1966         if( midRegion.pointInRect( mid ) )
1967            registerSnap( axis, mousePoint, mid, SnapEdgeMid, ctrl );
1968      }
1969      
1970      // Test combined center+edge snap cases.
1971      
1972      if( mSnapToEdges && mSnapToCenters )
1973      {
1974         // Min to mid.
1975         
1976         if( minRegion.pointInRect( mid ) )
1977            registerSnap( axis, mousePoint, mid, SnapEdgeMin, ctrl );
1978         
1979         // Max to mid.
1980         
1981         if( maxRegion.pointInRect( mid ) )
1982            registerSnap( axis, mousePoint, mid, SnapEdgeMax, ctrl );
1983         
1984         // Mid to min.
1985         
1986         if( midRegion.pointInRect( min ) )
1987            registerSnap( axis, mousePoint, min, SnapEdgeMid, ctrl );
1988         
1989         // Mid to max.
1990         
1991         if( midRegion.pointInRect( max ) )
1992            registerSnap( axis, mousePoint, max, SnapEdgeMid, ctrl );
1993      }
1994   }
1995}
1996
1997//-----------------------------------------------------------------------------
1998
1999void GuiEditCtrl::doControlSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta )
2000{
2001   if( !mSnapToControls || ( !mSnapToEdges && !mSnapToCenters ) )
2002      return;
2003      
2004   // Allow restricting to just vertical (ALT+SHIFT) or just horizontal (ALT+CTRL)
2005   // snaps.
2006   
2007   bool snapAxisEnabled[ 2 ];
2008            
2009   if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_SHIFT )
2010      snapAxisEnabled[ SnapHorizontal ] = false;
2011   else
2012      snapAxisEnabled[ SnapHorizontal ] = true;
2013      
2014   if( event.modifier & SI_PRIMARY_ALT && event.modifier & SI_CTRL )
2015      snapAxisEnabled[ SnapVertical ] = false;
2016   else
2017      snapAxisEnabled[ SnapVertical ] = true;
2018   
2019   // Compute snap regions.  There is one region centered on and aligned with
2020   // each of the selection bounds edges plus two regions aligned on the selection
2021   // bounds center.  For the selection bounds origin, we use the point that the
2022   // selection would be at, if we had already done the mouse drag.
2023   
2024   RectI snapRegions[ 2 ][ 3 ];
2025   Point2I projectedOrigin( selectionBounds.point + delta );
2026   dMemset( snapRegions, 0, sizeof( snapRegions ) );
2027   
2028   for( U32 axis = 0; axis < 2; ++ axis )
2029   {
2030      if( !snapAxisEnabled[ axis ] )
2031         continue;
2032         
2033      if( mSizingMode == sizingNone ||
2034          ( axis == 0 && mSizingMode & sizingLeft ) ||
2035          ( axis == 1 && mSizingMode & sizingTop ) )
2036         snapRegions[ axis ][ 0 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin );
2037         
2038      if( mSizingMode == sizingNone )
2039         snapRegions[ axis ][ 1 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin + Point2I( selectionBounds.extent.x / 2, selectionBounds.extent.y / 2 ) );
2040         
2041      if( mSizingMode == sizingNone ||
2042          ( axis == 0 && mSizingMode & sizingRight ) ||
2043          ( axis == 1 && mSizingMode & sizingBottom ) )
2044         snapRegions[ axis ][ 2 ] = getSnapRegion( ( snappingAxis ) axis, projectedOrigin + selectionBounds.extent - Point2I( 1, 1 ) );
2045   }
2046   
2047   // Find hit controls.
2048   
2049   canHitSelectedControls( false );
2050   
2051   if( mSnapToEdges )
2052   {
2053      for( U32 axis = 0; axis < 2; ++ axis )
2054         if( snapAxisEnabled[ axis ] )
2055         {
2056            getContentControl()->findHitControls( snapRegions[ axis ][ 0 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse );
2057            getContentControl()->findHitControls( snapRegions[ axis ][ 2 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse );
2058         }
2059   }
2060   if( mSnapToCenters && mSizingMode == sizingNone )
2061   {
2062      for( U32 axis = 0; axis < 2; ++ axis )
2063         if( snapAxisEnabled[ axis ] )
2064            getContentControl()->findHitControls( snapRegions[ axis ][ 1 ], mSnapHits[ axis ], HIT_NoCanHitNoRecurse );
2065   }
2066   
2067   canHitSelectedControls( true );
2068   
2069   // Add the content control itself to the hit controls
2070   // so we can always get a snap on it.
2071   
2072   if( mSnapToCanvas )
2073   {
2074      mSnapHits[ 0 ].push_back( mContentControl );
2075      mSnapHits[ 1 ].push_back( mContentControl );
2076   }
2077            
2078   // Find snaps.
2079   
2080   for( U32 i = 0; i < 2; ++ i )
2081      if( snapAxisEnabled[ i ] )
2082         findSnaps( ( snappingAxis ) i,
2083                    event.mousePoint,
2084                    snapRegions[ i ][ 0 ],
2085                    snapRegions[ i ][ 1 ],
2086                    snapRegions[ i ][ 2 ] );
2087                                       
2088   // Clean up.
2089   
2090   mSnapHits[ 0 ].clear();
2091   mSnapHits[ 1 ].clear();
2092}
2093
2094//-----------------------------------------------------------------------------
2095
2096void GuiEditCtrl::doGridSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta )
2097{
2098   delta += selectionBounds.point;
2099         
2100   if( mGridSnap.x )
2101      delta.x -= delta.x % mGridSnap.x;
2102   if( mGridSnap.y )
2103      delta.y -= delta.y % mGridSnap.y;
2104
2105   delta -= selectionBounds.point;
2106}
2107
2108//-----------------------------------------------------------------------------
2109
2110void GuiEditCtrl::doGuideSnap( const GuiEvent& event, const RectI& selectionBounds, const RectI& selectionBoundsGlobal, Point2I& delta )
2111{
2112   if( !mSnapToGuides )
2113      return;
2114      
2115   Point2I min = getContentControl()->localToGlobalCoord( selectionBounds.point + delta );
2116   Point2I mid = min + selectionBounds.extent / 2;
2117   Point2I max = min + selectionBounds.extent - Point2I( 1, 1 );
2118   
2119   for( U32 axis = 0; axis < 2; ++ axis )
2120   {
2121      if( mSnapToEdges )
2122      {
2123         S32 guideMin = -1;
2124         S32 guideMax = -1;
2125         
2126         if( mSizingMode == sizingNone ||
2127             ( axis == 0 && mSizingMode & sizingLeft ) ||
2128             ( axis == 1 && mSizingMode & sizingTop ) )
2129            guideMin = findGuide( ( guideAxis ) axis, min, mSnapSensitivity );
2130         if( mSizingMode == sizingNone ||
2131             ( axis == 0 && mSizingMode & sizingRight ) ||
2132             ( axis == 1 && mSizingMode & sizingBottom ) )
2133            guideMax = findGuide( ( guideAxis ) axis, max, mSnapSensitivity );
2134         
2135         Point2I pos( 0, 0 );
2136         
2137         if( guideMin != -1 )
2138         {
2139            pos[ axis ] = mGuides[ axis ][ guideMin ];
2140            registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMin );
2141         }
2142         
2143         if( guideMax != -1 )
2144         {
2145            pos[ axis ] = mGuides[ axis ][ guideMax ];
2146            registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMax );
2147         }
2148      }
2149      
2150      if( mSnapToCenters && mSizingMode == sizingNone )
2151      {
2152         const S32 guideMid = findGuide( ( guideAxis ) axis, mid, mSnapSensitivity );
2153         if( guideMid != -1 )
2154         {
2155            Point2I pos( 0, 0 );
2156            pos[ axis ] = mGuides[ axis ][ guideMid ];
2157            registerSnap( ( snappingAxis ) axis, event.mousePoint, pos, SnapEdgeMid );
2158         }
2159      }
2160   }
2161}
2162
2163//-----------------------------------------------------------------------------
2164
2165void GuiEditCtrl::doSnapping( const GuiEvent& event, const RectI& selectionBounds, Point2I& delta )
2166{
2167   // Clear snapping.  If we have snapping on, we want to find a new best snap.
2168   
2169   mSnapped[ SnapVertical ] = false;
2170   mSnapped[ SnapHorizontal ] = false;
2171   
2172   // Compute global bounds.
2173   
2174   RectI selectionBoundsGlobal = selectionBounds;
2175   selectionBoundsGlobal.point = getContentControl()->localToGlobalCoord( selectionBoundsGlobal.point );
2176
2177   // Apply the snaps.
2178   
2179   doGridSnap( event, selectionBounds, selectionBoundsGlobal, delta );
2180   doGuideSnap( event, selectionBounds, selectionBoundsGlobal, delta );
2181   doControlSnap( event, selectionBounds, selectionBoundsGlobal, delta );
2182
2183  // If we have a horizontal snap, compute a delta.
2184
2185   if( mSnapped[ SnapVertical ] )
2186      snapDelta( SnapVertical, mSnapEdge[ SnapVertical ], mSnapOffset[ SnapVertical ], selectionBoundsGlobal, delta );
2187   
2188   // If we have a vertical snap, compute a delta.
2189   
2190   if( mSnapped[ SnapHorizontal ] )
2191      snapDelta( SnapHorizontal, mSnapEdge[ SnapHorizontal ], mSnapOffset[ SnapHorizontal ], selectionBoundsGlobal, delta );
2192}
2193
2194//-----------------------------------------------------------------------------
2195
2196void GuiEditCtrl::snapToGrid( Point2I& point )
2197{
2198   if( mGridSnap.x )
2199      point.x -= point.x % mGridSnap.x;
2200   if( mGridSnap.y )
2201      point.y -= point.y % mGridSnap.y;
2202}
2203
2204//=============================================================================
2205//    Misc.
2206//=============================================================================
2207// MARK: ---- Misc ----
2208
2209//-----------------------------------------------------------------------------
2210
2211void GuiEditCtrl::setContentControl(GuiControl *root)
2212{
2213   mContentControl = root;
2214   if( root != NULL )
2215      root->mIsContainer = true;
2216      
2217   setCurrentAddSet( root );
2218}
2219
2220//-----------------------------------------------------------------------------
2221
2222void GuiEditCtrl::setEditMode(bool value)
2223{
2224   mActive = value;
2225
2226   clearSelection();
2227   if( mActive && mAwake )
2228      mCurrentAddSet = NULL;
2229}
2230
2231//-----------------------------------------------------------------------------
2232
2233void GuiEditCtrl::setMouseMode( mouseModes mode )
2234{
2235   if( mMouseDownMode != mode )
2236   {
2237      mMouseDownMode = mode;
2238
2239      onMouseModeChange_callback();
2240   }
2241}
2242
2243//-----------------------------------------------------------------------------
2244
2245void GuiEditCtrl::setCurrentAddSet(GuiControl *ctrl, bool doclearSelection)
2246{
2247   if (ctrl != mCurrentAddSet)
2248   {
2249      if(doclearSelection)
2250         clearSelection();
2251
2252      mCurrentAddSet = ctrl;
2253   }
2254}
2255
2256//-----------------------------------------------------------------------------
2257
2258GuiControl* GuiEditCtrl::getCurrentAddSet()
2259{
2260   if( !mCurrentAddSet )
2261      setCurrentAddSet( mContentControl, false );
2262      
2263   return mCurrentAddSet;
2264}
2265
2266//-----------------------------------------------------------------------------
2267
2268void GuiEditCtrl::addNewControl(GuiControl *ctrl)
2269{
2270   getCurrentAddSet()->addObject(ctrl);
2271   select( ctrl );
2272
2273   // undo
2274   onAddNewCtrl_callback( ctrl );
2275}
2276
2277//-----------------------------------------------------------------------------
2278
2279S32 GuiEditCtrl::getSizingHitKnobs(const Point2I &pt, const RectI &box)
2280{
2281   S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1;
2282   S32 cx = (lx + rx) >> 1;
2283   S32 ty = box.point.y, by = box.point.y + box.extent.y - 1;
2284   S32 cy = (ty + by) >> 1;
2285   
2286   // adjust nuts, so they dont straddle the controls
2287   lx -= NUT_SIZE;
2288   ty -= NUT_SIZE;
2289   rx += NUT_SIZE;
2290   by += NUT_SIZE;
2291
2292   if (inNut(pt, lx, ty))
2293      return sizingLeft | sizingTop;
2294   if (inNut(pt, cx, ty))
2295      return sizingTop;
2296   if (inNut(pt, rx, ty))
2297      return sizingRight | sizingTop;
2298   if (inNut(pt, lx, by))
2299      return sizingLeft | sizingBottom;
2300   if (inNut(pt, cx, by))
2301      return sizingBottom;
2302   if (inNut(pt, rx, by))
2303      return sizingRight | sizingBottom;
2304   if (inNut(pt, lx, cy))
2305      return sizingLeft;
2306   if (inNut(pt, rx, cy))
2307      return sizingRight;
2308   return sizingNone;
2309}
2310
2311//-----------------------------------------------------------------------------
2312
2313void GuiEditCtrl::getDragRect(RectI &box)
2314{
2315   box.point.x = getMin(mLastMousePos.x, mSelectionAnchor.x);
2316   box.extent.x = getMax(mLastMousePos.x, mSelectionAnchor.x) - box.point.x + 1;
2317   box.point.y = getMin(mLastMousePos.y, mSelectionAnchor.y);
2318   box.extent.y = getMax(mLastMousePos.y, mSelectionAnchor.y) - box.point.y + 1;
2319}
2320
2321//-----------------------------------------------------------------------------
2322
2323void GuiEditCtrl::getCursor(GuiCursor *&cursor, bool &showCursor, const GuiEvent &lastGuiEvent)
2324{
2325   GuiCanvas *pRoot = getRoot();
2326   if( !pRoot )
2327      return;
2328
2329   showCursor = false;
2330   cursor = NULL;
2331   
2332   Point2I ctOffset;
2333   Point2I cext;
2334   GuiControl *ctrl;
2335
2336   Point2I mousePos  = globalToLocalCoord(lastGuiEvent.mousePoint);
2337
2338   PlatformWindow *pWindow = static_cast<GuiCanvas*>(getRoot())->getPlatformWindow();
2339   AssertFatal(pWindow != NULL,"GuiControl without owning platform window!  This should not be possible.");
2340   PlatformCursorController *pController = pWindow->getCursorController();
2341   AssertFatal(pController != NULL,"PlatformWindow without an owned CursorController!");
2342
2343   S32 desiredCursor = PlatformCursorController::curArrow;
2344
2345   // first see if we hit a sizing knob on the currently selected control...
2346   if (mSelectedControls.size() == 1 )
2347   {
2348      ctrl = mSelectedControls.first();
2349      cext = ctrl->getExtent();
2350      ctOffset = globalToLocalCoord(ctrl->localToGlobalCoord(Point2I(0,0)));
2351      RectI box(ctOffset.x,ctOffset.y,cext.x, cext.y);
2352
2353      GuiEditCtrl::sizingModes sizeMode = (GuiEditCtrl::sizingModes)getSizingHitKnobs(mousePos, box);
2354
2355      if( mMouseDownMode == SizingSelection )
2356      {
2357         if ( ( mSizingMode == ( sizingBottom | sizingRight ) ) || ( mSizingMode == ( sizingTop | sizingLeft ) ) )
2358            desiredCursor = PlatformCursorController::curResizeNWSE;
2359         else if (  ( mSizingMode == ( sizingBottom | sizingLeft ) ) || ( mSizingMode == ( sizingTop | sizingRight ) ) )
2360            desiredCursor = PlatformCursorController::curResizeNESW;
2361         else if ( mSizingMode == sizingLeft || mSizingMode == sizingRight ) 
2362            desiredCursor = PlatformCursorController::curResizeVert;
2363         else if (mSizingMode == sizingTop || mSizingMode == sizingBottom )
2364            desiredCursor = PlatformCursorController::curResizeHorz;
2365      }
2366      else
2367      {
2368         // Check for current mouse position after checking for actual sizing mode
2369         if ( ( sizeMode == ( sizingBottom | sizingRight ) ) || ( sizeMode == ( sizingTop | sizingLeft ) ) )
2370            desiredCursor = PlatformCursorController::curResizeNWSE;
2371         else if ( ( sizeMode == ( sizingBottom | sizingLeft ) ) || ( sizeMode == ( sizingTop | sizingRight ) ) )
2372            desiredCursor = PlatformCursorController::curResizeNESW;
2373         else if (sizeMode == sizingLeft || sizeMode == sizingRight )
2374            desiredCursor = PlatformCursorController::curResizeVert;
2375         else if (sizeMode == sizingTop || sizeMode == sizingBottom )
2376            desiredCursor = PlatformCursorController::curResizeHorz;
2377      }
2378   }
2379   
2380   if( mMouseDownMode == MovingSelection && cursor == NULL )
2381      desiredCursor = PlatformCursorController::curResizeAll;
2382
2383   if( pRoot->mCursorChanged != desiredCursor )
2384   {
2385      // We've already changed the cursor, 
2386      // so set it back before we change it again.
2387      if(pRoot->mCursorChanged != -1)
2388         pController->popCursor();
2389
2390      // Now change the cursor shape
2391      pController->pushCursor(desiredCursor);
2392      pRoot->mCursorChanged = desiredCursor;
2393   }
2394}
2395
2396//-----------------------------------------------------------------------------
2397
2398void GuiEditCtrl::setSnapToGrid(U32 gridsize)
2399{
2400   if( gridsize != 0 )
2401      gridsize = getMax( gridsize, ( U32 ) MIN_GRID_SIZE );
2402   mGridSnap.set( gridsize, gridsize );
2403}
2404
2405//-----------------------------------------------------------------------------
2406
2407void GuiEditCtrl::controlInspectPreApply(GuiControl* object)
2408{
2409   // undo
2410   onControlInspectPreApply_callback( object );
2411}
2412
2413//-----------------------------------------------------------------------------
2414
2415void GuiEditCtrl::controlInspectPostApply(GuiControl* object)
2416{
2417   // undo
2418   onControlInspectPostApply_callback( object );
2419}
2420
2421//-----------------------------------------------------------------------------
2422
2423void GuiEditCtrl::startDragMove( const Point2I& startPoint )
2424{
2425   mDragMoveUndo = true;
2426   
2427   // For calculating mouse delta
2428   mDragBeginPoint = globalToLocalCoord( startPoint );
2429
2430   // Allocate enough space for our selected controls
2431   mDragBeginPoints.reserve( mSelectedControls.size() );
2432
2433   // For snapping to origin
2434   Vector<GuiControl *>::iterator i;
2435   for(i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
2436      mDragBeginPoints.push_back( (*i)->getPosition() );
2437
2438   // Set Mouse Mode
2439   setMouseMode( MovingSelection );
2440   
2441   // undo
2442   onPreEdit_callback( getSelectedSet() );
2443}
2444
2445//-----------------------------------------------------------------------------
2446
2447void GuiEditCtrl::startDragRectangle( const Point2I& startPoint )
2448{
2449   mSelectionAnchor = globalToLocalCoord( startPoint );
2450   setMouseMode( DragSelecting );
2451}
2452
2453//-----------------------------------------------------------------------------
2454
2455void GuiEditCtrl::startDragClone( const Point2I& startPoint )
2456{
2457   mDragBeginPoint = globalToLocalCoord( startPoint );
2458   
2459   setMouseMode( DragClone );
2460}
2461
2462//-----------------------------------------------------------------------------
2463
2464void GuiEditCtrl::startMouseGuideDrag( guideAxis axis, U32 guideIndex, bool lockMouse )
2465{
2466   mDragGuideIndex[ axis ] = guideIndex;
2467   mDragGuide[ axis ] = true;
2468   
2469   setMouseMode( DragGuide );
2470   
2471   // Grab the mouse.
2472   
2473   if( lockMouse )
2474      mouseLock();
2475}
2476
2477//=============================================================================
2478//    Console Methods.
2479//=============================================================================
2480// MARK: ---- Console Methods ----
2481
2482//-----------------------------------------------------------------------------
2483
2484DefineEngineMethod( GuiEditCtrl, getContentControl, S32, (), , "() - Return the toplevel control edited inside the GUI editor." )
2485{
2486   GuiControl* ctrl = object->getContentControl();
2487   if( ctrl )
2488      return ctrl->getId();
2489   else
2490      return 0;
2491}
2492
2493//-----------------------------------------------------------------------------
2494
2495DefineEngineMethod( GuiEditCtrl, setContentControl, void, (GuiControl *ctrl ), , "( GuiControl ctrl ) - Set the toplevel control to edit in the GUI editor." )
2496{
2497   if (ctrl)
2498      object->setContentControl(ctrl);
2499}
2500
2501//-----------------------------------------------------------------------------
2502
2503DefineEngineMethod( GuiEditCtrl, addNewCtrl, void, (GuiControl *ctrl), , "(GuiControl ctrl)")
2504{
2505   if (ctrl)
2506      object->addNewControl(ctrl);
2507}
2508
2509//-----------------------------------------------------------------------------
2510
2511DefineEngineMethod( GuiEditCtrl, addSelection, void, (S32 id), , "selects a control.")
2512{
2513   object->addSelection(id);
2514}
2515
2516//-----------------------------------------------------------------------------
2517
2518DefineEngineMethod( GuiEditCtrl, removeSelection, void, (S32 id), , "deselects a control.")
2519{
2520   object->removeSelection(id);
2521}
2522
2523//-----------------------------------------------------------------------------
2524
2525DefineEngineMethod( GuiEditCtrl, clearSelection, void, (), , "Clear selected controls list.")
2526{
2527   object->clearSelection();
2528}
2529
2530//-----------------------------------------------------------------------------
2531
2532DefineEngineMethod( GuiEditCtrl, select, void, (GuiControl *ctrl), , "(GuiControl ctrl)")
2533{
2534   if (ctrl)
2535   object->setSelection(ctrl, false);
2536}
2537
2538//-----------------------------------------------------------------------------
2539
2540DefineEngineMethod( GuiEditCtrl, setCurrentAddSet, void, (GuiControl *addSet), , "(GuiControl ctrl)")
2541{
2542   if (addSet)
2543   object->setCurrentAddSet(addSet);
2544}
2545
2546//-----------------------------------------------------------------------------
2547
2548DefineEngineMethod( GuiEditCtrl, getCurrentAddSet, S32, (), , "Returns the set to which new controls will be added")
2549{
2550   const GuiControl* add = object->getCurrentAddSet();
2551   return add ? add->getId() : 0;
2552}
2553
2554//-----------------------------------------------------------------------------
2555
2556DefineEngineMethod( GuiEditCtrl, toggle, void, (), , "Toggle activation.")
2557{
2558   object->setEditMode( !object->isActive() );
2559}
2560
2561//-----------------------------------------------------------------------------
2562
2563DefineEngineMethod( GuiEditCtrl, justify, void, (U32 mode), , "(int mode)" )
2564{
2565   object->justifySelection( (GuiEditCtrl::Justification)mode );
2566}
2567
2568//-----------------------------------------------------------------------------
2569
2570DefineEngineMethod( GuiEditCtrl, bringToFront, void, (), , "")
2571{
2572   object->bringToFront();
2573}
2574
2575//-----------------------------------------------------------------------------
2576
2577DefineEngineMethod( GuiEditCtrl, pushToBack, void, (), , "")
2578{
2579   object->pushToBack();
2580}
2581
2582//-----------------------------------------------------------------------------
2583
2584DefineEngineMethod( GuiEditCtrl, deleteSelection, void, (), , "() - Delete the selected controls.")
2585{
2586   object->deleteSelection();
2587}
2588
2589//-----------------------------------------------------------------------------
2590
2591DefineEngineMethod( GuiEditCtrl, moveSelection, void, (S32 dx, S32 dy), , "Move all controls in the selection by (dx,dy) pixels.")
2592{
2593   object->moveAndSnapSelection(Point2I(dx, dy));
2594}
2595
2596//-----------------------------------------------------------------------------
2597
2598DefineEngineMethod( GuiEditCtrl, saveSelection, void, (const char * filename), (nullAsType<const char*>()), "( string fileName=null ) - Save selection to file or clipboard.")
2599{
2600      
2601   object->saveSelection( filename );
2602}
2603
2604//-----------------------------------------------------------------------------
2605
2606DefineEngineMethod( GuiEditCtrl, loadSelection, void, (const char * filename), (nullAsType<const char*>()), "( string fileName=null ) - Load selection from file or clipboard.")
2607{
2608
2609   object->loadSelection( filename );
2610}
2611
2612//-----------------------------------------------------------------------------
2613
2614DefineEngineMethod( GuiEditCtrl, selectAll, void, (), , "()")
2615{
2616   object->selectAll();
2617}
2618
2619//-----------------------------------------------------------------------------
2620
2621DefineEngineMethod( GuiEditCtrl, getSelection, SimSet*, (),,
2622   "Gets the set of GUI controls currently selected in the editor." )
2623{
2624   return object->getSelectedSet();
2625}
2626
2627//-----------------------------------------------------------------------------
2628
2629DefineEngineMethod( GuiEditCtrl, getNumSelected, S32, (), , "() - Return the number of controls currently selected." )
2630{
2631   return object->getNumSelected();
2632}
2633
2634//-----------------------------------------------------------------------------
2635
2636DefineEngineMethod( GuiEditCtrl, getSelectionGlobalBounds, const char*, (), , "() - Returns global bounds of current selection as vector 'x y width height'." )
2637{
2638   RectI bounds = object->getSelectionGlobalBounds();
2639   String str = String::ToString( "%i %i %i %i", bounds.point.x, bounds.point.y, bounds.extent.x, bounds.extent.y );
2640   
2641   char* buffer = Con::getReturnBuffer( str.size() );
2642   dStrcpy( buffer, str.c_str(), str.size() );
2643   
2644   return buffer;
2645}
2646
2647//-----------------------------------------------------------------------------
2648
2649DefineEngineMethod( GuiEditCtrl, selectParents, void, ( bool addToSelection ), (false), "( bool addToSelection=false ) - Select parents of currently selected controls." )
2650{
2651      
2652   object->selectParents( addToSelection );
2653}
2654
2655//-----------------------------------------------------------------------------
2656
2657DefineEngineMethod( GuiEditCtrl, selectChildren, void, ( bool addToSelection ), (false), "( bool addToSelection=false ) - Select children of currently selected controls." )
2658{
2659      
2660   object->selectChildren( addToSelection );
2661}
2662
2663//-----------------------------------------------------------------------------
2664
2665DefineEngineMethod( GuiEditCtrl, getTrash, SimGroup*, (),,
2666   "Gets the GUI controls(s) that are currently in the trash.")
2667{
2668   return object->getTrash();
2669}
2670
2671//-----------------------------------------------------------------------------
2672
2673DefineEngineMethod(GuiEditCtrl, setSnapToGrid, void, (U32 gridsize), , "GuiEditCtrl.setSnapToGrid(gridsize)")
2674{
2675   object->setSnapToGrid(gridsize);
2676}
2677
2678//-----------------------------------------------------------------------------
2679
2680DefineEngineMethod( GuiEditCtrl, readGuides, void, ( GuiControl* ctrl, S32 axis ), (-1), "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." )
2681{
2682   // Find the control.
2683   
2684   if( !ctrl  )
2685   {
2686      return;
2687   }
2688   
2689   // Read the guides.
2690   
2691   if( axis != -1 )
2692   {
2693      if( axis < 0 || axis > 1 )
2694      {
2695         Con::errorf( "GuiEditCtrl::readGuides - invalid axis '%s'", axis );
2696         return;
2697      }
2698      
2699      object->readGuides( ( GuiEditCtrl::guideAxis ) axis, ctrl );
2700   }
2701   else
2702   {
2703      object->readGuides( GuiEditCtrl::GuideHorizontal, ctrl );
2704      object->readGuides( GuiEditCtrl::GuideVertical, ctrl );
2705   }
2706}
2707
2708//-----------------------------------------------------------------------------
2709
2710DefineEngineMethod( GuiEditCtrl, writeGuides, void, ( GuiControl* ctrl, S32 axis ), ( -1), "( GuiControl ctrl [, int axis ] ) - Write the guides to the given control." )
2711{
2712   // Find the control.
2713   
2714   if( ! ctrl ) 
2715   {
2716      return;
2717   }
2718   
2719   // Write the guides.
2720   
2721   if( axis != -1 )
2722   {
2723      if( axis < 0 || axis > 1 )
2724      {
2725         Con::errorf( "GuiEditCtrl::writeGuides - invalid axis '%s'", axis );
2726         return;
2727      }
2728      
2729      object->writeGuides( ( GuiEditCtrl::guideAxis ) axis, ctrl );
2730   }
2731   else
2732   {
2733      object->writeGuides( GuiEditCtrl::GuideHorizontal, ctrl );
2734      object->writeGuides( GuiEditCtrl::GuideVertical, ctrl );
2735   }
2736}
2737
2738//-----------------------------------------------------------------------------
2739
2740DefineEngineMethod( GuiEditCtrl, clearGuides, void, ( S32 axis ), (-1), "( [ int axis ] ) - Clear all currently set guide lines." )
2741{
2742   if( axis != -1 )
2743   {
2744      if( axis < 0 || axis > 1 )
2745      {
2746         Con::errorf( "GuiEditCtrl::clearGuides - invalid axis '%i'", axis );
2747         return;
2748      }
2749      
2750      object->clearGuides( ( GuiEditCtrl::guideAxis ) axis );
2751   }
2752   else
2753   {
2754      object->clearGuides( GuiEditCtrl::GuideHorizontal );
2755      object->clearGuides( GuiEditCtrl::GuideVertical );
2756   }
2757}
2758
2759//-----------------------------------------------------------------------------
2760
2761DefineEngineMethod( GuiEditCtrl, fitIntoParents, void, (bool width, bool height), (true, true), "( bool width=true, bool height=true ) - Fit selected controls into their parents." )
2762{
2763      
2764   object->fitIntoParents( width, height );
2765}
2766
2767//-----------------------------------------------------------------------------
2768
2769DefineEngineMethod( GuiEditCtrl, getMouseMode, const char*, (), , "() - Return the current mouse mode." )
2770{
2771   switch( object->getMouseMode() )
2772   {
2773      case GuiEditCtrl::Selecting:
2774         return "Selecting";
2775      
2776      case GuiEditCtrl::DragSelecting:
2777         return "DragSelecting";
2778
2779      case GuiEditCtrl::MovingSelection:
2780         return "MovingSelection";
2781      
2782      case GuiEditCtrl::SizingSelection:
2783         return "SizingSelection";
2784            
2785      case GuiEditCtrl::DragGuide:
2786         return "DragGuide";
2787         
2788      case GuiEditCtrl::DragClone:
2789         return "DragClone";
2790         
2791      default:
2792         return "";
2793   }
2794}
2795
2796//=============================================================================
2797//    GuiEditorRuler.
2798//=============================================================================
2799
2800class GuiEditorRuler : public GuiControl
2801{
2802   public:
2803   
2804      typedef GuiControl Parent;
2805      
2806   protected:
2807   
2808      String mRefCtrlName;
2809      String mEditCtrlName;
2810      
2811      GuiScrollCtrl* mRefCtrl;
2812      GuiEditCtrl* mEditCtrl;
2813
2814   public:
2815
2816      enum EOrientation
2817      {
2818         ORIENTATION_Horizontal,
2819         ORIENTATION_Vertical
2820      };
2821      
2822      GuiEditorRuler()
2823         : mRefCtrl( 0 ),
2824           mEditCtrl( 0 )
2825      {
2826      }
2827      
2828      EOrientation getOrientation() const
2829      {
2830         if( getWidth() > getHeight() )
2831            return ORIENTATION_Horizontal;
2832         else
2833            return ORIENTATION_Vertical;
2834      }
2835      
2836      bool onWake()
2837      {
2838         if( !Parent::onWake() )
2839            return false;
2840            
2841         if( !mEditCtrlName.isEmpty() && !Sim::findObject( mEditCtrlName, mEditCtrl ) )
2842            Con::errorf( "GuiEditorRuler::onWake() - no GuiEditCtrl '%s'", mEditCtrlName.c_str() );
2843         
2844         if( !mRefCtrlName.isEmpty() && !Sim::findObject( mRefCtrlName, mRefCtrl ) )
2845            Con::errorf( "GuiEditorRuler::onWake() - no GuiScrollCtrl '%s'", mRefCtrlName.c_str() );
2846         
2847         return true;
2848      }
2849
2850      void onPreRender()
2851      {
2852         setUpdate();
2853      }
2854      
2855      void onMouseDown( const GuiEvent& event )
2856      {         
2857         if( !mEditCtrl )
2858            return;
2859            
2860         // Determine the guide axis.
2861         
2862         GuiEditCtrl::guideAxis axis;
2863         if( getOrientation() == ORIENTATION_Horizontal )
2864            axis = GuiEditCtrl::GuideHorizontal;
2865         else
2866            axis = GuiEditCtrl::GuideVertical;
2867            
2868         // Start dragging a new guide out in the editor.
2869         
2870         U32 guideIndex = mEditCtrl->addGuide( axis, 0 );
2871         mEditCtrl->startMouseGuideDrag( axis, guideIndex );
2872      }
2873      
2874      void onRender(Point2I offset, const RectI &updateRect)
2875      {
2876         GFX->getDrawUtil()->drawRectFill(updateRect, ColorI::WHITE);
2877         
2878         Point2I choffset(0,0);
2879         if( mRefCtrl != NULL )
2880            choffset = mRefCtrl->getChildPos();
2881
2882         if( getOrientation() == ORIENTATION_Horizontal )
2883         {
2884            // it's horizontal.
2885            for(U32 i = 0; i < getWidth(); i++)
2886            {
2887               S32 x = offset.x + i;
2888               S32 pos = i - choffset.x;
2889               if(!(pos % 10))
2890               {
2891                  S32 start = 6;
2892                  if(!(pos %20))
2893                     start = 4;
2894                  if(!(pos % 100))
2895                     start = 1;
2896                  GFX->getDrawUtil()->drawLine(x, offset.y + start, x, offset.y + 10, ColorI::BLACK);
2897               }
2898            }
2899         }
2900         else
2901         {
2902            // it's vertical.
2903            for(U32 i = 0; i < getHeight(); i++)
2904            {
2905               S32 y = offset.y + i;
2906               S32 pos = i - choffset.y;
2907               if(!(pos % 10))
2908               {
2909                  S32 start = 6;
2910                  if(!(pos %20))
2911                     start = 4;
2912                  if(!(pos % 100))
2913                     start = 1;
2914                  GFX->getDrawUtil()->drawLine(offset.x + start, y, offset.x + 10, y, ColorI::BLACK);
2915               }
2916            }
2917         }
2918      }
2919      static void initPersistFields()
2920      {
2921         addField( "refCtrl", TypeRealString, Offset( mRefCtrlName, GuiEditorRuler ) );
2922         addField( "editCtrl", TypeRealString, Offset( mEditCtrlName, GuiEditorRuler ) );
2923         
2924         Parent::initPersistFields();
2925      }
2926      
2927      DECLARE_CONOBJECT(GuiEditorRuler);
2928      DECLARE_CATEGORY( "Gui Editor" );
2929};
2930
2931IMPLEMENT_CONOBJECT(GuiEditorRuler);
2932
2933ConsoleDocClass( GuiEditorRuler,
2934   "@brief Visual representation of markers on top and left sides of GUI Editor\n\n"
2935   "Editor use only.\n\n"
2936   "@internal"
2937);
2938