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