| 1 | ///////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: wx/splitter.h |
| 3 | // Purpose: wxSplitterWindow class |
| 4 | // Author: Julian Smart |
| 5 | // Modified by: |
| 6 | // Created: 01/02/97 |
| 7 | // RCS-ID: $Id$ |
| 8 | // Copyright: (c) Julian Smart |
| 9 | // Licence: wxWindows licence |
| 10 | ///////////////////////////////////////////////////////////////////////////// |
| 11 | |
| 12 | #ifndef _WX_GENERIC_SPLITTER_H_ |
| 13 | #define _WX_GENERIC_SPLITTER_H_ |
| 14 | |
| 15 | #include "wx/window.h" // base class declaration |
| 16 | #include "wx/containr.h" // wxControlContainer |
| 17 | |
| 18 | class WXDLLEXPORT wxSplitterEvent; |
| 19 | |
| 20 | // --------------------------------------------------------------------------- |
| 21 | // splitter constants |
| 22 | // --------------------------------------------------------------------------- |
| 23 | |
| 24 | enum wxSplitMode |
| 25 | { |
| 26 | wxSPLIT_HORIZONTAL = 1, |
| 27 | wxSPLIT_VERTICAL |
| 28 | }; |
| 29 | |
| 30 | enum |
| 31 | { |
| 32 | wxSPLIT_DRAG_NONE, |
| 33 | wxSPLIT_DRAG_DRAGGING, |
| 34 | wxSPLIT_DRAG_LEFT_DOWN |
| 35 | }; |
| 36 | |
| 37 | // --------------------------------------------------------------------------- |
| 38 | // wxSplitterWindow maintains one or two panes, with |
| 39 | // an optional vertical or horizontal split which |
| 40 | // can be used with the mouse or programmatically. |
| 41 | // --------------------------------------------------------------------------- |
| 42 | |
| 43 | // TODO: |
| 44 | // 1) Perhaps make the borders sensitive to dragging in order to create a split. |
| 45 | // The MFC splitter window manages scrollbars as well so is able to |
| 46 | // put sash buttons on the scrollbars, but we probably don't want to go down |
| 47 | // this path. |
| 48 | // 2) for wxWidgets 2.0, we must find a way to set the WS_CLIPCHILDREN style |
| 49 | // to prevent flickering. (WS_CLIPCHILDREN doesn't work in all cases so can't be |
| 50 | // standard). |
| 51 | |
| 52 | class WXDLLEXPORT wxSplitterWindow: public wxWindow |
| 53 | { |
| 54 | public: |
| 55 | |
| 56 | //////////////////////////////////////////////////////////////////////////// |
| 57 | // Public API |
| 58 | |
| 59 | // Default constructor |
| 60 | wxSplitterWindow() |
| 61 | { |
| 62 | Init(); |
| 63 | } |
| 64 | |
| 65 | // Normal constructor |
| 66 | wxSplitterWindow(wxWindow *parent, wxWindowID id = wxID_ANY, |
| 67 | const wxPoint& pos = wxDefaultPosition, |
| 68 | const wxSize& size = wxDefaultSize, |
| 69 | long style = wxSP_3D, |
| 70 | const wxString& name = wxT("splitter")) |
| 71 | { |
| 72 | Init(); |
| 73 | Create(parent, id, pos, size, style, name); |
| 74 | } |
| 75 | |
| 76 | virtual ~wxSplitterWindow(); |
| 77 | |
| 78 | bool Create(wxWindow *parent, wxWindowID id = wxID_ANY, |
| 79 | const wxPoint& pos = wxDefaultPosition, |
| 80 | const wxSize& size = wxDefaultSize, |
| 81 | long style = wxSP_3D, |
| 82 | const wxString& name = wxT("splitter")); |
| 83 | |
| 84 | // Gets the only or left/top pane |
| 85 | wxWindow *GetWindow1() const { return m_windowOne; } |
| 86 | |
| 87 | // Gets the right/bottom pane |
| 88 | wxWindow *GetWindow2() const { return m_windowTwo; } |
| 89 | |
| 90 | // Sets the split mode |
| 91 | void SetSplitMode(int mode) |
| 92 | { |
| 93 | wxASSERT_MSG( mode == wxSPLIT_VERTICAL || mode == wxSPLIT_HORIZONTAL, |
| 94 | _T("invalid split mode") ); |
| 95 | |
| 96 | m_splitMode = (wxSplitMode)mode; |
| 97 | } |
| 98 | |
| 99 | // Gets the split mode |
| 100 | wxSplitMode GetSplitMode() const { return m_splitMode; } |
| 101 | |
| 102 | // Initialize with one window |
| 103 | void Initialize(wxWindow *window); |
| 104 | |
| 105 | // Associates the given window with window 2, drawing the appropriate sash |
| 106 | // and changing the split mode. |
| 107 | // Does nothing and returns false if the window is already split. |
| 108 | // A sashPosition of 0 means choose a default sash position, |
| 109 | // negative sashPosition specifies the size of right/lower pane as it's |
| 110 | // absolute value rather than the size of left/upper pane. |
| 111 | virtual bool SplitVertically(wxWindow *window1, |
| 112 | wxWindow *window2, |
| 113 | int sashPosition = 0) |
| 114 | { return DoSplit(wxSPLIT_VERTICAL, window1, window2, sashPosition); } |
| 115 | virtual bool SplitHorizontally(wxWindow *window1, |
| 116 | wxWindow *window2, |
| 117 | int sashPosition = 0) |
| 118 | { return DoSplit(wxSPLIT_HORIZONTAL, window1, window2, sashPosition); } |
| 119 | |
| 120 | // Removes the specified (or second) window from the view |
| 121 | // Doesn't actually delete the window. |
| 122 | bool Unsplit(wxWindow *toRemove = (wxWindow *) NULL); |
| 123 | |
| 124 | // Replaces one of the windows with another one (neither old nor new |
| 125 | // parameter should be NULL) |
| 126 | bool ReplaceWindow(wxWindow *winOld, wxWindow *winNew); |
| 127 | |
| 128 | // Make sure the child window sizes are updated. This is useful |
| 129 | // for reducing flicker by updating the sizes before a |
| 130 | // window is shown, if you know the overall size is correct. |
| 131 | void UpdateSize(); |
| 132 | |
| 133 | // Is the window split? |
| 134 | bool IsSplit() const { return (m_windowTwo != NULL); } |
| 135 | |
| 136 | // Sets the sash size |
| 137 | void SetSashSize(int width) { m_sashSize = width; } |
| 138 | |
| 139 | // Sets the border size |
| 140 | void SetBorderSize(int WXUNUSED(width)) { } |
| 141 | |
| 142 | // Gets the sash size |
| 143 | int GetSashSize() const; |
| 144 | |
| 145 | // Gets the border size |
| 146 | int GetBorderSize() const; |
| 147 | |
| 148 | // Set the sash position |
| 149 | void SetSashPosition(int position, bool redraw = true); |
| 150 | |
| 151 | // Gets the sash position |
| 152 | int GetSashPosition() const { return m_sashPosition; } |
| 153 | |
| 154 | // Set the sash gravity |
| 155 | void SetSashGravity(double gravity); |
| 156 | |
| 157 | // Gets the sash gravity |
| 158 | double GetSashGravity() const { return m_sashGravity; } |
| 159 | |
| 160 | // If this is zero, we can remove panes by dragging the sash. |
| 161 | void SetMinimumPaneSize(int min); |
| 162 | int GetMinimumPaneSize() const { return m_minimumPaneSize; } |
| 163 | |
| 164 | // NB: the OnXXX() functions below are for backwards compatibility only, |
| 165 | // don't use them in new code but handle the events instead! |
| 166 | |
| 167 | // called when the sash position is about to change, may return a new value |
| 168 | // for the sash or -1 to prevent the change from happening at all |
| 169 | virtual int OnSashPositionChanging(int newSashPosition); |
| 170 | |
| 171 | // Called when the sash position is about to be changed, return |
| 172 | // false from here to prevent the change from taking place. |
| 173 | // Repositions sash to minimum position if pane would be too small. |
| 174 | // newSashPosition here is always positive or zero. |
| 175 | virtual bool OnSashPositionChange(int newSashPosition); |
| 176 | |
| 177 | // If the sash is moved to an extreme position, a subwindow |
| 178 | // is removed from the splitter window, and the app is |
| 179 | // notified. The app should delete or hide the window. |
| 180 | virtual void OnUnsplit(wxWindow *removed); |
| 181 | |
| 182 | // Called when the sash is double-clicked. |
| 183 | // The default behaviour is to remove the sash if the |
| 184 | // minimum pane size is zero. |
| 185 | virtual void OnDoubleClickSash(int x, int y); |
| 186 | |
| 187 | //////////////////////////////////////////////////////////////////////////// |
| 188 | // Implementation |
| 189 | |
| 190 | // Paints the border and sash |
| 191 | void OnPaint(wxPaintEvent& event); |
| 192 | |
| 193 | // Handles mouse events |
| 194 | void OnMouseEvent(wxMouseEvent& ev); |
| 195 | |
| 196 | // Adjusts the panes |
| 197 | void OnSize(wxSizeEvent& event); |
| 198 | |
| 199 | // In live mode, resize child windows in idle time |
| 200 | void OnInternalIdle(); |
| 201 | |
| 202 | // Draws the sash |
| 203 | virtual void DrawSash(wxDC& dc); |
| 204 | |
| 205 | // Draws the sash tracker (for whilst moving the sash) |
| 206 | virtual void DrawSashTracker(int x, int y); |
| 207 | |
| 208 | // Tests for x, y over sash |
| 209 | virtual bool SashHitTest(int x, int y, int tolerance = 5); |
| 210 | |
| 211 | // Resizes subwindows |
| 212 | virtual void SizeWindows(); |
| 213 | |
| 214 | void SetNeedUpdating(bool needUpdating) { m_needUpdating = needUpdating; } |
| 215 | bool GetNeedUpdating() const { return m_needUpdating ; } |
| 216 | |
| 217 | #ifdef __WXMAC__ |
| 218 | virtual bool MacClipGrandChildren() const { return true ; } |
| 219 | #endif |
| 220 | |
| 221 | protected: |
| 222 | // event handlers |
| 223 | #if defined(__WXMSW__) || defined(__WXMAC__) |
| 224 | void OnSetCursor(wxSetCursorEvent& event); |
| 225 | #endif // wxMSW |
| 226 | |
| 227 | // send the given event, return false if the event was processed and vetoed |
| 228 | // by the user code |
| 229 | bool DoSendEvent(wxSplitterEvent& event); |
| 230 | |
| 231 | // common part of all ctors |
| 232 | void Init(); |
| 233 | |
| 234 | // common part of SplitVertically() and SplitHorizontally() |
| 235 | bool DoSplit(wxSplitMode mode, |
| 236 | wxWindow *window1, wxWindow *window2, |
| 237 | int sashPosition); |
| 238 | |
| 239 | // adjusts sash position with respect to min. pane and window sizes |
| 240 | int AdjustSashPosition(int sashPos) const; |
| 241 | |
| 242 | // get either width or height depending on the split mode |
| 243 | int GetWindowSize() const; |
| 244 | |
| 245 | // convert the user specified sash position which may be > 0 (as is), < 0 |
| 246 | // (specifying the size of the right pane) or 0 (use default) to the real |
| 247 | // position to be passed to DoSetSashPosition() |
| 248 | int ConvertSashPosition(int sashPos) const; |
| 249 | |
| 250 | // set the real sash position, sashPos here must be positive |
| 251 | // |
| 252 | // returns true if the sash position has been changed, false otherwise |
| 253 | bool DoSetSashPosition(int sashPos); |
| 254 | |
| 255 | // set the sash position and send an event about it having been changed |
| 256 | void SetSashPositionAndNotify(int sashPos); |
| 257 | |
| 258 | // callbacks executed when we detect that the mouse has entered or left |
| 259 | // the sash |
| 260 | virtual void OnEnterSash(); |
| 261 | virtual void OnLeaveSash(); |
| 262 | |
| 263 | // set the cursor appropriate for the current split mode |
| 264 | void SetResizeCursor(); |
| 265 | |
| 266 | // redraw the splitter if its "hotness" changed if necessary |
| 267 | void RedrawIfHotSensitive(bool isHot); |
| 268 | |
| 269 | // return the best size of the splitter equal to best sizes of its |
| 270 | // subwindows |
| 271 | virtual wxSize DoGetBestSize() const; |
| 272 | |
| 273 | |
| 274 | wxSplitMode m_splitMode; |
| 275 | wxWindow* m_windowOne; |
| 276 | wxWindow* m_windowTwo; |
| 277 | int m_dragMode; |
| 278 | int m_oldX; |
| 279 | int m_oldY; |
| 280 | int m_sashPosition; // Number of pixels from left or top |
| 281 | double m_sashGravity; |
| 282 | int m_sashSize; |
| 283 | wxSize m_lastSize; |
| 284 | int m_requestedSashPosition; |
| 285 | int m_sashPositionCurrent; // while dragging |
| 286 | int m_firstX; |
| 287 | int m_firstY; |
| 288 | int m_minimumPaneSize; |
| 289 | wxCursor m_sashCursorWE; |
| 290 | wxCursor m_sashCursorNS; |
| 291 | wxPen *m_sashTrackerPen; |
| 292 | |
| 293 | // when in live mode, set this to true to resize children in idle |
| 294 | bool m_needUpdating:1; |
| 295 | bool m_permitUnsplitAlways:1; |
| 296 | bool m_isHot:1; |
| 297 | bool m_checkRequestedSashPosition:1; |
| 298 | |
| 299 | private: |
| 300 | WX_DECLARE_CONTROL_CONTAINER(); |
| 301 | |
| 302 | DECLARE_DYNAMIC_CLASS(wxSplitterWindow) |
| 303 | DECLARE_EVENT_TABLE() |
| 304 | DECLARE_NO_COPY_CLASS(wxSplitterWindow) |
| 305 | }; |
| 306 | |
| 307 | // ---------------------------------------------------------------------------- |
| 308 | // event class and macros |
| 309 | // ---------------------------------------------------------------------------- |
| 310 | |
| 311 | // we reuse the same class for all splitter event types because this is the |
| 312 | // usual wxWin convention, but the three event types have different kind of |
| 313 | // data associated with them, so the accessors can be only used if the real |
| 314 | // event type matches with the one for which the accessors make sense |
| 315 | class WXDLLEXPORT wxSplitterEvent : public wxNotifyEvent |
| 316 | { |
| 317 | public: |
| 318 | wxSplitterEvent(wxEventType type = wxEVT_NULL, |
| 319 | wxSplitterWindow *splitter = (wxSplitterWindow *)NULL) |
| 320 | : wxNotifyEvent(type) |
| 321 | { |
| 322 | SetEventObject(splitter); |
| 323 | if (splitter) m_id = splitter->GetId(); |
| 324 | } |
| 325 | |
| 326 | // SASH_POS_CHANGED methods |
| 327 | |
| 328 | // setting the sash position to -1 prevents the change from taking place at |
| 329 | // all |
| 330 | void SetSashPosition(int pos) |
| 331 | { |
| 332 | wxASSERT( GetEventType() == wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED |
| 333 | || GetEventType() == wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING); |
| 334 | |
| 335 | m_data.pos = pos; |
| 336 | } |
| 337 | |
| 338 | int GetSashPosition() const |
| 339 | { |
| 340 | wxASSERT( GetEventType() == wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGED |
| 341 | || GetEventType() == wxEVT_COMMAND_SPLITTER_SASH_POS_CHANGING); |
| 342 | |
| 343 | return m_data.pos; |
| 344 | } |
| 345 | |
| 346 | // UNSPLIT event methods |
| 347 | wxWindow *GetWindowBeingRemoved() const |
| 348 | { |
| 349 | wxASSERT( GetEventType() == wxEVT_COMMAND_SPLITTER_UNSPLIT ); |
| 350 | |
| 351 | return m_data.win; |
| 352 | } |
| 353 | |
| 354 | // DCLICK event methods |
| 355 | int GetX() const |
| 356 | { |
| 357 | wxASSERT( GetEventType() == wxEVT_COMMAND_SPLITTER_DOUBLECLICKED ); |
| 358 | |
| 359 | return m_data.pt.x; |
| 360 | } |
| 361 | |
| 362 | int GetY() const |
| 363 | { |
| 364 | wxASSERT( GetEventType() == wxEVT_COMMAND_SPLITTER_DOUBLECLICKED ); |
| 365 | |
| 366 | return m_data.pt.y; |
| 367 | } |
| 368 | |
| 369 | private: |
| 370 | friend class WXDLLEXPORT wxSplitterWindow; |
| 371 | |
| 372 | // data for the different types of event |
| 373 | union |
| 374 | { |
| 375 | int pos; // position for SASH_POS_CHANGED event |
| 376 | wxWindow *win; // window being removed for UNSPLIT event |
| 377 | struct |
| 378 | { |
| 379 | int x, y; |
| 380 | } pt; // position of double click for DCLICK event |
| 381 | } m_data; |
| 382 | |
| 383 | DECLARE_DYNAMIC_CLASS_NO_COPY(wxSplitterEvent) |
| 384 | }; |
| 385 | |
| 386 | typedef void (wxEvtHandler::*wxSplitterEventFunction)(wxSplitterEvent&); |
| 387 | |
| 388 | #define wxSplitterEventHandler(func) \ |
| 389 | (wxObjectEventFunction)(wxEventFunction)wxStaticCastEvent(wxSplitterEventFunction, &func) |
| 390 | |
| 391 | #define wx__DECLARE_SPLITTEREVT(evt, id, fn) \ |
| 392 | wx__DECLARE_EVT1(wxEVT_COMMAND_SPLITTER_ ## evt, id, wxSplitterEventHandler(fn)) |
| 393 | |
| 394 | #define EVT_SPLITTER_SASH_POS_CHANGED(id, fn) \ |
| 395 | wx__DECLARE_SPLITTEREVT(SASH_POS_CHANGED, id, fn) |
| 396 | |
| 397 | #define EVT_SPLITTER_SASH_POS_CHANGING(id, fn) \ |
| 398 | wx__DECLARE_SPLITTEREVT(SASH_POS_CHANGING, id, fn) |
| 399 | |
| 400 | #define EVT_SPLITTER_DCLICK(id, fn) \ |
| 401 | wx__DECLARE_SPLITTEREVT(DOUBLECLICKED, id, fn) |
| 402 | |
| 403 | #define EVT_SPLITTER_UNSPLIT(id, fn) \ |
| 404 | wx__DECLARE_SPLITTEREVT(UNSPLIT, id, fn) |
| 405 | |
| 406 | #endif // _WX_GENERIC_SPLITTER_H_ |