View Javadoc

1   package com.eviware.soapui.support.propertyexpansion.scrollmenu;
2   
3   import java.awt.Component;
4   import java.awt.Dimension;
5   import java.awt.event.ActionEvent;
6   import java.awt.event.ActionListener;
7   import java.awt.event.MouseAdapter;
8   import java.awt.event.MouseEvent;
9   import java.awt.event.MouseListener;
10  import java.util.Vector;
11  
12  import javax.swing.Action;
13  import javax.swing.JButton;
14  import javax.swing.JComponent;
15  import javax.swing.JMenu;
16  import javax.swing.JMenuItem;
17  import javax.swing.JPopupMenu;
18  import javax.swing.JSeparator;
19  import javax.swing.MenuElement;
20  import javax.swing.MenuSelectionManager;
21  import javax.swing.SwingConstants;
22  import javax.swing.Timer;
23  
24  import com.eviware.soapui.support.UISupport;
25  
26  /***
27   * JMenu with the scrolling feature.
28   */
29  public class ScrollableMenu extends JMenu implements ScrollableMenuContainer
30  {
31  	/*** How fast the scrolling will happen. */
32  	private int scrollSpeed = 10;
33  	/*** Handles the scrolling upwards. */
34  	private Timer timerUp;
35  	/*** Handles the scrolling downwards. */
36  	private Timer timerDown;
37  	/*** How many items are visible. */
38  	private int visibleItems;
39  	/***
40  	 * Menuitem's index which is used to control if up and downbutton are visible
41  	 * or not.
42  	 */
43  	private int indexVisible = 0;
44  	/*** Button to scroll menu upwards. */
45  	private JButton upButton;
46  	/*** Button to scroll menu downwards. */
47  	private JButton downButton;
48  	/*** Container to hold submenus. */
49  	private Vector<JMenuItem> subMenus = new Vector<JMenuItem>();
50  	/*** Height of the screen. */
51  	private double screenHeight;
52  	/*** Height of the menu. */
53  	private double menuHeight;
54  	// private JMenuItem header;
55  
56  	private int headerCount;
57  	private int footerCount;
58  
59  	// private JSeparator headerSeparator;
60  
61  	/***
62  	 * Creates a new ScrollableMenu object with a given name.
63  	 * <p>
64  	 * This also instantiates the timers and buttons. After the buttons are
65  	 * created they are set invisible.
66  	 * 
67  	 * @param name
68  	 *           name to be displayed on the JMenu
69  	 */
70  	public ScrollableMenu( String name )
71  	{
72  		super( name );
73  
74  		timerUp = new Timer( scrollSpeed, new ActionListener()
75  		{
76  			public void actionPerformed( ActionEvent evt )
77  			{
78  				scrollUp();
79  			}
80  		} );
81  		timerDown = new Timer( scrollSpeed, new ActionListener()
82  		{
83  			public void actionPerformed( ActionEvent evt )
84  			{
85  				scrollDown();
86  			}
87  		} );
88  
89  		screenHeight = 400;
90  		createButtons();
91  		hideButtons();
92  	}
93  
94  	/*
95  	 * ScrollableMenuContainer#add(javax.swing.JMenuItem)
96  	 */
97  	public JMenuItem add( JMenuItem menuItem )
98  	{
99  		add( menuItem, subMenus.size() + headerCount + 1 + ( headerCount == 0 ? 0 : 1 ) );
100 		subMenus.add( menuItem );
101 
102 		menuHeight += menuItem.getPreferredSize().getHeight();
103 
104 		if( menuHeight > screenHeight )
105 		{
106 			menuItem.setVisible( false );
107 			downButton.setVisible( true );
108 		}
109 		else
110 		{
111 			visibleItems++ ;
112 		}
113 
114 		return menuItem;
115 	}
116 
117 	@Override
118 	public int getMenuComponentCount()
119 	{
120 		int result = super.getMenuComponentCount() - 2;
121 		if( headerCount > 0 )
122 			result-- ;
123 
124 		if( footerCount > 0 )
125 			result-- ;
126 
127 		return result;
128 	}
129 
130 	public Component add( Component comp )
131 	{
132 		if( comp instanceof JMenuItem )
133 			return add( ( JMenuItem )comp );
134 		else
135 			return super.add( comp );
136 	}
137 
138 	/***
139 	 * Closes the opened submenus when scrolling starts
140 	 */
141 	private void closeOpenedSubMenus()
142 	{
143 		MenuSelectionManager manager = MenuSelectionManager.defaultManager();
144 		MenuElement[] path = manager.getSelectedPath();
145 		int i = 0;
146 		JPopupMenu popup = getPopupMenu();
147 
148 		for( ; i < path.length; i++ )
149 		{
150 			if( path[i] == popup )
151 			{
152 				break;
153 			}
154 		}
155 
156 		MenuElement[] subPath = new MenuElement[i + 1];
157 
158 		try
159 		{
160 			System.arraycopy( path, 0, subPath, 0, i + 1 );
161 			manager.setSelectedPath( subPath );
162 		}
163 		catch( Exception ekasd )
164 		{
165 		}
166 	}
167 
168 	/***
169 	 * When timerUp is started it calls constantly this method to make the JMenu
170 	 * scroll upwards. When the top of menu is reached then upButton is set
171 	 * invisible. When scrollUp starts downButton is setVisible.
172 	 */
173 	private void scrollUp()
174 	{
175 		closeOpenedSubMenus();
176 
177 		if( indexVisible == 0 )
178 		{
179 			upButton.setVisible( false );
180 
181 			return;
182 		}
183 		else
184 		{
185 			indexVisible-- ;
186 			( ( JComponent )subMenus.get( indexVisible + visibleItems ) ).setVisible( false );
187 			( ( JComponent )subMenus.get( indexVisible ) ).setVisible( true );
188 			downButton.setVisible( true );
189 			if( indexVisible == 0 )
190 			{
191 				upButton.setVisible( false );
192 			}
193 		}
194 	}
195 
196 	/***
197 	 * When timerDown is started it calls constantly this method to make the
198 	 * JMenu scroll downwards. When the bottom of menu is reached then downButton
199 	 * is set invisible. When scrolldown starts upButton is setVisible.
200 	 */
201 	private void scrollDown()
202 	{
203 		closeOpenedSubMenus();
204 
205 		if( ( indexVisible + visibleItems ) == subMenus.size() )
206 		{
207 			downButton.setVisible( false );
208 
209 			return;
210 		}
211 		else if( ( indexVisible + visibleItems ) > subMenus.size() )
212 		{
213 			return;
214 		}
215 		else
216 		{
217 			try
218 			{
219 				( ( JComponent )subMenus.get( indexVisible ) ).setVisible( false );
220 				( ( JComponent )subMenus.get( indexVisible + visibleItems ) ).setVisible( true );
221 				upButton.setVisible( true );
222 				indexVisible++ ;
223 				if( ( indexVisible + visibleItems ) == subMenus.size() )
224 				{
225 					downButton.setVisible( false );
226 				}
227 			}
228 			catch( Exception eks )
229 			{
230 				eks.printStackTrace();
231 			}
232 		}
233 	}
234 
235 	/***
236 	 * Creates two button: upButton and downButton.
237 	 */
238 	private void createButtons()
239 	{
240 		setHorizontalAlignment( SwingConstants.CENTER );
241 		upButton = new JButton( UISupport.createImageIcon( "/up_arrow.gif" ) );
242 
243 		Dimension d = new Dimension( 100, 20 );
244 		upButton.setPreferredSize( d );
245 		upButton.setBorderPainted( false );
246 		upButton.setFocusPainted( false );
247 		upButton.setRolloverEnabled( true );
248 
249 		class Up extends MouseAdapter
250 		{
251 			/***
252 			 * When mouse enters over the upbutton, timerUp starts the scrolling
253 			 * upwards.
254 			 * 
255 			 * @param e
256 			 *           MouseEvent
257 			 */
258 			public void mouseEntered( MouseEvent e )
259 			{
260 				try
261 				{
262 					timerUp.start();
263 				}
264 				catch( Exception ekas )
265 				{
266 				}
267 			}
268 
269 			/***
270 			 * When mouse exites the upbutton, timerUp stops.
271 			 * 
272 			 * @param e
273 			 *           MouseEvent
274 			 */
275 			public void mouseExited( MouseEvent e )
276 			{
277 				try
278 				{
279 					timerUp.stop();
280 				}
281 				catch( Exception ekas )
282 				{
283 				}
284 			}
285 		}
286 
287 		MouseListener scrollUpListener = new Up();
288 		upButton.addMouseListener( scrollUpListener );
289 
290 		add( upButton );
291 		downButton = new JButton( UISupport.createImageIcon( "/down_arrow.gif" ) );
292 		downButton.setPreferredSize( d );
293 		downButton.setBorderPainted( false );
294 		downButton.setFocusPainted( false );
295 
296 		class Down extends MouseAdapter
297 		{
298 			/***
299 			 * When mouse enters over the downbutton, timerDown starts the
300 			 * scrolling downwards.
301 			 * 
302 			 * @param e
303 			 *           MouseEvent
304 			 */
305 			public void mouseEntered( MouseEvent e )
306 			{
307 				try
308 				{
309 					timerDown.start();
310 				}
311 				catch( Exception ekas )
312 				{
313 				}
314 			}
315 
316 			/***
317 			 * When mouse exites the downbutton, timerDown stops.
318 			 * 
319 			 * @param e
320 			 *           MouseEvent
321 			 */
322 			public void mouseExited( MouseEvent e )
323 			{
324 				try
325 				{
326 					timerDown.stop();
327 				}
328 				catch( Exception ekas )
329 				{
330 				}
331 			}
332 		}
333 
334 		MouseListener scrollDownListener = new Down();
335 		downButton.addMouseListener( scrollDownListener );
336 		add( downButton, subMenus.size() + 1 );
337 		setHorizontalAlignment( SwingConstants.LEFT );
338 	}
339 
340 	/***
341 	 * Hides the scrollButtons.
342 	 */
343 	public void hideButtons()
344 	{
345 		upButton.setVisible( false );
346 		downButton.setVisible( false );
347 	}
348 
349 	public JMenuItem addHeader( JMenuItem header )
350 	{
351 		add( header, headerCount );
352 
353 		if( ++headerCount == 1 )
354 			add( new JSeparator(), 1 );
355 
356 		return header;
357 	}
358 
359 	public JMenuItem addHeader( Action action )
360 	{
361 		return addHeader( new JMenuItem( action ) );
362 	}
363 
364 	public JMenuItem addFooter( JMenuItem footer )
365 	{
366 		if( footerCount == 0 )
367 			add( new JSeparator(), subMenus.size() + headerCount + 2 + ( headerCount == 0 ? 0 : 1 ) );
368 
369 		add( footer, subMenus.size() + headerCount + footerCount + 3 + ( headerCount == 0 ? 0 : 1 ) );
370 		footerCount++ ;
371 
372 		return footer;
373 	}
374 
375 	public JMenuItem addFooter( Action action )
376 	{
377 		return addFooter( new JMenuItem( action ) );
378 	}
379 
380 	public void removeAll()
381 	{
382 		super.removeAll();
383 
384 		headerCount = 0;
385 		footerCount = 0;
386 		menuHeight = 0;
387 		indexVisible = 0;
388 		visibleItems = 0;
389 
390 		subMenus.clear();
391 
392 		add( upButton );
393 		add( downButton );
394 	}
395 }