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