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