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
55
56 private int headerCount;
57 private int footerCount;
58
59
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
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 }