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