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