]> git.saurik.com Git - wxWidgets.git/blob - src/generic/splitter.cpp
Misc small changes
[wxWidgets.git] / src / generic / splitter.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: splitter.cpp
3 // Purpose: wxSplitterWindow implementation
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart and Markus Holzem
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "splitter.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/window.h"
25 #include "wx/dialog.h"
26 #include "wx/frame.h"
27 #endif
28
29 #include <stdlib.h>
30
31 #include "wx/string.h"
32 #include "wx/splitter.h"
33 #include "wx/dcscreen.h"
34 #include "wx/settings.h"
35 #include "wx/log.h"
36
37 IMPLEMENT_DYNAMIC_CLASS(wxSplitterWindow, wxWindow)
38 IMPLEMENT_DYNAMIC_CLASS(wxSplitterEvent, wxCommandEvent)
39
40 BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
41 EVT_PAINT(wxSplitterWindow::OnPaint)
42 EVT_SIZE(wxSplitterWindow::OnSize)
43 EVT_IDLE(wxSplitterWindow::OnIdle)
44 EVT_MOUSE_EVENTS(wxSplitterWindow::OnMouseEvent)
45
46 EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor)
47
48 EVT_SPLITTER_SASH_POS_CHANGED(-1, wxSplitterWindow::OnSashPosChanged)
49 // NB: we borrow OnSashPosChanged for purposes of
50 // EVT_SPLITTER_SASH_POS_CHANGING since default implementation is identical
51 EVT_SPLITTER_SASH_POS_CHANGING(-1, wxSplitterWindow::OnSashPosChanged)
52 EVT_SPLITTER_DCLICK(-1, wxSplitterWindow::OnDoubleClick)
53 EVT_SPLITTER_UNSPLIT(-1, wxSplitterWindow::OnUnsplitEvent)
54 END_EVENT_TABLE()
55
56 wxSplitterWindow::wxSplitterWindow()
57 {
58 m_splitMode = wxSPLIT_VERTICAL;
59 m_permitUnsplitAlways = FALSE;
60 m_windowOne = (wxWindow *) NULL;
61 m_windowTwo = (wxWindow *) NULL;
62 m_dragMode = wxSPLIT_DRAG_NONE;
63 m_oldX = 0;
64 m_oldY = 0;
65 m_firstX = 0;
66 m_firstY = 0;
67 m_sashSize = 7;
68 m_borderSize = 2;
69 m_sashPosition = 0;
70 m_sashCursorWE = (wxCursor *) NULL;
71 m_sashCursorNS = (wxCursor *) NULL;
72 m_sashTrackerPen = (wxPen *) NULL;
73 m_lightShadowPen = (wxPen *) NULL;
74 m_mediumShadowPen = (wxPen *) NULL;
75 m_darkShadowPen = (wxPen *) NULL;
76 m_faceBrush = (wxBrush *) NULL;
77 m_facePen = (wxPen *) NULL;
78 m_hilightPen = (wxPen *) NULL;
79 m_minimumPaneSize = 0;
80 m_needUpdating = FALSE;
81 }
82
83 wxSplitterWindow::wxSplitterWindow(wxWindow *parent, wxWindowID id,
84 const wxPoint& pos,
85 const wxSize& size,
86 long style,
87 const wxString& name)
88 : wxWindow(parent, id, pos, size, style, name)
89 {
90 m_splitMode = wxSPLIT_VERTICAL;
91 m_permitUnsplitAlways = (style & wxSP_PERMIT_UNSPLIT) != 0;
92 m_windowOne = (wxWindow *) NULL;
93 m_windowTwo = (wxWindow *) NULL;
94 m_dragMode = wxSPLIT_DRAG_NONE;
95 m_oldX = 0;
96 m_oldY = 0;
97 m_firstX = 0;
98 m_firstY = 0;
99 m_sashSize = 7;
100 m_borderSize = 2;
101 m_sashPosition = 0;
102 m_minimumPaneSize = 0;
103 m_sashCursorWE = new wxCursor(wxCURSOR_SIZEWE);
104 m_sashCursorNS = new wxCursor(wxCURSOR_SIZENS);
105 m_sashTrackerPen = new wxPen(*wxBLACK, 2, wxSOLID);
106 m_lightShadowPen = (wxPen *) NULL;
107 m_mediumShadowPen = (wxPen *) NULL;
108 m_darkShadowPen = (wxPen *) NULL;
109 m_faceBrush = (wxBrush *) NULL;
110 m_facePen = (wxPen *) NULL;
111 m_hilightPen = (wxPen *) NULL;
112
113 if ( style & wxSP_3D )
114 {
115 m_borderSize = 2;
116 m_sashSize = 7;
117 }
118 else if ( style & wxSP_BORDER )
119 {
120 m_borderSize = 1;
121 m_sashSize = 3;
122 }
123 else
124 {
125 m_borderSize = 0;
126 m_sashSize = 3;
127 }
128
129 // Eventually, we'll respond to colour change messages
130 InitColours();
131
132 // For debugging purposes, to see the background.
133 // SetBackground(wxBLUE_BRUSH);
134
135 m_needUpdating = FALSE;
136 }
137
138 wxSplitterWindow::~wxSplitterWindow()
139 {
140 delete m_sashCursorWE;
141 delete m_sashCursorNS;
142 delete m_sashTrackerPen;
143 delete m_lightShadowPen;
144 delete m_darkShadowPen;
145 delete m_mediumShadowPen;
146 delete m_hilightPen;
147 delete m_facePen;
148 delete m_faceBrush;
149 }
150
151 void wxSplitterWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
152 {
153 wxPaintDC dc(this);
154
155 if ( m_borderSize > 0 )
156 DrawBorders(dc);
157 DrawSash(dc);
158 }
159
160 void wxSplitterWindow::OnIdle(wxIdleEvent& WXUNUSED(event))
161 {
162 if (m_needUpdating)
163 SizeWindows();
164 }
165
166 void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
167 {
168 wxCoord x = (wxCoord)event.GetX(),
169 y = (wxCoord)event.GetY();
170
171 // reset the cursor
172 #ifdef __WXMOTIF__
173 SetCursor(* wxSTANDARD_CURSOR);
174 #endif
175 #ifdef __WXMSW__
176 SetCursor(wxCursor());
177 #endif
178
179
180 if (event.LeftDown())
181 {
182 if ( SashHitTest(x, y) )
183 {
184 CaptureMouse();
185
186 m_dragMode = wxSPLIT_DRAG_DRAGGING;
187
188 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
189 {
190 DrawSashTracker(x, y);
191 }
192
193 m_oldX = x;
194 m_oldY = y;
195
196 if ( m_splitMode == wxSPLIT_VERTICAL )
197 {
198 SetCursor(*m_sashCursorWE);
199 }
200 else
201 {
202 SetCursor(*m_sashCursorNS);
203 }
204 return;
205 }
206 }
207 else if (event.LeftUp() && m_dragMode == wxSPLIT_DRAG_DRAGGING)
208 {
209 // We can stop dragging now and see what we've got.
210 m_dragMode = wxSPLIT_DRAG_NONE;
211 ReleaseMouse();
212
213 // Erase old tracker
214 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
215 {
216 DrawSashTracker(m_oldX, m_oldY);
217 }
218
219 // Obtain window size. We are only interested in the dimension the sash
220 // splits up
221 int w, h;
222 GetClientSize(&w, &h);
223 int window_size = (m_splitMode == wxSPLIT_VERTICAL ? w : h );
224 int new_sash_position =
225 (int) ( m_splitMode == wxSPLIT_VERTICAL ? x : y );
226
227 wxSplitterEvent eventSplitter(wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED,
228 this);
229 eventSplitter.m_data.pos = new_sash_position;
230 if ( GetEventHandler()->ProcessEvent(eventSplitter) )
231 {
232 new_sash_position = eventSplitter.GetSashPosition();
233 if ( new_sash_position == -1 )
234 {
235 // change not allowed
236 return;
237 }
238 }
239
240 if ( m_permitUnsplitAlways || m_minimumPaneSize == 0 )
241 {
242 // Deal with possible unsplit scenarios
243 if ( new_sash_position == 0 )
244 {
245 // We remove the first window from the view
246 wxWindow *removedWindow = m_windowOne;
247 m_windowOne = m_windowTwo;
248 m_windowTwo = (wxWindow *) NULL;
249 SendUnsplitEvent(removedWindow);
250 m_sashPosition = 0;
251 }
252 else if ( new_sash_position == window_size )
253 {
254 // We remove the second window from the view
255 wxWindow *removedWindow = m_windowTwo;
256 m_windowTwo = (wxWindow *) NULL;
257 SendUnsplitEvent(removedWindow);
258 m_sashPosition = 0;
259 }
260 else
261 {
262 m_sashPosition = new_sash_position;
263 }
264 }
265 else
266 {
267 m_sashPosition = new_sash_position;
268 }
269
270 SizeWindows();
271 } // left up && dragging
272 else if (event.Moving() && !event.Dragging())
273 {
274 // Just change the cursor if required
275 if ( SashHitTest(x, y) )
276 {
277 if ( m_splitMode == wxSPLIT_VERTICAL )
278 {
279 SetCursor(*m_sashCursorWE);
280 }
281 else
282 {
283 SetCursor(*m_sashCursorNS);
284 }
285 }
286 #if defined(__WXGTK__) || defined(__WXMSW__)
287 else
288 {
289 // We must set the normal cursor in MSW, because
290 // if the child window doesn't have a cursor, the
291 // parent's (splitter window) will be used, and this
292 // must be the standard cursor.
293
294 // where else do we unset the cursor?
295 SetCursor(* wxSTANDARD_CURSOR);
296 }
297 #endif // __WXGTK__
298 }
299 else if (event.Dragging() && (m_dragMode == wxSPLIT_DRAG_DRAGGING))
300 {
301 #ifdef __WXMSW__
302 // Otherwise, the cursor sometimes reverts to the normal cursor
303 // during dragging.
304 if ( m_splitMode == wxSPLIT_VERTICAL )
305 {
306 SetCursor(*m_sashCursorWE);
307 }
308 else
309 {
310 SetCursor(*m_sashCursorNS);
311 }
312 #endif
313
314 // Obtain window size. We are only interested in the dimension the sash
315 // splits up
316 int new_sash_position =
317 (int) ( m_splitMode == wxSPLIT_VERTICAL ? x : y );
318
319 wxSplitterEvent eventSplitter(wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING,
320 this);
321 eventSplitter.m_data.pos = new_sash_position;
322 if ( GetEventHandler()->ProcessEvent(eventSplitter) )
323 {
324 new_sash_position = eventSplitter.GetSashPosition();
325 if ( new_sash_position == -1 )
326 {
327 // change not allowed
328 return;
329 }
330 }
331
332 if (new_sash_position == m_sashPosition)
333 return;
334
335 // Erase old tracker
336 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
337 {
338 DrawSashTracker(m_oldX, m_oldY);
339 }
340
341 if (m_splitMode == wxSPLIT_VERTICAL)
342 x = new_sash_position;
343 else
344 y = new_sash_position;
345
346 // Remember old positions
347 m_oldX = x;
348 m_oldY = y;
349
350 #ifdef __WXMSW__
351 // As we captured the mouse, we may get the mouse events from outside
352 // our window - for example, negative values in x, y. This has a weird
353 // consequence under MSW where we use unsigned values sometimes and
354 // signed ones other times: the coordinates turn as big positive
355 // numbers and so the sash is drawn on the *right* side of the window
356 // instead of the left (or bottom instead of top). Correct this.
357 if ( (short)m_oldX < 0 )
358 m_oldX = 0;
359 if ( (short)m_oldY < 0 )
360 m_oldY = 0;
361 #endif // __WXMSW__
362
363 // Draw new one
364 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
365 {
366 DrawSashTracker(m_oldX, m_oldY);
367 }
368 else
369 {
370 m_sashPosition = new_sash_position;
371 m_needUpdating = TRUE;
372 }
373 }
374 else if ( event.LeftDClick() )
375 {
376 wxSplitterEvent eventSplitter(wxEVT_COMMAND_SPLITTER_DOUBLECLICKED,
377 this);
378 eventSplitter.m_data.pt.x = x;
379 eventSplitter.m_data.pt.y = y;
380
381 (void)GetEventHandler()->ProcessEvent(eventSplitter);
382 }
383 }
384
385 void wxSplitterWindow::OnSize(wxSizeEvent& event)
386 {
387 // only process this message if we're not iconized - otherwise iconizing
388 // and restoring a window containing the splitter has a funny side effect
389 // of changing the splitter position!
390 wxWindow *parent = GetParent();
391 while ( parent && !parent->IsTopLevel() )
392 {
393 parent = parent->GetParent();
394 }
395
396 bool iconized = FALSE;
397 wxFrame *frame = wxDynamicCast(parent, wxFrame);
398 if ( frame )
399 iconized = frame->IsIconized();
400 else
401 {
402 wxDialog *dialog = wxDynamicCast(parent, wxDialog);
403 if ( dialog )
404 iconized = dialog->IsIconized();
405 else
406 wxFAIL_MSG(wxT("should have a top level frame or dialog parent!"));
407 }
408
409 if ( iconized )
410 {
411 event.Skip();
412 }
413 else
414 {
415 int cw, ch;
416 GetClientSize( &cw, &ch );
417 if ( m_windowTwo )
418 {
419 if ( m_splitMode == wxSPLIT_VERTICAL )
420 {
421 if ( m_sashPosition >= (cw - 5) )
422 m_sashPosition = wxMax(10, cw - 40);
423 }
424 if ( m_splitMode == wxSPLIT_HORIZONTAL )
425 {
426 if ( m_sashPosition >= (ch - 5) )
427 m_sashPosition = wxMax(10, ch - 40);
428 }
429 }
430
431 SizeWindows();
432 }
433 }
434
435 bool wxSplitterWindow::SashHitTest(int x, int y, int tolerance)
436 {
437 if ( m_windowTwo == NULL || m_sashPosition == 0)
438 return FALSE; // No sash
439
440 if ( m_splitMode == wxSPLIT_VERTICAL )
441 {
442 if ( (x >= m_sashPosition - tolerance) && (x <= m_sashPosition + m_sashSize + tolerance) )
443 return TRUE;
444 else
445 return FALSE;
446 }
447 else
448 {
449 if ( (y >= (m_sashPosition- tolerance)) && (y <= (m_sashPosition + m_sashSize + tolerance)) )
450 return TRUE;
451 else
452 return FALSE;
453 }
454 }
455
456 // Draw 3D effect borders
457 void wxSplitterWindow::DrawBorders(wxDC& dc)
458 {
459 int w, h;
460 GetClientSize(&w, &h);
461
462 if ( GetWindowStyleFlag() & wxSP_3D )
463 {
464
465 dc.SetPen(*m_facePen);
466 dc.SetBrush(*m_faceBrush);
467 dc.DrawRectangle(1, 1 , w-1, m_borderSize-2 ); //high
468 dc.DrawRectangle(1, m_borderSize-2 , m_borderSize-2, h-1 ); // left
469 dc.DrawRectangle(w-m_borderSize+2, m_borderSize-2 , w-1, h-1 ); // right
470 dc.DrawRectangle(m_borderSize-2, h-m_borderSize+2 , w-m_borderSize+2, h-1 ); //bottom
471
472 dc.SetPen(*m_mediumShadowPen);
473 dc.DrawLine(m_borderSize-2, m_borderSize-2, w-m_borderSize+1, m_borderSize-2);
474 dc.DrawLine(m_borderSize-2, m_borderSize-2, m_borderSize-2, h-m_borderSize+1);
475
476 dc.SetPen(*m_darkShadowPen);
477 dc.DrawLine(m_borderSize-1, m_borderSize-1, w-m_borderSize, m_borderSize-1);
478 dc.DrawLine(m_borderSize-1, m_borderSize-1, m_borderSize-1, h-m_borderSize);
479
480 dc.SetPen(*m_hilightPen);
481 dc.DrawLine(m_borderSize - 2, h-m_borderSize+1, w-m_borderSize+1, h-m_borderSize+1);
482 dc.DrawLine(w-m_borderSize+1, m_borderSize - 2, w-m_borderSize+1, h-m_borderSize+2); // Surely the maximum y pos. should be h - 1.
483 /// Anyway, h is required for MSW.
484
485 dc.SetPen(*m_lightShadowPen);
486 dc.DrawLine(w-m_borderSize, m_borderSize-1, w-m_borderSize, h-m_borderSize); // Right hand side
487 dc.DrawLine(m_borderSize-1, h-m_borderSize, w-m_borderSize+1, h-m_borderSize); // Bottom
488 }
489 else if ( GetWindowStyleFlag() & wxSP_BORDER )
490 {
491 dc.SetBrush(*wxTRANSPARENT_BRUSH);
492 dc.SetPen(*wxBLACK_PEN);
493 dc.DrawRectangle(0, 0, w-1, h-1);
494 }
495
496 dc.SetPen(wxNullPen);
497 dc.SetBrush(wxNullBrush);
498 }
499
500 // Draw the sash
501 void wxSplitterWindow::DrawSash(wxDC& dc)
502 {
503 if ( m_sashPosition == 0 || !m_windowTwo)
504 return;
505
506 int w, h;
507 GetClientSize(&w, &h);
508
509 if ( GetWindowStyleFlag() & wxSP_3D )
510 {
511 if ( m_splitMode == wxSPLIT_VERTICAL )
512 {
513 dc.SetPen(*m_facePen);
514 dc.SetBrush(*m_faceBrush);
515 dc.DrawRectangle(m_sashPosition + 2, 0 , m_sashSize - 4, h );
516
517 dc.SetBrush(*wxTRANSPARENT_BRUSH);
518
519 dc.SetPen(*m_lightShadowPen);
520 int xShadow = m_borderSize ? m_borderSize - 1 : 0 ;
521 dc.DrawLine(m_sashPosition, xShadow , m_sashPosition, h-m_borderSize);
522
523 dc.SetPen(*m_hilightPen);
524 dc.DrawLine(m_sashPosition+1, m_borderSize - 2, m_sashPosition+1, h - m_borderSize+2);
525
526 dc.SetPen(*m_mediumShadowPen);
527 int yMedium = m_borderSize ? h-m_borderSize+1 : h ;
528 dc.DrawLine(m_sashPosition+m_sashSize-2, xShadow, m_sashPosition+m_sashSize-2, yMedium);
529
530 dc.SetPen(*m_darkShadowPen);
531 dc.DrawLine(m_sashPosition+m_sashSize-1, m_borderSize, m_sashPosition+m_sashSize-1, h-m_borderSize );
532 }
533 else
534 {
535 dc.SetPen(*m_facePen);
536 dc.SetBrush(*m_faceBrush);
537 dc.DrawRectangle( m_borderSize-2, m_sashPosition + 2, w-m_borderSize+2, m_sashSize - 4);
538
539 dc.SetBrush(*wxTRANSPARENT_BRUSH);
540
541 dc.SetPen(*m_lightShadowPen);
542 dc.DrawLine(m_borderSize-1, m_sashPosition, w-m_borderSize, m_sashPosition);
543
544 dc.SetPen(*m_hilightPen);
545 dc.DrawLine(m_borderSize-2, m_sashPosition+1, w-m_borderSize+1, m_sashPosition+1);
546
547 dc.SetPen(*m_mediumShadowPen);
548 dc.DrawLine(m_borderSize-1, m_sashPosition+m_sashSize-2, w-m_borderSize+1, m_sashPosition+m_sashSize-2);
549
550 dc.SetPen(*m_darkShadowPen);
551 dc.DrawLine(m_borderSize, m_sashPosition+m_sashSize-1, w-m_borderSize, m_sashPosition+m_sashSize-1);
552 }
553 }
554 else
555 {
556 if ( m_splitMode == wxSPLIT_VERTICAL )
557 {
558 dc.SetPen(*wxBLACK_PEN);
559 dc.SetBrush(*wxBLACK_BRUSH);
560 int h1 = h-1;
561 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER )
562 h1 += 1; // Not sure why this is necessary...
563 dc.DrawRectangle(m_sashPosition, 0, m_sashSize, h1);
564 }
565 else
566 {
567 dc.SetPen(*wxBLACK_PEN);
568 dc.SetBrush(*wxBLACK_BRUSH);
569 int w1 = w-1;
570 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER )
571 w1 ++;
572
573 dc.DrawRectangle(0, m_sashPosition, w1, m_sashSize);
574 }
575
576 }
577
578 dc.SetPen(wxNullPen);
579 dc.SetBrush(wxNullBrush);
580 }
581
582 // Draw the sash tracker (for whilst moving the sash)
583 void wxSplitterWindow::DrawSashTracker(int x, int y)
584 {
585 int w, h;
586 GetClientSize(&w, &h);
587
588 wxScreenDC screenDC;
589 int x1, y1;
590 int x2, y2;
591
592 if ( m_splitMode == wxSPLIT_VERTICAL )
593 {
594 x1 = x; y1 = 2;
595 x2 = x; y2 = h-2;
596
597 if ( x1 > w )
598 {
599 x1 = w; x2 = w;
600 }
601 else if ( x1 < 0 )
602 {
603 x1 = 0; x2 = 0;
604 }
605 }
606 else
607 {
608 x1 = 2; y1 = y;
609 x2 = w-2; y2 = y;
610
611 if ( y1 > h )
612 {
613 y1 = h;
614 y2 = h;
615 }
616 else if ( y1 < 0 )
617 {
618 y1 = 0;
619 y2 = 0;
620 }
621 }
622
623 ClientToScreen(&x1, &y1);
624 ClientToScreen(&x2, &y2);
625
626 screenDC.SetLogicalFunction(wxINVERT);
627 screenDC.SetPen(*m_sashTrackerPen);
628 screenDC.SetBrush(*wxTRANSPARENT_BRUSH);
629
630 screenDC.DrawLine(x1, y1, x2, y2);
631
632 screenDC.SetLogicalFunction(wxCOPY);
633
634 screenDC.SetPen(wxNullPen);
635 screenDC.SetBrush(wxNullBrush);
636 }
637
638 // Position and size subwindows.
639 // Note that the border size applies to each subwindow, not
640 // including the edges next to the sash.
641 void wxSplitterWindow::SizeWindows()
642 {
643 int w, h;
644 GetClientSize(&w, &h);
645
646 if ( m_windowOne && !m_windowTwo )
647 {
648 m_windowOne->SetSize(m_borderSize, m_borderSize, w - 2*m_borderSize, h - 2*m_borderSize);
649
650 }
651 else if ( m_windowOne && m_windowTwo )
652 {
653 if (m_splitMode == wxSPLIT_VERTICAL)
654 {
655 int x1 = m_borderSize;
656 int y1 = m_borderSize;
657 int w1 = m_sashPosition - m_borderSize;
658 int h1 = h - 2*m_borderSize;
659
660 int x2 = m_sashPosition + m_sashSize;
661 int y2 = m_borderSize;
662 int w2 = w - 2*m_borderSize - m_sashSize - w1;
663 int h2 = h - 2*m_borderSize;
664
665 m_windowOne->SetSize(x1, y1, w1, h1);
666 m_windowTwo->SetSize(x2, y2, w2, h2);
667
668 }
669 else
670 {
671 m_windowOne->SetSize(m_borderSize, m_borderSize,
672 w - 2*m_borderSize, m_sashPosition - m_borderSize);
673 m_windowTwo->SetSize(m_borderSize, m_sashPosition + m_sashSize,
674 w - 2*m_borderSize, h - 2*m_borderSize - m_sashSize - (m_sashPosition - m_borderSize));
675
676 }
677 }
678 wxClientDC dc(this);
679 if ( m_borderSize > 0 )
680 DrawBorders(dc);
681 DrawSash(dc);
682
683 m_needUpdating = FALSE;
684 }
685
686 // Set pane for unsplit window
687 void wxSplitterWindow::Initialize(wxWindow *window)
688 {
689 m_windowOne = window;
690 m_windowTwo = (wxWindow *) NULL;
691 m_sashPosition = 0;
692 }
693
694 // Associates the given window with window 2, drawing the appropriate sash
695 // and changing the split mode.
696 // Does nothing and returns FALSE if the window is already split.
697 bool wxSplitterWindow::SplitVertically(wxWindow *window1, wxWindow *window2, int sashPosition)
698 {
699 if ( IsSplit() )
700 return FALSE;
701
702 int w, h;
703 GetClientSize(&w, &h);
704
705 m_splitMode = wxSPLIT_VERTICAL;
706 m_windowOne = window1;
707 m_windowTwo = window2;
708 if ( sashPosition > 0 )
709 m_sashPosition = sashPosition;
710 else if ( sashPosition < 0 )
711 m_sashPosition = w - sashPosition;
712 else // default
713 m_sashPosition = w/2;
714
715 SizeWindows();
716
717 return TRUE;
718 }
719
720 bool wxSplitterWindow::SplitHorizontally(wxWindow *window1, wxWindow *window2, int sashPosition)
721 {
722 if ( IsSplit() )
723 return FALSE;
724
725 int w, h;
726 GetClientSize(&w, &h);
727
728 m_splitMode = wxSPLIT_HORIZONTAL;
729 m_windowOne = window1;
730 m_windowTwo = window2;
731 if ( sashPosition > 0 )
732 m_sashPosition = sashPosition;
733 else if ( sashPosition < 0 )
734 m_sashPosition = h - sashPosition;
735 else // default
736 m_sashPosition = h/2;
737
738 SizeWindows();
739
740 return TRUE;
741 }
742
743
744 // Remove the specified (or second) window from the view
745 // Doesn't actually delete the window.
746 bool wxSplitterWindow::Unsplit(wxWindow *toRemove)
747 {
748 if ( ! IsSplit() )
749 return FALSE;
750
751 wxWindow *win = NULL;
752 if ( toRemove == NULL || toRemove == m_windowTwo)
753 {
754 win = m_windowTwo ;
755 m_windowTwo = (wxWindow *) NULL;
756 }
757 else if ( toRemove == m_windowOne )
758 {
759 win = m_windowOne ;
760 m_windowOne = m_windowTwo;
761 m_windowTwo = (wxWindow *) NULL;
762 }
763 else
764 {
765 wxFAIL_MSG(wxT("splitter: attempt to remove a non-existent window"));
766
767 return FALSE;
768 }
769
770 SendUnsplitEvent(win);
771 m_sashPosition = 0;
772 SizeWindows();
773
774 return TRUE;
775 }
776
777 // Replace a window with another one
778 bool wxSplitterWindow::ReplaceWindow(wxWindow *winOld, wxWindow *winNew)
779 {
780 wxCHECK_MSG( winOld, FALSE, wxT("use one of Split() functions instead") );
781 wxCHECK_MSG( winNew, FALSE, wxT("use Unsplit() functions instead") );
782
783 if ( winOld == m_windowTwo )
784 {
785 m_windowTwo = winNew;
786 }
787 else if ( winOld == m_windowOne )
788 {
789 m_windowOne = winNew;
790 }
791 else
792 {
793 wxFAIL_MSG(wxT("splitter: attempt to replace a non-existent window"));
794
795 return FALSE;
796 }
797
798 SizeWindows();
799
800 return TRUE;
801 }
802
803 void wxSplitterWindow::SetSashPosition(int position, bool redraw)
804 {
805 m_sashPosition = position;
806
807 if ( redraw )
808 {
809 SizeWindows();
810 }
811 }
812
813 // Initialize colours
814 void wxSplitterWindow::InitColours()
815 {
816 wxDELETE( m_facePen );
817 wxDELETE( m_faceBrush );
818 wxDELETE( m_mediumShadowPen );
819 wxDELETE( m_darkShadowPen );
820 wxDELETE( m_lightShadowPen );
821 wxDELETE( m_hilightPen );
822
823 // Shadow colours
824 #if defined(__WIN95__)
825 wxColour faceColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
826 m_facePen = new wxPen(faceColour, 1, wxSOLID);
827 m_faceBrush = new wxBrush(faceColour, wxSOLID);
828
829 wxColour mediumShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW));
830 m_mediumShadowPen = new wxPen(mediumShadowColour, 1, wxSOLID);
831
832 wxColour darkShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW));
833 m_darkShadowPen = new wxPen(darkShadowColour, 1, wxSOLID);
834
835 wxColour lightShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT));
836 m_lightShadowPen = new wxPen(lightShadowColour, 1, wxSOLID);
837
838 wxColour hilightColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DHILIGHT));
839 m_hilightPen = new wxPen(hilightColour, 1, wxSOLID);
840 #else // !Win32
841 m_facePen = new wxPen("LIGHT GREY", 1, wxSOLID);
842 m_faceBrush = new wxBrush("LIGHT GREY", wxSOLID);
843 m_mediumShadowPen = new wxPen("GREY", 1, wxSOLID);
844 m_darkShadowPen = new wxPen("BLACK", 1, wxSOLID);
845 m_lightShadowPen = new wxPen("LIGHT GREY", 1, wxSOLID);
846 m_hilightPen = new wxPen("WHITE", 1, wxSOLID);
847 #endif // Win32/!Win32
848 }
849
850 void wxSplitterWindow::SendUnsplitEvent(wxWindow *winRemoved)
851 {
852 wxSplitterEvent event(wxEVT_COMMAND_SPLITTER_UNSPLIT, this);
853 event.m_data.win = winRemoved;
854
855 (void)GetEventHandler()->ProcessEvent(event);
856 }
857
858 // ---------------------------------------------------------------------------
859 // splitter event handlers
860 // ---------------------------------------------------------------------------
861
862 void wxSplitterWindow::OnSashPosChanged(wxSplitterEvent& event)
863 {
864 // If within UNSPLIT_THRESHOLD from edge, set to edge to cause closure.
865 const int UNSPLIT_THRESHOLD = 4;
866
867 int newSashPosition = event.GetSashPosition();
868
869 // Obtain relevant window dimension for bottom / right threshold check
870 int w, h;
871 GetClientSize(&w, &h);
872 int window_size = (m_splitMode == wxSPLIT_VERTICAL) ? w : h ;
873
874 bool unsplit_scenario = FALSE;
875 if ( m_permitUnsplitAlways
876 || m_minimumPaneSize == 0 )
877 {
878 // Do edge detection if unsplit premitted
879 if ( newSashPosition <= UNSPLIT_THRESHOLD )
880 {
881 // threshold top / left check
882 newSashPosition = 0;
883 unsplit_scenario = TRUE;
884 }
885 if ( newSashPosition >= window_size - UNSPLIT_THRESHOLD )
886 {
887 // threshold bottom/right check
888 newSashPosition = window_size;
889 unsplit_scenario = TRUE;
890 }
891 }
892
893 if ( !unsplit_scenario )
894 {
895 // If resultant pane would be too small, enlarge it
896 if ( newSashPosition < m_minimumPaneSize )
897 newSashPosition = m_minimumPaneSize;
898 if ( newSashPosition > window_size - m_minimumPaneSize )
899 newSashPosition = window_size - m_minimumPaneSize;
900 }
901
902 // If the result is out of bounds it means minimum size is too big,
903 // so split window in half as best compromise.
904 if ( newSashPosition < 0 || newSashPosition > window_size )
905 newSashPosition = window_size / 2;
906
907 // for compatibility, call the virtual function
908 if ( !OnSashPositionChange(newSashPosition) )
909 {
910 newSashPosition = -1;
911 }
912
913 event.SetSashPosition(newSashPosition);
914 }
915
916 // Called when the sash is double-clicked. The default behaviour is to remove
917 // the sash if the minimum pane size is zero.
918 void wxSplitterWindow::OnDoubleClick(wxSplitterEvent& event)
919 {
920 // for compatibility, call the virtual function
921 OnDoubleClickSash(event.GetX(), event.GetY());
922
923 if ( GetMinimumPaneSize() == 0 || m_permitUnsplitAlways )
924 {
925 Unsplit();
926 }
927 }
928
929 void wxSplitterWindow::OnUnsplitEvent(wxSplitterEvent& event)
930 {
931 wxWindow *win = event.GetWindowBeingRemoved();
932
933 // do it before calling OnUnsplit() which may delete the window
934 win->Show(FALSE);
935
936 // for compatibility, call the virtual function
937 OnUnsplit(win);
938 }
939
940 #if defined(__WXMSW__)
941 #define WXUNUSED_UNLESS_MSW(identifier) identifier
942 #else
943 #define WXUNUSED_UNLESS_MSW(identifier) WXUNUSED(identifier)
944 #endif
945
946 void wxSplitterWindow::OnSetCursor(wxSetCursorEvent& WXUNUSED_UNLESS_MSW(event))
947 {
948 // this is currently called (and needed) under MSW only...
949 #ifdef __WXMSW__
950
951 // if we don't do it, the resizing cursor might be set for child window:
952 // and like this we explicitly say that our cursor should not be used for
953 // children windows which overlap us
954
955 if ( SashHitTest(event.GetX(), event.GetY()) )
956 {
957 // default processing is ok
958 event.Skip();
959 }
960 //else: do nothing, in particular, don't call Skip()
961 #endif // wxMSW
962 }