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