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