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