Coverage Report - org.argouml.ui.ZoomSliderButton
 
Classes in this File Line Coverage Branch Coverage Complexity
ZoomSliderButton
82%
66/80
40%
8/20
2.118
ZoomSliderButton$1
100%
4/4
50%
1/2
2.118
ZoomSliderButton$2
100%
3/3
N/A
2.118
ZoomSliderButton$3
33%
1/3
N/A
2.118
ZoomSliderButton$4
33%
1/3
N/A
2.118
ZoomSliderButton$MyMouseListener
8%
1/12
0%
0/8
2.118
ZoomSliderButton$MyPopupMenuListener
11%
1/9
0%
0/2
2.118
 
 1  
 /* $Id: ZoomSliderButton.java 17841 2010-01-12 19:17:52Z linus $
 2  
  *****************************************************************************
 3  
  * Copyright (c) 2009 Contributors - see below
 4  
  * All rights reserved. This program and the accompanying materials
 5  
  * are made available under the terms of the Eclipse Public License v1.0
 6  
  * which accompanies this distribution, and is available at
 7  
  * http://www.eclipse.org/legal/epl-v10.html
 8  
  *
 9  
  * Contributors:
 10  
  *    tfmorris
 11  
  *****************************************************************************
 12  
  *
 13  
  * Some portions of this file was previously release using the BSD License:
 14  
  */
 15  
 
 16  
 // Copyright (c) 2003-2008 The Regents of the University of California. All
 17  
 // Rights Reserved. Permission to use, copy, modify, and distribute this
 18  
 // software and its documentation without fee, and without a written
 19  
 // agreement is hereby granted, provided that the above copyright notice
 20  
 // and this paragraph appear in all copies.  This software program and
 21  
 // documentation are copyrighted by The Regents of the University of
 22  
 // California. The software program and documentation are supplied "AS
 23  
 // IS", without any accompanying services from The Regents. The Regents
 24  
 // does not warrant that the operation of the program will be
 25  
 // uninterrupted or error-free. The end-user understands that the program
 26  
 // was developed for research purposes and is advised not to rely
 27  
 // exclusively on the program for any reason.  IN NO EVENT SHALL THE
 28  
 // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
 29  
 // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
 30  
 // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 31  
 // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 32  
 // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
 33  
 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 34  
 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 35  
 // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 36  
 // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
 37  
 // UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 38  
 
 39  
 package org.argouml.ui;
 40  
 
 41  
 import java.awt.BorderLayout;
 42  
 import java.awt.Component;
 43  
 import java.awt.Dimension;
 44  
 import java.awt.FlowLayout;
 45  
 import java.awt.Font;
 46  
 import java.awt.event.ActionEvent;
 47  
 import java.awt.event.ActionListener;
 48  
 import java.awt.event.FocusAdapter;
 49  
 import java.awt.event.FocusEvent;
 50  
 import java.awt.event.MouseEvent;
 51  
 import java.util.Enumeration;
 52  
 
 53  
 import javax.swing.AbstractAction;
 54  
 import javax.swing.Icon;
 55  
 import javax.swing.JLabel;
 56  
 import javax.swing.JPanel;
 57  
 import javax.swing.JPopupMenu;
 58  
 import javax.swing.JSlider;
 59  
 import javax.swing.JTextField;
 60  
 import javax.swing.event.ChangeEvent;
 61  
 import javax.swing.event.ChangeListener;
 62  
 import javax.swing.event.MouseInputAdapter;
 63  
 import javax.swing.event.PopupMenuEvent;
 64  
 import javax.swing.event.PopupMenuListener;
 65  
 
 66  
 import org.argouml.application.helpers.ResourceLoaderWrapper;
 67  
 import org.argouml.i18n.Translator;
 68  
 import org.tigris.swidgets.PopupButton;
 69  
 import org.tigris.gef.base.Editor;
 70  
 import org.tigris.gef.base.Globals;
 71  
 
 72  
 /**
 73  
  * A button that can be used to change the zoom magnification of the current
 74  
  * diagram. When the user presses the button, a popup is displayed which
 75  
  * contains a vertical slider representing the range of zoom magnifications.
 76  
  * Dragging the slider changes the zoom magnification for the current diagram.
 77  
  *
 78  
  * @author Jeremy Jones
 79  
  */
 80  19
 public class ZoomSliderButton extends PopupButton {
 81  
 
 82  
     /**
 83  
      * Used for loading the zoom icon from the Zoom Reset action.
 84  
      */
 85  
     private static final String RESOURCE_NAME = "Zoom Reset";
 86  
 
 87  
     /**
 88  
      * Font used for the slider tick labels and for the current magnification
 89  
      * value label.
 90  
      */
 91  900
     private static final Font LABEL_FONT = new Font("Dialog", Font.PLAIN, 10);
 92  
 
 93  
     /**
 94  
      * The minimum zoom slider value (as percentage).
 95  
      */
 96  
     public static final int MINIMUM_ZOOM = 25;
 97  
 
 98  
     /**
 99  
      * The maximum zoom slider value (as percentage).
 100  
      */
 101  
     public static final int MAXIMUM_ZOOM = 300;
 102  
 
 103  
     /**
 104  
      * The preferred height of the slider component.
 105  
      */
 106  
     private static final int SLIDER_HEIGHT = 250;
 107  
 
 108  
     /**
 109  
      * The slider component.
 110  
      */
 111  900
     private JSlider slider = null;
 112  
 
 113  
     /**
 114  
      * The text field which shows the current zoom magnification value.
 115  
      */
 116  900
     private JTextField currentValue = null;
 117  
 
 118  
     /**
 119  
      * Used to enable/disable the popup button.
 120  
      */
 121  900
     private boolean popupButtonIsActive = true;
 122  
 
 123  
     /**
 124  
      * Indicates whether the popupmenu is showing or not.
 125  
      */
 126  900
     private boolean popupMenuIsShowing = false;
 127  
 
 128  
     /**
 129  
      * Indicates whether the mouse is over the popup button or not.
 130  
      */
 131  900
     private boolean mouseIsOverPopupButton = false;
 132  
 
 133  
     /**
 134  
      * Constructs a new ZoomSliderButton.
 135  
      */
 136  
     public ZoomSliderButton() {
 137  900
         super();
 138  900
         setAction(new AbstractAction() {
 139  
             public void actionPerformed(ActionEvent e) {
 140  
                 /* If action comes in with *no* modifiers, it is a pure
 141  
                  * keyboard event (e.g. spacebar), so do it.  Anything else
 142  
                  * is probably a mouse event, so ignore it. Mouse events are
 143  
                  * dealt with by mousePressed() instead (see bottom of page).
 144  
                  */ 
 145  19
                 if (e.getModifiers() == 0) {
 146  19
                     showPopup();
 147  
                 }
 148  19
             }
 149  
         });
 150  
 
 151  900
         Icon icon = ResourceLoaderWrapper.lookupIcon(RESOURCE_NAME);
 152  
 
 153  900
         MyMouseListener myMouseListener = new MyMouseListener();
 154  900
         addMouseMotionListener(myMouseListener);
 155  900
         addMouseListener(myMouseListener);
 156  
 
 157  900
         setIcon(icon);
 158  900
         setToolTipText(Translator.localize("button.zoom"));
 159  900
     }
 160  
 
 161  
     /**
 162  
      * Creates the slider popup component.
 163  
      */
 164  
     private void createPopupComponent() {
 165  19
         slider =
 166  
             new JSlider(
 167  
                     JSlider.VERTICAL,
 168  
                     MINIMUM_ZOOM,
 169  
                     MAXIMUM_ZOOM,
 170  
                     MINIMUM_ZOOM);
 171  19
         slider.setInverted(true);
 172  19
         slider.setMajorTickSpacing(25);
 173  19
         slider.setMinorTickSpacing(5);
 174  19
         slider.setPaintTicks(true);
 175  19
         slider.setPaintTrack(true);
 176  19
         int sliderBaseWidth = slider.getPreferredSize().width;
 177  19
         slider.setPaintLabels(true);
 178  
 
 179  19
         for (Enumeration components = slider.getLabelTable().elements();
 180  247
              components.hasMoreElements();) {
 181  228
             ((Component) components.nextElement()).setFont(LABEL_FONT);
 182  
         }
 183  
 
 184  19
         slider.setToolTipText(Translator.localize(
 185  
                 "button.zoom.slider-tooltip"));
 186  
 
 187  19
         slider.addChangeListener(new ChangeListener() {
 188  
             public void stateChanged(ChangeEvent e) {
 189  19
                 handleSliderValueChange();
 190  19
             }
 191  
         });
 192  
 
 193  19
         int labelWidth =
 194  
             slider.getFontMetrics(LABEL_FONT).stringWidth(
 195  
                     String.valueOf(MAXIMUM_ZOOM)) + 6;
 196  
 
 197  19
         slider.setPreferredSize(new Dimension(
 198  
             sliderBaseWidth + labelWidth, SLIDER_HEIGHT));
 199  
 
 200  19
         currentValue = new JTextField(5);
 201  19
         currentValue.setHorizontalAlignment(JLabel.CENTER);
 202  19
         currentValue.setFont(LABEL_FONT);
 203  19
         currentValue.setToolTipText(Translator.localize(
 204  
             "button.zoom.current-zoom-magnification"));
 205  19
         updateCurrentValueLabel();
 206  19
         currentValue.addActionListener(new ActionListener() {
 207  
             public void actionPerformed(ActionEvent e) {
 208  0
                 handleTextEntry();
 209  0
             }
 210  
         });
 211  19
         currentValue.addFocusListener(new FocusAdapter() {
 212  
             @Override
 213  
             public void focusLost(FocusEvent e) {
 214  0
                 handleTextEntry();
 215  0
             }
 216  
         });
 217  
 
 218  19
         JPanel currentValuePanel =
 219  
             new JPanel(new FlowLayout(
 220  
                     FlowLayout.CENTER, 0, 0));
 221  19
         currentValuePanel.add(currentValue);
 222  
 
 223  19
         JPanel zoomPanel = new JPanel(new BorderLayout(0, 0));
 224  19
         zoomPanel.add(slider, BorderLayout.CENTER);
 225  19
         zoomPanel.add(currentValuePanel, BorderLayout.NORTH);
 226  
 
 227  19
         setPopupComponent(zoomPanel);
 228  19
     }
 229  
 
 230  
     /**
 231  
      * Update the slider value every time the popup is shown.
 232  
      */
 233  
     @Override
 234  
     protected void showPopup() {
 235  19
         if (slider == null) {
 236  19
             createPopupComponent();
 237  
         }
 238  
 
 239  19
         Editor ed = Globals.curEditor();
 240  19
         if (ed != null) {
 241  19
             slider.setValue((int) (ed.getScale() * 100d));
 242  
         }
 243  
 
 244  19
         if ( popupButtonIsActive ) {
 245  19
             super.showPopup();
 246  19
             JPopupMenu pm = (JPopupMenu) this.getPopupComponent().getParent();
 247  19
             PopupMenuListener pml = new MyPopupMenuListener();
 248  19
             pm.addPopupMenuListener(pml);
 249  19
             popupMenuIsShowing = true;
 250  
         }
 251  19
         slider.requestFocus();
 252  19
     }
 253  
 
 254  
     /**
 255  
      * Called when the slider value changes.
 256  
      */
 257  
     private void handleSliderValueChange() {
 258  19
         updateCurrentValueLabel();
 259  
 
 260  
         //if (!source.getValueIsAdjusting()) {
 261  19
         double zoomPercentage = slider.getValue() / 100d;
 262  
 
 263  19
         Editor ed = Globals.curEditor();
 264  19
         if (ed == null || zoomPercentage <= 0.0) {
 265  0
             return;
 266  
         }
 267  
 
 268  19
         if (zoomPercentage != ed.getScale()) {
 269  0
             ed.setScale(zoomPercentage);
 270  0
             ed.damageAll();
 271  
         }
 272  
         //}
 273  19
     }
 274  
 
 275  
     /**
 276  
      * Called when the text field value changes.
 277  
      */
 278  
     private void handleTextEntry() {
 279  0
         String value = currentValue.getText();
 280  0
         if (value.endsWith("%")) {
 281  0
             value = value.substring(0, value.length() - 1);
 282  
         }
 283  
         try {
 284  0
             int newZoom = Integer.parseInt(value);
 285  0
             if (newZoom < MINIMUM_ZOOM || newZoom > MAXIMUM_ZOOM) {
 286  0
                 throw new NumberFormatException();
 287  
             }
 288  0
             slider.setValue(newZoom);
 289  0
         } catch (NumberFormatException ex) {
 290  0
             updateCurrentValueLabel();
 291  0
         }
 292  0
     }
 293  
 
 294  
     /**
 295  
      * Sets the current value label's text to the current slider value.
 296  
      */
 297  
     private void updateCurrentValueLabel() {
 298  38
         currentValue.setText(String.valueOf(slider.getValue()) + '%');
 299  38
     }
 300  
     
 301  38
     private class MyPopupMenuListener extends AbstractAction implements
 302  
         PopupMenuListener {
 303  
 
 304  
         /**
 305  
          * Empty method to satisfy interface.
 306  
          */
 307  
         public void actionPerformed(ActionEvent e) {
 308  0
         }
 309  
 
 310  
         /**
 311  
          * Method gets fired when the popup dies.
 312  
          * Conditionally re-enable the button depending on where the pointer 
 313  
          * is.
 314  
          */
 315  
         public void popupMenuCanceled(PopupMenuEvent e) {
 316  0
             if (mouseIsOverPopupButton) {
 317  0
                 popupButtonIsActive = false;
 318  
             } else {
 319  0
                 popupButtonIsActive = true;
 320  
             }
 321  0
             popupMenuIsShowing = false;
 322  0
         }
 323  
 
 324  
         public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
 325  0
         }
 326  
 
 327  
         public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
 328  0
         }
 329  
 
 330  
     }
 331  
 
 332  1800
     private class MyMouseListener extends MouseInputAdapter {
 333  
 
 334  
         /**
 335  
          * Keeps track of mouseover status. 
 336  
          */
 337  
         @Override
 338  
         public void mouseEntered(MouseEvent me) {
 339  0
             mouseIsOverPopupButton = true;
 340  0
         }
 341  
         
 342  
         /**
 343  
          * Keeps track of mouseover status, and renables button if necessary.
 344  
          */
 345  
         @Override
 346  
         public void mouseExited(MouseEvent me) {
 347  0
             mouseIsOverPopupButton = false;
 348  0
             if (!popupButtonIsActive && !popupMenuIsShowing)
 349  
             {
 350  0
                 popupButtonIsActive = true;
 351  
             }
 352  0
         }
 353  
 
 354  
         /**
 355  
          * Catch the down stroke of mouse click to make the popup appear a tiny
 356  
          * bit earlier.
 357  
          */
 358  
         @Override
 359  
         public void mousePressed(MouseEvent me) {
 360  0
             if (popupButtonIsActive) {
 361  0
                 showPopup();
 362  
             }
 363  0
             else if ( !popupMenuIsShowing )
 364  
             {
 365  0
                 popupButtonIsActive = true;
 366  
             }
 367  0
         }
 368  
     }
 369  
 }