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