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