wxUSE_PROPGRID is now recognized by source and header files
[wxWidgets.git] / src / propgrid / propgridpagestate.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/propgrid/propgridpagestate.cpp
3 // Purpose: wxPropertyGridPageState class
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: 2008-08-24
7 // RCS-ID: $Id:
8 // Copyright: (c) Jaakko Salli
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_PROPGRID
20
21 #ifndef WX_PRECOMP
22 #include "wx/defs.h"
23 #include "wx/object.h"
24 #include "wx/hash.h"
25 #include "wx/string.h"
26 #include "wx/log.h"
27 #include "wx/event.h"
28 #include "wx/window.h"
29 #include "wx/panel.h"
30 #include "wx/dc.h"
31 #include "wx/dcmemory.h"
32 #include "wx/button.h"
33 #include "wx/pen.h"
34 #include "wx/brush.h"
35 #include "wx/cursor.h"
36 #include "wx/dialog.h"
37 #include "wx/settings.h"
38 #include "wx/msgdlg.h"
39 #include "wx/choice.h"
40 #include "wx/stattext.h"
41 #include "wx/scrolwin.h"
42 #include "wx/dirdlg.h"
43 #include "wx/layout.h"
44 #include "wx/sizer.h"
45 #include "wx/textdlg.h"
46 #include "wx/filedlg.h"
47 #include "wx/statusbr.h"
48 #include "wx/intl.h"
49 #include "wx/frame.h"
50 #include "wx/stopwatch.h"
51 #endif
52
53 // This define is necessary to prevent macro clearing
54 #define __wxPG_SOURCE_FILE__
55
56 #include <wx/propgrid/propgridpagestate.h>
57 #include <wx/propgrid/propgrid.h>
58 #include <wx/propgrid/editors.h>
59
60 #include <typeinfo>
61
62 #define wxPG_DEFAULT_SPLITTERX 110
63
64
65 // -----------------------------------------------------------------------
66 // wxPropertyGridIterator
67 // -----------------------------------------------------------------------
68
69 void wxPropertyGridIteratorBase::Init( wxPropertyGridPageState* state, int flags, wxPGProperty* property, int dir )
70 {
71 wxASSERT( dir == 1 || dir == -1 );
72
73 m_state = state;
74 m_baseParent = state->DoGetRoot();
75 if ( !property && m_baseParent->GetChildCount() )
76 property = m_baseParent->Item(0);
77
78 m_property = property;
79
80 wxPG_ITERATOR_CREATE_MASKS(flags, m_itemExMask, m_parentExMask)
81
82 // Need to skip first?
83 if ( property && (property->GetFlags() & m_itemExMask) )
84 {
85 if ( dir == 1 )
86 Next();
87 else
88 Prev();
89 }
90 }
91
92 void wxPropertyGridIteratorBase::Init( wxPropertyGridPageState* state, int flags, int startPos, int dir )
93 {
94 wxPGProperty* property;
95
96 if ( startPos == wxTOP )
97 {
98 property = NULL;
99 if ( dir == 0 )
100 dir = 1;
101 }
102 else if ( startPos == wxBOTTOM )
103 {
104 property = state->GetLastItem(flags);
105 if ( dir == 0 )
106 dir = -1;
107 }
108 else
109 {
110 wxASSERT_MSG( false, wxT("Only supported stating positions are wxTOP and wxBOTTOM") );
111 property = NULL;
112 }
113
114 Init( state, flags, property, dir );
115 }
116
117 void wxPropertyGridIteratorBase::Assign( const wxPropertyGridIteratorBase& it )
118 {
119 m_property = it.m_property;
120 m_state = it.m_state;
121 m_baseParent = it.m_baseParent;
122 m_itemExMask = it.m_itemExMask;
123 m_parentExMask = it.m_parentExMask;
124 }
125
126 void wxPropertyGridIteratorBase::Prev()
127 {
128 wxPGProperty* property = m_property;
129 wxASSERT( property );
130
131 wxPGProperty* parent = property->GetParent();
132 wxASSERT( parent );
133 unsigned int index = property->GetIndexInParent();
134
135 if ( index > 0 )
136 {
137 // Previous sibling
138 index--;
139
140 property = parent->Item(index);
141
142 // Go to last children?
143 if ( property->GetChildCount() &&
144 wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) )
145 {
146 // First child
147 property = property->Last();
148 }
149 }
150 else
151 {
152 // Up to a parent
153 if ( parent == m_baseParent )
154 {
155 m_property = NULL;
156 return;
157 }
158 else
159 {
160 property = parent;
161 }
162 }
163
164 m_property = property;
165
166 // If property does not match our criteria, skip it
167 if ( property->GetFlags() & m_itemExMask )
168 Prev();
169 }
170
171 void wxPropertyGridIteratorBase::Next( bool iterateChildren )
172 {
173 wxPGProperty* property = m_property;
174 wxASSERT( property );
175
176 if ( property->GetChildCount() &&
177 wxPG_ITERATOR_PARENTEXMASK_TEST(property, m_parentExMask) &&
178 iterateChildren )
179 {
180 // First child
181 property = property->Item(0);
182 }
183 else
184 {
185 wxPGProperty* parent = property->GetParent();
186 wxASSERT( parent );
187 unsigned int index = property->GetIndexInParent() + 1;
188
189 if ( index < parent->GetChildCount() )
190 {
191 // Next sibling
192 property = parent->Item(index);
193 }
194 else
195 {
196 // Next sibling of parent
197 if ( parent == m_baseParent )
198 {
199 m_property = NULL;
200 }
201 else
202 {
203 m_property = parent;
204 Next(false);
205 }
206 return;
207 }
208 }
209
210 m_property = property;
211
212 // If property does not match our criteria, skip it
213 if ( property->GetFlags() & m_itemExMask )
214 Next();
215 }
216
217 // -----------------------------------------------------------------------
218 // wxPropertyGridPageState
219 // -----------------------------------------------------------------------
220
221 wxPropertyGridPageState::wxPropertyGridPageState()
222 {
223 m_pPropGrid = (wxPropertyGrid*) NULL;
224 m_regularArray.SetParentState(this);
225 m_properties = &m_regularArray;
226 m_abcArray = (wxPGRootProperty*) NULL;
227 m_currentCategory = (wxPropertyCategory*) NULL;
228 m_selected = (wxPGProperty*) NULL;
229 m_width = 0;
230 m_virtualHeight = 0;
231 m_lastCaptionBottomnest = 1;
232 m_itemsAdded = 0;
233 m_anyModified = 0;
234 m_vhCalcPending = 0;
235 m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
236 m_colWidths.push_back( wxPG_DEFAULT_SPLITTERX );
237 m_fSplitterX = wxPG_DEFAULT_SPLITTERX;
238 }
239
240 // -----------------------------------------------------------------------
241
242 wxPropertyGridPageState::~wxPropertyGridPageState()
243 {
244 delete m_abcArray;
245 }
246
247 // -----------------------------------------------------------------------
248
249 void wxPropertyGridPageState::InitNonCatMode()
250 {
251 if ( !m_abcArray )
252 {
253 m_abcArray = new wxPGRootProperty();
254 m_abcArray->SetParentState(this);
255 m_abcArray->SetFlag(wxPG_PROP_CHILDREN_ARE_COPIES);
256 }
257
258 // Must be called when state::m_properties still points to regularArray.
259 wxPGProperty* oldProperties = m_properties;
260
261 // Must use temp value in state::m_properties for item iteration loop
262 // to run as expected.
263 m_properties = &m_regularArray;
264
265 if ( m_properties->GetChildCount() )
266 {
267 // Copy items.
268 wxPropertyGridIterator it( this, wxPG_ITERATE_DEFAULT|wxPG_ITERATE_CATEGORIES );
269
270 for ( ; !it.AtEnd(); it.Next() )
271 {
272 wxPGProperty* p = it.GetProperty();
273 wxPGProperty* parent = p->GetParent();
274 if ( p->HasFlag(wxPG_PROP_MISC_PARENT) &&
275 ( parent == m_properties || (parent->IsCategory() || parent->IsRoot()) ) )
276 {
277 m_abcArray->AddChild2( p );
278 p->m_parent = &m_regularArray;
279 }
280 }
281 }
282
283 m_properties = oldProperties;
284 }
285
286 // -----------------------------------------------------------------------
287
288 void wxPropertyGridPageState::DoClear()
289 {
290 m_regularArray.Empty();
291 if ( m_abcArray )
292 m_abcArray->Empty();
293
294 m_dictName.clear();
295
296 m_currentCategory = (wxPropertyCategory*) NULL;
297 m_lastCaptionBottomnest = 1;
298 m_itemsAdded = 0;
299
300 m_virtualHeight = 0;
301 m_vhCalcPending = 0;
302
303 m_selected = (wxPGProperty*) NULL;
304 }
305
306 // -----------------------------------------------------------------------
307
308 void wxPropertyGridPageState::CalculateFontAndBitmapStuff( int WXUNUSED(vspacing) )
309 {
310 wxPropertyGrid* propGrid = GetGrid();
311
312 VirtualHeightChanged();
313
314 // Recalculate caption text extents.
315 unsigned int i;
316
317 for ( i=0;i<m_regularArray.GetChildCount();i++ )
318 {
319 wxPGProperty* p =m_regularArray.Item(i);
320
321 if ( p->IsCategory() )
322 ((wxPropertyCategory*)p)->CalculateTextExtent(propGrid, propGrid->GetCaptionFont());
323 }
324 }
325
326 // -----------------------------------------------------------------------
327
328 void wxPropertyGridPageState::SetVirtualWidth( int width )
329 {
330 wxASSERT( width >= 0 );
331 wxPropertyGrid* pg = GetGrid();
332 int gw = pg->GetClientSize().x;
333 if ( width < gw )
334 width = gw;
335
336 m_width = width;
337 }
338
339 // -----------------------------------------------------------------------
340
341 void wxPropertyGridPageState::OnClientWidthChange( int newWidth, int widthChange, bool fromOnResize )
342 {
343 wxPropertyGrid* pg = GetGrid();
344
345 if ( pg->HasVirtualWidth() )
346 {
347 if ( m_width < newWidth )
348 SetVirtualWidth( newWidth );
349
350 CheckColumnWidths(widthChange);
351 }
352 else
353 {
354 SetVirtualWidth( newWidth );
355
356 // This should be done before splitter auto centering
357 // NOTE: Splitter auto-centering is done in this function.
358 if ( !fromOnResize )
359 widthChange = 0;
360 CheckColumnWidths(widthChange);
361
362 if ( !(GetGrid()->GetInternalFlags() & wxPG_FL_SPLITTER_PRE_SET) &&
363 (GetGrid()->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER) )
364 {
365 long timeSinceCreation = (::wxGetLocalTimeMillis() - GetGrid()->m_timeCreated).ToLong();
366
367 // If too long, don't set splitter
368 if ( timeSinceCreation < 3000 )
369 {
370 if ( m_properties->GetChildCount() || timeSinceCreation > 750 )
371 {
372 SetSplitterLeft( false );
373 }
374 else
375 {
376 DoSetSplitterPosition( newWidth / 2 );
377 GetGrid()->ClearInternalFlag(wxPG_FL_SPLITTER_PRE_SET);
378 }
379 }
380 }
381 }
382 }
383
384 // -----------------------------------------------------------------------
385 // wxPropertyGridPageState item iteration methods
386 // -----------------------------------------------------------------------
387
388 wxPGProperty* wxPropertyGridPageState::GetLastItem( int flags )
389 {
390 if ( !m_properties->GetChildCount() )
391 return (wxPGProperty*) NULL;
392
393 wxPG_ITERATOR_CREATE_MASKS(flags, int itemExMask, int parentExMask)
394
395 // First, get last child of last parent
396 wxPGProperty* pwc = (wxPGProperty*)m_properties->Last();
397 while ( pwc->GetChildCount() &&
398 wxPG_ITERATOR_PARENTEXMASK_TEST(pwc, parentExMask) )
399 pwc = (wxPGProperty*) pwc->Last();
400
401 // Then, if it doesn't fit our criteria, back up until we find something that does
402 if ( pwc->GetFlags() & itemExMask )
403 {
404 wxPropertyGridIterator it( this, flags, pwc );
405 for ( ; !it.AtEnd(); it.Prev() )
406 ;
407 pwc = (wxPGProperty*) it.GetProperty();
408 }
409
410 return pwc;
411 }
412
413 wxPropertyCategory* wxPropertyGridPageState::GetPropertyCategory( const wxPGProperty* p ) const
414 {
415 const wxPGProperty* parent = (const wxPGProperty*)p;
416 const wxPGProperty* grandparent = (const wxPGProperty*)parent->GetParent();
417 do
418 {
419 parent = grandparent;
420 grandparent = (wxPGProperty*)parent->GetParent();
421 if ( parent->IsCategory() && grandparent )
422 return (wxPropertyCategory*)parent;
423 } while ( grandparent );
424
425 return (wxPropertyCategory*) NULL;
426 }
427
428 // -----------------------------------------------------------------------
429 // wxPropertyGridPageState GetPropertyXXX methods
430 // -----------------------------------------------------------------------
431
432 wxPGProperty* wxPropertyGridPageState::GetPropertyByLabel( const wxString& label,
433 wxPGProperty* parent ) const
434 {
435
436 size_t i;
437
438 if ( !parent ) parent = (wxPGProperty*) &m_regularArray;
439
440 for ( i=0; i<parent->GetChildCount(); i++ )
441 {
442 wxPGProperty* p = parent->Item(i);
443 if ( p->m_label == label )
444 return p;
445 // Check children recursively.
446 if ( p->GetChildCount() )
447 {
448 p = GetPropertyByLabel(label,(wxPGProperty*)p);
449 if ( p )
450 return p;
451 }
452 }
453
454 return NULL;
455 }
456
457 // -----------------------------------------------------------------------
458
459 wxPGProperty* wxPropertyGridPageState::BaseGetPropertyByName( const wxString& name ) const
460 {
461 wxPGHashMapS2P::const_iterator it;
462 it = m_dictName.find(name);
463 if ( it != m_dictName.end() )
464 return (wxPGProperty*) it->second;
465 return (wxPGProperty*) NULL;
466 }
467
468 // -----------------------------------------------------------------------
469 // wxPropertyGridPageState global operations
470 // -----------------------------------------------------------------------
471
472 // -----------------------------------------------------------------------
473 // Item iteration macros
474 // NB: Nowadays only needed for alphabetic/categoric mode switching.
475 // -----------------------------------------------------------------------
476
477 #define II_INVALID_I 0x00FFFFFF
478
479 #define ITEM_ITERATION_VARIABLES \
480 wxPGProperty* parent; \
481 unsigned int i; \
482 unsigned int iMax;
483
484 #define ITEM_ITERATION_INIT_FROM_THE_TOP \
485 parent = m_properties; \
486 i = 0;
487
488 #define ITEM_ITERATION_INIT(startparent, startindex, state) \
489 parent = startparent; \
490 i = (unsigned int)startindex; \
491 if ( parent == (wxPGProperty*) NULL ) \
492 { \
493 parent = state->m_properties; \
494 i = 0; \
495 }
496
497 #define ITEM_ITERATION_LOOP_BEGIN \
498 do \
499 { \
500 iMax = parent->GetChildCount(); \
501 while ( i < iMax ) \
502 { \
503 wxPGProperty* p = parent->Item(i);
504
505 #define ITEM_ITERATION_LOOP_END \
506 if ( p->GetChildCount() ) \
507 { \
508 i = 0; \
509 parent = (wxPGProperty*)p; \
510 iMax = parent->GetChildCount(); \
511 } \
512 else \
513 i++; \
514 } \
515 i = parent->m_arrIndex + 1; \
516 parent = parent->m_parent; \
517 } \
518 while ( parent != NULL );
519
520 bool wxPropertyGridPageState::EnableCategories( bool enable )
521 {
522 //
523 // NB: We can't use wxPropertyGridIterator in this
524 // function, since it depends on m_arrIndexes,
525 // which, among other things, is being fixed here.
526 //
527 ITEM_ITERATION_VARIABLES
528
529 if ( enable )
530 {
531 //
532 // Enable categories
533 //
534
535 if ( !IsInNonCatMode() )
536 return false;
537
538 m_properties = &m_regularArray;
539
540 // fix parents, indexes, and depths
541 ITEM_ITERATION_INIT_FROM_THE_TOP
542
543 ITEM_ITERATION_LOOP_BEGIN
544
545 p->m_arrIndex = i;
546
547 p->m_parent = parent;
548
549 // If parent was category, and this is not,
550 // then the depth stays the same.
551 if ( parent->IsCategory() &&
552 !p->IsCategory() )
553 p->m_depth = parent->m_depth;
554 else
555 p->m_depth = parent->m_depth + 1;
556
557 ITEM_ITERATION_LOOP_END
558
559 }
560 else
561 {
562 //
563 // Disable categories
564 //
565
566 if ( IsInNonCatMode() )
567 return false;
568
569 // Create array, if necessary.
570 if ( !m_abcArray )
571 InitNonCatMode();
572
573 m_properties = m_abcArray;
574
575 // fix parents, indexes, and depths
576 ITEM_ITERATION_INIT_FROM_THE_TOP
577
578 ITEM_ITERATION_LOOP_BEGIN
579
580 p->m_arrIndex = i;
581
582 p->m_parent = parent;
583
584 p->m_depth = parent->m_depth + 1;
585
586 ITEM_ITERATION_LOOP_END
587 }
588
589 VirtualHeightChanged();
590
591 if ( m_pPropGrid->GetState() == this )
592 m_pPropGrid->RecalculateVirtualSize();
593
594 return true;
595 }
596
597 // -----------------------------------------------------------------------
598
599 static int wxPG_SortFunc(void **p1, void **p2)
600 {
601 wxPGProperty *pp1 = *((wxPGProperty**)p1);
602 wxPGProperty *pp2 = *((wxPGProperty**)p2);
603 return pp1->GetLabel().compare( pp2->GetLabel() );
604 }
605
606 void wxPropertyGridPageState::SortChildren( wxPGProperty* p )
607 {
608 if ( !p )
609 p = (wxPGProperty*)m_properties;
610
611 if ( !p->GetChildCount() )
612 return;
613
614 wxPGProperty* pwc = (wxPGProperty*)p;
615
616 // Can only sort items with children
617 if ( pwc->GetChildCount() < 1 )
618 return;
619
620 pwc->m_children.Sort( wxPG_SortFunc );
621
622 // Fix indexes
623 pwc->FixIndexesOfChildren();
624
625 }
626
627 // -----------------------------------------------------------------------
628
629 void wxPropertyGridPageState::Sort()
630 {
631 SortChildren( m_properties );
632
633 // Sort categories as well
634 if ( !IsInNonCatMode() )
635 {
636 size_t i;
637 for ( i=0;i<m_properties->GetChildCount();i++)
638 {
639 wxPGProperty* p = m_properties->Item(i);
640 if ( p->IsCategory() )
641 SortChildren( p );
642 }
643 }
644 }
645
646 // -----------------------------------------------------------------------
647 // wxPropertyGridPageState splitter, column and hittest functions
648 // -----------------------------------------------------------------------
649
650 wxPGProperty* wxPropertyGridPageState::DoGetItemAtY( int y ) const
651 {
652 // Outside?
653 if ( y < 0 )
654 return (wxPGProperty*) NULL;
655
656 unsigned int a = 0;
657 return m_properties->GetItemAtY(y, GetGrid()->m_lineHeight, &a);
658 }
659
660 // -----------------------------------------------------------------------
661
662 wxPropertyGridHitTestResult wxPropertyGridPageState::HitTest( const wxPoint&pt ) const
663 {
664 wxPropertyGridHitTestResult result;
665 result.column = HitTestH( pt.x, &result.splitter, &result.splitterHitOffset );
666 result.property = DoGetItemAtY( pt.y );
667 return result;
668 }
669
670 // -----------------------------------------------------------------------
671
672 // Used by SetSplitterLeft() and DotFitColumns()
673 int wxPropertyGridPageState::GetColumnFitWidth(wxClientDC& dc,
674 wxPGProperty* pwc,
675 unsigned int col,
676 bool subProps) const
677 {
678 wxPropertyGrid* pg = m_pPropGrid;
679 size_t i;
680 int maxW = 0;
681 int w, h;
682
683 for ( i=0; i<pwc->GetChildCount(); i++ )
684 {
685 wxPGProperty* p = pwc->Item(i);
686 if ( !p->IsCategory() )
687 {
688 dc.GetTextExtent( p->GetColumnText(col), &w, &h );
689 if ( col == 0 )
690 w += ( ((int)p->m_depth-1) * pg->m_subgroup_extramargin );
691
692 //
693 // TODO: Add bitmap support.
694
695 w += (wxPG_XBEFORETEXT*2);
696
697 if ( w > maxW )
698 maxW = w;
699 }
700
701 if ( p->GetChildCount() &&
702 ( subProps || p->IsCategory() ) )
703 {
704 w = GetColumnFitWidth( dc, p, col, subProps );
705
706 if ( w > maxW )
707 maxW = w;
708 }
709 }
710
711 return maxW;
712 }
713
714 int wxPropertyGridPageState::DoGetSplitterPosition( int splitterColumn ) const
715 {
716 int n = GetGrid()->m_marginWidth;
717 int i;
718 for ( i=0; i<=splitterColumn; i++ )
719 n += m_colWidths[i];
720 return n;
721 }
722
723 int wxPropertyGridPageState::GetColumnMinWidth( int WXUNUSED(column) ) const
724 {
725 return wxPG_DRAG_MARGIN;
726 }
727
728 void wxPropertyGridPageState::PropagateColSizeDec( int column, int decrease, int dir )
729 {
730 int origWidth = m_colWidths[column];
731 m_colWidths[column] -= decrease;
732 int min = GetColumnMinWidth(column);
733 int more = 0;
734 if ( m_colWidths[column] < min )
735 {
736 more = decrease - (origWidth - min);
737 m_colWidths[column] = min;
738 }
739
740 //
741 // FIXME: Causes erratic splitter changing, so as a workaround
742 // disabled if two or less columns.
743
744 if ( m_colWidths.size() <= 2 )
745 return;
746
747 column += dir;
748 if ( more && column < (int)m_colWidths.size() && column >= 0 )
749 PropagateColSizeDec( column, more, dir );
750 }
751
752 void wxPropertyGridPageState::DoSetSplitterPosition( int newXPos, int splitterColumn, bool WXUNUSED(allPages), bool fromAutoCenter )
753 {
754 wxPropertyGrid* pg = GetGrid();
755
756 int adjust = newXPos - DoGetSplitterPosition(splitterColumn);
757
758 if ( !pg->HasVirtualWidth() )
759 {
760 // No virtual width
761 int otherColumn;
762 if ( adjust > 0 )
763 {
764 otherColumn = splitterColumn + 1;
765 if ( otherColumn == (int)m_colWidths.size() )
766 otherColumn = 0;
767 m_colWidths[splitterColumn] += adjust;
768 PropagateColSizeDec( otherColumn, adjust, 1 );
769 }
770 else
771 {
772 otherColumn = splitterColumn + 1;
773 if ( otherColumn == (int)m_colWidths.size() )
774 otherColumn = 0;
775 m_colWidths[otherColumn] -= adjust;
776 PropagateColSizeDec( splitterColumn, -adjust, -1 );
777 }
778 }
779 else
780 {
781 m_colWidths[splitterColumn] += adjust;
782 }
783
784 if ( splitterColumn == 0 )
785 m_fSplitterX = (double) newXPos;
786
787 if ( !fromAutoCenter )
788 {
789 // Don't allow initial splitter auto-positioning after this.
790 if ( pg->GetState() == this )
791 pg->SetInternalFlag(wxPG_FL_SPLITTER_PRE_SET);
792
793 CheckColumnWidths();
794 }
795 }
796
797 // Moves splitter so that all labels are visible, but just.
798 void wxPropertyGridPageState::SetSplitterLeft( bool subProps )
799 {
800 wxPropertyGrid* pg = GetGrid();
801 wxClientDC dc(pg);
802 dc.SetFont(pg->m_font);
803
804 int maxW = GetColumnFitWidth(dc, m_properties, 0, subProps);
805
806 if ( maxW > 0 )
807 {
808 maxW += pg->m_marginWidth;
809 DoSetSplitterPosition( maxW );
810 }
811
812 pg->SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
813 }
814
815 wxSize wxPropertyGridPageState::DoFitColumns( bool WXUNUSED(allowGridResize) )
816 {
817 wxPropertyGrid* pg = GetGrid();
818 wxClientDC dc(pg);
819 dc.SetFont(pg->m_font);
820
821 int marginWidth = pg->m_marginWidth;
822 int accWid = marginWidth;
823 int maxColWidth = 500;
824
825 for ( unsigned int col=0; col < GetColumnCount(); col++ )
826 {
827 int fitWid = GetColumnFitWidth(dc, m_properties, col, true);
828 int colMinWidth = GetColumnMinWidth(col);
829 if ( fitWid < colMinWidth )
830 fitWid = colMinWidth;
831 else if ( fitWid > maxColWidth )
832 fitWid = maxColWidth;
833
834 m_colWidths[col] = fitWid;
835
836 accWid += fitWid;
837 }
838
839 // Expand last one to fill the width
840 int remaining = m_width - accWid;
841 m_colWidths[GetColumnCount()-1] += remaining;
842
843 pg->SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
844
845 int firstSplitterX = marginWidth + m_colWidths[0];
846 m_fSplitterX = (double) firstSplitterX;
847
848 // Don't allow initial splitter auto-positioning after this.
849 if ( pg->GetState() == this )
850 {
851 pg->SetSplitterPosition(firstSplitterX, false);
852 pg->Refresh();
853 }
854
855 int x, y;
856 pg->GetVirtualSize(&x, &y);
857
858 return wxSize(accWid, y);
859 }
860
861 void wxPropertyGridPageState::CheckColumnWidths( int widthChange )
862 {
863 if ( m_width == 0 )
864 return;
865
866 wxPropertyGrid* pg = GetGrid();
867
868 #ifdef __WXDEBUG__
869 const bool debug = false;
870 #endif
871
872 unsigned int i;
873 unsigned int lastColumn = m_colWidths.size() - 1;
874 int width = m_width;
875 int clientWidth = pg->GetClientSize().x;
876
877 //
878 // Column to reduce, if needed. Take last one that exceeds minimum width.
879 // Except if auto splitter centering is used, in which case use widest.
880 int reduceCol = -1;
881 int highestColWidth = 0;
882
883 bool minimizedCols = false;
884
885 #ifdef __WXDEBUG__
886 if ( debug )
887 wxLogDebug(wxT("ColumnWidthCheck (virtualWidth: %i, clientWidth: %i)"), width, clientWidth);
888 #endif
889
890 //
891 // Check min sizes
892 for ( i=0; i<m_colWidths.size(); i++ )
893 {
894 int min = GetColumnMinWidth(i);
895 if ( m_colWidths[i] <= min )
896 {
897 m_colWidths[i] = min;
898 minimizedCols = true;
899 }
900 else
901 {
902 if ( pg->HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
903 {
904 if ( m_colWidths[i] >= highestColWidth )
905 {
906 highestColWidth = m_colWidths[i];
907 reduceCol = i;
908 }
909 }
910 else
911 {
912 reduceCol = i;
913 }
914 }
915 }
916
917 int colsWidth = pg->m_marginWidth;
918 for ( i=0; i<m_colWidths.size(); i++ )
919 colsWidth += m_colWidths[i];
920
921 #ifdef __WXDEBUG__
922 if ( debug )
923 wxLogDebug(wxT(" HasVirtualWidth: %i colsWidth: %i"),(int)pg->HasVirtualWidth(),colsWidth);
924 #endif
925
926 // Then mode-based requirement
927 if ( !pg->HasVirtualWidth() )
928 {
929 int widthHigher = width - colsWidth;
930
931 // Adapt colsWidth to width
932 if ( colsWidth < width )
933 {
934 // Increase column
935 #ifdef __WXDEBUG__
936 if ( debug )
937 wxLogDebug(wxT(" Adjust last column to %i"), m_colWidths[lastColumn] + widthHigher);
938 #endif
939 m_colWidths[lastColumn] = m_colWidths[lastColumn] + widthHigher;
940 }
941 else if ( colsWidth > width )
942 {
943 // Reduce column
944 if ( reduceCol != -1 )
945 {
946 #ifdef __WXDEBUG__
947 if ( debug )
948 wxLogDebug(wxT(" Reduce column %i (by %i)"), reduceCol, -widthHigher);
949 #endif
950 // Reduce widest column, and recheck
951 m_colWidths[reduceCol] = m_colWidths[reduceCol] + widthHigher;
952 CheckColumnWidths();
953 }
954 }
955 }
956 else
957 {
958 // Only check colsWidth against clientWidth
959 if ( colsWidth < clientWidth )
960 {
961 m_colWidths[lastColumn] = m_colWidths[lastColumn] + (clientWidth-colsWidth);
962 }
963
964 m_width = colsWidth;
965
966 // If width changed, recalculate virtual size
967 if ( pg->GetState() == this )
968 pg->RecalculateVirtualSize();
969 }
970
971 #ifdef __WXDEBUG__
972 if ( debug )
973 for ( i=0; i<m_colWidths.size(); i++ )
974 wxLogDebug(wxT("col%i: %i"),i,m_colWidths[i]);
975 #endif
976
977 // Auto center splitter
978 if ( !(pg->GetInternalFlags() & wxPG_FL_DONT_CENTER_SPLITTER) &&
979 m_colWidths.size() == 2 )
980 {
981 float centerX = (float)(pg->m_width/2);
982 float splitterX;
983
984 if ( m_fSplitterX < 0.0 )
985 {
986 splitterX = centerX;
987 }
988 else if ( widthChange )
989 {
990 //float centerX = float(pg->GetSize().x) * 0.5;
991
992 // Recenter?
993 splitterX = m_fSplitterX + (float(widthChange) * 0.5);
994 float deviation = fabs(centerX - splitterX);
995
996 // If deviating from center, adjust towards it
997 if ( deviation > 20.0 )
998 {
999 if ( splitterX > centerX)
1000 splitterX -= 2;
1001 else
1002 splitterX += 2;
1003 }
1004 }
1005 else
1006 {
1007 // No width change, just keep sure we keep splitter position intact
1008 splitterX = m_fSplitterX;
1009 float deviation = fabs(centerX - splitterX);
1010 if ( deviation > 50.0 )
1011 {
1012 splitterX = centerX;
1013 }
1014 }
1015
1016 DoSetSplitterPosition((int)splitterX, 0, false, true);
1017
1018 m_fSplitterX = splitterX; // needed to retain accuracy
1019 }
1020 }
1021
1022 void wxPropertyGridPageState::SetColumnCount( int colCount )
1023 {
1024 wxASSERT( colCount >= 2 );
1025 m_colWidths.SetCount( colCount, wxPG_DRAG_MARGIN );
1026 if ( m_colWidths.size() > (unsigned int)colCount )
1027 m_colWidths.RemoveAt( m_colWidths.size(), m_colWidths.size() - colCount );
1028
1029 if ( m_pPropGrid->GetState() == this )
1030 m_pPropGrid->RecalculateVirtualSize();
1031 else
1032 CheckColumnWidths();
1033 }
1034
1035 // Returns column index, -1 for margin
1036 int wxPropertyGridPageState::HitTestH( int x, int* pSplitterHit, int* pSplitterHitOffset ) const
1037 {
1038 int cx = GetGrid()->m_marginWidth;
1039 int col = -1;
1040 int prevSplitter = -1;
1041
1042 while ( x > cx )
1043 {
1044 col++;
1045 if ( col >= (int)m_colWidths.size() )
1046 {
1047 *pSplitterHit = -1;
1048 return col;
1049 }
1050 prevSplitter = cx;
1051 cx += m_colWidths[col];
1052 }
1053
1054 // Near prev. splitter
1055 if ( col >= 1 )
1056 {
1057 int diff = x - prevSplitter;
1058 if ( abs(diff) < wxPG_SPLITTERX_DETECTMARGIN1 )
1059 {
1060 *pSplitterHit = col - 1;
1061 *pSplitterHitOffset = diff;
1062 return col;
1063 }
1064 }
1065
1066 // Near next splitter
1067 int nextSplitter = cx;
1068 if ( col < (int)(m_colWidths.size()-1) )
1069 {
1070 int diff = x - nextSplitter;
1071 if ( abs(diff) < wxPG_SPLITTERX_DETECTMARGIN1 )
1072 {
1073 *pSplitterHit = col;
1074 *pSplitterHitOffset = diff;
1075 return col;
1076 }
1077 }
1078
1079 *pSplitterHit = -1;
1080 return col;
1081 }
1082
1083 // -----------------------------------------------------------------------
1084 // wxPropertyGridPageState property value setting and getting
1085 // -----------------------------------------------------------------------
1086
1087 bool wxPropertyGridPageState::DoSetPropertyValueString( wxPGProperty* p, const wxString& value )
1088 {
1089 if ( p )
1090 {
1091 int flags = wxPG_REPORT_ERROR|wxPG_FULL_VALUE;
1092
1093 wxVariant variant = p->GetValueRef();
1094 bool res;
1095
1096 if ( p->GetMaxLength() <= 0 )
1097 res = p->StringToValue( variant, value, flags );
1098 else
1099 res = p->StringToValue( variant, value.Mid(0,p->GetMaxLength()), flags );
1100
1101 if ( res )
1102 {
1103 p->SetValue(variant);
1104 if ( m_selected==p && this==m_pPropGrid->GetState() )
1105 p->UpdateControl(m_pPropGrid->GetEditorControl());
1106 }
1107
1108 return true;
1109 }
1110 return false;
1111 }
1112
1113 // -----------------------------------------------------------------------
1114
1115 bool wxPropertyGridPageState::DoSetPropertyValue( wxPGProperty* p, wxVariant& value )
1116 {
1117 if ( p )
1118 {
1119 p->SetValue(value);
1120 if ( m_selected==p && this==m_pPropGrid->GetState() )
1121 p->UpdateControl(m_pPropGrid->GetEditorControl());
1122
1123 return true;
1124 }
1125 return false;
1126 }
1127
1128 // -----------------------------------------------------------------------
1129
1130 bool wxPropertyGridPageState::DoSetPropertyValueWxObjectPtr( wxPGProperty* p, wxObject* value )
1131 {
1132 if ( p )
1133 {
1134 // wnd_primary has to be given so the control can be updated as well.
1135 wxVariant v(value);
1136 DoSetPropertyValue(p, v);
1137 return true;
1138 }
1139 return false;
1140 }
1141
1142 // -----------------------------------------------------------------------
1143
1144 void wxPropertyGridPageState::DoSetPropertyValueUnspecified( wxPGProperty* p )
1145 {
1146 wxCHECK_RET( p, wxT("invalid property id") );
1147
1148 if ( !p->IsValueUnspecified() )
1149 {
1150 // Value should be set first - editor class methods may need it
1151 p->m_value.MakeNull();
1152
1153 wxASSERT( m_pPropGrid );
1154
1155 if ( m_pPropGrid->GetState() == this )
1156 {
1157 if ( m_pPropGrid->m_selected == p && m_pPropGrid->m_wndEditor )
1158 {
1159 p->GetEditorClass()->SetValueToUnspecified(p, m_pPropGrid->GetEditorControl());
1160 }
1161 }
1162
1163 unsigned int i;
1164 for ( i = 0; i < p->GetChildCount(); i++ )
1165 DoSetPropertyValueUnspecified( p->Item(i) );
1166 }
1167 }
1168
1169 // -----------------------------------------------------------------------
1170 // wxPropertyGridPageState property operations
1171 // -----------------------------------------------------------------------
1172
1173 bool wxPropertyGridPageState::DoCollapse( wxPGProperty* p )
1174 {
1175 wxCHECK_MSG( p, false, wxT("invalid property id") );
1176
1177 if ( !p->GetChildCount() ) return false;
1178
1179 if ( !p->IsExpanded() ) return false;
1180
1181 p->SetExpanded(false);
1182
1183 VirtualHeightChanged();
1184
1185 return true;
1186 }
1187
1188 // -----------------------------------------------------------------------
1189
1190 bool wxPropertyGridPageState::DoExpand( wxPGProperty* p )
1191 {
1192 wxCHECK_MSG( p, false, wxT("invalid property id") );
1193
1194 if ( !p->GetChildCount() ) return false;
1195
1196 if ( p->IsExpanded() ) return false;
1197
1198 p->SetExpanded(true);
1199
1200 VirtualHeightChanged();
1201
1202 return true;
1203 }
1204
1205 // -----------------------------------------------------------------------
1206
1207 bool wxPropertyGridPageState::DoSelectProperty( wxPGProperty* p, unsigned int flags )
1208 {
1209 if ( this == m_pPropGrid->GetState() )
1210 return m_pPropGrid->DoSelectProperty( p, flags );
1211
1212 m_selected = p;
1213 return true;
1214 }
1215
1216 // -----------------------------------------------------------------------
1217
1218 bool wxPropertyGridPageState::DoHideProperty( wxPGProperty* p, bool hide, int flags )
1219 {
1220 if ( !hide )
1221 p->ClearFlag( wxPG_PROP_HIDDEN );
1222 else
1223 p->SetFlag( wxPG_PROP_HIDDEN );
1224
1225 if ( flags & wxPG_RECURSE )
1226 {
1227 unsigned int i;
1228 for ( i = 0; i < p->GetChildCount(); i++ )
1229 DoHideProperty(p->Item(i), hide, flags | wxPG_RECURSE_STARTS);
1230 }
1231
1232 VirtualHeightChanged();
1233
1234 return true;
1235 }
1236
1237 // -----------------------------------------------------------------------
1238
1239 bool wxPropertyGridPageState::DoEnableProperty( wxPGProperty* p, bool enable )
1240 {
1241 if ( p )
1242 {
1243 if ( enable )
1244 {
1245 if ( !(p->m_flags & wxPG_PROP_DISABLED) )
1246 return false;
1247
1248 // Enabling
1249
1250 p->m_flags &= ~(wxPG_PROP_DISABLED);
1251 }
1252 else
1253 {
1254 if ( p->m_flags & wxPG_PROP_DISABLED )
1255 return false;
1256
1257 // Disabling
1258
1259 p->m_flags |= wxPG_PROP_DISABLED;
1260
1261 }
1262
1263 // Apply same to sub-properties as well
1264 unsigned int i;
1265 for ( i = 0; i < p->GetChildCount(); i++ )
1266 DoEnableProperty( p->Item(i), enable );
1267
1268 return true;
1269 }
1270 return false;
1271 }
1272
1273 // -----------------------------------------------------------------------
1274 // wxPropertyGridPageState wxVariant related routines
1275 // -----------------------------------------------------------------------
1276
1277 // Returns list of wxVariant objects (non-categories and non-sub-properties only).
1278 // Never includes sub-properties (unless they are parented by wxParentProperty).
1279 wxVariant wxPropertyGridPageState::DoGetPropertyValues( const wxString& listname,
1280 wxPGProperty* baseparent,
1281 long flags ) const
1282 {
1283 wxPGProperty* pwc = (wxPGProperty*) baseparent;
1284
1285 // Root is the default base-parent.
1286 if ( !pwc )
1287 pwc = m_properties;
1288
1289 wxVariantList tempList;
1290 wxVariant v( tempList, listname );
1291
1292 if ( pwc->GetChildCount() )
1293 {
1294 if ( flags & wxPG_KEEP_STRUCTURE )
1295 {
1296 wxASSERT( !pwc->HasFlag(wxPG_PROP_AGGREGATE) );
1297
1298 size_t i;
1299 for ( i=0; i<pwc->GetChildCount(); i++ )
1300 {
1301 wxPGProperty* p = pwc->Item(i);
1302 if ( !p->GetChildCount() || p->HasFlag(wxPG_PROP_AGGREGATE) )
1303 {
1304 wxVariant variant = p->GetValue();
1305 variant.SetName( p->GetBaseName() );
1306 v.Append( variant );
1307 }
1308 else
1309 {
1310 v.Append( DoGetPropertyValues(p->m_name,p,flags|wxPG_KEEP_STRUCTURE) );
1311 }
1312 if ( (flags & wxPG_INC_ATTRIBUTES) && p->m_attributes.GetCount() )
1313 v.Append( p->GetAttributesAsList() );
1314 }
1315 }
1316 else
1317 {
1318 wxPropertyGridConstIterator it( this, wxPG_ITERATE_DEFAULT, pwc->Item(0) );
1319 it.SetBaseParent( pwc );
1320
1321 for ( ; !it.AtEnd(); it.Next() )
1322 {
1323 const wxPGProperty* p = it.GetProperty();
1324
1325 // Use a trick to ignore wxParentProperty itself, but not its sub-properties.
1326 if ( !p->GetChildCount() || p->HasFlag(wxPG_PROP_AGGREGATE) )
1327 {
1328 wxVariant variant = p->GetValue();
1329 variant.SetName( p->GetName() );
1330 v.Append( variant );
1331 if ( (flags & wxPG_INC_ATTRIBUTES) && p->m_attributes.GetCount() )
1332 v.Append( p->GetAttributesAsList() );
1333 }
1334 }
1335 }
1336 }
1337
1338 return v;
1339 }
1340
1341 // -----------------------------------------------------------------------
1342
1343 void wxPropertyGridPageState::DoSetPropertyValues( const wxVariantList& list, wxPGProperty* defaultCategory )
1344 {
1345 unsigned char origFrozen = 1;
1346
1347 if ( m_pPropGrid->GetState() == this )
1348 {
1349 origFrozen = m_pPropGrid->m_frozen;
1350 if ( !origFrozen ) m_pPropGrid->Freeze();
1351 }
1352
1353 wxPropertyCategory* use_category = (wxPropertyCategory*)defaultCategory;
1354
1355 if ( !use_category )
1356 use_category = (wxPropertyCategory*)m_properties;
1357
1358 // Let's iterate over the list of variants.
1359 wxVariantList::const_iterator node;
1360 int numSpecialEntries = 0;
1361
1362 //
1363 // Second pass for special entries
1364 for ( node = list.begin(); node != list.end(); node++ )
1365 {
1366 wxVariant *current = (wxVariant*)*node;
1367
1368 // Make sure it is wxVariant.
1369 wxASSERT( current );
1370 wxASSERT( wxStrcmp(current->GetClassInfo()->GetClassName(),wxT("wxVariant")) == 0 );
1371
1372 const wxString& name = current->GetName();
1373 if ( name.length() > 0 )
1374 {
1375 //
1376 // '@' signified a special entry
1377 if ( name[0] == wxS('@') )
1378 {
1379 numSpecialEntries++;
1380 }
1381 else
1382 {
1383 wxPGProperty* foundProp = BaseGetPropertyByName(name);
1384 if ( foundProp )
1385 {
1386 wxPGProperty* p = foundProp;
1387
1388 // If it was a list, we still have to go through it.
1389 if ( wxStrcmp(current->GetType(), wxS("list")) == 0 )
1390 {
1391 DoSetPropertyValues( current->GetList(),
1392 p->IsCategory()?p:((wxPGProperty*)NULL)
1393 );
1394 }
1395 else
1396 {
1397 #ifdef __WXDEBUG__
1398 if ( wxStrcmp(current->GetType(), p->GetValue().GetType()) != 0)
1399 {
1400 wxLogDebug(wxT("wxPropertyGridPageState::DoSetPropertyValues Warning: Setting value of property \"%s\" from variant"),
1401 p->GetName().c_str());
1402 }
1403 #endif
1404
1405 p->SetValue(*current);
1406 }
1407 }
1408 else
1409 {
1410 // Is it list?
1411 if ( current->GetType() != wxS("list") )
1412 {
1413 // Not.
1414 }
1415 else
1416 {
1417 // Yes, it is; create a sub category and append contents there.
1418 wxPGProperty* newCat = DoInsert(use_category,-1,new wxPropertyCategory(current->GetName(),wxPG_LABEL));
1419 DoSetPropertyValues( current->GetList(), newCat );
1420 }
1421 }
1422 }
1423 }
1424 }
1425
1426 if ( numSpecialEntries )
1427 {
1428 for ( node = list.begin(); node != list.end(); node++ )
1429 {
1430 wxVariant *current = (wxVariant*)*node;
1431
1432 const wxString& name = current->GetName();
1433 if ( name.length() > 0 )
1434 {
1435 //
1436 // '@' signified a special entry
1437 if ( name[0] == wxS('@') )
1438 {
1439 numSpecialEntries--;
1440
1441 size_t pos2 = name.rfind(wxS('@'));
1442 if ( pos2 > 0 && pos2 < (name.size()-1) )
1443 {
1444 wxString propName = name.substr(1, pos2-1);
1445 wxString entryType = name.substr(pos2+1, wxString::npos);
1446
1447 if ( entryType == wxS("attr") )
1448 {
1449 //
1450 // List of attributes
1451 wxPGProperty* foundProp = BaseGetPropertyByName(propName);
1452 if ( foundProp )
1453 {
1454 wxASSERT( current->GetType() == wxPG_VARIANT_TYPE_LIST );
1455
1456 wxVariantList& list2 = current->GetList();
1457 wxVariantList::const_iterator node2;
1458
1459 for ( node2 = list2.begin(); node2 != list2.end(); node2++ )
1460 {
1461 wxVariant *attr = (wxVariant*)*node2;
1462 foundProp->SetAttribute( attr->GetName(), *attr );
1463 }
1464 }
1465 else
1466 {
1467 // ERROR: No such property: 'propName'
1468 }
1469 }
1470 }
1471 else
1472 {
1473 // ERROR: Special entry requires name of format @<propname>@<entrytype>
1474 }
1475 }
1476 }
1477
1478 if ( !numSpecialEntries )
1479 break;
1480 }
1481 }
1482
1483 if ( !origFrozen )
1484 {
1485 m_pPropGrid->Thaw();
1486
1487 if ( this == m_pPropGrid->GetState() )
1488 {
1489 m_selected->UpdateControl(m_pPropGrid->GetEditorControl());
1490 }
1491 }
1492
1493 }
1494
1495 // -----------------------------------------------------------------------
1496 // wxPropertyGridPageState property adding and removal
1497 // -----------------------------------------------------------------------
1498
1499 int wxPropertyGridPageState::PrepareToAddItem( wxPGProperty* property,
1500 wxPGProperty* scheduledParent )
1501 {
1502 wxPropertyGrid* propGrid = m_pPropGrid;
1503
1504 // This will allow better behavior.
1505 if ( scheduledParent == m_properties )
1506 scheduledParent = (wxPGProperty*) NULL;
1507
1508 if ( scheduledParent && !scheduledParent->IsCategory() )
1509 {
1510 wxASSERT_MSG( property->GetBaseName().length(),
1511 "Property's children must have unique, non-empty names within their scope" );
1512 }
1513
1514 property->m_parentState = this;
1515
1516 if ( property->IsCategory() )
1517 {
1518
1519 // Parent of a category must be either root or another category
1520 // (otherwise Bad Things might happen).
1521 wxASSERT_MSG( scheduledParent == NULL ||
1522 scheduledParent == m_properties ||
1523 scheduledParent->IsCategory(),
1524 wxT("Parent of a category must be either root or another category."));
1525
1526 // If we already have category with same name, delete given property
1527 // and use it instead as most recent caption item.
1528 wxPGProperty* found_id = BaseGetPropertyByName( property->GetBaseName() );
1529 if ( found_id )
1530 {
1531 wxPropertyCategory* pwc = (wxPropertyCategory*) found_id;
1532 if ( pwc->IsCategory() ) // Must be a category.
1533 {
1534 delete property;
1535 m_currentCategory = pwc;
1536 return 2; // Tells the caller what we did.
1537 }
1538 }
1539 }
1540
1541 #ifdef __WXDEBUG__
1542 // Warn for identical names in debug mode.
1543 if ( BaseGetPropertyByName(property->GetName()) &&
1544 (!scheduledParent || scheduledParent->IsCategory()) )
1545 {
1546 wxLogError(wxT("wxPropertyGrid: Warning - item with name \"%s\" already exists."),
1547 property->GetName().c_str());
1548 wxPGGlobalVars->m_warnings++;
1549 }
1550 #endif
1551
1552 // Make sure nothing is selected.
1553 if ( propGrid && propGrid->m_selected )
1554 {
1555 bool selRes = propGrid->ClearSelection();
1556 wxPG_CHECK_MSG_DBG( selRes,
1557 -1,
1558 wxT("failed to deselect a property (editor probably had invalid value)") );
1559 }
1560
1561 if ( scheduledParent )
1562 {
1563 // Use parent's colours.
1564 property->m_bgColIndex = scheduledParent->m_bgColIndex;
1565 property->m_fgColIndex = scheduledParent->m_fgColIndex;
1566
1567 // Fix no parent does not yet have parenting flag yet, set one now
1568 if ( !scheduledParent->HasFlag(wxPG_PROP_PARENTAL_FLAGS) )
1569 scheduledParent->SetParentalType(wxPG_PROP_MISC_PARENT);
1570 //scheduledParent->SetFlag(wxPG_PROP_MISC_PARENT);
1571 }
1572
1573 // If in hideable adding mode, or if assigned parent is hideable, then
1574 // make this one hideable.
1575 if (
1576 ( scheduledParent && (scheduledParent->m_flags & wxPG_PROP_HIDDEN) ) ||
1577 ( propGrid && (propGrid->m_iFlags & wxPG_FL_ADDING_HIDEABLES) )
1578 )
1579 property->SetFlag( wxPG_PROP_HIDDEN );
1580
1581 // Set custom image flag.
1582 int custImgHeight = property->OnMeasureImage().y;
1583 if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
1584 {
1585 property->m_flags |= wxPG_PROP_CUSTOMIMAGE;
1586 }
1587
1588 if ( propGrid && (propGrid->GetWindowStyleFlag() & wxPG_LIMITED_EDITING) )
1589 property->m_flags |= wxPG_PROP_NOEDITOR;
1590
1591 if ( !property->IsCategory() )
1592 {
1593 // This is not a category.
1594
1595 //wxASSERT_MSG( property->GetEditorClass(), wxT("Editor class not initialized!") );
1596
1597 // Depth.
1598 //
1599 unsigned char depth = 1;
1600 if ( scheduledParent )
1601 {
1602 depth = scheduledParent->m_depth;
1603 if ( !scheduledParent->IsCategory() )
1604 depth++;
1605 }
1606 property->m_depth = depth;
1607 unsigned char greyDepth = depth;
1608
1609 if ( scheduledParent )
1610 {
1611 wxPropertyCategory* pc;
1612
1613 if ( scheduledParent->IsCategory() || scheduledParent->IsRoot() )
1614 pc = (wxPropertyCategory*)scheduledParent;
1615 else
1616 // This conditional compile is necessary to
1617 // bypass some compiler bug.
1618 pc = GetPropertyCategory(scheduledParent);
1619
1620 if ( pc )
1621 greyDepth = pc->GetDepth();
1622 else
1623 greyDepth = scheduledParent->m_depthBgCol;
1624 }
1625
1626 property->m_depthBgCol = greyDepth;
1627
1628 // Prepare children pre-added children
1629 if ( property->GetChildCount() )
1630 {
1631 property->SetParentalType(wxPG_PROP_AGGREGATE);
1632
1633 property->SetExpanded(false); // Properties with children are not expanded by default.
1634 if ( propGrid && propGrid->GetWindowStyleFlag() & wxPG_HIDE_MARGIN )
1635 property->SetExpanded(true); // ...unless it cannot be expanded.
1636
1637 property->PrepareSubProperties();
1638
1639 return -1;
1640 }
1641
1642 if ( propGrid && (propGrid->GetExtraStyle() & wxPG_EX_AUTO_UNSPECIFIED_VALUES) )
1643 property->SetFlagRecursively(wxPG_PROP_AUTO_UNSPECIFIED, true);
1644
1645 return 0;
1646 }
1647 else
1648 {
1649 // This is a category.
1650
1651 // depth
1652 unsigned char depth = 1;
1653 if ( scheduledParent )
1654 {
1655 depth = scheduledParent->m_depth + 1;
1656 }
1657 property->m_depth = depth;
1658 property->m_depthBgCol = depth;
1659
1660 m_currentCategory = (wxPropertyCategory*)property;
1661
1662 wxPropertyCategory* pc = (wxPropertyCategory*)property;
1663
1664 // Calculate text extent for caption item.
1665 if ( propGrid )
1666 pc->CalculateTextExtent(propGrid, propGrid->GetCaptionFont());
1667
1668 return 1;
1669 }
1670 }
1671
1672 // -----------------------------------------------------------------------
1673
1674 wxPGProperty* wxPropertyGridPageState::DoAppend( wxPGProperty* property )
1675 {
1676 wxPropertyCategory* cur_cat = m_currentCategory;
1677 if ( property->IsCategory() )
1678 cur_cat = (wxPropertyCategory*) NULL;
1679
1680 return DoInsert( cur_cat, -1, property );
1681 }
1682
1683 // -----------------------------------------------------------------------
1684
1685 wxPGProperty* wxPropertyGridPageState::DoInsert( wxPGProperty* parent, int index, wxPGProperty* property )
1686 {
1687 if ( !parent )
1688 parent = m_properties;
1689
1690 wxCHECK_MSG( !parent->HasFlag(wxPG_PROP_AGGREGATE),
1691 wxNullProperty,
1692 wxT("when adding properties to fixed parents, use BeginAddChildren and EndAddChildren.") );
1693
1694 int parenting = PrepareToAddItem( property, (wxPropertyCategory*)parent );
1695
1696 // This type of invalid parenting value indicates we should exit now, returning
1697 // id of most recent category.
1698 if ( parenting > 1 )
1699 return m_currentCategory;
1700
1701 // Note that item must be added into current mode later.
1702
1703 // If parent is wxParentProperty, just stick it in...
1704 // If parent is root (m_properties), then...
1705 // In categoric mode: Add as last item in m_abcArray (if not category).
1706 // Add to given index in m_regularArray.
1707 // In non-cat mode: Add as last item in m_regularArray.
1708 // Add to given index in m_abcArray.
1709 // If parent is category, then...
1710 // 1) Add to given category in given index.
1711 // 2) Add as last item in m_abcArray.
1712
1713 if ( !parent->IsCategory() && !parent->IsRoot() )
1714 {
1715 // Parent is wxParentingProperty: Just stick it in...
1716 parent->AddChild2( property, index );
1717 }
1718 else
1719 {
1720 // Parent is Category or Root.
1721
1722 if ( m_properties == &m_regularArray )
1723 {
1724 // Categorized mode
1725
1726 // Only add non-categories to m_abcArray.
1727 if ( m_abcArray && parenting <= 0 )
1728 m_abcArray->AddChild2( property, -1, false );
1729
1730 // Add to current mode.
1731 parent->AddChild2( property, index );
1732
1733 }
1734 else
1735 {
1736 // Non-categorized mode.
1737
1738 if ( parent != m_properties )
1739 // Parent is category.
1740 parent->AddChild2( property, index, false );
1741 else
1742 // Parent is root.
1743 m_regularArray.AddChild2( property, -1, false );
1744
1745 // Add to current mode (no categories).
1746 if ( parenting <= 0 )
1747 m_abcArray->AddChild2( property, index );
1748 }
1749 }
1750
1751 // category stuff
1752 if ( property->IsCategory() )
1753 {
1754 // This is a category caption item.
1755
1756 // Last caption is not the bottom one (this info required by append)
1757 m_lastCaptionBottomnest = 0;
1758 }
1759
1760 // Only add name to hashmap if parent is root or category
1761 if ( (parent->IsCategory() || parent->IsRoot()) && property->m_name.length() )
1762 m_dictName[property->m_name] = (void*) property;
1763
1764 VirtualHeightChanged();
1765
1766 property->UpdateParentValues();
1767
1768 m_itemsAdded = 1;
1769
1770 return property;
1771 }
1772
1773 // -----------------------------------------------------------------------
1774
1775 void wxPropertyGridPageState::DoDelete( wxPGProperty* item )
1776 {
1777 wxCHECK_RET( item->GetParent(),
1778 wxT("this property was already deleted") );
1779
1780 wxCHECK_RET( item != &m_regularArray && item != m_abcArray,
1781 wxT("wxPropertyGrid: Do not attempt to remove the root item.") );
1782
1783 size_t i;
1784 unsigned int indinparent = item->GetIndexInParent();
1785
1786 wxPGProperty* pwc = (wxPGProperty*)item;
1787
1788 wxCHECK_RET( !item->GetParent()->HasFlag(wxPG_PROP_AGGREGATE),
1789 wxT("wxPropertyGrid: Do not attempt to remove sub-properties.") );
1790
1791 if ( item->IsCategory() )
1792 {
1793 // deleting a category
1794
1795 // erase category entries from the hash table
1796 for ( i=0; i<pwc->GetChildCount(); i++ )
1797 {
1798 wxPGProperty* sp = pwc->Item( i );
1799 if ( sp->GetBaseName().Len() ) m_dictName.erase(sp->GetBaseName());
1800 }
1801
1802 if ( pwc == m_currentCategory )
1803 m_currentCategory = (wxPropertyCategory*) NULL;
1804
1805 if ( m_abcArray )
1806 {
1807 // Remove children from non-categorized array.
1808 for ( i=0; i<pwc->GetChildCount(); i++ )
1809 {
1810 wxPGProperty * p = pwc->Item( i );
1811 wxASSERT( p != NULL );
1812 if ( !p->IsCategory() )
1813 m_abcArray->m_children.Remove( p );
1814 }
1815
1816 if ( IsInNonCatMode() )
1817 m_abcArray->FixIndexesOfChildren();
1818 }
1819 }
1820
1821 if ( !IsInNonCatMode() )
1822 {
1823 // categorized mode - non-categorized array
1824
1825 // Remove from non-cat array, but only if parent is in it
1826 if ( !item->IsCategory() && item->GetParent()->IsCategory() )
1827 {
1828 if ( m_abcArray )
1829 {
1830 m_abcArray->m_children.Remove( item );
1831 }
1832 }
1833
1834 // categorized mode - categorized array
1835 item->m_parent->m_children.RemoveAt(indinparent);
1836 item->m_parent->FixIndexesOfChildren(/*indinparent*/);
1837 }
1838 else
1839 {
1840 // non-categorized mode - categorized array
1841
1842 // We need to find location of item.
1843 wxPGProperty* cat_parent = &m_regularArray;
1844 int cat_index = m_regularArray.GetChildCount();
1845 size_t i;
1846 for ( i = 0; i < m_regularArray.GetChildCount(); i++ )
1847 {
1848 wxPGProperty* p = m_regularArray.Item(i);
1849 if ( p == item ) { cat_index = i; break; }
1850 if ( p->IsCategory() )
1851 {
1852 int subind = ((wxPGProperty*)p)->Index(item);
1853 if ( subind != wxNOT_FOUND )
1854 {
1855 cat_parent = ((wxPGProperty*)p);
1856 cat_index = subind;
1857 break;
1858 }
1859 }
1860 }
1861 cat_parent->m_children.RemoveAt(cat_index);
1862
1863 // non-categorized mode - non-categorized array
1864 if ( !item->IsCategory() )
1865 {
1866 wxASSERT( item->m_parent == m_abcArray );
1867 item->m_parent->m_children.RemoveAt(indinparent);
1868 item->m_parent->FixIndexesOfChildren(indinparent);
1869 }
1870 }
1871
1872 if ( item->GetBaseName().Len() ) m_dictName.erase(item->GetBaseName());
1873
1874 // We can actually delete it now
1875 delete item;
1876
1877 m_itemsAdded = 1; // Not a logical assignment (but required nonetheless).
1878
1879 VirtualHeightChanged();
1880 }
1881
1882 // -----------------------------------------------------------------------
1883
1884 #endif // wxUSE_PROPGRID