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