]> git.saurik.com Git - wxWidgets.git/blame - src/generic/tabg.cpp
fixes for the focus handling: don't set back to back set/kill focus events
[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
1e6feb95
VZ
23#if wxUSE_TAB_DIALOG
24
c801d85f
KB
25#ifndef WX_PRECOMP
26#include "wx/wx.h"
27#endif
28
29#include <stdio.h>
30#include <stdlib.h>
31#include <stdarg.h>
32#include <math.h>
33
34#include "wx/tab.h"
35
4b5f3fe6
JS
36// not defined: use old, square tab implementation (fills in tabs)
37// defined: use new, rounded tab implementation (doesn't colour in tabs)
38// #define wxUSE_NEW_METHOD
39
c801d85f
KB
40IMPLEMENT_DYNAMIC_CLASS(wxTabControl, wxObject)
41
42IMPLEMENT_DYNAMIC_CLASS(wxTabLayer, wxList)
43
44wxTabControl::wxTabControl(wxTabView *v)
45{
46 m_view = v;
47 m_isSelected = FALSE;
c801d85f
KB
48 m_offsetX = 0;
49 m_offsetY = 0;
50 m_width = 0;
51 m_height = 0;
52 m_id = 0;
53 m_rowPosition = 0;
54 m_colPosition = 0;
55}
56
57wxTabControl::~wxTabControl(void)
58{
59}
2b854a32 60
c801d85f
KB
61void wxTabControl::OnDraw(wxDC& dc, bool lastInRow)
62{
63 // Old, but in some ways better (drawing opaque tabs)
4b5f3fe6 64#ifndef wxUSE_NEW_METHOD
c801d85f
KB
65 if (!m_view)
66 return;
2b854a32 67
c801d85f
KB
68 // Top-left of tab view area
69 int viewX = m_view->GetViewRect().x;
70 int viewY = m_view->GetViewRect().y;
2b854a32 71
c801d85f
KB
72 // Top-left of tab control
73 int tabX = GetX() + viewX;
74 int tabY = GetY() + viewY;
75 int tabHeightInc = 0;
76 if (m_isSelected)
77 {
4b5f3fe6 78 tabHeightInc = (m_view->GetTabSelectionHeight() - m_view->GetTabHeight());
c801d85f
KB
79 tabY -= tabHeightInc;
80 }
2b854a32 81
4b5f3fe6 82 dc.SetPen(*wxTRANSPARENT_PEN);
c801d85f
KB
83
84 // Draw grey background
4b5f3fe6 85 if (m_view->GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
c801d85f 86 {
4b5f3fe6 87 dc.SetBrush(*m_view->GetBackgroundBrush());
c801d85f
KB
88
89 // Add 1 because the pen is transparent. Under Motif, may be different.
02800301
JS
90#ifdef __WXMOTIF__
91 dc.DrawRectangle(tabX, tabY, (GetWidth()+1), (GetHeight() + tabHeightInc));
92#else
c801d85f 93 dc.DrawRectangle(tabX, tabY, (GetWidth()+1), (GetHeight() + 1 + tabHeightInc));
02800301 94#endif
c801d85f 95 }
2b854a32 96
c801d85f 97 // Draw highlight and shadow
4b5f3fe6 98 dc.SetPen(*m_view->GetHighlightPen());
c801d85f
KB
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);
4b5f3fe6 119 dc.SetPen(*m_view->GetShadowPen());
c801d85f
KB
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
4b5f3fe6 138 dc.SetPen(*wxBLACK_PEN);
c801d85f
KB
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
2b854a32 151
c801d85f
KB
152 int topY = m_view->GetViewRect().y - m_view->GetTopMargin();
153
154 int maxPositions = ((wxTabLayer *)m_view->GetLayers().Nth(0)->Data())->Number();
155
156 // Only down to the bottom of the tab, not to the top of the view
4b5f3fe6 157 if ( GetRowPosition() < (maxPositions - 1) )
c801d85f
KB
158 topY = tabY + GetHeight() + tabHeightInc;
159
02800301
JS
160#ifdef __WXMOTIF__
161 topY -= 1;
162#endif
163
c801d85f
KB
164 // Shadow
165 dc.DrawLine((tabX + GetWidth()), tabY, (tabX + GetWidth()), topY);
166 // Draw black line to emphasize shadow
4b5f3fe6 167 dc.SetPen(*wxBLACK_PEN);
c801d85f
KB
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
02800301
JS
182#ifdef __WXMOTIF__
183 subtractThis += 1;
184#endif
185
c801d85f
KB
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
4b5f3fe6 191 dc.SetPen(*wxBLACK_PEN);
c801d85f
KB
192 dc.DrawLine((tabX + GetWidth() + 1), (tabY+1), (tabX + GetWidth() + 1),
193 (tabY + GetHeight() + tabHeightInc - subtractThis));
194 }
195 }
2b854a32 196
c801d85f
KB
197 // Draw centered text
198 int textY = tabY + m_view->GetVerticalTabTextSpacing() + tabHeightInc;
199
200 if (m_isSelected)
02800301 201 dc.SetFont(* m_view->GetSelectedTabFont());
c801d85f 202 else
02800301 203 dc.SetFont(* GetFont());
c801d85f
KB
204
205 wxColour col(m_view->GetTextColour());
4b5f3fe6 206 dc.SetTextForeground(col);
c801d85f 207 dc.SetBackgroundMode(wxTRANSPARENT);
4b5f3fe6 208 long textWidth, textHeight;
c801d85f
KB
209 dc.GetTextExtent(GetLabel(), &textWidth, &textHeight);
210
211 int textX = (int)(tabX + (GetWidth() - textWidth)/2.0);
7fe7d506
JS
212 if (textX < (tabX + 2))
213 textX = (tabX + 2);
214
215 dc.SetClippingRegion(tabX, tabY, GetWidth(), GetHeight());
c801d85f 216 dc.DrawText(GetLabel(), textX, textY);
7fe7d506 217 dc.DestroyClippingRegion();
c801d85f
KB
218
219 if (m_isSelected)
220 {
4b5f3fe6 221 dc.SetPen(*m_view->GetHighlightPen());
c801d85f
KB
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 }
4b5f3fe6
JS
231#else
232 // New HEL version with rounder tabs
c801d85f 233
2b854a32
VZ
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().Nth(0)->Data())->Number();
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);
c801d85f
KB
486#endif
487}
488
489bool 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();
2b854a32 498
c801d85f
KB
499 if (x >= tabX1 && y >= tabY1 && x <= tabX2 && y <= tabY2)
500 return TRUE;
501 else
502 return FALSE;
503}
504
505IMPLEMENT_DYNAMIC_CLASS(wxTabView, wxObject)
506
507wxTabView::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 = *wxLIGHT_GREY;
526 m_textColour = *wxBLACK;
527 m_highlightPen = wxWHITE_PEN;
528 m_shadowPen = wxGREY_PEN;
529 m_backgroundPen = wxLIGHT_GREY_PEN;
530 m_backgroundBrush = wxLIGHT_GREY_BRUSH;
a756f210
VS
531 m_tabFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
532 m_tabSelectedFont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
c67daf87 533 m_window = (wxWindow *) NULL;
c801d85f
KB
534}
535
536wxTabView::~wxTabView()
537{
02800301 538 ClearTabs(TRUE);
c801d85f 539}
2b854a32 540
c801d85f 541// Automatically positions tabs
4b5f3fe6
JS
542// TODO: this should just add the tab to a list, and then
543// a layout function (e.g. Realize) should be called when all tabs have been added.
544// The view rect could easily change as the view window is resized.
c801d85f
KB
545wxTabControl *wxTabView::AddTab(int id, const wxString& label, wxTabControl *existingTab)
546{
547 // First, find which layer we should be adding to.
548 wxNode *node = m_layers.Last();
549 if (!node)
550 {
551 wxTabLayer *newLayer = new wxTabLayer;
552 node = m_layers.Append(newLayer);
553 }
554 // Check if adding another tab control would go off the
555 // right-hand edge of the layer.
556 wxTabLayer *tabLayer = (wxTabLayer *)node->Data();
557 wxNode *lastTabNode = tabLayer->Last();
558 if (lastTabNode)
559 {
560 wxTabControl *lastTab = (wxTabControl *)lastTabNode->Data();
561 // Start another layer (row).
562 // Tricky choice: can't just check if will be overlapping the edge, because
563 // this happens anyway for 2nd and subsequent rows.
564 // Should check this for 1st row, and then subsequent rows should not exceed 1st
565 // in length.
566 if (((tabLayer == m_layers.First()->Data()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
567 > GetViewRect().width)) ||
568 ((tabLayer != m_layers.First()->Data()) && (tabLayer->Number() == ((wxTabLayer *)m_layers.First()->Data())->Number())))
569 {
570 tabLayer = new wxTabLayer;
571 m_layers.Append(tabLayer);
c67daf87 572 lastTabNode = (wxNode *) NULL;
c801d85f
KB
573 }
574 }
575 int layer = m_layers.Number() - 1;
2b854a32 576
c801d85f
KB
577 wxTabControl *tabControl = existingTab;
578 if (!existingTab)
579 tabControl = OnCreateTabControl();
580 tabControl->SetRowPosition(tabLayer->Number());
581 tabControl->SetColPosition(layer);
2b854a32 582
c67daf87 583 wxTabControl *lastTab = (wxTabControl *) NULL;
c801d85f
KB
584 if (lastTabNode)
585 lastTab = (wxTabControl *)lastTabNode->Data();
2b854a32 586
c801d85f
KB
587 // Top of new tab
588 int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
589 // Offset from view top-left
590 int horizontalOffset = 0;
591 if (!lastTab)
592 horizontalOffset = layer*GetHorizontalTabOffset();
593 else
594 horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();
2b854a32 595
c801d85f
KB
596 tabControl->SetPosition(horizontalOffset, verticalOffset);
597 tabControl->SetSize(GetTabWidth(), GetTabHeight());
598 tabControl->SetId(id);
599 tabControl->SetLabel(label);
02800301 600 tabControl->SetFont(* GetTabFont());
2b854a32 601
c801d85f
KB
602 tabLayer->Append(tabControl);
603 m_noTabs ++;
2b854a32 604
c801d85f
KB
605 return tabControl;
606}
621793f4
JS
607
608// Remove the tab without deleting the window
609bool wxTabView::RemoveTab(int id)
610{
611 wxNode *layerNode = m_layers.First();
612 while (layerNode)
613 {
614 wxTabLayer *layer = (wxTabLayer *)layerNode->Data();
615 wxNode *tabNode = layer->First();
616 while (tabNode)
617 {
618 wxTabControl *tab = (wxTabControl *)tabNode->Data();
619 if (tab->GetId() == id)
620 {
621 if (id == m_tabSelection)
622 m_tabSelection = -1;
623 delete tab;
624 delete tabNode;
7fe7d506 625 m_noTabs --;
621793f4
JS
626
627 // The layout has changed
f03fc89f 628 LayoutTabs();
621793f4
JS
629 return TRUE;
630 }
631 tabNode = tabNode->Next();
632 }
633 layerNode = layerNode->Next();
634 }
635 return FALSE;
636}
7fe7d506
JS
637
638bool wxTabView::SetTabText(int id, const wxString& label)
639{
640 wxTabControl* control = FindTabControlForId(id);
641 if (!control)
642 return FALSE;
643 control->SetLabel(label);
644 return TRUE;
645}
646
647wxString wxTabView::GetTabText(int id) const
648{
649 wxTabControl* control = FindTabControlForId(id);
650 if (!control)
651 return wxEmptyString;
652 else
653 return control->GetLabel();
654}
2b854a32 655
4b5f3fe6
JS
656// Returns the total height of the tabs component -- this may be several
657// times the height of a tab, if there are several tab layers (rows).
658int wxTabView::GetTotalTabHeight()
659{
660 int minY = 0;
661
662 wxNode *layerNode = m_layers.First();
663 while (layerNode)
664 {
665 wxTabLayer *layer = (wxTabLayer *)layerNode->Data();
666 wxNode *tabNode = layer->First();
667 while (tabNode)
668 {
669 wxTabControl *tab = (wxTabControl *)tabNode->Data();
670
671 if (tab->GetY() < minY)
672 minY = tab->GetY();
673
674 tabNode = tabNode->Next();
675 }
676 layerNode = layerNode->Next();
677 }
678
679 return - minY;
680}
681
c801d85f
KB
682void wxTabView::ClearTabs(bool deleteTabs)
683{
684 wxNode *layerNode = m_layers.First();
685 while (layerNode)
686 {
687 wxTabLayer *layer = (wxTabLayer *)layerNode->Data();
688 wxNode *tabNode = layer->First();
689 while (tabNode)
690 {
691 wxTabControl *tab = (wxTabControl *)tabNode->Data();
692 if (deleteTabs)
693 delete tab;
694 wxNode *next = tabNode->Next();
695 delete tabNode;
696 tabNode = next;
697 }
698 wxNode *nextLayerNode = layerNode->Next();
699 delete layer;
700 delete layerNode;
701 layerNode = nextLayerNode;
702 }
7fe7d506 703 m_noTabs = 0;
51babd09 704 m_tabSelection = -1;
c801d85f 705}
4b5f3fe6
JS
706
707
c801d85f 708// Layout tabs (optional, e.g. if resizing window)
f03fc89f 709void wxTabView::LayoutTabs(void)
c801d85f
KB
710{
711 // Make a list of the tab controls, deleting the wxTabLayers.
712 wxList controls;
713
714 wxNode *layerNode = m_layers.First();
715 while (layerNode)
716 {
717 wxTabLayer *layer = (wxTabLayer *)layerNode->Data();
718 wxNode *tabNode = layer->First();
719 while (tabNode)
720 {
721 wxTabControl *tab = (wxTabControl *)tabNode->Data();
722 controls.Append(tab);
723 wxNode *next = tabNode->Next();
724 delete tabNode;
725 tabNode = next;
726 }
727 wxNode *nextLayerNode = layerNode->Next();
728 delete layer;
729 delete layerNode;
730 layerNode = nextLayerNode;
731 }
2b854a32 732
c67daf87 733 wxTabControl *lastTab = (wxTabControl *) NULL;
2b854a32 734
c801d85f
KB
735 wxTabLayer *currentLayer = new wxTabLayer;
736 m_layers.Append(currentLayer);
2b854a32 737
c801d85f
KB
738 wxNode *node = controls.First();
739 while (node)
740 {
741 wxTabControl *tabControl = (wxTabControl *)node->Data();
742 if (lastTab)
743 {
744 // Start another layer (row).
745 // Tricky choice: can't just check if will be overlapping the edge, because
746 // this happens anyway for 2nd and subsequent rows.
747 // Should check this for 1st row, and then subsequent rows should not exceed 1st
748 // in length.
749 if (((currentLayer == m_layers.First()->Data()) && ((lastTab->GetX() + 2*lastTab->GetWidth() + GetHorizontalTabSpacing())
750 > GetViewRect().width)) ||
751 ((currentLayer != m_layers.First()->Data()) && (currentLayer->Number() == ((wxTabLayer *)m_layers.First()->Data())->Number())))
752 {
753 currentLayer = new wxTabLayer;
754 m_layers.Append(currentLayer);
c67daf87 755 lastTab = (wxTabControl *) NULL;
c801d85f
KB
756 }
757 }
2b854a32 758
c801d85f
KB
759 int layer = m_layers.Number() - 1;
760
761 tabControl->SetRowPosition(currentLayer->Number());
762 tabControl->SetColPosition(layer);
2b854a32 763
c801d85f
KB
764 // Top of new tab
765 int verticalOffset = (- GetTopMargin()) - ((layer+1)*GetTabHeight());
766 // Offset from view top-left
767 int horizontalOffset = 0;
768 if (!lastTab)
769 horizontalOffset = layer*GetHorizontalTabOffset();
770 else
771 horizontalOffset = lastTab->GetX() + GetTabWidth() + GetHorizontalTabSpacing();
2b854a32 772
c801d85f
KB
773 tabControl->SetPosition(horizontalOffset, verticalOffset);
774 tabControl->SetSize(GetTabWidth(), GetTabHeight());
775
776 currentLayer->Append(tabControl);
777 lastTab = tabControl;
778
779 node = node->Next();
780 }
781
782 // Move the selected tab to the bottom
783 wxTabControl *control = FindTabControlForId(m_tabSelection);
784 if (control)
785 MoveSelectionTab(control);
786
787}
788
789// Draw all tabs
790void wxTabView::Draw(wxDC& dc)
791{
7fe7d506
JS
792 // Don't draw anything if there are no tabs.
793 if (GetNumberOfTabs() == 0)
794 return;
795
2b854a32
VZ
796 // Draw top margin area (beneath tabs and above view area)
797 if (GetTabStyle() & wxTAB_STYLE_COLOUR_INTERIOR)
798 {
799 dc.SetPen(*wxTRANSPARENT_PEN);
800 dc.SetBrush(*GetBackgroundBrush());
801
802 // Add 1 because the pen is transparent. Under Motif, may be different.
803 dc.DrawRectangle(
804 m_tabViewRect.x,
805 (m_tabViewRect.y - m_topMargin),
806 (m_tabViewRect.width + 1),
807 (m_topMargin + 1)
808 );
809 }
810
811 // Draw layers in reverse order
812 wxNode *node = m_layers.Last();
813 while (node)
814 {
815 wxTabLayer *layer = (wxTabLayer *)node->Data();
816 wxNode *node2 = layer->First();
817 while (node2)
818 {
819 wxTabControl *control = (wxTabControl *)node2->Data();
820 control->OnDraw(dc, (node2->Next() == NULL));
821 node2 = node2->Next();
822 }
823
824 node = node->Previous();
825 }
c801d85f
KB
826
827
4b5f3fe6 828#ifndef wxUSE_NEW_METHOD
2b854a32
VZ
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),
02800301 855#if defined(__WXMOTIF__)
2b854a32 856 (GetViewRect().x + GetViewRect().width + 1),
02800301 857#else
2b854a32 858 (GetViewRect().x + GetViewRect().width + 2),
02800301
JS
859#endif
860
2b854a32
VZ
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 }
c801d85f
KB
872#endif
873}
2b854a32 874
c801d85f
KB
875// Process mouse event, return FALSE if we didn't process it
876bool wxTabView::OnEvent(wxMouseEvent& event)
877{
878 if (!event.LeftDown())
879 return FALSE;
2b854a32 880
e6575209
VZ
881 wxCoord x, y;
882 event.GetPosition(&x, &y);
2b854a32 883
c67daf87 884 wxTabControl *hitControl = (wxTabControl *) NULL;
2b854a32 885
c801d85f
KB
886 wxNode *node = m_layers.First();
887 while (node)
888 {
889 wxTabLayer *layer = (wxTabLayer *)node->Data();
890 wxNode *node2 = layer->First();
891 while (node2)
892 {
893 wxTabControl *control = (wxTabControl *)node2->Data();
894 if (control->HitTest((int)x, (int)y))
895 {
896 hitControl = control;
c67daf87
UR
897 node = (wxNode *) NULL;
898 node2 = (wxNode *) NULL;
c801d85f
KB
899 }
900 else
901 node2 = node2->Next();
902 }
2b854a32 903
c801d85f
KB
904 if (node)
905 node = node->Next();
906 }
2b854a32 907
c801d85f
KB
908 if (!hitControl)
909 return FALSE;
2b854a32 910
c801d85f 911 wxTabControl *currentTab = FindTabControlForId(m_tabSelection);
2b854a32 912
c801d85f
KB
913 if (hitControl == currentTab)
914 return FALSE;
2b854a32 915
c801d85f 916 ChangeTab(hitControl);
2b854a32 917
c801d85f
KB
918 return TRUE;
919}
920
921bool wxTabView::ChangeTab(wxTabControl *control)
922{
923 wxTabControl *currentTab = FindTabControlForId(m_tabSelection);
924 int oldTab = -1;
925 if (currentTab)
926 oldTab = currentTab->GetId();
2b854a32 927
c801d85f
KB
928 if (control == currentTab)
929 return TRUE;
2b854a32 930
c801d85f
KB
931 if (m_layers.Number() == 0)
932 return FALSE;
2b854a32 933
c801d85f
KB
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);
2b854a32 942
c801d85f
KB
943 control->SetSelected(TRUE);
944 m_tabSelection = control->GetId();
945
946 OnTabActivate(control->GetId(), oldTab);
2b854a32 947
c801d85f
KB
948 // Leave window refresh for the implementing window
949
950 return TRUE;
951}
952
953// Move the selected tab to the bottom layer, if necessary,
954// without calling app activation code
955bool wxTabView::MoveSelectionTab(wxTabControl *control)
956{
957 if (m_layers.Number() == 0)
958 return FALSE;
2b854a32 959
c801d85f 960 wxTabLayer *firstLayer = (wxTabLayer *)m_layers.First()->Data();
2b854a32 961
c801d85f
KB
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 wxNode *thisNode = FindTabNodeAndColumn(control, &col);
969 if (!thisNode)
970 return FALSE;
971 wxNode *otherNode = firstLayer->Nth(col);
972 if (!otherNode)
973 return FALSE;
2b854a32 974
c801d85f
KB
975 // If this is already in the bottom layer, return now
976 if (otherNode == thisNode)
977 return TRUE;
2b854a32 978
c801d85f 979 wxTabControl *otherTab = (wxTabControl *)otherNode->Data();
2b854a32 980
c801d85f
KB
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.
2b854a32 984
c801d85f
KB
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();
2b854a32 991
c801d85f
KB
992 control->SetPosition(otherX, otherY);
993 control->SetColPosition(otherColPos);
994 otherTab->SetPosition(thisX, thisY);
995 otherTab->SetColPosition(thisColPos);
2b854a32 996
c801d85f
KB
997 // Swap the data for the nodes
998 thisNode->SetData(otherTab);
999 otherNode->SetData(control);
1000 }
1001 return TRUE;
1002}
1003
1004// Called when a tab is activated
1005void wxTabView::OnTabActivate(int /*activateId*/, int /*deactivateId*/)
1006{
1007}
2b854a32 1008
c801d85f
KB
1009void wxTabView::SetHighlightColour(const wxColour& col)
1010{
1011 m_highlightColour = col;
1012 m_highlightPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1013}
1014
1015void wxTabView::SetShadowColour(const wxColour& col)
1016{
1017 m_shadowColour = col;
1018 m_shadowPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1019}
1020
1021void wxTabView::SetBackgroundColour(const wxColour& col)
1022{
1023 m_backgroundColour = col;
1024 m_backgroundPen = wxThePenList->FindOrCreatePen(col, 1, wxSOLID);
1025 m_backgroundBrush = wxTheBrushList->FindOrCreateBrush(col, wxSOLID);
1026}
1027
1028void wxTabView::SetTabSelection(int sel, bool activateTool)
1029{
51babd09
JS
1030 if ( sel==m_tabSelection )
1031 return;
1032
c801d85f
KB
1033 int oldSel = m_tabSelection;
1034 wxTabControl *control = FindTabControlForId(sel);
621793f4 1035 wxTabControl *oldControl = FindTabControlForId(m_tabSelection);
c801d85f
KB
1036
1037 if (!OnTabPreActivate(sel, oldSel))
1038 return;
2b854a32 1039
c801d85f 1040 if (control)
621793f4
JS
1041 control->SetSelected((sel != -1)); // TODO ??
1042 else if (sel != -1)
c801d85f 1043 {
621793f4 1044 wxFAIL_MSG(_("Could not find tab for id"));
c801d85f
KB
1045 return;
1046 }
621793f4
JS
1047
1048 if (oldControl)
1049 oldControl->SetSelected(FALSE);
2b854a32 1050
c801d85f 1051 m_tabSelection = sel;
621793f4
JS
1052
1053 if (control)
1054 MoveSelectionTab(control);
2b854a32 1055
c801d85f
KB
1056 if (activateTool)
1057 OnTabActivate(sel, oldSel);
1058}
1059
1060// Find tab control for id
1061wxTabControl *wxTabView::FindTabControlForId(int id) const
1062{
1063 wxNode *node1 = m_layers.First();
1064 while (node1)
1065 {
1066 wxTabLayer *layer = (wxTabLayer *)node1->Data();
1067 wxNode *node2 = layer->First();
1068 while (node2)
1069 {
1070 wxTabControl *control = (wxTabControl *)node2->Data();
1071 if (control->GetId() == id)
1072 return control;
1073 node2 = node2->Next();
1074 }
1075 node1 = node1->Next();
1076 }
c67daf87 1077 return (wxTabControl *) NULL;
c801d85f
KB
1078}
1079
1080// Find tab control for layer, position (starting from zero)
1081wxTabControl *wxTabView::FindTabControlForPosition(int layer, int position) const
1082{
1083 wxNode *node1 = m_layers.Nth(layer);
1084 if (!node1)
c67daf87 1085 return (wxTabControl *) NULL;
c801d85f
KB
1086 wxTabLayer *tabLayer = (wxTabLayer *)node1->Data();
1087 wxNode *node2 = tabLayer->Nth(position);
1088 if (!node2)
c67daf87 1089 return (wxTabControl *) NULL;
c801d85f
KB
1090 return (wxTabControl *)node2->Data();
1091}
1092
1093// Find the node and the column at which this control is positioned.
1094wxNode *wxTabView::FindTabNodeAndColumn(wxTabControl *control, int *col) const
1095{
1096 wxNode *node1 = m_layers.First();
1097 while (node1)
1098 {
1099 wxTabLayer *layer = (wxTabLayer *)node1->Data();
1100 int c = 0;
1101 wxNode *node2 = layer->First();
1102 while (node2)
1103 {
1104 wxTabControl *cnt = (wxTabControl *)node2->Data();
1105 if (cnt == control)
1106 {
1107 *col = c;
1108 return node2;
1109 }
1110 node2 = node2->Next();
1111 c ++;
1112 }
1113 node1 = node1->Next();
1114 }
c67daf87 1115 return (wxNode *) NULL;
c801d85f
KB
1116}
1117
1118int wxTabView::CalculateTabWidth(int noTabs, bool adjustView)
1119{
1120 m_tabWidth = (int)((m_tabViewRect.width - ((noTabs - 1)*GetHorizontalTabSpacing()))/noTabs);
1121 if (adjustView)
1122 {
1123 m_tabViewRect.width = noTabs*m_tabWidth + ((noTabs-1)*GetHorizontalTabSpacing());
1124 }
1125 return m_tabWidth;
1126}
1127
1128/*
1129 * wxTabbedDialog
1130 */
2b854a32 1131
c801d85f
KB
1132IMPLEMENT_CLASS(wxTabbedDialog, wxDialog)
1133
1134BEGIN_EVENT_TABLE(wxTabbedDialog, wxDialog)
1135 EVT_CLOSE(wxTabbedDialog::OnCloseWindow)
1136 EVT_MOUSE_EVENTS(wxTabbedDialog::OnMouseEvent)
1137 EVT_PAINT(wxTabbedDialog::OnPaint)
1138END_EVENT_TABLE()
1139
debe6624 1140wxTabbedDialog::wxTabbedDialog(wxWindow *parent, wxWindowID id,
c801d85f
KB
1141 const wxString& title,
1142 const wxPoint& pos, const wxSize& size,
debe6624 1143 long windowStyle, const wxString& name):
c801d85f
KB
1144 wxDialog(parent, id, title, pos, size, windowStyle, name)
1145{
c67daf87 1146 m_tabView = (wxTabView *) NULL;
c801d85f
KB
1147}
1148
1149wxTabbedDialog::~wxTabbedDialog(void)
1150{
1151 if (m_tabView)
1152 delete m_tabView;
1153}
2b854a32 1154
c801d85f
KB
1155void wxTabbedDialog::OnCloseWindow(wxCloseEvent& WXUNUSED(event) )
1156{
1157 Destroy();
1158}
1159
1160void wxTabbedDialog::OnMouseEvent(wxMouseEvent& event )
1161{
1162 if (m_tabView)
1163 m_tabView->OnEvent(event);
1164}
1165
1166void wxTabbedDialog::OnPaint(wxPaintEvent& WXUNUSED(event) )
1167{
1168 wxPaintDC dc(this);
1169 if (m_tabView)
1170 m_tabView->Draw(dc);
1171}
1172
1173/*
1174 * wxTabbedPanel
1175 */
2b854a32 1176
c801d85f
KB
1177IMPLEMENT_CLASS(wxTabbedPanel, wxPanel)
1178
1179BEGIN_EVENT_TABLE(wxTabbedPanel, wxPanel)
1180 EVT_MOUSE_EVENTS(wxTabbedPanel::OnMouseEvent)
1181 EVT_PAINT(wxTabbedPanel::OnPaint)
1182END_EVENT_TABLE()
1183
debe6624
JS
1184wxTabbedPanel::wxTabbedPanel(wxWindow *parent, wxWindowID id, const wxPoint& pos,
1185 const wxSize& size, long windowStyle, const wxString& name):
c801d85f
KB
1186 wxPanel(parent, id, pos, size, windowStyle, name)
1187{
c67daf87 1188 m_tabView = (wxTabView *) NULL;
c801d85f
KB
1189}
1190
1191wxTabbedPanel::~wxTabbedPanel(void)
1192{
1193 delete m_tabView;
1194}
2b854a32 1195
c801d85f
KB
1196void wxTabbedPanel::OnMouseEvent(wxMouseEvent& event)
1197{
1198 if (m_tabView)
1199 m_tabView->OnEvent(event);
1200}
1201
1202void wxTabbedPanel::OnPaint(wxPaintEvent& WXUNUSED(event) )
1203{
1204 wxPaintDC dc(this);
1205 if (m_tabView)
1206 m_tabView->Draw(dc);
1207}
1208
1209/*
4b5f3fe6 1210 * wxPanelTabView
c801d85f 1211 */
2b854a32 1212
c801d85f 1213IMPLEMENT_CLASS(wxPanelTabView, wxTabView)
2b854a32 1214
c801d85f
KB
1215wxPanelTabView::wxPanelTabView(wxPanel *pan, long style): wxTabView(style), m_tabWindows(wxKEY_INTEGER)
1216{
1217 m_panel = pan;
c67daf87 1218 m_currentWindow = (wxWindow *) NULL;
c801d85f
KB
1219
1220 if (m_panel->IsKindOf(CLASSINFO(wxTabbedDialog)))
1221 ((wxTabbedDialog *)m_panel)->SetTabView(this);
1222 else if (m_panel->IsKindOf(CLASSINFO(wxTabbedPanel)))
1223 ((wxTabbedPanel *)m_panel)->SetTabView(this);
1224
1225 SetWindow(m_panel);
1226}
1227
1228wxPanelTabView::~wxPanelTabView(void)
1229{
1230 ClearWindows(TRUE);
1231}
1232
1233// Called when a tab is activated
1234void wxPanelTabView::OnTabActivate(int activateId, int deactivateId)
1235{
1236 if (!m_panel)
1237 return;
2b854a32 1238
c801d85f
KB
1239 wxWindow *oldWindow = ((deactivateId == -1) ? 0 : GetTabWindow(deactivateId));
1240 wxWindow *newWindow = GetTabWindow(activateId);
1241
1242 if (oldWindow)
1243 oldWindow->Show(FALSE);
1244 if (newWindow)
1245 newWindow->Show(TRUE);
2b854a32 1246
c801d85f
KB
1247 m_panel->Refresh();
1248}
1249
2b854a32 1250
c801d85f
KB
1251void wxPanelTabView::AddTabWindow(int id, wxWindow *window)
1252{
1253 m_tabWindows.Append((long)id, window);
1254 window->Show(FALSE);
1255}
1256
1257wxWindow *wxPanelTabView::GetTabWindow(int id) const
1258{
1259 wxNode *node = m_tabWindows.Find((long)id);
1260 if (!node)
c67daf87 1261 return (wxWindow *) NULL;
2b854a32 1262 return (wxWindow *)node->Data();
c801d85f
KB
1263}
1264
1265void wxPanelTabView::ClearWindows(bool deleteWindows)
1266{
1267 if (deleteWindows)
1268 m_tabWindows.DeleteContents(TRUE);
1269 m_tabWindows.Clear();
1270 m_tabWindows.DeleteContents(FALSE);
1271}
1272
1273void wxPanelTabView::ShowWindowForTab(int id)
1274{
1275 wxWindow *newWindow = GetTabWindow(id);
1276 if (newWindow == m_currentWindow)
1277 return;
1278 if (m_currentWindow)
1279 m_currentWindow->Show(FALSE);
1280 newWindow->Show(TRUE);
1281 newWindow->Refresh();
1282}
1283
1e6feb95 1284#endif // wxUSE_TAB_DIALOG