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