]> git.saurik.com Git - wxWidgets.git/blame - src/propgrid/propgridpagestate.cpp
added wxPropertyGrid from Jaakko Salli (#9934)
[wxWidgets.git] / src / propgrid / propgridpagestate.cpp
CommitLineData
1c4293cb
VZ
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
65void 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
88void 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
113void 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
122void 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
167void 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
217wxPropertyGridPageState::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
238wxPropertyGridPageState::~wxPropertyGridPageState()
239{
240 delete m_abcArray;
241}
242
243// -----------------------------------------------------------------------
244
245void 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
284void 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
304void 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
324void 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
337void 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
384wxPGProperty* 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
408wxPropertyCategory* 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
427wxPGProperty* 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
454wxPGProperty* 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
515bool 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
594static 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
601void 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
624void 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
645wxPGProperty* 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
657wxPropertyGridHitTestResult 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()
668int 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
709int 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
718int wxPropertyGridPageState::GetColumnMinWidth( int WXUNUSED(column) ) const
719{
720 return wxPG_DRAG_MARGIN;
721}
722
723void 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
747void 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.
793void 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
810wxSize 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
856void 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
1017void 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
1031int 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
1082bool 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
1110bool 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
1125bool 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
1139void 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
1168bool 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
1185bool 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
1202bool 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
1213bool 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
1234bool 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).
1274wxVariant 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
1338void 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
1494int 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
1663wxPGProperty* 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
1674wxPGProperty* 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
1764void 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// -----------------------------------------------------------------------