1 19 package org.netbeans.swing.tabcontrol.plaf; 20 21 import org.netbeans.swing.tabcontrol.TabDataModel; 22 23 import javax.swing.*; 24 import java.awt.*; 25 import java.util.Arrays ; 26 27 32 33 46 public final class ScrollingTabLayoutModel implements TabLayoutModel { 47 51 private int offset = -1; 52 56 private TabLayoutModel wrapped; 57 61 private boolean changed = true; 62 65 TabDataModel mdl; 66 70 SingleSelectionModel sel; 71 76 private int makeVisibleTab = -1; 77 81 int pixelsToAddToSelection = 0; 82 86 private boolean lastTabClipped = false; 87 90 private int firstVisibleTab = -1; 91 94 private int lastVisibleTab = -1; 95 98 private int width = -1; 99 103 private int[] widths = null; 104 105 108 public ScrollingTabLayoutModel(TabLayoutModel wrapped, 109 SingleSelectionModel sel, TabDataModel mdl) { 110 this.wrapped = wrapped; 111 this.mdl = mdl; 112 this.sel = sel; 113 } 114 115 public ScrollingTabLayoutModel(TabLayoutModel wrapped, 116 SingleSelectionModel sel, TabDataModel mdl, 117 int minimumXposition) { 118 this(wrapped, sel, mdl); 119 this.minimumXposition = minimumXposition; 120 } 121 122 public void setMinimumXposition (int x) { 123 this.minimumXposition = x; 124 setChanged(true); 125 } 126 127 132 public void setPixelsToAddToSelection (int i) { 133 pixelsToAddToSelection = i; 134 setChanged (true); 135 } 136 137 private int minimumXposition = 0; 138 139 144 public void clearCachedData() { 145 setChanged(true); 146 } 147 148 152 private TabLayoutModel getWrapped() { 153 return wrapped; 154 } 155 156 161 public int getOffset() { 162 if (mdl.size() <= 1) { 163 return -1; 164 } 165 return offset; 166 } 167 168 173 private void change() { 174 if (mdl.size() == 0) { 175 widths = new int[0]; 177 updateActions(); 178 setChanged(false); 179 return; 180 } 181 if (widths == null || widths.length != mdl.size()) { 184 widths = new int[mdl.size()]; 185 } 187 Arrays.fill(widths, 0); 188 189 if (widths.length == 1) { 190 offset = -1; 194 } 195 196 if (width < getMinimumLeftClippedWidth()) { 200 int toBeShown = makeVisibleTab != -1 ? 201 makeVisibleTab : sel.getSelectedIndex(); 202 if (toBeShown != -1) { 203 widths[toBeShown] = width; 204 } else { 205 widths[0] = width; 206 } 207 firstVisibleTab = toBeShown; 208 lastVisibleTab = toBeShown; 209 setChanged(false); 210 return; 211 } 212 213 int x = minimumXposition; 215 int start = offset >= 0 ? offset : 0; 217 int toRedistribute = -1; 221 lastVisibleTab = -1; 224 firstVisibleTab = start; 227 lastTabClipped = false; 229 230 if (start == mdl.size() - 1 && width < getWrapped().getW(start) + getMinimumLeftClippedWidth()) { 233 lastVisibleTab = start; 234 if (start != 0) { 235 firstVisibleTab = start - 1; 236 widths[start] = width - getMinimumLeftClippedWidth(); 237 widths[start - 1] = getMinimumLeftClippedWidth(); 238 lastTabClipped = width - getMinimumLeftClippedWidth() < getWrapped().getW(start); 239 } else { 240 firstVisibleTab = start; 241 widths[start] = width; 242 lastTabClipped = width < getWrapped().getW(start); 243 } 244 updateActions(); 245 setChanged(false); 248 249 return; 250 } 251 252 for (int i = start; i < widths.length; i++) { 253 int w; 254 if (i == offset) { 255 w = getMinimumLeftClippedWidth(); 258 } else { 259 w = getWrapped().getW(i); 262 } 263 if (x + w > width) { 266 if (width - x < getMinimumRightClippedWidth() && i != start) { 267 widths[i - 1] += (width - x) - 1; 272 toRedistribute = (width - x); 274 lastVisibleTab = i - 1; 277 widths[i] = 0; 279 } else { 280 widths[i] = (width - x) - 1; 284 lastVisibleTab = i; 286 } 287 lastTabClipped = true; 290 break; 292 } 293 widths[i] = w; 296 x += w; 297 if (i == widths.length - 1) { 300 lastVisibleTab = widths.length - 1; 301 } 302 } 303 304 int selected = sel.getSelectedIndex(); 307 if (pixelsToAddToSelection != 0 && selected > start 310 && selected < lastVisibleTab) { 311 widths[selected] += pixelsToAddToSelection; 313 int perTab = pixelsToAddToSelection 317 - 1 / (lastVisibleTab - start); 318 int pixels = pixelsToAddToSelection - 1; 320 for (int i = start; i <= lastVisibleTab; i++) { 322 if (i != selected) { 323 if (perTab == 0) { 327 widths[i] -= 2; 329 pixels -= 2; 330 if (pixels <= 0) { 331 break; 333 } 334 } else { 335 widths[i] -= perTab; 338 pixels -= perTab; 341 if (pixels <= 0) { 343 break; 344 } 345 } 346 } 347 } 348 } 349 350 if (toRedistribute != -1 && lastVisibleTab != start 354 && lastVisibleTab != start + 1) { 355 int perTab = toRedistribute / ((lastVisibleTab + 1) - start); 357 for (int i = start; i < lastVisibleTab; i++) { 358 if (perTab != 0) { 359 widths[i] += perTab; 360 widths[lastVisibleTab] -= perTab; 361 } else { 362 int use = toRedistribute > 2 ? 2 : toRedistribute; 363 widths[i] += use; 364 widths[lastVisibleTab] -= use; 365 toRedistribute -= use; 366 if (toRedistribute <= 0) { 367 break; 369 } 370 } 371 } 372 } 373 updateActions(); 374 setChanged(false); 377 } 378 379 private void setChanged(boolean val) { 380 if (changed != val) { 381 changed = val; 382 } 383 } 384 385 392 public int getPixelsToAddToSelection() { 393 return pixelsToAddToSelection; 394 } 395 396 400 public boolean isLastTabClipped() { 401 if (width < getMinimumLeftClippedWidth()) { 402 return true; 403 } 404 return lastTabClipped; 405 } 406 407 421 public boolean makeVisible (int index, int width) { 422 if (width < 0) { 423 setWidth (width); 424 makeVisibleTab = index; 425 return false; 426 } 427 428 boolean resized = width != this.width || recentlyResized; 429 recentlyResized = false; 430 431 setWidth(width); 433 434 if (index == -1) { 435 return false; 436 } 437 438 if (mdl.size() == 1) { 440 setOffset (-1); 441 return changed; 442 } 443 444 if (mdl.size() == 2) { 446 int totalWidth = getWrapped().getW(0) + getWrapped().getW(1); 447 if (totalWidth > width) { 448 setOffset (0); 449 return changed; 450 } 451 } 452 453 if (changed) { 454 change(); 455 } 456 457 if (index == 0) { 459 int off = setOffset(-1); 460 return off != -1; 461 } 462 int widthForRequestedTab = getWrapped().getW(index); 463 464 if (widthForRequestedTab > width) { 467 setOffset (index-1); 469 return changed; 470 } 471 472 if (index == mdl.size() - 1 && !isLastTabClipped() && !resized) { 475 return false; 476 } 477 478 int newOffset = -2; 479 480 int currW = 0; 481 boolean isOffBack = false; 482 boolean result = changed; 483 boolean switchForward = false; 484 if (index >= getLastVisibleTab(width)) { 487 int selIdx = sel.getSelectedIndex(); 488 switchForward = index >= selIdx; 489 490 currW = getWrapped().getW(index); 492 if (index == selIdx) { 493 currW += pixelsToAddToSelection; 494 } 495 int firstTab = index; 496 do { 498 firstTab--; 499 if (firstTab > -1) { 500 if (firstTab == selIdx) { 501 currW += pixelsToAddToSelection; 502 } 503 int wid = getWrapped().getW(firstTab); 504 currW += wid; 505 } 506 } while (currW <= width && firstTab >= -1); 507 newOffset = firstTab; 508 if( currW <= width || switchForward ) { 509 newOffset++; 510 if( getOffset() == -1 && newOffset == -1 ) 511 newOffset = 0; 512 } 513 } else if (index <= getFirstVisibleTab(width)) { 514 isOffBack = true; 515 newOffset = index-1; 516 } 517 518 if (resized || !isOffBack || index == mdl.size() && getFirstVisibleTab(width) == index) { 519 if (newOffset != -2) { 520 setOffset (newOffset); 521 } 522 result = ensureAvailableSpaceUsed(false); 523 524 } else { 525 if (newOffset != -2) { 526 int old = offset; 527 int nue = setOffset (Math.min (mdl.size(), newOffset)); 528 result = old != nue; 529 } 530 } 531 return result; 532 } 533 534 boolean ensureAvailableSpaceUsed(boolean useCached) { 535 if (mdl.size() == 0) { 536 return false; 537 } 538 boolean result = false; 539 if (changed && !useCached) { 540 result = true; 541 change(); 542 } 543 int last = mdl.size() -1; 544 int lastTab = useCached ? getCachedLastVisibleTab() : getLastVisibleTab(width); 545 if (lastTab == last || lastTab == mdl.size() && last > -1) { int off = offset; 547 int availableWidth = width - (getX(last) + getW(last)); 548 549 while (availableWidth > 0 && off > -1) { 550 availableWidth -= getWrapped().getW(off); 551 if (availableWidth > 0) { 552 off--; 553 } 554 } 555 setOffset (off); 556 if (changed) { 557 result = true; 558 change(); 559 } 560 } 561 return result; 562 } 563 564 568 int getMinimumRightClippedWidth() { 569 return 40; 570 } 571 572 576 int getMinimumLeftClippedWidth() { 577 return 40; 578 } 579 580 585 public void setWidth(int width) { 586 if (this.width != width) { 587 recentlyResized = true; 588 if (width < this.width) { 591 makeVisibleTab = sel.getSelectedIndex(); 593 } 594 boolean needMakeVisible = (width > 0 && this.width < 0 595 && makeVisibleTab != -1); 596 this.width = width - minimumXposition; 597 setChanged(width > getMinimumLeftClippedWidth()); 598 if (changed && needMakeVisible 599 && width > getMinimumLeftClippedWidth()) { 600 makeVisible(makeVisibleTab, width); 601 makeVisibleTab = -1; 602 } 603 } 604 } 605 606 private boolean recentlyResized = true; 607 608 613 public int setOffset(int i) { 614 int prevOffset = offset; 615 if (mdl.size() == 1) { 616 if (offset > -1) { 617 offset = -1; 618 setChanged(true); 619 } 620 return prevOffset; 621 } 622 623 if (mdl.size() == 2 && width < getMinimumLeftClippedWidth() 624 + getMinimumRightClippedWidth()) { 625 offset = -1; 626 setChanged(false); 627 return prevOffset; 628 } 629 630 if (i < -1) { 631 i = -1; 633 } 634 if (i != offset) { 635 setChanged(true); 636 offset = i; 637 } 638 return prevOffset; 639 } 640 641 645 public int getFirstVisibleTab(int width) { 646 setWidth(width); 647 if (mdl.size() == 0) { 648 return -1; 649 } 650 if (width < getMinimumLeftClippedWidth()) { 651 int first = makeVisibleTab == -1 ? 652 sel.getSelectedIndex() : makeVisibleTab; 653 return first; 654 } 655 if (changed) { 656 change(); 657 } 658 return firstVisibleTab; 659 } 660 661 664 public int countVisibleTabs(int width) { 665 return (getLastVisibleTab(width) + 1) 666 - getFirstVisibleTab(width); 667 } 668 669 672 public int getLastVisibleTab(int width) { 673 setWidth(width); 674 if (mdl.size() == 0) { 675 return -1; 676 } 677 if (width < getMinimumLeftClippedWidth()) { 678 int first = makeVisibleTab == -1 ? 679 sel.getSelectedIndex() : makeVisibleTab; 680 return first; 681 } 682 if (changed) { 683 change(); 684 } 685 return lastVisibleTab; 686 } 687 688 693 int getCachedLastVisibleTab() { 694 return lastVisibleTab; 695 } 696 697 702 int getCachedFirstVisibleTab() { 703 return firstVisibleTab; 704 } 705 706 public int dropIndexOfPoint(int x, int y) { 707 if (changed) { 708 change(); 709 } 710 int first = getFirstVisibleTab(width); 711 int last = getLastVisibleTab(width); 712 int pos = 0; for (int i = first; i <= last; i++) { 714 int lastPos = pos; 715 pos += getW(i); 716 int h = getH(i); 717 int ay = getY(i); 718 if (y < 0 || y > ay + h) { 719 return -1; 720 } 721 if (i == last && x > lastPos + (getW(i) / 2)) { 722 return last + 1; 723 } 724 if (x >= lastPos && x <= pos) { 725 return i; 726 } 727 } 728 return -1; 729 } 730 731 public void setPadding(Dimension d) { 732 getWrapped().setPadding(d); 733 setChanged (true); 734 } 735 736 public int getH(int index) { 737 if (changed) { 738 change(); 739 } 740 try { 741 return getWrapped().getH(index); 742 } catch (IndexOutOfBoundsException e) { 743 return 0; 746 } 747 } 748 749 753 public int getW(int index) { 754 if (changed || widths == null || index > widths.length) { 757 change(); 758 } 759 if (index >= widths.length) { 760 return 0; 763 } 764 return widths[index]; 765 } 766 767 public int getX(int index) { 768 if (changed) { 769 change(); 770 } 771 int result = minimumXposition; 772 for (int i = 0; i < index; i++) { 773 result += getW(i); 774 } 775 return result; 776 } 777 778 public int getY(int index) { 779 if (changed) { 780 change(); 781 } 782 return getWrapped().getY(index); 783 } 784 785 public int indexOfPoint(int x, int y) { 786 if (changed) { 787 change(); 788 } 789 int pos = minimumXposition; 790 int lastPos; 791 for (int i = offset == -1 ? 0 : offset; i < mdl.size(); i++) { 792 lastPos = pos; 793 int w = getW(i); 794 pos += w; 795 if (w == 0) { 796 break; 797 } 798 int h = getH(i); 799 int ay = getY(i); 800 if (y < 0 || y > ay + h) { 801 return -1; 802 } 803 if (x > lastPos && x < pos) { 804 return i; 805 } 806 } 807 return -1; 808 } 809 810 811 private Action fAction = null; 812 private Action bAction = null; 813 814 817 public Action getForwardAction() { 818 if (fAction == null) { 819 fAction = new ForwardAction(); 820 } 821 return fAction; 822 } 823 824 827 public Action getBackwardAction() { 828 if (bAction == null) { 829 bAction = new BackwardAction(); 830 } 831 return bAction; 832 } 833 834 838 private void updateActions() { 839 if (width <= getMinimumLeftClippedWidth()) { 840 bAction.setEnabled(false); 841 fAction.setEnabled(false); 842 } 843 if (bAction != null) { 844 bAction.setEnabled(mdl.size() > 1 && offset > -1); 845 } 846 if (fAction != null) { 847 fAction.setEnabled(isLastTabClipped() && mdl.size() > 2 848 && (lastVisibleTab-firstVisibleTab > 1 || lastVisibleTab < mdl.size()-1)); 850 } 851 } 852 853 856 private class ForwardAction extends AbstractAction { 857 public void actionPerformed(java.awt.event.ActionEvent e) { 858 setOffset(getOffset() + 1); 859 Component jc = (Component) getValue("control"); if (jc != null) { 862 jc.repaint(); 863 } 864 } 865 } 866 867 870 private class BackwardAction extends AbstractAction { 871 public void actionPerformed(java.awt.event.ActionEvent e) { 872 setOffset(getOffset() - 1); 873 Component jc = (Component) getValue("control"); if (jc != null) { 876 jc.repaint(); 877 } 878 } 879 } 880 } 881 | Popular Tags |