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