Docview mended (grrr) and OGL studio partially working under wxGTK
[wxWidgets.git] / src / generic / splitter.cpp
CommitLineData
c801d85f
KB
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
f4621a09 9// Licence: wxWindows license
c801d85f
KB
10/////////////////////////////////////////////////////////////////////////////
11
12#ifdef __GNUG__
f4621a09 13 #pragma implementation "splitter.h"
c801d85f
KB
14#endif
15
16// For compilers that support precompilation, includes "wx.h".
17#include "wx/wxprec.h"
18
19#ifdef __BORLANDC__
f4621a09 20 #pragma hdrstop
c801d85f
KB
21#endif
22
23#ifndef WX_PRECOMP
2f073eb2
RR
24 #include "wx/window.h"
25 #include "wx/dialog.h"
26 #include "wx/frame.h"
c801d85f
KB
27#endif
28
c801d85f
KB
29#include <stdlib.h>
30
31#include "wx/string.h"
32#include "wx/splitter.h"
33#include "wx/dcscreen.h"
1a7f3062 34#include "wx/settings.h"
a8a0b892 35#include "wx/log.h"
c801d85f 36
c801d85f 37IMPLEMENT_DYNAMIC_CLASS(wxSplitterWindow, wxWindow)
42e69d6b 38IMPLEMENT_DYNAMIC_CLASS(wxSplitterEvent, wxCommandEvent)
c801d85f
KB
39
40BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
41 EVT_PAINT(wxSplitterWindow::OnPaint)
42 EVT_SIZE(wxSplitterWindow::OnSize)
72195a0f 43 EVT_IDLE(wxSplitterWindow::OnIdle)
c801d85f 44 EVT_MOUSE_EVENTS(wxSplitterWindow::OnMouseEvent)
42e69d6b 45
43b5058d
VZ
46 EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor)
47
42e69d6b 48 EVT_SPLITTER_SASH_POS_CHANGED(-1, wxSplitterWindow::OnSashPosChanged)
370938d9
UB
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)
42e69d6b
VZ
52 EVT_SPLITTER_DCLICK(-1, wxSplitterWindow::OnDoubleClick)
53 EVT_SPLITTER_UNSPLIT(-1, wxSplitterWindow::OnUnsplitEvent)
c801d85f 54END_EVENT_TABLE()
c801d85f 55
0d559d69 56wxSplitterWindow::wxSplitterWindow()
c801d85f
KB
57{
58 m_splitMode = wxSPLIT_VERTICAL;
370938d9 59 m_permitUnsplitAlways = FALSE;
c67daf87
UR
60 m_windowOne = (wxWindow *) NULL;
61 m_windowTwo = (wxWindow *) NULL;
c801d85f
KB
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;
c67daf87
UR
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;
c801d85f 79 m_minimumPaneSize = 0;
72195a0f 80 m_needUpdating = FALSE;
c801d85f
KB
81}
82
0d559d69
VZ
83wxSplitterWindow::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)
c801d85f
KB
89{
90 m_splitMode = wxSPLIT_VERTICAL;
370938d9 91 m_permitUnsplitAlways = (style & wxSP_PERMIT_UNSPLIT) != 0;
c67daf87
UR
92 m_windowOne = (wxWindow *) NULL;
93 m_windowTwo = (wxWindow *) NULL;
c801d85f
KB
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);
c67daf87
UR
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;
c801d85f
KB
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
c801d85f
KB
132 // For debugging purposes, to see the background.
133// SetBackground(wxBLUE_BRUSH);
72195a0f
RR
134
135 m_needUpdating = FALSE;
c801d85f
KB
136}
137
0d559d69 138wxSplitterWindow::~wxSplitterWindow()
c801d85f
KB
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
151void wxSplitterWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
152{
153 wxPaintDC dc(this);
154
155 if ( m_borderSize > 0 )
156 DrawBorders(dc);
157 DrawSash(dc);
158}
159
72195a0f
RR
160void wxSplitterWindow::OnIdle(wxIdleEvent& WXUNUSED(event))
161{
162 if (m_needUpdating)
163 SizeWindows();
164}
4c013092 165
c801d85f
KB
166void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
167{
6f2a55e3
VZ
168 wxCoord x = (wxCoord)event.GetX(),
169 y = (wxCoord)event.GetY();
c801d85f 170
f4621a09 171 // reset the cursor
b69f1bd1
JS
172#ifdef __WXMOTIF__
173 SetCursor(* wxSTANDARD_CURSOR);
17867d61
RR
174#endif
175#ifdef __WXMSW__
f4621a09 176 SetCursor(wxCursor());
b69f1bd1 177#endif
f4621a09 178
58d1c1ae 179
0d559d69
VZ
180 if (event.LeftDown())
181 {
c801d85f
KB
182 if ( SashHitTest(x, y) )
183 {
0d559d69 184 CaptureMouse();
c801d85f 185
4a33eba6 186 m_dragMode = wxSPLIT_DRAG_DRAGGING;
f4621a09 187
72195a0f 188 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
4419ba31
VZ
189 {
190 DrawSashTracker(x, y);
191 }
a6aa9b1e 192
4a33eba6
RR
193 m_oldX = x;
194 m_oldY = y;
9f334bea
JS
195
196 if ( m_splitMode == wxSPLIT_VERTICAL )
197 {
198 SetCursor(*m_sashCursorWE);
199 }
200 else
201 {
202 SetCursor(*m_sashCursorNS);
203 }
f4621a09 204 return;
c801d85f 205 }
0d559d69 206 }
0d559d69
VZ
207 else if (event.LeftUp() && m_dragMode == wxSPLIT_DRAG_DRAGGING)
208 {
c801d85f
KB
209 // We can stop dragging now and see what we've got.
210 m_dragMode = wxSPLIT_DRAG_NONE;
0d559d69 211 ReleaseMouse();
dbc208e9 212
c801d85f 213 // Erase old tracker
72195a0f 214 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
4419ba31 215 {
72195a0f 216 DrawSashTracker(m_oldX, m_oldY);
4419ba31 217 }
c801d85f 218
4c013092
UB
219 // Obtain window size. We are only interested in the dimension the sash
220 // splits up
c801d85f 221 int w, h;
0d559d69 222 GetClientSize(&w, &h);
4c013092
UB
223 int window_size = (m_splitMode == wxSPLIT_VERTICAL ? w : h );
224 int new_sash_position =
225 (int) ( m_splitMode == wxSPLIT_VERTICAL ? x : y );
226
42e69d6b
VZ
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 }
4c013092 239
43b5058d 240 if ( m_permitUnsplitAlways || m_minimumPaneSize == 0 )
4c013092 241 {
370938d9
UB
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 }
c801d85f 264 }
4c013092 265 else
c801d85f 266 {
4c013092
UB
267 m_sashPosition = new_sash_position;
268 }
42e69d6b 269
c801d85f 270 SizeWindows();
4a33eba6 271 } // left up && dragging
0d559d69
VZ
272 else if (event.Moving() && !event.Dragging())
273 {
c801d85f
KB
274 // Just change the cursor if required
275 if ( SashHitTest(x, y) )
276 {
0d559d69 277 if ( m_splitMode == wxSPLIT_VERTICAL )
c801d85f 278 {
0d559d69 279 SetCursor(*m_sashCursorWE);
c801d85f
KB
280 }
281 else
282 {
0d559d69 283 SetCursor(*m_sashCursorNS);
c801d85f
KB
284 }
285 }
c0bcc480 286#if defined(__WXGTK__) || defined(__WXMSW__)
58d1c1ae 287 else
42e69d6b 288 {
c0bcc480
JS
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.
c0bcc480 293
42e69d6b 294 // where else do we unset the cursor?
58d1c1ae 295 SetCursor(* wxSTANDARD_CURSOR);
42e69d6b
VZ
296 }
297#endif // __WXGTK__
0d559d69 298 }
a6aa9b1e 299 else if (event.Dragging() && (m_dragMode == wxSPLIT_DRAG_DRAGGING))
0d559d69 300 {
9f334bea
JS
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
370938d9
UB
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 }
7c1122c4
RR
331
332 if (new_sash_position == m_sashPosition)
333 return;
370938d9 334
4a33eba6 335 // Erase old tracker
72195a0f 336 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
4419ba31 337 {
72195a0f 338 DrawSashTracker(m_oldX, m_oldY);
4419ba31 339 }
c801d85f 340
370938d9
UB
341 if (m_splitMode == wxSPLIT_VERTICAL)
342 x = new_sash_position;
343 else
344 y = new_sash_position;
345
7c1122c4
RR
346 // Remember old positions
347 m_oldX = x;
348 m_oldY = y;
370938d9 349
d66a042c
VZ
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.
d66a042c
VZ
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
72195a0f 364 if ((GetWindowStyleFlag() & wxSP_LIVE_UPDATE) == 0)
4419ba31 365 {
72195a0f 366 DrawSashTracker(m_oldX, m_oldY);
4419ba31
VZ
367 }
368 else
369 {
72195a0f 370 m_sashPosition = new_sash_position;
4419ba31
VZ
371 m_needUpdating = TRUE;
372 }
0d559d69 373 }
c801d85f
KB
374 else if ( event.LeftDClick() )
375 {
42e69d6b
VZ
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);
c801d85f 382 }
c801d85f
KB
383}
384
a8731351 385void wxSplitterWindow::OnSize(wxSizeEvent& event)
c801d85f 386{
a8731351
VZ
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() )
c801d85f 392 {
a8731351
VZ
393 parent = parent->GetParent();
394 }
395
58c7cd12 396 bool iconized = FALSE;
a8731351
VZ
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
223d09f6 406 wxFAIL_MSG(wxT("should have a top level frame or dialog parent!"));
a8731351
VZ
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 )
c801d85f 418 {
a8731351
VZ
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 }
c801d85f 429 }
a8731351
VZ
430
431 SizeWindows();
c801d85f 432 }
c801d85f
KB
433}
434
debe6624 435bool wxSplitterWindow::SashHitTest(int x, int y, int tolerance)
c801d85f
KB
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 }
c801d85f
KB
454}
455
456// Draw 3D effect borders
457void wxSplitterWindow::DrawBorders(wxDC& dc)
458{
459 int w, h;
460 GetClientSize(&w, &h);
461
462 if ( GetWindowStyleFlag() & wxSP_3D )
463 {
a6aa9b1e 464
1e2c86ca
PA
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
c801d85f 472 dc.SetPen(*m_mediumShadowPen);
1e2c86ca
PA
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);
c801d85f
KB
475
476 dc.SetPen(*m_darkShadowPen);
1e2c86ca
PA
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);
c801d85f
KB
479
480 dc.SetPen(*m_hilightPen);
1e2c86ca
PA
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.
c801d85f
KB
483 /// Anyway, h is required for MSW.
484
485 dc.SetPen(*m_lightShadowPen);
1e2c86ca
PA
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
c801d85f
KB
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
501void 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);
1e2c86ca 515 dc.DrawRectangle(m_sashPosition + 2, 0 , m_sashSize - 4, h );
c801d85f
KB
516
517 dc.SetBrush(*wxTRANSPARENT_BRUSH);
518
519 dc.SetPen(*m_lightShadowPen);
4419ba31 520 int xShadow = m_borderSize ? m_borderSize - 1 : 0 ;
1e2c86ca 521 dc.DrawLine(m_sashPosition, xShadow , m_sashPosition, h-m_borderSize);
c801d85f
KB
522
523 dc.SetPen(*m_hilightPen);
1e2c86ca 524 dc.DrawLine(m_sashPosition+1, m_borderSize - 2, m_sashPosition+1, h - m_borderSize+2);
c801d85f
KB
525
526 dc.SetPen(*m_mediumShadowPen);
4419ba31 527 int yMedium = m_borderSize ? h-m_borderSize+1 : h ;
1e2c86ca 528 dc.DrawLine(m_sashPosition+m_sashSize-2, xShadow, m_sashPosition+m_sashSize-2, yMedium);
c801d85f
KB
529
530 dc.SetPen(*m_darkShadowPen);
1e2c86ca 531 dc.DrawLine(m_sashPosition+m_sashSize-1, m_borderSize, m_sashPosition+m_sashSize-1, h-m_borderSize );
0d559d69 532 }
c801d85f
KB
533 else
534 {
535 dc.SetPen(*m_facePen);
536 dc.SetBrush(*m_faceBrush);
1e2c86ca 537 dc.DrawRectangle( m_borderSize-2, m_sashPosition + 2, w-m_borderSize+2, m_sashSize - 4);
c801d85f
KB
538
539 dc.SetBrush(*wxTRANSPARENT_BRUSH);
540
541 dc.SetPen(*m_lightShadowPen);
1e2c86ca 542 dc.DrawLine(m_borderSize-1, m_sashPosition, w-m_borderSize, m_sashPosition);
c801d85f
KB
543
544 dc.SetPen(*m_hilightPen);
1e2c86ca 545 dc.DrawLine(m_borderSize-2, m_sashPosition+1, w-m_borderSize+1, m_sashPosition+1);
c801d85f
KB
546
547 dc.SetPen(*m_mediumShadowPen);
1e2c86ca 548 dc.DrawLine(m_borderSize-1, m_sashPosition+m_sashSize-2, w-m_borderSize+1, m_sashPosition+m_sashSize-2);
c801d85f
KB
549
550 dc.SetPen(*m_darkShadowPen);
1e2c86ca 551 dc.DrawLine(m_borderSize, m_sashPosition+m_sashSize-1, w-m_borderSize, m_sashPosition+m_sashSize-1);
c801d85f
KB
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)
debe6624 583void wxSplitterWindow::DrawSashTracker(int x, int y)
c801d85f
KB
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
3c679789 626 screenDC.SetLogicalFunction(wxINVERT);
c801d85f
KB
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.
0d559d69 641void wxSplitterWindow::SizeWindows()
c801d85f
KB
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);
a6aa9b1e 649
c801d85f
KB
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
0d559d69
VZ
665 m_windowOne->SetSize(x1, y1, w1, h1);
666 m_windowTwo->SetSize(x2, y2, w2, h2);
a6aa9b1e 667
c801d85f
KB
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));
a6aa9b1e 675
c801d85f
KB
676 }
677 }
678 wxClientDC dc(this);
4419ba31
VZ
679 if ( m_borderSize > 0 )
680 DrawBorders(dc);
c801d85f 681 DrawSash(dc);
7c1122c4
RR
682
683 m_needUpdating = FALSE;
c801d85f
KB
684}
685
686// Set pane for unsplit window
687void wxSplitterWindow::Initialize(wxWindow *window)
688{
689 m_windowOne = window;
c67daf87 690 m_windowTwo = (wxWindow *) NULL;
c801d85f
KB
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.
debe6624 697bool wxSplitterWindow::SplitVertically(wxWindow *window1, wxWindow *window2, int sashPosition)
c801d85f
KB
698{
699 if ( IsSplit() )
700 return FALSE;
701
0d559d69
VZ
702 int w, h;
703 GetClientSize(&w, &h);
704
c801d85f
KB
705 m_splitMode = wxSPLIT_VERTICAL;
706 m_windowOne = window1;
707 m_windowTwo = window2;
0d559d69 708 if ( sashPosition > 0 )
c801d85f 709 m_sashPosition = sashPosition;
0d559d69
VZ
710 else if ( sashPosition < 0 )
711 m_sashPosition = w - sashPosition;
712 else // default
713 m_sashPosition = w/2;
c801d85f
KB
714
715 SizeWindows();
716
717 return TRUE;
718}
719
debe6624 720bool wxSplitterWindow::SplitHorizontally(wxWindow *window1, wxWindow *window2, int sashPosition)
c801d85f
KB
721{
722 if ( IsSplit() )
723 return FALSE;
724
0d559d69
VZ
725 int w, h;
726 GetClientSize(&w, &h);
727
c801d85f
KB
728 m_splitMode = wxSPLIT_HORIZONTAL;
729 m_windowOne = window1;
730 m_windowTwo = window2;
0d559d69 731 if ( sashPosition > 0 )
c801d85f 732 m_sashPosition = sashPosition;
0d559d69
VZ
733 else if ( sashPosition < 0 )
734 m_sashPosition = h - sashPosition;
735 else // default
736 m_sashPosition = h/2;
c801d85f
KB
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.
746bool wxSplitterWindow::Unsplit(wxWindow *toRemove)
747{
748 if ( ! IsSplit() )
749 return FALSE;
750
3ad5e06b 751 wxWindow *win = NULL;
c801d85f
KB
752 if ( toRemove == NULL || toRemove == m_windowTwo)
753 {
3ad5e06b 754 win = m_windowTwo ;
c67daf87 755 m_windowTwo = (wxWindow *) NULL;
c801d85f
KB
756 }
757 else if ( toRemove == m_windowOne )
758 {
3ad5e06b 759 win = m_windowOne ;
c801d85f 760 m_windowOne = m_windowTwo;
c67daf87 761 m_windowTwo = (wxWindow *) NULL;
c801d85f
KB
762 }
763 else
dbc208e9 764 {
223d09f6 765 wxFAIL_MSG(wxT("splitter: attempt to remove a non-existent window"));
dbc208e9 766
c801d85f 767 return FALSE;
dbc208e9 768 }
c801d85f 769
42e69d6b 770 SendUnsplitEvent(win);
3ad5e06b
VZ
771 m_sashPosition = 0;
772 SizeWindows();
773
774 return TRUE;
775}
776
777// Replace a window with another one
778bool wxSplitterWindow::ReplaceWindow(wxWindow *winOld, wxWindow *winNew)
779{
223d09f6
KB
780 wxCHECK_MSG( winOld, FALSE, wxT("use one of Split() functions instead") );
781 wxCHECK_MSG( winNew, FALSE, wxT("use Unsplit() functions instead") );
3ad5e06b
VZ
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 {
223d09f6 793 wxFAIL_MSG(wxT("splitter: attempt to replace a non-existent window"));
3ad5e06b
VZ
794
795 return FALSE;
796 }
797
798 SizeWindows();
799
c801d85f
KB
800 return TRUE;
801}
802
debe6624 803void wxSplitterWindow::SetSashPosition(int position, bool redraw)
c801d85f
KB
804{
805 m_sashPosition = position;
806
807 if ( redraw )
808 {
809 SizeWindows();
810 }
811}
812
c801d85f 813// Initialize colours
0d559d69 814void wxSplitterWindow::InitColours()
c801d85f 815{
0d559d69
VZ
816 wxDELETE( m_facePen );
817 wxDELETE( m_faceBrush );
818 wxDELETE( m_mediumShadowPen );
819 wxDELETE( m_darkShadowPen );
820 wxDELETE( m_lightShadowPen );
821 wxDELETE( m_hilightPen );
c801d85f
KB
822
823 // Shadow colours
824#if defined(__WIN95__)
c801d85f
KB
825 wxColour faceColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE));
826 m_facePen = new wxPen(faceColour, 1, wxSOLID);
827 m_faceBrush = new wxBrush(faceColour, wxSOLID);
828
c801d85f
KB
829 wxColour mediumShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW));
830 m_mediumShadowPen = new wxPen(mediumShadowColour, 1, wxSOLID);
831
c801d85f
KB
832 wxColour darkShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW));
833 m_darkShadowPen = new wxPen(darkShadowColour, 1, wxSOLID);
834
c801d85f
KB
835 wxColour lightShadowColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT));
836 m_lightShadowPen = new wxPen(lightShadowColour, 1, wxSOLID);
837
c801d85f
KB
838 wxColour hilightColour(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DHILIGHT));
839 m_hilightPen = new wxPen(hilightColour, 1, wxSOLID);
0d559d69 840#else // !Win32
c801d85f
KB
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);
0d559d69 847#endif // Win32/!Win32
c801d85f
KB
848}
849
42e69d6b
VZ
850void 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
862void 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
370938d9
UB
874 bool unsplit_scenario = FALSE;
875 if ( m_permitUnsplitAlways
876 || m_minimumPaneSize == 0 )
bc79aa6b 877 {
370938d9
UB
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 }
bc79aa6b
UB
891 }
892
370938d9 893 if ( !unsplit_scenario )
bc79aa6b
UB
894 {
895 // If resultant pane would be too small, enlarge it
370938d9
UB
896 if ( newSashPosition < m_minimumPaneSize )
897 newSashPosition = m_minimumPaneSize;
898 if ( newSashPosition > window_size - m_minimumPaneSize )
899 newSashPosition = window_size - m_minimumPaneSize;
bc79aa6b 900 }
42e69d6b
VZ
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.
918void wxSplitterWindow::OnDoubleClick(wxSplitterEvent& event)
919{
920 // for compatibility, call the virtual function
921 OnDoubleClickSash(event.GetX(), event.GetY());
922
4419ba31 923 if ( GetMinimumPaneSize() == 0 || m_permitUnsplitAlways )
42e69d6b
VZ
924 {
925 Unsplit();
926 }
927}
928
929void wxSplitterWindow::OnUnsplitEvent(wxSplitterEvent& event)
930{
931 wxWindow *win = event.GetWindowBeingRemoved();
932
4419ba31
VZ
933 // do it before calling OnUnsplit() which may delete the window
934 win->Show(FALSE);
935
42e69d6b
VZ
936 // for compatibility, call the virtual function
937 OnUnsplit(win);
42e69d6b 938}
43b5058d 939
bc1dcfc1
VZ
940#if defined(__WXMSW__)
941 #define WXUNUSED_UNLESS_MSW(identifier) identifier
942#else
943 #define WXUNUSED_UNLESS_MSW(identifier) WXUNUSED(identifier)
944#endif
945
946void wxSplitterWindow::OnSetCursor(wxSetCursorEvent& WXUNUSED_UNLESS_MSW(event))
43b5058d
VZ
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}