001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: aschultz $' 006 * '$Date: 2011-03-19 02:24:12 +0000 (Sat, 19 Mar 2011) $' 007 * '$Revision: 27324 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.gui; 031 032import java.awt.Dimension; 033import java.awt.Font; 034import java.awt.Insets; 035import java.awt.event.ActionListener; 036import java.awt.event.HierarchyEvent; 037import java.awt.event.HierarchyListener; 038import java.awt.event.KeyEvent; 039 040import javax.swing.Action; 041import javax.swing.BorderFactory; 042import javax.swing.Box; 043import javax.swing.BoxLayout; 044import javax.swing.JButton; 045import javax.swing.JPanel; 046import javax.swing.JTextField; 047import javax.swing.SwingConstants; 048import javax.swing.UIManager; 049import javax.swing.border.Border; 050import javax.swing.plaf.InsetsUIResource; 051 052import org.kepler.util.StaticResources; 053 054/** 055 * Class to build the Search User Interface JPanel that allows the user to enter 056 * a single search term to search by. 5 buttons can be optionally configured by 057 * setting Actions for those buttons. 058 * <ul> 059 * The 5 configurable buttons are 060 * <li>Search Button</li> 061 * <li>Reset Button</li> 062 * <li>Cancel Button</li> 063 * <li>Source Button</li> 064 * <li>Advanced Button</li> 065 * </ul> 066 * 067 * @author brooke 068 * @since 4 Nov 2005 069 */ 070public class SearchUIJPanel extends JPanel { 071 072 // constants 073 074 // SPACING is the exterior space around buttons 075 private static final int SPACING_HORIZ = 0; //originally:1 but now trying to minimize pane width 076 private static final int SPACING_VERT = 1; 077 078 private static final int BUTTON_WIDTH = 83; //originally:72 but didn't fit on os X default jbutton 079 private static final int BUTTON_HEIGHT = 25; 080 private static final int SPACER_ABOVE_BELOW_TITLEDBORDER = 10; 081 private static final int WIDE_BUTTON_SPACER = 10; 082 083 private int PANEL_WIDTH = 200; // recalculated during init() 084 public static boolean SEARCHREPOS = false; 085 public static final String SEARCH_BUTTON_CAPTION = StaticResources 086 .getDisplayString("search.search", ""); 087 //public static final String RESET_BUTTON_CAPTION = StaticResources 088 // .getDisplayString("search.reset", ""); 089 public static final String CANCEL_BUTTON_CAPTION = StaticResources 090 .getDisplayString("search.cancel", ""); 091 public static final String SOURCE_BUTTON_CAPTION = StaticResources 092 .getDisplayString("search.sources", ""); 093 public static final String ADV_BUTTON_CAPTION = StaticResources 094 .getDisplayString("search.advanced", ""); 095 096 private final Dimension BUTTON_DIMS = new Dimension(BUTTON_WIDTH, 097 BUTTON_HEIGHT); 098 099 // this seemed too wide 100 //private final Dimension WIDE_BUTTON_DIMS = new Dimension(SPACING_HORIZ + 2 101 // * BUTTON_WIDTH, BUTTON_HEIGHT); 102 private final Dimension WIDE_BUTTON_DIMS = new Dimension(WIDE_BUTTON_SPACER+ 103 BUTTON_WIDTH, BUTTON_HEIGHT); 104 105 // set the inside space between the button's borders 106 // and the text it contains 107 private final InsetsUIResource BUTTON_INSIDE_PADDING = new InsetsUIResource( 108 2, 0, 2, 0); // (top, left, bottom, right) 109 110 private static int BUTTON_FONT_SIZE = StaticResources.getSize( 111 "button.limitedSpace.maxFontSize", 11); 112 113 private JTextField searchValueField = null; 114 115 private String borderTitle = "Search"; 116 private Border titledBorder; 117 118 private Action searchAction; 119 //private Action resetAction; 120 private Action cancelAction; 121 private Action sourceAction; 122 private Action advancedAction; 123 124 private JButton searchButton; 125 //private JButton resetButton; 126 private JButton cancelButton; 127 private JButton sourceButton; 128 private JButton advancedButton; 129 130 /** 131 * Empty constructor. 132 */ 133 public SearchUIJPanel() { 134 } 135 136 /** 137 * Constructor - creates a panel with a titled border (from String param 138 * panelBorderTitle), containing a textfield with up to 5 buttons beneath 139 * it. Constructor can accept <code>javax.swing.Action</code> objects for 140 * these 5 buttons; if any of these Action objects is null, the button will 141 * not be displayed on the user interface at runtime 142 * 143 * @param panelBorderTitle 144 * String 145 * @param searchButtonAction 146 * Action 147 * @param resetButtonAction 148 * Action 149 * @param cancelButtonAction 150 * Action 151 * @param sourceButtonAction 152 * Action 153 * @param advancedButtonAction 154 * Action 155 */ 156 public SearchUIJPanel(String panelBorderTitle, Action searchButtonAction, 157 Action resetButtonAction, Action cancelButtonAction, 158 Action sourceButtonAction, Action advancedButtonAction) { 159 160 setBorderTitle(panelBorderTitle); 161 setSearchAction(searchButtonAction); 162 //setResetAction(resetButtonAction); 163 setCancelAction(cancelButtonAction); 164 setSourceAction(sourceButtonAction); 165 setAdvancedAction(advancedButtonAction); 166 } 167 168 /** 169 * Returns the border title for this search panel. The default title is 170 * "Search". 171 * 172 * @return the search border title 173 */ 174 public String getBorderTitle() { 175 return borderTitle; 176 } 177 178 public void setBorderTitle(String borderTitle) { 179 this.borderTitle = borderTitle; 180 } 181 182 /** 183 * Returns the javax.swing.Action object that is called when the Search 184 * button is pressed. 185 * 186 * @return the Action object that handles the search button 187 */ 188 public Action getSearchAction() { 189 return searchAction; 190 } 191 192 /** 193 * Sets the Action to be used when the Search button is pressed. If this 194 * Action is not set then the Search button will not be visible to the user. 195 * 196 * @param searchAction 197 * the Action to be used when the Search button is pressed 198 */ 199 public void setSearchAction(Action searchAction) { 200 this.searchAction = searchAction; 201 } 202 203 /** 204 * Returns the javax.swing.Action object that is called when the Reset 205 * button is pressed. 206 * 207 * @return the Action object that handles the reset button 208 */ 209 //public Action getResetAction() { 210 // return resetAction; 211 //} 212 213 /** 214 * Sets the Action to be used when the Reset button is pressed. If this 215 * Action is not set then the Reset button will not be visible to the user. 216 * 217 * @param resetAction 218 * the Action to be used when the Reset button is pressed 219 */ 220 //public void setResetAction(Action resetAction) { 221 // this.resetAction = resetAction; 222 //} 223 224 /** 225 * Returns the javax.swing.Action object that is called when the Cancel 226 * button is pressed. 227 * 228 * @return the Action object that handles the Cancel button 229 */ 230 public Action getCancelAction() { 231 return cancelAction; 232 } 233 234 /** 235 * Sets the Action to be used when the Cancel button is pressed. If this 236 * Action is not set then the Cancel button will not be visible to the user. 237 * 238 * @param cancelAction 239 * the Action to be used when the Cancel button is pressed 240 */ 241 public void setCancelAction(Action cancelAction) { 242 this.cancelAction = cancelAction; 243 } 244 245 /** 246 * Returns the javax.swing.Action object that is called when the Source 247 * button is pressed. 248 * 249 * @return the Action object that handles the Source button 250 */ 251 public Action getSourceAction() { 252 return sourceAction; 253 } 254 255 /** 256 * Sets the Action to be used when the Source button is pressed. If this 257 * Action is not set then the Source button will not be visible to the user. 258 * 259 * @param sourceAction 260 * the Action to be used when the Source button is pressed 261 */ 262 public void setSourceAction(Action sourceAction) { 263 this.sourceAction = sourceAction; 264 } 265 266 /** 267 * Returns the javax.swing.Action object that is called when the Advanced 268 * button is pressed. 269 * 270 * @return the Action object that handles the Advanced button 271 */ 272 public Action getAdvancedAction() { 273 return advancedAction; 274 } 275 276 /** 277 * Sets the Action to be used when the Advanced button is pressed. If this 278 * Action is not set then the Advanced button will not be visible to the 279 * user. 280 * 281 * @param advancedAction 282 * the Action to be used when the Advanced button is pressed 283 */ 284 public void setAdvancedAction(Action advancedAction) { 285 this.advancedAction = advancedAction; 286 } 287 288 /** 289 * 290 * @return the current search term String from the textfield 291 */ 292 public String getSearchTerm() { 293 return searchValueField.getText(); 294 } 295 296 /** 297 * get the preferred/minimum width of this panel - calculated to allow 298 * enough space for all buttons and spacers etc 299 * 300 * @return the minimum allowable width of this panel 301 */ 302 public final int getMinimumWidth() { 303 return PANEL_WIDTH; 304 } 305 306 /** 307 * set the current search term String in the textfield 308 * 309 * @param searchTerm 310 * String 311 */ 312 public void setSearchTerm(String searchTerm) { 313 if (searchTerm == null){ 314 searchTerm = ""; 315 } 316 searchValueField.setText(searchTerm.trim()); 317 } 318 319 /** 320 * if enabled==true, enable the textfield, search, reset, source and 321 * advanced buttons, and disable the cancel button. If enabled==false, do 322 * the reverse. 323 * 324 * @param enabled 325 * boolean 326 */ 327 public void setSearchEnabled(boolean enabled) { 328 searchValueField.setEnabled(enabled); 329 if (searchButton != null){ 330 searchButton.setEnabled(enabled); 331 } 332 //if (resetButton != null) 333 // resetButton.setEnabled(enabled); 334 if (cancelButton != null){ 335 cancelButton.setEnabled(!enabled); 336 } 337 if (sourceButton != null){ 338 sourceButton.setEnabled(enabled); 339 } 340 if (advancedButton != null){ 341 advancedButton.setEnabled(enabled); 342 } 343 } 344 345 //enable/disable cancel button 346 public void setCancelButtonEnabled(boolean enabled) { 347 if (cancelButton != null){ 348 cancelButton.setEnabled(enabled); 349 } 350 } 351 352 /** 353 * Enables/disables _all_ Search buttons we will use this to disable all 354 * buttons as we fetch datasources from the repository if enabled==true, 355 * enable the textfield, search, reset, source and advanced buttons, and 356 * cancel button. If enabled==false, do the reverse. 357 * 358 * @param enabled 359 * boolean 360 */ 361 public void setAllSearchEnabled(boolean enabled) { 362 searchValueField.setEnabled(enabled); 363 if (searchButton != null){ 364 searchButton.setEnabled(enabled); 365 } 366 //if (resetButton != null) 367 // resetButton.setEnabled(enabled); 368 if (cancelButton != null){ 369 cancelButton.setEnabled(enabled); 370 } 371 if (sourceButton != null){ 372 sourceButton.setEnabled(enabled); 373 } 374 if (advancedButton != null){ 375 advancedButton.setEnabled(enabled); 376 } 377 } 378 379 /** 380 * Initialize the search panel. This should be called after constructing and 381 * adding whichever Actions you want the search panel to contain. 382 */ 383 public void init() { 384 385 // the panel that contains the textfield 386 JPanel searchPanel = createPanel(); 387 searchPanel.setBackground(TabManager.BGCOLOR); 388 JPanel checkBoxPanel = createPanel(); 389 checkBoxPanel.setBackground(TabManager.BGCOLOR); 390 391 // remember default button margins 392 final InsetsUIResource defaultUIMgrButtonMargin = (InsetsUIResource) UIManager 393 .get("Button.margin"); 394 // now set our custom ones 395 UIManager.put("Button.margin", BUTTON_INSIDE_PADDING); 396 397 // remember default button font 398 final Font defaultUIMgrButtonFont = (Font) UIManager.get("Button.font"); 399 400 // now set our custom size, provided it's smaller than the default: 401 int buttonFontSize = (defaultUIMgrButtonFont.getSize() < BUTTON_FONT_SIZE) ? defaultUIMgrButtonFont 402 .getSize() 403 : BUTTON_FONT_SIZE; 404 405 final Font BUTTON_FONT = new Font(defaultUIMgrButtonFont.getFontName(), 406 defaultUIMgrButtonFont.getStyle(), buttonFontSize); 407 408 UIManager.put("Button.font", BUTTON_FONT); 409 410 searchValueField = new JTextField(); 411 //special search style for mac os X 412 searchValueField.putClientProperty("JTextField.variant", "search"); 413 //when mac os X user clicks x in textfield, Reset 414 searchValueField.putClientProperty("JTextField.Search.CancelAction", cancelAction); 415 searchValueField.setAction(searchAction); 416 417 searchPanel.add(searchValueField); 418 419 if (searchAction != null) { 420 searchButton = createButton(SEARCH_BUTTON_CAPTION, searchAction); 421 searchButton.setMnemonic(KeyEvent.VK_ENTER); 422 searchPanel.add(searchButton); 423 //searchValueField.addActionListener(searchAction); 424 } 425 426 JPanel buttonsPanel = createButtonPanel(); 427 buttonsPanel.setBackground(TabManager.BGCOLOR); 428 429 if (advancedAction != null) { 430 advancedButton = createWideButton(ADV_BUTTON_CAPTION, 431 advancedAction); 432 buttonsPanel.add(advancedButton); 433 } 434 else{ 435 buttonsPanel.add(Box.createHorizontalStrut(BUTTON_DIMS.width)); 436 } 437 if (sourceAction != null) { 438 sourceButton = createButton(SOURCE_BUTTON_CAPTION, sourceAction); 439 440 buttonsPanel.add(sourceButton); 441 if (advancedAction != null) { 442 buttonsPanel 443 .add(Box.createHorizontalStrut(SPACING_HORIZ)); 444 } 445 } 446 447 if (cancelAction != null) { 448 cancelButton = createButton(CANCEL_BUTTON_CAPTION, cancelAction); 449 buttonsPanel.add(cancelButton); 450 } 451 452 // restore default button margins 453 if (defaultUIMgrButtonMargin != null) { 454 UIManager.put("Button.margin", defaultUIMgrButtonMargin); 455 } 456 // restore default button font 457 if (defaultUIMgrButtonFont != null) { 458 UIManager.put("Button.font", defaultUIMgrButtonFont); 459 } 460 461 JPanel titledPanel = new JPanel(); 462 titledPanel.setBackground(TabManager.BGCOLOR); 463 titledPanel.setLayout(new BoxLayout(titledPanel, BoxLayout.Y_AXIS)); 464 titledBorder = BorderFactory.createTitledBorder(borderTitle); 465 titledPanel.setBorder(titledBorder); 466 titledPanel.add(searchPanel); 467 titledPanel.add(checkBoxPanel); 468 titledPanel.add(buttonsPanel); 469 470 this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 471 this.add(Box.createVerticalStrut(SPACER_ABOVE_BELOW_TITLEDBORDER)); 472 this.add(titledPanel); 473 this.add(Box.createVerticalStrut(SPACER_ABOVE_BELOW_TITLEDBORDER)); 474 475 final JPanel instance = this; 476 this.addHierarchyListener(new HierarchyListener() { 477 public void hierarchyChanged(HierarchyEvent e) { 478 if (getRootPane() == null){ 479 return; 480 } 481 if (!instance.isShowing()){ 482 return; 483 } 484 // having searchButton be defaultButton 485 // means Enters get accepted a little too liberally 486 // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4544 487 //getRootPane().setDefaultButton(searchButton); 488 } 489 }); 490 491 final Insets borderInsets = titledBorder.getBorderInsets(this); 492 PANEL_WIDTH = (3 * (BUTTON_WIDTH + (2 * SPACING_HORIZ))) 493 + borderInsets.left + borderInsets.right; 494 495 setSearchEnabled(true); 496 this.setBackground(TabManager.BGCOLOR); 497 } 498 499 public void closing() { 500 //System.out.println("SearchUIJPanel.closing()"); 501 searchButton.setAction(null); 502 sourceButton.setAction(null); 503 cancelButton.setAction(null); 504 advancedButton.setAction(null); 505 searchAction = null; 506 sourceAction = null; 507 cancelAction = null; 508 advancedAction = null; 509 } 510 511 private JPanel createButtonPanel() { 512 JPanel buttonPanel = createPanel(); 513 buttonPanel.setBackground(TabManager.BGCOLOR); 514 buttonPanel.add(Box.createHorizontalGlue()); 515 return buttonPanel; 516 } 517 518 private JPanel createPanel() { 519 JPanel panel = new JPanel(); 520 panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); 521 panel.setBorder(BorderFactory.createEmptyBorder(SPACING_VERT, 522 SPACING_HORIZ, SPACING_VERT, SPACING_HORIZ)); 523 return panel; 524 } 525 526 private JButton createButton(String caption, ActionListener listener) { 527 return createButton(caption, listener, BUTTON_DIMS); 528 } 529 530 private JButton createWideButton(String caption, ActionListener listener) { 531 return createButton(caption, listener, WIDE_BUTTON_DIMS); 532 } 533 534 private JButton createButton(String caption, ActionListener listener, 535 Dimension dims) { 536 JButton button = new JButton(caption); 537 button.addActionListener(listener); 538 button.setMinimumSize(dims); 539 button.setPreferredSize(dims); 540 button.setMaximumSize(dims); 541 button.setAlignmentX(SwingConstants.CENTER); 542 return button; 543 } 544}