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