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