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" )
DefineConsoleMethod(GuiEditCtrl , addNewCtrl , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineConsoleMethod(GuiEditCtrl , addSelection , void , (S32 id) , "selects a control." )
DefineConsoleMethod(GuiEditCtrl , bringToFront , void , () , "" )
DefineConsoleMethod(GuiEditCtrl , clearGuides , void , (S32 axis) , (-1) , "( [ int axis ] ) - Clear all currently set guide lines." )
DefineConsoleMethod(GuiEditCtrl , clearSelection , void , () , "Clear selected controls list." )
DefineConsoleMethod(GuiEditCtrl , deleteSelection , void , () , "() - Delete the selected controls." )
DefineConsoleMethod(GuiEditCtrl , fitIntoParents , void , (bool width, bool height) , (true, true) , "( bool width=true, bool height=true ) - Fit selected controls into their parents." )
DefineConsoleMethod(GuiEditCtrl , getContentControl , S32 , () , "() - Return the toplevel <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> edited inside the GUI editor." )
DefineConsoleMethod(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" )
DefineConsoleMethod(GuiEditCtrl , getMouseMode , const char * , () , "() - Return the current mouse mode." )
DefineConsoleMethod(GuiEditCtrl , getNumSelected , S32 , () , "() - Return the number of controls currently selected." )
DefineConsoleMethod(GuiEditCtrl , getSelectionGlobalBounds , const char * , () , "() - Returns global bounds of current selection as vector 'x y width height'." )
DefineConsoleMethod(GuiEditCtrl , justify , void , (U32 mode) , "(int <a href="/coding/file/zipobject_8cpp/#zipobject_8cpp_1ac6c3dfb4c3a68f849f32cbfb21da4e77">mode</a>)" )
DefineConsoleMethod(GuiEditCtrl , loadSelection , void , (const char *filename) , (NULL) , "( string fileName=null ) - Load selection from <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> or clipboard." )
DefineConsoleMethod(GuiEditCtrl , moveSelection , void , (S32 dx, S32 dy) , "Move all controls in the selection by (dx,dy) pixels." )
DefineConsoleMethod(GuiEditCtrl , pushToBack , void , () , "" )
DefineConsoleMethod(GuiEditCtrl , readGuides , void , (GuiControl *ctrl, S32 axis) , (-1) , "( GuiControl ctrl [, int axis ] ) - Read the guides from the given control." )
DefineConsoleMethod(GuiEditCtrl , removeSelection , void , (S32 id) , "deselects a control." )
DefineConsoleMethod(GuiEditCtrl , saveSelection , void , (const char *filename) , (NULL) , "( 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." )
DefineConsoleMethod(GuiEditCtrl , select , void , (GuiControl *ctrl) , "(GuiControl ctrl)" )
DefineConsoleMethod(GuiEditCtrl , selectAll , void , () , "()" )
DefineConsoleMethod(GuiEditCtrl , selectChildren , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select children of currently selected controls." )
DefineConsoleMethod(GuiEditCtrl , selectParents , void , (bool addToSelection) , (false) , "( bool addToSelection=false ) - Select parents of currently selected controls." )
DefineConsoleMethod(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." )
DefineConsoleMethod(GuiEditCtrl , setCurrentAddSet , void , (GuiControl *addSet) , "(GuiControl ctrl)" )
DefineConsoleMethod(GuiEditCtrl , setSnapToGrid , void , (U32 gridsize) , "GuiEditCtrl.setSnapToGrid(gridsize)" )
DefineConsoleMethod(GuiEditCtrl , toggle , void , () , "Toggle activation." )
DefineConsoleMethod(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." )
DefineEngineMethod(GuiEditCtrl , getSelection , SimSet * , () , "Gets the set of GUI controls currently selected in the editor." )
DefineEngineMethod(GuiEditCtrl , getTrash , SimGroup * , () , "Gets the GUI controls(s) that are currently in the trash." )
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" )

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

DefineConsoleMethod(GuiEditCtrl , addSelection , void , (S32 id) , "selects a control." )

DefineConsoleMethod(GuiEditCtrl , bringToFront , void , () , "" )

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

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

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

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

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

DefineConsoleMethod(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" )

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

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

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

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

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

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

DefineConsoleMethod(GuiEditCtrl , pushToBack , void , () , "" )

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

DefineConsoleMethod(GuiEditCtrl , removeSelection , void , (S32 id) , "deselects a control." )

DefineConsoleMethod(GuiEditCtrl , saveSelection , void , (const char *filename) , (NULL) , "( 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." )

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

DefineConsoleMethod(GuiEditCtrl , selectAll , void , () , "()" )

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

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

DefineConsoleMethod(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." )

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

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

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

DefineConsoleMethod(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." )

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

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

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