1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest.exec; 28 29 import java.awt.GridBagConstraints; 30 import java.awt.GridBagLayout; 31 import java.awt.event.ActionEvent; 32 import java.awt.event.ActionListener; 33 34 import javax.swing.Action; 35 import javax.swing.BorderFactory; 36 import javax.swing.ButtonGroup; 37 import javax.swing.JButton; 38 import javax.swing.JComboBox; 39 import javax.swing.JComponent; 40 import javax.swing.JMenu; 41 import javax.swing.JMenuItem; 42 import javax.swing.JPanel; 43 import javax.swing.JRadioButtonMenuItem; 44 45 import com.sun.javatest.TestFilter; 46 import com.sun.javatest.tool.ToolAction; 47 import com.sun.javatest.tool.UIFactory; 48 import com.sun.javatest.util.Debug; 49 import com.sun.javatest.util.DynamicArray; 50 import com.sun.javatest.util.OrderedTwoWayTable; 51 import java.util.Arrays; 52 53 class FilterSelectionHandler { 54 /** 55 * Observe changes to the state of the view. The changes to the view state 56 * will generally be the result of user actions. 57 */ 58 public interface Observer { 59 /** 60 * The state of the given filter has changed. 61 */ 62 public void filterUpdated(TestFilter f); 63 64 /** 65 * The system is requesting a different filter. 66 */ 67 public void filterSelected(TestFilter f); 68 69 public void filterAdded(TestFilter f); 70 71 /** 72 * Removing the active filter will result in an exception. 73 */ 74 public void filterRemoved(TestFilter f); 75 } 76 77 FilterSelectionHandler(FilterConfig fc, UIFactory uif) { 78 this.filterConfig = fc; 79 this.uif = uif; 80 81 listener = new Listener(); 82 83 filterConfig.addObserver(listener); 84 } 85 86 public void addObserver(Observer o) { 87 if (o == null) 88 return; 89 90 if (obs == null) 91 obs = new Observer[0]; 92 93 obs = (Observer[])DynamicArray.append(obs, o); 94 } 95 96 public void removeObserver(Observer o) { 97 obs = (Observer[])DynamicArray.remove(obs, o); 98 } 99 100 /** 101 * Get the gui component that allows the user to select and configure 102 * the active filter. This should only be called once for each instance. 103 * Only the last instance of the selector widget will be used. 104 */ 105 JComponent getFilterSelector() { 106 // may need to start offering the caller a way to control layout 107 // fill and anchor settings... 108 JPanel panel = uif.createPanel("fconfig.sp", new GridBagLayout(), false); 109 GridBagConstraints gbc = new GridBagConstraints(); 110 gbc.weightx = 1.0; 111 gbc.gridx = 0; 112 gbc.gridy = 0; 113 gbc.fill = GridBagConstraints.NONE; 114 gbc.anchor = GridBagConstraints.NORTHWEST; 115 116 selectBox = uif.createLiteralChoice("fconfig.box", filterConfig.getFilters()); 117 selectBox.addActionListener(listener); 118 selectBox.setRenderer(RenderingUtilities.createFilterListRenderer()); 119 uif.setAccessibleName(selectBox, "fconfig.box"); 120 panel.add(selectBox, gbc); 121 122 gbc.gridx = 1; 123 124 configButton = uif.createButton("fconfig.config"); 125 // make button smaller, with minimal border 126 configButton.setBorder(BorderFactory.createCompoundBorder( 127 BorderFactory.createEtchedBorder(), 128 BorderFactory.createEmptyBorder(0,3,0,3))); 129 configButton.addActionListener(listener); 130 configButton.setMaximumSize(configButton.getPreferredSize()); 131 uif.setAccessibleName(configButton, "fconfig.config"); 132 gbc.insets.left = 11; 133 gbc.weightx = 0.0; 134 panel.add(configButton, gbc); 135 panel.setMaximumSize(panel.getPreferredSize()); 136 137 // make sure a filter is selected 138 if (activeFilter == null) { 139 TestFilter[] filters = filterConfig.getFilters(); 140 if (filters != null && filters.length > 0) 141 setFilter(filters[0]); 142 } 143 else { 144 selectBox.setSelectedItem(activeFilter); 145 } 146 147 configButton.setEnabled((activeFilter instanceof ConfigurableTestFilter)); 148 149 return panel; 150 } 151 152 /** 153 * Get the filtering submenu. 154 */ 155 synchronized JMenu getFilterMenu() { 156 menuGroup = new ButtonGroup(); 157 filterMenuTable = new OrderedTwoWayTable(); 158 159 Action showEditorAction = new ToolAction(uif, "fconfig.submenu.edit") { 160 public void actionPerformed(ActionEvent e) { 161 filterConfig.showEditorDialog(getActiveFilter()); 162 } 163 }; 164 165 editMenu = uif.createMenu("fconfig.submenu"); 166 167 TestFilter[] filters = filterConfig.getFilters(); 168 for (int i = 0; i < filters.length; i++) 169 addToMenu(filters[i], -1); 170 171 // make sure a filter is selected 172 // addToMenu() also tries to do this 173 if (activeFilter == null) { 174 if (filters != null && filters.length > 0) 175 setFilter(filters[0]); 176 } 177 178 // radio set of filter names 179 editMenu.addSeparator(); 180 editMenu.add(uif.createMenuItem(showEditorAction)); 181 updateMenu(); 182 183 return editMenu; 184 185 } 186 187 /** 188 * Set the active filter. 189 * If the name cannot be matched, the request will be ignored. 190 * 191 * @param name Should never be null. 192 */ 193 synchronized void setFilter(String name) { 194 setFilter(filterConfig.getFilter(name)); 195 } 196 197 /** 198 * Set the active filter. 199 * If the filter supplied is part of the known set of filters, it 200 * becomes the active filter and all observers are notified. 201 * 202 * @param f A null filter will be ignored. 203 */ 204 synchronized void setFilter(TestFilter f) { 205 if (f == null) 206 return; 207 if (filterConfig.contains(f)) { 208 activeFilter = f; 209 210 if (selectBox != null) 211 selectBox.setSelectedItem(activeFilter); 212 213 if (configButton != null) 214 configButton.setEnabled((f instanceof ConfigurableTestFilter)); 215 216 updateMenu(); 217 218 for (int i = 0; i < obs.length; i++) 219 obs[i].filterSelected(f); 220 } 221 } 222 223 /** 224 * Return the filter currently selected. 225 * 226 * @return Null if no filter is active. 227 */ 228 synchronized TestFilter getActiveFilter() { 229 return activeFilter; 230 } 231 232 /** 233 * Hint to update any meta info about the filter if necessary. 234 */ 235 void updateFilterMetaInfo(TestFilter f) { 236 // update the drop down box if needed 237 if (activeFilter == f && selectBox != null) 238 selectBox.repaint(); 239 240 // update menu item if needed 241 // could check editMenu, filterMenuTable or menuGroup 242 if (editMenu != null) { 243 int index = filterMenuTable.getKeyIndex(f); 244 245 if (index >= 0) { 246 JMenuItem jmi = (JMenuItem)(filterMenuTable.getValueAt(index)); 247 int mne = jmi.getMnemonic(); 248 if (mne > 0) 249 jmi.setText((char)mne + " " + f.getName()); 250 else 251 jmi.setText(f.getName()); 252 } 253 } 254 } 255 256 /** 257 * Add the filter to the list of filters in the menu. 258 * No action is taken if the menu system is not initilized. No 259 * checking for duplicate insertions occurs. 260 * @param location Position to place the new item at. -1 indicates 261 * indifference. 262 * @param f The test filter to add. 263 */ 264 private synchronized void addToMenu(TestFilter f, int location) { 265 if (editMenu == null) 266 return; 267 268 boolean[] mnemonics = new boolean[10]; 269 Arrays.fill(mnemonics, false); 270 271 // guess a mnemonic 272 for (int i = 0; i < editMenu.getItemCount(); i++) { 273 JMenuItem mi = editMenu.getItem(i); 274 if (mi != null) { 275 int itemMne = mi.getMnemonic() - '0' - 1; 276 if (itemMne == -1) itemMne = 9; 277 if (itemMne >= 0 && itemMne <= 9) 278 mnemonics[itemMne] = true; 279 } 280 } 281 282 int mne = -1; 283 for (int i = 0; i < mnemonics.length; i++) { 284 if (mnemonics[i] == false) { 285 mne = i; 286 break; 287 } 288 } 289 290 if (mne == 9) { 291 mne = '0'; 292 } else if (mne >= 0) { 293 mne = mne + 1 + '0'; 294 } 295 296 String text = (mne == 0 ? "" : (char) mne) + " " + f.getName(); 297 JRadioButtonMenuItem b = new JRadioButtonMenuItem(text); 298 b.setName(f.getName()); 299 if (mne != 0) 300 b.setMnemonic(mne); 301 b.getAccessibleContext().setAccessibleDescription(f.getDescription()); 302 b.addActionListener(listener); 303 menuGroup.add(b); 304 305 filterMenuTable.put(f, b); 306 307 if (location < 0) // no preference, insert as last filter 308 editMenu.insert(b, menuGroup.getButtonCount()-1); 309 else 310 editMenu.insert(b, location); 311 312 if (f == activeFilter) 313 b.setEnabled(true); 314 } 315 316 /** 317 * This should never be used on an active filter. 318 * No action is taken if the menu system is not initilized. 319 */ 320 private synchronized void removeFromMenu(TestFilter f) { 321 if (editMenu == null) 322 return; 323 324 int where = filterMenuTable.getKeyIndex(f); 325 if (where != -1) { // found, continue 326 JRadioButtonMenuItem mi = (JRadioButtonMenuItem)(filterMenuTable.getValueAt(where)); 327 editMenu.remove(mi); 328 filterMenuTable.remove(where); 329 menuGroup.remove(mi); 330 } 331 } 332 333 /** 334 * Currently just selects the correct active filter. 335 */ 336 private void updateMenu() { 337 if (editMenu == null) 338 return; 339 340 // select the right item in the menu 341 int where = filterMenuTable.getKeyIndex(activeFilter); 342 if (where != -1) { 343 JRadioButtonMenuItem mi = (JRadioButtonMenuItem)(filterMenuTable.getValueAt(where)); 344 mi.setSelected(true); 345 } 346 347 } 348 349 private FilterConfig filterConfig; 350 private UIFactory uif; 351 352 private TestFilter activeFilter; 353 private JComboBox<TestFilter> selectBox; 354 private JButton configButton; 355 private JMenu editMenu; 356 private ButtonGroup menuGroup; 357 358 private Listener listener; 359 private Observer[] obs = new Observer[0]; 360 private OrderedTwoWayTable filterMenuTable; // filter, menu item 361 private static boolean debug = Debug.getBoolean(FilterConfig.class); 362 363 class Listener implements ActionListener, FilterConfig.Observer { 364 public void actionPerformed(ActionEvent e) { 365 Object source = e.getSource(); 366 if (source == selectBox) { 367 String action = e.getActionCommand(); 368 TestFilter vf = (TestFilter)(selectBox.getSelectedItem()); 369 370 if (vf == getActiveFilter()) { 371 if (debug) 372 Debug.println("FC - keeping filter"); 373 374 return; 375 } 376 else { 377 if (debug) 378 Debug.println("FC - changing filter"); 379 380 setFilter(vf); 381 } 382 } 383 else if (source == configButton) { 384 TestFilter vf = (TestFilter)(selectBox.getSelectedItem()); 385 filterConfig.showEditorDialog(vf); 386 } 387 else if (source instanceof JRadioButtonMenuItem) { 388 // a filter menu item 389 int which = filterMenuTable.getValueIndex(source); 390 if (which != -1) { 391 TestFilter f = (TestFilter)(filterMenuTable.getKeyAt(which)); 392 setFilter(f); 393 } 394 } 395 } 396 397 public void filterUpdated(TestFilter f) { 398 if (obs == null) // this really should not happen 399 return; 400 401 if (activeFilter == f) { 402 for (int i = 0; i < obs.length; i++) 403 obs[i].filterUpdated(f); 404 } 405 } 406 407 public void filterAdded(TestFilter f) { 408 // add to the list box 409 if (selectBox != null) 410 selectBox.addItem(f); 411 412 // update the menu 413 addToMenu(f, -1); 414 415 // tell people about the addition 416 // obs should never be null 417 for (int i = 0; i < obs.length; i++) 418 obs[i].filterAdded(f); 419 } 420 421 /** 422 * Removing the active filter will result in an exception. 423 */ 424 public void filterRemoved(TestFilter f) { 425 // remove from list box 426 if (selectBox != null) 427 selectBox.removeItem(f); 428 429 // update the menu 430 removeFromMenu(f); 431 432 // tell people about the removal 433 // obs should never be null 434 for (int i = 0; i < obs.length; i++) 435 obs[i].filterRemoved(f); 436 } 437 } 438 } 439