]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/gizmos/splittree.cpp
84f52d843161f6927ad848dfec5d5a5350800145
[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 implementation "splittree.h"
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 #ifdef __WXMSW__
39 #include <windows.h>
40 #include "wx/msw/winundef.h"
41 #endif
42
43 #include "wx/gizmos/splittree.h"
44 #include <math.h>
45
46 /*
47 * wxRemotelyScrolledTreeCtrl
48 */
49
50 #if USE_GENERIC_TREECTRL
51 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
52 #else
53 IMPLEMENT_CLASS(wxRemotelyScrolledTreeCtrl, wxTreeCtrl)
54 #endif
55
56 #if USE_GENERIC_TREECTRL
57 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl, wxGenericTreeCtrl)
58 #else
59 BEGIN_EVENT_TABLE(wxRemotelyScrolledTreeCtrl, wxTreeCtrl)
60 #endif
61 EVT_SIZE(wxRemotelyScrolledTreeCtrl::OnSize)
62 EVT_TREE_ITEM_EXPANDED(-1, wxRemotelyScrolledTreeCtrl::OnExpand)
63 EVT_TREE_ITEM_COLLAPSED(-1, wxRemotelyScrolledTreeCtrl::OnExpand)
64 EVT_SCROLLWIN(wxRemotelyScrolledTreeCtrl::OnScroll)
65 END_EVENT_TABLE()
66
67 wxRemotelyScrolledTreeCtrl::wxRemotelyScrolledTreeCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pt,
68 const wxSize& sz, long style):
69 wxTreeCtrl(parent, id, pt, sz, style)
70 {
71 m_companionWindow = NULL;
72 }
73
74 wxRemotelyScrolledTreeCtrl::~wxRemotelyScrolledTreeCtrl()
75 {
76 }
77
78 void wxRemotelyScrolledTreeCtrl::HideVScrollbar()
79 {
80 #if defined(__WXMSW__)
81 #if USE_GENERIC_TREECTRL
82 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
83 #endif
84 {
85 ::ShowScrollBar((HWND) GetHWND(), SB_VERT, FALSE);
86 }
87 #if USE_GENERIC_TREECTRL
88 else
89 {
90 // Implicit in overriding SetScrollbars
91 }
92 #endif
93 #endif
94 }
95
96 // Number of pixels per user unit (0 or -1 for no scrollbar)
97 // Length of virtual canvas in user units
98 // Length of page in user units
99 void wxRemotelyScrolledTreeCtrl::SetScrollbars(int pixelsPerUnitX, int pixelsPerUnitY,
100 int noUnitsX, int noUnitsY,
101 int xPos, int yPos,
102 bool noRefresh)
103 {
104 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
105 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
106 {
107 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
108 win->wxGenericTreeCtrl::SetScrollbars(pixelsPerUnitX, 0, noUnitsX, 0, xPos, 0, noRefresh);
109
110 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
111 if (scrolledWindow)
112 {
113 scrolledWindow->SetScrollbars(0, pixelsPerUnitY, 0, noUnitsY, 0, yPos, noRefresh);
114 }
115 }
116 #endif
117 }
118
119 // In case we're using the generic tree control.
120 int wxRemotelyScrolledTreeCtrl::GetScrollPos(int orient) const
121 {
122 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
123
124 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
125 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
126 {
127 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
128
129 if (orient == wxHORIZONTAL)
130 return win->wxGenericTreeCtrl::GetScrollPos(orient);
131 else
132 {
133 return scrolledWindow->GetScrollPos(orient);
134 }
135 }
136 #endif
137 return 0;
138 }
139
140
141 // In case we're using the generic tree control.
142 // Get the view start
143 void wxRemotelyScrolledTreeCtrl::GetViewStart(int *x, int *y) const
144 {
145 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
146
147 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
148 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
149 {
150
151 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
152 int x1, y1, x2, y2;
153 win->wxGenericTreeCtrl::GetViewStart(& x1, & y1);
154 * x = x1; * y = y1;
155 if (!scrolledWindow)
156 return;
157
158 scrolledWindow->GetViewStart(& x2, & y2);
159 * y = y2;
160 }
161 else
162 #endif
163 {
164 // x is wrong since the horizontal scrollbar is controlled by the
165 // tree control, but we probably don't need it.
166 scrolledWindow->GetViewStart(x, y);
167 }
168 }
169
170 // In case we're using the generic tree control.
171 void wxRemotelyScrolledTreeCtrl::PrepareDC(wxDC& dc)
172 {
173 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
174 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
175 {
176 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
177
178 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
179
180 int startX, startY;
181 GetViewStart(& startX, & startY);
182
183 int xppu1, yppu1, xppu2, yppu2;
184 win->wxGenericTreeCtrl::GetScrollPixelsPerUnit(& xppu1, & yppu1);
185 scrolledWindow->GetScrollPixelsPerUnit(& xppu2, & yppu2);
186
187 dc.SetDeviceOrigin( -startX * xppu1, -startY * yppu2 );
188 // dc.SetUserScale( win->GetScaleX(), win->GetScaleY() );
189 }
190 #endif
191 }
192
193 // Scroll to the given line (in scroll units where each unit is
194 // the height of an item)
195 void wxRemotelyScrolledTreeCtrl::ScrollToLine(int posHoriz, int posVert)
196 {
197 #ifdef __WXMSW__
198 #if USE_GENERIC_TREECTRL
199 if (!IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
200 #endif
201 {
202 UINT sbCode = SB_THUMBPOSITION;
203 HWND vertScrollBar = 0;
204 MSWDefWindowProc((WXUINT) WM_VSCROLL, MAKELONG(sbCode, posVert), (WXHWND) vertScrollBar);
205 }
206 #if USE_GENERIC_TREECTRL
207 else
208 #endif
209 #endif
210 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
211 {
212 wxGenericTreeCtrl* win = (wxGenericTreeCtrl*) this;
213 win->Refresh();
214 /* Doesn't work yet because scrolling is ignored by Scroll
215 int xppu, yppu;
216 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
217 if (scrolledWindow)
218 {
219 scrolledWindow->GetScrollPixelsPerUnit(& xppu, & yppu);
220 win->Scroll(-1, posVert*yppu);
221 }
222 */
223 }
224 #endif
225 }
226
227 void wxRemotelyScrolledTreeCtrl::OnSize(wxSizeEvent& event)
228 {
229 HideVScrollbar();
230 AdjustRemoteScrollbars();
231 event.Skip();
232 }
233
234 void wxRemotelyScrolledTreeCtrl::OnExpand(wxTreeEvent& event)
235 {
236 AdjustRemoteScrollbars();
237 event.Skip();
238
239 // If we don't have this, we get some bits of lines still remaining
240 if (event.GetEventType() == wxEVT_COMMAND_TREE_ITEM_COLLAPSED)
241 Refresh();
242
243 // Pass on the event
244 if (m_companionWindow)
245 m_companionWindow->GetEventHandler()->ProcessEvent(event);
246 }
247
248 // Adjust the containing wxScrolledWindow's scrollbars appropriately
249 void wxRemotelyScrolledTreeCtrl::AdjustRemoteScrollbars()
250 {
251 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
252 if (IsKindOf(CLASSINFO(wxGenericTreeCtrl)))
253 {
254 // This is for the generic tree control.
255 // It calls SetScrollbars which has been overridden
256 // to adjust the parent scrolled window vertical
257 // scrollbar.
258 ((wxGenericTreeCtrl*) this)->AdjustMyScrollbars();
259 return;
260 }
261 else
262 #endif
263 {
264 // This is for the wxMSW tree control
265 wxScrolledWindow* scrolledWindow = GetScrolledWindow();
266 if (scrolledWindow)
267 {
268 wxRect itemRect;
269 if (GetBoundingRect(GetRootItem(), itemRect))
270 {
271 // Actually, the real height seems to be 1 less than reported
272 // (e.g. 16 instead of 16)
273 int itemHeight = itemRect.GetHeight() - 1;
274
275 int w, h;
276 GetClientSize(&w, &h);
277
278 wxRect rect(0, 0, 0, 0);
279 CalcTreeSize(rect);
280
281 double f = ((double) (rect.GetHeight()) / (double) itemHeight) ;
282 int treeViewHeight = (int) ceil(f);
283
284 int scrollPixelsPerLine = itemHeight;
285 int scrollPos = - (itemRect.y / itemHeight);
286
287 scrolledWindow->SetScrollbars(0, scrollPixelsPerLine, 0, treeViewHeight, 0, scrollPos);
288
289 // Ensure that when a scrollbar becomes hidden or visible,
290 // the contained window sizes are right.
291 // Problem: this is called too early (?)
292 wxSizeEvent event(scrolledWindow->GetSize(), scrolledWindow->GetId());
293 scrolledWindow->GetEventHandler()->ProcessEvent(event);
294 }
295 }
296 }
297 }
298
299
300 // Calculate the area that contains both rectangles
301 static wxRect CombineRectangles(const wxRect& rect1, const wxRect& rect2)
302 {
303 wxRect rect;
304
305 int right1 = rect1.GetRight();
306 int bottom1 = rect1.GetBottom();
307 int right2 = rect2.GetRight();
308 int bottom2 = rect2.GetBottom();
309
310 wxPoint topLeft = wxPoint(wxMin(rect1.x, rect2.x), wxMin(rect1.y, rect2.y));
311 wxPoint bottomRight = wxPoint(wxMax(right1, right2), wxMax(bottom1, bottom2));
312
313 rect.x = topLeft.x; rect.y = topLeft.y;
314 rect.SetRight(bottomRight.x);
315 rect.SetBottom(bottomRight.y);
316
317 return rect;
318 }
319
320
321 // Calculate the tree overall size so we can set the scrollbar
322 // correctly
323 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(wxRect& rect)
324 {
325 CalcTreeSize(GetRootItem(), rect);
326 }
327
328 void wxRemotelyScrolledTreeCtrl::CalcTreeSize(const wxTreeItemId& id, wxRect& rect)
329 {
330 // More efficient implementation would be to find the last item (but how?)
331 // Q: is the bounding rect relative to the top of the virtual tree workspace
332 // or the top of the window? How would we convert?
333 wxRect itemSize;
334 if (GetBoundingRect(id, itemSize))
335 {
336 rect = CombineRectangles(rect, itemSize);
337 }
338
339 long cookie;
340 wxTreeItemId childId = GetFirstChild(id, cookie);
341 while (childId != 0)
342 {
343 CalcTreeSize(childId, rect);
344 childId = GetNextChild(childId, cookie);
345 }
346 }
347
348 // Find the scrolled window that contains this control
349 wxScrolledWindow* wxRemotelyScrolledTreeCtrl::GetScrolledWindow() const
350 {
351 wxWindow* parent = wxWindow::GetParent();
352 while (parent)
353 {
354 if (parent->IsKindOf(CLASSINFO(wxScrolledWindow)))
355 return (wxScrolledWindow*) parent;
356 parent = parent->GetParent();
357 }
358 return NULL;
359 }
360
361 void wxRemotelyScrolledTreeCtrl::OnScroll(wxScrollWinEvent& event)
362 {
363 int orient = event.GetOrientation();
364 if (orient == wxHORIZONTAL)
365 {
366 event.Skip();
367 return;
368 }
369 wxScrolledWindow* scrollWin = GetScrolledWindow();
370 if (!scrollWin)
371 return;
372
373 int x, y;
374 scrollWin->GetViewStart(& x, & y);
375
376 ScrollToLine(-1, y);
377 }
378
379 /*
380 * wxTreeCompanionWindow
381 *
382 * A window displaying values associated with tree control items.
383 */
384
385 IMPLEMENT_CLASS(wxTreeCompanionWindow, wxWindow)
386
387 BEGIN_EVENT_TABLE(wxTreeCompanionWindow, wxWindow)
388 EVT_PAINT(wxTreeCompanionWindow::OnPaint)
389 EVT_SCROLLWIN(wxTreeCompanionWindow::OnScroll)
390 EVT_TREE_ITEM_EXPANDED(-1, wxTreeCompanionWindow::OnExpand)
391 EVT_TREE_ITEM_COLLAPSED(-1, wxTreeCompanionWindow::OnExpand)
392 END_EVENT_TABLE()
393
394 wxTreeCompanionWindow::wxTreeCompanionWindow(wxWindow* parent, wxWindowID id,
395 const wxPoint& pos,
396 const wxSize& sz,
397 long style):
398 wxWindow(parent, id, pos, sz, style)
399 {
400 m_treeCtrl = NULL;
401 }
402
403 void wxTreeCompanionWindow::DrawItem(wxDC& dc, wxTreeItemId id, const wxRect& rect)
404 {
405 // TEST CODE
406 #if 1
407 if (m_treeCtrl)
408 {
409 wxString text = m_treeCtrl->GetItemText(id);
410 dc.SetTextForeground(* wxBLACK);
411 dc.SetBackgroundMode(wxTRANSPARENT);
412
413 int textW, textH;
414 dc.GetTextExtent(text, & textW, & textH);
415
416 int x = 5;
417 int y = rect.GetY() + wxMax(0, (rect.GetHeight() - textH) / 2);
418
419 dc.DrawText(text, x, y);
420 }
421 #endif
422 }
423
424 void wxTreeCompanionWindow::OnPaint(wxPaintEvent& event)
425 {
426 wxPaintDC dc(this);
427
428 if (!m_treeCtrl)
429 return;
430
431 wxPen pen(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_3DLIGHT), 1, wxSOLID);
432 dc.SetPen(pen);
433 dc.SetBrush(* wxTRANSPARENT_BRUSH);
434 wxFont font(wxSystemSettings::GetSystemFont(wxSYS_DEFAULT_GUI_FONT));
435 dc.SetFont(font);
436
437 wxSize clientSize = GetClientSize();
438 wxRect itemRect;
439 int cy=0;
440 wxTreeItemId h, lastH;
441 for(h=m_treeCtrl->GetFirstVisibleItem();h;h=m_treeCtrl->GetNextVisible(h))
442 {
443 if (m_treeCtrl->GetBoundingRect(h, itemRect))
444 {
445 cy = itemRect.GetTop();
446 wxRect drawItemRect(0, cy, clientSize.x, itemRect.GetHeight());
447
448 lastH = h;
449
450 // Draw the actual item
451 DrawItem(dc, h, drawItemRect);
452 dc.DrawLine(0, cy, clientSize.x, cy);
453 }
454 }
455 if (lastH.IsOk() && m_treeCtrl->GetBoundingRect(lastH, itemRect))
456 {
457 cy = itemRect.GetBottom();
458 dc.DrawLine(0, cy, clientSize.x, cy);
459 }
460 }
461
462 void wxTreeCompanionWindow::OnScroll(wxScrollWinEvent& event)
463 {
464 int orient = event.GetOrientation();
465 if (orient == wxHORIZONTAL)
466 {
467 event.Skip();
468 return;
469 }
470 if (!m_treeCtrl)
471 return;
472
473 // TODO: scroll the window physically instead of just refreshing.
474 Refresh(TRUE);
475 }
476
477 void wxTreeCompanionWindow::OnExpand(wxTreeEvent& event)
478 {
479 // TODO: something more optimized than simply refresh the whole
480 // window when the tree is expanded/collapsed. Tricky.
481 Refresh();
482 }
483
484 /*
485 * wxThinSplitterWindow
486 */
487
488 IMPLEMENT_CLASS(wxThinSplitterWindow, wxSplitterWindow)
489
490 BEGIN_EVENT_TABLE(wxThinSplitterWindow, wxSplitterWindow)
491 EVT_SIZE(wxThinSplitterWindow::OnSize)
492 END_EVENT_TABLE()
493
494 wxThinSplitterWindow::wxThinSplitterWindow(wxWindow* parent, wxWindowID id,
495 const wxPoint& pos,
496 const wxSize& sz,
497 long style):
498 wxSplitterWindow(parent, id, pos, sz, style)
499 {
500 }
501
502 void wxThinSplitterWindow::SizeWindows()
503 {
504 // The client size may have changed inbetween
505 // the sizing of the first window and the sizing of
506 // the second. So repeat SizeWindows.
507 wxSplitterWindow::SizeWindows();
508 wxSplitterWindow::SizeWindows();
509 }
510
511 // Tests for x, y over sash
512 bool wxThinSplitterWindow::SashHitTest(int x, int y, int tolerance)
513 {
514 return wxSplitterWindow::SashHitTest(x, y, 4);
515 }
516
517 void wxThinSplitterWindow::DrawSash(wxDC& dc)
518 {
519 if ( m_sashPosition == 0 || !m_windowTwo)
520 return;
521 if (GetWindowStyle() & wxSP_NOSASH)
522 return;
523
524 int w, h;
525 GetClientSize(&w, &h);
526
527 if ( m_splitMode == wxSPLIT_VERTICAL )
528 {
529 dc.SetPen(* m_facePen);
530 dc.SetBrush(* m_faceBrush);
531 int h1 = h-1;
532 int y1 = 0;
533 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
534 h1 += 1; // Not sure why this is necessary...
535 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
536 {
537 y1 = 2; h1 -= 3;
538 }
539 dc.DrawRectangle(m_sashPosition, y1, m_sashSize, h1);
540 }
541 else
542 {
543 dc.SetPen(* m_facePen);
544 dc.SetBrush(* m_faceBrush);
545 int w1 = w-1;
546 int x1 = 0;
547 if ( (GetWindowStyleFlag() & wxSP_BORDER) != wxSP_BORDER && (GetWindowStyleFlag() & wxSP_3DBORDER) != wxSP_3DBORDER )
548 w1 ++;
549 if ( (GetWindowStyleFlag() & wxSP_3DBORDER) == wxSP_3DBORDER)
550 {
551 x1 = 2; w1 -= 3;
552 }
553 dc.DrawRectangle(x1, m_sashPosition, w1, m_sashSize);
554 }
555
556 dc.SetPen(wxNullPen);
557 dc.SetBrush(wxNullBrush);
558 }
559
560 void wxThinSplitterWindow::OnSize(wxSizeEvent& event)
561 {
562 wxSplitterWindow::OnSize(event);
563 }
564
565 /*
566 * wxSplitterScrolledWindow
567 */
568
569 IMPLEMENT_CLASS(wxSplitterScrolledWindow, wxScrolledWindow)
570
571 BEGIN_EVENT_TABLE(wxSplitterScrolledWindow, wxScrolledWindow)
572 EVT_SCROLLWIN(wxSplitterScrolledWindow::OnScroll)
573 EVT_SIZE(wxSplitterScrolledWindow::OnSize)
574 END_EVENT_TABLE()
575
576 wxSplitterScrolledWindow::wxSplitterScrolledWindow(wxWindow* parent, wxWindowID id,
577 const wxPoint& pos,
578 const wxSize& sz,
579 long style):
580 wxScrolledWindow(parent, id, pos, sz, style)
581 {
582 }
583
584 void wxSplitterScrolledWindow::OnSize(wxSizeEvent& event)
585 {
586 wxSize sz = GetClientSize();
587 if (GetChildren().First())
588 {
589 ((wxWindow*) GetChildren().First()->Data())->SetSize(0, 0, sz.x, sz.y);
590 }
591 }
592
593 void wxSplitterScrolledWindow::OnScroll(wxScrollWinEvent& event)
594 {
595 // Ensure that events being propagated back up the window hierarchy
596 // don't cause an infinite loop
597 static bool inOnScroll = FALSE;
598 if (inOnScroll)
599 {
600 event.Skip();
601 return;
602 }
603 inOnScroll = TRUE;
604
605 int orient = event.GetOrientation();
606
607 int nScrollInc = CalcScrollInc(event);
608 if (nScrollInc == 0)
609 {
610 inOnScroll = FALSE;
611 return;
612 }
613
614 if (orient == wxHORIZONTAL)
615 {
616 inOnScroll = FALSE;
617 event.Skip();
618 return;
619 #if 0
620 int newPos = m_xScrollPosition + nScrollInc;
621 SetScrollPos(wxHORIZONTAL, newPos, TRUE );
622 #endif
623 }
624 else
625 {
626 int newPos = m_yScrollPosition + nScrollInc;
627 SetScrollPos(wxVERTICAL, newPos, TRUE );
628 }
629
630 if (orient == wxHORIZONTAL)
631 {
632 m_xScrollPosition += nScrollInc;
633 }
634 else
635 {
636 m_yScrollPosition += nScrollInc;
637 }
638
639 // Find targets in splitter window and send the event to them
640 wxNode* node = GetChildren().First();
641 while (node)
642 {
643 wxWindow* child = (wxWindow*) node->Data();
644 if (child->IsKindOf(CLASSINFO(wxSplitterWindow)))
645 {
646 wxSplitterWindow* splitter = (wxSplitterWindow*) child;
647 if (splitter->GetWindow1())
648 splitter->GetWindow1()->ProcessEvent(event);
649 if (splitter->GetWindow2())
650 splitter->GetWindow2()->ProcessEvent(event);
651 break;
652 }
653 node = node->Next();
654 }
655
656 #ifdef __WXMAC__
657 m_targetWindow->MacUpdateImmediately() ;
658 #endif
659
660 inOnScroll = FALSE;
661 }
662