]> git.saurik.com Git - wxWidgets.git/blame - src/generic/sashwin.cpp
Added a comment about the 'UpdateWindow' issue in SetScrollBars()
[wxWidgets.git] / src / generic / sashwin.cpp
CommitLineData
a6d70308
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: sashwin.cpp
3// Purpose: wxSashWindow implementation. A sash window has an optional
4// sash on each edge, allowing it to be dragged. An event
5// is generated when the sash is released.
6// Author: Julian Smart
7// Modified by:
8// Created: 01/02/97
9// RCS-ID: $Id$
10// Copyright: (c) Julian Smart
88ac883a 11// Licence: wxWindows license
a6d70308
JS
12/////////////////////////////////////////////////////////////////////////////
13
14#ifdef __GNUG__
15#pragma implementation "sashwin.h"
16#endif
17
18// For compilers that support precompilation, includes "wx.h".
19#include "wx/wxprec.h"
20
21#ifdef __BORLANDC__
22#pragma hdrstop
23#endif
24
25#ifndef WX_PRECOMP
26#include "wx/wx.h"
27#endif
28
75be3f5e 29#if wxUSE_SASH
88ac883a 30
a6d70308
JS
31#include <math.h>
32#include <stdlib.h>
33
34#include "wx/string.h"
35#include "wx/dcscreen.h"
36#include "wx/sashwin.h"
f9b1708c 37#include "wx/laywin.h"
a6d70308 38
a6d70308
JS
39IMPLEMENT_DYNAMIC_CLASS(wxSashWindow, wxWindow)
40IMPLEMENT_DYNAMIC_CLASS(wxSashEvent, wxCommandEvent)
41
42BEGIN_EVENT_TABLE(wxSashWindow, wxWindow)
43 EVT_PAINT(wxSashWindow::OnPaint)
44 EVT_SIZE(wxSashWindow::OnSize)
45 EVT_MOUSE_EVENTS(wxSashWindow::OnMouseEvent)
46END_EVENT_TABLE()
a6d70308
JS
47
48wxSashWindow::wxSashWindow()
49{
50 m_draggingEdge = wxSASH_NONE;
51 m_dragMode = wxSASH_DRAG_NONE;
52 m_oldX = 0;
53 m_oldY = 0;
54 m_firstX = 0;
55 m_firstY = 0;
56 m_borderSize = 3 ;
57 m_extraBorderSize = 0;
58 m_sashCursorWE = NULL;
59 m_sashCursorNS = NULL;
60
61 m_minimumPaneSizeX = 0;
62 m_minimumPaneSizeY = 0;
f66b7050
MR
63 m_maximumPaneSizeX = 10000;
64 m_maximumPaneSizeY = 10000;
a6d70308
JS
65}
66
67wxSashWindow::wxSashWindow(wxWindow *parent, wxWindowID id, const wxPoint& pos,
68 const wxSize& size, long style, const wxString& name)
69 :wxWindow(parent, id, pos, size, style, name)
70{
71 m_draggingEdge = wxSASH_NONE;
72 m_dragMode = wxSASH_DRAG_NONE;
73 m_oldX = 0;
74 m_oldY = 0;
75 m_firstX = 0;
76 m_firstY = 0;
77 m_borderSize = 3;
78 m_extraBorderSize = 0;
79 m_minimumPaneSizeX = 0;
80 m_minimumPaneSizeY = 0;
f66b7050
MR
81 m_maximumPaneSizeX = 10000;
82 m_maximumPaneSizeY = 10000;
a6d70308
JS
83 m_sashCursorWE = new wxCursor(wxCURSOR_SIZEWE);
84 m_sashCursorNS = new wxCursor(wxCURSOR_SIZENS);
85
86 // Eventually, we'll respond to colour change messages
87 InitColours();
88}
89
90wxSashWindow::~wxSashWindow()
91{
92 delete m_sashCursorWE;
93 delete m_sashCursorNS;
94}
95
96void wxSashWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
97{
98 wxPaintDC dc(this);
99
ee4c6942
JS
100 // if ( m_borderSize > 0 )
101 DrawBorders(dc);
a6d70308
JS
102
103 DrawSashes(dc);
104}
105
106void wxSashWindow::OnMouseEvent(wxMouseEvent& event)
107{
cfeb83fd
VZ
108 wxCoord x, y;
109 event.GetPosition(&x, &y);
a6d70308
JS
110
111 wxSashEdgePosition sashHit = SashHitTest(x, y);
112
66688764 113 // reset the cursor
b69f1bd1
JS
114#ifdef __WXMOTIF__
115 SetCursor(* wxSTANDARD_CURSOR);
17867d61
RR
116#endif
117#ifdef __WXMSW__
66688764 118 SetCursor(wxCursor());
b69f1bd1 119#endif
66688764 120
a1b82138
VZ
121 if (event.LeftDown())
122 {
a6d70308
JS
123 if ( sashHit != wxSASH_NONE )
124 {
b412f9be 125 CaptureMouse();
a6d70308 126
a1b82138 127 // Required for X to specify that
b412f9be
JS
128 // that we wish to draw on top of all windows
129 // - and we optimise by specifying the area
130 // for creating the overlap window.
131 // Find the first frame or dialog and use this to specify
132 // the area to draw on.
133 wxWindow* parent = this;
134
135 while (parent && !parent->IsKindOf(CLASSINFO(wxDialog)) &&
136 !parent->IsKindOf(CLASSINFO(wxFrame)))
137 parent = parent->GetParent();
138
139 wxScreenDC::StartDrawingOnTop(parent);
a6d70308
JS
140
141 // We don't say we're dragging yet; we leave that
142 // decision for the Dragging() branch, to ensure
143 // the user has dragged a little bit.
144 m_dragMode = wxSASH_DRAG_LEFT_DOWN;
145 m_draggingEdge = sashHit;
146 m_firstX = x;
147 m_firstY = y;
148 }
a1b82138 149 }
a6d70308
JS
150 else if ( event.LeftUp() && m_dragMode == wxSASH_DRAG_LEFT_DOWN )
151 {
152 // Wasn't a proper drag
153 ReleaseMouse();
154 wxScreenDC::EndDrawingOnTop();
155 m_dragMode = wxSASH_DRAG_NONE;
156 m_draggingEdge = wxSASH_NONE;
a6d70308 157 }
a1b82138
VZ
158 else if (event.LeftUp() && m_dragMode == wxSASH_DRAG_DRAGGING)
159 {
a6d70308
JS
160 // We can stop dragging now and see what we've got.
161 m_dragMode = wxSASH_DRAG_NONE;
a1b82138 162 ReleaseMouse();
a6d70308
JS
163 // Erase old tracker
164 DrawSashTracker(m_draggingEdge, m_oldX, m_oldY);
165
166 // End drawing on top (frees the window used for drawing
167 // over the screen)
168 wxScreenDC::EndDrawingOnTop();
169
170 int w, h;
a1b82138 171 GetSize(&w, &h);
a6d70308 172 int xp, yp;
a1b82138 173 GetPosition(&xp, &yp);
a6d70308
JS
174
175 wxSashEdgePosition edge = m_draggingEdge;
176 m_draggingEdge = wxSASH_NONE;
177
178 wxRect dragRect;
179 wxSashDragStatus status = wxSASH_STATUS_OK;
a1b82138
VZ
180
181 // the new height and width of the window - if -1, it didn't change
182 int newHeight = -1,
183 newWidth = -1;
184
185 // NB: x and y may be negative and they're relative to the sash window
186 // upper left corner, while xp and yp are expressed in the parent
187 // window system of coordinates, so adjust them! After this
188 // adjustment, all coordinates are relative to the parent window.
189 y += yp;
190 x += xp;
191
a6d70308
JS
192 switch (edge)
193 {
194 case wxSASH_TOP:
a1b82138
VZ
195 if ( y > yp + h )
196 {
197 // top sash shouldn't get below the bottom one
a6d70308 198 status = wxSASH_STATUS_OUT_OF_RANGE;
a1b82138
VZ
199 }
200 else
201 {
202 newHeight = h - (y - yp);
203 }
a6d70308 204 break;
a1b82138 205
a6d70308 206 case wxSASH_BOTTOM:
a1b82138
VZ
207 if ( y < yp )
208 {
209 // bottom sash shouldn't get above the top one
a6d70308 210 status = wxSASH_STATUS_OUT_OF_RANGE;
a1b82138
VZ
211 }
212 else
213 {
214 newHeight = y - yp;
215 }
a6d70308 216 break;
a1b82138 217
a6d70308 218 case wxSASH_LEFT:
a1b82138
VZ
219 if ( x > xp + w )
220 {
221 // left sash shouldn't get beyond the right one
a6d70308 222 status = wxSASH_STATUS_OUT_OF_RANGE;
a1b82138
VZ
223 }
224 else
225 {
226 newWidth = w - (x - xp);
227 }
a6d70308 228 break;
a1b82138 229
a6d70308 230 case wxSASH_RIGHT:
a1b82138
VZ
231 if ( x < xp )
232 {
233 // and the right sash, finally, shouldn't be beyond the
234 // left one
a6d70308 235 status = wxSASH_STATUS_OUT_OF_RANGE;
a1b82138
VZ
236 }
237 else
238 {
239 newWidth = x - xp;
240 }
a6d70308 241 break;
a1b82138
VZ
242
243 case wxSASH_NONE:
244 // can this happen at all?
245 break;
246 }
247
248 if ( newHeight == -1 )
249 {
250 // didn't change
251 newHeight = h;
252 }
253 else
254 {
255 // make sure it's in m_minimumPaneSizeY..m_maximumPaneSizeY range
256 newHeight = wxMax(newHeight, m_minimumPaneSizeY);
257 newHeight = wxMin(newHeight, m_maximumPaneSizeY);
a6d70308
JS
258 }
259
a1b82138
VZ
260 if ( newWidth == -1 )
261 {
262 // didn't change
263 newWidth = w;
264 }
265 else
266 {
267 // make sure it's in m_minimumPaneSizeY..m_maximumPaneSizeY range
268 newWidth = wxMax(newWidth, m_minimumPaneSizeX);
269 newWidth = wxMin(newWidth, m_maximumPaneSizeX);
270 }
271
272 dragRect = wxRect(x, y, newWidth, newHeight);
273
a6d70308
JS
274 wxSashEvent event(GetId(), edge);
275 event.SetEventObject(this);
276 event.SetDragStatus(status);
277 event.SetDragRect(dragRect);
278 GetEventHandler()->ProcessEvent(event);
a1b82138
VZ
279 }
280 else if (event.Moving() && !event.Dragging())
281 {
a6d70308
JS
282 // Just change the cursor if required
283 if ( sashHit != wxSASH_NONE )
284 {
a1b82138
VZ
285 if ( (sashHit == wxSASH_LEFT) || (sashHit == wxSASH_RIGHT) )
286 {
287 SetCursor(*m_sashCursorWE);
288 }
289 else
290 {
291 SetCursor(*m_sashCursorNS);
292 }
293 }
294 }
295 else if ( event.Dragging() &&
296 ((m_dragMode == wxSASH_DRAG_DRAGGING) ||
297 (m_dragMode == wxSASH_DRAG_LEFT_DOWN)) )
298 {
299 if ( (m_draggingEdge == wxSASH_LEFT) || (m_draggingEdge == wxSASH_RIGHT) )
300 {
301 SetCursor(*m_sashCursorWE);
302 }
303 else
304 {
305 SetCursor(*m_sashCursorNS);
a6d70308 306 }
a6d70308
JS
307
308 if (m_dragMode == wxSASH_DRAG_LEFT_DOWN)
309 {
310 m_dragMode = wxSASH_DRAG_DRAGGING;
311 DrawSashTracker(m_draggingEdge, x, y);
312 }
313 else
314 {
a1b82138
VZ
315 if ( m_dragMode == wxSASH_DRAG_DRAGGING )
316 {
317 // Erase old tracker
318 DrawSashTracker(m_draggingEdge, m_oldX, m_oldY);
a6d70308 319
a1b82138
VZ
320 // Draw new one
321 DrawSashTracker(m_draggingEdge, x, y);
322 }
a6d70308
JS
323 }
324 m_oldX = x;
325 m_oldY = y;
a1b82138 326 }
a6d70308
JS
327 else if ( event.LeftDClick() )
328 {
329 // Nothing
330 }
331 else
332 {
333 }
334}
335
336void wxSashWindow::OnSize(wxSizeEvent& WXUNUSED(event))
337{
338 SizeWindows();
339}
340
341287bf 341wxSashEdgePosition wxSashWindow::SashHitTest(int x, int y, int WXUNUSED(tolerance))
a6d70308
JS
342{
343 int cx, cy;
344 GetClientSize(& cx, & cy);
345
346 int i;
347 for (i = 0; i < 4; i++)
348 {
349 wxSashEdge& edge = m_sashes[i];
350 wxSashEdgePosition position = (wxSashEdgePosition) i ;
351
352 if (edge.m_show)
353 {
354 switch (position)
355 {
356 case wxSASH_TOP:
357 {
358 if (y >= 0 && y <= GetEdgeMargin(position))
359 return wxSASH_TOP;
360 break;
361 }
362 case wxSASH_RIGHT:
363 {
364 if ((x >= cx - GetEdgeMargin(position)) && (x <= cx))
365 return wxSASH_RIGHT;
366 break;
367 }
368 case wxSASH_BOTTOM:
369 {
370 if ((y >= cy - GetEdgeMargin(position)) && (y <= cy))
371 return wxSASH_BOTTOM;
372 break;
373 }
374 case wxSASH_LEFT:
375 {
82540ef2 376 if ((x <= GetEdgeMargin(position)) && (x >= 0))
a6d70308
JS
377 return wxSASH_LEFT;
378 break;
379 }
341287bf
JS
380 case wxSASH_NONE:
381 {
382 break;
383 }
a6d70308
JS
384 }
385 }
386 }
387 return wxSASH_NONE;
388}
389
390// Draw 3D effect borders
391void wxSashWindow::DrawBorders(wxDC& dc)
392{
393 int w, h;
394 GetClientSize(&w, &h);
395
396 wxPen mediumShadowPen(m_mediumShadowColour, 1, wxSOLID);
397 wxPen darkShadowPen(m_darkShadowColour, 1, wxSOLID);
398 wxPen lightShadowPen(m_lightShadowColour, 1, wxSOLID);
399 wxPen hilightPen(m_hilightColour, 1, wxSOLID);
400
448af9a4 401 if ( GetWindowStyleFlag() & wxSW_3D )
a6d70308
JS
402 {
403 dc.SetPen(mediumShadowPen);
404 dc.DrawLine(0, 0, w-1, 0);
405 dc.DrawLine(0, 0, 0, h - 1);
406
407 dc.SetPen(darkShadowPen);
408 dc.DrawLine(1, 1, w-2, 1);
409 dc.DrawLine(1, 1, 1, h-2);
410
411 dc.SetPen(hilightPen);
412 dc.DrawLine(0, h-1, w-1, h-1);
413 dc.DrawLine(w-1, 0, w-1, h); // Surely the maximum y pos. should be h - 1.
414 /// Anyway, h is required for MSW.
415
416 dc.SetPen(lightShadowPen);
417 dc.DrawLine(w-2, 1, w-2, h-2); // Right hand side
418 dc.DrawLine(1, h-2, w-1, h-2); // Bottom
419 }
448af9a4 420 else if ( GetWindowStyleFlag() & wxSW_BORDER )
a6d70308
JS
421 {
422 dc.SetBrush(*wxTRANSPARENT_BRUSH);
423 dc.SetPen(*wxBLACK_PEN);
424 dc.DrawRectangle(0, 0, w-1, h-1);
425 }
426
427 dc.SetPen(wxNullPen);
428 dc.SetBrush(wxNullBrush);
429}
430
431void wxSashWindow::DrawSashes(wxDC& dc)
432{
433 int i;
434 for (i = 0; i < 4; i++)
435 if (m_sashes[i].m_show)
436 DrawSash((wxSashEdgePosition) i, dc);
437}
438
439// Draw the sash
440void wxSashWindow::DrawSash(wxSashEdgePosition edge, wxDC& dc)
441{
442 int w, h;
443 GetClientSize(&w, &h);
444
445 wxPen facePen(m_faceColour, 1, wxSOLID);
446 wxBrush faceBrush(m_faceColour, wxSOLID);
447 wxPen mediumShadowPen(m_mediumShadowColour, 1, wxSOLID);
448 wxPen darkShadowPen(m_darkShadowColour, 1, wxSOLID);
449 wxPen lightShadowPen(m_lightShadowColour, 1, wxSOLID);
450 wxPen hilightPen(m_hilightColour, 1, wxSOLID);
451 wxPen blackPen(wxColour(0, 0, 0), 1, wxSOLID);
452 wxPen whitePen(wxColour(255, 255, 255), 1, wxSOLID);
453
454 if ( edge == wxSASH_LEFT || edge == wxSASH_RIGHT )
455 {
456 int sashPosition = 0;
457 if (edge == wxSASH_LEFT)
458 sashPosition = 0;
459 else
460 sashPosition = w - GetEdgeMargin(edge);
461
462 dc.SetPen(facePen);
463 dc.SetBrush(faceBrush);
464 dc.DrawRectangle(sashPosition, 0, GetEdgeMargin(edge), h);
465
466 if (GetWindowStyleFlag() & wxSW_3D)
467 {
468 if (edge == wxSASH_LEFT)
469 {
470 // Draw a black line on the left to indicate that the
471 // sash is raised
472 dc.SetPen(blackPen);
473 dc.DrawLine(GetEdgeMargin(edge), 0, GetEdgeMargin(edge), h);
474 }
475 else
476 {
477 // Draw a white line on the right to indicate that the
478 // sash is raised
479 dc.SetPen(whitePen);
480 dc.DrawLine(w - GetEdgeMargin(edge), 0, w - GetEdgeMargin(edge), h);
481 }
482 }
483 }
484 else // top or bottom
485 {
486 int sashPosition = 0;
487 if (edge == wxSASH_TOP)
488 sashPosition = 0;
489 else
490 sashPosition = h - GetEdgeMargin(edge);
491
492 dc.SetPen(facePen);
493 dc.SetBrush(faceBrush);
494 dc.DrawRectangle(0, sashPosition, w, GetEdgeMargin(edge));
495
496 if (GetWindowStyleFlag() & wxSW_3D)
497 {
498 if (edge == wxSASH_BOTTOM)
499 {
500 // Draw a black line on the bottom to indicate that the
501 // sash is raised
502 dc.SetPen(blackPen);
503 dc.DrawLine(0, h - GetEdgeMargin(edge), w, h - GetEdgeMargin(edge));
504 }
505 else
506 {
507 // Draw a white line on the top to indicate that the
508 // sash is raised
509 dc.SetPen(whitePen);
510 dc.DrawLine(0, GetEdgeMargin(edge), w, GetEdgeMargin(edge));
511 }
512 }
513 }
514
515 dc.SetPen(wxNullPen);
516 dc.SetBrush(wxNullBrush);
517}
518
519// Draw the sash tracker (for whilst moving the sash)
520void wxSashWindow::DrawSashTracker(wxSashEdgePosition edge, int x, int y)
521{
522 int w, h;
523 GetClientSize(&w, &h);
524
525 wxScreenDC screenDC;
526 int x1, y1;
527 int x2, y2;
528
529 if ( edge == wxSASH_LEFT || edge == wxSASH_RIGHT )
530 {
531 x1 = x; y1 = 2;
532 x2 = x; y2 = h-2;
533
534 if ( (edge == wxSASH_LEFT) && (x1 > w) )
535 {
536 x1 = w; x2 = w;
537 }
538 else if ( (edge == wxSASH_RIGHT) && (x1 < 0) )
539 {
540 x1 = 0; x2 = 0;
541 }
542 }
543 else
544 {
545 x1 = 2; y1 = y;
546 x2 = w-2; y2 = y;
547
548 if ( (edge == wxSASH_TOP) && (y1 > h) )
549 {
550 y1 = h;
551 y2 = h;
552 }
553 else if ( (edge == wxSASH_BOTTOM) && (y1 < 0) )
554 {
555 y1 = 0;
556 y2 = 0;
557 }
558 }
559
560 ClientToScreen(&x1, &y1);
561 ClientToScreen(&x2, &y2);
562
563 wxPen sashTrackerPen(*wxBLACK, 2, wxSOLID);
564
3c679789 565 screenDC.SetLogicalFunction(wxINVERT);
a6d70308
JS
566 screenDC.SetPen(sashTrackerPen);
567 screenDC.SetBrush(*wxTRANSPARENT_BRUSH);
568
569 screenDC.DrawLine(x1, y1, x2, y2);
570
571 screenDC.SetLogicalFunction(wxCOPY);
572
573 screenDC.SetPen(wxNullPen);
574 screenDC.SetBrush(wxNullBrush);
575}
576
577// Position and size subwindows.
578// Note that the border size applies to each subwindow, not
579// including the edges next to the sash.
580void wxSashWindow::SizeWindows()
581{
582 int cw, ch;
583 GetClientSize(&cw, &ch);
584
f9b1708c 585 if (GetChildren().Number() == 1)
a6d70308 586 {
c0ed460c 587 wxWindow* child = (wxWindow*) (GetChildren().First()->Data());
a6d70308
JS
588
589 int x = 0;
590 int y = 0;
591 int width = cw;
592 int height = ch;
593
594 // Top
595 if (m_sashes[0].m_show)
596 {
597 y = m_borderSize;
598 height -= m_borderSize;
599 }
600 y += m_extraBorderSize;
601
602 // Left
603 if (m_sashes[3].m_show)
604 {
605 x = m_borderSize;
606 width -= m_borderSize;
607 }
608 x += m_extraBorderSize;
609
610 // Right
611 if (m_sashes[1].m_show)
612 {
613 width -= m_borderSize;
614 }
615 width -= 2*m_extraBorderSize;
616
617 // Bottom
618 if (m_sashes[2].m_show)
619 {
620 height -= m_borderSize;
621 }
622 height -= 2*m_extraBorderSize;
623
624 child->SetSize(x, y, width, height);
625 }
f9b1708c
JS
626 else if (GetChildren().Number() > 1)
627 {
628 // Perhaps multiple children are themselves sash windows.
629 // TODO: this doesn't really work because the subwindows sizes/positions
630 // must be set to leave a gap for the parent's sash (hit-test and decorations).
631 // Perhaps we can allow for this within LayoutWindow, testing whether the parent
632 // is a sash window, and if so, allowing some space for the edges.
633 wxLayoutAlgorithm layout;
634 layout.LayoutWindow(this);
635 }
a6d70308
JS
636
637 wxClientDC dc(this);
638 DrawBorders(dc);
639 DrawSashes(dc);
640}
641
642// Initialize colours
643void wxSashWindow::InitColours()
644{
645 // Shadow colours
646#if defined(__WIN95__)
647 m_faceColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
648 m_mediumShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW);
649 m_darkShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW);
650 m_lightShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT);
651 m_hilightColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DHILIGHT);
652#else
653 m_faceColour = *(wxTheColourDatabase->FindColour("LIGHT GREY"));
341287bf 654 m_mediumShadowColour = *(wxTheColourDatabase->FindColour("GREY"));
a6d70308
JS
655 m_darkShadowColour = *(wxTheColourDatabase->FindColour("BLACK"));
656 m_lightShadowColour = *(wxTheColourDatabase->FindColour("LIGHT GREY"));
657 m_hilightColour = *(wxTheColourDatabase->FindColour("WHITE"));
658#endif
659}
660
661void wxSashWindow::SetSashVisible(wxSashEdgePosition edge, bool sash)
662{
663 m_sashes[edge].m_show = sash;
664 if (sash)
665 m_sashes[edge].m_margin = m_borderSize;
666 else
667 m_sashes[edge].m_margin = 0;
668}
669
75be3f5e 670#endif // wxUSE_SASH