1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/generic/tabg.cpp
3 // Purpose:     Generic tabbed dialogs
4 // Author:      Julian Smart
5 // Modified by:
6 // Created:     01/02/97
7 // RCS-ID:      $Id: tabg.cpp 39745 2006-06-15 17:58:49Z ABX $
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     #include "wx/math.h"
26 #endif
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 
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 
IMPLEMENT_DYNAMIC_CLASS(wxTabControl,wxObject)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 
~wxTabControl(void)58 wxTabControl::~wxTabControl(void)
59 {
60 }
61 
OnDraw(wxDC & dc,bool lastInRow)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 
HitTest(int x,int y) const490 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 
IMPLEMENT_DYNAMIC_CLASS(wxTabView,wxObject)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 
~wxTabView()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.
AddTab(int id,const wxString & label,wxTabControl * existingTab)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
RemoveTab(int id)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 
SetTabText(int id,const wxString & label)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 
GetTabText(int id) const647 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).
GetTotalTabHeight()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 
ClearTabs(bool deleteTabs)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)
LayoutTabs(void)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
Draw(wxDC & dc)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
OnEvent(wxMouseEvent & event)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 
ChangeTab(wxTabControl * control)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
MoveSelectionTab(wxTabControl * control)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
OnTabActivate(int,int)1005 void wxTabView::OnTabActivate(int /*activateId*/, int /*deactivateId*/)
1006 {
1007 }
1008 
SetHighlightColour(const wxColour & col)1009 void wxTabView::SetHighlightColour(const wxColour& col)
1010 {
1011   m_highlightColour = col;
1012   m_highlightPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1013 }
1014 
SetShadowColour(const wxColour & col)1015 void wxTabView::SetShadowColour(const wxColour& col)
1016 {
1017   m_shadowColour = col;
1018   m_shadowPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1019 }
1020 
SetBackgroundColour(const wxColour & col)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 
SetTabSelection(int sel,bool activateTool)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...
FindTabControlForId(int id) const1068 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)
FindTabControlForPosition(int layer,int position) const1088 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.
FindTabNodeAndColumn(wxTabControl * control,int * col) const1101 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 
CalculateTabWidth(int noTabs,bool adjustView)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 
IMPLEMENT_CLASS(wxTabbedDialog,wxDialog)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 
~wxTabbedDialog(void)1156 wxTabbedDialog::~wxTabbedDialog(void)
1157 {
1158   if (m_tabView)
1159     delete m_tabView;
1160 }
1161 
OnCloseWindow(wxCloseEvent & WXUNUSED (event))1162 void wxTabbedDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event) )
1163 {
1164   Destroy();
1165 }
1166 
OnMouseEvent(wxMouseEvent & event)1167 void wxTabbedDialog::OnMouseEvent(wxMouseEvent& event )
1168 {
1169   if (m_tabView)
1170     m_tabView->OnEvent(event);
1171 }
1172 
OnPaint(wxPaintEvent & WXUNUSED (event))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 
IMPLEMENT_CLASS(wxTabbedPanel,wxPanel)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 
~wxTabbedPanel(void)1198 wxTabbedPanel::~wxTabbedPanel(void)
1199 {
1200   delete m_tabView;
1201 }
1202 
OnMouseEvent(wxMouseEvent & event)1203 void wxTabbedPanel::OnMouseEvent(wxMouseEvent& event)
1204 {
1205   if (m_tabView)
1206     m_tabView->OnEvent(event);
1207 }
1208 
OnPaint(wxPaintEvent & WXUNUSED (event))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 
IMPLEMENT_CLASS(wxPanelTabView,wxTabView)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 
~wxPanelTabView(void)1236 wxPanelTabView::~wxPanelTabView(void)
1237 {
1238   ClearWindows(true);
1239 }
1240 
1241 // Called when a tab is activated
OnTabActivate(int activateId,int deactivateId)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 
AddTabWindow(int id,wxWindow * window)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 
GetTabWindow(int id) const1266 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 
ClearWindows(bool deleteWindows)1272 void wxPanelTabView::ClearWindows(bool deleteWindows)
1273 {
1274   if (deleteWindows)
1275     WX_CLEAR_HASH_MAP(wxIntToWindowHashMap, m_tabWindows);
1276   m_tabWindows.clear();
1277 }
1278 
ShowWindowForTab(int id)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
1291