group.cpp

Engine/source/gui/editor/inspector/group.cpp

More...

Public Functions

ConsoleDocClass(GuiInspectorGroup , "@brief The <a href="/coding/class/classguiinspectorgroup/">GuiInspectorGroup</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> helper <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that the inspector " "makes use of which houses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> collapsible pane type <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> separating " "inspected objects fields into <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">groups.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )

Detailed Description

Public Functions

ConsoleDocClass(GuiInspectorGroup , "@brief The <a href="/coding/class/classguiinspectorgroup/">GuiInspectorGroup</a> <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> is <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> helper <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> that the inspector " "makes use of which houses <a href="/coding/file/pointer_8h/#pointer_8h_1aeeddce917cf130d62c370b8f216026dd">a</a> collapsible pane type <a href="/coding/file/guieditctrl_8cpp/#guieditctrl_8cpp_1abb04e3738c4c5a96b3ade6fa47013a6c">control</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a2732ab74fa0237854c2ba0f75f88a624">for</a> separating " "inspected objects fields into <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">groups.\n\n</a>" "Editor use <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">only.\n\n</a>" "@internal" )

IMPLEMENT_CONOBJECT(GuiInspectorGroup )

  1
  2//-----------------------------------------------------------------------------
  3// Copyright (c) 2012 GarageGames, LLC
  4//
  5// Permission is hereby granted, free of charge, to any person obtaining a copy
  6// of this software and associated documentation files (the "Software"), to
  7// deal in the Software without restriction, including without limitation the
  8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9// sell copies of the Software, and to permit persons to whom the Software is
 10// furnished to do so, subject to the following conditions:
 11//
 12// The above copyright notice and this permission notice shall be included in
 13// all copies or substantial portions of the Software.
 14//
 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 21// IN THE SOFTWARE.
 22//-----------------------------------------------------------------------------
 23
 24#include "gui/editor/guiInspector.h"
 25#include "gui/editor/inspector/group.h"
 26#include "gui/editor/inspector/dynamicField.h"
 27#include "gui/editor/inspector/datablockField.h"
 28#include "gui/buttons/guiIconButtonCtrl.h"
 29
 30
 31IMPLEMENT_CONOBJECT(GuiInspectorGroup);
 32
 33ConsoleDocClass( GuiInspectorGroup,
 34   "@brief The GuiInspectorGroup control is a helper control that the inspector "
 35   "makes use of which houses a collapsible pane type control for separating "
 36   "inspected objects fields into groups.\n\n"
 37   "Editor use only.\n\n"
 38   "@internal"
 39);
 40
 41//#define DEBUG_SPEW
 42
 43
 44//-----------------------------------------------------------------------------
 45
 46GuiInspectorGroup::GuiInspectorGroup() 
 47 : mParent( NULL ), 
 48   mStack(NULL)
 49{
 50   setBounds(0,0,200,20);
 51
 52   mChildren.clear();
 53
 54   setCanSave( false );
 55
 56   // Make sure we receive our ticks.
 57   setProcessTicks();
 58   mMargin.set(0,0,5,0);
 59}
 60
 61//-----------------------------------------------------------------------------
 62
 63GuiInspectorGroup::GuiInspectorGroup( const String& groupName, 
 64                                      SimObjectPtr<GuiInspector> parent ) 
 65 : mParent( parent ), 
 66   mStack(NULL)
 67{
 68
 69   setBounds(0,0,200,20);
 70
 71   mCaption = groupName;
 72   setCanSave( false );
 73
 74   mChildren.clear();
 75   mMargin.set(0,0,4,0);
 76}
 77
 78//-----------------------------------------------------------------------------
 79
 80GuiInspectorGroup::~GuiInspectorGroup()
 81{  
 82}
 83
 84//-----------------------------------------------------------------------------
 85
 86bool GuiInspectorGroup::onAdd()
 87{
 88   setDataField( StringTable->insert("profile"), NULL, "GuiInspectorGroupProfile" );
 89
 90   if( !Parent::onAdd() )
 91      return false;
 92
 93   // Create our inner controls. Allow subclasses to provide other content.
 94   if(!createContent())
 95      return false;
 96
 97   inspectGroup();
 98
 99   return true;
100}
101
102//-----------------------------------------------------------------------------
103
104bool GuiInspectorGroup::createContent()
105{
106   // Create our field stack control
107   mStack = new GuiStackControl();
108
109   // Prefer GuiTransperantProfile for the stack.
110   mStack->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorStackProfile" );
111   mStack->setInternalName(StringTable->insert("stack"));
112   if( !mStack->registerObject() )
113   {
114      SAFE_DELETE( mStack );
115      return false;
116   }
117
118   addObject( mStack );
119   mStack->setField( "padding", "0" );
120   return true;
121}
122
123//-----------------------------------------------------------------------------
124
125void GuiInspectorGroup::animateToContents()
126{
127   calculateHeights();
128   if(size() > 0)
129      animateTo( mExpanded.extent.y );
130   else
131      animateTo( mHeader.extent.y );
132}
133
134//-----------------------------------------------------------------------------
135
136GuiInspectorField* GuiInspectorGroup::constructField( S32 fieldType )
137{
138   // See if we can construct a field of this type
139   ConsoleBaseType *cbt = ConsoleBaseType::getType(fieldType);
140   if( !cbt )
141      return NULL;
142
143   // Alright, is it a datablock?
144   if(cbt->isDatablock())
145   {
146      // Default to GameBaseData
147      StringTableEntry typeClassName = cbt->getTypeClassName();
148
149      if( mParent->getNumInspectObjects() == 1 && !dStricmp(typeClassName, "GameBaseData") )
150      {
151         // Try and setup the classname based on the object type
152         char className[256];
153         dSprintf(className,256,"%sData", mParent->getInspectObject( 0 )->getClassName());
154         // Walk the ACR list and find a matching class if any.
155         AbstractClassRep *walk = AbstractClassRep::getClassList();
156         while(walk)
157         {
158            if(!dStricmp(walk->getClassName(), className))
159               break;
160
161            walk = walk->getNextClass();
162         }
163
164         // We found a valid class
165         if (walk)
166            typeClassName = walk->getClassName();
167
168      }
169
170
171      GuiInspectorDatablockField *dbFieldClass = new GuiInspectorDatablockField( typeClassName );
172
173      // return our new datablock field with correct datablock type enumeration info
174      return dbFieldClass;
175}
176
177   // Nope, not a datablock. So maybe it has a valid inspector field override we can use?
178   if(!cbt->getInspectorFieldType())
179      // Nothing, so bail.
180      return NULL;
181
182   // Otherwise try to make it!
183   ConsoleObject *co = create(cbt->getInspectorFieldType());
184   GuiInspectorField *gif = dynamic_cast<GuiInspectorField*>(co);
185
186   if(!gif)
187   {
188      // Wasn't appropriate type, bail.
189      delete co;
190      return NULL;
191   }
192
193   return gif;
194}
195
196//-----------------------------------------------------------------------------
197
198GuiInspectorField *GuiInspectorGroup::findField( const char *fieldName )
199{
200   // If we don't have any field children we can't very well find one then can we?
201   if( mChildren.empty() )
202      return NULL;
203
204   Vector<GuiInspectorField*>::iterator i = mChildren.begin();
205
206   for( ; i != mChildren.end(); i++ )
207   {
208      if( (*i)->getFieldName() != NULL && dStricmp( (*i)->getFieldName(), fieldName ) == 0 )
209         return (*i);
210   }
211
212   return NULL;
213}
214
215//-----------------------------------------------------------------------------
216
217void GuiInspectorGroup::clearFields()
218{
219   // Deallocates all field related controls.
220   mStack->clear();
221
222   // Then just cleanup our vectors which also point to children
223   // that we keep for our own convenience.
224   mArrayCtrls.clear();
225   mChildren.clear();
226}
227
228//-----------------------------------------------------------------------------
229
230bool GuiInspectorGroup::inspectGroup()
231{
232   // We can't inspect a group without a target!
233   if( !mParent || !mParent->getNumInspectObjects() )
234      return false;
235
236   // to prevent crazy resizing, we'll just freeze our stack for a sec..
237   mStack->freeze(true);
238
239   bool bNoGroup = false;
240
241   // Un-grouped fields are all sorted into the 'general' group
242   if ( dStricmp( mCaption, "General" ) == 0 )
243      bNoGroup = true;
244      
245   // Just delete all fields and recreate them (like the dynamicGroup)
246   // because that makes creating controls for array fields a lot easier
247   clearFields();
248   
249   bool bNewItems = false;
250   bool bMakingArray = false;
251   GuiStackControl *pArrayStack = NULL;
252   GuiRolloutCtrl *pArrayRollout = NULL;
253   bool bGrabItems = false;
254
255   AbstractClassRep* commonAncestorClass = findCommonAncestorClass();
256   AbstractClassRep::FieldList& fieldList = commonAncestorClass->mFieldList;
257   for( AbstractClassRep::FieldList::iterator itr = fieldList.begin();
258        itr != fieldList.end(); ++ itr )
259   {
260      AbstractClassRep::Field* field = &( *itr );
261      if( field->type == AbstractClassRep::StartGroupFieldType )
262      {
263         // If we're dealing with general fields, always set grabItems to true (to skip them)
264         if( bNoGroup == true )
265            bGrabItems = true;
266         else if( dStricmp( field->pGroupname, mCaption ) == 0 )
267            bGrabItems = true;
268         continue;
269      }
270      else if ( field->type == AbstractClassRep::EndGroupFieldType )
271      {
272         // If we're dealing with general fields, always set grabItems to false (to grab them)
273         if( bNoGroup == true )
274            bGrabItems = false;
275         else if( dStricmp( field->pGroupname, mCaption ) == 0 )
276            bGrabItems = false;
277         continue;
278      }
279      
280      // Skip field if it has the HideInInspectors flag set.
281      
282      if( field->flag.test( AbstractClassRep::FIELD_HideInInspectors ) )
283         continue;
284
285      if( ( bGrabItems == true || ( bNoGroup == true && bGrabItems == false ) ) && itr->type != AbstractClassRep::DeprecatedFieldType )
286      {
287         if( bNoGroup == true && bGrabItems == true )
288            continue;
289
290         if ( field->type == AbstractClassRep::StartArrayFieldType )
291         {
292            #ifdef DEBUG_SPEW
293            Platform::outputDebugString( "[GuiInspectorGroup] Beginning array '%s'",
294               field->pFieldname );
295            #endif
296            
297            // Starting an array...
298            // Create a rollout for the Array, give it the array's name.
299            GuiRolloutCtrl *arrayRollout = new GuiRolloutCtrl();            
300            GuiControlProfile *arrayRolloutProfile = dynamic_cast<GuiControlProfile*>( Sim::findObject( "GuiInspectorRolloutProfile0" ) );
301            
302            arrayRollout->setControlProfile(arrayRolloutProfile);
303            //arrayRollout->mCaption = StringTable->insert( String::ToString( "%s (%i)", field->pGroupname, field->elementCount ) );
304            arrayRollout->setCaption( field->pGroupname );
305            //arrayRollout->setMargin( 14, 0, 0, 0 );
306            arrayRollout->registerObject();
307            
308            GuiStackControl *arrayStack = new GuiStackControl();
309            arrayStack->registerObject();
310            arrayStack->freeze(true);
311            arrayRollout->addObject(arrayStack);
312            
313            // Allocate a rollout for each element-count in the array
314            // Give it the element count name.
315            for ( U32 i = 0; i < field->elementCount; i++ )
316            {
317               GuiRolloutCtrl *elementRollout = new GuiRolloutCtrl();            
318               GuiControlProfile *elementRolloutProfile = dynamic_cast<GuiControlProfile*>( Sim::findObject( "GuiInspectorRolloutProfile0" ) );
319               
320               char buf[256];
321               dSprintf( buf, 256, "  [%i]", i ); 
322               
323               elementRollout->setControlProfile(elementRolloutProfile);
324               elementRollout->setCaption(buf);
325               //elementRollout->setMargin( 14, 0, 0, 0 );
326               elementRollout->registerObject();
327               
328               GuiStackControl *elementStack = new GuiStackControl();
329               elementStack->registerObject();            
330               elementRollout->addObject(elementStack);
331               elementRollout->instantCollapse();
332               
333               arrayStack->addObject( elementRollout );
334            }
335            
336            pArrayRollout = arrayRollout;
337            pArrayStack = arrayStack;
338            arrayStack->freeze(false);
339            pArrayRollout->instantCollapse();
340            mStack->addObject(arrayRollout);
341            
342            bMakingArray = true;
343            continue;
344         }      
345         else if ( field->type == AbstractClassRep::EndArrayFieldType )
346         {
347            #ifdef DEBUG_SPEW
348            Platform::outputDebugString( "[GuiInspectorGroup] Ending array '%s'",
349               field->pFieldname );
350            #endif
351
352            bMakingArray = false;
353            continue;
354         }
355         
356         if ( bMakingArray )
357         {
358            // Add a GuiInspectorField for this field, 
359            // for every element in the array...
360            for ( U32 i = 0; i < pArrayStack->size(); i++ )
361            {
362               FrameTemp<char> intToStr( 64 );
363               dSprintf( intToStr, 64, "%d", i );
364               
365               // The array stack should have a rollout for each element
366               // as children...
367               GuiRolloutCtrl *pRollout = dynamic_cast<GuiRolloutCtrl*>(pArrayStack->at(i));
368               // And the each of those rollouts should have a stack for 
369               // fields...
370               GuiStackControl *pStack = dynamic_cast<GuiStackControl*>(pRollout->at(0));
371               
372               // And we add a new GuiInspectorField to each of those stacks...            
373               GuiInspectorField *fieldGui = constructField( field->type );
374               if ( fieldGui == NULL )                
375                  fieldGui = new GuiInspectorField();
376               
377               fieldGui->init( mParent, this );
378               StringTableEntry caption = field->pFieldname;
379               fieldGui->setInspectorField( field, caption, intToStr );
380               
381               if( fieldGui->registerObject() )
382               {
383                  #ifdef DEBUG_SPEW
384                  Platform::outputDebugString( "[GuiInspectorGroup] Adding array element '%s[%i]'",
385                     field->pFieldname, i );
386                  #endif
387
388                  mChildren.push_back( fieldGui );
389                  pStack->addObject( fieldGui );
390               }
391               else
392                  delete fieldGui;
393            }
394            
395            continue;
396         }
397         
398         // This is weird, but it should work for now. - JDD
399         // We are going to check to see if this item is an array
400         // if so, we're going to construct a field for each array element
401         if( field->elementCount > 1 )
402         {
403            // Make a rollout control for this array
404            //
405            GuiRolloutCtrl *rollout = new GuiRolloutCtrl();  
406            rollout->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorRolloutProfile0" );            
407            rollout->setCaption(String::ToString( "%s (%i)", field->pFieldname, field->elementCount));
408            rollout->setMargin( 14, 0, 0, 0 );
409            rollout->registerObject();
410            mArrayCtrls.push_back(rollout);
411            
412            // Put a stack control within the rollout
413            //
414            GuiStackControl *stack = new GuiStackControl();
415            stack->setDataField( StringTable->insert("profile"), NULL, "GuiInspectorStackProfile" );
416            stack->registerObject();
417            stack->freeze(true);
418            rollout->addObject(stack);
419            
420            mStack->addObject(rollout);
421            
422            // Create each field and add it to the stack.
423            //
424            for (S32 nI = 0; nI < field->elementCount; nI++)
425            {
426               FrameTemp<char> intToStr( 64 );
427               dSprintf( intToStr, 64, "%d", nI );
428               
429               // Construct proper ValueName[nI] format which is "ValueName0" for index 0, etc.
430               
431               String fieldName = String::ToString( "%s%d", field->pFieldname, nI );
432               
433               // If the field already exists, just update it
434               GuiInspectorField *fieldGui = findField( fieldName );
435               if( fieldGui != NULL )
436               {
437                  fieldGui->updateValue();
438                  continue;
439               }
440               
441               bNewItems = true;
442               
443               fieldGui = constructField( field->type );
444               if ( fieldGui == NULL )               
445                  fieldGui = new GuiInspectorField();
446               
447               fieldGui->init( mParent, this );               
448               StringTableEntry caption = StringTable->insert( String::ToString("   [%i]",nI) );
449               fieldGui->setInspectorField( field, caption, intToStr );
450               
451               if ( fieldGui->registerObject() )
452               {
453                  mChildren.push_back( fieldGui );
454                  stack->addObject( fieldGui );
455               }
456               else
457                  delete fieldGui;
458            }
459            
460            stack->freeze(false);
461            stack->updatePanes();
462            rollout->instantCollapse();
463         }
464         else
465         {
466            // If the field already exists, just update it
467            GuiInspectorField *fieldGui = findField( field->pFieldname );
468            if ( fieldGui != NULL )
469            {
470               fieldGui->updateValue();
471               continue;
472            }
473            
474            bNewItems = true;
475            
476            fieldGui = constructField( field->type );
477            if ( fieldGui == NULL )
478               fieldGui = new GuiInspectorField();
479            
480            fieldGui->init( mParent, this );            
481            fieldGui->setInspectorField( field );
482                     
483            if( fieldGui->registerObject() )
484            {
485               #ifdef DEBUG_SPEW
486               Platform::outputDebugString( "[GuiInspectorGroup] Adding field '%s'",
487                  field->pFieldname );
488               #endif
489
490               mChildren.push_back( fieldGui );
491               mStack->addObject( fieldGui );
492            }
493            else
494            {
495               SAFE_DELETE( fieldGui );
496            }
497         }
498      }
499   }
500   mStack->freeze(false);
501   mStack->updatePanes();
502
503   // If we've no new items, there's no need to resize anything!
504   if( bNewItems == false && !mChildren.empty() )
505      return true;
506
507   sizeToContents();
508
509   setUpdate();
510
511   return true;
512}
513
514//-----------------------------------------------------------------------------
515
516bool GuiInspectorGroup::updateFieldValue( StringTableEntry fieldName, StringTableEntry arrayIdx )
517{
518   // Check if we contain a field of this name,
519   // if so update its value and return true.
520   Vector<GuiInspectorField*>::iterator iter = mChildren.begin();
521   
522   if( arrayIdx == StringTable->EmptyString() )
523      arrayIdx = NULL;
524
525   for( ; iter != mChildren.end(); iter++ )
526   {   
527      GuiInspectorField *field = (*iter);
528      if ( field->mField &&
529           field->mField->pFieldname == fieldName &&
530           field->mFieldArrayIndex == arrayIdx )
531      {
532         field->updateValue();
533         return true;
534      }
535   }
536
537   return false;
538}
539
540//-----------------------------------------------------------------------------
541
542void GuiInspectorGroup::updateAllFields()
543{   
544   Vector<GuiInspectorField*>::iterator iter = mChildren.begin();
545   for( ; iter != mChildren.end(); iter++ )
546      (*iter)->updateValue();
547}
548
549//-----------------------------------------------------------------------------
550
551AbstractClassRep* GuiInspectorGroup::findCommonAncestorClass()
552{
553   AbstractClassRep* classRep = getInspector()->getInspectObject( 0 )->getClassRep();
554   const U32 numInspectObjects = getInspector()->getNumInspectObjects();
555   
556   for( U32 i = 1; i < numInspectObjects; ++ i )
557   {
558      SimObject* object = getInspector()->getInspectObject( i );
559      while( !object->getClassRep()->isClass( classRep ) )
560      {
561         classRep = classRep->getParentClass();
562         AssertFatal( classRep, "GuiInspectorGroup::findcommonAncestorClass - Walked above ConsoleObject!" );
563      }
564   }
565      
566   return classRep;
567}
568