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
35WX_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
41IMPLEMENT_DYNAMIC_CLASS(wxTabControl, wxObject)
42
43// IMPLEMENT_DYNAMIC_CLASS(wxTabLayer, wxList)
44
45wxTabControl::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
58wxTabControl::~wxTabControl(void)
59{
60}
61
62void 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
490bool 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
506IMPLEMENT_DYNAMIC_CLASS(wxTabView, wxObject)
507
508wxTabView::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
536wxTabView::~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.
545wxTabControl *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
609bool 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
638bool 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
647wxString 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).
658int 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
682void 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)
709void 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
790void 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
876bool 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
921bool 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
955bool 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
1005void wxTabView::OnTabActivate(int /*activateId*/, int /*deactivateId*/)
1006{
1007}
1008
1009void wxTabView::SetHighlightColour(const wxColour& col)
1010{
1011  m_highlightColour = col;
1012  m_highlightPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1013}
1014
1015void wxTabView::SetShadowColour(const wxColour& col)
1016{
1017  m_shadowColour = col;
1018  m_shadowPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1019}
1020
1021void 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
1032void 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...
1068wxTabControl *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)
1088wxTabControl *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.
1101wxList::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
1125int 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
1139IMPLEMENT_CLASS(wxTabbedDialog, wxDialog)
1140
1141BEGIN_EVENT_TABLE(wxTabbedDialog, wxDialog)
1142    EVT_CLOSE(wxTabbedDialog::OnCloseWindow)
1143    EVT_MOUSE_EVENTS(wxTabbedDialog::OnMouseEvent)
1144    EVT_PAINT(wxTabbedDialog::OnPaint)
1145END_EVENT_TABLE()
1146
1147wxTabbedDialog::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
1156wxTabbedDialog::~wxTabbedDialog(void)
1157{
1158  if (m_tabView)
1159    delete m_tabView;
1160}
1161
1162void wxTabbedDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event) )
1163{
1164  Destroy();
1165}
1166
1167void wxTabbedDialog::OnMouseEvent(wxMouseEvent& event )
1168{
1169  if (m_tabView)
1170    m_tabView->OnEvent(event);
1171}
1172
1173void 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
1184IMPLEMENT_CLASS(wxTabbedPanel, wxPanel)
1185
1186BEGIN_EVENT_TABLE(wxTabbedPanel, wxPanel)
1187    EVT_MOUSE_EVENTS(wxTabbedPanel::OnMouseEvent)
1188    EVT_PAINT(wxTabbedPanel::OnPaint)
1189END_EVENT_TABLE()
1190
1191wxTabbedPanel::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
1198wxTabbedPanel::~wxTabbedPanel(void)
1199{
1200  delete m_tabView;
1201}
1202
1203void wxTabbedPanel::OnMouseEvent(wxMouseEvent& event)
1204{
1205  if (m_tabView)
1206    m_tabView->OnEvent(event);
1207}
1208
1209void 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
1220IMPLEMENT_CLASS(wxPanelTabView, wxTabView)
1221
1222wxPanelTabView::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
1236wxPanelTabView::~wxPanelTabView(void)
1237{
1238  ClearWindows(true);
1239}
1240
1241// Called when a tab is activated
1242void 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
1259void 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
1266wxWindow *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
1272void wxPanelTabView::ClearWindows(bool deleteWindows)
1273{
1274  if (deleteWindows)
1275    WX_CLEAR_HASH_MAP(wxIntToWindowHashMap, m_tabWindows);
1276  m_tabWindows.clear();
1277}
1278
1279void 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