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