Use wxCLASSINFO() instead of deprecated CLASSINFO().
[wxWidgets.git] / src / propgrid / manager.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/propgrid/manager.cpp
3 // Purpose: wxPropertyGridManager
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: 2005-01-14
7 // RCS-ID: $Id$
8 // Copyright: (c) Jaakko Salli
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_PROPGRID
20
21 #ifndef WX_PRECOMP
22 #include "wx/defs.h"
23 #include "wx/object.h"
24 #include "wx/hash.h"
25 #include "wx/string.h"
26 #include "wx/log.h"
27 #include "wx/event.h"
28 #include "wx/window.h"
29 #include "wx/panel.h"
30 #include "wx/dc.h"
31 #include "wx/pen.h"
32 #include "wx/brush.h"
33 #include "wx/cursor.h"
34 #include "wx/settings.h"
35 #include "wx/textctrl.h"
36 #include "wx/sizer.h"
37 #include "wx/statusbr.h"
38 #include "wx/intl.h"
39 #endif
40
41 // This define is necessary to prevent macro clearing
42 #define __wxPG_SOURCE_FILE__
43
44 #include "wx/propgrid/propgrid.h"
45
46 #include "wx/propgrid/manager.h"
47
48
49 #define wxPG_MAN_ALTERNATE_BASE_ID 11249 // Needed for wxID_ANY madnesss
50
51
52 // -----------------------------------------------------------------------
53
54 // For wxMSW cursor consistency, we must do mouse capturing even
55 // when using custom controls
56
57 #define BEGIN_MOUSE_CAPTURE \
58 if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) ) \
59 { \
60 CaptureMouse(); \
61 m_iFlags |= wxPG_FL_MOUSE_CAPTURED; \
62 }
63
64 #define END_MOUSE_CAPTURE \
65 if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) \
66 { \
67 ReleaseMouse(); \
68 m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED); \
69 }
70
71 // -----------------------------------------------------------------------
72 // wxPropertyGridManager
73 // -----------------------------------------------------------------------
74
75 const char wxPropertyGridManagerNameStr[] = "wxPropertyGridManager";
76
77
78 // Categoric Mode Icon
79 static const char* const gs_xpm_catmode[] = {
80 "16 16 5 1",
81 ". c none",
82 "B c black",
83 "D c #868686",
84 "L c #CACACA",
85 "W c #FFFFFF",
86 ".DDD............",
87 ".DLD.BBBBBB.....",
88 ".DDD............",
89 ".....DDDDD.DDD..",
90 "................",
91 ".....DDDDD.DDD..",
92 "................",
93 ".....DDDDD.DDD..",
94 "................",
95 ".....DDDDD.DDD..",
96 "................",
97 ".DDD............",
98 ".DLD.BBBBBB.....",
99 ".DDD............",
100 ".....DDDDD.DDD..",
101 "................"
102 };
103
104 // Alphabetic Mode Icon
105 static const char* const gs_xpm_noncatmode[] = {
106 "16 16 5 1",
107 ". c none",
108 "B c black",
109 "D c #868686",
110 "L c #000080",
111 "W c #FFFFFF",
112 "..DBD...DDD.DDD.",
113 ".DB.BD..........",
114 ".BBBBB..DDD.DDD.",
115 ".B...B..........",
116 "...L....DDD.DDD.",
117 "...L............",
118 ".L.L.L..DDD.DDD.",
119 "..LLL...........",
120 "...L....DDD.DDD.",
121 "................",
122 ".BBBBB..DDD.DDD.",
123 "....BD..........",
124 "...BD...DDD.DDD.",
125 "..BD............",
126 ".BBBBB..DDD.DDD.",
127 "................"
128 };
129
130 // Default Page Icon.
131 static const char* const gs_xpm_defpage[] = {
132 "16 16 5 1",
133 ". c none",
134 "B c black",
135 "D c #868686",
136 "L c #000080",
137 "W c #FFFFFF",
138 "................",
139 "................",
140 "..BBBBBBBBBBBB..",
141 "..B..........B..",
142 "..B.BB.LLLLL.B..",
143 "..B..........B..",
144 "..B.BB.LLLLL.B..",
145 "..B..........B..",
146 "..B.BB.LLLLL.B..",
147 "..B..........B..",
148 "..B.BB.LLLLL.B..",
149 "..B..........B..",
150 "..BBBBBBBBBBBB..",
151 "................",
152 "................",
153 "................"
154 };
155
156 // -----------------------------------------------------------------------
157 // wxPropertyGridPage
158 // -----------------------------------------------------------------------
159
160
161 IMPLEMENT_CLASS(wxPropertyGridPage, wxEvtHandler)
162
163
164 BEGIN_EVENT_TABLE(wxPropertyGridPage, wxEvtHandler)
165 END_EVENT_TABLE()
166
167
168 wxPropertyGridPage::wxPropertyGridPage()
169 : wxEvtHandler(), wxPropertyGridInterface(), wxPropertyGridPageState()
170 {
171 m_pState = this; // wxPropertyGridInterface to point to State
172 m_manager = NULL;
173 m_isDefault = false;
174 }
175
176 wxPropertyGridPage::~wxPropertyGridPage()
177 {
178 }
179
180 void wxPropertyGridPage::Clear()
181 {
182 GetStatePtr()->DoClear();
183 }
184
185 wxSize wxPropertyGridPage::FitColumns()
186 {
187 wxSize sz = DoFitColumns();
188 return sz;
189 }
190
191 void wxPropertyGridPage::RefreshProperty( wxPGProperty* p )
192 {
193 if ( m_manager )
194 m_manager->RefreshProperty(p);
195 }
196
197 void wxPropertyGridPage::OnShow()
198 {
199 }
200
201 void wxPropertyGridPage::SetSplitterPosition( int splitterPos, int col )
202 {
203 wxPropertyGrid* pg = GetGrid();
204 if ( pg->GetState() == this )
205 pg->SetSplitterPosition(splitterPos);
206 else
207 DoSetSplitterPosition(splitterPos, col, false);
208 }
209
210 void wxPropertyGridPage::DoSetSplitterPosition( int pos,
211 int splitterColumn,
212 int flags )
213 {
214 if ( (flags & wxPG_SPLITTER_ALL_PAGES) && m_manager->GetPageCount() )
215 m_manager->SetSplitterPosition( pos, splitterColumn );
216 else
217 wxPropertyGridPageState::DoSetSplitterPosition( pos,
218 splitterColumn,
219 flags );
220 }
221
222 // -----------------------------------------------------------------------
223 // wxPGHeaderCtrl
224 // -----------------------------------------------------------------------
225
226 #if wxUSE_HEADERCTRL
227
228 class wxPGHeaderCtrl : public wxHeaderCtrl
229 {
230 public:
231 wxPGHeaderCtrl(wxPropertyGridManager* manager) :
232 wxHeaderCtrl()
233 {
234 m_manager = manager;
235 EnsureColumnCount(2);
236
237 // Seed titles with defaults
238 m_columns[0]->SetTitle(_("Property"));
239 m_columns[1]->SetTitle(_("Value"));
240 }
241
242 virtual ~wxPGHeaderCtrl()
243 {
244 for (unsigned int i=0; i<m_columns.size(); i++ )
245 delete m_columns[i];
246 }
247
248 int DetermineColumnWidth(unsigned int idx, int* pMinWidth) const
249 {
250 const wxPropertyGridPage* page = m_page;
251 int colWidth = page->GetColumnWidth(idx);
252 int colMinWidth = page->GetColumnMinWidth(idx);
253 if ( idx == 0 )
254 {
255 wxPropertyGrid* pg = m_manager->GetGrid();
256 int margin = pg->GetMarginWidth();
257
258 // Compensate for the internal border
259 margin += (pg->GetSize().x - pg->GetClientSize().x) / 2;
260
261 colWidth += margin;
262 colMinWidth += margin;
263 }
264 *pMinWidth = colMinWidth;
265 return colWidth;
266 }
267
268 void OnPageChanged(const wxPropertyGridPage* page)
269 {
270 m_page = page;
271 OnPageUpdated();
272 }
273
274 void OnPageUpdated()
275 {
276 // Get column info from the page
277 const wxPropertyGridPage* page = m_page;
278 unsigned int colCount = page->GetColumnCount();
279 EnsureColumnCount(colCount);
280
281 for ( unsigned int i=0; i<colCount; i++ )
282 {
283 wxHeaderColumnSimple* colInfo = m_columns[i];
284 int colMinWidth = 0;
285 int colWidth = DetermineColumnWidth(i, &colMinWidth);
286 colInfo->SetWidth(colWidth);
287 colInfo->SetMinWidth(colMinWidth);
288 }
289
290 SetColumnCount(colCount);
291 }
292
293 void OnColumWidthsChanged()
294 {
295 const wxPropertyGridPage* page = m_page;
296 unsigned int colCount = page->GetColumnCount();
297
298 for ( unsigned int i=0; i<colCount; i++ )
299 {
300 wxHeaderColumnSimple* colInfo = m_columns[i];
301 int colMinWidth = 0;
302 int colWidth = DetermineColumnWidth(i, &colMinWidth);
303 colInfo->SetWidth(colWidth);
304 colInfo->SetMinWidth(colMinWidth);
305 UpdateColumn(i);
306 }
307 }
308
309 virtual const wxHeaderColumn& GetColumn(unsigned int idx) const
310 {
311 return *m_columns[idx];
312 }
313
314 void SetColumnTitle(unsigned int idx, const wxString& title)
315 {
316 EnsureColumnCount(idx+1);
317 m_columns[idx]->SetTitle(title);
318 }
319
320 private:
321 void EnsureColumnCount(unsigned int count)
322 {
323 while ( m_columns.size() < count )
324 {
325 wxHeaderColumnSimple* colInfo = new wxHeaderColumnSimple("");
326 m_columns.push_back(colInfo);
327 }
328 }
329
330 void OnSetColumnWidth(int col, int colWidth)
331 {
332 wxPropertyGrid* pg = m_manager->GetGrid();
333
334 // Compensate for the internal border
335 int x = -((pg->GetSize().x - pg->GetClientSize().x) / 2);
336
337 for ( int i=0; i<col; i++ )
338 x += m_columns[i]->GetWidth();
339
340 x += colWidth;
341
342 pg->DoSetSplitterPosition(x, col,
343 wxPG_SPLITTER_REFRESH |
344 wxPG_SPLITTER_FROM_EVENT);
345 }
346
347 virtual bool ProcessEvent( wxEvent& event )
348 {
349 if ( event.IsKindOf(wxCLASSINFO(wxHeaderCtrlEvent)) )
350 {
351 wxHeaderCtrlEvent& hcEvent =
352 static_cast<wxHeaderCtrlEvent&>(event);
353
354 wxPropertyGrid* pg = m_manager->GetGrid();
355 int col = hcEvent.GetColumn();
356 int evtType = event.GetEventType();
357
358 if ( evtType == wxEVT_COMMAND_HEADER_RESIZING )
359 {
360 int colWidth = hcEvent.GetWidth();
361
362 OnSetColumnWidth(col, colWidth);
363
364 pg->SendEvent(wxEVT_PG_COL_DRAGGING,
365 NULL, NULL, 0,
366 (unsigned int)col);
367
368 return true;
369 }
370 else if ( evtType == wxEVT_COMMAND_HEADER_BEGIN_RESIZE )
371 {
372 // Never allow column resize if layout is static
373 if ( m_manager->HasFlag(wxPG_STATIC_SPLITTER) )
374 hcEvent.Veto();
375 // Allow application to veto dragging
376 else if ( pg->SendEvent(wxEVT_PG_COL_BEGIN_DRAG,
377 NULL, NULL, 0,
378 (unsigned int)col) )
379 hcEvent.Veto();
380
381 return true;
382 }
383 else if ( evtType == wxEVT_COMMAND_HEADER_END_RESIZE )
384 {
385 pg->SendEvent(wxEVT_PG_COL_END_DRAG,
386 NULL, NULL, 0,
387 (unsigned int)col);
388
389 return true;
390 }
391 }
392
393 return wxHeaderCtrl::ProcessEvent(event);
394 }
395
396 wxPropertyGridManager* m_manager;
397 const wxPropertyGridPage* m_page;
398 wxVector<wxHeaderColumnSimple*> m_columns;
399 };
400
401 #endif // wxUSE_HEADERCTRL
402
403 // -----------------------------------------------------------------------
404 // wxPropertyGridManager
405 // -----------------------------------------------------------------------
406
407 // Final default splitter y is client height minus this.
408 #define wxPGMAN_DEFAULT_NEGATIVE_SPLITTER_Y 100
409
410 // -----------------------------------------------------------------------
411
412 IMPLEMENT_CLASS(wxPropertyGridManager, wxPanel)
413
414 // -----------------------------------------------------------------------
415
416 BEGIN_EVENT_TABLE(wxPropertyGridManager, wxPanel)
417 EVT_MOTION(wxPropertyGridManager::OnMouseMove)
418 EVT_SIZE(wxPropertyGridManager::OnResize)
419 EVT_PAINT(wxPropertyGridManager::OnPaint)
420 EVT_LEFT_DOWN(wxPropertyGridManager::OnMouseClick)
421 EVT_LEFT_UP(wxPropertyGridManager::OnMouseUp)
422 EVT_LEAVE_WINDOW(wxPropertyGridManager::OnMouseEntry)
423 //EVT_ENTER_WINDOW(wxPropertyGridManager::OnMouseEntry)
424 END_EVENT_TABLE()
425
426 // -----------------------------------------------------------------------
427
428 wxPropertyGridManager::wxPropertyGridManager()
429 : wxPanel()
430 {
431 Init1();
432 }
433
434 // -----------------------------------------------------------------------
435
436 wxPropertyGridManager::wxPropertyGridManager( wxWindow *parent,
437 wxWindowID id,
438 const wxPoint& pos,
439 const wxSize& size,
440 long style,
441 const wxString& name )
442 : wxPanel()
443 {
444 Init1();
445 Create(parent,id,pos,size,style,name);
446 }
447
448 // -----------------------------------------------------------------------
449
450 bool wxPropertyGridManager::Create( wxWindow *parent,
451 wxWindowID id,
452 const wxPoint& pos,
453 const wxSize& size,
454 long style,
455 const wxString& name )
456 {
457 if ( !m_pPropGrid )
458 m_pPropGrid = CreatePropertyGrid();
459
460 bool res = wxPanel::Create( parent, id, pos, size,
461 (style&0xFFFF0000)|wxWANTS_CHARS,
462 name );
463 Init2(style);
464
465 // FIXME: this changes call ordering so wxPropertyGrid is created
466 // immediately, before SetExtraStyle has a chance to be called. However,
467 // without it, we may get assertions if size is wxDefaultSize.
468 //SetInitialSize(size);
469
470 return res;
471 }
472
473 // -----------------------------------------------------------------------
474
475 //
476 // Initialize values to defaults
477 //
478 void wxPropertyGridManager::Init1()
479 {
480
481 m_pPropGrid = NULL;
482
483 #if wxUSE_TOOLBAR
484 m_pToolbar = NULL;
485 #endif
486 #if wxUSE_HEADERCTRL
487 m_pHeaderCtrl = NULL;
488 m_showHeader = false;
489 #endif
490 m_pTxtHelpCaption = NULL;
491 m_pTxtHelpContent = NULL;
492
493 m_emptyPage = NULL;
494
495 m_selPage = -1;
496
497 m_width = m_height = 0;
498
499 m_splitterHeight = 5;
500
501 m_splitterY = -1; // -1 causes default to be set.
502
503 m_nextDescBoxSize = -1;
504
505 m_categorizedModeToolId = -1;
506 m_alphabeticModeToolId = -1;
507
508 m_extraHeight = 0;
509 m_dragStatus = 0;
510 m_onSplitter = 0;
511 m_iFlags = 0;
512 }
513
514 // -----------------------------------------------------------------------
515
516 // These flags are always used in wxPropertyGrid integrated in wxPropertyGridManager.
517 #define wxPG_MAN_PROPGRID_FORCED_FLAGS ( wxBORDER_THEME | \
518 wxNO_FULL_REPAINT_ON_RESIZE| \
519 wxCLIP_CHILDREN)
520
521 // Which flags can be passed to underlying wxPropertyGrid.
522 #define wxPG_MAN_PASS_FLAGS_MASK (0xFFF0|wxTAB_TRAVERSAL)
523
524 //
525 // Initialize after parent etc. set
526 //
527 void wxPropertyGridManager::Init2( int style )
528 {
529
530 if ( m_iFlags & wxPG_FL_INITIALIZED )
531 return;
532
533 m_windowStyle |= (style&0x0000FFFF);
534
535 wxSize csz = GetClientSize();
536
537 m_cursorSizeNS = wxCursor(wxCURSOR_SIZENS);
538
539 // Prepare the first page
540 // NB: But just prepare - you still need to call Add/InsertPage
541 // to actually add properties on it.
542 wxPropertyGridPage* pd = new wxPropertyGridPage();
543 pd->m_isDefault = true;
544 pd->m_manager = this;
545 wxPropertyGridPageState* state = pd->GetStatePtr();
546 state->m_pPropGrid = m_pPropGrid;
547 m_arrPages.push_back( pd );
548 m_pPropGrid->m_pState = state;
549
550 wxWindowID baseId = GetId();
551 wxWindowID useId = baseId;
552 if ( baseId < 0 )
553 baseId = wxPG_MAN_ALTERNATE_BASE_ID;
554
555 #ifdef __WXMAC__
556 // Smaller controls on Mac
557 SetWindowVariant(wxWINDOW_VARIANT_SMALL);
558 #endif
559
560 long propGridFlags = (m_windowStyle&wxPG_MAN_PASS_FLAGS_MASK)
561 |wxPG_MAN_PROPGRID_FORCED_FLAGS;
562
563 propGridFlags &= ~wxBORDER_MASK;
564
565 if ((style & wxPG_NO_INTERNAL_BORDER) == 0)
566 {
567 propGridFlags |= wxBORDER_THEME;
568 }
569 else
570 {
571 propGridFlags |= wxBORDER_NONE;
572 wxWindow::SetExtraStyle(wxPG_EX_TOOLBAR_SEPARATOR);
573 }
574
575 // Create propertygrid.
576 m_pPropGrid->Create(this,baseId,wxPoint(0,0),csz, propGridFlags);
577
578 m_pPropGrid->m_eventObject = this;
579
580 m_pPropGrid->SetId(useId);
581
582 m_pPropGrid->m_iFlags |= wxPG_FL_IN_MANAGER;
583
584 m_pState = m_pPropGrid->m_pState;
585
586 m_pPropGrid->SetExtraStyle(wxPG_EX_INIT_NOCAT);
587
588 // Connect to property grid onselect event.
589 // NB: Even if wxID_ANY is used, this doesn't connect properly in wxPython
590 // (see wxPropertyGridManager::ProcessEvent).
591 Connect(m_pPropGrid->GetId(),
592 wxEVT_PG_SELECTED,
593 wxPropertyGridEventHandler(wxPropertyGridManager::OnPropertyGridSelect));
594
595 Connect(m_pPropGrid->GetId(),
596 wxEVT_PG_COL_DRAGGING,
597 wxPropertyGridEventHandler(wxPropertyGridManager::OnPGColDrag));
598
599 // Optional initial controls.
600 m_width = -12345;
601
602 m_iFlags |= wxPG_FL_INITIALIZED;
603
604 }
605
606 // -----------------------------------------------------------------------
607
608 wxPropertyGridManager::~wxPropertyGridManager()
609 {
610 END_MOUSE_CAPTURE
611
612 //m_pPropGrid->ClearSelection();
613 wxDELETE(m_pPropGrid);
614
615 size_t i;
616 for ( i=0; i<m_arrPages.size(); i++ )
617 {
618 delete m_arrPages[i];
619 }
620
621 delete m_emptyPage;
622 }
623
624 // -----------------------------------------------------------------------
625
626 wxPropertyGrid* wxPropertyGridManager::CreatePropertyGrid() const
627 {
628 return new wxPropertyGrid();
629 }
630
631 // -----------------------------------------------------------------------
632
633 void wxPropertyGridManager::SetId( wxWindowID winid )
634 {
635 wxWindow::SetId(winid);
636
637 // TODO: Reconnect propgrid event handler(s).
638
639 m_pPropGrid->SetId(winid);
640 }
641
642 // -----------------------------------------------------------------------
643
644 wxSize wxPropertyGridManager::DoGetBestSize() const
645 {
646 return wxSize(60,150);
647 }
648
649 // -----------------------------------------------------------------------
650
651 bool wxPropertyGridManager::SetFont( const wxFont& font )
652 {
653 bool res = wxWindow::SetFont(font);
654 m_pPropGrid->SetFont(font);
655
656 // TODO: Need to do caption recacalculations for other pages as well.
657 unsigned int i;
658 for ( i=0; i<m_arrPages.size(); i++ )
659 {
660 wxPropertyGridPage* page = GetPage(i);
661
662 if ( page != m_pPropGrid->GetState() )
663 page->CalculateFontAndBitmapStuff(-1);
664 }
665
666 return res;
667 }
668
669 // -----------------------------------------------------------------------
670
671 void wxPropertyGridManager::SetExtraStyle( long exStyle )
672 {
673 wxWindow::SetExtraStyle( exStyle );
674 m_pPropGrid->SetExtraStyle( exStyle & 0xFFFFF000 );
675 #if wxUSE_TOOLBAR
676 if ( (exStyle & wxPG_EX_NO_FLAT_TOOLBAR) && m_pToolbar )
677 RecreateControls();
678 #endif
679 }
680
681 // -----------------------------------------------------------------------
682
683 void wxPropertyGridManager::Freeze()
684 {
685 m_pPropGrid->Freeze();
686 wxWindow::Freeze();
687 }
688
689 // -----------------------------------------------------------------------
690
691 void wxPropertyGridManager::Thaw()
692 {
693 wxWindow::Thaw();
694 m_pPropGrid->Thaw();
695 }
696
697 // -----------------------------------------------------------------------
698
699 void wxPropertyGridManager::SetWindowStyleFlag( long style )
700 {
701 int oldWindowStyle = GetWindowStyleFlag();
702
703 wxWindow::SetWindowStyleFlag( style );
704 m_pPropGrid->SetWindowStyleFlag( (m_pPropGrid->GetWindowStyleFlag()&~(wxPG_MAN_PASS_FLAGS_MASK)) |
705 (style&wxPG_MAN_PASS_FLAGS_MASK) );
706
707 // Need to re-position windows?
708 if ( (oldWindowStyle & (wxPG_TOOLBAR|wxPG_DESCRIPTION)) !=
709 (style & (wxPG_TOOLBAR|wxPG_DESCRIPTION)) )
710 {
711 RecreateControls();
712 }
713 }
714
715 // -----------------------------------------------------------------------
716
717 bool wxPropertyGridManager::Reparent( wxWindowBase *newParent )
718 {
719 if ( m_pPropGrid )
720 m_pPropGrid->OnTLPChanging((wxWindow*)newParent);
721
722 bool res = wxPanel::Reparent(newParent);
723
724 return res;
725 }
726
727 // -----------------------------------------------------------------------
728
729 // Actually shows given page.
730 bool wxPropertyGridManager::DoSelectPage( int index )
731 {
732 // -1 means no page was selected
733 //wxASSERT( m_selPage >= 0 );
734
735 wxCHECK_MSG( index >= -1 && index < (int)GetPageCount(),
736 false,
737 wxT("invalid page index") );
738
739 if ( m_selPage == index )
740 return true;
741
742 if ( m_pPropGrid->GetSelection() )
743 {
744 if ( !m_pPropGrid->ClearSelection() )
745 return false;
746 }
747
748 wxPropertyGridPage* prevPage;
749
750 if ( m_selPage >= 0 )
751 prevPage = GetPage(m_selPage);
752 else
753 prevPage = m_emptyPage;
754
755 wxPropertyGridPage* nextPage;
756
757 if ( index >= 0 )
758 {
759 nextPage = m_arrPages[index];
760
761 nextPage->OnShow();
762 }
763 else
764 {
765 if ( !m_emptyPage )
766 {
767 m_emptyPage = new wxPropertyGridPage();
768 m_emptyPage->m_pPropGrid = m_pPropGrid;
769 }
770
771 nextPage = m_emptyPage;
772 }
773
774 m_iFlags |= wxPG_FL_DESC_REFRESH_REQUIRED;
775
776 m_pPropGrid->SwitchState( nextPage->GetStatePtr() );
777
778 m_pState = m_pPropGrid->m_pState;
779
780 m_selPage = index;
781
782 #if wxUSE_TOOLBAR
783 if ( m_pToolbar )
784 {
785 if ( index >= 0 )
786 m_pToolbar->ToggleTool( nextPage->m_toolId, true );
787 else
788 m_pToolbar->ToggleTool( prevPage->m_toolId, false );
789 }
790 #endif
791
792 #if wxUSE_HEADERCTRL
793 if ( m_showHeader )
794 m_pHeaderCtrl->OnPageChanged(nextPage);
795 #endif
796
797 return true;
798 }
799
800 // -----------------------------------------------------------------------
801
802 // Changes page *and* set the target page for insertion operations.
803 void wxPropertyGridManager::SelectPage( int index )
804 {
805 DoSelectPage(index);
806 }
807
808 // -----------------------------------------------------------------------
809
810 int wxPropertyGridManager::GetPageByName( const wxString& name ) const
811 {
812 size_t i;
813 for ( i=0; i<GetPageCount(); i++ )
814 {
815 if ( m_arrPages[i]->m_label == name )
816 return i;
817 }
818 return wxNOT_FOUND;
819 }
820
821 // -----------------------------------------------------------------------
822
823 int wxPropertyGridManager::GetPageByState( const wxPropertyGridPageState* pState ) const
824 {
825 wxASSERT( pState );
826
827 size_t i;
828 for ( i=0; i<GetPageCount(); i++ )
829 {
830 if ( pState == m_arrPages[i]->GetStatePtr() )
831 return i;
832 }
833
834 return wxNOT_FOUND;
835 }
836
837 // -----------------------------------------------------------------------
838
839 const wxString& wxPropertyGridManager::GetPageName( int index ) const
840 {
841 wxASSERT( index >= 0 && index < (int)GetPageCount() );
842 return m_arrPages[index]->m_label;
843 }
844
845 // -----------------------------------------------------------------------
846
847 wxPropertyGridPageState* wxPropertyGridManager::GetPageState( int page ) const
848 {
849 // Do not change this into wxCHECK because returning NULL is important
850 // for wxPropertyGridInterface page enumeration mechanics.
851 if ( page >= (int)GetPageCount() )
852 return NULL;
853
854 if ( page == -1 )
855 return m_pState;
856 return m_arrPages[page];
857 }
858
859 // -----------------------------------------------------------------------
860
861 void wxPropertyGridManager::Clear()
862 {
863 m_pPropGrid->ClearSelection(false);
864
865 m_pPropGrid->Freeze();
866
867 int i;
868 for ( i=(int)GetPageCount()-1; i>=0; i-- )
869 RemovePage(i);
870
871 m_pPropGrid->Thaw();
872 }
873
874 // -----------------------------------------------------------------------
875
876 void wxPropertyGridManager::ClearPage( int page )
877 {
878 wxASSERT( page >= 0 );
879 wxASSERT( page < (int)GetPageCount() );
880
881 if ( page >= 0 && page < (int)GetPageCount() )
882 {
883 wxPropertyGridPageState* state = m_arrPages[page];
884
885 if ( state == m_pPropGrid->GetState() )
886 m_pPropGrid->Clear();
887 else
888 state->DoClear();
889 }
890 }
891
892 // -----------------------------------------------------------------------
893
894 int wxPropertyGridManager::GetColumnCount( int page ) const
895 {
896 wxASSERT( page >= -1 );
897 wxASSERT( page < (int)GetPageCount() );
898
899 return GetPageState(page)->GetColumnCount();
900 }
901
902 // -----------------------------------------------------------------------
903
904 void wxPropertyGridManager::SetColumnCount( int colCount, int page )
905 {
906 wxASSERT( page >= -1 );
907 wxASSERT( page < (int)GetPageCount() );
908
909 GetPageState(page)->SetColumnCount( colCount );
910 GetGrid()->Refresh();
911
912 #if wxUSE_HEADERCTRL
913 if ( m_showHeader )
914 m_pHeaderCtrl->OnPageUpdated();
915 #endif
916 }
917 // -----------------------------------------------------------------------
918
919 size_t wxPropertyGridManager::GetPageCount() const
920 {
921 if ( !(m_iFlags & wxPG_MAN_FL_PAGE_INSERTED) )
922 return 0;
923
924 return m_arrPages.size();
925 }
926
927 // -----------------------------------------------------------------------
928
929 wxPropertyGridPage* wxPropertyGridManager::InsertPage( int index,
930 const wxString& label,
931 const wxBitmap& bmp,
932 wxPropertyGridPage* pageObj )
933 {
934 if ( index < 0 )
935 index = GetPageCount();
936
937 wxCHECK_MSG( (size_t)index == GetPageCount(), NULL,
938 wxT("wxPropertyGridManager currently only supports appending pages (due to wxToolBar limitation)."));
939
940 bool needInit = true;
941 bool isPageInserted = m_iFlags & wxPG_MAN_FL_PAGE_INSERTED ? true : false;
942
943 wxASSERT( index == 0 || isPageInserted );
944
945 if ( !pageObj )
946 {
947 // No custom page object was given, so we will either re-use the default base
948 // page (if index==0), or create a new default page object.
949 if ( !isPageInserted )
950 {
951 pageObj = GetPage(0);
952 // Of course, if the base page was custom, we need to delete and
953 // re-create it.
954 if ( !pageObj->m_isDefault )
955 {
956 delete pageObj;
957 pageObj = new wxPropertyGridPage();
958 m_arrPages[0] = pageObj;
959 }
960 needInit = false;
961 }
962 else
963 {
964 pageObj = new wxPropertyGridPage();
965 }
966 pageObj->m_isDefault = true;
967 }
968 else
969 {
970 if ( !isPageInserted )
971 {
972 // Initial page needs to be deleted and replaced
973 delete GetPage(0);
974 m_arrPages[0] = pageObj;
975 m_pPropGrid->m_pState = pageObj->GetStatePtr();
976 }
977 }
978
979 wxPropertyGridPageState* state = pageObj->GetStatePtr();
980
981 pageObj->m_manager = this;
982
983 if ( needInit )
984 {
985 state->m_pPropGrid = m_pPropGrid;
986 state->InitNonCatMode();
987 }
988
989 if ( !label.empty() )
990 {
991 wxASSERT_MSG( !pageObj->m_label.length(),
992 wxT("If page label is given in constructor, empty label must be given in AddPage"));
993 pageObj->m_label = label;
994 }
995
996 pageObj->m_toolId = -1;
997
998 if ( !HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
999 pageObj->m_dontCenterSplitter = true;
1000
1001 if ( isPageInserted )
1002 m_arrPages.push_back( pageObj );
1003
1004 #if wxUSE_TOOLBAR
1005 if ( m_windowStyle & wxPG_TOOLBAR )
1006 {
1007 if ( !m_pToolbar )
1008 RecreateControls();
1009
1010 if ( !(GetExtraStyle()&wxPG_EX_HIDE_PAGE_BUTTONS) )
1011 {
1012 wxASSERT( m_pToolbar );
1013
1014 // Add separator before first page.
1015 if ( GetPageCount() < 2 && (GetExtraStyle()&wxPG_EX_MODE_BUTTONS) &&
1016 m_pToolbar->GetToolsCount() < 3 )
1017 m_pToolbar->AddSeparator();
1018
1019 wxToolBarToolBase* tool;
1020
1021 if ( &bmp != &wxNullBitmap )
1022 tool = m_pToolbar->AddTool(wxID_ANY, label, bmp,
1023 label, wxITEM_RADIO);
1024 else
1025 tool = m_pToolbar->AddTool(wxID_ANY, label,
1026 wxBitmap(gs_xpm_defpage),
1027 label, wxITEM_RADIO);
1028
1029 pageObj->m_toolId = tool->GetId();
1030
1031 // Connect to toolbar button events.
1032 Connect(pageObj->m_toolId,
1033 wxEVT_COMMAND_TOOL_CLICKED,
1034 wxCommandEventHandler(
1035 wxPropertyGridManager::OnToolbarClick));
1036
1037 m_pToolbar->Realize();
1038 }
1039 }
1040 #else
1041 wxUnusedVar(bmp);
1042 #endif
1043
1044 // If selected page was above the point of insertion, fix the current page index
1045 if ( isPageInserted )
1046 {
1047 if ( m_selPage >= index )
1048 {
1049 m_selPage += 1;
1050 }
1051 }
1052 else
1053 {
1054 // Set this value only when adding the first page
1055 m_selPage = 0;
1056 }
1057
1058 pageObj->Init();
1059
1060 m_iFlags |= wxPG_MAN_FL_PAGE_INSERTED;
1061
1062 wxASSERT( pageObj->GetGrid() );
1063
1064 return pageObj;
1065 }
1066
1067 // -----------------------------------------------------------------------
1068
1069 bool wxPropertyGridManager::IsAnyModified() const
1070 {
1071 size_t i;
1072 for ( i=0; i<GetPageCount(); i++ )
1073 {
1074 if ( m_arrPages[i]->GetStatePtr()->m_anyModified )
1075 return true;
1076 }
1077 return false;
1078 }
1079
1080 // -----------------------------------------------------------------------
1081
1082 bool wxPropertyGridManager::IsPageModified( size_t index ) const
1083 {
1084 if ( m_arrPages[index]->GetStatePtr()->m_anyModified )
1085 return true;
1086 return false;
1087 }
1088
1089 // -----------------------------------------------------------------------
1090
1091 #if wxUSE_HEADERCTRL
1092 void wxPropertyGridManager::ShowHeader(bool show)
1093 {
1094 if ( show != m_showHeader)
1095 {
1096 m_showHeader = show;
1097 RecreateControls();
1098 }
1099 }
1100 #endif
1101
1102 // -----------------------------------------------------------------------
1103
1104 #if wxUSE_HEADERCTRL
1105 void wxPropertyGridManager::SetColumnTitle( int idx, const wxString& title )
1106 {
1107 if ( !m_pHeaderCtrl )
1108 ShowHeader();
1109
1110 m_pHeaderCtrl->SetColumnTitle(idx, title);
1111 }
1112 #endif
1113
1114 // -----------------------------------------------------------------------
1115
1116 bool wxPropertyGridManager::IsPropertySelected( wxPGPropArg id ) const
1117 {
1118 wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
1119 for ( unsigned int i=0; i<GetPageCount(); i++ )
1120 {
1121 if ( GetPageState(i)->DoIsPropertySelected(p) )
1122 return true;
1123 }
1124 return false;
1125 }
1126
1127 // -----------------------------------------------------------------------
1128
1129 wxPGProperty* wxPropertyGridManager::GetPageRoot( int index ) const
1130 {
1131 wxASSERT( index >= 0 );
1132 wxASSERT( index < (int)m_arrPages.size() );
1133
1134 return m_arrPages[index]->GetStatePtr()->m_properties;
1135 }
1136
1137 // -----------------------------------------------------------------------
1138
1139 bool wxPropertyGridManager::RemovePage( int page )
1140 {
1141 wxCHECK_MSG( (page >= 0) && (page < (int)GetPageCount()),
1142 false,
1143 wxT("invalid page index") );
1144
1145 wxPropertyGridPage* pd = m_arrPages[page];
1146
1147 if ( m_arrPages.size() == 1 )
1148 {
1149 // Last page: do not remove page entry
1150 m_pPropGrid->Clear();
1151 m_selPage = -1;
1152 m_iFlags &= ~wxPG_MAN_FL_PAGE_INSERTED;
1153 pd->m_label.clear();
1154 }
1155
1156 // Change selection if current is page
1157 else if ( page == m_selPage )
1158 {
1159 if ( !m_pPropGrid->ClearSelection() )
1160 return false;
1161
1162 // Substitute page to select
1163 int substitute = page - 1;
1164 if ( substitute < 0 )
1165 substitute = page + 1;
1166
1167 SelectPage(substitute);
1168 }
1169
1170 // Remove toolbar icon
1171 #if wxUSE_TOOLBAR
1172 if ( HasFlag(wxPG_TOOLBAR) )
1173 {
1174 wxASSERT( m_pToolbar );
1175
1176 int toolPos = GetExtraStyle() & wxPG_EX_MODE_BUTTONS ? 3 : 0;
1177 toolPos += page;
1178
1179 // Delete separator as well, for consistency
1180 if ( (GetExtraStyle() & wxPG_EX_MODE_BUTTONS) &&
1181 GetPageCount() == 1 )
1182 m_pToolbar->DeleteToolByPos(2);
1183
1184 m_pToolbar->DeleteToolByPos(toolPos);
1185 }
1186 #endif
1187
1188 if ( m_arrPages.size() > 1 )
1189 {
1190 m_arrPages.erase(m_arrPages.begin() + page);
1191 delete pd;
1192 }
1193
1194 // Adjust indexes that were above removed
1195 if ( m_selPage > page )
1196 m_selPage--;
1197
1198 return true;
1199 }
1200
1201 // -----------------------------------------------------------------------
1202
1203 bool wxPropertyGridManager::ProcessEvent( wxEvent& event )
1204 {
1205 int evtType = event.GetEventType();
1206
1207 // NB: For some reason, under wxPython, Connect in Init doesn't work properly,
1208 // so we'll need to call OnPropertyGridSelect manually. Multiple call's
1209 // don't really matter.
1210 if ( evtType == wxEVT_PG_SELECTED )
1211 OnPropertyGridSelect((wxPropertyGridEvent&)event);
1212
1213 // Property grid events get special attention
1214 if ( evtType >= wxPG_BASE_EVT_TYPE &&
1215 evtType < (wxPG_MAX_EVT_TYPE) &&
1216 m_selPage >= 0 )
1217 {
1218 wxPropertyGridPage* page = GetPage(m_selPage);
1219 wxPropertyGridEvent* pgEvent = wxDynamicCast(&event, wxPropertyGridEvent);
1220
1221 // Add property grid events to appropriate custom pages
1222 // but stop propagating to parent if page says it is
1223 // handling everything.
1224 if ( pgEvent && !page->m_isDefault )
1225 {
1226 /*if ( pgEvent->IsPending() )
1227 page->AddPendingEvent(event);
1228 else*/
1229 page->ProcessEvent(event);
1230
1231 if ( page->IsHandlingAllEvents() )
1232 event.StopPropagation();
1233 }
1234 }
1235
1236 return wxPanel::ProcessEvent(event);
1237 }
1238
1239 // -----------------------------------------------------------------------
1240
1241 void wxPropertyGridManager::RepaintDescBoxDecorations( wxDC& dc,
1242 int newSplitterY,
1243 int newWidth,
1244 int newHeight )
1245 {
1246 // Draw background
1247 wxColour bgcol = GetBackgroundColour();
1248 dc.SetBrush(bgcol);
1249 dc.SetPen(bgcol);
1250 int rectHeight = m_splitterHeight;
1251 dc.DrawRectangle(0, newSplitterY, newWidth, rectHeight);
1252 dc.SetPen( wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW) );
1253 int splitterBottom = newSplitterY + m_splitterHeight - 1;
1254 int boxHeight = newHeight - splitterBottom;
1255 if ( boxHeight > 1 )
1256 dc.DrawRectangle(0, splitterBottom, newWidth, boxHeight);
1257 else
1258 dc.DrawLine(0, splitterBottom, newWidth, splitterBottom);
1259 }
1260
1261 // -----------------------------------------------------------------------
1262
1263 void wxPropertyGridManager::UpdateDescriptionBox( int new_splittery, int new_width, int new_height )
1264 {
1265 int use_hei = new_height;
1266 use_hei--;
1267
1268 // Fix help control positions.
1269 int cap_hei = m_pPropGrid->m_fontHeight;
1270 int cap_y = new_splittery+m_splitterHeight+5;
1271 int cnt_y = cap_y+cap_hei+3;
1272 int sub_cap_hei = cap_y+cap_hei-use_hei;
1273 int cnt_hei = use_hei-cnt_y;
1274 if ( sub_cap_hei > 0 )
1275 {
1276 cap_hei -= sub_cap_hei;
1277 cnt_hei = 0;
1278 }
1279 if ( cap_hei <= 2 )
1280 {
1281 m_pTxtHelpCaption->Show( false );
1282 m_pTxtHelpContent->Show( false );
1283 }
1284 else
1285 {
1286 m_pTxtHelpCaption->SetSize(3,cap_y,new_width-6,cap_hei);
1287 m_pTxtHelpCaption->Wrap(-1);
1288 m_pTxtHelpCaption->Show( true );
1289 if ( cnt_hei <= 2 )
1290 {
1291 m_pTxtHelpContent->Show( false );
1292 }
1293 else
1294 {
1295 m_pTxtHelpContent->SetSize(3,cnt_y,new_width-6,cnt_hei);
1296 m_pTxtHelpContent->Show( true );
1297 }
1298 }
1299
1300 wxRect r(0, new_splittery, new_width, new_height-new_splittery);
1301 RefreshRect(r);
1302
1303 m_splitterY = new_splittery;
1304
1305 m_iFlags &= ~(wxPG_FL_DESC_REFRESH_REQUIRED);
1306 }
1307
1308 // -----------------------------------------------------------------------
1309
1310 void wxPropertyGridManager::RecalculatePositions( int width, int height )
1311 {
1312 int propgridY = 0;
1313 int propgridBottomY = height;
1314
1315 // Toolbar at the top.
1316 #if wxUSE_TOOLBAR
1317 if ( m_pToolbar )
1318 {
1319 m_pToolbar->SetSize(0, 0, width, -1);
1320 propgridY += m_pToolbar->GetSize().y;
1321
1322 if (GetExtraStyle() & wxPG_EX_TOOLBAR_SEPARATOR)
1323 propgridY += 1;
1324 }
1325 #endif
1326
1327 // Header comes after the tool bar
1328 #if wxUSE_HEADERCTRL
1329 if ( m_showHeader )
1330 {
1331 m_pHeaderCtrl->SetSize(0, propgridY, width, -1);
1332 propgridY += m_pHeaderCtrl->GetSize().y;
1333 }
1334 #endif
1335
1336 // Help box.
1337 if ( m_pTxtHelpCaption )
1338 {
1339 int new_splittery = m_splitterY;
1340
1341 // Move m_splitterY
1342 if ( ( m_splitterY >= 0 || m_nextDescBoxSize ) && m_height > 32 )
1343 {
1344 if ( m_nextDescBoxSize >= 0 )
1345 {
1346 new_splittery = m_height - m_nextDescBoxSize - m_splitterHeight;
1347 m_nextDescBoxSize = -1;
1348 }
1349 new_splittery += (height-m_height);
1350 }
1351 else
1352 {
1353 new_splittery = height - wxPGMAN_DEFAULT_NEGATIVE_SPLITTER_Y;
1354 if ( new_splittery < 32 )
1355 new_splittery = 32;
1356 }
1357
1358 // Check if beyond minimum.
1359 int nspy_min = propgridY + m_pPropGrid->m_lineHeight;
1360 if ( new_splittery < nspy_min )
1361 new_splittery = nspy_min;
1362
1363 propgridBottomY = new_splittery;
1364
1365 UpdateDescriptionBox( new_splittery, width, height );
1366 }
1367
1368 if ( m_iFlags & wxPG_FL_INITIALIZED )
1369 {
1370 int pgh = propgridBottomY - propgridY;
1371 if ( pgh < 0 )
1372 pgh = 0;
1373 m_pPropGrid->SetSize( 0, propgridY, width, pgh );
1374
1375 m_extraHeight = height - pgh;
1376
1377 m_width = width;
1378 m_height = height;
1379 }
1380 }
1381
1382 // -----------------------------------------------------------------------
1383
1384 void wxPropertyGridManager::SetDescBoxHeight( int ht, bool refresh )
1385 {
1386 if ( m_windowStyle & wxPG_DESCRIPTION )
1387 {
1388 if ( ht != GetDescBoxHeight() )
1389 {
1390 m_nextDescBoxSize = ht;
1391 if ( refresh )
1392 RecalculatePositions(m_width, m_height);
1393 }
1394 }
1395 }
1396
1397 // -----------------------------------------------------------------------
1398
1399 int wxPropertyGridManager::GetDescBoxHeight() const
1400 {
1401 return GetClientSize().y - m_splitterY - m_splitterHeight;
1402 }
1403
1404 // -----------------------------------------------------------------------
1405
1406 void wxPropertyGridManager::OnPaint( wxPaintEvent& WXUNUSED(event) )
1407 {
1408 wxPaintDC dc(this);
1409
1410 // Update everything inside the box
1411 wxRect r = GetUpdateRegion().GetBox();
1412
1413 if (GetExtraStyle() & wxPG_EX_TOOLBAR_SEPARATOR)
1414 {
1415 if (m_pToolbar && m_pPropGrid)
1416 {
1417 wxPen marginPen(m_pPropGrid->GetMarginColour());
1418 dc.SetPen(marginPen);
1419
1420 int y = m_pPropGrid->GetPosition().y-1;
1421 dc.DrawLine(0, y, GetClientSize().x, y);
1422 }
1423 }
1424
1425 // Repaint splitter and any other description box decorations
1426 if ( (r.y + r.height) >= m_splitterY && m_splitterY != -1)
1427 RepaintDescBoxDecorations( dc, m_splitterY, m_width, m_height );
1428 }
1429
1430 // -----------------------------------------------------------------------
1431
1432 void wxPropertyGridManager::Refresh(bool eraseBackground, const wxRect* rect )
1433 {
1434 m_pPropGrid->Refresh(eraseBackground);
1435 wxWindow::Refresh(eraseBackground,rect);
1436 }
1437
1438 // -----------------------------------------------------------------------
1439
1440 void wxPropertyGridManager::RefreshProperty( wxPGProperty* p )
1441 {
1442 wxPropertyGrid* grid = p->GetGrid();
1443
1444 if ( GetPage(m_selPage)->GetStatePtr() == p->GetParent()->GetParentState() )
1445 grid->RefreshProperty(p);
1446 }
1447
1448 // -----------------------------------------------------------------------
1449
1450 void wxPropertyGridManager::RecreateControls()
1451 {
1452
1453 bool was_shown = IsShown();
1454 if ( was_shown )
1455 Show ( false );
1456
1457 #if wxUSE_TOOLBAR
1458 if ( m_windowStyle & wxPG_TOOLBAR )
1459 {
1460 // Has toolbar.
1461 if ( !m_pToolbar )
1462 {
1463 long toolBarFlags = ((GetExtraStyle()&wxPG_EX_NO_FLAT_TOOLBAR)?0:wxTB_FLAT);
1464 if (GetExtraStyle() & wxPG_EX_NO_TOOLBAR_DIVIDER)
1465 toolBarFlags |= wxTB_NODIVIDER;
1466
1467 m_pToolbar = new wxToolBar(this, wxID_ANY,
1468 wxDefaultPosition,
1469 wxDefaultSize,
1470 toolBarFlags);
1471 m_pToolbar->SetToolBitmapSize(wxSize(16, 15));
1472
1473 #if defined(__WXMSW__)
1474 // Eliminate toolbar flicker on XP
1475 // NOTE: Not enabled since it corrupts drawing somewhat.
1476
1477 /*
1478 #ifndef WS_EX_COMPOSITED
1479 #define WS_EX_COMPOSITED 0x02000000L
1480 #endif
1481
1482 HWND hWnd = (HWND)m_pToolbar->GetHWND();
1483
1484 ::SetWindowLong( hWnd, GWL_EXSTYLE,
1485 ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED );
1486 */
1487
1488 #endif
1489
1490 m_pToolbar->SetCursor ( *wxSTANDARD_CURSOR );
1491
1492 if ( (GetExtraStyle()&wxPG_EX_MODE_BUTTONS) )
1493 {
1494 wxString desc1(_("Categorized Mode"));
1495 wxString desc2(_("Alphabetic Mode"));
1496
1497 wxToolBarToolBase* tool;
1498
1499 tool = m_pToolbar->AddTool(wxID_ANY,
1500 desc1,
1501 wxBitmap(gs_xpm_catmode),
1502 desc1,
1503 wxITEM_RADIO);
1504 m_categorizedModeToolId = tool->GetId();
1505
1506 tool = m_pToolbar->AddTool(wxID_ANY,
1507 desc2,
1508 wxBitmap(gs_xpm_noncatmode),
1509 desc2,
1510 wxITEM_RADIO);
1511 m_alphabeticModeToolId = tool->GetId();
1512
1513 m_pToolbar->Realize();
1514
1515 Connect(m_categorizedModeToolId,
1516 wxEVT_COMMAND_TOOL_CLICKED,
1517 wxCommandEventHandler(
1518 wxPropertyGridManager::OnToolbarClick));
1519 Connect(m_alphabeticModeToolId,
1520 wxEVT_COMMAND_TOOL_CLICKED,
1521 wxCommandEventHandler(
1522 wxPropertyGridManager::OnToolbarClick));
1523 }
1524 else
1525 {
1526 m_categorizedModeToolId = -1;
1527 m_alphabeticModeToolId = -1;
1528 }
1529
1530 }
1531
1532 if ( (GetExtraStyle() & wxPG_EX_MODE_BUTTONS) )
1533 {
1534 // Toggle correct mode button.
1535 // TODO: This doesn't work in wxMSW (when changing,
1536 // both items will get toggled).
1537 int toggle_but_on_ind;
1538 int toggle_but_off_ind;
1539 if ( m_pPropGrid->m_pState->IsInNonCatMode() )
1540 {
1541 toggle_but_on_ind = m_alphabeticModeToolId;
1542 toggle_but_off_ind = m_categorizedModeToolId;
1543 }
1544 else
1545 {
1546 toggle_but_on_ind = m_categorizedModeToolId;
1547 toggle_but_off_ind = m_alphabeticModeToolId;
1548 }
1549
1550 m_pToolbar->ToggleTool(toggle_but_on_ind, true);
1551 m_pToolbar->ToggleTool(toggle_but_off_ind, false);
1552 }
1553
1554 }
1555 else
1556 {
1557 // No toolbar.
1558 if ( m_pToolbar )
1559 m_pToolbar->Destroy();
1560 m_pToolbar = NULL;
1561 }
1562 #endif
1563
1564 #if wxUSE_HEADERCTRL
1565 if ( m_showHeader )
1566 {
1567 wxPGHeaderCtrl* hc;
1568
1569 if ( !m_pHeaderCtrl )
1570 {
1571 hc = new wxPGHeaderCtrl(this);
1572 hc->Create(this, wxID_ANY);
1573 m_pHeaderCtrl = hc;
1574 }
1575 else
1576 {
1577 m_pHeaderCtrl->Show();
1578 }
1579
1580 m_pHeaderCtrl->OnPageChanged(GetCurrentPage());
1581 }
1582 else
1583 {
1584 if ( m_pHeaderCtrl )
1585 m_pHeaderCtrl->Hide();
1586 }
1587 #endif
1588
1589 if ( m_windowStyle & wxPG_DESCRIPTION )
1590 {
1591 // Has help box.
1592 m_pPropGrid->m_iFlags |= (wxPG_FL_NOSTATUSBARHELP);
1593
1594 if ( !m_pTxtHelpCaption )
1595 {
1596 m_pTxtHelpCaption = new wxStaticText(this,
1597 wxID_ANY,
1598 wxT(""),
1599 wxDefaultPosition,
1600 wxDefaultSize,
1601 wxALIGN_LEFT|wxST_NO_AUTORESIZE);
1602 m_pTxtHelpCaption->SetFont( m_pPropGrid->m_captionFont );
1603 m_pTxtHelpCaption->SetCursor( *wxSTANDARD_CURSOR );
1604 }
1605 if ( !m_pTxtHelpContent )
1606 {
1607 m_pTxtHelpContent = new wxStaticText(this,
1608 wxID_ANY,
1609 wxT(""),
1610 wxDefaultPosition,
1611 wxDefaultSize,
1612 wxALIGN_LEFT|wxST_NO_AUTORESIZE);
1613 m_pTxtHelpContent->SetCursor( *wxSTANDARD_CURSOR );
1614 }
1615
1616 SetDescribedProperty(GetSelection());
1617 }
1618 else
1619 {
1620 // No help box.
1621 m_pPropGrid->m_iFlags &= ~(wxPG_FL_NOSTATUSBARHELP);
1622
1623 if ( m_pTxtHelpCaption )
1624 m_pTxtHelpCaption->Destroy();
1625
1626 m_pTxtHelpCaption = NULL;
1627
1628 if ( m_pTxtHelpContent )
1629 m_pTxtHelpContent->Destroy();
1630
1631 m_pTxtHelpContent = NULL;
1632 }
1633
1634 int width, height;
1635
1636 GetClientSize(&width,&height);
1637
1638 RecalculatePositions(width,height);
1639
1640 if ( was_shown )
1641 Show ( true );
1642 }
1643
1644 // -----------------------------------------------------------------------
1645
1646 wxPGProperty* wxPropertyGridManager::DoGetPropertyByName( const wxString& name ) const
1647 {
1648 size_t i;
1649 for ( i=0; i<GetPageCount(); i++ )
1650 {
1651 wxPropertyGridPageState* pState = m_arrPages[i]->GetStatePtr();
1652 wxPGProperty* p = pState->BaseGetPropertyByName(name);
1653 if ( p )
1654 {
1655 return p;
1656 }
1657 }
1658 return NULL;
1659 }
1660
1661 // -----------------------------------------------------------------------
1662
1663 bool wxPropertyGridManager::EnsureVisible( wxPGPropArg id )
1664 {
1665 wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
1666
1667 wxPropertyGridPageState* parentState = p->GetParentState();
1668
1669 // Select correct page.
1670 if ( m_pPropGrid->m_pState != parentState )
1671 DoSelectPage( GetPageByState(parentState) );
1672
1673 return m_pPropGrid->EnsureVisible(id);
1674 }
1675
1676 // -----------------------------------------------------------------------
1677
1678 void wxPropertyGridManager::OnToolbarClick( wxCommandEvent &event )
1679 {
1680 int id = event.GetId();
1681
1682 if ( id == m_categorizedModeToolId )
1683 {
1684 // Categorized mode.
1685 if ( m_pPropGrid->m_windowStyle & wxPG_HIDE_CATEGORIES )
1686 {
1687 if ( !m_pPropGrid->HasInternalFlag(wxPG_FL_CATMODE_AUTO_SORT) )
1688 m_pPropGrid->m_windowStyle &= ~wxPG_AUTO_SORT;
1689 m_pPropGrid->EnableCategories( true );
1690 }
1691 }
1692 else if ( id == m_alphabeticModeToolId )
1693 {
1694 // Alphabetic mode.
1695 if ( !(m_pPropGrid->m_windowStyle & wxPG_HIDE_CATEGORIES) )
1696 {
1697 if ( m_pPropGrid->HasFlag(wxPG_AUTO_SORT) )
1698 m_pPropGrid->SetInternalFlag(wxPG_FL_CATMODE_AUTO_SORT);
1699 else
1700 m_pPropGrid->ClearInternalFlag(wxPG_FL_CATMODE_AUTO_SORT);
1701
1702 m_pPropGrid->m_windowStyle |= wxPG_AUTO_SORT;
1703 m_pPropGrid->EnableCategories( false );
1704 }
1705 }
1706 else
1707 {
1708 // Page Switching.
1709
1710 int index = -1;
1711 size_t i;
1712 wxPropertyGridPage* pdc;
1713
1714 // Find page with given id.
1715 for ( i=0; i<GetPageCount(); i++ )
1716 {
1717 pdc = m_arrPages[i];
1718 if ( pdc->m_toolId == id )
1719 {
1720 index = i;
1721 break;
1722 }
1723 }
1724
1725 wxASSERT( index >= 0 );
1726
1727 if ( DoSelectPage( index ) )
1728 {
1729 // Event dispatching must be last.
1730 m_pPropGrid->SendEvent( wxEVT_PG_PAGE_CHANGED, NULL );
1731 }
1732 else
1733 {
1734 // TODO: Depress the old button on toolbar.
1735 }
1736 }
1737 }
1738
1739 // -----------------------------------------------------------------------
1740
1741 bool wxPropertyGridManager::SetEditableStateItem( const wxString& name, wxVariant value )
1742 {
1743 if ( name == wxS("descboxheight") )
1744 {
1745 SetDescBoxHeight(value.GetLong(), true);
1746 return true;
1747 }
1748 return false;
1749 }
1750
1751 // -----------------------------------------------------------------------
1752
1753 wxVariant wxPropertyGridManager::GetEditableStateItem( const wxString& name ) const
1754 {
1755 if ( name == wxS("descboxheight") )
1756 {
1757 return (long) GetDescBoxHeight();
1758 }
1759 return wxNullVariant;
1760 }
1761
1762 // -----------------------------------------------------------------------
1763
1764 void wxPropertyGridManager::SetDescription( const wxString& label, const wxString& content )
1765 {
1766 if ( m_pTxtHelpCaption )
1767 {
1768 wxSize osz1 = m_pTxtHelpCaption->GetSize();
1769 wxSize osz2 = m_pTxtHelpContent->GetSize();
1770
1771 m_pTxtHelpCaption->SetLabel(label);
1772 m_pTxtHelpContent->SetLabel(content);
1773
1774 m_pTxtHelpCaption->SetSize(-1,osz1.y);
1775 m_pTxtHelpContent->SetSize(-1,osz2.y);
1776
1777 UpdateDescriptionBox( m_splitterY, m_width, m_height );
1778 }
1779 }
1780
1781 // -----------------------------------------------------------------------
1782
1783 void wxPropertyGridManager::SetDescribedProperty( wxPGProperty* p )
1784 {
1785 if ( m_pTxtHelpCaption )
1786 {
1787 if ( p )
1788 {
1789 SetDescription( p->GetLabel(), p->GetHelpString() );
1790 }
1791 else
1792 {
1793 SetDescription( wxEmptyString, wxEmptyString );
1794 }
1795 }
1796 }
1797
1798 // -----------------------------------------------------------------------
1799
1800 void wxPropertyGridManager::SetSplitterLeft( bool subProps, bool allPages )
1801 {
1802 if ( !allPages )
1803 {
1804 m_pPropGrid->SetSplitterLeft(subProps);
1805 }
1806 else
1807 {
1808 wxClientDC dc(this);
1809 dc.SetFont(m_pPropGrid->GetFont());
1810
1811 int highest = 0;
1812 unsigned int i;
1813
1814 for ( i=0; i<GetPageCount(); i++ )
1815 {
1816 int maxW = m_pState->GetColumnFitWidth(dc, m_arrPages[i]->m_properties, 0, subProps );
1817 maxW += m_pPropGrid->m_marginWidth;
1818 if ( maxW > highest )
1819 highest = maxW;
1820 m_pState->m_dontCenterSplitter = true;
1821 }
1822
1823 if ( highest > 0 )
1824 SetSplitterPosition( highest );
1825 }
1826
1827 #if wxUSE_HEADERCTRL
1828 if ( m_showHeader )
1829 m_pHeaderCtrl->OnColumWidthsChanged();
1830 #endif
1831 }
1832
1833 void wxPropertyGridManager::SetPageSplitterLeft(int page, bool subProps)
1834 {
1835 wxASSERT_MSG( (page < (int) GetPageCount()),
1836 wxT("SetPageSplitterLeft() has no effect until pages have been added") );
1837
1838 if (page < (int) GetPageCount())
1839 {
1840 wxClientDC dc(this);
1841 dc.SetFont(m_pPropGrid->GetFont());
1842
1843 int maxW = m_pState->GetColumnFitWidth(dc, m_arrPages[page]->m_properties, 0, subProps );
1844 maxW += m_pPropGrid->m_marginWidth;
1845 SetPageSplitterPosition( page, maxW );
1846
1847 #if wxUSE_HEADERCTRL
1848 if ( m_showHeader )
1849 m_pHeaderCtrl->OnColumWidthsChanged();
1850 #endif
1851 }
1852 }
1853
1854 // -----------------------------------------------------------------------
1855
1856 void wxPropertyGridManager::OnPropertyGridSelect( wxPropertyGridEvent& event )
1857 {
1858 // Check id.
1859 wxASSERT_MSG( GetId() == m_pPropGrid->GetId(),
1860 wxT("wxPropertyGridManager id must be set with wxPropertyGridManager::SetId (not wxWindow::SetId).") );
1861
1862 SetDescribedProperty(event.GetProperty());
1863 event.Skip();
1864 }
1865
1866 // -----------------------------------------------------------------------
1867
1868 void
1869 wxPropertyGridManager::OnPGColDrag( wxPropertyGridEvent& WXUNUSED(event) )
1870 {
1871 #if wxUSE_HEADERCTRL
1872 if ( !m_showHeader )
1873 return;
1874
1875 m_pHeaderCtrl->OnColumWidthsChanged();
1876 #endif
1877 }
1878
1879 // -----------------------------------------------------------------------
1880
1881 void wxPropertyGridManager::OnResize( wxSizeEvent& WXUNUSED(event) )
1882 {
1883 int width, height;
1884
1885 GetClientSize(&width, &height);
1886
1887 if ( m_width == -12345 )
1888 RecreateControls();
1889
1890 RecalculatePositions(width, height);
1891
1892 if ( m_pPropGrid && m_pPropGrid->m_parent )
1893 {
1894 int pgWidth, pgHeight;
1895 m_pPropGrid->GetClientSize(&pgWidth, &pgHeight);
1896
1897 // Regenerate splitter positions for non-current pages
1898 for ( unsigned int i=0; i<GetPageCount(); i++ )
1899 {
1900 wxPropertyGridPage* page = GetPage(i);
1901 if ( page != m_pPropGrid->GetState() )
1902 {
1903 page->OnClientWidthChange(pgWidth,
1904 pgWidth - page->m_width,
1905 true);
1906 }
1907 }
1908 }
1909 }
1910
1911 // -----------------------------------------------------------------------
1912
1913 void wxPropertyGridManager::OnMouseEntry( wxMouseEvent& WXUNUSED(event) )
1914 {
1915 // Correct cursor. This is required atleast for wxGTK, for which
1916 // setting button's cursor to *wxSTANDARD_CURSOR does not work.
1917 SetCursor( wxNullCursor );
1918 m_onSplitter = 0;
1919 }
1920
1921 // -----------------------------------------------------------------------
1922
1923 void wxPropertyGridManager::OnMouseMove( wxMouseEvent &event )
1924 {
1925 if ( !m_pTxtHelpCaption )
1926 return;
1927
1928 int y = event.m_y;
1929
1930 if ( m_dragStatus > 0 )
1931 {
1932 int sy = y - m_dragOffset;
1933
1934 // Calculate drag limits
1935 int bottom_limit = m_height - m_splitterHeight + 1;
1936 int top_limit = m_pPropGrid->m_lineHeight;
1937 #if wxUSE_TOOLBAR
1938 if ( m_pToolbar ) top_limit += m_pToolbar->GetSize().y;
1939 #endif
1940
1941 if ( sy >= top_limit && sy < bottom_limit )
1942 {
1943
1944 int change = sy - m_splitterY;
1945 if ( change )
1946 {
1947 m_splitterY = sy;
1948
1949 m_pPropGrid->SetSize( m_width, m_splitterY - m_pPropGrid->GetPosition().y );
1950 UpdateDescriptionBox( m_splitterY, m_width, m_height );
1951
1952 m_extraHeight -= change;
1953 InvalidateBestSize();
1954 }
1955
1956 }
1957
1958 }
1959 else
1960 {
1961 if ( y >= m_splitterY && y < (m_splitterY+m_splitterHeight+2) )
1962 {
1963 SetCursor ( m_cursorSizeNS );
1964 m_onSplitter = 1;
1965 }
1966 else
1967 {
1968 if ( m_onSplitter )
1969 {
1970 SetCursor ( wxNullCursor );
1971 }
1972 m_onSplitter = 0;
1973 }
1974 }
1975 }
1976
1977 // -----------------------------------------------------------------------
1978
1979 void wxPropertyGridManager::OnMouseClick( wxMouseEvent &event )
1980 {
1981 int y = event.m_y;
1982
1983 // Click on splitter.
1984 if ( y >= m_splitterY && y < (m_splitterY+m_splitterHeight+2) )
1985 {
1986 if ( m_dragStatus == 0 )
1987 {
1988 //
1989 // Begin draggin the splitter
1990 //
1991
1992 BEGIN_MOUSE_CAPTURE
1993
1994 m_dragStatus = 1;
1995
1996 m_dragOffset = y - m_splitterY;
1997
1998 }
1999 }
2000 }
2001
2002 // -----------------------------------------------------------------------
2003
2004 void wxPropertyGridManager::OnMouseUp( wxMouseEvent &event )
2005 {
2006 // No event type check - basicly calling this method should
2007 // just stop dragging.
2008
2009 if ( m_dragStatus >= 1 )
2010 {
2011 //
2012 // End Splitter Dragging
2013 //
2014
2015 int y = event.m_y;
2016
2017 // DO NOT ENABLE FOLLOWING LINE!
2018 // (it is only here as a reminder to not to do it)
2019 //m_splitterY = y;
2020
2021 // This is necessary to return cursor
2022 END_MOUSE_CAPTURE
2023
2024 // Set back the default cursor, if necessary
2025 if ( y < m_splitterY || y >= (m_splitterY+m_splitterHeight+2) )
2026 {
2027 SetCursor ( wxNullCursor );
2028 }
2029
2030 m_dragStatus = 0;
2031 }
2032 }
2033
2034 // -----------------------------------------------------------------------
2035
2036 void wxPropertyGridManager::SetSplitterPosition( int pos, int splitterColumn )
2037 {
2038 wxASSERT_MSG( GetPageCount(),
2039 wxT("SetSplitterPosition() has no effect until pages have been added") );
2040
2041 size_t i;
2042 for ( i=0; i<GetPageCount(); i++ )
2043 {
2044 wxPropertyGridPage* page = GetPage(i);
2045 page->DoSetSplitterPosition( pos, splitterColumn,
2046 wxPG_SPLITTER_REFRESH );
2047 }
2048
2049 #if wxUSE_HEADERCTRL
2050 if ( m_showHeader )
2051 m_pHeaderCtrl->OnColumWidthsChanged();
2052 #endif
2053 }
2054
2055 // -----------------------------------------------------------------------
2056
2057 void wxPropertyGridManager::SetPageSplitterPosition( int page,
2058 int pos,
2059 int column )
2060 {
2061 GetPage(page)->DoSetSplitterPosition( pos, column );
2062
2063 #if wxUSE_HEADERCTRL
2064 if ( m_showHeader )
2065 m_pHeaderCtrl->OnColumWidthsChanged();
2066 #endif
2067 }
2068
2069 // -----------------------------------------------------------------------
2070 // wxPGVIterator_Manager
2071 // -----------------------------------------------------------------------
2072
2073 // Default returned by wxPropertyGridInterface::CreateVIterator().
2074 class wxPGVIteratorBase_Manager : public wxPGVIteratorBase
2075 {
2076 public:
2077 wxPGVIteratorBase_Manager( wxPropertyGridManager* manager, int flags )
2078 : m_manager(manager), m_flags(flags), m_curPage(0)
2079 {
2080 m_it.Init(manager->GetPage(0), flags);
2081 }
2082 virtual ~wxPGVIteratorBase_Manager() { }
2083 virtual void Next()
2084 {
2085 m_it.Next();
2086
2087 // Next page?
2088 if ( m_it.AtEnd() )
2089 {
2090 m_curPage++;
2091 if ( m_curPage < m_manager->GetPageCount() )
2092 m_it.Init( m_manager->GetPage(m_curPage), m_flags );
2093 }
2094 }
2095 private:
2096 wxPropertyGridManager* m_manager;
2097 int m_flags;
2098 unsigned int m_curPage;
2099 };
2100
2101 wxPGVIterator wxPropertyGridManager::GetVIterator( int flags ) const
2102 {
2103 return wxPGVIterator( new wxPGVIteratorBase_Manager( (wxPropertyGridManager*)this, flags ) );
2104 }
2105
2106 #endif // wxUSE_PROPGRID