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