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