1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2008, 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 com.sun.javatest.tool.StringFitter;
  30 import com.sun.javatest.tool.ToolAction;
  31 import com.sun.javatest.tool.UIFactory;
  32 import java.awt.Component;
  33 import java.awt.Container;
  34 import java.awt.GridBagConstraints;
  35 import java.awt.GridBagLayout;
  36 import java.awt.event.ActionEvent;
  37 import java.awt.event.ItemEvent;
  38 import java.awt.event.ItemListener;
  39 import java.awt.Font;
  40 import java.io.File;
  41 import java.net.URL;
  42 import java.util.Vector;
  43 import javax.swing.Action;
  44 import javax.swing.BorderFactory;
  45 import javax.swing.DefaultComboBoxModel;
  46 import javax.swing.DefaultListCellRenderer;
  47 import javax.swing.JButton;
  48 import javax.swing.JComboBox;
  49 import javax.swing.JLabel;
  50 import javax.swing.JList;
  51 import javax.swing.JPanel;
  52 import javax.swing.JScrollPane;
  53 import javax.swing.JToolBar;
  54 import javax.swing.ScrollPaneConstants;
  55 import javax.swing.plaf.basic.BasicComboBoxUI;
  56 import javax.swing.plaf.basic.BasicComboPopup;
  57 import javax.swing.plaf.basic.ComboPopup;
  58 
  59 public class NavigationPane extends JPanel {
  60 
  61     public NavigationPane(UIFactory uif, MultiFormatPane mediaPane)
  62                                                throws IllegalArgumentException {
  63 
  64         if(mediaPane == null) {
  65             throw new IllegalArgumentException("Corresponding media pane should exist");
  66         }
  67         this.mediaPane = mediaPane;
  68 
  69         this.uif = uif;
  70 
  71         history = new History();
  72 
  73         initActions();
  74 
  75         initGUI();
  76 
  77         mediaPane.setNavigationPane(this);
  78     }
  79 
  80     public void setHomeURL(URL url) {
  81         homeURL = url;
  82         homeAction.setEnabled(true);
  83     }
  84 
  85     public URL getHomeURL() {
  86         return homeURL;
  87     }
  88 
  89     public void setURLs(URL[] urls) {
  90         if(model != null) {
  91             model.removeAllElements();
  92 
  93             homeURL = urls[0];
  94             homeAction.setEnabled(homeURL != null);
  95 
  96             for(URL url : urls) {
  97                 model.addElement(url);
  98             }
  99             model.setSelectedItem(homeURL);
 100         }
 101     }
 102     // When click link on corresponding media pane
 103     public void processURL(URL url) {
 104         history.add(url);
 105         backAction.setEnabled(history.hasPrev());
 106         forwardAction.setEnabled(history.hasNext());
 107         updateCombo(url);
 108     }
 109 
 110     public void clear() {
 111         if (model != null)
 112             model.removeAllElements();
 113 
 114         history.clear();
 115         backAction.setEnabled(false);
 116         forwardAction.setEnabled(false);
 117         homeAction.setEnabled(false);
 118 
 119         homeURL = null;
 120     }
 121 
 122     private void initGUI() {
 123         uif.initPanel(this, "np", false);
 124         setLayout(new GridBagLayout());
 125         setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
 126 
 127         GridBagConstraints c = new GridBagConstraints();
 128         c.anchor = GridBagConstraints.EAST;
 129         c.gridx = 0;
 130         c.gridy = 0;
 131 
 132         JLabel fileLbl = uif.createLabel("np.file", true);
 133         fileLbl.setFont(new Font(Font.MONOSPACED, uif.getBaseFont().getStyle(), uif.getBaseFont().getSize()));
 134         add(fileLbl, c);
 135 
 136         selectBox = uif.createChoice("np.choice", fileLbl);
 137         selectBox.setFont(new Font(Font.MONOSPACED, uif.getBaseFont().getStyle(), uif.getBaseFont().getSize()));
 138         selectBox.setRenderer(new Renderer(selectBox));
 139         selectBox.setModel(createModel());
 140         selectBox.addItemListener(listener);
 141         selectBox.setMaximumRowCount(MAX_ROWS_DISPLAY);
 142         selectBox.setUI(new BasicComboBoxUI() {
 143             // wrap the content with a scrolling interface
 144             // would be nice if Swing did this for us
 145             protected ComboPopup createPopup() {
 146                 BasicComboPopup popup = new BasicComboPopup(selectBox) {
 147                     protected JScrollPane createScroller() {
 148                         return new JScrollPane(list,
 149                             ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
 150                             ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
 151                     }
 152                 };
 153                 return popup;
 154             }
 155             }   // class
 156             );
 157         uif.setAccessibleName(selectBox, "np.choice");  // override default a11y name
 158 
 159         c.gridx = 1;
 160         c.weightx = 2.0;
 161         c.fill = GridBagConstraints.HORIZONTAL;
 162 
 163         add(selectBox, c);
 164 
 165         Action[] actions = { backAction, forwardAction, null, homeAction };
 166 
 167         toolBar = uif.createToolBar("np.toolbar", actions );
 168         toolBar.setFloatable(false);
 169 
 170         c.weightx = 0;
 171         c.gridx = 2;
 172         c.insets.left = 5;
 173 
 174         add(toolBar, c);
 175 
 176     }
 177 
 178     private void initActions() {
 179         homeAction = new ToolAction(uif, "np.home", true) {
 180             public void actionPerformed(ActionEvent e) {
 181                 if (homeURL == null) {
 182                     mediaPane.setDefaultView();
 183                 }
 184                 else{
 185                     mediaPane.stopAudio();
 186                     mediaPane.loadPage(homeURL);
 187                 }
 188             }
 189         };
 190 
 191         backAction = new ToolAction(uif, "np.back", true) {
 192             public void actionPerformed(ActionEvent e) {
 193                 URL url = history.prev();
 194                 if (url != null) {
 195                     mediaPane.stopAudio();
 196                     mediaPane.loadPage(url);
 197                 }
 198             }
 199         };
 200 
 201         forwardAction = new ToolAction(uif, "np.forward", true) {
 202             public void actionPerformed(ActionEvent e) {
 203                 URL url = history.next();
 204                 if (url != null) {
 205                     mediaPane.stopAudio();
 206                     mediaPane.loadPage(url);
 207                 }
 208             }
 209         };
 210     }
 211 
 212 
 213     private DefaultComboBoxModel<URL> createModel() {
 214         if (model == null)
 215             model = new DefaultComboBoxModel<>();
 216         return model;
 217     }
 218 
 219     private void updateCombo(URL s) {
 220         // check if the new element exists in the combo box...
 221         if (model.getIndexOf(s) < 0)
 222             model.addElement(s);
 223 
 224         URL item = (URL) selectBox.getSelectedItem();
 225         // check if the new element is already selected.
 226         // URL.equals can result in a big performance hit
 227         if (s != null && !item.toString().equals(s.toString()))
 228             selectBox.setSelectedItem(s);
 229     }
 230 
 231     private class Listener implements ItemListener {
 232         public void itemStateChanged(ItemEvent e) {
 233             if (e.getStateChange() == ItemEvent.SELECTED) {
 234                 mediaPane.stopAudio();
 235                 URL url = (URL) e.getItem();
 236                 mediaPane.loadPage(url);
 237             }
 238         }
 239     }
 240 
 241 
 242 // History
 243     private static class History {
 244         boolean hasPrev() {
 245             return (index > 0);
 246         }
 247 
 248         URL prev() {
 249             if (index == 0)
 250                 return null;
 251 
 252             return entries.elementAt(--index);
 253         }
 254 
 255         boolean hasNext() {
 256             return (index < entries.size() - 1);
 257         }
 258 
 259         URL next() {
 260             if (index == entries.size() - 1)
 261                 return null;
 262 
 263             return entries.elementAt(++index);
 264         }
 265 
 266         void add(URL u) {
 267             if (u == null)
 268                 throw new NullPointerException();
 269 
 270             // if there is a current entry, and it matches the one to be added, we're done
 271             if (index >= 0 && index < entries.size() && entries.elementAt(index).equals(u))
 272                 return;
 273 
 274             // if current entry not the last one, truncate to the current entry
 275             if (index < entries.size() - 1)
 276                 entries.setSize(index + 1);
 277 
 278             // finally, add new entry
 279             entries.addElement(u);
 280             index = entries.size() - 1;
 281         }
 282 
 283         void clear() {
 284             entries.setSize(0);
 285             index = -1;
 286         }
 287 
 288         private Vector<URL> entries = new Vector<>();
 289         private int index;
 290     }
 291 
 292 
 293 
 294 
 295     private class Renderer extends DefaultListCellRenderer {
 296 
 297         public Renderer() {
 298             setPreferredSize(new JLabel("MinSize").getPreferredSize());
 299         }
 300 
 301         public Renderer(Container container) {
 302             setPreferredSize(new JLabel("MinSize").getPreferredSize());
 303             this.container = container;
 304             sf = new StringFitter(getFontMetrics(getFont()));
 305         }
 306 
 307         public Component getListCellRendererComponent(JList<?> list, Object o, int index, boolean isSelected, boolean cellHasFocus) {
 308             String name = null;
 309             if (o instanceof URL) {
 310                 URL url = (URL) o;
 311 
 312                 // if not file URL
 313                 if (!url.getProtocol().equals("file")) {
 314                     name = url.toString();
 315                 }
 316                 else {
 317                     // if file URL, remove the "file:" prefix
 318                     name = extractPrefix(url.toString(), "file:");
 319                     String baseName = null;
 320                     name = new File(name).getAbsolutePath();
 321 
 322                     File baseDir = mediaPane.getBaseDirectory();
 323                     if (baseDir != null && baseDir.getParentFile() != null) {
 324                         baseName = baseDir.getParentFile().getAbsolutePath();
 325                     }
 326                     // if contains base dir, only show file name
 327                     if (baseName != null &&
 328                         name.startsWith(baseName) &&
 329                         (name.length() > baseName.length())) {
 330                         name = name.substring(baseName.length() );
 331                         // in case of Unix
 332                         if (name.startsWith(File.separator)) {
 333                             name = name.substring(1);
 334                         }
 335                     }
 336                 }
 337             }
 338             else
 339                 name = String.valueOf(o);
 340 
 341         JLabel cell = (JLabel) super.getListCellRendererComponent(list, name, index, isSelected, cellHasFocus);
 342         if(container != null)
 343             cell.setText(sf.truncateBeginning(name, container.getWidth() - 17));
 344             cell.setToolTipText(name);
 345             return cell;
 346         }
 347 
 348         private String extractPrefix(String origStr, String target) {
 349             return (!origStr.startsWith(target)) ? origStr : origStr.substring(target.length());
 350         }
 351 
 352     private Container container;
 353     private StringFitter sf;
 354     }
 355 
 356 
 357     private UIFactory uif;
 358     private String uiKey;
 359 
 360     private History history;
 361 
 362     private Action homeAction;
 363     private Action backAction;
 364     private Action forwardAction;
 365 
 366     private JButton homeBtn;
 367     private JButton backBtn;
 368     private JButton forwardBtn;
 369 
 370     private JComboBox<URL> selectBox;
 371 
 372     private DefaultComboBoxModel<URL> model;
 373     private Listener listener = new Listener();
 374     private JToolBar toolBar;
 375 
 376     private static final int MAX_ROWS_DISPLAY = 20;
 377 
 378     private MultiFormatPane mediaPane;
 379 
 380     private URL homeURL;
 381 
 382 
 383 }