new makefiles (part I)
[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 #if !wxUSE_SASH
30 #error "Thisfile requires wxUSE_SASH to be defined."
31 #endif // wxUSE_SASH
32
33 #include <math.h>
34 #include <stdlib.h>
35
36 #include "wx/string.h"
37 #include "wx/dcscreen.h"
38 #include "wx/sashwin.h"
39 #include "wx/laywin.h"
40
41 #if !USE_SHARED_LIBRARY
42 IMPLEMENT_DYNAMIC_CLASS(wxSashWindow, wxWindow)
43 IMPLEMENT_DYNAMIC_CLASS(wxSashEvent, wxCommandEvent)
44
45 BEGIN_EVENT_TABLE(wxSashWindow, wxWindow)
46 EVT_PAINT(wxSashWindow::OnPaint)
47 EVT_SIZE(wxSashWindow::OnSize)
48 EVT_MOUSE_EVENTS(wxSashWindow::OnMouseEvent)
49 END_EVENT_TABLE()
50 #endif
51
52 wxSashWindow::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;
67 m_maximumPaneSizeX = 10000;
68 m_maximumPaneSizeY = 10000;
69 }
70
71 wxSashWindow::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;
85 m_maximumPaneSizeX = 10000;
86 m_maximumPaneSizeY = 10000;
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
94 wxSashWindow::~wxSashWindow()
95 {
96 delete m_sashCursorWE;
97 delete m_sashCursorNS;
98 }
99
100 void wxSashWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
101 {
102 wxPaintDC dc(this);
103
104 // if ( m_borderSize > 0 )
105 DrawBorders(dc);
106
107 DrawSashes(dc);
108 }
109
110 void wxSashWindow::OnMouseEvent(wxMouseEvent& event)
111 {
112 long x, y;
113 event.Position(&x, &y);
114
115 wxSashEdgePosition sashHit = SashHitTest(x, y);
116
117 // reset the cursor
118 #ifdef __WXMOTIF__
119 SetCursor(* wxSTANDARD_CURSOR);
120 #endif
121 #ifdef __WXMSW__
122 SetCursor(wxCursor());
123 #endif
124
125 if (event.LeftDown())
126 {
127 if ( sashHit != wxSASH_NONE )
128 {
129 CaptureMouse();
130
131 // Required for X to specify that
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);
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 }
153 }
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;
161 }
162 else if (event.LeftUp() && m_dragMode == wxSASH_DRAG_DRAGGING)
163 {
164 // We can stop dragging now and see what we've got.
165 m_dragMode = wxSASH_DRAG_NONE;
166 ReleaseMouse();
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;
175 GetSize(&w, &h);
176 int xp, yp;
177 GetPosition(&xp, &yp);
178
179 wxSashEdgePosition edge = m_draggingEdge;
180 m_draggingEdge = wxSASH_NONE;
181
182 wxRect dragRect;
183 wxSashDragStatus status = wxSASH_STATUS_OK;
184 switch (edge)
185 {
186 case wxSASH_TOP:
187 {
188 if (y > (yp + h))
189 status = wxSASH_STATUS_OUT_OF_RANGE;
190 int newHeight = (h - y);
191 newHeight=wxMax(newHeight,m_minimumPaneSizeY);
192 newHeight=wxMin(newHeight,m_maximumPaneSizeY);
193 dragRect = wxRect(xp, (yp + h) - newHeight, w, newHeight);
194 break;
195 }
196 case wxSASH_BOTTOM:
197 {
198 if (y < 0)
199 status = wxSASH_STATUS_OUT_OF_RANGE;
200 int newHeight = y;
201 newHeight=wxMax(newHeight,m_minimumPaneSizeY);
202 newHeight=wxMin(newHeight,m_maximumPaneSizeY);
203 dragRect = wxRect(xp, yp, w, newHeight);
204 break;
205 }
206 case wxSASH_LEFT:
207 {
208 if (x > (xp + w))
209 status = wxSASH_STATUS_OUT_OF_RANGE;
210 int newWidth = (w - x);
211 newWidth=wxMax(newWidth,m_minimumPaneSizeX);
212 newWidth=wxMin(newWidth,m_maximumPaneSizeX);
213 dragRect = wxRect((xp + w) - newWidth, yp, newWidth, h);
214 break;
215 }
216 case wxSASH_RIGHT:
217 {
218 if (x < 0)
219 status = wxSASH_STATUS_OUT_OF_RANGE;
220 int newWidth = x;
221 newWidth=wxMax(newWidth,m_minimumPaneSizeX);
222 newWidth=wxMin(newWidth,m_maximumPaneSizeX);
223 dragRect = wxRect(xp, yp, newWidth, h);
224 break;
225 }
226 case wxSASH_NONE:
227 {
228 break;
229 }
230 }
231
232 wxSashEvent event(GetId(), edge);
233 event.SetEventObject(this);
234 event.SetDragStatus(status);
235 event.SetDragRect(dragRect);
236 GetEventHandler()->ProcessEvent(event);
237 }
238 else if (event.Moving() && !event.Dragging())
239 {
240 // Just change the cursor if required
241 if ( sashHit != wxSASH_NONE )
242 {
243 if ( (sashHit == wxSASH_LEFT) || (sashHit == wxSASH_RIGHT) )
244 {
245 SetCursor(*m_sashCursorWE);
246 }
247 else
248 {
249 SetCursor(*m_sashCursorNS);
250 }
251 }
252 }
253 else if ( event.Dragging() &&
254 ((m_dragMode == wxSASH_DRAG_DRAGGING) || (m_dragMode == wxSASH_DRAG_LEFT_DOWN))
255 )
256 {
257 if ( (m_draggingEdge == wxSASH_LEFT) || (m_draggingEdge == wxSASH_RIGHT) )
258 {
259 SetCursor(*m_sashCursorWE);
260 }
261 else
262 {
263 SetCursor(*m_sashCursorNS);
264 }
265
266 if (m_dragMode == wxSASH_DRAG_LEFT_DOWN)
267 {
268 m_dragMode = wxSASH_DRAG_DRAGGING;
269 DrawSashTracker(m_draggingEdge, x, y);
270 }
271 else
272 {
273 if ( m_dragMode == wxSASH_DRAG_DRAGGING )
274 {
275 // Erase old tracker
276 DrawSashTracker(m_draggingEdge, m_oldX, m_oldY);
277
278 // Draw new one
279 DrawSashTracker(m_draggingEdge, x, y);
280 }
281 }
282 m_oldX = x;
283 m_oldY = y;
284 }
285 else if ( event.LeftDClick() )
286 {
287 // Nothing
288 }
289 else
290 {
291 }
292 }
293
294 void wxSashWindow::OnSize(wxSizeEvent& WXUNUSED(event))
295 {
296 SizeWindows();
297 }
298
299 wxSashEdgePosition wxSashWindow::SashHitTest(int x, int y, int WXUNUSED(tolerance))
300 {
301 int cx, cy;
302 GetClientSize(& cx, & cy);
303
304 int i;
305 for (i = 0; i < 4; i++)
306 {
307 wxSashEdge& edge = m_sashes[i];
308 wxSashEdgePosition position = (wxSashEdgePosition) i ;
309
310 if (edge.m_show)
311 {
312 switch (position)
313 {
314 case wxSASH_TOP:
315 {
316 if (y >= 0 && y <= GetEdgeMargin(position))
317 return wxSASH_TOP;
318 break;
319 }
320 case wxSASH_RIGHT:
321 {
322 if ((x >= cx - GetEdgeMargin(position)) && (x <= cx))
323 return wxSASH_RIGHT;
324 break;
325 }
326 case wxSASH_BOTTOM:
327 {
328 if ((y >= cy - GetEdgeMargin(position)) && (y <= cy))
329 return wxSASH_BOTTOM;
330 break;
331 }
332 case wxSASH_LEFT:
333 {
334 if ((x <= GetEdgeMargin(position)) && (x >= 0))
335 return wxSASH_LEFT;
336 break;
337 }
338 case wxSASH_NONE:
339 {
340 break;
341 }
342 }
343 }
344 }
345 return wxSASH_NONE;
346 }
347
348 // Draw 3D effect borders
349 void wxSashWindow::DrawBorders(wxDC& dc)
350 {
351 int w, h;
352 GetClientSize(&w, &h);
353
354 wxPen mediumShadowPen(m_mediumShadowColour, 1, wxSOLID);
355 wxPen darkShadowPen(m_darkShadowColour, 1, wxSOLID);
356 wxPen lightShadowPen(m_lightShadowColour, 1, wxSOLID);
357 wxPen hilightPen(m_hilightColour, 1, wxSOLID);
358
359 if ( GetWindowStyleFlag() & wxSP_3D )
360 {
361 dc.SetPen(mediumShadowPen);
362 dc.DrawLine(0, 0, w-1, 0);
363 dc.DrawLine(0, 0, 0, h - 1);
364
365 dc.SetPen(darkShadowPen);
366 dc.DrawLine(1, 1, w-2, 1);
367 dc.DrawLine(1, 1, 1, h-2);
368
369 dc.SetPen(hilightPen);
370 dc.DrawLine(0, h-1, w-1, h-1);
371 dc.DrawLine(w-1, 0, w-1, h); // Surely the maximum y pos. should be h - 1.
372 /// Anyway, h is required for MSW.
373
374 dc.SetPen(lightShadowPen);
375 dc.DrawLine(w-2, 1, w-2, h-2); // Right hand side
376 dc.DrawLine(1, h-2, w-1, h-2); // Bottom
377 }
378 else if ( GetWindowStyleFlag() & wxSP_BORDER )
379 {
380 dc.SetBrush(*wxTRANSPARENT_BRUSH);
381 dc.SetPen(*wxBLACK_PEN);
382 dc.DrawRectangle(0, 0, w-1, h-1);
383 }
384
385 dc.SetPen(wxNullPen);
386 dc.SetBrush(wxNullBrush);
387 }
388
389 void wxSashWindow::DrawSashes(wxDC& dc)
390 {
391 int i;
392 for (i = 0; i < 4; i++)
393 if (m_sashes[i].m_show)
394 DrawSash((wxSashEdgePosition) i, dc);
395 }
396
397 // Draw the sash
398 void wxSashWindow::DrawSash(wxSashEdgePosition edge, wxDC& dc)
399 {
400 int w, h;
401 GetClientSize(&w, &h);
402
403 wxPen facePen(m_faceColour, 1, wxSOLID);
404 wxBrush faceBrush(m_faceColour, wxSOLID);
405 wxPen mediumShadowPen(m_mediumShadowColour, 1, wxSOLID);
406 wxPen darkShadowPen(m_darkShadowColour, 1, wxSOLID);
407 wxPen lightShadowPen(m_lightShadowColour, 1, wxSOLID);
408 wxPen hilightPen(m_hilightColour, 1, wxSOLID);
409 wxPen blackPen(wxColour(0, 0, 0), 1, wxSOLID);
410 wxPen whitePen(wxColour(255, 255, 255), 1, wxSOLID);
411
412 if ( edge == wxSASH_LEFT || edge == wxSASH_RIGHT )
413 {
414 int sashPosition = 0;
415 if (edge == wxSASH_LEFT)
416 sashPosition = 0;
417 else
418 sashPosition = w - GetEdgeMargin(edge);
419
420 dc.SetPen(facePen);
421 dc.SetBrush(faceBrush);
422 dc.DrawRectangle(sashPosition, 0, GetEdgeMargin(edge), h);
423
424 if (GetWindowStyleFlag() & wxSW_3D)
425 {
426 if (edge == wxSASH_LEFT)
427 {
428 // Draw a black line on the left to indicate that the
429 // sash is raised
430 dc.SetPen(blackPen);
431 dc.DrawLine(GetEdgeMargin(edge), 0, GetEdgeMargin(edge), h);
432 }
433 else
434 {
435 // Draw a white line on the right to indicate that the
436 // sash is raised
437 dc.SetPen(whitePen);
438 dc.DrawLine(w - GetEdgeMargin(edge), 0, w - GetEdgeMargin(edge), h);
439 }
440 }
441 }
442 else // top or bottom
443 {
444 int sashPosition = 0;
445 if (edge == wxSASH_TOP)
446 sashPosition = 0;
447 else
448 sashPosition = h - GetEdgeMargin(edge);
449
450 dc.SetPen(facePen);
451 dc.SetBrush(faceBrush);
452 dc.DrawRectangle(0, sashPosition, w, GetEdgeMargin(edge));
453
454 if (GetWindowStyleFlag() & wxSW_3D)
455 {
456 if (edge == wxSASH_BOTTOM)
457 {
458 // Draw a black line on the bottom to indicate that the
459 // sash is raised
460 dc.SetPen(blackPen);
461 dc.DrawLine(0, h - GetEdgeMargin(edge), w, h - GetEdgeMargin(edge));
462 }
463 else
464 {
465 // Draw a white line on the top to indicate that the
466 // sash is raised
467 dc.SetPen(whitePen);
468 dc.DrawLine(0, GetEdgeMargin(edge), w, GetEdgeMargin(edge));
469 }
470 }
471 }
472
473 dc.SetPen(wxNullPen);
474 dc.SetBrush(wxNullBrush);
475 }
476
477 // Draw the sash tracker (for whilst moving the sash)
478 void wxSashWindow::DrawSashTracker(wxSashEdgePosition edge, int x, int y)
479 {
480 int w, h;
481 GetClientSize(&w, &h);
482
483 wxScreenDC screenDC;
484 int x1, y1;
485 int x2, y2;
486
487 if ( edge == wxSASH_LEFT || edge == wxSASH_RIGHT )
488 {
489 x1 = x; y1 = 2;
490 x2 = x; y2 = h-2;
491
492 if ( (edge == wxSASH_LEFT) && (x1 > w) )
493 {
494 x1 = w; x2 = w;
495 }
496 else if ( (edge == wxSASH_RIGHT) && (x1 < 0) )
497 {
498 x1 = 0; x2 = 0;
499 }
500 }
501 else
502 {
503 x1 = 2; y1 = y;
504 x2 = w-2; y2 = y;
505
506 if ( (edge == wxSASH_TOP) && (y1 > h) )
507 {
508 y1 = h;
509 y2 = h;
510 }
511 else if ( (edge == wxSASH_BOTTOM) && (y1 < 0) )
512 {
513 y1 = 0;
514 y2 = 0;
515 }
516 }
517
518 ClientToScreen(&x1, &y1);
519 ClientToScreen(&x2, &y2);
520
521 wxPen sashTrackerPen(*wxBLACK, 2, wxSOLID);
522
523 screenDC.SetLogicalFunction(wxINVERT);
524 screenDC.SetPen(sashTrackerPen);
525 screenDC.SetBrush(*wxTRANSPARENT_BRUSH);
526
527 screenDC.DrawLine(x1, y1, x2, y2);
528
529 screenDC.SetLogicalFunction(wxCOPY);
530
531 screenDC.SetPen(wxNullPen);
532 screenDC.SetBrush(wxNullBrush);
533 }
534
535 // Position and size subwindows.
536 // Note that the border size applies to each subwindow, not
537 // including the edges next to the sash.
538 void wxSashWindow::SizeWindows()
539 {
540 int cw, ch;
541 GetClientSize(&cw, &ch);
542
543 if (GetChildren().Number() == 1)
544 {
545 wxWindow* child = (wxWindow*) (GetChildren().First()->Data());
546
547 int x = 0;
548 int y = 0;
549 int width = cw;
550 int height = ch;
551
552 // Top
553 if (m_sashes[0].m_show)
554 {
555 y = m_borderSize;
556 height -= m_borderSize;
557 }
558 y += m_extraBorderSize;
559
560 // Left
561 if (m_sashes[3].m_show)
562 {
563 x = m_borderSize;
564 width -= m_borderSize;
565 }
566 x += m_extraBorderSize;
567
568 // Right
569 if (m_sashes[1].m_show)
570 {
571 width -= m_borderSize;
572 }
573 width -= 2*m_extraBorderSize;
574
575 // Bottom
576 if (m_sashes[2].m_show)
577 {
578 height -= m_borderSize;
579 }
580 height -= 2*m_extraBorderSize;
581
582 child->SetSize(x, y, width, height);
583 }
584 else if (GetChildren().Number() > 1)
585 {
586 // Perhaps multiple children are themselves sash windows.
587 // TODO: this doesn't really work because the subwindows sizes/positions
588 // must be set to leave a gap for the parent's sash (hit-test and decorations).
589 // Perhaps we can allow for this within LayoutWindow, testing whether the parent
590 // is a sash window, and if so, allowing some space for the edges.
591 wxLayoutAlgorithm layout;
592 layout.LayoutWindow(this);
593 }
594
595 wxClientDC dc(this);
596 DrawBorders(dc);
597 DrawSashes(dc);
598 }
599
600 // Initialize colours
601 void wxSashWindow::InitColours()
602 {
603 // Shadow colours
604 #if defined(__WIN95__)
605 m_faceColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE);
606 m_mediumShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW);
607 m_darkShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW);
608 m_lightShadowColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT);
609 m_hilightColour = wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DHILIGHT);
610 #else
611 m_faceColour = *(wxTheColourDatabase->FindColour("LIGHT GREY"));
612 m_mediumShadowColour = *(wxTheColourDatabase->FindColour("GREY"));
613 m_darkShadowColour = *(wxTheColourDatabase->FindColour("BLACK"));
614 m_lightShadowColour = *(wxTheColourDatabase->FindColour("LIGHT GREY"));
615 m_hilightColour = *(wxTheColourDatabase->FindColour("WHITE"));
616 #endif
617 }
618
619 void wxSashWindow::SetSashVisible(wxSashEdgePosition edge, bool sash)
620 {
621 m_sashes[edge].m_show = sash;
622 if (sash)
623 m_sashes[edge].m_margin = m_borderSize;
624 else
625 m_sashes[edge].m_margin = 0;
626 }
627