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