]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/gizmos/splittree.cpp
Added gizmos hierarchy
[wxWidgets.git] / contrib / src / gizmos / splittree.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: splittree.cpp
3 // Purpose: Classes to achieve a remotely-scrolled tree in a splitter
4 // window that can be scrolled by a scrolled window higher in the
5 // hierarchy
6 // Author: Julian Smart
7 // Modified by:
8 // Created: 8/7/2000
9 // RCS-ID: $Id$
10 // Copyright: (c) Julian Smart
11 // Licence: wxWindows licence
12 /////////////////////////////////////////////////////////////////////////////
13
14 // ============================================================================
15 // declarations
16 // ============================================================================
17
18 // ----------------------------------------------------------------------------
19 // headers
20 // ----------------------------------------------------------------------------
21 #ifdef __GNUG__
22 #pragma interface "splittree.cpp"
23 #endif
24
25 // For compilers that support precompilation, includes "wx/wx.h".
26 #include "wx/wxprec.h"
27
28 #ifdef __BORLANDC__
29 #pragma hdrstop
30 #endif
31
32 // for all others, include the necessary headers (this file is usually all you
33 // need because it includes almost all "standard" wxWindows headers)
34 #ifndef WX_PRECOMP
35 #include "wx/wx.h"
36 #endif
37
38 #include "wx/generic/treectlg.h"
39
40 #include "wx/gizmos/splittree.h"
41
42 /*
43 * wxRemotelyScrolledTreeCtrl
44 */
45
46 #if USE_GENERIC_TREECTRL
47 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
48 #else
49 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxTreeCtrl)
50 #endif
51
52 #if USE_GENERIC_TREECTRL
53 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
54 #else
55 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl, wxTreeCtrl)
56 #endif
57 EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize)
58 EVT_PAINT(wxRemotelyScrolledTreeCtrl::OnPaint)
59 EVT_TREE_ITEM_EXPANDED(-1, wxRemotelyScrolledTreeCtrl::OnExpand)
60 EVT_TREE_ITEM_COLLAPSED(-1, wxRemotelyScrolledTreeCtrl::OnExpand)
61 EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll)
62 END_EVENT_TABLE()
63
64 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pt,
65 const wxSize& sz, long style):
66 wxTreeCtrl(parent, id, pt, sz, style)
67 {
68 }
69
70 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
71 {
72 }
73
74 void wxRemotelyScrolledTreeCtrl::HideVScrollbar()
75 {
76 #ifdef __WXMSW__
77 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
78 {
79 ::ShowScrollBar((HWND) GetHWND(), SB_VERT, FALSE);
80 }
81 else
82 #endif
83 {
84 // Implicit in overriding SetScrollbars
85 }
86 }
87
88 // Number of pixels per user unit (0 or -1 for no scrollbar)
89 // Length of virtual canvas in user units
90 // Length of page in user units
91 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int pixelsPerUnitX, int pixelsPerUnitY,
92 int noUnitsX, int noUnitsY,
93 int xPos, int yPos,
94 bool noRefresh)
95 {
96 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
97 {
98 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
99 win->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX, 0, noUnitsX, 0, xPos, 0, noRefresh);
100
101 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
102 if (scrolledWindow)
103 {
104 scrolledWindow->SetScrollbars(0, pixelsPerUnitY, 0, noUnitsY, 0, yPos, noRefresh);
105 }
106 }
107 }
108
109 // In case we're using the generic tree control.
110 // Get the view start
111 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x, int *y) const
112 {
113 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
114 {
115 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
116
117 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
118 int x1, y1, x2, y2;
119 win->wxGenericTreeCtrl::GetViewStart(& x1, & y1);
120 * x = x1; * y = y1;
121 if (!scrolledWindow)
122 return;
123
124 scrolledWindow->GetViewStart(& x2, & y2);
125 * y = y2;
126 }
127 }
128
129 // In case we're using the generic tree control.
130 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC& dc)
131 {
132 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
133 {
134 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
135
136 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
137
138 int startX, startY;
139 GetViewStart(& startX, & startY);
140
141 int xppu1, yppu1, xppu2, yppu2;
142 win->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1, & yppu1);
143 scrolledWindow->GetScrollPixelsPerUnit(& xppu2, & yppu2);
144
145 dc.SetDeviceOrigin( -startX * xppu1, -startY * yppu2 );
146 dc.SetUserScale( m_scaleX, m_scaleY );
147 }
148 }
149
150 // Scroll to the given line (in scroll units where each unit is
151 // the height of an item)
152 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz, int posVert)
153 {
154 #ifdef __WXMSW__
155 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
156 {
157 UINT sbCode = SB_THUMBPOSITION;
158 HWND vertScrollBar = 0;
159 MSWDefWindowProc((WXUINT) WM_VSCROLL, MAKELONG(sbCode, posVert), (WXHWND) vertScrollBar);
160 }
161 else
162 #endif
163 {
164 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
165 win->Refresh();
166 /* Doesn't work yet because scrolling is ignored by Scroll
167 int xppu, yppu;
168 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
169 if (scrolledWindow)
170 {
171 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
172 win->Scroll(-1, posVert*yppu);
173 }
174 */
175 }
176 }
177
178 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent& event)
179 {
180 HideVScrollbar();
181 AdjustRemoteScrollbars();
182 event.Skip();
183 }
184
185 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent& event)
186 {
187 AdjustRemoteScrollbars();
188 event.Skip();
189
190 // If we don't have this, we get some bits of lines still remaining
191 if (event.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED)
192 Refresh();
193 }
194
195 // Adjust the containing wxScrolledWindow's scrollbars appropriately
196 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
197 {
198 // WILL THIS BE DONE AUTOMATICALLY BY THE GENERIC TREE CONTROL?
199 /*
200
201 Problem with remote-scrolling the generic tree control. It relies
202 on PrepareDC for adjusting the device origin, which in turn takes
203 values from wxScrolledWindow: which we've turned off in order to use
204 a different scrollbar :-( So we could override PrepareDC and use
205 the _other_ scrolled window's position instead.
206 Note also ViewStart would need to be overridden.
207 Plus, wxGenericTreeCtrl::OnPaint will reset the device origin.
208
209 */
210
211 // Assumption: wxGenericTreeCtrl will adjust the scrollbars automatically,
212 // since it'll call SetScrollbars and we've defined this to Do The Right Thing.
213 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
214 return;
215
216 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
217 if (scrolledWindow)
218 {
219 wxRect itemRect;
220 if (GetBoundingRect(GetRootItem(), itemRect))
221 {
222 int itemHeight = itemRect.GetHeight();
223
224 int w, h;
225 GetClientSize(&w, &h);
226
227 wxRect rect(0, 0, 0, 0);
228 CalcTreeSize(rect);
229 int treeViewHeight = rect.GetHeight()/itemHeight;
230
231 int scrollPixelsPerLine = itemHeight;
232 int scrollPos = - (itemRect.y / itemHeight);
233
234 scrolledWindow->SetScrollbars(0, scrollPixelsPerLine, 0, treeViewHeight, 0, scrollPos);
235
236 // Ensure that when a scrollbar becomes hidden or visible,
237 // the contained window sizes are right.
238 // Problem: this is called too early (?)
239 wxSizeEvent event(scrolledWindow->GetSize(), scrolledWindow->GetId());
240 scrolledWindow->GetEventHandler()->ProcessEvent(event);
241 }
242 }
243 }
244
245
246 // Calculate the area that contains both rectangles
247 static wxRect CombineRectangles(const wxRect& rect1, const wxRect& rect2)
248 {
249 wxRect rect;
250
251 int right1 = rect1.GetRight();
252 int bottom1 = rect1.GetBottom();
253 int right2 = rect2.GetRight();
254 int bottom2 = rect2.GetBottom();
255
256 wxPoint topLeft = wxPoint(wxMin(rect1.x, rect2.x), wxMin(rect1.y, rect2.y));
257 wxPoint bottomRight = wxPoint(wxMax(right1, right2), wxMax(bottom1, bottom2));
258
259 rect.x = topLeft.x; rect.y = topLeft.y;
260 rect.SetRight(bottomRight.x);
261 rect.SetBottom(bottomRight.y);
262
263 return rect;
264 }
265
266
267 // Calculate the tree overall size so we can set the scrollbar
268 // correctly
269 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect& rect)
270 {
271 CalcTreeSize(GetRootItem(), rect);
272 }
273
274 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxTreeItemId& id, wxRect& rect)
275 {
276 // TODO: implement GetFirst/NextVisibleItem
277 // for wxGenericTreeCtrl, plus GetBoundingRect.
278
279 // More efficient implementation would be to find the last item (but how?)
280 // Q: is the bounding rect relative to the top of the virtual tree workspace
281 // or the top of the window? How would we convert?
282 wxRect itemSize;
283 if (GetBoundingRect(id, itemSize))
284 {
285 rect = CombineRectangles(rect, itemSize);
286 }
287
288 long cookie;
289 wxTreeItemId childId = GetFirstChild(id, cookie);
290 while (childId != 0)
291 {
292 CalcTreeSize(childId, rect);
293 childId = GetNextChild(childId, cookie);
294 }
295 }
296
297 // Find the scrolled window that contains this control
298 wxScrolledWindow* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
299 {
300 wxWindow* parent = wxWindow::GetParent();
301 while (parent)
302 {
303 if (parent->IsKindOf(CLASSINFO(wxScrolledWindow)))
304 return (wxScrolledWindow*) parent;
305 parent = parent->GetParent();
306 }
307 return NULL;
308 }
309
310 void wxRemotelyScrolledTreeCtrl::OnPaint(wxPaintEvent& event)
311 {
312 wxPaintDC dc(this);
313
314 wxTreeCtrl::OnPaint(event);
315
316 // Reset the device origin since it may have been set
317 dc.SetDeviceOrigin(0, 0);
318
319 wxSize sz = GetClientSize();
320
321 wxPen pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID);
322 dc.SetPen(pen);
323 dc.SetBrush(* wxTRANSPARENT_BRUSH);
324
325 wxRect itemRect;
326 if (GetBoundingRect(GetRootItem(), itemRect))
327 {
328 int itemHeight = itemRect.GetHeight();
329 wxRect rcClient = GetRect();
330 int cy=0;
331 wxTreeItemId h;
332 for(h=GetFirstVisibleItem();h;h=GetNextVisible(h))
333 {
334 dc.DrawLine(rcClient.x, cy, rcClient.x + rcClient.width, cy);
335 cy += itemHeight;
336 }
337 dc.DrawLine(rcClient.x, cy, rcClient.x + rcClient.width, cy);
338 }
339 }
340
341 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent& event)
342 {
343 int orient = event.GetOrientation();
344 if (orient == wxHORIZONTAL)
345 {
346 // Don't 'skip' or we'd get into infinite recursion
347 return;
348 }
349 wxScrolledWindow* scrollWin = GetScrolledWindow();
350 if (!scrollWin)
351 return;
352
353 int x, y;
354 scrollWin->GetViewStart(& x, & y);
355
356 ScrollToLine(-1, y);
357 }
358
359 /*
360 * wxThinSplitterWindow
361 */
362
363 IMPLEMENT_CLASS(wxThinSplitterWindow, wxSplitterWindow)
364
365 BEGIN_EVENT_TABLE(wxThinSplitterWindow, wxSplitterWindow)
366 EVT_SIZE(wxThinSplitterWindow::OnSize)
367 END_EVENT_TABLE()
368
369 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow* parent, wxWindowID id,
370 const wxPoint& pos,
371 const wxSize& sz,
372 long style):
373 wxSplitterWindow(parent, id, pos, sz, style)
374 {
375 }
376
377 void wxThinSplitterWindow::SizeWindows()
378 {
379 // The client size may have changed inbetween
380 // the sizing of the first window and the sizing of
381 // the second. So repeat SizeWindows.
382 wxSplitterWindow::SizeWindows();
383 wxSplitterWindow::SizeWindows();
384 }
385
386 // Tests for x, y over sash
387 bool wxThinSplitterWindow::SashHitTest(int x, int y, int tolerance)
388 {
389 return wxSplitterWindow::SashHitTest(x, y, 4);
390 }
391
392 void wxThinSplitterWindow::DrawSash(wxDC& dc)
393 {
394 if ( m_sashPosition == 0 || !m_windowTwo)
395 return;
396 if (GetWindowStyle() & wxSP_NOSASH)
397 return;
398
399 int w, h;
400 GetClientSize(&w, &h);
401
402 if ( m_splitMode == wxSPLIT_VERTICAL )
403 {
404 dc.SetPen(* m_facePen);
405 dc.SetBrush(* m_faceBrush);
406 int h1 = h-1;
407 int y1 = 0;
408 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
409 h1 += 1; // Not sure why this is necessary...
410 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
411 {
412 y1 = 2; h1 -= 3;
413 }
414 dc.DrawRectangle(m_sashPosition, y1, m_sashSize, h1);
415 }
416 else
417 {
418 dc.SetPen(* m_facePen);
419 dc.SetBrush(* m_faceBrush);
420 int w1 = w-1;
421 int x1 = 0;
422 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
423 w1 ++;
424 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
425 {
426 x1 = 2; w1 -= 3;
427 }
428 dc.DrawRectangle(x1, m_sashPosition, w1, m_sashSize);
429 }
430
431 dc.SetPen(wxNullPen);
432 dc.SetBrush(wxNullBrush);
433 }
434
435 void wxThinSplitterWindow::OnSize(wxSizeEvent& event)
436 {
437 wxSplitterWindow::OnSize(event);
438 }
439
440 /*
441 * wxSplitterScrolledWindow
442 */
443
444 IMPLEMENT_CLASS(wxSplitterScrolledWindow, wxScrolledWindow)
445
446 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow, wxScrolledWindow)
447 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll)
448 EVT_SIZE(wxSplitterScrolledWindow::OnSize)
449 END_EVENT_TABLE()
450
451 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow* parent, wxWindowID id,
452 const wxPoint& pos,
453 const wxSize& sz,
454 long style):
455 wxScrolledWindow(parent, id, pos, sz, style)
456 {
457 }
458
459 void wxSplitterScrolledWindow::OnSize(wxSizeEvent& event)
460 {
461 wxSize sz = GetClientSize();
462 if (GetChildren().First())
463 {
464 ((wxWindow*) GetChildren().First()->Data())->SetSize(0, 0, sz.x, sz.y);
465 }
466 }
467
468 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent& event)
469 {
470 // Ensure that events being propagated back up the window hierarchy
471 // don't cause an infinite loop
472 static bool inOnScroll = FALSE;
473 if (inOnScroll)
474 return;
475 inOnScroll = TRUE;
476
477 int orient = event.GetOrientation();
478
479 int nScrollInc = CalcScrollInc(event);
480 if (nScrollInc == 0)
481 {
482 inOnScroll = FALSE;
483 return;
484 }
485
486 if (orient == wxHORIZONTAL)
487 {
488 int newPos = m_xScrollPosition + nScrollInc;
489 SetScrollPos(wxHORIZONTAL, newPos, TRUE );
490 }
491 else
492 {
493 int newPos = m_yScrollPosition + nScrollInc;
494 SetScrollPos(wxVERTICAL, newPos, TRUE );
495 }
496
497 if (orient == wxHORIZONTAL)
498 {
499 m_xScrollPosition += nScrollInc;
500 }
501 else
502 {
503 m_yScrollPosition += nScrollInc;
504 }
505
506 // Find targets in splitter window and send the event to them
507 wxNode* node = GetChildren().First();
508 while (node)
509 {
510 wxWindow* child = (wxWindow*) node->Data();
511 if (child->IsKindOf(CLASSINFO(wxSplitterWindow)))
512 {
513 wxSplitterWindow* splitter = (wxSplitterWindow*) child;
514 if (splitter->GetWindow1())
515 splitter->GetWindow1()->ProcessEvent(event);
516 if (splitter->GetWindow2())
517 splitter->GetWindow2()->ProcessEvent(event);
518 break;
519 }
520 node = node->Next();
521 }
522
523 #ifdef __WXMAC__
524 m_targetWindow->MacUpdateImmediately() ;
525 #endif
526
527 inOnScroll = FALSE;
528 }
529