Lots of wxMotif fixes
[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 = 10000;
63 m_maximumPaneSizeY = 10000;
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 = 10000;
81 m_maximumPaneSizeY = 10000;
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 // Find the first frame or dialog and use this to specify
125 // the area to draw on.
126 wxWindow* parent = this;
127
128 while (parent && !parent->IsKindOf(CLASSINFO(wxDialog)) &&
129 !parent->IsKindOf(CLASSINFO(wxFrame)))
130 parent = parent->GetParent();
131
132 wxScreenDC::StartDrawingOnTop(parent);
133
134 // We don't say we're dragging yet; we leave that
135 // decision for the Dragging() branch, to ensure
136 // the user has dragged a little bit.
137 m_dragMode = wxSASH_DRAG_LEFT_DOWN;
138 m_draggingEdge = sashHit;
139 m_firstX = x;
140 m_firstY = y;
141 }
142 }
143 else if ( event.LeftUp() && m_dragMode == wxSASH_DRAG_LEFT_DOWN )
144 {
145 // Wasn't a proper drag
146 ReleaseMouse();
147 wxScreenDC::EndDrawingOnTop();
148 m_dragMode = wxSASH_DRAG_NONE;
149 m_draggingEdge = wxSASH_NONE;
150
151 SetCursor(*wxSTANDARD_CURSOR);
152 }
153 else if (event.LeftUp() && m_dragMode == wxSASH_DRAG_DRAGGING)
154 {
155 // We can stop dragging now and see what we've got.
156 m_dragMode = wxSASH_DRAG_NONE;
157 ReleaseMouse();
158 // Erase old tracker
159 DrawSashTracker(m_draggingEdge, m_oldX, m_oldY);
160
161 // End drawing on top (frees the window used for drawing
162 // over the screen)
163 wxScreenDC::EndDrawingOnTop();
164
165 int w, h;
166 GetSize(&w, &h);
167 int xp, yp;
168 GetPosition(&xp, &yp);
169
170 wxSashEdgePosition edge = m_draggingEdge;
171 m_draggingEdge = wxSASH_NONE;
172
173 wxRect dragRect;
174 wxSashDragStatus status = wxSASH_STATUS_OK;
175 switch (edge)
176 {
177 case wxSASH_TOP:
178 {
179 if (y > (yp + h))
180 status = wxSASH_STATUS_OUT_OF_RANGE;
181 int newHeight = (h - y);
182 newHeight=wxMax(newHeight,m_minimumPaneSizeY);
183 newHeight=wxMin(newHeight,m_maximumPaneSizeY);
184 dragRect = wxRect(xp, (yp + h) - newHeight, w, newHeight);
185 break;
186 }
187 case wxSASH_BOTTOM:
188 {
189 if (y < 0)
190 status = wxSASH_STATUS_OUT_OF_RANGE;
191 int newHeight = y;
192 newHeight=wxMax(newHeight,m_minimumPaneSizeY);
193 newHeight=wxMin(newHeight,m_maximumPaneSizeY);
194 dragRect = wxRect(xp, yp, w, newHeight);
195 break;
196 }
197 case wxSASH_LEFT:
198 {
199 if (x > (xp + w))
200 status = wxSASH_STATUS_OUT_OF_RANGE;
201 int newWidth = (w - x);
202 newWidth=wxMax(newWidth,m_minimumPaneSizeX);
203 newWidth=wxMin(newWidth,m_maximumPaneSizeX);
204 dragRect = wxRect((xp + w) - newWidth, yp, newWidth, h);
205 break;
206 }
207 case wxSASH_RIGHT:
208 {
209 if (x < 0)
210 status = wxSASH_STATUS_OUT_OF_RANGE;
211 int newWidth = x;
212 newWidth=wxMax(newWidth,m_minimumPaneSizeX);
213 newWidth=wxMin(newWidth,m_maximumPaneSizeX);
214 dragRect = wxRect(xp, yp, newWidth, h);
215 break;
216 }
217 case wxSASH_NONE:
218 {
219 break;
220 }
221 }
222
223 wxSashEvent event(GetId(), edge);
224 event.SetEventObject(this);
225 event.SetDragStatus(status);
226 event.SetDragRect(dragRect);
227 GetEventHandler()->ProcessEvent(event);
228 }
229 else if (event.Moving() && !event.Dragging())
230 {
231 // Just change the cursor if required
232 if ( sashHit != wxSASH_NONE )
233 {
234 if ( (sashHit == wxSASH_LEFT) || (sashHit == wxSASH_RIGHT) )
235 {
236 SetCursor(*m_sashCursorWE);
237 }
238 else
239 {
240 SetCursor(*m_sashCursorNS);
241 }
242 }
243 else
244 {
245 SetCursor(*wxSTANDARD_CURSOR);
246 }
247 }
248 else if ( event.Dragging() &&
249 ((m_dragMode == wxSASH_DRAG_DRAGGING) || (m_dragMode == wxSASH_DRAG_LEFT_DOWN))
250 )
251 {
252 if ( (m_draggingEdge == wxSASH_LEFT) || (m_draggingEdge == wxSASH_RIGHT) )
253 {
254 SetCursor(*m_sashCursorWE);
255 }
256 else
257 {
258 SetCursor(*m_sashCursorNS);
259 }
260
261 if (m_dragMode == wxSASH_DRAG_LEFT_DOWN)
262 {
263 m_dragMode = wxSASH_DRAG_DRAGGING;
264 DrawSashTracker(m_draggingEdge, x, y);
265 }
266 else
267 {
268 if ( m_dragMode == wxSASH_DRAG_DRAGGING )
269 {
270 // Erase old tracker
271 DrawSashTracker(m_draggingEdge, m_oldX, m_oldY);
272
273 // Draw new one
274 DrawSashTracker(m_draggingEdge, x, y);
275 }
276 }
277 m_oldX = x;
278 m_oldY = y;
279 }
280 else if ( event.LeftDClick() )
281 {
282 // Nothing
283 }
284 else
285 {
286 }
287 }
288
289 void wxSashWindow::OnSize(wxSizeEvent& WXUNUSED(event))
290 {
291 SizeWindows();
292 }
293
294 wxSashEdgePosition wxSashWindow::SashHitTest(int x, int y, int WXUNUSED(tolerance))
295 {
296 int cx, cy;
297 GetClientSize(& cx, & cy);
298
299 int i;
300 for (i = 0; i < 4; i++)
301 {
302 wxSashEdge& edge = m_sashes[i];
303 wxSashEdgePosition position = (wxSashEdgePosition) i ;
304
305 if (edge.m_show)
306 {
307 switch (position)
308 {
309 case wxSASH_TOP:
310 {
311 if (y >= 0 && y <= GetEdgeMargin(position))
312 return wxSASH_TOP;
313 break;
314 }
315 case wxSASH_RIGHT:
316 {
317 if ((x >= cx - GetEdgeMargin(position)) && (x <= cx))
318 return wxSASH_RIGHT;
319 break;
320 }
321 case wxSASH_BOTTOM:
322 {
323 if ((y >= cy - GetEdgeMargin(position)) && (y <= cy))
324 return wxSASH_BOTTOM;
325 break;
326 }
327 case wxSASH_LEFT:
328 {
329 if ((x >= GetEdgeMargin(position)) && (x >= 0))
330 return wxSASH_LEFT;
331 break;
332 }
333 case wxSASH_NONE:
334 {
335 break;
336 }
337 }
338 }
339 }
340 return wxSASH_NONE;
341 }
342
343 // Draw 3D effect borders
344 void wxSashWindow::DrawBorders(wxDC& dc)
345 {
346 int w, h;
347 GetClientSize(&w, &h);
348
349 wxPen mediumShadowPen(m_mediumShadowColour, 1, wxSOLID);
350 wxPen darkShadowPen(m_darkShadowColour, 1, wxSOLID);
351 wxPen lightShadowPen(m_lightShadowColour, 1, wxSOLID);
352 wxPen hilightPen(m_hilightColour, 1, wxSOLID);
353
354 if ( GetWindowStyleFlag() & wxSP_3D )
355 {
356 dc.SetPen(mediumShadowPen);
357 dc.DrawLine(0, 0, w-1, 0);
358 dc.DrawLine(0, 0, 0, h - 1);
359
360 dc.SetPen(darkShadowPen);
361 dc.DrawLine(1, 1, w-2, 1);
362 dc.DrawLine(1, 1, 1, h-2);
363
364 dc.SetPen(hilightPen);
365 dc.DrawLine(0, h-1, w-1, h-1);
366 dc.DrawLine(w-1, 0, w-1, h); // Surely the maximum y pos. should be h - 1.
367 /// Anyway, h is required for MSW.
368
369 dc.SetPen(lightShadowPen);
370 dc.DrawLine(w-2, 1, w-2, h-2); // Right hand side
371 dc.DrawLine(1, h-2, w-1, h-2); // Bottom
372 }
373 else if ( GetWindowStyleFlag() & wxSP_BORDER )
374 {
375 dc.SetBrush(*wxTRANSPARENT_BRUSH);
376 dc.SetPen(*wxBLACK_PEN);
377 dc.DrawRectangle(0, 0, w-1, h-1);
378 }
379
380 dc.SetPen(wxNullPen);
381 dc.SetBrush(wxNullBrush);
382 }
383
384 void wxSashWindow::DrawSashes(wxDC& dc)
385 {
386 int i;
387 for (i = 0; i < 4; i++)
388 if (m_sashes[i].m_show)
389 DrawSash((wxSashEdgePosition) i, dc);
390 }
391
392 // Draw the sash
393 void wxSashWindow::DrawSash(wxSashEdgePosition edge, wxDC& dc)
394 {
395 int w, h;
396 GetClientSize(&w, &h);
397
398 wxPen facePen(m_faceColour, 1, wxSOLID);
399 wxBrush faceBrush(m_faceColour, wxSOLID);
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 wxPen blackPen(wxColour(0, 0, 0), 1, wxSOLID);
405 wxPen whitePen(wxColour(255, 255, 255), 1, wxSOLID);
406
407 if ( edge == wxSASH_LEFT || edge == wxSASH_RIGHT )
408 {
409 int sashPosition = 0;
410 if (edge == wxSASH_LEFT)
411 sashPosition = 0;
412 else
413 sashPosition = w - GetEdgeMargin(edge);
414
415 dc.SetPen(facePen);
416 dc.SetBrush(faceBrush);
417 dc.DrawRectangle(sashPosition, 0, GetEdgeMargin(edge), h);
418
419 if (GetWindowStyleFlag() & wxSW_3D)
420 {
421 if (edge == wxSASH_LEFT)
422 {
423 // Draw a black line on the left to indicate that the
424 // sash is raised
425 dc.SetPen(blackPen);
426 dc.DrawLine(GetEdgeMargin(edge), 0, GetEdgeMargin(edge), h);
427 }
428 else
429 {
430 // Draw a white line on the right to indicate that the
431 // sash is raised
432 dc.SetPen(whitePen);
433 dc.DrawLine(w - GetEdgeMargin(edge), 0, w - GetEdgeMargin(edge), h);
434 }
435 }
436 }
437 else // top or bottom
438 {
439 int sashPosition = 0;
440 if (edge == wxSASH_TOP)
441 sashPosition = 0;
442 else
443 sashPosition = h - GetEdgeMargin(edge);
444
445 dc.SetPen(facePen);
446 dc.SetBrush(faceBrush);
447 dc.DrawRectangle(0, sashPosition, w, GetEdgeMargin(edge));
448
449 if (GetWindowStyleFlag() & wxSW_3D)
450 {
451 if (edge == wxSASH_BOTTOM)
452 {
453 // Draw a black line on the bottom to indicate that the
454 // sash is raised
455 dc.SetPen(blackPen);
456 dc.DrawLine(0, h - GetEdgeMargin(edge), w, h - GetEdgeMargin(edge));
457 }
458 else
459 {
460 // Draw a white line on the top to indicate that the
461 // sash is raised
462 dc.SetPen(whitePen);
463 dc.DrawLine(0, GetEdgeMargin(edge), w, GetEdgeMargin(edge));
464 }
465 }
466 }
467
468 dc.SetPen(wxNullPen);
469 dc.SetBrush(wxNullBrush);
470 }
471
472 // Draw the sash tracker (for whilst moving the sash)
473 void wxSashWindow::DrawSashTracker(wxSashEdgePosition edge, int x, int y)
474 {
475 int w, h;
476 GetClientSize(&w, &h);
477
478 wxScreenDC screenDC;
479 int x1, y1;
480 int x2, y2;
481
482 if ( edge == wxSASH_LEFT || edge == wxSASH_RIGHT )
483 {
484 x1 = x; y1 = 2;
485 x2 = x; y2 = h-2;
486
487 if ( (edge == wxSASH_LEFT) && (x1 > w) )
488 {
489 x1 = w; x2 = w;
490 }
491 else if ( (edge == wxSASH_RIGHT) && (x1 < 0) )
492 {
493 x1 = 0; x2 = 0;
494 }
495 }
496 else
497 {
498 x1 = 2; y1 = y;
499 x2 = w-2; y2 = y;
500
501 if ( (edge == wxSASH_TOP) && (y1 > h) )
502 {
503 y1 = h;
504 y2 = h;
505 }
506 else if ( (edge == wxSASH_BOTTOM) && (y1 < 0) )
507 {
508 y1 = 0;
509 y2 = 0;
510 }
511 }
512
513 ClientToScreen(&x1, &y1);
514 ClientToScreen(&x2, &y2);
515
516 wxPen sashTrackerPen(*wxBLACK, 2, wxSOLID);
517
518 screenDC.SetLogicalFunction(wxXOR);
519 screenDC.SetPen(sashTrackerPen);
520 screenDC.SetBrush(*wxTRANSPARENT_BRUSH);
521
522 screenDC.DrawLine(x1, y1, x2, y2);
523
524 screenDC.SetLogicalFunction(wxCOPY);
525
526 screenDC.SetPen(wxNullPen);
527 screenDC.SetBrush(wxNullBrush);
528 }
529
530 // Position and size subwindows.
531 // Note that the border size applies to each subwindow, not
532 // including the edges next to the sash.
533 void wxSashWindow::SizeWindows()
534 {
535 int cw, ch;
536 GetClientSize(&cw, &ch);
537
538 if (GetChildren().Number() > 0)
539 {
540 wxWindow* child = (wxWindow*) (GetChildren().First()->Data());
541
542 int x = 0;
543 int y = 0;
544 int width = cw;
545 int height = ch;
546
547 // Top
548 if (m_sashes[0].m_show)
549 {
550 y = m_borderSize;
551 height -= m_borderSize;
552 }
553 y += m_extraBorderSize;
554
555 // Left
556 if (m_sashes[3].m_show)
557 {
558 x = m_borderSize;
559 width -= m_borderSize;
560 }
561 x += m_extraBorderSize;
562
563 // Right
564 if (m_sashes[1].m_show)
565 {
566 width -= m_borderSize;
567 }
568 width -= 2*m_extraBorderSize;
569
570 // Bottom
571 if (m_sashes[2].m_show)
572 {
573 height -= m_borderSize;
574 }
575 height -= 2*m_extraBorderSize;
576
577 child->SetSize(x, y, width, height);
578 }
579
580 wxClientDC dc(this);
581 DrawBorders(dc);
582 DrawSashes(dc);
583 }
584
585 // Initialize colours
586 void wxSashWindow::InitColours()
587 {
588 // Shadow colours
589 #if defined(__WIN95__)
590 m_faceColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
591 m_mediumShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW);
592 m_darkShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW);
593 m_lightShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT);
594 m_hilightColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DHILIGHT);
595 #else
596 m_faceColour = *(wxTheColourDatabase->FindColour("LIGHT GREY"));
597 m_mediumShadowColour = *(wxTheColourDatabase->FindColour("GREY"));
598 m_darkShadowColour = *(wxTheColourDatabase->FindColour("BLACK"));
599 m_lightShadowColour = *(wxTheColourDatabase->FindColour("LIGHT GREY"));
600 m_hilightColour = *(wxTheColourDatabase->FindColour("WHITE"));
601 #endif
602 }
603
604 void wxSashWindow::SetSashVisible(wxSashEdgePosition edge, bool sash)
605 {
606 m_sashes[edge].m_show = sash;
607 if (sash)
608 m_sashes[edge].m_margin = m_borderSize;
609 else
610 m_sashes[edge].m_margin = 0;
611 }
612