restore tabg.h and tabg.cpp for wxMotif
[wxWidgets.git] / src / generic / tabg.cpp
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 // RCS-ID: $Id$
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 #ifndef WX_PRECOMP
20 #include "wx/settings.h"
21 #include "wx/intl.h"
22 #include "wx/dcclient.h"
23 #include "wx/math.h"
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdarg.h>
29
30 #include "wx/generic/tabg.h"
31 #include "wx/listimpl.cpp"
32
33 WX_DEFINE_LIST(wxTabLayerList)
34
35 // not defined: use old, square tab implementation (fills in tabs)
36 // defined: use new, rounded tab implementation (doesn't colour in tabs)
37 // #define wxUSE_NEW_METHOD
38
39 IMPLEMENT_DYNAMIC_CLASS(wxTabControl, wxObject)
40
41 // IMPLEMENT_DYNAMIC_CLASS(wxTabLayer, wxList)
42
43 wxTabControl::wxTabControl(wxTabView *v)
44 {
45 m_view = v;
46 m_isSelected = false;
47 m_offsetX = 0;
48 m_offsetY = 0;
49 m_width = 0;
50 m_height = 0;
51 m_id = 0;
52 m_rowPosition = 0;
53 m_colPosition = 0;
54 }
55
56 wxTabControl::~wxTabControl(void)
57 {
58 }
59
60 void wxTabControl::OnDraw(wxDC& dc, bool lastInRow)
61 {
62 // Old, but in some ways better (drawing opaque tabs)
63 #ifndef wxUSE_NEW_METHOD
64 if (!m_view)
65 return;
66
67 // Top-left of tab view area
68 int viewX = m_view->GetViewRect().x;
69 int viewY = m_view->GetViewRect().y;
70
71 // Top-left of tab control
72 int tabX = GetX() + viewX;
73 int tabY = GetY() + viewY;
74 int tabHeightInc = 0;
75 if (m_isSelected)
76 {
77 tabHeightInc = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
78 tabY -= tabHeightInc;
79 }
80
81 dc.SetPen(*wxTRANSPARENT_PEN);
82
83 // Draw grey background
84 if (m_view->GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
85 {
86 if(m_view->GetBackgroundBrush())
87 dc.SetBrush(*m_view->GetBackgroundBrush());
88
89 // Add 1 because the pen is transparent. Under Motif, may be different.
90 #ifdef __WXMOTIF__
91 dc.DrawRectangle(tabX, tabY, (GetWidth()+1), (GetHeight() + tabHeightInc));
92 #else
93 dc.DrawRectangle(tabX, tabY, (GetWidth()+1), (GetHeight() + 1 + tabHeightInc));
94 #endif
95 }
96
97 // Draw highlight and shadow
98 dc.SetPen(*m_view->GetHighlightPen());
99
100 // Calculate the top of the tab beneath. It's the height of the tab, MINUS
101 // a bit if the tab below happens to be selected. Check.
102 wxTabControl *tabBeneath = NULL;
103 int subtractThis = 0;
104 if (GetColPosition() > 0)
105 tabBeneath = m_view->FindTabControlForPosition(GetColPosition() - 1, GetRowPosition());
106 if (tabBeneath && tabBeneath->IsSelected())
107 subtractThis = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
108
109 // Vertical highlight: if first tab, draw to bottom of view
110 if (tabX == m_view->GetViewRect().x && (m_view->GetTabStyle() & wxTAB_STYLE_DRAW_BOX))
111 dc.DrawLine(tabX, tabY, tabX, (m_view->GetViewRect().y + m_view->GetViewRect().height));
112 else if (tabX == m_view->GetViewRect().x)
113 // Not box drawing, just to top of view.
114 dc.DrawLine(tabX, tabY, tabX, (m_view->GetViewRect().y));
115 else
116 dc.DrawLine(tabX, tabY, tabX, (tabY + GetHeight() + tabHeightInc - subtractThis));
117
118 dc.DrawLine(tabX, tabY, (tabX + GetWidth()), tabY);
119 dc.SetPen(*m_view->GetShadowPen());
120
121 // Test if we're outside the right-hand edge of the view area
122 if (((tabX + GetWidth()) >= m_view->GetViewRect().x + m_view->GetViewRect().width) && (m_view->GetTabStyle() & wxTAB_STYLE_DRAW_BOX))
123 {
124 int bottomY = m_view->GetViewRect().y + m_view->GetViewRect().height + GetY() + m_view->GetTabHeight() + m_view->GetTopMargin();
125 // Add a tab height since we wish to draw to the bottom of the view.
126 dc.DrawLine((tabX + GetWidth()), tabY,
127 (tabX + GetWidth()), bottomY);
128
129 // Calculate the far-right of the view, since we don't wish to
130 // draw inside that
131 int rightOfView = m_view->GetViewRect().x + m_view->GetViewRect().width + 1;
132
133 // Draw the horizontal bit to connect to the view rectangle
134 dc.DrawLine((wxMax((tabX + GetWidth() - m_view->GetHorizontalTabOffset()), rightOfView)), (bottomY-1),
135 (tabX + GetWidth()), (bottomY-1));
136
137 // Draw black line to emphasize shadow
138 dc.SetPen(*wxBLACK_PEN);
139 dc.DrawLine((tabX + GetWidth() + 1), (tabY+1),
140 (tabX + GetWidth() + 1), bottomY);
141
142 // Draw the horizontal bit to connect to the view rectangle
143 dc.DrawLine((wxMax((tabX + GetWidth() - m_view->GetHorizontalTabOffset()), rightOfView)), (bottomY),
144 (tabX + GetWidth() + 1), (bottomY));
145 }
146 else
147 {
148 if (lastInRow)
149 {
150 // 25/5/97 UNLESS it's less than the max number of positions in this row
151
152 int topY = m_view->GetViewRect().y - m_view->GetTopMargin();
153
154 int maxPositions = ((wxTabLayer *)m_view->GetLayers().Item(0)->GetData())->GetCount();
155
156 // Only down to the bottom of the tab, not to the top of the view
157 if ( GetRowPosition() < (maxPositions - 1) )
158 topY = tabY + GetHeight() + tabHeightInc;
159
160 #ifdef __WXMOTIF__
161 topY -= 1;
162 #endif
163
164 // Shadow
165 dc.DrawLine((tabX + GetWidth()), tabY, (tabX + GetWidth()), topY);
166 // Draw black line to emphasize shadow
167 dc.SetPen(*wxBLACK_PEN);
168 dc.DrawLine((tabX + GetWidth() + 1), (tabY+1), (tabX + GetWidth() + 1),
169 topY);
170 }
171 else
172 {
173 // Calculate the top of the tab beneath. It's the height of the tab, MINUS
174 // a bit if the tab below (and next col along) happens to be selected. Check.
175 wxTabControl *tabBeneath = NULL;
176 int subtractThis = 0;
177 if (GetColPosition() > 0)
178 tabBeneath = m_view->FindTabControlForPosition(GetColPosition() - 1, GetRowPosition() + 1);
179 if (tabBeneath && tabBeneath->IsSelected())
180 subtractThis = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
181
182 #ifdef __WXMOTIF__
183 subtractThis += 1;
184 #endif
185
186 // Draw only to next tab down.
187 dc.DrawLine((tabX + GetWidth()), tabY,
188 (tabX + GetWidth()), (tabY + GetHeight() + tabHeightInc - subtractThis));
189
190 // Draw black line to emphasize shadow
191 dc.SetPen(*wxBLACK_PEN);
192 dc.DrawLine((tabX + GetWidth() + 1), (tabY+1), (tabX + GetWidth() + 1),
193 (tabY + GetHeight() + tabHeightInc - subtractThis));
194 }
195 }
196
197 // Draw centered text
198 int textY = tabY + m_view->GetVerticalTabTextSpacing() + tabHeightInc;
199
200 if (m_isSelected)
201 dc.SetFont(* m_view->GetSelectedTabFont());
202 else
203 dc.SetFont(* GetFont());
204
205 wxColour col(m_view->GetTextColour());
206 dc.SetTextForeground(col);
207 dc.SetBackgroundMode(wxTRANSPARENT);
208 wxCoord textWidth, textHeight;
209 dc.GetTextExtent(GetLabel(), &textWidth, &textHeight);
210
211 int textX = (int)(tabX + (GetWidth() - textWidth)/2.0);
212 if (textX < (tabX + 2))
213 textX = (tabX + 2);
214
215 dc.SetClippingRegion(tabX, tabY, GetWidth(), GetHeight());
216 dc.DrawText(GetLabel(), textX, textY);
217 dc.DestroyClippingRegion();
218
219 if (m_isSelected)
220 {
221 dc.SetPen(*m_view->GetHighlightPen());
222
223 // Draw white highlight from the tab's left side to the left hand edge of the view
224 dc.DrawLine(m_view->GetViewRect().x, (tabY + GetHeight() + tabHeightInc),
225 tabX, (tabY + GetHeight() + tabHeightInc));
226
227 // Draw white highlight from the tab's right side to the right hand edge of the view
228 dc.DrawLine((tabX + GetWidth()), (tabY + GetHeight() + tabHeightInc),
229 m_view->GetViewRect().x + m_view->GetViewRect().width, (tabY + GetHeight() + tabHeightInc));
230 }
231 #else
232 // New HEL version with rounder tabs
233
234 if (!m_view) return;
235
236 int tabInc = 0;
237 if (m_isSelected)
238 {
239 tabInc = m_view->GetTabSelectionHeight() - m_view->GetTabHeight();
240 }
241 int tabLeft = GetX() + m_view->GetViewRect().x;
242 int tabTop = GetY() + m_view->GetViewRect().y - tabInc;
243 int tabRight = tabLeft + m_view->GetTabWidth();
244 int left = m_view->GetViewRect().x;
245 int top = tabTop + m_view->GetTabHeight() + tabInc;
246 int right = left + m_view->GetViewRect().width;
247 int bottom = top + m_view->GetViewRect().height;
248
249 if (m_isSelected)
250 {
251 // TAB is selected - draw TAB and the View's full outline
252
253 dc.SetPen(*(m_view->GetHighlightPen()));
254 wxPoint pnts[10];
255 int n = 0;
256 pnts[n].x = left; pnts[n++].y = bottom;
257 pnts[n].x = left; pnts[n++].y = top;
258 pnts[n].x = tabLeft; pnts[n++].y = top;
259 pnts[n].x = tabLeft; pnts[n++].y = tabTop + 2;
260 pnts[n].x = tabLeft + 2; pnts[n++].y = tabTop;
261 pnts[n].x = tabRight - 1; pnts[n++].y = tabTop;
262 dc.DrawLines(n, pnts);
263 if (!lastInRow)
264 {
265 dc.DrawLine(
266 (tabRight + 2),
267 top,
268 right,
269 top
270 );
271 }
272
273 dc.SetPen(*(m_view->GetShadowPen()));
274 dc.DrawLine(
275 tabRight,
276 tabTop + 2,
277 tabRight,
278 top
279 );
280 dc.DrawLine(
281 right,
282 top,
283 right,
284 bottom
285 );
286 dc.DrawLine(
287 right,
288 bottom,
289 left,
290 bottom
291 );
292
293 dc.SetPen(*wxBLACK_PEN);
294 dc.DrawPoint(
295 tabRight,
296 tabTop + 1
297 );
298 dc.DrawPoint(
299 tabRight + 1,
300 tabTop + 2
301 );
302 if (lastInRow)
303 {
304 dc.DrawLine(
305 tabRight + 1,
306 bottom,
307 tabRight + 1,
308 tabTop + 1
309 );
310 }
311 else
312 {
313 dc.DrawLine(
314 tabRight + 1,
315 tabTop + 2,
316 tabRight + 1,
317 top
318 );
319 dc.DrawLine(
320 right + 1,
321 top,
322 right + 1,
323 bottom + 1
324 );
325 }
326 dc.DrawLine(
327 right + 1,
328 bottom + 1,
329 left + 1,
330 bottom + 1
331 );
332 }
333 else
334 {
335 // TAB is not selected - just draw TAB outline and RH edge
336 // if the TAB is the last in the row
337
338 int maxPositions = ((wxTabLayer*)m_view->GetLayers().Item(0)->GetData())->GetCount();
339 wxTabControl* tabBelow = 0;
340 wxTabControl* tabBelowRight = 0;
341 if (GetColPosition() > 0)
342 {
343 tabBelow = m_view->FindTabControlForPosition(
344 GetColPosition() - 1,
345 GetRowPosition()
346 );
347 }
348 if (!lastInRow && GetColPosition() > 0)
349 {
350 tabBelowRight = m_view->FindTabControlForPosition(
351 GetColPosition() - 1,
352 GetRowPosition() + 1
353 );
354 }
355
356 float raisedTop = top - m_view->GetTabSelectionHeight() +
357 m_view->GetTabHeight();
358
359 dc.SetPen(*(m_view->GetHighlightPen()));
360 wxPoint pnts[10];
361 int n = 0;
362
363 pnts[n].x = tabLeft;
364
365 if (tabBelow && tabBelow->IsSelected())
366 {
367 pnts[n++].y = (long)raisedTop;
368 }
369 else
370 {
371 pnts[n++].y = top;
372 }
373 pnts[n].x = tabLeft; pnts[n++].y = tabTop + 2;
374 pnts[n].x = tabLeft + 2; pnts[n++].y = tabTop;
375 pnts[n].x = tabRight - 1; pnts[n++].y = tabTop;
376 dc.DrawLines(n, pnts);
377
378 dc.SetPen(*(m_view->GetShadowPen()));
379 if (GetRowPosition() >= maxPositions - 1)
380 {
381 dc.DrawLine(
382 tabRight,
383 (tabTop + 2),
384 tabRight,
385 bottom
386 );
387 dc.DrawLine(
388 tabRight,
389 bottom,
390 (tabRight - m_view->GetHorizontalTabOffset()),
391 bottom
392 );
393 }
394 else
395 {
396 if (tabBelowRight && tabBelowRight->IsSelected())
397 {
398 dc.DrawLine(
399 tabRight,
400 (long)raisedTop,
401 tabRight,
402 tabTop + 1
403 );
404 }
405 else
406 {
407 dc.DrawLine(
408 tabRight,
409 top - 1,
410 tabRight,
411 tabTop + 1
412 );
413 }
414 }
415
416 dc.SetPen(*wxBLACK_PEN);
417 dc.DrawPoint(
418 tabRight,
419 tabTop + 1
420 );
421 dc.DrawPoint(
422 tabRight + 1,
423 tabTop + 2
424 );
425 if (GetRowPosition() >= maxPositions - 1)
426 {
427 // draw right hand edge to bottom of view
428 dc.DrawLine(
429 tabRight + 1,
430 bottom + 1,
431 tabRight + 1,
432 tabTop + 2
433 );
434 dc.DrawLine(
435 tabRight + 1,
436 bottom + 1,
437 (tabRight - m_view->GetHorizontalTabOffset()),
438 bottom + 1
439 );
440 }
441 else
442 {
443 // draw right hand edge of TAB
444 if (tabBelowRight && tabBelowRight->IsSelected())
445 {
446 dc.DrawLine(
447 tabRight + 1,
448 (long)(raisedTop - 1),
449 tabRight + 1,
450 tabTop + 2
451 );
452 }
453 else
454 {
455 dc.DrawLine(
456 tabRight + 1,
457 top - 1,
458 tabRight + 1,
459 tabTop + 2
460 );
461 }
462 }
463 }
464
465 // Draw centered text
466 dc.SetPen(*wxBLACK_PEN);
467 if (m_isSelected)
468 {
469 dc.SetFont(*(m_view->GetSelectedTabFont()));
470 }
471 else
472 {
473 dc.SetFont(*(GetFont()));
474 }
475
476 wxColour col(m_view->GetTextColour());
477 dc.SetTextForeground(col);
478 dc.SetBackgroundMode(wxTRANSPARENT);
479 long textWidth, textHeight;
480 dc.GetTextExtent(GetLabel(), &textWidth, &textHeight);
481
482 float textX = (tabLeft + tabRight - textWidth) / 2;
483 float textY = (tabInc + tabTop + m_view->GetVerticalTabTextSpacing());
484
485 dc.DrawText(GetLabel(), (long)textX, (long)textY);
486 #endif
487 }
488
489 bool wxTabControl::HitTest(int x, int y) const
490 {
491 // Top-left of tab control
492 int tabX1 = GetX() + m_view->GetViewRect().x;
493 int tabY1 = GetY() + m_view->GetViewRect().y;
494
495 // Bottom-right
496 int tabX2 = tabX1 + GetWidth();
497 int tabY2 = tabY1 + GetHeight();
498
499 if (x >= tabX1 && y >= tabY1 && x <= tabX2 && y <= tabY2)
500 return true;
501 else
502 return false;
503 }
504
505 IMPLEMENT_DYNAMIC_CLASS(wxTabView, wxObject)
506
507 wxTabView::wxTabView(long style)
508 {
509 m_noTabs = 0;
510 m_tabStyle = style;
511 m_tabSelection = -1;
512 m_tabHeight = 20;
513 m_tabSelectionHeight = m_tabHeight + 2;
514 m_tabWidth = 80;
515 m_tabHorizontalOffset = 10;
516 m_tabHorizontalSpacing = 2;
517 m_tabVerticalTextSpacing = 3;
518 m_topMargin = 5;
519 m_tabViewRect.x = 20;
520 m_tabViewRect.y = 20;
521 m_tabViewRect.width = 300;
522 m_tabViewRect.x = 300;
523 m_highlightColour = *wxWHITE;
524 m_shadowColour = wxColour(128, 128, 128);
525 // m_backgroundColour = wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE);
526 m_textColour = *wxBLACK;
527 m_highlightPen = wxWHITE_PEN;
528 m_shadowPen = wxGREY_PEN;
529 // SetBackgroundColour(m_backgroundColour);
530 m_tabFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
531 m_tabSelectedFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
532 m_window = (wxWindow *) NULL;
533 }
534
535 wxTabView::~wxTabView()
536 {
537 ClearTabs(true);
538 }
539
540 // Automatically positions tabs
541 // TODO: this should just add the tab to a list, and then
542 // a layout function (e.g. Realize) should be called when all tabs have been added.
543 // The view rect could easily change as the view window is resized.
544 wxTabControl *wxTabView::AddTab(int id, const wxString& label, wxTabControl *existingTab)
545 {
546 // First, find which layer we should be adding to.
547 wxTabLayerList::compatibility_iterator node = m_layers.GetLast();
548 if (!node)
549 {
550 wxTabLayer *newLayer = new wxTabLayer;
551 node = m_layers.Append(newLayer);
552 }
553 // Check if adding another tab control would go off the
554 // right-hand edge of the layer.
555 wxTabLayer *tabLayer = (wxTabLayer *)node->GetData();
556 wxList::compatibility_iterator lastTabNode = tabLayer->GetLast();
557 if (lastTabNode)
558 {
559 wxTabControl *lastTab = (wxTabControl *)lastTabNode->GetData();
560 // Start another layer (row).
561 // Tricky choice: can't just check if will be overlapping the edge, because
562 // this happens anyway for 2nd and subsequent rows.
563 // Should check this for 1st row, and then subsequent rows should not exceed 1st
564 // in length.
565 if (((tabLayer == m_layers.GetFirst()->GetData()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
566 > GetViewRect().width)) ||
567 ((tabLayer != m_layers.GetFirst()->GetData()) && (tabLayer->GetCount() == ((wxTabLayer *)m_layers.GetFirst()->GetData())->GetCount())))
568 {
569 tabLayer = new wxTabLayer;
570 m_layers.Append(tabLayer);
571 lastTabNode = wxList::compatibility_iterator();
572 }
573 }
574 int layer = m_layers.GetCount() - 1;
575
576 wxTabControl *tabControl = existingTab;
577 if (!existingTab)
578 tabControl = OnCreateTabControl();
579 tabControl->SetRowPosition(tabLayer->GetCount());
580 tabControl->SetColPosition(layer);
581
582 wxTabControl *lastTab = (wxTabControl *) NULL;
583 if (lastTabNode)
584 lastTab = (wxTabControl *)lastTabNode->GetData();
585
586 // Top of new tab
587 int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
588 // Offset from view top-left
589 int horizontalOffset = 0;
590 if (!lastTab)
591 horizontalOffset = layer*GetHorizontalTabOffset();
592 else
593 horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();
594
595 tabControl->SetPosition(horizontalOffset, verticalOffset);
596 tabControl->SetSize(GetTabWidth(), GetTabHeight());
597 tabControl->SetId(id);
598 tabControl->SetLabel(label);
599 tabControl->SetFont(* GetTabFont());
600
601 tabLayer->Append(tabControl);
602 m_noTabs ++;
603
604 return tabControl;
605 }
606
607 // Remove the tab without deleting the window
608 bool wxTabView::RemoveTab(int id)
609 {
610 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
611 while (layerNode)
612 {
613 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
614 wxList::compatibility_iterator tabNode = layer->GetFirst();
615 while (tabNode)
616 {
617 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
618 if (tab->GetId() == id)
619 {
620 if (id == m_tabSelection)
621 m_tabSelection = -1;
622 delete tab;
623 layer->Erase(tabNode);
624 m_noTabs --;
625
626 // The layout has changed
627 LayoutTabs();
628 return true;
629 }
630 tabNode = tabNode->GetNext();
631 }
632 layerNode = layerNode->GetNext();
633 }
634 return false;
635 }
636
637 bool wxTabView::SetTabText(int id, const wxString& label)
638 {
639 wxTabControl* control = FindTabControlForId(id);
640 if (!control)
641 return false;
642 control->SetLabel(label);
643 return true;
644 }
645
646 wxString wxTabView::GetTabText(int id) const
647 {
648 wxTabControl* control = FindTabControlForId(id);
649 if (!control)
650 return wxEmptyString;
651 else
652 return control->GetLabel();
653 }
654
655 // Returns the total height of the tabs component -- this may be several
656 // times the height of a tab, if there are several tab layers (rows).
657 int wxTabView::GetTotalTabHeight()
658 {
659 int minY = 0;
660
661 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
662 while (layerNode)
663 {
664 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
665 wxList::compatibility_iterator tabNode = layer->GetFirst();
666 while (tabNode)
667 {
668 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
669
670 if (tab->GetY() < minY)
671 minY = tab->GetY();
672
673 tabNode = tabNode->GetNext();
674 }
675 layerNode = layerNode->GetNext();
676 }
677
678 return - minY;
679 }
680
681 void wxTabView::ClearTabs(bool deleteTabs)
682 {
683 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
684 while (layerNode)
685 {
686 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
687 wxList::compatibility_iterator tabNode = layer->GetFirst();
688 while (tabNode)
689 {
690 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
691 if (deleteTabs)
692 delete tab;
693 wxList::compatibility_iterator next = tabNode->GetNext();
694 layer->Erase(tabNode);
695 tabNode = next;
696 }
697 wxTabLayerList::compatibility_iterator nextLayerNode = layerNode->GetNext();
698 delete layer;
699 m_layers.Erase(layerNode);
700 layerNode = nextLayerNode;
701 }
702 m_noTabs = 0;
703 m_tabSelection = -1;
704 }
705
706
707 // Layout tabs (optional, e.g. if resizing window)
708 void wxTabView::LayoutTabs(void)
709 {
710 // Make a list of the tab controls, deleting the wxTabLayers.
711 wxList controls;
712
713 wxTabLayerList::compatibility_iterator layerNode = m_layers.GetFirst();
714 while (layerNode)
715 {
716 wxTabLayer *layer = (wxTabLayer *)layerNode->GetData();
717 wxList::compatibility_iterator tabNode = layer->GetFirst();
718 while (tabNode)
719 {
720 wxTabControl *tab = (wxTabControl *)tabNode->GetData();
721 controls.Append(tab);
722 wxList::compatibility_iterator next = tabNode->GetNext();
723 layer->Erase(tabNode);
724 tabNode = next;
725 }
726 wxTabLayerList::compatibility_iterator nextLayerNode = layerNode->GetNext();
727 delete layer;
728 m_layers.Erase(layerNode);
729 layerNode = nextLayerNode;
730 }
731
732 wxTabControl *lastTab = (wxTabControl *) NULL;
733
734 wxTabLayer *currentLayer = new wxTabLayer;
735 m_layers.Append(currentLayer);
736
737 wxList::compatibility_iterator node = controls.GetFirst();
738 while (node)
739 {
740 wxTabControl *tabControl = (wxTabControl *)node->GetData();
741 if (lastTab)
742 {
743 // Start another layer (row).
744 // Tricky choice: can't just check if will be overlapping the edge, because
745 // this happens anyway for 2nd and subsequent rows.
746 // Should check this for 1st row, and then subsequent rows should not exceed 1st
747 // in length.
748 if (((currentLayer == m_layers.GetFirst()->GetData()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
749 > GetViewRect().width)) ||
750 ((currentLayer != m_layers.GetFirst()->GetData()) && (currentLayer->GetCount() == ((wxTabLayer *)m_layers.GetFirst()->GetData())->GetCount())))
751 {
752 currentLayer = new wxTabLayer;
753 m_layers.Append(currentLayer);
754 lastTab = (wxTabControl *) NULL;
755 }
756 }
757
758 int layer = m_layers.GetCount() - 1;
759
760 tabControl->SetRowPosition(currentLayer->GetCount());
761 tabControl->SetColPosition(layer);
762
763 // Top of new tab
764 int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
765 // Offset from view top-left
766 int horizontalOffset = 0;
767 if (!lastTab)
768 horizontalOffset = layer*GetHorizontalTabOffset();
769 else
770 horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();
771
772 tabControl->SetPosition(horizontalOffset, verticalOffset);
773 tabControl->SetSize(GetTabWidth(), GetTabHeight());
774
775 currentLayer->Append(tabControl);
776 lastTab = tabControl;
777
778 node = node->GetNext();
779 }
780
781 // Move the selected tab to the bottom
782 wxTabControl *control = FindTabControlForId(m_tabSelection);
783 if (control)
784 MoveSelectionTab(control);
785
786 }
787
788 // Draw all tabs
789 void wxTabView::Draw(wxDC& dc)
790 {
791 // Don't draw anything if there are no tabs.
792 if (GetNumberOfTabs() == 0)
793 return;
794
795 // Draw top margin area (beneath tabs and above view area)
796 if (GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
797 {
798 dc.SetPen(*wxTRANSPARENT_PEN);
799 if(GetBackgroundBrush())
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
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
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
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
1005 void wxTabView::OnTabActivate(int /*activateId*/, int /*deactivateId*/)
1006 {
1007 }
1008
1009 void wxTabView::SetHighlightColour(const wxColour& col)
1010 {
1011 m_highlightColour = col;
1012 m_highlightPen = wxThePenList->FindOrCreatePen(col);
1013 }
1014
1015 void wxTabView::SetShadowColour(const wxColour& col)
1016 {
1017 m_shadowColour = col;
1018 m_shadowPen = wxThePenList->FindOrCreatePen(col);
1019 }
1020
1021 void wxTabView::SetBackgroundColour(const wxColour& col)
1022 {
1023 m_backgroundColour = col;
1024 m_backgroundPen = wxThePenList->FindOrCreatePen(col);
1025 m_backgroundBrush = wxTheBrushList->FindOrCreateBrush(col);
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
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...
1068 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)
1088 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.
1101 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
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
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
1156 wxTabbedDialog::~wxTabbedDialog(void)
1157 {
1158 if (m_tabView)
1159 delete m_tabView;
1160 }
1161
1162 void wxTabbedDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event) )
1163 {
1164 Destroy();
1165 }
1166
1167 void wxTabbedDialog::OnMouseEvent(wxMouseEvent& event )
1168 {
1169 if (m_tabView)
1170 m_tabView->OnEvent(event);
1171 }
1172
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
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
1198 wxTabbedPanel::~wxTabbedPanel(void)
1199 {
1200 delete m_tabView;
1201 }
1202
1203 void wxTabbedPanel::OnMouseEvent(wxMouseEvent& event)
1204 {
1205 if (m_tabView)
1206 m_tabView->OnEvent(event);
1207 }
1208
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
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
1236 wxPanelTabView::~wxPanelTabView(void)
1237 {
1238 ClearWindows(true);
1239 }
1240
1241 // Called when a tab is activated
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
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
1266 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
1272 void wxPanelTabView::ClearWindows(bool deleteWindows)
1273 {
1274 if (deleteWindows)
1275 WX_CLEAR_HASH_MAP(wxIntToWindowHashMap, m_tabWindows);
1276 m_tabWindows.clear();
1277 }
1278
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