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