Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 376   Methods: 21
NCLOC: 267   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
DetachedFrame.java 22% 34.1% 42.9% 32.1%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.ui;
 38   
 39    import edu.rice.cs.util.swing.SwingFrame;
 40    import edu.rice.cs.plt.lambda.Runnable1;
 41    import edu.rice.cs.util.swing.Utilities;
 42    import edu.rice.cs.drjava.platform.PlatformFactory;
 43    import java.awt.Point;
 44    import java.awt.Dimension;
 45    import java.awt.event.WindowAdapter;
 46    import java.awt.event.WindowEvent;
 47    import java.awt.event.KeyEvent;
 48    import java.awt.event.ActionEvent;
 49    import java.beans.PropertyChangeListener;
 50    import java.beans.PropertyChangeEvent;
 51    import java.util.StringTokenizer;
 52    import java.util.NoSuchElementException;
 53    import java.util.Vector;
 54    import java.util.HashMap;
 55    import java.util.Map;
 56    import javax.swing.*;
 57   
 58    import edu.rice.cs.drjava.config.OptionConstants;
 59    import edu.rice.cs.drjava.config.OptionListener;
 60    import edu.rice.cs.drjava.config.OptionEvent;
 61    import edu.rice.cs.drjava.ui.KeyBindingManager;
 62    import edu.rice.cs.drjava.ui.KeyBindingManager.KeyStrokeData;
 63    import edu.rice.cs.drjava.DrJava;
 64   
 65    public class DetachedFrame extends SwingFrame {
 66    /** Class to save the frame state, i.e. location. */
 67    public static class FrameState {
 68    private Point _loc;
 69    private Dimension _dim;
 70  0 public FrameState(Point l, Dimension d) {
 71  0 _loc = l;
 72  0 _dim = d;
 73    }
 74  76 public FrameState(String s) {
 75  76 StringTokenizer tok = new StringTokenizer(s);
 76  76 try {
 77  76 int x = Integer.valueOf(tok.nextToken());
 78  0 int y = Integer.valueOf(tok.nextToken());
 79  0 int w = Integer.valueOf(tok.nextToken());
 80  0 int h = Integer.valueOf(tok.nextToken());
 81  0 _loc = new Point(x, y);
 82  0 _dim = new Dimension(w, h);
 83    }
 84    catch(NoSuchElementException nsee) {
 85  0 throw new IllegalArgumentException("Wrong FrameState string: " + nsee);
 86    }
 87    catch(NumberFormatException nfe) {
 88  76 throw new IllegalArgumentException("Wrong FrameState string: " + nfe);
 89    }
 90    }
 91  38 public FrameState(DetachedFrame comp) {
 92  38 _loc = comp.getLocation();
 93  38 _dim = comp.getSize();
 94    }
 95  0 public String toString() {
 96  0 final StringBuilder sb = new StringBuilder();
 97  0 sb.append(_loc.x);
 98  0 sb.append(' ');
 99  0 sb.append(_loc.y);
 100  0 sb.append(' ');
 101  0 sb.append(_dim.width);
 102  0 sb.append(' ');
 103  0 sb.append(_dim.height);
 104  0 return sb.toString();
 105    }
 106  0 public Point getLocation() { return _loc; }
 107  0 public Dimension getDimension() { return _dim; }
 108    }
 109   
 110    /** Lambda to execute when component is being detached. The parameter is the instance of DetachedFrame that
 111    * may contain the component. */
 112    Runnable1<DetachedFrame> _detach;
 113    /** Lambda to execute when component is being re-attached. The parameter is the instance of DetachedFrame that
 114    * may contain the component. */
 115    Runnable1<DetachedFrame> _reattach;
 116    /** Last frame state. It can be stored and restored. */
 117    private FrameState _lastState = null;
 118    /** Main frame. */
 119    private MainFrame _mainFrame;
 120    /** Window adapter to re-attach the tabbed pane when the window is closed. */
 121    private WindowAdapter _wa = new WindowAdapter() {
 122  0 public void windowClosing(WindowEvent we) {
 123  0 setDisplayInFrame(false);
 124    }
 125    };
 126    /** Old InputMap without accelerators added. */
 127    private final InputMap _oldInputMap = new InputMap();
 128    /** Listeners that need to be removed when this frame is disposed. Key=Listener, Value=JMenuItem listened to. */
 129    private final HashMap<PropertyChangeListener,JMenuItem> _listenersToRemoveWhenDisposed =
 130    new HashMap<PropertyChangeListener,JMenuItem>();
 131   
 132    /** Returns the last state of the frame, i.e. the location and dimension.
 133    * @return frame state
 134    */
 135  0 public FrameState getFrameState() {
 136  0 if (isVisible()) {
 137  0 return new FrameState(this);
 138    }
 139    else {
 140  0 return _lastState;
 141    }
 142    }
 143   
 144    /** Sets state of the frame, i.e. the location and dimension of the frame for the next use.
 145    * @param ds State to update to, or {@code null} to reset
 146    */
 147  0 public void setFrameState(FrameState ds) {
 148  0 _lastState = ds;
 149  0 if (_lastState != null) {
 150  0 setLocation(_lastState.getLocation());
 151  0 setSize(_lastState.getDimension());
 152  0 validate();
 153    }
 154    }
 155   
 156    /** Sets state of the frame, i.e. the location and dimension of the frame for the next use.
 157    * @param s State to update to, or {@code null} to reset
 158    */
 159  76 public void setFrameState(String s) {
 160  76 try { _lastState = new FrameState(s); }
 161  76 catch(IllegalArgumentException e) { _lastState = null; }
 162  76 if (_lastState != null) {
 163  0 setLocation(_lastState.getLocation());
 164  0 setSize(_lastState.getDimension());
 165    }
 166    else {
 167  76 Utilities.setPopupLoc(this, _mainFrame);
 168  76 setSize(700,400);
 169    }
 170  76 validate();
 171    }
 172   
 173    /** Recursively process the MenuElement and add entries to the InputMap and ActionMap so that the
 174    * menu element's accelerator will invoke the menu element's action even if the MenuElement is
 175    * not present in another frame.
 176    * Note that this will only use the first key stroke configured for an action, because a menu item
 177    * can only have one accelerator key stroke.
 178    */
 179  10336 protected static void processMenuElement(MenuElement elt, InputMap im, ActionMap am) {
 180  10336 if ((elt instanceof JMenuItem) && !(elt instanceof JMenu)) {
 181    // this is a leaf
 182  8132 JMenuItem menuItem = (JMenuItem)elt;
 183  8132 KeyStroke ks = menuItem.getAccelerator();
 184  8132 if (ks != null) {
 185    // it has an accelerator
 186  5320 Action a = menuItem.getAction();
 187  5320 final String ACTION_NAME =
 188    ks.toString()+"-"+System.identityHashCode(ks)+"-"+
 189    a.toString()+"-"+System.identityHashCode(a);
 190  5320 im.put(ks, ACTION_NAME);
 191  5320 am.put(ACTION_NAME, a);
 192    }
 193    }
 194    else {
 195    // interior node or root, process recursively
 196  10260 for(MenuElement subElt: elt.getSubElements()) { processMenuElement(subElt, im, am); }
 197    }
 198    }
 199   
 200    /** Recursively copy the first menu bar's accelerators into the second menu bar.
 201    * Installs listeners to keep the accelerators updated. */
 202  0 protected void copyAccelerators(JMenuBar source, JMenuBar dest) {
 203  0 int sourceIndex = 0;
 204  0 int destIndex = 0;
 205  0 while(sourceIndex<source.getMenuCount() && destIndex<dest.getMenuCount()) {
 206  0 JMenu sourceMenu = source.getMenu(sourceIndex++);
 207  0 while((destIndex<dest.getMenuCount()) &&
 208    (dest.getMenu(destIndex).getText() == null ||
 209    !dest.getMenu(destIndex).getText().equals(sourceMenu.getText()))) {
 210  0 ++destIndex;
 211    }
 212  0 if (destIndex<dest.getMenuCount()) {
 213  0 JMenu destMenu = dest.getMenu(destIndex++);
 214  0 copyAccelerators(sourceMenu, destMenu);
 215    }
 216    }
 217    }
 218   
 219    /** Recursively copy the first menu's accelerators into the second menu.
 220    * Installs listeners to keep the accelerators updated. */
 221  0 protected void copyAccelerators(MenuElement source, MenuElement dest) {
 222  0 if (!(source instanceof JMenu) && !(source instanceof JPopupMenu) &&
 223    !(dest instanceof JMenu) && !(dest instanceof JPopupMenu) &&
 224    (source instanceof JMenuItem) && (dest instanceof JMenuItem)) {
 225    // this is a leaf
 226  0 final JMenuItem sourceItem = (JMenuItem)source;
 227  0 final JMenuItem destItem = (JMenuItem)dest;
 228  0 if ((sourceItem.getText() != null) &&
 229    sourceItem.getText().equals(destItem.getText())) {
 230  0 destItem.setAccelerator(sourceItem.getAccelerator());
 231  0 PropertyChangeListener listener = new PropertyChangeListener() {
 232  0 public void propertyChange(PropertyChangeEvent evt) {
 233  0 if ("accelerator".equals(evt.getPropertyName())) {
 234  0 destItem.setAccelerator(sourceItem.getAccelerator());
 235    }
 236    }
 237    };
 238  0 sourceItem.addPropertyChangeListener(listener);
 239  0 _listenersToRemoveWhenDisposed.put(listener, sourceItem);
 240    }
 241    }
 242    else {
 243  0 MenuElement[] sourceElts = source.getSubElements();
 244  0 MenuElement[] destElts = dest.getSubElements();
 245  0 int sourceIndex = 0;
 246  0 int destIndex = 0;
 247  0 while(sourceIndex<sourceElts.length && destIndex<destElts.length) {
 248  0 MenuElement sourceElement = sourceElts[sourceIndex++];
 249  0 boolean matches = false;
 250  0 do {
 251  0 while((destIndex<destElts.length) &&
 252    !(destElts[destIndex].getClass().equals(sourceElement.getClass()))) {
 253  0 ++destIndex;
 254    }
 255  0 if (destIndex>=destElts.length) { break; }
 256  0 if ((sourceElement instanceof JMenuItem) &&
 257    (destElts[destIndex]instanceof JMenuItem)) {
 258  0 JMenuItem sourceItem = (JMenuItem)sourceElement;
 259  0 JMenuItem destItem = (JMenuItem)destElts[destIndex];
 260  0 if (sourceItem.getText() == null) {
 261  0 matches = (destItem.getText() == null);
 262    }
 263    else {
 264  0 matches = sourceItem.getText().equals(destItem.getText());
 265    }
 266    }
 267    else {
 268  0 matches = true;
 269    }
 270  0 } while(!matches);
 271  0 if (destIndex<destElts.length) {
 272  0 MenuElement destElement = destElts[destIndex++];
 273  0 copyAccelerators(sourceElement, destElement);
 274    }
 275    }
 276    }
 277    }
 278   
 279    /** Create a tabbed pane frame. Initially, the tabbed pane is displayed in the split pane
 280    * @param name frame name
 281    * @param mf the MainFrame
 282    * @param detach command to detach the component. The parameter is the instance of DetachedFrame that may contain the component.
 283    * @param reattach command to re-attach the component. The parameter is the instance of DetachedFrame that may contain the component.
 284    */
 285  76 public DetachedFrame(String name, MainFrame mf, Runnable1<DetachedFrame> detach, Runnable1<DetachedFrame> reattach) {
 286  76 super(name);
 287  76 _mainFrame = mf;
 288  76 _detach = detach;
 289  76 _reattach = reattach;
 290   
 291    // not strictly necessary on Mac, because Mac DetachedFrames have a menu bar
 292  76 final InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 293    // First clone the InputMap so we can change the keystroke mappings
 294  76 if (im.keys()!=null) { // keys() may return null!
 295  0 for(KeyStroke ks: im.keys()) { _oldInputMap.put(ks, im.get(ks)); }
 296    }
 297    // Add listeners to all key bindings
 298  76 for (KeyStrokeData ksd: KeyBindingManager.ONLY.getKeyStrokeData()) {
 299  47100 if (ksd.getOption() != null) {
 300  47100 DrJava.getConfig().addOptionListener(ksd.getOption(), _keyBindingOptionListener);
 301    }
 302    }
 303    // Then update the key bindings
 304  76 updateKeyBindings();
 305   
 306  76 initDone(); // call mandated by SwingFrame contract
 307    }
 308   
 309    /** OptionListener responding to changes for the undo/redo key bindings. */
 310    private final OptionListener<Vector<KeyStroke>> _keyBindingOptionListener = new OptionListener<Vector<KeyStroke>>() {
 311  0 public void optionChanged(OptionEvent<Vector<KeyStroke>> oce) {
 312  0 updateKeyBindings();
 313    }
 314    };
 315   
 316    /** Update the key bindings from the MainFrame menu bar. */
 317  76 public void updateKeyBindings() {
 318    // first restore old InputMap.
 319  76 final InputMap im = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
 320  76 final ActionMap am = getRootPane().getActionMap();
 321   
 322  76 if (im.keys()!=null) { // keys() may return null!
 323  0 for(KeyStroke ks: im.keys()) { im.remove(ks); }
 324    }
 325  76 if (_oldInputMap.keys()!=null) { // keys() may return null!
 326  0 for(KeyStroke ks: _oldInputMap.keys()) { im.put(ks, _oldInputMap.get(ks)); }
 327    }
 328   
 329  76 processMenuElement(_mainFrame.getJMenuBar(), im, am);
 330    }
 331   
 332  0 public void dispose() {
 333    // Mac only
 334  0 if (PlatformFactory.ONLY.isMacPlatform()) {
 335  0 for(Map.Entry<PropertyChangeListener,JMenuItem> e: _listenersToRemoveWhenDisposed.entrySet()) {
 336  0 e.getValue().removePropertyChangeListener(e.getKey());
 337    }
 338   
 339  0 _mainFrame.removeMenuBarInOtherFrame(getJMenuBar());
 340    }
 341  0 super.dispose();
 342    }
 343   
 344  76 public void setUpMenuBar() {
 345    // Mac only
 346  76 if (PlatformFactory.ONLY.isMacPlatform() && (getJMenuBar()==null)) {
 347  0 JMenuBar menuBar = new MainFrame.MenuBar(_mainFrame);
 348  0 _mainFrame._setUpMenuBar(menuBar);
 349  0 _mainFrame.addMenuBarInOtherFrame(menuBar);
 350  0 copyAccelerators(_mainFrame.getJMenuBar(), menuBar);
 351  0 setJMenuBar(menuBar); // it's not that easy to reproduce the MainFrame's menu bar
 352    }
 353    }
 354   
 355    /** Toggle visibility of this frame. Warning, it behaves like a modal dialog. */
 356  38 public void setVisible(boolean vis) {
 357  38 super.setVisible(vis);
 358  38 _lastState = new FrameState(this);
 359    }
 360   
 361    /** Set whether the the tabbed pane is displayed in this frame.
 362    * @param b true to display the tabbed pane in this window, false to display it in the MainFrame split pane */
 363  38 public void setDisplayInFrame(boolean b) {
 364  38 if (b) {
 365  0 _detach.run(this);
 366  0 setVisible(true);
 367  0 addWindowListener(_wa);
 368    }
 369    else {
 370  38 removeWindowListener(_wa);
 371  38 setVisible(false);
 372  38 getContentPane().removeAll();
 373  38 _reattach.run(this);
 374    }
 375    }
 376    }