Fix crash when auto-sizing a wxDataViewCtrl column.
[wxWidgets.git] / samples / splitter / splitter.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: splitter.cpp
3 // Purpose: wxSplitterWindow sample
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 04/01/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx/wx.h".
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #include "wx/log.h"
28
29 #include "wx/app.h"
30 #include "wx/frame.h"
31
32 #include "wx/scrolwin.h"
33 #include "wx/menu.h"
34
35 #include "wx/textdlg.h" // for wxGetTextFromUser
36 #endif
37
38 #include "wx/splitter.h"
39 #include "wx/dcmirror.h"
40
41 #ifndef wxHAS_IMAGES_IN_RESOURCES
42 #include "../sample.xpm"
43 #endif
44
45 // ----------------------------------------------------------------------------
46 // constants
47 // ----------------------------------------------------------------------------
48
49 // ID for the menu commands
50 enum
51 {
52 SPLIT_QUIT = 1,
53 SPLIT_HORIZONTAL,
54 SPLIT_VERTICAL,
55 SPLIT_UNSPLIT,
56 SPLIT_LIVE,
57 SPLIT_BORDER,
58 SPLIT_3DSASH,
59 SPLIT_SETPOSITION,
60 SPLIT_SETMINSIZE,
61 SPLIT_SETGRAVITY,
62 SPLIT_REPLACE,
63 SPLIT_INVISIBLE
64 };
65
66 // ----------------------------------------------------------------------------
67 // our classes
68 // ----------------------------------------------------------------------------
69
70 class MyApp: public wxApp
71 {
72 public:
73 MyApp() { }
74
75 virtual bool OnInit();
76
77 wxDECLARE_NO_COPY_CLASS(MyApp);
78 };
79
80 class MyFrame: public wxFrame
81 {
82 public:
83 MyFrame();
84 virtual ~MyFrame();
85
86 void ToggleFlag(int flag, bool enable);
87
88 // Menu commands
89 void OnSplitHorizontal(wxCommandEvent& event);
90 void OnSplitVertical(wxCommandEvent& event);
91 void OnUnsplit(wxCommandEvent& event);
92 void OnToggleLive(wxCommandEvent& event)
93 { ToggleFlag(wxSP_LIVE_UPDATE, event.IsChecked()); }
94 void OnToggleBorder(wxCommandEvent& event)
95 { ToggleFlag(wxSP_BORDER, event.IsChecked()); }
96 void OnToggle3DSash(wxCommandEvent& event)
97 { ToggleFlag(wxSP_3DSASH, event.IsChecked()); }
98 void OnSetPosition(wxCommandEvent& event);
99 void OnSetMinSize(wxCommandEvent& event);
100 void OnSetGravity(wxCommandEvent& event);
101 void OnReplace(wxCommandEvent &event);
102 void OnToggleInvisible(wxCommandEvent &event);
103
104 void OnQuit(wxCommandEvent& event);
105
106 // Menu command update functions
107 void OnUpdateUIHorizontal(wxUpdateUIEvent& event);
108 void OnUpdateUIVertical(wxUpdateUIEvent& event);
109 void OnUpdateUIUnsplit(wxUpdateUIEvent& event);
110 void OnUpdateUIInvisible(wxUpdateUIEvent& event);
111
112 private:
113 wxWindow *m_left, *m_right;
114
115 wxSplitterWindow* m_splitter;
116 wxWindow *m_replacewindow;
117
118 DECLARE_EVENT_TABLE()
119 wxDECLARE_NO_COPY_CLASS(MyFrame);
120 };
121
122 class MySplitterWindow : public wxSplitterWindow
123 {
124 public:
125 MySplitterWindow(wxFrame *parent);
126
127 // event handlers
128 void OnPositionChanged(wxSplitterEvent& event);
129 void OnPositionChanging(wxSplitterEvent& event);
130 void OnDClick(wxSplitterEvent& event);
131 void OnUnsplitEvent(wxSplitterEvent& event);
132
133 private:
134 wxFrame *m_frame;
135
136 DECLARE_EVENT_TABLE()
137 wxDECLARE_NO_COPY_CLASS(MySplitterWindow);
138 };
139
140 class MyCanvas: public wxScrolledWindow
141 {
142 public:
143 MyCanvas(wxWindow* parent, bool mirror);
144 virtual ~MyCanvas(){};
145
146 virtual void OnDraw(wxDC& dc);
147
148 private:
149 bool m_mirror;
150
151 wxDECLARE_NO_COPY_CLASS(MyCanvas);
152 };
153
154 // ============================================================================
155 // implementation
156 // ============================================================================
157
158 // ----------------------------------------------------------------------------
159 // MyApp
160 // ----------------------------------------------------------------------------
161
162 IMPLEMENT_APP(MyApp)
163
164 bool MyApp::OnInit()
165 {
166 if ( !wxApp::OnInit() )
167 return false;
168
169 // create and show the main frame
170 MyFrame* frame = new MyFrame;
171
172 frame->Show(true);
173
174 return true;
175 }
176
177 // ----------------------------------------------------------------------------
178 // MyFrame
179 // ----------------------------------------------------------------------------
180
181 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
182 EVT_MENU(SPLIT_VERTICAL, MyFrame::OnSplitVertical)
183 EVT_MENU(SPLIT_HORIZONTAL, MyFrame::OnSplitHorizontal)
184 EVT_MENU(SPLIT_UNSPLIT, MyFrame::OnUnsplit)
185 EVT_MENU(SPLIT_LIVE, MyFrame::OnToggleLive)
186 EVT_MENU(SPLIT_BORDER, MyFrame::OnToggleBorder)
187 EVT_MENU(SPLIT_3DSASH, MyFrame::OnToggle3DSash)
188 EVT_MENU(SPLIT_SETPOSITION, MyFrame::OnSetPosition)
189 EVT_MENU(SPLIT_SETMINSIZE, MyFrame::OnSetMinSize)
190 EVT_MENU(SPLIT_SETGRAVITY, MyFrame::OnSetGravity)
191 EVT_MENU(SPLIT_REPLACE, MyFrame::OnReplace)
192 EVT_MENU(SPLIT_INVISIBLE, MyFrame::OnToggleInvisible)
193
194 EVT_MENU(SPLIT_QUIT, MyFrame::OnQuit)
195
196 EVT_UPDATE_UI(SPLIT_VERTICAL, MyFrame::OnUpdateUIVertical)
197 EVT_UPDATE_UI(SPLIT_HORIZONTAL, MyFrame::OnUpdateUIHorizontal)
198 EVT_UPDATE_UI(SPLIT_UNSPLIT, MyFrame::OnUpdateUIUnsplit)
199 EVT_UPDATE_UI(SPLIT_INVISIBLE, MyFrame::OnUpdateUIInvisible)
200 END_EVENT_TABLE()
201
202 // My frame constructor
203 MyFrame::MyFrame()
204 : wxFrame(NULL, wxID_ANY, wxT("wxSplitterWindow sample"),
205 wxDefaultPosition, wxSize(420, 300),
206 wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE)
207 {
208 SetIcon(wxICON(sample));
209
210 #if wxUSE_STATUSBAR
211 CreateStatusBar(2);
212 #endif // wxUSE_STATUSBAR
213
214 // Make a menubar
215 wxMenu *splitMenu = new wxMenu;
216 splitMenu->Append(SPLIT_VERTICAL,
217 wxT("Split &Vertically\tCtrl-V"),
218 wxT("Split vertically"));
219 splitMenu->Append(SPLIT_HORIZONTAL,
220 wxT("Split &Horizontally\tCtrl-H"),
221 wxT("Split horizontally"));
222 splitMenu->Append(SPLIT_UNSPLIT,
223 wxT("&Unsplit\tCtrl-U"),
224 wxT("Unsplit"));
225 splitMenu->AppendCheckItem(SPLIT_INVISIBLE,
226 wxT("Toggle sash &invisibility\tCtrl-I"),
227 wxT("Toggle sash invisibility"));
228 splitMenu->AppendSeparator();
229
230 splitMenu->AppendCheckItem(SPLIT_LIVE,
231 wxT("&Live update\tCtrl-L"),
232 wxT("Toggle live update mode"));
233 splitMenu->AppendCheckItem(SPLIT_BORDER,
234 wxT("3D &Border"),
235 wxT("Toggle wxSP_BORDER flag"));
236 splitMenu->Check(SPLIT_BORDER, true);
237 splitMenu->AppendCheckItem(SPLIT_3DSASH,
238 wxT("&3D Sash"),
239 wxT("Toggle wxSP_3DSASH flag"));
240 splitMenu->Check(SPLIT_3DSASH, true);
241 splitMenu->Append(SPLIT_SETPOSITION,
242 wxT("Set splitter &position\tCtrl-P"),
243 wxT("Set the splitter position"));
244 splitMenu->Append(SPLIT_SETMINSIZE,
245 wxT("Set &min size\tCtrl-M"),
246 wxT("Set minimum pane size"));
247 splitMenu->Append(SPLIT_SETGRAVITY,
248 wxT("Set &gravity\tCtrl-G"),
249 wxT("Set gravity of sash"));
250 splitMenu->AppendSeparator();
251
252 splitMenu->Append(SPLIT_REPLACE,
253 wxT("&Replace right window"),
254 wxT("Replace right window"));
255 splitMenu->AppendSeparator();
256
257 splitMenu->Append(SPLIT_QUIT, wxT("E&xit\tAlt-X"), wxT("Exit"));
258
259 wxMenuBar *menuBar = new wxMenuBar;
260 menuBar->Append(splitMenu, wxT("&Splitter"));
261
262 SetMenuBar(menuBar);
263
264 menuBar->Check(SPLIT_LIVE, true);
265 m_splitter = new MySplitterWindow(this);
266
267 // If you use non-zero gravity you must initialize the splitter with its
268 // correct initial size, otherwise it will change the sash position by a
269 // huge amount when it's resized from its initial default size to its real
270 // size when the frame lays it out. This wouldn't be necessary if default
271 // zero gravity were used (although it would do no harm neither).
272 m_splitter->SetSize(GetClientSize());
273 m_splitter->SetSashGravity(1.0);
274
275 #if 1
276 m_left = new MyCanvas(m_splitter, true);
277 m_left->SetBackgroundColour(*wxRED);
278 m_left->SetCursor(wxCursor(wxCURSOR_MAGNIFIER));
279
280 m_right = new MyCanvas(m_splitter, false);
281 m_right->SetBackgroundColour(*wxCYAN);
282 #else // for testing kbd navigation inside the splitter
283 m_left = new wxTextCtrl(m_splitter, wxID_ANY, wxT("first text"));
284 m_right = new wxTextCtrl(m_splitter, wxID_ANY, wxT("second text"));
285 #endif
286
287 // you can also do this to start with a single window
288 #if 0
289 m_right->Show(false);
290 m_splitter->Initialize(m_left);
291 #else
292 // you can also try -100
293 m_splitter->SplitVertically(m_left, m_right, 100);
294 #endif
295
296 #if wxUSE_STATUSBAR
297 SetStatusText(wxT("Min pane size = 0"), 1);
298 #endif // wxUSE_STATUSBAR
299
300 m_replacewindow = NULL;
301 }
302
303 MyFrame::~MyFrame()
304 {
305 if (m_replacewindow) {
306 m_replacewindow->Destroy();
307 }
308 }
309
310 // menu command handlers
311
312 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
313 {
314 Close(true);
315 }
316
317 void MyFrame::OnSplitHorizontal(wxCommandEvent& WXUNUSED(event) )
318 {
319 if ( m_splitter->IsSplit() )
320 m_splitter->Unsplit();
321 m_left->Show(true);
322 m_right->Show(true);
323 m_splitter->SplitHorizontally( m_left, m_right );
324 m_replacewindow = NULL;
325
326 #if wxUSE_STATUSBAR
327 SetStatusText(wxT("Splitter split horizontally"), 1);
328 #endif // wxUSE_STATUSBAR
329 }
330
331 void MyFrame::OnSplitVertical(wxCommandEvent& WXUNUSED(event) )
332 {
333 if ( m_splitter->IsSplit() )
334 m_splitter->Unsplit();
335 m_left->Show(true);
336 m_right->Show(true);
337 m_splitter->SplitVertically( m_left, m_right );
338 m_replacewindow = NULL;
339
340 #if wxUSE_STATUSBAR
341 SetStatusText(wxT("Splitter split vertically"), 1);
342 #endif // wxUSE_STATUSBAR
343 }
344
345 void MyFrame::OnUnsplit(wxCommandEvent& WXUNUSED(event) )
346 {
347 if ( m_splitter->IsSplit() )
348 m_splitter->Unsplit();
349 #if wxUSE_STATUSBAR
350 SetStatusText(wxT("No splitter"));
351 #endif // wxUSE_STATUSBAR
352 }
353
354 void MyFrame::ToggleFlag(int flag, bool enable)
355 {
356 long style = m_splitter->GetWindowStyleFlag();
357 if ( enable )
358 style |= flag;
359 else
360 style &= ~flag;
361
362 m_splitter->SetWindowStyleFlag(style);
363
364 // we need to move sash to redraw it
365 int pos = m_splitter->GetSashPosition();
366 m_splitter->SetSashPosition(pos + 1);
367 m_splitter->SetSashPosition(pos);
368 }
369
370 void MyFrame::OnSetPosition(wxCommandEvent& WXUNUSED(event) )
371 {
372 wxString str;
373 str.Printf( wxT("%d"), m_splitter->GetSashPosition());
374 #if wxUSE_TEXTDLG
375 str = wxGetTextFromUser(wxT("Enter splitter position:"), wxT(""), str, this);
376 #endif
377 if ( str.empty() )
378 return;
379
380 long pos;
381 if ( !str.ToLong(&pos) )
382 {
383 wxLogError(wxT("The splitter position should be an integer."));
384 return;
385 }
386
387 m_splitter->SetSashPosition(pos);
388
389 wxLogStatus(this, wxT("Splitter position set to %ld"), pos);
390 }
391
392 void MyFrame::OnSetMinSize(wxCommandEvent& WXUNUSED(event) )
393 {
394 wxString str;
395 str.Printf( wxT("%d"), m_splitter->GetMinimumPaneSize());
396 #if wxUSE_TEXTDLG
397 str = wxGetTextFromUser(wxT("Enter minimal size for panes:"), wxT(""), str, this);
398 #endif
399 if ( str.empty() )
400 return;
401
402 int minsize = wxStrtol( str, (wxChar**)NULL, 10 );
403 m_splitter->SetMinimumPaneSize(minsize);
404 #if wxUSE_STATUSBAR
405 str.Printf( wxT("Min pane size = %d"), minsize);
406 SetStatusText(str, 1);
407 #endif // wxUSE_STATUSBAR
408 }
409
410 void MyFrame::OnSetGravity(wxCommandEvent& WXUNUSED(event) )
411 {
412 wxString str;
413 str.Printf( wxT("%g"), m_splitter->GetSashGravity());
414 #if wxUSE_TEXTDLG
415 str = wxGetTextFromUser(wxT("Enter sash gravity (0,1):"), wxT(""), str, this);
416 #endif
417 if ( str.empty() )
418 return;
419
420 double gravity = wxStrtod( str, (wxChar**)NULL);
421 m_splitter->SetSashGravity(gravity);
422 #if wxUSE_STATUSBAR
423 str.Printf( wxT("Gravity = %g"), gravity);
424 SetStatusText(str, 1);
425 #endif // wxUSE_STATUSBAR
426 }
427
428 void MyFrame::OnReplace(wxCommandEvent& WXUNUSED(event) )
429 {
430 if (m_replacewindow == NULL) {
431 m_replacewindow = m_splitter->GetWindow2();
432 m_splitter->ReplaceWindow(m_replacewindow, new wxPanel(m_splitter, wxID_ANY));
433 m_replacewindow->Hide();
434 } else {
435 wxWindow *empty = m_splitter->GetWindow2();
436 wxASSERT(empty != m_replacewindow);
437 m_splitter->ReplaceWindow(empty, m_replacewindow);
438 m_replacewindow->Show();
439 m_replacewindow = NULL;
440 empty->Destroy();
441 }
442 }
443
444 void MyFrame::OnToggleInvisible(wxCommandEvent& WXUNUSED(event) )
445 {
446 m_splitter->SetSashInvisible(!m_splitter->IsSashInvisible());
447 m_splitter->SizeWindows();
448 }
449
450 // Update UI handlers
451
452 void MyFrame::OnUpdateUIHorizontal(wxUpdateUIEvent& event)
453 {
454 event.Enable( (!m_splitter->IsSplit()) || (m_splitter->GetSplitMode() != wxSPLIT_HORIZONTAL) );
455 }
456
457 void MyFrame::OnUpdateUIVertical(wxUpdateUIEvent& event)
458 {
459 event.Enable( ( (!m_splitter->IsSplit()) || (m_splitter->GetSplitMode() != wxSPLIT_VERTICAL) ) );
460 }
461
462 void MyFrame::OnUpdateUIUnsplit(wxUpdateUIEvent& event)
463 {
464 event.Enable( m_splitter->IsSplit() );
465 }
466
467 void MyFrame::OnUpdateUIInvisible(wxUpdateUIEvent& event)
468 {
469 event.Check( m_splitter->IsSashInvisible() );
470 }
471
472 // ----------------------------------------------------------------------------
473 // MySplitterWindow
474 // ----------------------------------------------------------------------------
475
476 BEGIN_EVENT_TABLE(MySplitterWindow, wxSplitterWindow)
477 EVT_SPLITTER_SASH_POS_CHANGED(wxID_ANY, MySplitterWindow::OnPositionChanged)
478 EVT_SPLITTER_SASH_POS_CHANGING(wxID_ANY, MySplitterWindow::OnPositionChanging)
479
480 EVT_SPLITTER_DCLICK(wxID_ANY, MySplitterWindow::OnDClick)
481
482 EVT_SPLITTER_UNSPLIT(wxID_ANY, MySplitterWindow::OnUnsplitEvent)
483 END_EVENT_TABLE()
484
485 MySplitterWindow::MySplitterWindow(wxFrame *parent)
486 : wxSplitterWindow(parent, wxID_ANY,
487 wxDefaultPosition, wxDefaultSize,
488 wxSP_3D | wxSP_LIVE_UPDATE |
489 wxCLIP_CHILDREN /* | wxSP_NO_XP_THEME */ )
490 {
491 m_frame = parent;
492 }
493
494 void MySplitterWindow::OnPositionChanged(wxSplitterEvent& event)
495 {
496 wxLogStatus(m_frame, wxT("Position has changed, now = %d (or %d)"),
497 event.GetSashPosition(), GetSashPosition());
498
499 event.Skip();
500 }
501
502 void MySplitterWindow::OnPositionChanging(wxSplitterEvent& event)
503 {
504 wxLogStatus(m_frame, wxT("Position is changing, now = %d (or %d)"),
505 event.GetSashPosition(), GetSashPosition());
506
507 event.Skip();
508 }
509
510 void MySplitterWindow::OnDClick(wxSplitterEvent& event)
511 {
512 #if wxUSE_STATUSBAR
513 m_frame->SetStatusText(wxT("Splitter double clicked"), 1);
514 #endif // wxUSE_STATUSBAR
515
516 event.Skip();
517 }
518
519 void MySplitterWindow::OnUnsplitEvent(wxSplitterEvent& event)
520 {
521 #if wxUSE_STATUSBAR
522 m_frame->SetStatusText(wxT("Splitter unsplit"), 1);
523 #endif // wxUSE_STATUSBAR
524
525 event.Skip();
526 }
527
528 // ----------------------------------------------------------------------------
529 // MyCanvas
530 // ----------------------------------------------------------------------------
531
532 MyCanvas::MyCanvas(wxWindow* parent, bool mirror)
533 : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
534 wxHSCROLL | wxVSCROLL | wxNO_FULL_REPAINT_ON_RESIZE)
535 {
536 m_mirror = mirror;
537 SetScrollbars(20, 20, 5, 5);
538 }
539
540 void MyCanvas::OnDraw(wxDC& dcOrig)
541 {
542 wxMirrorDC dc(dcOrig, m_mirror);
543
544 dc.SetPen(*wxBLACK_PEN);
545 dc.DrawLine(0, 0, 100, 200);
546
547 dc.SetBackgroundMode(wxBRUSHSTYLE_TRANSPARENT);
548 dc.DrawText(wxT("Testing"), 50, 50);
549
550 dc.SetPen(*wxRED_PEN);
551 dc.SetBrush(*wxGREEN_BRUSH);
552 dc.DrawRectangle(120, 120, 100, 80);
553 }
554