]> git.saurik.com Git - wxWidgets.git/blob - src/generic/tabg.cpp
Fix horizontal mouse wheel scrolling in wxGTK.
[wxWidgets.git] / src / generic / tabg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/tabg.cpp
3 // Purpose: Generic tabbed dialogs; used by wxMotif's wxNotebook
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // Copyright: (c)
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // For compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #ifdef __BORLANDC__
15 #pragma hdrstop
16 #endif
17
18 #ifndef WX_PRECOMP
19 #include "wx/settings.h"
20 #include "wx/intl.h"
21 #include "wx/dcclient.h"
22 #include "wx/math.h"
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdarg.h>
28
29 #include "wx/generic/tabg.h"
30 #include "wx/listimpl.cpp"
31
32 WX_DEFINE_LIST(wxTabLayerList)
33
34 // not defined: use old, square tab implementation (fills in tabs)
35 // defined: use new, rounded tab implementation (doesn't colour in tabs)
36 // #define wxUSE_NEW_METHOD
37
38 IMPLEMENT_DYNAMIC_CLASS(wxTabControl, wxObject)
39
40 // IMPLEMENT_DYNAMIC_CLASS(wxTabLayer, wxList)
41
42 wxTabControl::wxTabControl(wxTabView *v)
43 {
44 m_view = v;
45 m_isSelected = false;
46 m_offsetX = 0;
47 m_offsetY = 0;
48 m_width = 0;
49 m_height = 0;
50 m_id = 0;
51 m_rowPosition = 0;
52 m_colPosition = 0;
53 }
54
55 wxTabControl::~wxTabControl(void)
56 {
57 }
58
59 void wxTabControl::OnDraw(wxDC& dc, bool lastInRow)
60 {
61 // Old, but in some ways better (drawing opaque tabs)
62 #ifndef wxUSE_NEW_METHOD
63 if (!m_view)
64 return;
65
66 // Top-left of tab view area
67 int viewX = m_view->GetViewRect().x;
68 int viewY = m_view->GetViewRect().y;
69
70 // Top-left of tab control
71 int tabX = GetX() + viewX;
72 int tabY = GetY() + viewY;
73 int tabHeightInc = 0;
74 if (m_isSelected)
75 {
76 tabHeightInc = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
77 tabY -= tabHeightInc;
78 }
79
80 dc.SetPen(*wxTRANSPARENT_PEN);
81
82 // Draw grey background
83 if (m_view->GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
84 {
85 if(m_view->GetBackgroundBrush())
86 dc.SetBrush(*m_view->GetBackgroundBrush());
87
88 // Add 1 because the pen is transparent. Under Motif, may be different.
89 #ifdef __WXMOTIF__
90 dc.DrawRectangle(tabX, tabY, (GetWidth()+1), (GetHeight() + tabHeightInc));
91 #else
92 dc.DrawRectangle(tabX, tabY, (GetWidth()+1), (GetHeight() + 1 + tabHeightInc));
93 #endif
94 }
95
96 // Draw highlight and shadow
97 dc.SetPen(*m_view->GetHighlightPen());
98
99 // Calculate the top of the tab beneath. It's the height of the tab, MINUS
100 // a bit if the tab below happens to be selected. Check.
101 wxTabControl *tabBeneath = NULL;
102 int subtractThis = 0;
103 if (GetColPosition() > 0)
104 tabBeneath = m_view->FindTabControlForPosition(GetColPosition() - 1, GetRowPosition());
105 if (tabBeneath && tabBeneath->IsSelected())
106 subtractThis = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
107
108 // Vertical highlight: if first tab, draw to bottom of view
109 if (tabX == m_view->GetViewRect().x && (m_view->GetTabStyle() & wxTAB_STYLE_DRAW_BOX))
110 dc.DrawLine(tabX, tabY, tabX, (m_view->GetViewRect().y + m_view->GetViewRect().height));
111 else if (tabX == m_view->GetViewRect().x)
112 // Not box drawing, just to top of view.
113 dc.DrawLine(tabX, tabY, tabX, (m_view->GetViewRect().y));
114 else
115 dc.DrawLine(tabX, tabY, tabX, (tabY + GetHeight() + tabHeightInc - subtractThis));
116
117 dc.DrawLine(tabX, tabY, (tabX + GetWidth()), tabY);
118 dc.SetPen(*m_view->GetShadowPen());
119
120 // Test if we're outside the right-hand edge of the view area
121 if (((tabX + GetWidth()) >= m_view->GetViewRect().x + m_view->GetViewRect().width) && (m_view->GetTabStyle() & wxTAB_STYLE_DRAW_BOX))
122 {
123 int bottomY = m_view->GetViewRect().y + m_view->GetViewRect().height + GetY() + m_view->GetTabHeight() + m_view->GetTopMargin();
124 // Add a tab height since we wish to draw to the bottom of the view.
125 dc.DrawLine((tabX + GetWidth()), tabY,
126 (tabX + GetWidth()), bottomY);
127
128 // Calculate the far-right of the view, since we don't wish to
129 // draw inside that
130 int rightOfView = m_view->GetViewRect().x + m_view->GetViewRect().width + 1;
131
132 // Draw the horizontal bit to connect to the view rectangle
133 dc.DrawLine((wxMax((tabX + GetWidth() - m_view->GetHorizontalTabOffset()), rightOfView)), (bottomY-1),
134 (tabX + GetWidth()), (bottomY-1));
135
136 // Draw black line to emphasize shadow
137 dc.SetPen(*wxBLACK_PEN);
138 dc.DrawLine((tabX + GetWidth() + 1), (tabY+1),
139 (tabX + GetWidth() + 1), bottomY);
140
141 // Draw the horizontal bit to connect to the view rectangle
142 dc.DrawLine((wxMax((tabX + GetWidth() - m_view->GetHorizontalTabOffset()), rightOfView)), (bottomY),
143 (tabX + GetWidth() + 1), (bottomY));
144 }
145 else
146 {
147 if (lastInRow)
148 {
149 // 25/5/97 UNLESS it's less than the max number of positions in this row
150
151 int topY = m_view->GetViewRect().y - m_view->GetTopMargin();
152
153 int maxPositions = ((wxTabLayer *)m_view->GetLayers().Item(0)->GetData())->GetCount();
154
155 // Only down to the bottom of the tab, not to the top of the view
156 if ( GetRowPosition() < (maxPositions - 1) )
157 topY = tabY + GetHeight() + tabHeightInc;
158
159 #ifdef __WXMOTIF__
160 topY -= 1;
161 #endif
162
163 // Shadow
164 dc.DrawLine((tabX + GetWidth()), tabY, (tabX + GetWidth()), topY);
165 // Draw black line to emphasize shadow
166 dc.SetPen(*wxBLACK_PEN);
167 dc.DrawLine((tabX + GetWidth() + 1), (tabY+1), (tabX + GetWidth() + 1),
168 topY);
169 }
170 else
171 {
172 // Calculate the top of the tab beneath. It's the height of the tab, MINUS
173 // a bit if the tab below (and next col along) happens to be selected. Check.
174 wxTabControl *tabBeneath = NULL;
175 int subtractThis = 0;
176 if (GetColPosition() > 0)
177 tabBeneath = m_view->FindTabControlForPosition(GetColPosition() - 1, GetRowPosition() + 1);
178 if (tabBeneath && tabBeneath->IsSelected())
179 subtractThis = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
180
181 #ifdef __WXMOTIF__
182 subtractThis += 1;
183 #endif
184
185 // Draw only to next tab down.
186 dc.DrawLine((tabX + GetWidth()), tabY,
187 (tabX + GetWidth()), (tabY + GetHeight() + tabHeightInc - subtractThis));
188
189 // Draw black line to emphasize shadow
190 dc.SetPen(*wxBLACK_PEN);
191 dc.DrawLine((tabX + GetWidth() + 1), (tabY+1), (tabX + GetWidth() + 1),
192 (tabY + GetHeight() + tabHeightInc - subtractThis));
193 }
194 }
195
196 // Draw centered text
197 int textY = tabY + m_view->GetVerticalTabTextSpacing() + tabHeightInc;
198
199 if (m_isSelected)
200 dc.SetFont(* m_view->GetSelectedTabFont());
201 else
202 dc.SetFont(* GetFont());
203
204 wxColour col(m_view->GetTextColour());
205 dc.SetTextForeground(col);
206 dc.SetBackgroundMode(wxTRANSPARENT);
207 wxCoord textWidth, textHeight;
208 dc.GetTextExtent(GetLabel(), &textWidth, &textHeight);
209
210 int textX = (int)(tabX + (GetWidth() - textWidth)/2.0);
211 if (textX < (tabX + 2))
212 textX = (tabX + 2);
213
214 dc.SetClippingRegion(tabX, tabY, GetWidth(), GetHeight());
215 dc.DrawText(GetLabel(), textX, textY);
216 dc.DestroyClippingRegion();
217
218 if (m_isSelected)
219 {
220 dc.SetPen(*m_view->GetHighlightPen());
221
222 // Draw white highlight from the tab's left side to the left hand edge of the view
223 dc.DrawLine(m_view->GetViewRect().x, (tabY + GetHeight() + tabHeightInc),
224 tabX, (tabY + GetHeight() + tabHeightInc));
225
226 // Draw white highlight from the tab's right side to the right hand edge of the view
227 dc.DrawLine((tabX + GetWidth()), (tabY + GetHeight() + tabHeightInc),
228 m_view->GetViewRect().x + m_view->GetViewRect().width, (tabY + GetHeight() + tabHeightInc));
229 }
230 #else
231 // New HEL version with rounder tabs
232
233 if (!m_view) return;
234
235 int tabInc = 0;
236 if (m_isSelected)
237 {
238 tabInc = m_view->GetTabSelectionHeight() - m_view->GetTabHeight();
239 }
240 int tabLeft = GetX() + m_view->GetViewRect().x;
241 int tabTop = GetY() + m_view->GetViewRect().y - tabInc;
242 int tabRight = tabLeft + m_view->GetTabWidth();
243 int left = m_view->GetViewRect().x;
244 int top = tabTop + m_view->GetTabHeight() + tabInc;
245 int right = left + m_view->GetViewRect().width;
246 int bottom = top + m_view->GetViewRect().height;
247
248 if (m_isSelected)
249 {
250 // TAB is selected - draw TAB and the View's full outline
251
252 dc.SetPen(*(m_view->GetHighlightPen()));
253 wxPoint pnts[10];
254 int n = 0;
255 pnts[n].x = left; pnts[n++].y = bottom;
256 pnts[n].x = left; pnts[n++].y = top;
257 pnts[n].x = tabLeft; pnts[n++].y = top;
258 pnts[n].x = tabLeft; pnts[n++].y = tabTop + 2;
259 pnts[n].x = tabLeft + 2; pnts[n++].y = tabTop;
260 pnts[n].x = tabRight - 1; pnts[n++].y = tabTop;
261 dc.DrawLines(n, pnts);
262 if (!lastInRow)
263 {
264 dc.DrawLine(
265 (tabRight + 2),
266 top,
267 right,
268 top
269 );
270 }
271
272 dc.SetPen(*(m_view->GetShadowPen()));
273 dc.DrawLine(
274 tabRight,
275 tabTop + 2,
276 tabRight,
277 top
278 );
279 dc.DrawLine(
280 right,
281 top,
282 right,
283 bottom
284 );
285 dc.DrawLine(
286 right,
287 bottom,
288 left,
289 bottom
290 );
291
292 dc.SetPen(*wxBLACK_PEN);
293 dc.DrawPoint(
294 tabRight,
295 tabTop + 1
296 );
297 dc.DrawPoint(
298 tabRight + 1,
299 tabTop + 2
300 );
301 if (lastInRow)
302 {
303 dc.DrawLine(
304 tabRight + 1,
305 bottom,
306 tabRight + 1,
307 tabTop + 1
308 );
309 }
310 else
311 {
312 dc.DrawLine(
313 tabRight + 1,
314 tabTop + 2,
315 tabRight + 1,
316 top
317 );
318 dc.DrawLine(
319 right + 1,
320 top,
321 right + 1,
322 bottom + 1
323 );
324 }
325 dc.DrawLine(
326 right + 1,
327 bottom + 1,
328 left + 1,
329 bottom + 1
330 );
331 }
332 else
333 {
334 // TAB is not selected - just draw TAB outline and RH edge
335 // if the TAB is the last in the row
336
337 int maxPositions = ((wxTabLayer*)m_view->GetLayers().Item(0)->GetData())->GetCount();
338 wxTabControl* tabBelow = 0;
339 wxTabControl* tabBelowRight = 0;
340 if (GetColPosition() > 0)
341 {
342 tabBelow = m_view->FindTabControlForPosition(
343 GetColPosition() - 1,
344 GetRowPosition()
345 );
346 }
347 if (!lastInRow && GetColPosition() > 0)
348 {
349 tabBelowRight = m_view->FindTabControlForPosition(
350 GetColPosition() - 1,
351 GetRowPosition() + 1
352 );
353 }
354
355 float raisedTop = top - m_view->GetTabSelectionHeight() +
356 m_view->GetTabHeight();
357
358 dc.SetPen(*(m_view->GetHighlightPen()));
359 wxPoint pnts[10];
360 int n = 0;
361
362 pnts[n].x = tabLeft;
363
364 if (tabBelow && tabBelow->IsSelected())
365 {
366 pnts[n++].y = (long)raisedTop;
367 }
368 else
369 {
370 pnts[n++].y = top;
371 }
372 pnts[n].x = tabLeft; pnts[n++].y = tabTop + 2;
373 pnts[n].x = tabLeft + 2; pnts[n++].y = tabTop;
374 pnts[n].x = tabRight - 1; pnts[n++].y = tabTop;
375 dc.DrawLines(n, pnts);
376
377 dc.SetPen(*(m_view->GetShadowPen()));
378 if (GetRowPosition() >= maxPositions - 1)
379 {
380 dc.DrawLine(
381 tabRight,
382 (tabTop + 2),
383 tabRight,
384 bottom
385 );
386 dc.DrawLine(
387 tabRight,
388 bottom,
389 (tabRight - m_view->GetHorizontalTabOffset()),
390 bottom
391 );
392 }
393 else
394 {
395 if (tabBelowRight && tabBelowRight->IsSelected())
396 {
397 dc.DrawLine(
398 tabRight,
399 (long)raisedTop,
400 tabRight,
401 tabTop + 1
402 );
403 }
404 else
405 {
406 dc.DrawLine(
407 tabRight,
408 top - 1,
409 tabRight,
410 tabTop + 1
411 );
412 }
413 }
414
415 dc.SetPen(*wxBLACK_PEN);
416 dc.DrawPoint(
417 tabRight,
418 tabTop + 1
419 );
420 dc.DrawPoint(
421 tabRight + 1,
422 tabTop + 2
423 );
424 if (GetRowPosition() >= maxPositions - 1)
425 {
426 // draw right hand edge to bottom of view
427 dc.DrawLine(
428 tabRight + 1,
429 bottom + 1,
430 tabRight + 1,
431 tabTop + 2
432 );
433 dc.DrawLine(
434 tabRight + 1,
435 bottom + 1,
436 (tabRight - m_view->GetHorizontalTabOffset()),
437 bottom + 1
438 );
439 }
440 else
441 {
442 // draw right hand edge of TAB
443 if (tabBelowRight && tabBelowRight->IsSelected())
444 {
445 dc.DrawLine(
446 tabRight + 1,
447 (long)(raisedTop - 1),
448 tabRight + 1,
449 tabTop + 2
450 );
451 }
452 else
453 {
454 dc.DrawLine(
455 tabRight + 1,
456 top - 1,
457 tabRight + 1,
458 tabTop + 2
459 );
460 }
461 }
462 }
463
464 // Draw centered text
465 dc.SetPen(*wxBLACK_PEN);
466 if (m_isSelected)
467 {
468 dc.SetFont(*(m_view->GetSelectedTabFont()));
469 }
470 else
471 {
472 dc.SetFont(*(GetFont()));
473 }
474
475 wxColour col(m_view->GetTextColour());
476 dc.SetTextForeground(col);
477 dc.SetBackgroundMode(wxTRANSPARENT);
478 long textWidth, textHeight;
479 dc.GetTextExtent(GetLabel(), &textWidth, &textHeight);
480
481 float textX = (tabLeft + tabRight - textWidth) / 2;
482 float textY = (tabInc + tabTop + m_view->GetVerticalTabTextSpacing());
483
484 dc.DrawText(GetLabel(), (long)textX, (long)textY);
485 #endif
486 }
487
488 bool wxTabControl::HitTest(int x, int y) const
489 {
490 // Top-left of tab control
491 int tabX1 = GetX() + m_view->GetViewRect().x;
492 int tabY1 = GetY() + m_view->GetViewRect().y;
493
494 // Bottom-right
495 int tabX2 = tabX1 + GetWidth();
496 int tabY2 = tabY1 + GetHeight();
497
498 if (x >= tabX1 && y >= tabY1 && x <= tabX2 && y <= tabY2)
499 return true;
500 else
501 return false;
502 }
503
504 IMPLEMENT_DYNAMIC_CLASS(wxTabView, wxObject)
505
506 wxTabView::wxTabView(long style)
507 {
508 m_noTabs = 0;
509 m_tabStyle = style;
510 m_tabSelection = -1;
511 m_tabHeight = 20;
512 m_tabSelectionHeight = m_tabHeight + 2;
513 m_tabWidth = 80;
514 m_tabHorizontalOffset = 10;
515 m_tabHorizontalSpacing = 2;
516 m_tabVerticalTextSpacing = 3;
517 m_topMargin = 5;
518 m_tabViewRect.x = 20;
519 m_tabViewRect.y = 20;
520 m_tabViewRect.width = 300;
521 m_tabViewRect.x = 300;
522 m_highlightColour = *wxWHITE;
523 m_shadowColour = wxColour(128, 128, 128);
524 // m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
525 m_textColour = *wxBLACK;
526 m_highlightPen = wxWHITE_PEN;
527 m_shadowPen = wxGREY_PEN;
528 // SetBackgroundColour(m_backgroundColour);
529 m_tabFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
530 m_tabSelectedFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
531 m_window = NULL;
532 }
533
534 wxTabView::~wxTabView()
535 {
536 ClearTabs(true);
537 }
538
539 // Automatically positions tabs
540 // TODO: this should just add the tab to a list, and then
541 // a layout function (e.g. Realize) should be called when all tabs have been added.
542 // The view rect could easily change as the view window is resized.
543 wxTabControl *wxTabView::AddTab(int id, const wxString& label, wxTabControl *existingTab)
544 {
545 // First, find which layer we should be adding to.
546 wxTabLayerList::compatibility_iterator node = m_layers.GetLast();
547 if (!node)
548 {
549 wxTabLayer *newLayer = new wxTabLayer;
550 node = m_layers.Append(newLayer);
551 }
552 // Check if adding another tab control would go off the
553 // right-hand edge of the layer.
554 wxTabLayer *tabLayer = (wxTabLayer *)node->GetData();
555 wxList::compatibility_iterator lastTabNode = tabLayer->GetLast();
556 if (lastTabNode)
557 {
558 wxTabControl *lastTab = (wxTabControl *)lastTabNode->GetData();
559 // Start another layer (row).
560 // Tricky choice: can't just check if will be overlapping the edge, because
561 // this happens anyway for 2nd and subsequent rows.
562 // Should check this for 1st row, and then subsequent rows should not exceed 1st
563 // in length.
564 if (((tabLayer == m_layers.GetFirst()->GetData()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
565 > GetViewRect().width)) ||
566 ((tabLayer != m_layers.GetFirst()->GetData()) && (tabLayer->GetCount() == ((wxTabLayer *)m_layers.GetFirst()->GetData())->GetCount())))
567 {
568 tabLayer = new wxTabLayer;
569 m_layers.Append(tabLayer);
570 lastTabNode = wxList::compatibility_iterator();
571 }
572 }
573 int layer = m_layers.GetCount() - 1;
574
575 wxTabControl *tabControl = existingTab;
576 if (!existingTab)
577 tabControl = OnCreateTabControl();
578 tabControl->SetRowPosition(tabLayer->GetCount());
579 tabControl->SetColPosition(layer);
580
581 wxTabControl *lastTab = NULL;
582 if (lastTabNode)
583 lastTab = (wxTabControl *)lastTabNode->GetData();
584
585 // Top of new tab
586 int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
587 // Offset from view top-left
588 int horizontalOffset = 0;
589 if (!lastTab)
590 horizontalOffset = layer*GetHorizontalTabOffset();
591 else
592 horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();
593
594 tabControl->SetPosition(horizontalOffset, verticalOffset);
595 tabControl->SetSize(GetTabWidth(), GetTabHeight());
596 tabControl->SetId(id);
597 tabControl->SetLabel(label);
598 tabControl->SetFont(* GetTabFont());
599
600 tabLayer->Append(tabControl);
601 m_noTabs ++;
602
603 return tabControl;
604 }
605
606 // Remove the tab without deleting the window
607 bool wxTabView::RemoveTab(int id)
608 {
609 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
610 while (layerNode)
611 {
612 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
613 wxList::compatibility_iterator tabNode = layer->GetFirst();
614 while (tabNode)
615 {
616 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
617 if (tab->GetId() == id)
618 {
619 if (id == m_tabSelection)
620 m_tabSelection = -1;
621 delete tab;
622 layer->Erase(tabNode);
623 m_noTabs --;
624
625 // The layout has changed
626 LayoutTabs();
627 return true;
628 }
629 tabNode = tabNode->GetNext();
630 }
631 layerNode = layerNode->GetNext();
632 }
633 return false;
634 }
635
636 bool wxTabView::SetTabText(int id, const wxString& label)
637 {
638 wxTabControl* control = FindTabControlForId(id);
639 if (!control)
640 return false;
641 control->SetLabel(label);
642 return true;
643 }
644
645 wxString wxTabView::GetTabText(int id) const
646 {
647 wxTabControl* control = FindTabControlForId(id);
648 if (!control)
649 return wxEmptyString;
650 else
651 return control->GetLabel();
652 }
653
654 // Returns the total height of the tabs component -- this may be several
655 // times the height of a tab, if there are several tab layers (rows).
656 int wxTabView::GetTotalTabHeight()
657 {
658 int minY = 0;
659
660 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
661 while (layerNode)
662 {
663 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
664 wxList::compatibility_iterator tabNode = layer->GetFirst();
665 while (tabNode)
666 {
667 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
668
669 if (tab->GetY() < minY)
670 minY = tab->GetY();
671
672 tabNode = tabNode->GetNext();
673 }
674 layerNode = layerNode->GetNext();
675 }
676
677 return - minY;
678 }
679
680 void wxTabView::ClearTabs(bool deleteTabs)
681 {
682 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
683 while (layerNode)
684 {
685 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
686 wxList::compatibility_iterator tabNode = layer->GetFirst();
687 while (tabNode)
688 {
689 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
690 if (deleteTabs)
691 delete tab;
692 wxList::compatibility_iterator next = tabNode->GetNext();
693 layer->Erase(tabNode);
694 tabNode = next;
695 }
696 wxTabLayerList::compatibility_iterator nextLayerNode = layerNode->GetNext();
697 delete layer;
698 m_layers.Erase(layerNode);
699 layerNode = nextLayerNode;
700 }
701 m_noTabs = 0;
702 m_tabSelection = -1;
703 }
704
705
706 // Layout tabs (optional, e.g. if resizing window)
707 void wxTabView::LayoutTabs(void)
708 {
709 // Make a list of the tab controls, deleting the wxTabLayers.
710 wxList controls;
711
712 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
713 while (layerNode)
714 {
715 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
716 wxList::compatibility_iterator tabNode = layer->GetFirst();
717 while (tabNode)
718 {
719 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
720 controls.Append(tab);
721 wxList::compatibility_iterator next = tabNode->GetNext();
722 layer->Erase(tabNode);
723 tabNode = next;
724 }
725 wxTabLayerList::compatibility_iterator nextLayerNode = layerNode->GetNext();
726 delete layer;
727 m_layers.Erase(layerNode);
728 layerNode = nextLayerNode;
729 }
730
731 wxTabControl *lastTab = NULL;
732
733 wxTabLayer *currentLayer = new wxTabLayer;
734 m_layers.Append(currentLayer);
735
736 wxList::compatibility_iterator node = controls.GetFirst();
737 while (node)
738 {
739 wxTabControl *tabControl = (wxTabControl *)node->GetData();
740 if (lastTab)
741 {
742 // Start another layer (row).
743 // Tricky choice: can't just check if will be overlapping the edge, because
744 // this happens anyway for 2nd and subsequent rows.
745 // Should check this for 1st row, and then subsequent rows should not exceed 1st
746 // in length.
747 if (((currentLayer == m_layers.GetFirst()->GetData()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
748 > GetViewRect().width)) ||
749 ((currentLayer != m_layers.GetFirst()->GetData()) && (currentLayer->GetCount() == ((wxTabLayer *)m_layers.GetFirst()->GetData())->GetCount())))
750 {
751 currentLayer = new wxTabLayer;
752 m_layers.Append(currentLayer);
753 lastTab = NULL;
754 }
755 }
756
757 int layer = m_layers.GetCount() - 1;
758
759 tabControl->SetRowPosition(currentLayer->GetCount());
760 tabControl->SetColPosition(layer);
761
762 // Top of new tab
763 int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
764 // Offset from view top-left
765 int horizontalOffset = 0;
766 if (!lastTab)
767 horizontalOffset = layer*GetHorizontalTabOffset();
768 else
769 horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();
770
771 tabControl->SetPosition(horizontalOffset, verticalOffset);
772 tabControl->SetSize(GetTabWidth(), GetTabHeight());
773
774 currentLayer->Append(tabControl);
775 lastTab = tabControl;
776
777 node = node->GetNext();
778 }
779
780 // Move the selected tab to the bottom
781 wxTabControl *control = FindTabControlForId(m_tabSelection);
782 if (control)
783 MoveSelectionTab(control);
784
785 }
786
787 // Draw all tabs
788 void wxTabView::Draw(wxDC& dc)
789 {
790 // Don't draw anything if there are no tabs.
791 if (GetNumberOfTabs() == 0)
792 return;
793
794 // Draw top margin area (beneath tabs and above view area)
795 if (GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
796 {
797 dc.SetPen(*wxTRANSPARENT_PEN);
798 if(GetBackgroundBrush())
799 dc.SetBrush(*GetBackgroundBrush());
800
801 // Add 1 because the pen is transparent. Under Motif, may be different.
802 dc.DrawRectangle(
803 m_tabViewRect.x,
804 (m_tabViewRect.y - m_topMargin),
805 (m_tabViewRect.width + 1),
806 (m_topMargin + 1)
807 );
808 }
809
810 // Draw layers in reverse order
811 wxTabLayerList::compatibility_iterator node = m_layers.GetLast();
812 while (node)
813 {
814 wxTabLayer *layer = (wxTabLayer *)node->GetData();
815 wxList::compatibility_iterator node2 = layer->GetFirst();
816 while (node2)
817 {
818 wxTabControl *control = (wxTabControl *)node2->GetData();
819 control->OnDraw(dc, (!node2->GetNext()));
820 node2 = node2->GetNext();
821 }
822
823 node = node->GetPrevious();
824 }
825
826
827 #ifndef wxUSE_NEW_METHOD
828 if (GetTabStyle() & wxTAB_STYLE_DRAW_BOX)
829 {
830 dc.SetPen(* GetShadowPen());
831
832 // Draw bottom line
833 dc.DrawLine(
834 (GetViewRect().x + 1),
835 (GetViewRect().y + GetViewRect().height),
836 (GetViewRect().x + GetViewRect().width + 1),
837 (GetViewRect().y + GetViewRect().height)
838 );
839
840 // Draw right line
841 dc.DrawLine(
842 (GetViewRect().x + GetViewRect().width),
843 (GetViewRect().y - GetTopMargin() + 1),
844 (GetViewRect().x + GetViewRect().width),
845 (GetViewRect().y + GetViewRect().height)
846 );
847
848 dc.SetPen(* wxBLACK_PEN);
849
850 // Draw bottom line
851 dc.DrawLine(
852 (GetViewRect().x),
853 (GetViewRect().y + GetViewRect().height + 1),
854 #if defined(__WXMOTIF__)
855 (GetViewRect().x + GetViewRect().width + 1),
856 #else
857 (GetViewRect().x + GetViewRect().width + 2),
858 #endif
859
860 (GetViewRect().y + GetViewRect().height + 1)
861 );
862
863 // Draw right line
864 dc.DrawLine(
865 (GetViewRect().x + GetViewRect().width + 1),
866 (GetViewRect().y - GetTopMargin()),
867 (GetViewRect().x + GetViewRect().width + 1),
868 (GetViewRect().y + GetViewRect().height + 1)
869 );
870 }
871 #endif
872 }
873
874 // Process mouse event, return false if we didn't process it
875 bool wxTabView::OnEvent(wxMouseEvent& event)
876 {
877 if (!event.LeftDown())
878 return false;
879
880 wxCoord x, y;
881 event.GetPosition(&x, &y);
882
883 wxTabControl *hitControl = NULL;
884
885 wxTabLayerList::compatibility_iterator node = m_layers.GetFirst();
886 while (node)
887 {
888 wxTabLayer *layer = (wxTabLayer *)node->GetData();
889 wxList::compatibility_iterator node2 = layer->GetFirst();
890 while (node2)
891 {
892 wxTabControl *control = (wxTabControl *)node2->GetData();
893 if (control->HitTest((int)x, (int)y))
894 {
895 hitControl = control;
896 node = wxTabLayerList::compatibility_iterator();
897 node2 = wxList::compatibility_iterator();
898 }
899 else
900 node2 = node2->GetNext();
901 }
902
903 if (node)
904 node = node->GetNext();
905 }
906
907 if (!hitControl)
908 return false;
909
910 wxTabControl *currentTab = FindTabControlForId(m_tabSelection);
911
912 if (hitControl == currentTab)
913 return false;
914
915 ChangeTab(hitControl);
916
917 return true;
918 }
919
920 bool wxTabView::ChangeTab(wxTabControl *control)
921 {
922 wxTabControl *currentTab = FindTabControlForId(m_tabSelection);
923 int oldTab = -1;
924 if (currentTab)
925 oldTab = currentTab->GetId();
926
927 if (control == currentTab)
928 return true;
929
930 if (m_layers.GetCount() == 0)
931 return false;
932
933 if (!OnTabPreActivate(control->GetId(), oldTab))
934 return false;
935
936 // Move the tab to the bottom
937 MoveSelectionTab(control);
938
939 if (currentTab)
940 currentTab->SetSelected(false);
941
942 control->SetSelected(true);
943 m_tabSelection = control->GetId();
944
945 OnTabActivate(control->GetId(), oldTab);
946
947 // Leave window refresh for the implementing window
948
949 return true;
950 }
951
952 // Move the selected tab to the bottom layer, if necessary,
953 // without calling app activation code
954 bool wxTabView::MoveSelectionTab(wxTabControl *control)
955 {
956 if (m_layers.GetCount() == 0)
957 return false;
958
959 wxTabLayer *firstLayer = (wxTabLayer *)m_layers.GetFirst()->GetData();
960
961 // Find what column this tab is at, so we can swap with the one at the bottom.
962 // If we're on the bottom layer, then no need to swap.
963 if (!firstLayer->Member(control))
964 {
965 // Do a swap
966 int col = 0;
967 wxList::compatibility_iterator thisNode = FindTabNodeAndColumn(control, &col);
968 if (!thisNode)
969 return false;
970 wxList::compatibility_iterator otherNode = firstLayer->Item(col);
971 if (!otherNode)
972 return false;
973
974 // If this is already in the bottom layer, return now
975 if (otherNode == thisNode)
976 return true;
977
978 wxTabControl *otherTab = (wxTabControl *)otherNode->GetData();
979
980 // We now have pointers to the tab to be changed to,
981 // and the tab on the first layer. Swap tab structures and
982 // position details.
983
984 int thisX = control->GetX();
985 int thisY = control->GetY();
986 int thisColPos = control->GetColPosition();
987 int otherX = otherTab->GetX();
988 int otherY = otherTab->GetY();
989 int otherColPos = otherTab->GetColPosition();
990
991 control->SetPosition(otherX, otherY);
992 control->SetColPosition(otherColPos);
993 otherTab->SetPosition(thisX, thisY);
994 otherTab->SetColPosition(thisColPos);
995
996 // Swap the data for the nodes
997 thisNode->SetData(otherTab);
998 otherNode->SetData(control);
999 }
1000 return true;
1001 }
1002
1003 // Called when a tab is activated
1004 void wxTabView::OnTabActivate(int /*activateId*/, int /*deactivateId*/)
1005 {
1006 }
1007
1008 void wxTabView::SetHighlightColour(const wxColour& col)
1009 {
1010 m_highlightColour = col;
1011 m_highlightPen = wxThePenList->FindOrCreatePen(col);
1012 }
1013
1014 void wxTabView::SetShadowColour(const wxColour& col)
1015 {
1016 m_shadowColour = col;
1017 m_shadowPen = wxThePenList->FindOrCreatePen(col);
1018 }
1019
1020 void wxTabView::SetBackgroundColour(const wxColour& col)
1021 {
1022 m_backgroundColour = col;
1023 m_backgroundPen = wxThePenList->FindOrCreatePen(col);
1024 m_backgroundBrush = wxTheBrushList->FindOrCreateBrush(col);
1025 }
1026
1027 // this may be called with sel = zero (which doesn't match any page)
1028 // when wxMotif deletes a page
1029 // so return the first tab...
1030
1031 void wxTabView::SetTabSelection(int sel, bool activateTool)
1032 {
1033 if ( sel==m_tabSelection )
1034 return;
1035
1036 int oldSel = m_tabSelection;
1037 wxTabControl *control = FindTabControlForId(sel);
1038 if (sel == 0) sel=control->GetId();
1039 wxTabControl *oldControl = FindTabControlForId(m_tabSelection);
1040
1041 if (!OnTabPreActivate(sel, oldSel))
1042 return;
1043
1044 if (control)
1045 control->SetSelected((sel != -1)); // TODO ??
1046 else if (sel != -1)
1047 {
1048 wxFAIL_MSG(_("Could not find tab for id"));
1049 return;
1050 }
1051
1052 if (oldControl)
1053 oldControl->SetSelected(false);
1054
1055 m_tabSelection = sel;
1056
1057 if (control)
1058 MoveSelectionTab(control);
1059
1060 if (activateTool)
1061 OnTabActivate(sel, oldSel);
1062 }
1063
1064 // Find tab control for id
1065 // this may be called with zero (which doesn't match any page)
1066 // so return the first control...
1067 wxTabControl *wxTabView::FindTabControlForId(int id) const
1068 {
1069 wxTabLayerList::compatibility_iterator node1 = m_layers.GetFirst();
1070 while (node1)
1071 {
1072 wxTabLayer *layer = (wxTabLayer *)node1->GetData();
1073 wxList::compatibility_iterator node2 = layer->GetFirst();
1074 while (node2)
1075 {
1076 wxTabControl *control = (wxTabControl *)node2->GetData();
1077 if (control->GetId() == id || id == 0)
1078 return control;
1079 node2 = node2->GetNext();
1080 }
1081 node1 = node1->GetNext();
1082 }
1083 return NULL;
1084 }
1085
1086 // Find tab control for layer, position (starting from zero)
1087 wxTabControl *wxTabView::FindTabControlForPosition(int layer, int position) const
1088 {
1089 wxTabLayerList::compatibility_iterator node1 = m_layers.Item(layer);
1090 if (!node1)
1091 return NULL;
1092 wxTabLayer *tabLayer = (wxTabLayer *)node1->GetData();
1093 wxList::compatibility_iterator node2 = tabLayer->Item(position);
1094 if (!node2)
1095 return NULL;
1096 return (wxTabControl *)node2->GetData();
1097 }
1098
1099 // Find the node and the column at which this control is positioned.
1100 wxList::compatibility_iterator wxTabView::FindTabNodeAndColumn(wxTabControl *control, int *col) const
1101 {
1102 wxTabLayerList::compatibility_iterator node1 = m_layers.GetFirst();
1103 while (node1)
1104 {
1105 wxTabLayer *layer = (wxTabLayer *)node1->GetData();
1106 int c = 0;
1107 wxList::compatibility_iterator node2 = layer->GetFirst();
1108 while (node2)
1109 {
1110 wxTabControl *cnt = (wxTabControl *)node2->GetData();
1111 if (cnt == control)
1112 {
1113 *col = c;
1114 return node2;
1115 }
1116 node2 = node2->GetNext();
1117 c ++;
1118 }
1119 node1 = node1->GetNext();
1120 }
1121 return wxList::compatibility_iterator();
1122 }
1123
1124 int wxTabView::CalculateTabWidth(int noTabs, bool adjustView)
1125 {
1126 m_tabWidth = (int)((m_tabViewRect.width - ((noTabs - 1)*GetHorizontalTabSpacing()))/noTabs);
1127 if (adjustView)
1128 {
1129 m_tabViewRect.width = noTabs*m_tabWidth + ((noTabs-1)*GetHorizontalTabSpacing());
1130 }
1131 return m_tabWidth;
1132 }
1133
1134 /*
1135 * wxTabbedDialog
1136 */
1137
1138 IMPLEMENT_CLASS(wxTabbedDialog, wxDialog)
1139
1140 BEGIN_EVENT_TABLE(wxTabbedDialog, wxDialog)
1141 EVT_CLOSE(wxTabbedDialog::OnCloseWindow)
1142 EVT_MOUSE_EVENTS(wxTabbedDialog::OnMouseEvent)
1143 EVT_PAINT(wxTabbedDialog::OnPaint)
1144 END_EVENT_TABLE()
1145
1146 wxTabbedDialog::wxTabbedDialog(wxWindow *parent, wxWindowID id,
1147 const wxString& title,
1148 const wxPoint& pos, const wxSize& size,
1149 long windowStyle, const wxString& name):
1150 wxDialog(parent, id, title, pos, size, windowStyle, name)
1151 {
1152 m_tabView = NULL;
1153 }
1154
1155 wxTabbedDialog::~wxTabbedDialog(void)
1156 {
1157 if (m_tabView)
1158 delete m_tabView;
1159 }
1160
1161 void wxTabbedDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event) )
1162 {
1163 Destroy();
1164 }
1165
1166 void wxTabbedDialog::OnMouseEvent(wxMouseEvent& event )
1167 {
1168 if (m_tabView)
1169 m_tabView->OnEvent(event);
1170 }
1171
1172 void wxTabbedDialog::OnPaint(wxPaintEvent& WXUNUSED(event) )
1173 {
1174 wxPaintDC dc(this);
1175 if (m_tabView)
1176 m_tabView->Draw(dc);
1177 }
1178
1179 /*
1180 * wxTabbedPanel
1181 */
1182
1183 IMPLEMENT_CLASS(wxTabbedPanel, wxPanel)
1184
1185 BEGIN_EVENT_TABLE(wxTabbedPanel, wxPanel)
1186 EVT_MOUSE_EVENTS(wxTabbedPanel::OnMouseEvent)
1187 EVT_PAINT(wxTabbedPanel::OnPaint)
1188 END_EVENT_TABLE()
1189
1190 wxTabbedPanel::wxTabbedPanel(wxWindow *parent, wxWindowID id, const wxPoint& pos,
1191 const wxSize& size, long windowStyle, const wxString& name):
1192 wxPanel(parent, id, pos, size, windowStyle, name)
1193 {
1194 m_tabView = NULL;
1195 }
1196
1197 wxTabbedPanel::~wxTabbedPanel(void)
1198 {
1199 delete m_tabView;
1200 }
1201
1202 void wxTabbedPanel::OnMouseEvent(wxMouseEvent& event)
1203 {
1204 if (m_tabView)
1205 m_tabView->OnEvent(event);
1206 }
1207
1208 void wxTabbedPanel::OnPaint(wxPaintEvent& WXUNUSED(event) )
1209 {
1210 wxPaintDC dc(this);
1211 if (m_tabView)
1212 m_tabView->Draw(dc);
1213 }
1214
1215 /*
1216 * wxPanelTabView
1217 */
1218
1219 IMPLEMENT_CLASS(wxPanelTabView, wxTabView)
1220
1221 wxPanelTabView::wxPanelTabView(wxPanel *pan, long style)
1222 : wxTabView(style)
1223 {
1224 m_panel = pan;
1225 m_currentWindow = NULL;
1226
1227 if (m_panel->IsKindOf(wxCLASSINFO(wxTabbedDialog)))
1228 ((wxTabbedDialog *)m_panel)->SetTabView(this);
1229 else if (m_panel->IsKindOf(wxCLASSINFO(wxTabbedPanel)))
1230 ((wxTabbedPanel *)m_panel)->SetTabView(this);
1231
1232 SetWindow(m_panel);
1233 }
1234
1235 wxPanelTabView::~wxPanelTabView(void)
1236 {
1237 ClearWindows(true);
1238 }
1239
1240 // Called when a tab is activated
1241 void wxPanelTabView::OnTabActivate(int activateId, int deactivateId)
1242 {
1243 if (!m_panel)
1244 return;
1245
1246 wxWindow *oldWindow = ((deactivateId == -1) ? 0 : GetTabWindow(deactivateId));
1247 wxWindow *newWindow = GetTabWindow(activateId);
1248
1249 if (oldWindow)
1250 oldWindow->Show(false);
1251 if (newWindow)
1252 newWindow->Show(true);
1253
1254 m_panel->Refresh();
1255 }
1256
1257
1258 void wxPanelTabView::AddTabWindow(int id, wxWindow *window)
1259 {
1260 wxASSERT(m_tabWindows.find(id) == m_tabWindows.end());
1261 m_tabWindows[id] = window;
1262 window->Show(false);
1263 }
1264
1265 wxWindow *wxPanelTabView::GetTabWindow(int id) const
1266 {
1267 wxIntToWindowHashMap::const_iterator it = m_tabWindows.find(id);
1268 return it == m_tabWindows.end() ? NULL : it->second;
1269 }
1270
1271 void wxPanelTabView::ClearWindows(bool deleteWindows)
1272 {
1273 if (deleteWindows)
1274 WX_CLEAR_HASH_MAP(wxIntToWindowHashMap, m_tabWindows);
1275 m_tabWindows.clear();
1276 }
1277
1278 void wxPanelTabView::ShowWindowForTab(int id)
1279 {
1280 wxWindow *newWindow = GetTabWindow(id);
1281 if (newWindow == m_currentWindow)
1282 return;
1283 if (m_currentWindow)
1284 m_currentWindow->Show(false);
1285 newWindow->Show(true);
1286 newWindow->Refresh();
1287 }
1288