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