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