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