home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / generic / splitter.cpp < prev    next >
C/C++ Source or Header  |  2002-11-04  |  33KB  |  1,109 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        src/generic/splitter.cpp
  3. // Purpose:     wxSplitterWindow implementation
  4. // Author:      Julian Smart
  5. // Modified by:
  6. // Created:     01/02/97
  7. // RCS-ID:      $Id: splitter.cpp,v 1.73.2.4 2002/11/03 21:37:05 RR Exp $
  8. // Copyright:   (c) Julian Smart and Markus Holzem
  9. // Licence:     wxWindows license
  10. /////////////////////////////////////////////////////////////////////////////
  11.  
  12. #ifdef __GNUG__
  13.     #pragma implementation "splitter.h"
  14. #endif
  15.  
  16. // For compilers that support precompilation, includes "wx.h".
  17. #include "wx/wxprec.h"
  18.  
  19. #ifdef __BORLANDC__
  20.     #pragma hdrstop
  21. #endif
  22.  
  23. #ifndef WX_PRECOMP
  24.     #include "wx/string.h"
  25.     #include "wx/utils.h"
  26.     #include "wx/log.h"
  27.  
  28.     #include "wx/dcscreen.h"
  29.  
  30.     #include "wx/window.h"
  31.     #include "wx/dialog.h"
  32.     #include "wx/frame.h"
  33.  
  34.     #include "wx/settings.h"
  35. #endif
  36.  
  37. #include "wx/splitter.h"
  38.  
  39. #include <stdlib.h>
  40.  
  41. DEFINE_EVENT_TYPE(wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED)
  42. DEFINE_EVENT_TYPE(wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING)
  43. DEFINE_EVENT_TYPE(wxEVT_COMMAND_SPLITTER_DOUBLECLICKED)
  44. DEFINE_EVENT_TYPE(wxEVT_COMMAND_SPLITTER_UNSPLIT)
  45.  
  46. IMPLEMENT_DYNAMIC_CLASS(wxSplitterWindow, wxWindow)
  47. IMPLEMENT_DYNAMIC_CLASS(wxSplitterEvent, wxNotifyEvent)
  48.  
  49. BEGIN_EVENT_TABLE(wxSplitterWindow, wxWindow)
  50.     EVT_PAINT(wxSplitterWindow::OnPaint)
  51.     EVT_SIZE(wxSplitterWindow::OnSize)
  52.     EVT_IDLE(wxSplitterWindow::OnIdle)
  53.     EVT_MOUSE_EVENTS(wxSplitterWindow::OnMouseEvent)
  54.  
  55. #ifdef __WXMSW__
  56.     EVT_SET_CURSOR(wxSplitterWindow::OnSetCursor)
  57. #endif // wxMSW
  58.  
  59.     WX_EVENT_TABLE_CONTROL_CONTAINER(wxSplitterWindow)
  60. END_EVENT_TABLE()
  61.  
  62. WX_DELEGATE_TO_CONTROL_CONTAINER(wxSplitterWindow);
  63.  
  64. bool wxSplitterWindow::Create(wxWindow *parent, wxWindowID id,
  65.                                    const wxPoint& pos,
  66.                                    const wxSize& size,
  67.                                    long style,
  68.                                    const wxString& name)
  69. {
  70.     // allow TABbing from one window to the other
  71.     style |= wxTAB_TRAVERSAL;
  72.  
  73.     if (!wxWindow::Create(parent, id, pos, size, style, name))
  74.         return FALSE;
  75.  
  76.     m_permitUnsplitAlways = (style & wxSP_PERMIT_UNSPLIT) != 0;
  77.  
  78.     if ( style & wxSP_3DSASH )
  79.         m_sashSize = 7;
  80.     else
  81.         m_sashSize = 3;
  82.  
  83.     if ( style & wxSP_3DBORDER )
  84.         m_borderSize = 2;
  85.     else if ( style & wxSP_BORDER )
  86.         m_borderSize = 1;
  87.     else
  88.         m_borderSize = 0;
  89.  
  90. #ifdef __WXMAC__
  91.     int major,minor;
  92.     wxGetOsVersion( &major, &minor );
  93.     if (major >= 10)
  94.         m_windowStyle |= wxSP_SASH_AQUA;
  95. #endif
  96.  
  97.     return TRUE;
  98. }
  99.  
  100. void wxSplitterWindow::Init()
  101. {
  102.     m_container.SetContainerWindow(this);
  103.  
  104.     m_splitMode = wxSPLIT_VERTICAL;
  105.     m_permitUnsplitAlways = TRUE;
  106.     m_windowOne = (wxWindow *) NULL;
  107.     m_windowTwo = (wxWindow *) NULL;
  108.     m_dragMode = wxSPLIT_DRAG_NONE;
  109.     m_oldX = 0;
  110.     m_oldY = 0;
  111.     m_firstX = 0;
  112.     m_firstY = 0;
  113.     m_sashSize = 3;
  114.     m_borderSize = 0;
  115.     m_sashPosition = m_requestedSashPosition = 0;
  116.     m_minimumPaneSize = 0;
  117.     m_sashCursorWE = wxCursor(wxCURSOR_SIZEWE);
  118.     m_sashCursorNS = wxCursor(wxCURSOR_SIZENS);
  119.     m_sashTrackerPen = new wxPen(*wxBLACK, 2, wxSOLID);
  120.     m_lightShadowPen = (wxPen *) NULL;
  121.     m_mediumShadowPen = (wxPen *) NULL;
  122.     m_darkShadowPen = (wxPen *) NULL;
  123.     m_faceBrush = (wxBrush *) NULL;
  124.     m_facePen = (wxPen *) NULL;
  125.     m_hilightPen = (wxPen *) NULL;
  126.  
  127.     InitColours();
  128.  
  129.     m_needUpdating = FALSE;
  130. }
  131.  
  132. wxSplitterWindow::~wxSplitterWindow()
  133. {
  134.     delete m_sashTrackerPen;
  135.     delete m_lightShadowPen;
  136.     delete m_darkShadowPen;
  137.     delete m_mediumShadowPen;
  138.     delete m_hilightPen;
  139.     delete m_facePen;
  140.     delete m_faceBrush;
  141. }
  142.  
  143. void wxSplitterWindow::SetResizeCursor()
  144. {
  145.     SetCursor(m_splitMode == wxSPLIT_VERTICAL ? m_sashCursorWE
  146.                                               : m_sashCursorNS);
  147. }
  148.  
  149. void wxSplitterWindow::OnPaint(wxPaintEvent& WXUNUSED(event))
  150. {
  151.     wxPaintDC dc(this);
  152.  
  153.     if ( m_borderSize > 0 )
  154.         DrawBorders(dc);
  155.     DrawSash(dc);
  156. }
  157.  
  158. void wxSplitterWindow::OnIdle(wxIdleEvent& event)
  159. {
  160.     if (m_needUpdating)
  161.         SizeWindows();
  162.  
  163.     event.Skip();
  164. }
  165.  
  166. void wxSplitterWindow::OnMouseEvent(wxMouseEvent& event)
  167. {
  168.     int x = (int)event.GetX(),
  169.         y = (int)event.GetY();
  170.  
  171.     // reset the cursor
  172. #if defined( __WXMOTIF__ ) || defined( __WXGTK__ ) || defined( __WXMAC__ )
  173.     SetCursor(* wxSTANDARD_CURSOR);
  174. #elif defined(__WXMSW__)
  175.     SetCursor(wxCursor());
  176. #endif
  177.  
  178.     if (GetWindowStyle() & wxSP_NOSASH)
  179.         return;
  180.  
  181.     // with wxSP_LIVE_UPDATE style the splitter windows are always resized
  182.     // following the mouse movement while it drags the sash, without it we only
  183.     // draw the sash at the new position but only resize the windows when the
  184.     // dragging is finished
  185.     bool isLive = (GetWindowStyleFlag() & wxSP_LIVE_UPDATE) != 0;
  186.  
  187.     if (event.LeftDown())
  188.     {
  189.         if ( SashHitTest(x, y) )
  190.         {
  191.             CaptureMouse();
  192.  
  193.             m_dragMode = wxSPLIT_DRAG_DRAGGING;
  194.  
  195.             if ( !isLive )
  196.             {
  197.                 // remember the initial sash position and draw the initial
  198.                 // shadow sash
  199.                 m_sashPositionCurrent = m_sashPosition;
  200.  
  201.                 DrawSashTracker(x, y);
  202.             }
  203.  
  204.             m_oldX = x;
  205.             m_oldY = y;
  206.  
  207.             SetResizeCursor();
  208.             return;
  209.         }
  210.     }
  211.     else if (event.LeftUp() && m_dragMode == wxSPLIT_DRAG_DRAGGING)
  212.     {
  213.         // We can stop dragging now and see what we've got.
  214.         m_dragMode = wxSPLIT_DRAG_NONE;
  215.         ReleaseMouse();
  216.  
  217.         // exit if unsplit after doubleclick
  218.         if ( !IsSplit() )
  219.         {
  220.             return;
  221.         }
  222.  
  223.         // Erase old tracker
  224.         if ( !isLive )
  225.         {
  226.             DrawSashTracker(m_oldX, m_oldY);
  227.         }
  228.  
  229.         // the position of the click doesn't exactly correspond to
  230.         // m_sashPosition, rather it changes it by the distance by which the
  231.         // mouse has moved
  232.         int diff = m_splitMode == wxSPLIT_VERTICAL ? x - m_oldX : y - m_oldY;
  233.  
  234.         int posSashOld = isLive ? m_sashPosition : m_sashPositionCurrent;
  235.         int posSashNew = OnSashPositionChanging(posSashOld + diff);
  236.         if ( posSashNew == -1 )
  237.         {
  238.             // change not allowed
  239.             return;
  240.         }
  241.  
  242.         if ( m_permitUnsplitAlways || m_minimumPaneSize == 0 )
  243.         {
  244.             // Deal with possible unsplit scenarios
  245.             if ( posSashNew == 0 )
  246.             {
  247.                 // We remove the first window from the view
  248.                 wxWindow *removedWindow = m_windowOne;
  249.                 m_windowOne = m_windowTwo;
  250.                 m_windowTwo = (wxWindow *) NULL;
  251.                 OnUnsplit(removedWindow);
  252.                 SetSashPositionAndNotify(0);
  253.             }
  254.             else if ( posSashNew == GetWindowSize() )
  255.             {
  256.                 // We remove the second window from the view
  257.                 wxWindow *removedWindow = m_windowTwo;
  258.                 m_windowTwo = (wxWindow *) NULL;
  259.                 OnUnsplit(removedWindow);
  260.                 SetSashPositionAndNotify(0);
  261.             }
  262.             else
  263.             {
  264.                 SetSashPositionAndNotify(posSashNew);
  265.             }
  266.         }
  267.         else
  268.         {
  269.             SetSashPositionAndNotify(posSashNew);
  270.         }
  271.  
  272.         SizeWindows();
  273.     }  // left up && dragging
  274.     else if (event.Moving() && !event.Dragging())
  275.     {
  276.         // Just change the cursor if required
  277.         if ( SashHitTest(x, y) )
  278.         {
  279.             SetResizeCursor();
  280.         }
  281. #if defined(__WXGTK__) || defined(__WXMSW__) || defined(__WXMAC__)
  282.         else
  283.         {
  284.             // We must set the normal cursor in MSW, because
  285.             // if the child window doesn't have a cursor, the
  286.             // parent's (splitter window) will be used, and this
  287.             // must be the standard cursor.
  288.  
  289.             // where else do we unset the cursor?
  290.             SetCursor(* wxSTANDARD_CURSOR);
  291.         }
  292. #endif // __WXGTK__
  293.     }
  294.     else if (event.Dragging() && (m_dragMode == wxSPLIT_DRAG_DRAGGING))
  295.     {
  296. #if defined( __WXMSW__ ) || defined( __WXMAC__ )
  297.         // Otherwise, the cursor sometimes reverts to the normal cursor
  298.         // during dragging.
  299.         SetResizeCursor();
  300. #endif // __WXMSW__
  301.  
  302.         int diff = m_splitMode == wxSPLIT_VERTICAL ? x - m_oldX : y - m_oldY;
  303.         if ( !diff )
  304.         {
  305.             // nothing to do, mouse didn't really move far enough
  306.             return;
  307.         }
  308.  
  309.         int posSashOld = isLive ? m_sashPosition : m_sashPositionCurrent;
  310.         int posSashNew = OnSashPositionChanging(posSashOld + diff);
  311.         if ( posSashNew == -1 )
  312.         {
  313.             // change not allowed
  314.             return;
  315.         }
  316.  
  317.         if ( posSashNew == m_sashPosition )
  318.             return;
  319.  
  320.         // Erase old tracker
  321.         if ( !isLive )
  322.         {
  323.             DrawSashTracker(m_oldX, m_oldY);
  324.         }
  325.  
  326.         if (m_splitMode == wxSPLIT_VERTICAL)
  327.             x = posSashNew;
  328.         else
  329.             y = posSashNew;
  330.  
  331.         // Remember old positions
  332.         m_oldX = x;
  333.         m_oldY = y;
  334.  
  335. #ifdef __WXMSW__
  336.         // As we captured the mouse, we may get the mouse events from outside
  337.         // our window - for example, negative values in x, y. This has a weird
  338.         // consequence under MSW where we use unsigned values sometimes and
  339.         // signed ones other times: the coordinates turn as big positive
  340.         // numbers and so the sash is drawn on the *right* side of the window
  341.         // instead of the left (or bottom instead of top). Correct this.
  342.         if ( (short)m_oldX < 0 )
  343.             m_oldX = 0;
  344.         if ( (short)m_oldY < 0 )
  345.             m_oldY = 0;
  346. #endif // __WXMSW__
  347.  
  348.         // Draw new one
  349.         if ( !isLive )
  350.         {
  351.             m_sashPositionCurrent = posSashNew;
  352.  
  353.             DrawSashTracker(m_oldX, m_oldY);
  354.         }
  355.         else
  356.         {
  357.             SetSashPositionAndNotify(posSashNew);
  358.             m_needUpdating = TRUE;
  359.         }
  360.     }
  361.     else if ( event.LeftDClick() )
  362.     {
  363.         OnDoubleClickSash(x, y);
  364.     }
  365. }
  366.  
  367. void wxSplitterWindow::OnSize(wxSizeEvent& event)
  368. {
  369.     // only process this message if we're not iconized - otherwise iconizing
  370.     // and restoring a window containing the splitter has a funny side effect
  371.     // of changing the splitter position!
  372.     wxWindow *parent = GetParent();
  373.     while ( parent && !parent->IsTopLevel() )
  374.     {
  375.         parent = parent->GetParent();
  376.     }
  377.  
  378.     bool iconized = FALSE;
  379.  
  380.     // wxMotif doesn't yet have a wxTopLevelWindow implementation
  381. #ifdef __WXMOTIF__
  382.     wxFrame *winTop = wxDynamicCast(parent, wxFrame);
  383. #else
  384.     wxTopLevelWindow *winTop = wxDynamicCast(parent, wxTopLevelWindow);
  385. #endif
  386.     if ( winTop )
  387.     {
  388.         iconized = winTop->IsIconized();
  389.     }
  390. #ifndef __WXMOTIF__
  391.     else
  392.     {
  393.         wxFAIL_MSG(wxT("should have a top level parent!"));
  394.  
  395.         iconized = FALSE;
  396.     }
  397. #endif
  398.  
  399.     if ( iconized )
  400.     {
  401.         event.Skip();
  402.  
  403.         return;
  404.     }
  405.  
  406.     int cw, ch;
  407.     GetClientSize( &cw, &ch );
  408.     if ( m_windowTwo )
  409.     {
  410.         if ( m_splitMode == wxSPLIT_VERTICAL )
  411.         {
  412.             if ( m_sashPosition >= (cw - 5) )
  413.                 SetSashPositionAndNotify(wxMax(10, cw - 40));
  414.         }
  415.         else // m_splitMode == wxSPLIT_HORIZONTAL
  416.         {
  417.             if ( m_sashPosition >= (ch - 5) )
  418.                 SetSashPositionAndNotify(wxMax(10, ch - 40));
  419.         }
  420.     }
  421.  
  422.     SizeWindows();
  423. }
  424.  
  425. bool wxSplitterWindow::SashHitTest(int x, int y, int tolerance)
  426. {
  427.     if ( m_windowTwo == NULL || m_sashPosition == 0)
  428.         return FALSE; // No sash
  429.  
  430.     if ( m_splitMode == wxSPLIT_VERTICAL )
  431.     {
  432.         if ( (x >= m_sashPosition - tolerance) && (x <= m_sashPosition + m_sashSize + tolerance) )
  433.             return TRUE;
  434.         else
  435.             return FALSE;
  436.     }
  437.     else
  438.     {
  439.         if ( (y >= (m_sashPosition- tolerance)) && (y <= (m_sashPosition + m_sashSize + tolerance)) )
  440.             return TRUE;
  441.         else
  442.             return FALSE;
  443.     }
  444. }
  445.  
  446. // Draw 3D effect borders
  447. void wxSplitterWindow::DrawBorders(wxDC& dc)
  448. {
  449.     int w, h;
  450.     GetClientSize(&w, &h);
  451.  
  452.     if ( GetWindowStyleFlag() & wxSP_3DBORDER )
  453.     {
  454.  
  455.         dc.SetPen(*m_facePen);
  456.         dc.SetBrush(*m_faceBrush);
  457.         dc.DrawRectangle(1, 1 , w-1, m_borderSize-2 ); //high
  458.         dc.DrawRectangle(1, m_borderSize-2 , m_borderSize-2, h-1 ); // left
  459.         dc.DrawRectangle(w-m_borderSize+2, m_borderSize-2 , w-1, h-1 ); // right
  460.         dc.DrawRectangle(m_borderSize-2, h-m_borderSize+2 , w-m_borderSize+2, h-1 ); //bottom
  461.  
  462.         dc.SetPen(*m_mediumShadowPen);
  463.         dc.DrawLine(m_borderSize-2, m_borderSize-2, w-m_borderSize+1, m_borderSize-2);
  464.         dc.DrawLine(m_borderSize-2, m_borderSize-2, m_borderSize-2, h-m_borderSize+1);
  465.  
  466.         dc.SetPen(*m_darkShadowPen);
  467.         dc.DrawLine(m_borderSize-1, m_borderSize-1, w-m_borderSize, m_borderSize-1);
  468.         dc.DrawLine(m_borderSize-1, m_borderSize-1, m_borderSize-1, h-m_borderSize);
  469.  
  470.         dc.SetPen(*m_hilightPen);
  471.         dc.DrawLine(m_borderSize - 2, h-m_borderSize+1, w-m_borderSize+1, h-m_borderSize+1);
  472.         dc.DrawLine(w-m_borderSize+1, m_borderSize - 2, w-m_borderSize+1, h-m_borderSize+2); // Surely the maximum y pos. should be h - 1.
  473.                                      /// Anyway, h is required for MSW.
  474.  
  475.         dc.SetPen(*m_lightShadowPen);
  476.         dc.DrawLine(w-m_borderSize, m_borderSize-1, w-m_borderSize, h-m_borderSize); // Right hand side
  477.         dc.DrawLine(m_borderSize-1, h-m_borderSize, w-m_borderSize+1, h-m_borderSize);     // Bottom
  478.     }
  479.     else if ( GetWindowStyleFlag() & wxSP_BORDER )
  480.     {
  481.         dc.SetBrush(*wxTRANSPARENT_BRUSH);
  482.         dc.SetPen(*wxBLACK_PEN);
  483.         dc.DrawRectangle(0, 0, w-1, h-1);
  484.     }
  485.  
  486.     dc.SetPen(wxNullPen);
  487.     dc.SetBrush(wxNullBrush);
  488. }
  489.  
  490. // Draw the sash
  491. void wxSplitterWindow::DrawSash(wxDC& dc)
  492. {
  493.     if ( m_sashPosition == 0 || !m_windowTwo)
  494.         return;
  495.     if (GetWindowStyle() & wxSP_NOSASH)
  496.         return;
  497.  
  498.     int w, h;
  499.     GetClientSize(&w, &h);
  500.  
  501.     if ( GetWindowStyleFlag() & wxSP_3DSASH )
  502.     {
  503.         if ( m_splitMode == wxSPLIT_VERTICAL )
  504.         {
  505.             dc.SetPen(*m_facePen);
  506.  
  507.             if (HasFlag( wxSP_SASH_AQUA ))
  508.                 dc.SetBrush(*wxWHITE_BRUSH);
  509.             else
  510.                 dc.SetBrush(*m_faceBrush);
  511.             dc.DrawRectangle(m_sashPosition + 2, 0 , m_sashSize - 4, h );
  512.  
  513.             dc.SetBrush(*wxTRANSPARENT_BRUSH);
  514.  
  515.             dc.SetPen(*m_lightShadowPen);
  516.             int xShadow = m_borderSize ? m_borderSize - 1 : 0 ;
  517.             dc.DrawLine(m_sashPosition, xShadow , m_sashPosition, h-m_borderSize);
  518.  
  519.             dc.SetPen(*m_hilightPen);
  520.             dc.DrawLine(m_sashPosition+1, m_borderSize - 2, m_sashPosition+1, h - m_borderSize+2);
  521.  
  522.             if (!HasFlag( wxSP_SASH_AQUA ))
  523.                 dc.SetPen(*m_mediumShadowPen);
  524.  
  525.             int yMedium = m_borderSize ? h-m_borderSize+1 : h ;
  526.             dc.DrawLine(m_sashPosition+m_sashSize-2, xShadow, m_sashPosition+m_sashSize-2, yMedium);
  527.  
  528.             if (HasFlag( wxSP_SASH_AQUA ))
  529.                 dc.SetPen(*m_lightShadowPen);
  530.             else
  531.                 dc.SetPen(*m_darkShadowPen);
  532.             dc.DrawLine(m_sashPosition+m_sashSize-1, m_borderSize, m_sashPosition+m_sashSize-1, h-m_borderSize );
  533.  
  534.             // Draw the top and bottom edges of the sash, if requested
  535.             if (GetWindowStyle() & wxSP_FULLSASH)
  536.             {
  537.                 // Top
  538.                 dc.SetPen(*m_hilightPen);
  539.                 dc.DrawLine(m_sashPosition+1, m_borderSize, m_sashPosition+m_sashSize-1, m_borderSize);
  540.  
  541.                 // Bottom
  542.                 dc.SetPen(*m_darkShadowPen);
  543.                 dc.DrawLine(m_sashPosition+1, h-m_borderSize-1, m_sashPosition+m_sashSize-1, h-m_borderSize-1);
  544.             }
  545.         }
  546.         else // wxSPLIT_HORIZONTAL
  547.         {
  548.             dc.SetPen(*m_facePen);
  549.             if (HasFlag( wxSP_SASH_AQUA ))
  550.                 dc.SetBrush(*wxWHITE_BRUSH);
  551.             else
  552.                 dc.SetBrush(*m_faceBrush);
  553.             dc.DrawRectangle( m_borderSize-2, m_sashPosition + 2, w-m_borderSize+2, m_sashSize - 4);
  554.  
  555.             dc.SetBrush(*wxTRANSPARENT_BRUSH);
  556.  
  557.             dc.SetPen(*m_lightShadowPen);
  558.             dc.DrawLine(m_borderSize-1, m_sashPosition, w-m_borderSize, m_sashPosition);
  559.  
  560.             dc.SetPen(*m_hilightPen);
  561.             dc.DrawLine(m_borderSize-2, m_sashPosition+1, w-m_borderSize+1, m_sashPosition+1);
  562.  
  563.             if (!HasFlag( wxSP_SASH_AQUA ))
  564.                 dc.SetPen(*m_mediumShadowPen);
  565.             dc.DrawLine(m_borderSize-1, m_sashPosition+m_sashSize-2, w-m_borderSize+1, m_sashPosition+m_sashSize-2);
  566.  
  567.             if (HasFlag( wxSP_SASH_AQUA ))
  568.                 dc.SetPen(*m_lightShadowPen);
  569.             else
  570.                 dc.SetPen(*m_darkShadowPen);
  571.             dc.DrawLine(m_borderSize, m_sashPosition+m_sashSize-1, w-m_borderSize, m_sashPosition+m_sashSize-1);
  572.  
  573.             // Draw the left and right edges of the sash, if requested
  574.             if (GetWindowStyle() & wxSP_FULLSASH)
  575.             {
  576.                 // Left
  577.                 dc.SetPen(*m_hilightPen);
  578.                 dc.DrawLine(m_borderSize, m_sashPosition, m_borderSize, m_sashPosition+m_sashSize);
  579.  
  580.                 // Right
  581.                 dc.SetPen(*m_darkShadowPen);
  582.                 dc.DrawLine(w-m_borderSize-1, m_sashPosition+1, w-m_borderSize-1, m_sashPosition+m_sashSize-1);
  583.             }
  584.         }
  585.     }
  586.     else // !wxSP_3DSASH
  587.     {
  588.         dc.SetPen(*wxTRANSPARENT_PEN);
  589.         dc.SetBrush(*m_faceBrush);
  590.         if ( m_splitMode == wxSPLIT_VERTICAL )
  591.         {
  592.             int h1 = h-1;
  593.             int y1 = 0;
  594.             if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
  595.                 h1 += 1; // Not sure why this is necessary...
  596.             if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
  597.             {
  598.                 y1 = 2; h1 -= 3;
  599.             }
  600.             dc.DrawRectangle(m_sashPosition, y1, m_sashSize, h1);
  601.         }
  602.         else // wxSPLIT_HORIZONTAL
  603.         {
  604.             int w1 = w-1;
  605.             int x1 = 0;
  606.             if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
  607.                 w1 ++;
  608.             if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
  609.             {
  610.                 x1 = 2; w1 -= 3;
  611.             }
  612.             dc.DrawRectangle(x1, m_sashPosition, w1, m_sashSize);
  613.         }
  614.     }
  615.  
  616.     dc.SetPen(wxNullPen);
  617.     dc.SetBrush(wxNullBrush);
  618. }
  619.  
  620. // Draw the sash tracker (for whilst moving the sash)
  621. void wxSplitterWindow::DrawSashTracker(int x, int y)
  622. {
  623.     int w, h;
  624.     GetClientSize(&w, &h);
  625.  
  626.     wxScreenDC screenDC;
  627.     int x1, y1;
  628.     int x2, y2;
  629.  
  630.     if ( m_splitMode == wxSPLIT_VERTICAL )
  631.     {
  632.         x1 = x; y1 = 2;
  633.         x2 = x; y2 = h-2;
  634.  
  635.         if ( x1 > w )
  636.         {
  637.             x1 = w; x2 = w;
  638.         }
  639.         else if ( x1 < 0 )
  640.         {
  641.             x1 = 0; x2 = 0;
  642.         }
  643.     }
  644.     else
  645.     {
  646.         x1 = 2; y1 = y;
  647.         x2 = w-2; y2 = y;
  648.  
  649.         if ( y1 > h )
  650.         {
  651.             y1 = h;
  652.             y2 = h;
  653.         }
  654.         else if ( y1 < 0 )
  655.         {
  656.             y1 = 0;
  657.             y2 = 0;
  658.         }
  659.     }
  660.  
  661.     ClientToScreen(&x1, &y1);
  662.     ClientToScreen(&x2, &y2);
  663.  
  664.     screenDC.SetLogicalFunction(wxINVERT);
  665.     screenDC.SetPen(*m_sashTrackerPen);
  666.     screenDC.SetBrush(*wxTRANSPARENT_BRUSH);
  667.  
  668.     screenDC.DrawLine(x1, y1, x2, y2);
  669.  
  670.     screenDC.SetLogicalFunction(wxCOPY);
  671.  
  672.     screenDC.SetPen(wxNullPen);
  673.     screenDC.SetBrush(wxNullBrush);
  674. }
  675.  
  676. int wxSplitterWindow::GetWindowSize() const
  677. {
  678.     wxSize size = GetClientSize();
  679.  
  680.     return m_splitMode == wxSPLIT_VERTICAL ? size.x : size.y;
  681. }
  682.  
  683. int wxSplitterWindow::AdjustSashPosition(int sashPos) const
  684. {
  685.     int window_size = GetWindowSize();
  686.  
  687.     wxWindow *win;
  688.  
  689.     win = GetWindow1();
  690.     if ( win )
  691.     {
  692.         // the window shouldn't be smaller than its own minimal size nor
  693.         // smaller than the minimual pane size specified for this splitter
  694.         int minSize = m_splitMode == wxSPLIT_VERTICAL ? win->GetMinWidth()
  695.                                                       : win->GetMinHeight();
  696.  
  697.         if ( minSize == -1 || m_minimumPaneSize > minSize )
  698.             minSize = m_minimumPaneSize;
  699.  
  700.         minSize += GetBorderSize();
  701.  
  702.         if ( sashPos < minSize )
  703.             sashPos = minSize;
  704.     }
  705.  
  706.     win = GetWindow2();
  707.     if ( win )
  708.     {
  709.         int minSize = m_splitMode == wxSPLIT_VERTICAL ? win->GetMinWidth()
  710.                                                       : win->GetMinHeight();
  711.  
  712.         if ( minSize == -1 || m_minimumPaneSize > minSize )
  713.             minSize = m_minimumPaneSize;
  714.  
  715.         int maxSize = window_size - minSize - GetBorderSize();
  716.         if ( sashPos > maxSize )
  717.             sashPos = maxSize;
  718.     }
  719.  
  720.     return sashPos;
  721. }
  722.  
  723. bool wxSplitterWindow::DoSetSashPosition(int sashPos)
  724. {
  725.     int newSashPosition = AdjustSashPosition(sashPos);
  726.  
  727.     if ( newSashPosition == m_sashPosition )
  728.         return FALSE;
  729.  
  730.     m_sashPosition = newSashPosition;
  731.  
  732.     return TRUE;
  733. }
  734.  
  735. void wxSplitterWindow::SetSashPositionAndNotify(int sashPos)
  736. {
  737.     if ( DoSetSashPosition(sashPos) )
  738.     {
  739.         wxSplitterEvent event(wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED, this);
  740.         event.m_data.pos = m_sashPosition;
  741.  
  742.         (void)DoSendEvent(event);
  743.     }
  744. }
  745.  
  746. // Position and size subwindows.
  747. // Note that the border size applies to each subwindow, not
  748. // including the edges next to the sash.
  749. void wxSplitterWindow::SizeWindows()
  750. {
  751.     // check if we have delayed setting the real sash position
  752.     if ( m_requestedSashPosition != INT_MAX )
  753.     {
  754.         int newSashPosition = ConvertSashPosition(m_requestedSashPosition);
  755.         if ( newSashPosition != m_sashPosition )
  756.         {
  757.             DoSetSashPosition(newSashPosition);
  758.         }
  759.  
  760.         if ( newSashPosition <= m_sashPosition
  761.             && newSashPosition >= m_sashPosition - GetBorderSize() )
  762.         {
  763.             // don't update it any more
  764.             m_requestedSashPosition = INT_MAX;
  765.         }
  766.     }
  767.  
  768.     int w, h;
  769.     GetClientSize(&w, &h);
  770.  
  771.     if ( GetWindow1() && !GetWindow2() )
  772.     {
  773.         GetWindow1()->SetSize(GetBorderSize(), GetBorderSize(),
  774.                               w - 2*GetBorderSize(), h - 2*GetBorderSize());
  775.     }
  776.     else if ( GetWindow1() && GetWindow2() )
  777.     {
  778.         if (GetSplitMode() == wxSPLIT_VERTICAL)
  779.         {
  780.             int x1 = GetBorderSize();
  781.             int y1 = GetBorderSize();
  782.             int w1 = GetSashPosition() - GetBorderSize();
  783.             int h1 = h - 2*GetBorderSize();
  784.  
  785.             int x2 = GetSashPosition() + GetSashSize();
  786.             int y2 = GetBorderSize();
  787.             int w2 = w - 2*GetBorderSize() - GetSashSize() - w1;
  788.             int h2 = h - 2*GetBorderSize();
  789.  
  790.             GetWindow1()->SetSize(x1, y1, w1, h1);
  791.             GetWindow2()->SetSize(x2, y2, w2, h2);
  792.         }
  793.         else
  794.         {
  795.             GetWindow1()->SetSize(GetBorderSize(), GetBorderSize(),
  796.                 w - 2*GetBorderSize(), GetSashPosition() - GetBorderSize());
  797.             GetWindow2()->SetSize(GetBorderSize(), GetSashPosition() + GetSashSize(),
  798.                 w - 2*GetBorderSize(), h - 2*GetBorderSize() - GetSashSize() - (GetSashPosition() - GetBorderSize()));
  799.         }
  800.     }
  801.     wxClientDC dc(this);
  802.     if ( GetBorderSize() > 0 )
  803.         DrawBorders(dc);
  804.     DrawSash(dc);
  805.  
  806.     SetNeedUpdating(FALSE);
  807. }
  808.  
  809. // Set pane for unsplit window
  810. void wxSplitterWindow::Initialize(wxWindow *window)
  811. {
  812.     wxASSERT_MSG( window && window->GetParent() == this,
  813.                   _T("windows in the splitter should have it as parent!") );
  814.  
  815.     m_windowOne = window;
  816.     m_windowTwo = (wxWindow *) NULL;
  817.     DoSetSashPosition(0);
  818. }
  819.  
  820. // Associates the given window with window 2, drawing the appropriate sash
  821. // and changing the split mode.
  822. // Does nothing and returns FALSE if the window is already split.
  823. bool wxSplitterWindow::DoSplit(wxSplitMode mode,
  824.                                wxWindow *window1, wxWindow *window2,
  825.                                int sashPosition)
  826. {
  827.     if ( IsSplit() )
  828.         return FALSE;
  829.  
  830.     wxCHECK_MSG( window1 && window2, FALSE,
  831.                  _T("can not split with NULL window(s)") );
  832.  
  833.     wxCHECK_MSG( window1->GetParent() == this && window2->GetParent() == this, FALSE,
  834.                   _T("windows in the splitter should have it as parent!") );
  835.  
  836.     m_splitMode = mode;
  837.     m_windowOne = window1;
  838.     m_windowTwo = window2;
  839.  
  840.     // remember the sash position we want to set for later if we can't set it
  841.     // right now (e.g. because the window is too small)
  842.     m_requestedSashPosition = sashPosition;
  843.  
  844.     DoSetSashPosition(ConvertSashPosition(sashPosition));
  845.  
  846.     SizeWindows();
  847.  
  848.     return TRUE;
  849. }
  850.  
  851. int wxSplitterWindow::ConvertSashPosition(int sashPosition) const
  852. {
  853.     if ( sashPosition > 0 )
  854.     {
  855.         return sashPosition;
  856.     }
  857.     else if ( sashPosition < 0 )
  858.     {
  859.         // It's negative so adding is subtracting
  860.         return GetWindowSize() + sashPosition;
  861.     }
  862.     else // sashPosition == 0
  863.     {
  864.         // default, put it in the centre
  865.         return GetWindowSize() / 2;
  866.     }
  867. }
  868.  
  869. // Remove the specified (or second) window from the view
  870. // Doesn't actually delete the window.
  871. bool wxSplitterWindow::Unsplit(wxWindow *toRemove)
  872. {
  873.     if ( ! IsSplit() )
  874.         return FALSE;
  875.  
  876.     wxWindow *win = NULL;
  877.     if ( toRemove == NULL || toRemove == m_windowTwo)
  878.     {
  879.         win = m_windowTwo ;
  880.         m_windowTwo = (wxWindow *) NULL;
  881.     }
  882.     else if ( toRemove == m_windowOne )
  883.     {
  884.         win = m_windowOne ;
  885.         m_windowOne = m_windowTwo;
  886.         m_windowTwo = (wxWindow *) NULL;
  887.     }
  888.     else
  889.     {
  890.         wxFAIL_MSG(wxT("splitter: attempt to remove a non-existent window"));
  891.  
  892.         return FALSE;
  893.     }
  894.  
  895.     OnUnsplit(win);
  896.     DoSetSashPosition(0);
  897.     SizeWindows();
  898.  
  899.     return TRUE;
  900. }
  901.  
  902. // Replace a window with another one
  903. bool wxSplitterWindow::ReplaceWindow(wxWindow *winOld, wxWindow *winNew)
  904. {
  905.     wxCHECK_MSG( winOld, FALSE, wxT("use one of Split() functions instead") );
  906.     wxCHECK_MSG( winNew, FALSE, wxT("use Unsplit() functions instead") );
  907.  
  908.     if ( winOld == m_windowTwo )
  909.     {
  910.         m_windowTwo = winNew;
  911.     }
  912.     else if ( winOld == m_windowOne )
  913.     {
  914.         m_windowOne = winNew;
  915.     }
  916.     else
  917.     {
  918.         wxFAIL_MSG(wxT("splitter: attempt to replace a non-existent window"));
  919.  
  920.         return FALSE;
  921.     }
  922.  
  923.     SizeWindows();
  924.  
  925.     return TRUE;
  926. }
  927.  
  928. void wxSplitterWindow::SetMinimumPaneSize(int min)
  929. {
  930.     m_minimumPaneSize = min;
  931.     SetSashPosition(m_sashPosition); // re-check limits
  932. }
  933.  
  934. void wxSplitterWindow::SetSashPosition(int position, bool redraw)
  935. {
  936.     DoSetSashPosition(position);
  937.  
  938.     if ( redraw )
  939.     {
  940.         SizeWindows();
  941.     }
  942. }
  943.  
  944. // Initialize colours
  945. void wxSplitterWindow::InitColours()
  946. {
  947.     wxDELETE( m_facePen );
  948.     wxDELETE( m_faceBrush );
  949.     wxDELETE( m_mediumShadowPen );
  950.     wxDELETE( m_darkShadowPen );
  951.     wxDELETE( m_lightShadowPen );
  952.     wxDELETE( m_hilightPen );
  953.  
  954.     // Shadow colours
  955. #ifndef __WIN16__
  956.     wxColour faceColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
  957.     m_facePen = new wxPen(faceColour, 1, wxSOLID);
  958.     m_faceBrush = new wxBrush(faceColour, wxSOLID);
  959.  
  960.     wxColour mediumShadowColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DSHADOW));
  961.     m_mediumShadowPen = new wxPen(mediumShadowColour, 1, wxSOLID);
  962.  
  963.     wxColour darkShadowColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DDKSHADOW));
  964.     m_darkShadowPen = new wxPen(darkShadowColour, 1, wxSOLID);
  965.  
  966.     wxColour lightShadowColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT));
  967.     m_lightShadowPen = new wxPen(lightShadowColour, 1, wxSOLID);
  968.  
  969.     wxColour hilightColour(wxSystemSettings::GetColour(wxSYS_COLOUR_3DHILIGHT));
  970.     m_hilightPen = new wxPen(hilightColour, 1, wxSOLID);
  971. #else
  972.     m_facePen = new wxPen("LIGHT GREY", 1, wxSOLID);
  973.     m_faceBrush = new wxBrush("LIGHT GREY", wxSOLID);
  974.     m_mediumShadowPen = new wxPen("GREY", 1, wxSOLID);
  975.     m_darkShadowPen = new wxPen("BLACK", 1, wxSOLID);
  976.     m_lightShadowPen = new wxPen("LIGHT GREY", 1, wxSOLID);
  977.     m_hilightPen = new wxPen("WHITE", 1, wxSOLID);
  978. #endif // __WIN16__
  979. }
  980.  
  981. bool wxSplitterWindow::DoSendEvent(wxSplitterEvent& event)
  982. {
  983.     return !GetEventHandler()->ProcessEvent(event) || event.IsAllowed();
  984. }
  985.  
  986. // ---------------------------------------------------------------------------
  987. // wxSplitterWindow virtual functions: they now just generate the events
  988. // ---------------------------------------------------------------------------
  989.  
  990. bool wxSplitterWindow::OnSashPositionChange(int WXUNUSED(newSashPosition))
  991. {
  992.     // always allow by default
  993.     return TRUE;
  994. }
  995.  
  996. int wxSplitterWindow::OnSashPositionChanging(int newSashPosition)
  997. {
  998.     // If within UNSPLIT_THRESHOLD from edge, set to edge to cause closure.
  999.     const int UNSPLIT_THRESHOLD = 4;
  1000.  
  1001.     // first of all, check if OnSashPositionChange() doesn't forbid this change
  1002.     if ( !OnSashPositionChange(newSashPosition) )
  1003.     {
  1004.         // it does
  1005.         return -1;
  1006.     }
  1007.  
  1008.     // Obtain relevant window dimension for bottom / right threshold check
  1009.     int window_size = GetWindowSize();
  1010.  
  1011.     bool unsplit_scenario = FALSE;
  1012.     if ( m_permitUnsplitAlways || m_minimumPaneSize == 0 )
  1013.     {
  1014.         // Do edge detection if unsplit premitted
  1015.         if ( newSashPosition <= UNSPLIT_THRESHOLD )
  1016.         {
  1017.             // threshold top / left check
  1018.             newSashPosition = 0;
  1019.             unsplit_scenario = TRUE;
  1020.         }
  1021.         if ( newSashPosition >= window_size - UNSPLIT_THRESHOLD )
  1022.         {
  1023.             // threshold bottom/right check
  1024.             newSashPosition = window_size;
  1025.             unsplit_scenario = TRUE;
  1026.         }
  1027.     }
  1028.  
  1029.     if ( !unsplit_scenario )
  1030.     {
  1031.         // If resultant pane would be too small, enlarge it
  1032.         newSashPosition = AdjustSashPosition(newSashPosition);
  1033.     }
  1034.  
  1035.     // If the result is out of bounds it means minimum size is too big,
  1036.     // so split window in half as best compromise.
  1037.     if ( newSashPosition < 0 || newSashPosition > window_size )
  1038.         newSashPosition = window_size / 2;
  1039.  
  1040.     // now let the event handler have it
  1041.     //
  1042.     // FIXME: shouldn't we do it before the adjustments above so as to ensure
  1043.     //        that the sash position is always reasonable?
  1044.     wxSplitterEvent event(wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING, this);
  1045.     event.m_data.pos = newSashPosition;
  1046.  
  1047.     if ( !DoSendEvent(event) )
  1048.     {
  1049.         // the event handler vetoed the change
  1050.         newSashPosition = -1;
  1051.     }
  1052.     else
  1053.     {
  1054.         // it could have been changed by it
  1055.         newSashPosition = event.GetSashPosition();
  1056.     }
  1057.  
  1058.     return newSashPosition;
  1059. }
  1060.  
  1061. // Called when the sash is double-clicked. The default behaviour is to remove
  1062. // the sash if the minimum pane size is zero.
  1063. void wxSplitterWindow::OnDoubleClickSash(int x, int y)
  1064. {
  1065.     // new code should handle events instead of using the virtual functions
  1066.     wxSplitterEvent event(wxEVT_COMMAND_SPLITTER_DOUBLECLICKED, this);
  1067.     event.m_data.pt.x = x;
  1068.     event.m_data.pt.y = y;
  1069.     if ( DoSendEvent(event) )
  1070.     {
  1071.         if ( GetMinimumPaneSize() == 0 || m_permitUnsplitAlways )
  1072.         {
  1073.             Unsplit();
  1074.         }
  1075.     }
  1076.     //else: blocked by user
  1077. }
  1078.  
  1079. void wxSplitterWindow::OnUnsplit(wxWindow *winRemoved)
  1080. {
  1081.     // do it before calling the event handler which may delete the window
  1082.     winRemoved->Show(FALSE);
  1083.  
  1084.     wxSplitterEvent event(wxEVT_COMMAND_SPLITTER_UNSPLIT, this);
  1085.     event.m_data.win = winRemoved;
  1086.  
  1087.     (void)DoSendEvent(event);
  1088. }
  1089.  
  1090. #ifdef __WXMSW__
  1091.  
  1092. // this is currently called (and needed) under MSW only...
  1093. void wxSplitterWindow::OnSetCursor(wxSetCursorEvent& event)
  1094. {
  1095.     // if we don't do it, the resizing cursor might be set for child window:
  1096.     // and like this we explicitly say that our cursor should not be used for
  1097.     // children windows which overlap us
  1098.  
  1099.     if ( SashHitTest(event.GetX(), event.GetY()) )
  1100.     {
  1101.         // default processing is ok
  1102.         event.Skip();
  1103.     }
  1104.     //else: do nothing, in particular, don't call Skip()
  1105. }
  1106.  
  1107. #endif // wxMSW
  1108.  
  1109.