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" 
  28     #include "wx/dialog.h" 
  30     #include "wx/settings.h" 
  36 #include "wx/dcscreen.h" 
  37 #include "wx/sashwin.h" 
  38 #include "wx/laywin.h" 
  40 DEFINE_EVENT_TYPE(wxEVT_SASH_DRAGGED
) 
  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
) 
  50     EVT_SET_CURSOR(wxSashWindow::OnSetCursor
) 
  55 bool wxSashWindow::Create(wxWindow 
*parent
, wxWindowID id
, const wxPoint
& pos
, 
  56     const wxSize
& size
, long style
, const wxString
& name
) 
  58     return wxWindow::Create(parent
, id
, pos
, size
, style
, name
); 
  61 wxSashWindow::~wxSashWindow() 
  63     delete m_sashCursorWE
; 
  64     delete m_sashCursorNS
; 
  67 void wxSashWindow::Init() 
  69     m_draggingEdge 
= wxSASH_NONE
; 
  70     m_dragMode 
= wxSASH_DRAG_NONE
; 
  76     m_extraBorderSize 
= 0; 
  77     m_minimumPaneSizeX 
= 0; 
  78     m_minimumPaneSizeY 
= 0; 
  79     m_maximumPaneSizeX 
= 10000; 
  80     m_maximumPaneSizeY 
= 10000; 
  81     m_sashCursorWE 
= new wxCursor(wxCURSOR_SIZEWE
); 
  82     m_sashCursorNS 
= new wxCursor(wxCURSOR_SIZENS
); 
  83     m_mouseCaptured 
= FALSE
; 
  84     m_currentCursor 
= NULL
; 
  86     // Eventually, we'll respond to colour change messages 
  90 void wxSashWindow::OnPaint(wxPaintEvent
& WXUNUSED(event
)) 
  98 void wxSashWindow::OnMouseEvent(wxMouseEvent
& event
) 
 101     event
.GetPosition(&x
, &y
); 
 103     wxSashEdgePosition sashHit 
= SashHitTest(x
, y
); 
 105     if (event
.LeftDown()) 
 108         m_mouseCaptured 
= TRUE
; 
 110         if ( sashHit 
!= wxSASH_NONE 
) 
 112             // Required for X to specify that 
 113             // that we wish to draw on top of all windows 
 114             // - and we optimise by specifying the area 
 115             // for creating the overlap window. 
 116             // Find the first frame or dialog and use this to specify 
 117             // the area to draw on. 
 118             wxWindow
* parent 
= this; 
 120             while (parent 
&& !parent
->IsKindOf(CLASSINFO(wxDialog
)) && 
 121                              !parent
->IsKindOf(CLASSINFO(wxFrame
))) 
 122               parent 
= parent
->GetParent(); 
 124             wxScreenDC::StartDrawingOnTop(parent
); 
 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
; 
 134             if ( (sashHit 
== wxSASH_LEFT
) || (sashHit 
== wxSASH_RIGHT
) ) 
 136                 if (m_currentCursor 
!= m_sashCursorWE
) 
 138                     SetCursor(*m_sashCursorWE
); 
 140                 m_currentCursor 
= m_sashCursorWE
; 
 144                 if (m_currentCursor 
!= m_sashCursorNS
) 
 146                     SetCursor(*m_sashCursorNS
); 
 148                 m_currentCursor 
= m_sashCursorNS
; 
 152     else if ( event
.LeftUp() && m_dragMode 
== wxSASH_DRAG_LEFT_DOWN 
) 
 154         // Wasn't a proper drag 
 157         m_mouseCaptured 
= FALSE
; 
 159         wxScreenDC::EndDrawingOnTop(); 
 160         m_dragMode 
= wxSASH_DRAG_NONE
; 
 161         m_draggingEdge 
= wxSASH_NONE
; 
 163     else if (event
.LeftUp() && m_dragMode 
== wxSASH_DRAG_DRAGGING
) 
 165         // We can stop dragging now and see what we've got. 
 166         m_dragMode 
= wxSASH_DRAG_NONE
; 
 169         m_mouseCaptured 
= FALSE
; 
 172         DrawSashTracker(m_draggingEdge
, m_oldX
, m_oldY
); 
 174         // End drawing on top (frees the window used for drawing 
 176         wxScreenDC::EndDrawingOnTop(); 
 181         GetPosition(&xp
, &yp
); 
 183         wxSashEdgePosition edge 
= m_draggingEdge
; 
 184         m_draggingEdge 
= wxSASH_NONE
; 
 187         wxSashDragStatus status 
= wxSASH_STATUS_OK
; 
 189         // the new height and width of the window - if -1, it didn't change 
 193         // NB: x and y may be negative and they're relative to the sash window 
 194         //     upper left corner, while xp and yp are expressed in the parent 
 195         //     window system of coordinates, so adjust them! After this 
 196         //     adjustment, all coordinates are relative to the parent window. 
 205                     // top sash shouldn't get below the bottom one 
 206                     status 
= wxSASH_STATUS_OUT_OF_RANGE
; 
 210                     newHeight 
= h 
- (y 
- yp
); 
 217                     // bottom sash shouldn't get above the top one 
 218                     status 
= wxSASH_STATUS_OUT_OF_RANGE
; 
 229                     // left sash shouldn't get beyond the right one 
 230                     status 
= wxSASH_STATUS_OUT_OF_RANGE
; 
 234                     newWidth 
= w 
- (x 
- xp
); 
 241                     // and the right sash, finally, shouldn't be beyond the 
 243                     status 
= wxSASH_STATUS_OUT_OF_RANGE
; 
 252                 // can this happen at all? 
 256         if ( newHeight 
== -1 ) 
 263             // make sure it's in m_minimumPaneSizeY..m_maximumPaneSizeY range 
 264             newHeight 
= wxMax(newHeight
, m_minimumPaneSizeY
); 
 265             newHeight 
= wxMin(newHeight
, m_maximumPaneSizeY
); 
 268         if ( newWidth 
== -1 ) 
 275             // make sure it's in m_minimumPaneSizeY..m_maximumPaneSizeY range 
 276             newWidth 
= wxMax(newWidth
, m_minimumPaneSizeX
); 
 277             newWidth 
= wxMin(newWidth
, m_maximumPaneSizeX
); 
 280         dragRect 
= wxRect(x
, y
, newWidth
, newHeight
); 
 282         wxSashEvent 
event(GetId(), edge
); 
 283         event
.SetEventObject(this); 
 284         event
.SetDragStatus(status
); 
 285         event
.SetDragRect(dragRect
); 
 286         GetEventHandler()->ProcessEvent(event
); 
 288     else if ( event
.LeftUp() ) 
 292         m_mouseCaptured 
= FALSE
; 
 294     else if (event
.Moving() && !event
.Dragging()) 
 296         // Just change the cursor if required 
 297         if ( sashHit 
!= wxSASH_NONE 
) 
 299             if ( (sashHit 
== wxSASH_LEFT
) || (sashHit 
== wxSASH_RIGHT
) ) 
 301                 if (m_currentCursor 
!= m_sashCursorWE
) 
 303                     SetCursor(*m_sashCursorWE
); 
 305                 m_currentCursor 
= m_sashCursorWE
; 
 309                 if (m_currentCursor 
!= m_sashCursorNS
) 
 311                     SetCursor(*m_sashCursorNS
); 
 313                 m_currentCursor 
= m_sashCursorNS
; 
 318             SetCursor(wxNullCursor
); 
 319             m_currentCursor 
= NULL
; 
 322     else if ( event
.Dragging() && 
 323               ((m_dragMode 
== wxSASH_DRAG_DRAGGING
) || 
 324                (m_dragMode 
== wxSASH_DRAG_LEFT_DOWN
)) ) 
 326         if ( (m_draggingEdge 
== wxSASH_LEFT
) || (m_draggingEdge 
== wxSASH_RIGHT
) ) 
 328             if (m_currentCursor 
!= m_sashCursorWE
) 
 330                 SetCursor(*m_sashCursorWE
); 
 332             m_currentCursor 
= m_sashCursorWE
; 
 336             if (m_currentCursor 
!= m_sashCursorNS
) 
 338                 SetCursor(*m_sashCursorNS
); 
 340             m_currentCursor 
= m_sashCursorNS
; 
 343         if (m_dragMode 
== wxSASH_DRAG_LEFT_DOWN
) 
 345             m_dragMode 
= wxSASH_DRAG_DRAGGING
; 
 346             DrawSashTracker(m_draggingEdge
, x
, y
); 
 350             if ( m_dragMode 
== wxSASH_DRAG_DRAGGING 
) 
 353                 DrawSashTracker(m_draggingEdge
, m_oldX
, m_oldY
); 
 356                 DrawSashTracker(m_draggingEdge
, x
, y
); 
 362     else if ( event
.LeftDClick() ) 
 371 void wxSashWindow::OnSize(wxSizeEvent
& WXUNUSED(event
)) 
 376 wxSashEdgePosition 
wxSashWindow::SashHitTest(int x
, int y
, int WXUNUSED(tolerance
)) 
 379     GetClientSize(& cx
, & cy
); 
 382     for (i 
= 0; i 
< 4; i
++) 
 384         wxSashEdge
& edge 
= m_sashes
[i
]; 
 385         wxSashEdgePosition position 
= (wxSashEdgePosition
) i 
; 
 393                     if (y 
>= 0 && y 
<= GetEdgeMargin(position
)) 
 399                     if ((x 
>= cx 
- GetEdgeMargin(position
)) && (x 
<= cx
)) 
 405                     if ((y 
>= cy 
- GetEdgeMargin(position
)) && (y 
<= cy
)) 
 406                         return wxSASH_BOTTOM
; 
 411                     if ((x 
<= GetEdgeMargin(position
)) && (x 
>= 0)) 
 425 // Draw 3D effect borders 
 426 void wxSashWindow::DrawBorders(wxDC
& dc
) 
 429     GetClientSize(&w
, &h
); 
 431     wxPen 
mediumShadowPen(m_mediumShadowColour
, 1, wxSOLID
); 
 432     wxPen 
darkShadowPen(m_darkShadowColour
, 1, wxSOLID
); 
 433     wxPen 
lightShadowPen(m_lightShadowColour
, 1, wxSOLID
); 
 434     wxPen 
hilightPen(m_hilightColour
, 1, wxSOLID
); 
 436     if ( GetWindowStyleFlag() & wxSW_3DBORDER 
) 
 438         dc
.SetPen(mediumShadowPen
); 
 439         dc
.DrawLine(0, 0, w
-1, 0); 
 440         dc
.DrawLine(0, 0, 0, h 
- 1); 
 442         dc
.SetPen(darkShadowPen
); 
 443         dc
.DrawLine(1, 1, w
-2, 1); 
 444         dc
.DrawLine(1, 1, 1, h
-2); 
 446         dc
.SetPen(hilightPen
); 
 447         dc
.DrawLine(0, h
-1, w
-1, h
-1); 
 448         dc
.DrawLine(w
-1, 0, w
-1, h
); // Surely the maximum y pos. should be h - 1. 
 449                                      /// Anyway, h is required for MSW. 
 451         dc
.SetPen(lightShadowPen
); 
 452         dc
.DrawLine(w
-2, 1, w
-2, h
-2); // Right hand side 
 453         dc
.DrawLine(1, h
-2, w
-1, h
-2);     // Bottom 
 455     else if ( GetWindowStyleFlag() & wxSW_BORDER 
) 
 457         dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
 458         dc
.SetPen(*wxBLACK_PEN
); 
 459         dc
.DrawRectangle(0, 0, w
-1, h
-1); 
 462     dc
.SetPen(wxNullPen
); 
 463     dc
.SetBrush(wxNullBrush
); 
 466 void wxSashWindow::DrawSashes(wxDC
& dc
) 
 469     for (i 
= 0; i 
< 4; i
++) 
 470         if (m_sashes
[i
].m_show
) 
 471             DrawSash((wxSashEdgePosition
) i
, dc
); 
 475 void wxSashWindow::DrawSash(wxSashEdgePosition edge
, wxDC
& dc
) 
 478     GetClientSize(&w
, &h
); 
 480     wxPen 
facePen(m_faceColour
, 1, wxSOLID
); 
 481     wxBrush 
faceBrush(m_faceColour
, wxSOLID
); 
 482     wxPen 
mediumShadowPen(m_mediumShadowColour
, 1, wxSOLID
); 
 483     wxPen 
darkShadowPen(m_darkShadowColour
, 1, wxSOLID
); 
 484     wxPen 
lightShadowPen(m_lightShadowColour
, 1, wxSOLID
); 
 485     wxPen 
hilightPen(m_hilightColour
, 1, wxSOLID
); 
 486     wxPen 
blackPen(wxColour(0, 0, 0), 1, wxSOLID
); 
 487     wxPen 
whitePen(wxColour(255, 255, 255), 1, wxSOLID
); 
 489     if ( edge 
== wxSASH_LEFT 
|| edge 
== wxSASH_RIGHT 
) 
 491         int sashPosition 
= 0; 
 492         if (edge 
== wxSASH_LEFT
) 
 495             sashPosition 
= w 
- GetEdgeMargin(edge
); 
 498         dc
.SetBrush(faceBrush
); 
 499         dc
.DrawRectangle(sashPosition
, 0, GetEdgeMargin(edge
), h
); 
 501         if (GetWindowStyleFlag() & wxSW_3DSASH
) 
 503             if (edge 
== wxSASH_LEFT
) 
 505                 // Draw a dark grey line on the left to indicate that the 
 507                 dc
.SetPen(mediumShadowPen
); 
 508                 dc
.DrawLine(GetEdgeMargin(edge
), 0, GetEdgeMargin(edge
), h
); 
 512                 // Draw a highlight line on the right to indicate that the 
 514                 dc
.SetPen(hilightPen
); 
 515                 dc
.DrawLine(w 
- GetEdgeMargin(edge
), 0, w 
- GetEdgeMargin(edge
), h
); 
 519     else // top or bottom 
 521         int sashPosition 
= 0; 
 522         if (edge 
== wxSASH_TOP
) 
 525             sashPosition 
= h 
- GetEdgeMargin(edge
); 
 528         dc
.SetBrush(faceBrush
); 
 529         dc
.DrawRectangle(0, sashPosition
, w
, GetEdgeMargin(edge
)); 
 531         if (GetWindowStyleFlag() & wxSW_3DSASH
) 
 533             if (edge 
== wxSASH_BOTTOM
) 
 535                 // Draw a highlight line on the bottom to indicate that the 
 537                 dc
.SetPen(hilightPen
); 
 538                 dc
.DrawLine(0, h 
- GetEdgeMargin(edge
), w
, h 
- GetEdgeMargin(edge
)); 
 542                 // Draw a drak grey line on the top to indicate that the 
 544                 dc
.SetPen(mediumShadowPen
); 
 545                 dc
.DrawLine(1, GetEdgeMargin(edge
), w
-1, GetEdgeMargin(edge
)); 
 550     dc
.SetPen(wxNullPen
); 
 551     dc
.SetBrush(wxNullBrush
); 
 554 // Draw the sash tracker (for whilst moving the sash) 
 555 void wxSashWindow::DrawSashTracker(wxSashEdgePosition edge
, int x
, int y
) 
 558     GetClientSize(&w
, &h
); 
 564     if ( edge 
== wxSASH_LEFT 
|| edge 
== wxSASH_RIGHT 
) 
 569         if ( (edge 
== wxSASH_LEFT
) && (x1 
> w
) ) 
 573         else if ( (edge 
== wxSASH_RIGHT
) && (x1 
< 0) ) 
 583         if ( (edge 
== wxSASH_TOP
) && (y1 
> h
) ) 
 588         else if ( (edge 
== wxSASH_BOTTOM
) && (y1 
< 0) ) 
 595     ClientToScreen(&x1
, &y1
); 
 596     ClientToScreen(&x2
, &y2
); 
 598     wxPen 
sashTrackerPen(*wxBLACK
, 2, wxSOLID
); 
 600     screenDC
.SetLogicalFunction(wxINVERT
); 
 601     screenDC
.SetPen(sashTrackerPen
); 
 602     screenDC
.SetBrush(*wxTRANSPARENT_BRUSH
); 
 604     screenDC
.DrawLine(x1
, y1
, x2
, y2
); 
 606     screenDC
.SetLogicalFunction(wxCOPY
); 
 608     screenDC
.SetPen(wxNullPen
); 
 609     screenDC
.SetBrush(wxNullBrush
); 
 612 // Position and size subwindows. 
 613 // Note that the border size applies to each subwindow, not 
 614 // including the edges next to the sash. 
 615 void wxSashWindow::SizeWindows() 
 618     GetClientSize(&cw
, &ch
); 
 620     if (GetChildren().Number() == 1) 
 622         wxWindow
* child 
= (wxWindow
*) (GetChildren().First()->Data()); 
 630         if (m_sashes
[0].m_show
) 
 633             height 
-= m_borderSize
; 
 635         y 
+= m_extraBorderSize
; 
 638         if (m_sashes
[3].m_show
) 
 641             width 
-= m_borderSize
; 
 643         x 
+= m_extraBorderSize
; 
 646         if (m_sashes
[1].m_show
) 
 648             width 
-= m_borderSize
; 
 650         width 
-= 2*m_extraBorderSize
; 
 653         if (m_sashes
[2].m_show
) 
 655             height 
-= m_borderSize
; 
 657         height 
-= 2*m_extraBorderSize
; 
 659         child
->SetSize(x
, y
, width
, height
); 
 661     else if (GetChildren().Number() > 1) 
 663         // Perhaps multiple children are themselves sash windows. 
 664         // TODO: this doesn't really work because the subwindows sizes/positions 
 665         // must be set to leave a gap for the parent's sash (hit-test and decorations). 
 666         // Perhaps we can allow for this within LayoutWindow, testing whether the parent 
 667         // is a sash window, and if so, allowing some space for the edges. 
 668         wxLayoutAlgorithm layout
; 
 669         layout
.LayoutWindow(this); 
 677 // Initialize colours 
 678 void wxSashWindow::InitColours() 
 682     m_faceColour 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE
); 
 683     m_mediumShadowColour 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW
); 
 684     m_darkShadowColour 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW
); 
 685     m_lightShadowColour 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT
); 
 686     m_hilightColour 
= wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT
); 
 688     m_faceColour 
= *(wxTheColourDatabase
->FindColour("LIGHT GREY")); 
 689     m_mediumShadowColour 
= *(wxTheColourDatabase
->FindColour("GREY")); 
 690     m_darkShadowColour 
= *(wxTheColourDatabase
->FindColour("BLACK")); 
 691     m_lightShadowColour 
= *(wxTheColourDatabase
->FindColour("LIGHT GREY")); 
 692     m_hilightColour 
= *(wxTheColourDatabase
->FindColour("WHITE")); 
 696 void wxSashWindow::SetSashVisible(wxSashEdgePosition edge
, bool sash
) 
 698      m_sashes
[edge
].m_show 
= sash
; 
 700         m_sashes
[edge
].m_margin 
= m_borderSize
; 
 702         m_sashes
[edge
].m_margin 
= 0; 
 707 // this is currently called (and needed) under MSW only... 
 708 void wxSashWindow::OnSetCursor(wxSetCursorEvent
& event
) 
 710     // if we don't do it, the resizing cursor might be set for child window: 
 711     // and like this we explicitly say that our cursor should not be used for 
 712     // children windows which overlap us 
 714     if ( SashHitTest(event
.GetX(), event
.GetY()) != wxSASH_NONE
) 
 716         // default processing is ok 
 719     //else: do nothing, in particular, don't call Skip()