001/* 002 * Copyright (c) 2005 Einar Pehrson <einar@pehrson.nu>. 003 * Copyright (c) Nobuo Tamemasa 004 * 005 * This file is part of 006 * CleanSheets - a spreadsheet application for the Java platform. 007 * 008 * CleanSheets is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License as published by 010 * the Free Software Foundation; either version 2 of the License, or 011 * (at your option) any later version. 012 * 013 * CleanSheets is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with CleanSheets; if not, write to the Free Software 020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 021 */ 022package csheets.ui.sheet; 023 024import java.awt.Container; 025import java.awt.Dimension; 026import java.awt.Font; 027import java.awt.FontMetrics; 028import java.awt.Graphics; 029import java.awt.Insets; 030import java.awt.LayoutManager; 031import java.awt.Rectangle; 032import java.awt.event.ActionEvent; 033import java.awt.event.ActionListener; 034 035import javax.swing.AbstractAction; 036import javax.swing.JButton; 037import javax.swing.JComponent; 038import javax.swing.JTabbedPane; 039import javax.swing.SwingConstants; 040import javax.swing.plaf.basic.BasicTabbedPaneUI; 041import javax.swing.plaf.metal.MetalTabbedPaneUI; 042 043/** 044 * An extension of the <code>MetalTabbedPaneUI</code> that adds a number of 045 * navigation buttons to the pane. 046 * @author Nobuo Tamemasa 047 * @author Einar Pehrson 048 */ 049public class WorkbookPaneUI extends MetalTabbedPaneUI { 050 051 protected ActionListener[] buttonListeners; 052 053 /** 054 * Creates a new WorkbookPane UI. 055 */ 056 public WorkbookPaneUI() {} 057 058 public void installUI(JComponent c) { 059 this.tabPane = (JTabbedPane)c; 060 c.setLayout(createLayoutManager()); 061 installDefaults(); 062 installComponents(); 063 installListeners(); 064 installKeyboardActions(); 065 runCount = 1; 066 selectedRun = 0; 067 } 068 069 public void uninstallUI(JComponent c) { 070 uninstallComponents(); 071 super.uninstallUI(c); 072 } 073 074 protected LayoutManager createLayoutManager() { 075 return new SingleRowTabbedLayout(tabPane); 076 } 077 078 protected void installComponents() { 079 JButton[] buttons = ((WorkbookPane)tabPane).getButtons(); 080 for (int i=0;i<buttons.length;i++) { 081 tabPane.add(buttons[i]); 082 } 083 } 084 085 protected void uninstallComponents() { 086 JButton[] buttons = ((WorkbookPane)tabPane).getButtons(); 087 for (int i=0;i<buttons.length;i++) { 088 tabPane.remove(buttons[i]); 089 } 090 } 091 092 protected void installListeners() { 093 super.installListeners(); 094 WorkbookPane stabPane = (WorkbookPane)tabPane; 095 JButton[] buttons = stabPane.getButtons(); 096 int n = buttons.length; 097 buttonListeners = new ActionListener[n]; 098 099 for (int i=0;i<n;i++) { 100 buttonListeners[i] = null; 101 String str = buttons[i].getActionCommand(); 102 103 if (str.equals(WorkbookPane.FIRST_COMMAND)) { 104 buttonListeners[i] = new TabShifter(); 105 } else if (str.equals(WorkbookPane.PREV_COMMAND)) { 106 buttonListeners[i] = new TabShifter() { 107 protected int getStartIndex() { 108 return sPane.getVisibleStartIndex() - 1; 109 } 110 }; 111 } else if (str.equals(WorkbookPane.NEXT_COMMAND)) { 112 buttonListeners[i] = new TabShifter() { 113 protected int getStartIndex() { 114 return sPane.getVisibleStartIndex() + 1; 115 } 116 }; 117 } else if (str.equals(WorkbookPane.LAST_COMMAND)) { 118 buttonListeners[i] = new TabShifter() { 119 protected int getStartIndex() { 120 return getStartIndex(sPane.getTabCount() - 1); 121 } 122 }; 123 } 124 buttons[i].addActionListener(buttonListeners[i]); 125 } 126 } 127 128 protected void uninstallListeners() { 129 super.uninstallListeners(); 130 JButton[] buttons = ((WorkbookPane)tabPane).getButtons(); 131 for (int i=0;i<buttons.length;i++) { 132 buttons[i].removeActionListener(buttonListeners[i]); 133 } 134 } 135 136 public int tabForCoordinate(JTabbedPane pane, int x, int y) { 137 WorkbookPane stabPane = (WorkbookPane)tabPane; 138 int visibleCount = stabPane.getVisibleCount(); 139 int visibleStartIndex = stabPane.getVisibleStartIndex(); 140 141 for (int i=0,index = visibleStartIndex; i < visibleCount 142 && i < rects.length; i++,index++) { 143 if (rects[index].contains(x, y)) { 144 return index; 145 } 146 } 147 return -1; 148 } 149 150 public void paint(Graphics g, JComponent c) { 151 int selectedIndex = tabPane.getSelectedIndex(); 152 int tabPlacement = tabPane.getTabPlacement(); 153 ensureCurrentLayout(); 154 155 WorkbookPane stabPane = (WorkbookPane)tabPane; 156 int visibleCount = stabPane.getVisibleCount(); 157 int visibleStartIndex = stabPane.getVisibleStartIndex(); 158 159 Rectangle iconRect = new Rectangle(), 160 textRect = new Rectangle(); 161 Rectangle clipRect = g.getClipBounds(); 162 tabRuns[0] = visibleStartIndex; 163 164 for (int i=0,index=visibleStartIndex; i<visibleCount && i<rects.length; i++,index++) { 165 if (rects[index].intersects(clipRect)) { 166 paintTab(g, tabPlacement, rects, index, iconRect, textRect); 167 } 168 } 169 if (stabPane.isVisibleTab(selectedIndex)) { 170 if (rects[selectedIndex].intersects(clipRect)) { 171 paintTab(g, tabPlacement, rects, selectedIndex, iconRect, textRect); 172 } 173 } 174 175 paintContentBorder(g, tabPlacement, selectedIndex); 176 } 177 178 179 protected void paintContentBorderTopEdge( Graphics g, 180 int tabPlacement, int selectedIndex, int x, int y, int w, int h ) { 181 g.setColor(selectHighlight); 182 if (tabPlacement != TOP || selectedIndex < 0 || 183 (rects[selectedIndex].y + rects[selectedIndex].height + 1 < y) || 184 !((WorkbookPane)tabPane).isVisibleTab(selectedIndex) ) { 185 g.drawLine(x, y, x+w-2, y); 186 } else { 187 Rectangle selRect = rects[selectedIndex]; 188 g.drawLine(x, y, selRect.x + 1, y); 189 if (selRect.x + selRect.width < x + w - 2) { 190 g.drawLine(selRect.x + selRect.width, y, x+w-2, y); 191 } else { 192 g.setColor(shadow); 193 g.drawLine(x+w-2, y, x+w-2, y); 194 } 195 } 196 } 197 198 protected void paintContentBorderBottomEdge(Graphics g, 199 int tabPlacement, int selectedIndex, int x, int y, int w, int h) { 200 g.setColor(darkShadow); 201 if (tabPlacement != BOTTOM || selectedIndex < 0 || 202 (rects[selectedIndex].y - 1 > h) || 203 !((WorkbookPane)tabPane).isVisibleTab(selectedIndex) ) { 204 g.drawLine(x, y+h-1, x+w-1, y+h-1); 205 } else { 206 Rectangle selRect = rects[selectedIndex]; 207 g.drawLine(x, y+h-1, selRect.x, y+h-1); 208 if (selRect.x + selRect.width < x + w - 2) { 209 g.drawLine(selRect.x + selRect.width, y+h-1, x+w-1, y+h-1); 210 } 211 } 212 } 213 214 215 216 protected Insets getTabAreaInsets(int tabPlacement) { 217 WorkbookPane stabPane = (WorkbookPane)tabPane; 218 Dimension d = stabPane.getPreferredButtonSize(); 219 int n = 4; 220 Insets currentInsets = new Insets(0,0,0,0); 221 currentInsets.top = tabAreaInsets.bottom; 222 currentInsets.bottom = tabAreaInsets.top; 223 currentInsets.left = tabAreaInsets.left + n * d.width; 224 currentInsets.right = tabAreaInsets.right; 225 return currentInsets; 226 } 227 228 protected int lastTabInRun(int tabCount, int run) { 229 WorkbookPane stabPane = (WorkbookPane)tabPane; 230 return stabPane.getVisibleStartIndex() + stabPane.getVisibleCount() -1; 231 } 232 233 protected void ensureCurrentLayout() { 234 SingleRowTabbedLayout layout = (SingleRowTabbedLayout)tabPane.getLayout(); 235 layout.calculateLayoutInfo(); 236 setButtonsEnabled(); 237 } 238 239 protected void setButtonsEnabled() { 240 WorkbookPane stabPane = (WorkbookPane)tabPane; 241 int visibleCount = stabPane.getVisibleCount(); 242 int visibleStartIndex = stabPane.getVisibleStartIndex(); 243 JButton[] buttons = stabPane.getButtons(); 244 boolean lEnable = 0 < visibleStartIndex; 245 boolean rEnable = visibleStartIndex + visibleCount < tabPane.getTabCount(); 246 for (int i=0;i<buttons.length;i++) { 247 boolean enable = false; 248 String str = buttons[i].getActionCommand(); 249 if (str.equals(WorkbookPane.FIRST_COMMAND)) 250 enable = lEnable; 251 else if (str.equals(WorkbookPane.PREV_COMMAND)) 252 enable = lEnable; 253 else if (str.equals(WorkbookPane.NEXT_COMMAND)) 254 enable = rEnable; 255 else if (str.equals(WorkbookPane.LAST_COMMAND)) 256 enable = rEnable; 257 buttons[i].setEnabled(enable); 258 } 259 } 260 261 // 262 // Tab Navigation by Key 263 // (Not yet done) 264 // 265 protected void ensureVisibleTabAt(int index) { 266 WorkbookPane stabPane = (WorkbookPane)tabPane; 267 int visibleCount = stabPane.getVisibleCount(); 268 int visibleStartIndex = stabPane.getVisibleStartIndex(); 269 int visibleEndIndex = visibleStartIndex + visibleCount -1; 270 271 if (visibleStartIndex < index && index < visibleEndIndex) { 272 return; 273 } 274 // int selectedIndex = tabPane.getSelectedIndex(); 275 // boolean directionIsRight = (0 < index - selectedIndex)? true: false; 276 //if (directionIsRight) { 277 if (index <= visibleStartIndex) { 278 //System.out.println("dec"); 279 if (visibleStartIndex == 0) return; 280 stabPane.setVisibleStartIndex( --visibleStartIndex ); 281 ((SingleRowTabbedLayout)tabPane.getLayout()).calculateLayoutInfo(); 282 int count = stabPane.getVisibleCount(); 283 int startIndex = stabPane.getVisibleStartIndex(); 284 if (startIndex <= index && 285 index <= startIndex + count-1) { 286 } else { 287 stabPane.setVisibleStartIndex( ++visibleStartIndex ); 288 } 289 } 290 //} else { 291 if (visibleEndIndex <= index) { 292 if (visibleStartIndex == visibleCount+1) return; 293 stabPane.setVisibleStartIndex( ++visibleStartIndex ); 294 ((SingleRowTabbedLayout)tabPane.getLayout()).calculateLayoutInfo(); 295 int count = stabPane.getVisibleCount(); 296 int startIndex = stabPane.getVisibleStartIndex(); 297 if (startIndex <= index && 298 index <= startIndex + count-1) { 299 } else { 300 stabPane.setVisibleStartIndex( --visibleStartIndex ); 301 } 302 } 303 //} 304 } 305 306 protected void selectNextTab(int current) { 307 for (int i=current+1;i<tabPane.getTabCount();i++) { 308 if (tabPane.isEnabledAt(i)) { 309 ensureVisibleTabAt(i); 310 tabPane.setSelectedIndex(i); 311 break; 312 } 313 } 314 } 315 316 protected void selectPreviousTab(int current) { 317 for (int i=current-1;0<=i;i--) { 318 if (tabPane.isEnabledAt(i)) { 319 ensureVisibleTabAt(i); 320 tabPane.setSelectedIndex(i); 321 break; 322 } 323 } 324 } 325 326 // these methods exist for innerclass 327 void setMaxTabHeight(int maxTabHeight) { 328 this.maxTabHeight = maxTabHeight; 329 } 330 331 int getMaxTabHeight() { 332 return maxTabHeight; 333 } 334 335 Rectangle[] getRects() { 336 return rects; 337 } 338 339 WorkbookPane getTabbedPane() { 340 return (WorkbookPane)tabPane; 341 } 342 343 protected FontMetrics getFontMetrics() { 344 Font font = tabPane.getFont(); 345 return tabPane.getFontMetrics(font); 346 } 347 348 protected int calculateMaxTabHeight(int tabPlacement) { 349 return super.calculateMaxTabHeight(tabPlacement); 350 } 351 352 protected int calculateTabWidth(int tabPlacement, int tabIndex, FontMetrics metrics) { 353 return super.calculateTabWidth(tabPlacement, tabIndex, metrics); 354 } 355 356 protected void assureRectsCreated(int tabCount) { 357 super.assureRectsCreated(tabCount); 358 } 359 360 /** 361 * The layout used for the tabbed pane. 362 */ 363 protected class SingleRowTabbedLayout extends BasicTabbedPaneUI.TabbedPaneLayout { 364 JTabbedPane tabPane; 365 366 public SingleRowTabbedLayout(JTabbedPane tabPane) { 367 this.tabPane = tabPane; 368 } 369 370 public void layoutContainer(Container parent) { 371 super.layoutContainer(parent); 372 if (tabPane.getComponentCount() < 1) { 373 return; 374 } 375 376 int tabPlacement = tabPane.getTabPlacement(); 377 int maxTabHeight = calculateMaxTabHeight(tabPlacement); 378 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 379 Insets insets = tabPane.getInsets(); 380 Rectangle bounds = tabPane.getBounds(); 381 382 WorkbookPane stabPane = (WorkbookPane)tabPane; 383 Dimension d = stabPane.getPreferredButtonSize(); 384 JButton[] buttons = stabPane.getButtons(); 385 int x,y; 386 y = bounds.y + bounds.height - insets.bottom 387 - tabAreaInsets.bottom - maxTabHeight; 388 x = bounds.x + insets.left; 389 for (int i=0;i<buttons.length;i++) { 390 buttons[i].setBounds(x, y, d.width, d.height); 391 x += d.width; 392 } 393 } 394 395 public void calculateLayoutInfo() { 396 int tabCount = tabPane.getTabCount(); 397 assureRectsCreated(tabCount); 398 calculateTabWidths(tabPane.getTabPlacement(), tabCount); 399 calculateTabRects(tabPane.getTabPlacement(), tabCount); 400 } 401 402 protected void calculateTabWidths(int tabPlacement, int tabCount) { 403 if (tabCount == 0) { 404 return; 405 } 406 FontMetrics metrics = getFontMetrics(); 407 int maxTabHeight = calculateMaxTabHeight(tabPlacement); 408 setMaxTabHeight(maxTabHeight); 409 Rectangle[] rects = getRects(); 410 for (int i = 0; i < tabCount; i++) { 411 rects[i].width = calculateTabWidth(tabPlacement, i, metrics); 412 rects[i].height = maxTabHeight; 413 } 414 } 415 416 protected void calculateTabRects(int tabPlacement, int tabCount) { 417 if (tabCount == 0) { 418 return; 419 } 420 WorkbookPane stabPane = (WorkbookPane)tabPane; 421 Dimension size = tabPane.getSize(); 422 Insets insets = tabPane.getInsets(); 423 Insets tabAreaInsets = getTabAreaInsets(tabPlacement); 424 int maxTabHeight = getMaxTabHeight(); 425 int x = insets.left + tabAreaInsets.left; 426 int y; 427 if (tabPlacement == TOP) { 428 y = insets.top + tabAreaInsets.top; 429 } else { // BOTTOM 430 y = size.height - insets.bottom - tabAreaInsets.bottom - maxTabHeight; 431 } 432 433 int returnAt = size.width - (insets.right + tabAreaInsets.right); 434 Rectangle[] rects = getRects(); 435 int visibleStartIndex = stabPane.getVisibleStartIndex(); 436 int visibleCount = 0; 437 438 for (int i = visibleStartIndex; i < tabCount; i++) { 439 Rectangle rect = rects[i]; 440 if (visibleStartIndex < i) { 441 rect.x = rects[i-1].x + rects[i-1].width; 442 } else { 443 rect.x = x; 444 } 445 446 if (rect.x + rect.width > returnAt) { 447 break; 448 } else { 449 visibleCount++; 450 rect.y = y; 451 } 452 } 453 stabPane.setVisibleCount(visibleCount); 454 stabPane.setVisibleStartIndex(visibleStartIndex); 455 } 456 } 457 458 // Listener 459 protected class TabShifter implements ActionListener { 460 WorkbookPane sPane; 461 462 public void actionPerformed(ActionEvent e) { 463 sPane = getTabbedPane(); 464 int index = getStartIndex(); 465 sPane.setVisibleStartIndex(index); 466 sPane.repaint(); 467 } 468 469 //public abstract int getStartIndex(); 470 protected int getStartIndex() { 471 return 0; // first tab 472 } 473 474 protected int getStartIndex(int lastIndex) { 475 Insets insets = sPane.getInsets(); 476 Insets tabAreaInsets = getTabAreaInsets(sPane.getTabPlacement()); 477 int width = sPane.getSize().width 478 - (insets.left + insets.right) 479 - (tabAreaInsets.left + tabAreaInsets.right); 480 int index; 481 Rectangle[] rects = getRects(); 482 for (index=lastIndex;0<=index;index--) { 483 width -= rects[index].width; 484 if (width < 0) 485 break; 486 } 487 return ++index; 488 } 489 } 490 491 /** 492 * An action for navigating between the tabs in the pane. 493 * @author Einar Pehrson 494 */ 495 @SuppressWarnings("serial") 496 protected class NavigateAction extends AbstractAction { 497 498 /** The direction in which to navigate (a SwingConstants value) */ 499 private int direction; 500 501 public NavigateAction(int direction) { 502 if (direction == SwingConstants.PREVIOUS 503 || direction == SwingConstants.NEXT) 504 this.direction = direction; 505 else 506 throw new IllegalArgumentException("A SwingConstants value expected"); 507 } 508 509 public void actionPerformed(ActionEvent event) { 510 navigateSelectedTab(direction); 511 } 512 } 513}