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