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