1 /////////////////////////////////////////////////////////////////////////////
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
10 // Copyright: (c) Julian Smart
11 // Licence: wxWindows license
12 /////////////////////////////////////////////////////////////////////////////
15 #pragma implementation "sashwin.h"
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
30 #error "Thisfile requires wxUSE_SASH to be defined."
36 #include "wx/string.h"
37 #include "wx/dcscreen.h"
38 #include "wx/sashwin.h"
39 #include "wx/laywin.h"
41 #if !USE_SHARED_LIBRARY
42 IMPLEMENT_DYNAMIC_CLASS(wxSashWindow
, wxWindow
)
43 IMPLEMENT_DYNAMIC_CLASS(wxSashEvent
, wxCommandEvent
)
45 BEGIN_EVENT_TABLE(wxSashWindow
, wxWindow
)
46 EVT_PAINT(wxSashWindow::OnPaint
)
47 EVT_SIZE(wxSashWindow::OnSize
)
48 EVT_MOUSE_EVENTS(wxSashWindow::OnMouseEvent
)
52 wxSashWindow::wxSashWindow()
54 m_draggingEdge
= wxSASH_NONE
;
55 m_dragMode
= wxSASH_DRAG_NONE
;
61 m_extraBorderSize
= 0;
62 m_sashCursorWE
= NULL
;
63 m_sashCursorNS
= NULL
;
65 m_minimumPaneSizeX
= 0;
66 m_minimumPaneSizeY
= 0;
67 m_maximumPaneSizeX
= 10000;
68 m_maximumPaneSizeY
= 10000;
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
)
75 m_draggingEdge
= wxSASH_NONE
;
76 m_dragMode
= wxSASH_DRAG_NONE
;
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
);
90 // Eventually, we'll respond to colour change messages
94 wxSashWindow::~wxSashWindow()
96 delete m_sashCursorWE
;
97 delete m_sashCursorNS
;
100 void wxSashWindow::OnPaint(wxPaintEvent
& WXUNUSED(event
))
104 // if ( m_borderSize > 0 )
110 void wxSashWindow::OnMouseEvent(wxMouseEvent
& event
)
113 event
.Position(&x
, &y
);
115 wxSashEdgePosition sashHit
= SashHitTest(x
, y
);
119 SetCursor(* wxSTANDARD_CURSOR
);
122 SetCursor(wxCursor());
125 if (event
.LeftDown())
127 if ( sashHit
!= wxSASH_NONE
)
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;
139 while (parent
&& !parent
->IsKindOf(CLASSINFO(wxDialog
)) &&
140 !parent
->IsKindOf(CLASSINFO(wxFrame
)))
141 parent
= parent
->GetParent();
143 wxScreenDC::StartDrawingOnTop(parent
);
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
;
154 else if ( event
.LeftUp() && m_dragMode
== wxSASH_DRAG_LEFT_DOWN
)
156 // Wasn't a proper drag
158 wxScreenDC::EndDrawingOnTop();
159 m_dragMode
= wxSASH_DRAG_NONE
;
160 m_draggingEdge
= wxSASH_NONE
;
162 else if (event
.LeftUp() && m_dragMode
== wxSASH_DRAG_DRAGGING
)
164 // We can stop dragging now and see what we've got.
165 m_dragMode
= wxSASH_DRAG_NONE
;
168 DrawSashTracker(m_draggingEdge
, m_oldX
, m_oldY
);
170 // End drawing on top (frees the window used for drawing
172 wxScreenDC::EndDrawingOnTop();
177 GetPosition(&xp
, &yp
);
179 wxSashEdgePosition edge
= m_draggingEdge
;
180 m_draggingEdge
= wxSASH_NONE
;
185 wxSashDragStatus status
= wxSASH_STATUS_OK
;
191 status
= wxSASH_STATUS_OUT_OF_RANGE
;
192 int newHeight
= (yp
+ h
- y
);
193 newHeight
=wxMax(newHeight
,m_minimumPaneSizeY
);
194 newHeight
=wxMin(newHeight
,m_maximumPaneSizeY
);
195 dragRect
= wxRect(xp
, (yp
+ h
) - newHeight
, w
, newHeight
);
201 status
= wxSASH_STATUS_OUT_OF_RANGE
;
203 newHeight
=wxMax(newHeight
,m_minimumPaneSizeY
);
204 newHeight
=wxMin(newHeight
,m_maximumPaneSizeY
);
205 dragRect
= wxRect(xp
, yp
, w
, newHeight
);
211 status
= wxSASH_STATUS_OUT_OF_RANGE
;
212 int newWidth
= (w
- x
);
213 newWidth
=wxMax(newWidth
,m_minimumPaneSizeX
);
214 newWidth
=wxMin(newWidth
,m_maximumPaneSizeX
);
215 dragRect
= wxRect((xp
+ w
) - newWidth
, yp
, newWidth
, h
);
221 status
= wxSASH_STATUS_OUT_OF_RANGE
;
223 newWidth
=wxMax(newWidth
,m_minimumPaneSizeX
);
224 newWidth
=wxMin(newWidth
,m_maximumPaneSizeX
);
225 dragRect
= wxRect(xp
, yp
, newWidth
, h
);
234 wxSashEvent
event(GetId(), edge
);
235 event
.SetEventObject(this);
236 event
.SetDragStatus(status
);
237 event
.SetDragRect(dragRect
);
238 GetEventHandler()->ProcessEvent(event
);
240 else if (event
.Moving() && !event
.Dragging())
242 // Just change the cursor if required
243 if ( sashHit
!= wxSASH_NONE
)
245 if ( (sashHit
== wxSASH_LEFT
) || (sashHit
== wxSASH_RIGHT
) )
247 SetCursor(*m_sashCursorWE
);
251 SetCursor(*m_sashCursorNS
);
255 else if ( event
.Dragging() &&
256 ((m_dragMode
== wxSASH_DRAG_DRAGGING
) || (m_dragMode
== wxSASH_DRAG_LEFT_DOWN
))
259 if ( (m_draggingEdge
== wxSASH_LEFT
) || (m_draggingEdge
== wxSASH_RIGHT
) )
261 SetCursor(*m_sashCursorWE
);
265 SetCursor(*m_sashCursorNS
);
268 if (m_dragMode
== wxSASH_DRAG_LEFT_DOWN
)
270 m_dragMode
= wxSASH_DRAG_DRAGGING
;
271 DrawSashTracker(m_draggingEdge
, x
, y
);
275 if ( m_dragMode
== wxSASH_DRAG_DRAGGING
)
278 DrawSashTracker(m_draggingEdge
, m_oldX
, m_oldY
);
281 DrawSashTracker(m_draggingEdge
, x
, y
);
287 else if ( event
.LeftDClick() )
296 void wxSashWindow::OnSize(wxSizeEvent
& WXUNUSED(event
))
301 wxSashEdgePosition
wxSashWindow::SashHitTest(int x
, int y
, int WXUNUSED(tolerance
))
304 GetClientSize(& cx
, & cy
);
307 for (i
= 0; i
< 4; i
++)
309 wxSashEdge
& edge
= m_sashes
[i
];
310 wxSashEdgePosition position
= (wxSashEdgePosition
) i
;
318 if (y
>= 0 && y
<= GetEdgeMargin(position
))
324 if ((x
>= cx
- GetEdgeMargin(position
)) && (x
<= cx
))
330 if ((y
>= cy
- GetEdgeMargin(position
)) && (y
<= cy
))
331 return wxSASH_BOTTOM
;
336 if ((x
<= GetEdgeMargin(position
)) && (x
>= 0))
350 // Draw 3D effect borders
351 void wxSashWindow::DrawBorders(wxDC
& dc
)
354 GetClientSize(&w
, &h
);
356 wxPen
mediumShadowPen(m_mediumShadowColour
, 1, wxSOLID
);
357 wxPen
darkShadowPen(m_darkShadowColour
, 1, wxSOLID
);
358 wxPen
lightShadowPen(m_lightShadowColour
, 1, wxSOLID
);
359 wxPen
hilightPen(m_hilightColour
, 1, wxSOLID
);
361 if ( GetWindowStyleFlag() & wxSP_3D
)
363 dc
.SetPen(mediumShadowPen
);
364 dc
.DrawLine(0, 0, w
-1, 0);
365 dc
.DrawLine(0, 0, 0, h
- 1);
367 dc
.SetPen(darkShadowPen
);
368 dc
.DrawLine(1, 1, w
-2, 1);
369 dc
.DrawLine(1, 1, 1, h
-2);
371 dc
.SetPen(hilightPen
);
372 dc
.DrawLine(0, h
-1, w
-1, h
-1);
373 dc
.DrawLine(w
-1, 0, w
-1, h
); // Surely the maximum y pos. should be h - 1.
374 /// Anyway, h is required for MSW.
376 dc
.SetPen(lightShadowPen
);
377 dc
.DrawLine(w
-2, 1, w
-2, h
-2); // Right hand side
378 dc
.DrawLine(1, h
-2, w
-1, h
-2); // Bottom
380 else if ( GetWindowStyleFlag() & wxSP_BORDER
)
382 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
383 dc
.SetPen(*wxBLACK_PEN
);
384 dc
.DrawRectangle(0, 0, w
-1, h
-1);
387 dc
.SetPen(wxNullPen
);
388 dc
.SetBrush(wxNullBrush
);
391 void wxSashWindow::DrawSashes(wxDC
& dc
)
394 for (i
= 0; i
< 4; i
++)
395 if (m_sashes
[i
].m_show
)
396 DrawSash((wxSashEdgePosition
) i
, dc
);
400 void wxSashWindow::DrawSash(wxSashEdgePosition edge
, wxDC
& dc
)
403 GetClientSize(&w
, &h
);
405 wxPen
facePen(m_faceColour
, 1, wxSOLID
);
406 wxBrush
faceBrush(m_faceColour
, wxSOLID
);
407 wxPen
mediumShadowPen(m_mediumShadowColour
, 1, wxSOLID
);
408 wxPen
darkShadowPen(m_darkShadowColour
, 1, wxSOLID
);
409 wxPen
lightShadowPen(m_lightShadowColour
, 1, wxSOLID
);
410 wxPen
hilightPen(m_hilightColour
, 1, wxSOLID
);
411 wxPen
blackPen(wxColour(0, 0, 0), 1, wxSOLID
);
412 wxPen
whitePen(wxColour(255, 255, 255), 1, wxSOLID
);
414 if ( edge
== wxSASH_LEFT
|| edge
== wxSASH_RIGHT
)
416 int sashPosition
= 0;
417 if (edge
== wxSASH_LEFT
)
420 sashPosition
= w
- GetEdgeMargin(edge
);
423 dc
.SetBrush(faceBrush
);
424 dc
.DrawRectangle(sashPosition
, 0, GetEdgeMargin(edge
), h
);
426 if (GetWindowStyleFlag() & wxSW_3D
)
428 if (edge
== wxSASH_LEFT
)
430 // Draw a black line on the left to indicate that the
433 dc
.DrawLine(GetEdgeMargin(edge
), 0, GetEdgeMargin(edge
), h
);
437 // Draw a white line on the right to indicate that the
440 dc
.DrawLine(w
- GetEdgeMargin(edge
), 0, w
- GetEdgeMargin(edge
), h
);
444 else // top or bottom
446 int sashPosition
= 0;
447 if (edge
== wxSASH_TOP
)
450 sashPosition
= h
- GetEdgeMargin(edge
);
453 dc
.SetBrush(faceBrush
);
454 dc
.DrawRectangle(0, sashPosition
, w
, GetEdgeMargin(edge
));
456 if (GetWindowStyleFlag() & wxSW_3D
)
458 if (edge
== wxSASH_BOTTOM
)
460 // Draw a black line on the bottom to indicate that the
463 dc
.DrawLine(0, h
- GetEdgeMargin(edge
), w
, h
- GetEdgeMargin(edge
));
467 // Draw a white line on the top to indicate that the
470 dc
.DrawLine(0, GetEdgeMargin(edge
), w
, GetEdgeMargin(edge
));
475 dc
.SetPen(wxNullPen
);
476 dc
.SetBrush(wxNullBrush
);
479 // Draw the sash tracker (for whilst moving the sash)
480 void wxSashWindow::DrawSashTracker(wxSashEdgePosition edge
, int x
, int y
)
483 GetClientSize(&w
, &h
);
489 if ( edge
== wxSASH_LEFT
|| edge
== wxSASH_RIGHT
)
494 if ( (edge
== wxSASH_LEFT
) && (x1
> w
) )
498 else if ( (edge
== wxSASH_RIGHT
) && (x1
< 0) )
508 if ( (edge
== wxSASH_TOP
) && (y1
> h
) )
513 else if ( (edge
== wxSASH_BOTTOM
) && (y1
< 0) )
520 ClientToScreen(&x1
, &y1
);
521 ClientToScreen(&x2
, &y2
);
523 wxPen
sashTrackerPen(*wxBLACK
, 2, wxSOLID
);
525 screenDC
.SetLogicalFunction(wxINVERT
);
526 screenDC
.SetPen(sashTrackerPen
);
527 screenDC
.SetBrush(*wxTRANSPARENT_BRUSH
);
529 screenDC
.DrawLine(x1
, y1
, x2
, y2
);
531 screenDC
.SetLogicalFunction(wxCOPY
);
533 screenDC
.SetPen(wxNullPen
);
534 screenDC
.SetBrush(wxNullBrush
);
537 // Position and size subwindows.
538 // Note that the border size applies to each subwindow, not
539 // including the edges next to the sash.
540 void wxSashWindow::SizeWindows()
543 GetClientSize(&cw
, &ch
);
545 if (GetChildren().Number() == 1)
547 wxWindow
* child
= (wxWindow
*) (GetChildren().First()->Data());
555 if (m_sashes
[0].m_show
)
558 height
-= m_borderSize
;
560 y
+= m_extraBorderSize
;
563 if (m_sashes
[3].m_show
)
566 width
-= m_borderSize
;
568 x
+= m_extraBorderSize
;
571 if (m_sashes
[1].m_show
)
573 width
-= m_borderSize
;
575 width
-= 2*m_extraBorderSize
;
578 if (m_sashes
[2].m_show
)
580 height
-= m_borderSize
;
582 height
-= 2*m_extraBorderSize
;
584 child
->SetSize(x
, y
, width
, height
);
586 else if (GetChildren().Number() > 1)
588 // Perhaps multiple children are themselves sash windows.
589 // TODO: this doesn't really work because the subwindows sizes/positions
590 // must be set to leave a gap for the parent's sash (hit-test and decorations).
591 // Perhaps we can allow for this within LayoutWindow, testing whether the parent
592 // is a sash window, and if so, allowing some space for the edges.
593 wxLayoutAlgorithm layout
;
594 layout
.LayoutWindow(this);
602 // Initialize colours
603 void wxSashWindow::InitColours()
606 #if defined(__WIN95__)
607 m_faceColour
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DFACE
);
608 m_mediumShadowColour
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DSHADOW
);
609 m_darkShadowColour
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW
);
610 m_lightShadowColour
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT
);
611 m_hilightColour
= wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DHILIGHT
);
613 m_faceColour
= *(wxTheColourDatabase
->FindColour("LIGHT GREY"));
614 m_mediumShadowColour
= *(wxTheColourDatabase
->FindColour("GREY"));
615 m_darkShadowColour
= *(wxTheColourDatabase
->FindColour("BLACK"));
616 m_lightShadowColour
= *(wxTheColourDatabase
->FindColour("LIGHT GREY"));
617 m_hilightColour
= *(wxTheColourDatabase
->FindColour("WHITE"));
621 void wxSashWindow::SetSashVisible(wxSashEdgePosition edge
, bool sash
)
623 m_sashes
[edge
].m_show
= sash
;
625 m_sashes
[edge
].m_margin
= m_borderSize
;
627 m_sashes
[edge
].m_margin
= 0;