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