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"
34 #include "wx/string.h"
35 #include "wx/dcscreen.h"
36 #include "wx/sashwin.h"
37 #include "wx/laywin.h"
39 IMPLEMENT_DYNAMIC_CLASS(wxSashWindow
, wxWindow
)
40 IMPLEMENT_DYNAMIC_CLASS(wxSashEvent
, wxCommandEvent
)
42 BEGIN_EVENT_TABLE(wxSashWindow
, wxWindow
)
43 EVT_PAINT(wxSashWindow
::OnPaint
)
44 EVT_SIZE(wxSashWindow
::OnSize
)
45 EVT_MOUSE_EVENTS(wxSashWindow
::OnMouseEvent
)
48 bool wxSashWindow
::Create(wxWindow
*parent
, wxWindowID id
, const wxPoint
& pos
,
49 const wxSize
& size
, long style
, const wxString
& name
)
51 return wxWindow
::Create(parent
, id
, pos
, size
, style
, name
);
54 wxSashWindow
::~wxSashWindow()
56 delete m_sashCursorWE
;
57 delete m_sashCursorNS
;
60 void wxSashWindow
::Init()
62 m_draggingEdge
= wxSASH_NONE
;
63 m_dragMode
= wxSASH_DRAG_NONE
;
69 m_extraBorderSize
= 0;
70 m_minimumPaneSizeX
= 0;
71 m_minimumPaneSizeY
= 0;
72 m_maximumPaneSizeX
= 10000;
73 m_maximumPaneSizeY
= 10000;
74 m_sashCursorWE
= new wxCursor(wxCURSOR_SIZEWE
);
75 m_sashCursorNS
= new wxCursor(wxCURSOR_SIZENS
);
76 m_mouseCaptured
= FALSE
;
78 // Eventually, we'll respond to colour change messages
82 void wxSashWindow
::OnPaint(wxPaintEvent
& WXUNUSED(event
))
86 // if ( m_borderSize > 0 )
92 void wxSashWindow
::OnMouseEvent(wxMouseEvent
& event
)
95 event
.GetPosition(&x
, &y
);
97 wxSashEdgePosition sashHit
= SashHitTest(x
, y
);
100 #if defined(__WXMOTIF__) || defined(__WXGTK__)
101 SetCursor(* wxSTANDARD_CURSOR
);
104 SetCursor(wxNullCursor
);
107 if (event
.LeftDown())
110 m_mouseCaptured
= TRUE
;
112 if ( sashHit
!= wxSASH_NONE
)
114 // Required for X to specify that
115 // that we wish to draw on top of all windows
116 // - and we optimise by specifying the area
117 // for creating the overlap window.
118 // Find the first frame or dialog and use this to specify
119 // the area to draw on.
120 wxWindow
* parent
= this;
122 while (parent
&& !parent
->IsKindOf(CLASSINFO(wxDialog
)) &&
123 !parent
->IsKindOf(CLASSINFO(wxFrame
)))
124 parent
= parent
->GetParent();
126 wxScreenDC
::StartDrawingOnTop(parent
);
128 // We don't say we're dragging yet; we leave that
129 // decision for the Dragging() branch, to ensure
130 // the user has dragged a little bit.
131 m_dragMode
= wxSASH_DRAG_LEFT_DOWN
;
132 m_draggingEdge
= sashHit
;
136 if ( (sashHit
== wxSASH_LEFT
) || (sashHit
== wxSASH_RIGHT
) )
138 SetCursor(*m_sashCursorWE
);
142 SetCursor(*m_sashCursorNS
);
146 else if ( event
.LeftUp() && m_dragMode
== wxSASH_DRAG_LEFT_DOWN
)
148 // Wasn't a proper drag
151 m_mouseCaptured
= FALSE
;
153 wxScreenDC
::EndDrawingOnTop();
154 m_dragMode
= wxSASH_DRAG_NONE
;
155 m_draggingEdge
= wxSASH_NONE
;
157 else if (event
.LeftUp() && m_dragMode
== wxSASH_DRAG_DRAGGING
)
159 // We can stop dragging now and see what we've got.
160 m_dragMode
= wxSASH_DRAG_NONE
;
163 m_mouseCaptured
= FALSE
;
166 DrawSashTracker(m_draggingEdge
, m_oldX
, m_oldY
);
168 // End drawing on top (frees the window used for drawing
170 wxScreenDC
::EndDrawingOnTop();
175 GetPosition(&xp
, &yp
);
177 wxSashEdgePosition edge
= m_draggingEdge
;
178 m_draggingEdge
= wxSASH_NONE
;
181 wxSashDragStatus status
= wxSASH_STATUS_OK
;
183 // the new height and width of the window - if -1, it didn't change
187 // NB: x and y may be negative and they're relative to the sash window
188 // upper left corner, while xp and yp are expressed in the parent
189 // window system of coordinates, so adjust them! After this
190 // adjustment, all coordinates are relative to the parent window.
199 // top sash shouldn't get below the bottom one
200 status
= wxSASH_STATUS_OUT_OF_RANGE
;
204 newHeight
= h
- (y
- yp
);
211 // bottom sash shouldn't get above the top one
212 status
= wxSASH_STATUS_OUT_OF_RANGE
;
223 // left sash shouldn't get beyond the right one
224 status
= wxSASH_STATUS_OUT_OF_RANGE
;
228 newWidth
= w
- (x
- xp
);
235 // and the right sash, finally, shouldn't be beyond the
237 status
= wxSASH_STATUS_OUT_OF_RANGE
;
246 // can this happen at all?
250 if ( newHeight
== -1 )
257 // make sure it's in m_minimumPaneSizeY..m_maximumPaneSizeY range
258 newHeight
= wxMax(newHeight
, m_minimumPaneSizeY
);
259 newHeight
= wxMin(newHeight
, m_maximumPaneSizeY
);
262 if ( newWidth
== -1 )
269 // make sure it's in m_minimumPaneSizeY..m_maximumPaneSizeY range
270 newWidth
= wxMax(newWidth
, m_minimumPaneSizeX
);
271 newWidth
= wxMin(newWidth
, m_maximumPaneSizeX
);
274 dragRect
= wxRect(x
, y
, newWidth
, newHeight
);
276 wxSashEvent
event(GetId(), edge
);
277 event
.SetEventObject(this);
278 event
.SetDragStatus(status
);
279 event
.SetDragRect(dragRect
);
280 GetEventHandler()->ProcessEvent(event
);
282 else if ( event
.LeftUp() )
286 m_mouseCaptured
= FALSE
;
288 else if (event
.Moving() && !event
.Dragging())
290 // Just change the cursor if required
291 if ( sashHit
!= wxSASH_NONE
)
293 if ( (sashHit
== wxSASH_LEFT
) || (sashHit
== wxSASH_RIGHT
) )
295 SetCursor(*m_sashCursorWE
);
299 SetCursor(*m_sashCursorNS
);
304 SetCursor(wxNullCursor
);
307 else if ( event
.Dragging() &&
308 ((m_dragMode
== wxSASH_DRAG_DRAGGING
) ||
309 (m_dragMode
== wxSASH_DRAG_LEFT_DOWN
)) )
311 if ( (m_draggingEdge
== wxSASH_LEFT
) || (m_draggingEdge
== wxSASH_RIGHT
) )
313 SetCursor(*m_sashCursorWE
);
317 SetCursor(*m_sashCursorNS
);
320 if (m_dragMode
== wxSASH_DRAG_LEFT_DOWN
)
322 m_dragMode
= wxSASH_DRAG_DRAGGING
;
323 DrawSashTracker(m_draggingEdge
, x
, y
);
327 if ( m_dragMode
== wxSASH_DRAG_DRAGGING
)
330 DrawSashTracker(m_draggingEdge
, m_oldX
, m_oldY
);
333 DrawSashTracker(m_draggingEdge
, x
, y
);
339 else if ( event
.LeftDClick() )
348 void wxSashWindow
::OnSize(wxSizeEvent
& WXUNUSED(event
))
353 wxSashEdgePosition wxSashWindow
::SashHitTest(int x
, int y
, int WXUNUSED(tolerance
))
356 GetClientSize(& cx
, & cy
);
359 for (i
= 0; i
< 4; i
++)
361 wxSashEdge
& edge
= m_sashes
[i
];
362 wxSashEdgePosition position
= (wxSashEdgePosition
) i
;
370 if (y
>= 0 && y
<= GetEdgeMargin(position
))
376 if ((x
>= cx
- GetEdgeMargin(position
)) && (x
<= cx
))
382 if ((y
>= cy
- GetEdgeMargin(position
)) && (y
<= cy
))
383 return wxSASH_BOTTOM
;
388 if ((x
<= GetEdgeMargin(position
)) && (x
>= 0))
402 // Draw 3D effect borders
403 void wxSashWindow
::DrawBorders(wxDC
& dc
)
406 GetClientSize(&w
, &h
);
408 wxPen
mediumShadowPen(m_mediumShadowColour
, 1, wxSOLID
);
409 wxPen
darkShadowPen(m_darkShadowColour
, 1, wxSOLID
);
410 wxPen
lightShadowPen(m_lightShadowColour
, 1, wxSOLID
);
411 wxPen
hilightPen(m_hilightColour
, 1, wxSOLID
);
413 if ( GetWindowStyleFlag() & wxSW_3DBORDER
)
415 dc
.SetPen(mediumShadowPen
);
416 dc
.DrawLine(0, 0, w
-1, 0);
417 dc
.DrawLine(0, 0, 0, h
- 1);
419 dc
.SetPen(darkShadowPen
);
420 dc
.DrawLine(1, 1, w
-2, 1);
421 dc
.DrawLine(1, 1, 1, h
-2);
423 dc
.SetPen(hilightPen
);
424 dc
.DrawLine(0, h
-1, w
-1, h
-1);
425 dc
.DrawLine(w
-1, 0, w
-1, h
); // Surely the maximum y pos. should be h - 1.
426 /// Anyway, h is required for MSW.
428 dc
.SetPen(lightShadowPen
);
429 dc
.DrawLine(w
-2, 1, w
-2, h
-2); // Right hand side
430 dc
.DrawLine(1, h
-2, w
-1, h
-2); // Bottom
432 else if ( GetWindowStyleFlag() & wxSW_BORDER
)
434 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
435 dc
.SetPen(*wxBLACK_PEN
);
436 dc
.DrawRectangle(0, 0, w
-1, h
-1);
439 dc
.SetPen(wxNullPen
);
440 dc
.SetBrush(wxNullBrush
);
443 void wxSashWindow
::DrawSashes(wxDC
& dc
)
446 for (i
= 0; i
< 4; i
++)
447 if (m_sashes
[i
].m_show
)
448 DrawSash((wxSashEdgePosition
) i
, dc
);
452 void wxSashWindow
::DrawSash(wxSashEdgePosition edge
, wxDC
& dc
)
455 GetClientSize(&w
, &h
);
457 wxPen
facePen(m_faceColour
, 1, wxSOLID
);
458 wxBrush
faceBrush(m_faceColour
, wxSOLID
);
459 wxPen
mediumShadowPen(m_mediumShadowColour
, 1, wxSOLID
);
460 wxPen
darkShadowPen(m_darkShadowColour
, 1, wxSOLID
);
461 wxPen
lightShadowPen(m_lightShadowColour
, 1, wxSOLID
);
462 wxPen
hilightPen(m_hilightColour
, 1, wxSOLID
);
463 wxPen
blackPen(wxColour(0, 0, 0), 1, wxSOLID
);
464 wxPen
whitePen(wxColour(255, 255, 255), 1, wxSOLID
);
466 if ( edge
== wxSASH_LEFT
|| edge
== wxSASH_RIGHT
)
468 int sashPosition
= 0;
469 if (edge
== wxSASH_LEFT
)
472 sashPosition
= w
- GetEdgeMargin(edge
);
475 dc
.SetBrush(faceBrush
);
476 dc
.DrawRectangle(sashPosition
, 0, GetEdgeMargin(edge
), h
);
478 if (GetWindowStyleFlag() & wxSW_3DSASH
)
480 if (edge
== wxSASH_LEFT
)
482 // Draw a dark grey line on the left to indicate that the
484 dc
.SetPen(mediumShadowPen
);
485 dc
.DrawLine(GetEdgeMargin(edge
), 0, GetEdgeMargin(edge
), h
);
489 // Draw a highlight line on the right to indicate that the
491 dc
.SetPen(hilightPen
);
492 dc
.DrawLine(w
- GetEdgeMargin(edge
), 0, w
- GetEdgeMargin(edge
), h
);
496 else // top or bottom
498 int sashPosition
= 0;
499 if (edge
== wxSASH_TOP
)
502 sashPosition
= h
- GetEdgeMargin(edge
);
505 dc
.SetBrush(faceBrush
);
506 dc
.DrawRectangle(0, sashPosition
, w
, GetEdgeMargin(edge
));
508 if (GetWindowStyleFlag() & wxSW_3DSASH
)
510 if (edge
== wxSASH_BOTTOM
)
512 // Draw a highlight line on the bottom to indicate that the
514 dc
.SetPen(hilightPen
);
515 dc
.DrawLine(0, h
- GetEdgeMargin(edge
), w
, h
- GetEdgeMargin(edge
));
519 // Draw a drak grey line on the top to indicate that the
521 dc
.SetPen(mediumShadowPen
);
522 dc
.DrawLine(1, GetEdgeMargin(edge
), w
-1, GetEdgeMargin(edge
));
527 dc
.SetPen(wxNullPen
);
528 dc
.SetBrush(wxNullBrush
);
531 // Draw the sash tracker (for whilst moving the sash)
532 void wxSashWindow
::DrawSashTracker(wxSashEdgePosition edge
, int x
, int y
)
535 GetClientSize(&w
, &h
);
541 if ( edge
== wxSASH_LEFT
|| edge
== wxSASH_RIGHT
)
546 if ( (edge
== wxSASH_LEFT
) && (x1
> w
) )
550 else if ( (edge
== wxSASH_RIGHT
) && (x1
< 0) )
560 if ( (edge
== wxSASH_TOP
) && (y1
> h
) )
565 else if ( (edge
== wxSASH_BOTTOM
) && (y1
< 0) )
572 ClientToScreen(&x1
, &y1
);
573 ClientToScreen(&x2
, &y2
);
575 wxPen
sashTrackerPen(*wxBLACK
, 2, wxSOLID
);
577 screenDC
.SetLogicalFunction(wxINVERT
);
578 screenDC
.SetPen(sashTrackerPen
);
579 screenDC
.SetBrush(*wxTRANSPARENT_BRUSH
);
581 screenDC
.DrawLine(x1
, y1
, x2
, y2
);
583 screenDC
.SetLogicalFunction(wxCOPY
);
585 screenDC
.SetPen(wxNullPen
);
586 screenDC
.SetBrush(wxNullBrush
);
589 // Position and size subwindows.
590 // Note that the border size applies to each subwindow, not
591 // including the edges next to the sash.
592 void wxSashWindow
::SizeWindows()
595 GetClientSize(&cw
, &ch
);
597 if (GetChildren().Number() == 1)
599 wxWindow
* child
= (wxWindow
*) (GetChildren().First()->Data());
607 if (m_sashes
[0].m_show
)
610 height
-= m_borderSize
;
612 y
+= m_extraBorderSize
;
615 if (m_sashes
[3].m_show
)
618 width
-= m_borderSize
;
620 x
+= m_extraBorderSize
;
623 if (m_sashes
[1].m_show
)
625 width
-= m_borderSize
;
627 width
-= 2*m_extraBorderSize
;
630 if (m_sashes
[2].m_show
)
632 height
-= m_borderSize
;
634 height
-= 2*m_extraBorderSize
;
636 child
->SetSize(x
, y
, width
, height
);
638 else if (GetChildren().Number() > 1)
640 // Perhaps multiple children are themselves sash windows.
641 // TODO: this doesn't really work because the subwindows sizes/positions
642 // must be set to leave a gap for the parent's sash (hit-test and decorations).
643 // Perhaps we can allow for this within LayoutWindow, testing whether the parent
644 // is a sash window, and if so, allowing some space for the edges.
645 wxLayoutAlgorithm layout
;
646 layout
.LayoutWindow(this);
654 // Initialize colours
655 void wxSashWindow
::InitColours()
658 #if defined(__WIN95__)
659 m_faceColour
= wxSystemSettings
::GetSystemColour(wxSYS_COLOUR_3DFACE
);
660 m_mediumShadowColour
= wxSystemSettings
::GetSystemColour(wxSYS_COLOUR_3DSHADOW
);
661 m_darkShadowColour
= wxSystemSettings
::GetSystemColour(wxSYS_COLOUR_3DDKSHADOW
);
662 m_lightShadowColour
= wxSystemSettings
::GetSystemColour(wxSYS_COLOUR_3DLIGHT
);
663 m_hilightColour
= wxSystemSettings
::GetSystemColour(wxSYS_COLOUR_3DHILIGHT
);
665 m_faceColour
= *(wxTheColourDatabase
->FindColour("LIGHT GREY"));
666 m_mediumShadowColour
= *(wxTheColourDatabase
->FindColour("GREY"));
667 m_darkShadowColour
= *(wxTheColourDatabase
->FindColour("BLACK"));
668 m_lightShadowColour
= *(wxTheColourDatabase
->FindColour("LIGHT GREY"));
669 m_hilightColour
= *(wxTheColourDatabase
->FindColour("WHITE"));
673 void wxSashWindow
::SetSashVisible(wxSashEdgePosition edge
, bool sash
)
675 m_sashes
[edge
].m_show
= sash
;
677 m_sashes
[edge
].m_margin
= m_borderSize
;
679 m_sashes
[edge
].m_margin
= 0;