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