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