]> git.saurik.com Git - wxWidgets.git/blame - contrib/src/gizmos/splittree.cpp
Add quantize and treebase
[wxWidgets.git] / contrib / src / gizmos / splittree.cpp
CommitLineData
58580a7e
JS
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
47IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
48#else
49IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxTreeCtrl)
50#endif
51
52#if USE_GENERIC_TREECTRL
53BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
54#else
55BEGIN_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)
62END_EVENT_TABLE()
63
64wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pt,
65 const wxSize& sz, long style):
66 wxTreeCtrl(parent, id, pt, sz, style)
67{
68}
69
70wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
71{
72}
73
74void 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
91void 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
111void 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.
130void 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 );
37980ec8 146 dc.SetUserScale( win->GetScaleX(), win->GetScaleY() );
58580a7e
JS
147 }
148}
149
150// Scroll to the given line (in scroll units where each unit is
151// the height of an item)
152void 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
178void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent& event)
179{
180 HideVScrollbar();
181 AdjustRemoteScrollbars();
182 event.Skip();
183}
184
185void 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
196void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
197{
198 // WILL THIS BE DONE AUTOMATICALLY BY THE GENERIC TREE CONTROL?
199/*
200
201Problem with remote-scrolling the generic tree control. It relies
202on PrepareDC for adjusting the device origin, which in turn takes
203values from wxScrolledWindow: which we've turned off in order to use
204a different scrollbar :-( So we could override PrepareDC and use
205the _other_ scrolled window's position instead.
206Note also ViewStart would need to be overridden.
207Plus, 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
247static 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
269void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect& rect)
270{
271 CalcTreeSize(GetRootItem(), rect);
272}
273
274void 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
298wxScrolledWindow* 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
310void 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
341void 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
363IMPLEMENT_CLASS(wxThinSplitterWindow, wxSplitterWindow)
364
365BEGIN_EVENT_TABLE(wxThinSplitterWindow, wxSplitterWindow)
366 EVT_SIZE(wxThinSplitterWindow::OnSize)
367END_EVENT_TABLE()
368
369wxThinSplitterWindow::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
377void 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
387bool wxThinSplitterWindow::SashHitTest(int x, int y, int tolerance)
388{
389 return wxSplitterWindow::SashHitTest(x, y, 4);
390}
391
392void 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
435void wxThinSplitterWindow::OnSize(wxSizeEvent& event)
436{
437 wxSplitterWindow::OnSize(event);
438}
439
440/*
441 * wxSplitterScrolledWindow
442 */
443
444IMPLEMENT_CLASS(wxSplitterScrolledWindow, wxScrolledWindow)
445
446BEGIN_EVENT_TABLE(wxSplitterScrolledWindow, wxScrolledWindow)
447 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll)
448 EVT_SIZE(wxSplitterScrolledWindow::OnSize)
449END_EVENT_TABLE()
450
451wxSplitterScrolledWindow::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
459void 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
468void 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