wxWindow split into wxWindowBase and wxWindow (wxGTK part)
[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 }
703
704
705 // Layout tabs (optional, e.g. if resizing window)
706 void wxTabView::LayoutTabs(void)
707 {
708 // Make a list of the tab controls, deleting the wxTabLayers.
709 wxList controls;
710
711 wxNode *layerNode = m_layers.First();
712 while (layerNode)
713 {
714 wxTabLayer *layer = (wxTabLayer *)layerNode->Data();
715 wxNode *tabNode = layer->First();
716 while (tabNode)
717 {
718 wxTabControl *tab = (wxTabControl *)tabNode->Data();
719 controls.Append(tab);
720 wxNode *next = tabNode->Next();
721 delete tabNode;
722 tabNode = next;
723 }
724 wxNode *nextLayerNode = layerNode->Next();
725 delete layer;
726 delete layerNode;
727 layerNode = nextLayerNode;
728 }
729
730 wxTabControl *lastTab = (wxTabControl *) NULL;
731
732 wxTabLayer *currentLayer = new wxTabLayer;
733 m_layers.Append(currentLayer);
734
735 wxNode *node = controls.First();
736 while (node)
737 {
738 wxTabControl *tabControl = (wxTabControl *)node->Data();
739 if (lastTab)
740 {
741 // Start another layer (row).
742 // Tricky choice: can't just check if will be overlapping the edge, because
743 // this happens anyway for 2nd and subsequent rows.
744 // Should check this for 1st row, and then subsequent rows should not exceed 1st
745 // in length.
746 if (((currentLayer == m_layers.First()->Data()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
747 > GetViewRect().width)) ||
748 ((currentLayer != m_layers.First()->Data()) && (currentLayer->Number() == ((wxTabLayer *)m_layers.First()->Data())->Number())))
749 {
750 currentLayer = new wxTabLayer;
751 m_layers.Append(currentLayer);
752 lastTab = (wxTabControl *) NULL;
753 }
754 }
755
756 int layer = m_layers.Number() - 1;
757
758 tabControl->SetRowPosition(currentLayer->Number());
759 tabControl->SetColPosition(layer);
760
761 // Top of new tab
762 int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
763 // Offset from view top-left
764 int horizontalOffset = 0;
765 if (!lastTab)
766 horizontalOffset = layer*GetHorizontalTabOffset();
767 else
768 horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();
769
770 tabControl->SetPosition(horizontalOffset, verticalOffset);
771 tabControl->SetSize(GetTabWidth(), GetTabHeight());
772
773 currentLayer->Append(tabControl);
774 lastTab = tabControl;
775
776 node = node->Next();
777 }
778
779 // Move the selected tab to the bottom
780 wxTabControl *control = FindTabControlForId(m_tabSelection);
781 if (control)
782 MoveSelectionTab(control);
783
784 }
785
786 // Draw all tabs
787 void wxTabView::Draw(wxDC& dc)
788 {
789 // Don't draw anything if there are no tabs.
790 if (GetNumberOfTabs() == 0)
791 return;
792
793 // Draw top margin area (beneath tabs and above view area)
794 if (GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
795 {
796 dc.SetPen(*wxTRANSPARENT_PEN);
797 dc.SetBrush(*GetBackgroundBrush());
798
799 // Add 1 because the pen is transparent. Under Motif, may be different.
800 dc.DrawRectangle(
801 m_tabViewRect.x,
802 (m_tabViewRect.y - m_topMargin),
803 (m_tabViewRect.width + 1),
804 (m_topMargin + 1)
805 );
806 }
807
808 // Draw layers in reverse order
809 wxNode *node = m_layers.Last();
810 while (node)
811 {
812 wxTabLayer *layer = (wxTabLayer *)node->Data();
813 wxNode *node2 = layer->First();
814 while (node2)
815 {
816 wxTabControl *control = (wxTabControl *)node2->Data();
817 control->OnDraw(dc, (node2->Next() == NULL));
818 node2 = node2->Next();
819 }
820
821 node = node->Previous();
822 }
823
824
825 #ifndef wxUSE_NEW_METHOD
826 if (GetTabStyle() & wxTAB_STYLE_DRAW_BOX)
827 {
828 dc.SetPen(* GetShadowPen());
829
830 // Draw bottom line
831 dc.DrawLine(
832 (GetViewRect().x + 1),
833 (GetViewRect().y + GetViewRect().height),
834 (GetViewRect().x + GetViewRect().width + 1),
835 (GetViewRect().y + GetViewRect().height)
836 );
837
838 // Draw right line
839 dc.DrawLine(
840 (GetViewRect().x + GetViewRect().width),
841 (GetViewRect().y - GetTopMargin() + 1),
842 (GetViewRect().x + GetViewRect().width),
843 (GetViewRect().y + GetViewRect().height)
844 );
845
846 dc.SetPen(* wxBLACK_PEN);
847
848 // Draw bottom line
849 dc.DrawLine(
850 (GetViewRect().x),
851 (GetViewRect().y + GetViewRect().height + 1),
852 #if defined(__WXMOTIF__)
853 (GetViewRect().x + GetViewRect().width + 1),
854 #else
855 (GetViewRect().x + GetViewRect().width + 2),
856 #endif
857
858 (GetViewRect().y + GetViewRect().height + 1)
859 );
860
861 // Draw right line
862 dc.DrawLine(
863 (GetViewRect().x + GetViewRect().width + 1),
864 (GetViewRect().y - GetTopMargin()),
865 (GetViewRect().x + GetViewRect().width + 1),
866 (GetViewRect().y + GetViewRect().height + 1)
867 );
868 }
869 #endif
870 }
871
872 // Process mouse event, return FALSE if we didn't process it
873 bool wxTabView::OnEvent(wxMouseEvent& event)
874 {
875 if (!event.LeftDown())
876 return FALSE;
877
878 long x, y;
879 event.Position(&x, &y);
880
881 wxTabControl *hitControl = (wxTabControl *) NULL;
882
883 wxNode *node = m_layers.First();
884 while (node)
885 {
886 wxTabLayer *layer = (wxTabLayer *)node->Data();
887 wxNode *node2 = layer->First();
888 while (node2)
889 {
890 wxTabControl *control = (wxTabControl *)node2->Data();
891 if (control->HitTest((int)x, (int)y))
892 {
893 hitControl = control;
894 node = (wxNode *) NULL;
895 node2 = (wxNode *) NULL;
896 }
897 else
898 node2 = node2->Next();
899 }
900
901 if (node)
902 node = node->Next();
903 }
904
905 if (!hitControl)
906 return FALSE;
907
908 wxTabControl *currentTab = FindTabControlForId(m_tabSelection);
909
910 if (hitControl == currentTab)
911 return FALSE;
912
913 ChangeTab(hitControl);
914
915 return TRUE;
916 }
917
918 bool wxTabView::ChangeTab(wxTabControl *control)
919 {
920 wxTabControl *currentTab = FindTabControlForId(m_tabSelection);
921 int oldTab = -1;
922 if (currentTab)
923 oldTab = currentTab->GetId();
924
925 if (control == currentTab)
926 return TRUE;
927
928 if (m_layers.Number() == 0)
929 return FALSE;
930
931 if (!OnTabPreActivate(control->GetId(), oldTab))
932 return FALSE;
933
934 // Move the tab to the bottom
935 MoveSelectionTab(control);
936
937 if (currentTab)
938 currentTab->SetSelected(FALSE);
939
940 control->SetSelected(TRUE);
941 m_tabSelection = control->GetId();
942
943 OnTabActivate(control->GetId(), oldTab);
944
945 // Leave window refresh for the implementing window
946
947 return TRUE;
948 }
949
950 // Move the selected tab to the bottom layer, if necessary,
951 // without calling app activation code
952 bool wxTabView::MoveSelectionTab(wxTabControl *control)
953 {
954 if (m_layers.Number() == 0)
955 return FALSE;
956
957 wxTabLayer *firstLayer = (wxTabLayer *)m_layers.First()->Data();
958
959 // Find what column this tab is at, so we can swap with the one at the bottom.
960 // If we're on the bottom layer, then no need to swap.
961 if (!firstLayer->Member(control))
962 {
963 // Do a swap
964 int col = 0;
965 wxNode *thisNode = FindTabNodeAndColumn(control, &col);
966 if (!thisNode)
967 return FALSE;
968 wxNode *otherNode = firstLayer->Nth(col);
969 if (!otherNode)
970 return FALSE;
971
972 // If this is already in the bottom layer, return now
973 if (otherNode == thisNode)
974 return TRUE;
975
976 wxTabControl *otherTab = (wxTabControl *)otherNode->Data();
977
978 // We now have pointers to the tab to be changed to,
979 // and the tab on the first layer. Swap tab structures and
980 // position details.
981
982 int thisX = control->GetX();
983 int thisY = control->GetY();
984 int thisColPos = control->GetColPosition();
985 int otherX = otherTab->GetX();
986 int otherY = otherTab->GetY();
987 int otherColPos = otherTab->GetColPosition();
988
989 control->SetPosition(otherX, otherY);
990 control->SetColPosition(otherColPos);
991 otherTab->SetPosition(thisX, thisY);
992 otherTab->SetColPosition(thisColPos);
993
994 // Swap the data for the nodes
995 thisNode->SetData(otherTab);
996 otherNode->SetData(control);
997 }
998 return TRUE;
999 }
1000
1001 // Called when a tab is activated
1002 void wxTabView::OnTabActivate(int /*activateId*/, int /*deactivateId*/)
1003 {
1004 }
1005
1006 void wxTabView::SetHighlightColour(const wxColour& col)
1007 {
1008 m_highlightColour = col;
1009 m_highlightPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1010 }
1011
1012 void wxTabView::SetShadowColour(const wxColour& col)
1013 {
1014 m_shadowColour = col;
1015 m_shadowPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1016 }
1017
1018 void wxTabView::SetBackgroundColour(const wxColour& col)
1019 {
1020 m_backgroundColour = col;
1021 m_backgroundPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1022 m_backgroundBrush = wxTheBrushList->FindOrCreateBrush(col, wxSOLID);
1023 }
1024
1025 void wxTabView::SetTabSelection(int sel, bool activateTool)
1026 {
1027 int oldSel = m_tabSelection;
1028 wxTabControl *control = FindTabControlForId(sel);
1029 wxTabControl *oldControl = FindTabControlForId(m_tabSelection);
1030
1031 if (!OnTabPreActivate(sel, oldSel))
1032 return;
1033
1034 if (control)
1035 control->SetSelected((sel != -1)); // TODO ??
1036 else if (sel != -1)
1037 {
1038 wxFAIL_MSG(_("Could not find tab for id"));
1039 return;
1040 }
1041
1042 if (oldControl)
1043 oldControl->SetSelected(FALSE);
1044
1045 m_tabSelection = sel;
1046
1047 if (control)
1048 MoveSelectionTab(control);
1049
1050 if (activateTool)
1051 OnTabActivate(sel, oldSel);
1052 }
1053
1054 // Find tab control for id
1055 wxTabControl *wxTabView::FindTabControlForId(int id) const
1056 {
1057 wxNode *node1 = m_layers.First();
1058 while (node1)
1059 {
1060 wxTabLayer *layer = (wxTabLayer *)node1->Data();
1061 wxNode *node2 = layer->First();
1062 while (node2)
1063 {
1064 wxTabControl *control = (wxTabControl *)node2->Data();
1065 if (control->GetId() == id)
1066 return control;
1067 node2 = node2->Next();
1068 }
1069 node1 = node1->Next();
1070 }
1071 return (wxTabControl *) NULL;
1072 }
1073
1074 // Find tab control for layer, position (starting from zero)
1075 wxTabControl *wxTabView::FindTabControlForPosition(int layer, int position) const
1076 {
1077 wxNode *node1 = m_layers.Nth(layer);
1078 if (!node1)
1079 return (wxTabControl *) NULL;
1080 wxTabLayer *tabLayer = (wxTabLayer *)node1->Data();
1081 wxNode *node2 = tabLayer->Nth(position);
1082 if (!node2)
1083 return (wxTabControl *) NULL;
1084 return (wxTabControl *)node2->Data();
1085 }
1086
1087 // Find the node and the column at which this control is positioned.
1088 wxNode *wxTabView::FindTabNodeAndColumn(wxTabControl *control, int *col) const
1089 {
1090 wxNode *node1 = m_layers.First();
1091 while (node1)
1092 {
1093 wxTabLayer *layer = (wxTabLayer *)node1->Data();
1094 int c = 0;
1095 wxNode *node2 = layer->First();
1096 while (node2)
1097 {
1098 wxTabControl *cnt = (wxTabControl *)node2->Data();
1099 if (cnt == control)
1100 {
1101 *col = c;
1102 return node2;
1103 }
1104 node2 = node2->Next();
1105 c ++;
1106 }
1107 node1 = node1->Next();
1108 }
1109 return (wxNode *) NULL;
1110 }
1111
1112 int wxTabView::CalculateTabWidth(int noTabs, bool adjustView)
1113 {
1114 m_tabWidth = (int)((m_tabViewRect.width - ((noTabs - 1)*GetHorizontalTabSpacing()))/noTabs);
1115 if (adjustView)
1116 {
1117 m_tabViewRect.width = noTabs*m_tabWidth + ((noTabs-1)*GetHorizontalTabSpacing());
1118 }
1119 return m_tabWidth;
1120 }
1121
1122 /*
1123 * wxTabbedDialog
1124 */
1125
1126 IMPLEMENT_CLASS(wxTabbedDialog, wxDialog)
1127
1128 BEGIN_EVENT_TABLE(wxTabbedDialog, wxDialog)
1129 EVT_CLOSE(wxTabbedDialog::OnCloseWindow)
1130 EVT_MOUSE_EVENTS(wxTabbedDialog::OnMouseEvent)
1131 EVT_PAINT(wxTabbedDialog::OnPaint)
1132 END_EVENT_TABLE()
1133
1134 wxTabbedDialog::wxTabbedDialog(wxWindow *parent, wxWindowID id,
1135 const wxString& title,
1136 const wxPoint& pos, const wxSize& size,
1137 long windowStyle, const wxString& name):
1138 wxDialog(parent, id, title, pos, size, windowStyle, name)
1139 {
1140 m_tabView = (wxTabView *) NULL;
1141 }
1142
1143 wxTabbedDialog::~wxTabbedDialog(void)
1144 {
1145 if (m_tabView)
1146 delete m_tabView;
1147 }
1148
1149 void wxTabbedDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event) )
1150 {
1151 Destroy();
1152 }
1153
1154 void wxTabbedDialog::OnMouseEvent(wxMouseEvent& event )
1155 {
1156 if (m_tabView)
1157 m_tabView->OnEvent(event);
1158 }
1159
1160 void wxTabbedDialog::OnPaint(wxPaintEvent& WXUNUSED(event) )
1161 {
1162 wxPaintDC dc(this);
1163 if (m_tabView)
1164 m_tabView->Draw(dc);
1165 }
1166
1167 /*
1168 * wxTabbedPanel
1169 */
1170
1171 IMPLEMENT_CLASS(wxTabbedPanel, wxPanel)
1172
1173 BEGIN_EVENT_TABLE(wxTabbedPanel, wxPanel)
1174 EVT_MOUSE_EVENTS(wxTabbedPanel::OnMouseEvent)
1175 EVT_PAINT(wxTabbedPanel::OnPaint)
1176 END_EVENT_TABLE()
1177
1178 wxTabbedPanel::wxTabbedPanel(wxWindow *parent, wxWindowID id, const wxPoint& pos,
1179 const wxSize& size, long windowStyle, const wxString& name):
1180 wxPanel(parent, id, pos, size, windowStyle, name)
1181 {
1182 m_tabView = (wxTabView *) NULL;
1183 }
1184
1185 wxTabbedPanel::~wxTabbedPanel(void)
1186 {
1187 delete m_tabView;
1188 }
1189
1190 void wxTabbedPanel::OnMouseEvent(wxMouseEvent& event)
1191 {
1192 if (m_tabView)
1193 m_tabView->OnEvent(event);
1194 }
1195
1196 void wxTabbedPanel::OnPaint(wxPaintEvent& WXUNUSED(event) )
1197 {
1198 wxPaintDC dc(this);
1199 if (m_tabView)
1200 m_tabView->Draw(dc);
1201 }
1202
1203 /*
1204 * wxPanelTabView
1205 */
1206
1207 IMPLEMENT_CLASS(wxPanelTabView, wxTabView)
1208
1209 wxPanelTabView::wxPanelTabView(wxPanel *pan, long style): wxTabView(style), m_tabWindows(wxKEY_INTEGER)
1210 {
1211 m_panel = pan;
1212 m_currentWindow = (wxWindow *) NULL;
1213
1214 if (m_panel->IsKindOf(CLASSINFO(wxTabbedDialog)))
1215 ((wxTabbedDialog *)m_panel)->SetTabView(this);
1216 else if (m_panel->IsKindOf(CLASSINFO(wxTabbedPanel)))
1217 ((wxTabbedPanel *)m_panel)->SetTabView(this);
1218
1219 SetWindow(m_panel);
1220 }
1221
1222 wxPanelTabView::~wxPanelTabView(void)
1223 {
1224 ClearWindows(TRUE);
1225 }
1226
1227 // Called when a tab is activated
1228 void wxPanelTabView::OnTabActivate(int activateId, int deactivateId)
1229 {
1230 if (!m_panel)
1231 return;
1232
1233 wxWindow *oldWindow = ((deactivateId == -1) ? 0 : GetTabWindow(deactivateId));
1234 wxWindow *newWindow = GetTabWindow(activateId);
1235
1236 if (oldWindow)
1237 oldWindow->Show(FALSE);
1238 if (newWindow)
1239 newWindow->Show(TRUE);
1240
1241 m_panel->Refresh();
1242 }
1243
1244
1245 void wxPanelTabView::AddTabWindow(int id, wxWindow *window)
1246 {
1247 m_tabWindows.Append((long)id, window);
1248 window->Show(FALSE);
1249 }
1250
1251 wxWindow *wxPanelTabView::GetTabWindow(int id) const
1252 {
1253 wxNode *node = m_tabWindows.Find((long)id);
1254 if (!node)
1255 return (wxWindow *) NULL;
1256 return (wxWindow *)node->Data();
1257 }
1258
1259 void wxPanelTabView::ClearWindows(bool deleteWindows)
1260 {
1261 if (deleteWindows)
1262 m_tabWindows.DeleteContents(TRUE);
1263 m_tabWindows.Clear();
1264 m_tabWindows.DeleteContents(FALSE);
1265 }
1266
1267 void wxPanelTabView::ShowWindowForTab(int id)
1268 {
1269 wxWindow *newWindow = GetTabWindow(id);
1270 if (newWindow == m_currentWindow)
1271 return;
1272 if (m_currentWindow)
1273 m_currentWindow->Show(FALSE);
1274 newWindow->Show(TRUE);
1275 newWindow->Refresh();
1276 }
1277