-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathBoundsPopupMenuListener.java
More file actions
352 lines (302 loc) · 9.92 KB
/
BoundsPopupMenuListener.java
File metadata and controls
352 lines (302 loc) · 9.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
/**
* This class will change the bounds of the JComboBox popup menu to support
* different functionality. It will support the following features:
* - a horizontal scrollbar can be displayed when necessary
* - the popup can be wider than the combo box
* - the popup can be displayed above the combo box
*
* Class will only work for a JComboBox that uses a BasicComboPop.
*/
public class BoundsPopupMenuListener implements PopupMenuListener
{
private boolean scrollBarRequired = true;
private boolean popupWider;
private int maximumWidth = -1;
private boolean popupAbove;
private JScrollPane scrollPane;
/**
* Convenience constructore to allow the display of a horizontal scrollbar
* when required.
*/
public BoundsPopupMenuListener()
{
this(true, false, -1, false);
}
/**
* Convenience constructor that allows you to display the popup
* wider and/or above the combo box.
*
* @param popupWider when true, popup width is based on the popup
* preferred width
* @param popupAbove when true, popup is displayed above the combobox
*/
public BoundsPopupMenuListener(boolean popupWider, boolean popupAbove)
{
this(true, popupWider, -1, popupAbove);
}
/**
* Convenience constructor that allows you to display the popup
* wider than the combo box and to specify the maximum width
*
* @param maximumWidth the maximum width of the popup. The
* popupAbove value is set to "true".
*/
public BoundsPopupMenuListener(int maximumWidth)
{
this(true, true, maximumWidth, false);
}
/**
* General purpose constructor to set all popup properties at once.
*
* @param scrollBarRequired display a horizontal scrollbar when the
* preferred width of popup is greater than width of scrollPane.
* @param popupWider display the popup at its preferred with
* @param maximumWidth limit the popup width to the value specified
* (minimum size will be the width of the combo box)
* @param popupAbove display the popup above the combo box
*
*/
public BoundsPopupMenuListener(
boolean scrollBarRequired, boolean popupWider, int maximumWidth, boolean popupAbove)
{
setScrollBarRequired( scrollBarRequired );
setPopupWider( popupWider );
setMaximumWidth( maximumWidth );
setPopupAbove( popupAbove );
}
/**
* Return the maximum width of the popup.
*
* @return the maximumWidth value
*/
public int getMaximumWidth()
{
return maximumWidth;
}
/**
* Set the maximum width for the popup. This value is only used when
* setPopupWider( true ) has been specified. A value of -1 indicates
* that there is no maximum.
*
* @param maximumWidth the maximum width of the popup
*/
public void setMaximumWidth(int maximumWidth)
{
this.maximumWidth = maximumWidth;
}
/**
* Determine if the popup should be displayed above the combo box.
*
* @return the popupAbove value
*/
public boolean isPopupAbove()
{
return popupAbove;
}
/**
* Change the location of the popup relative to the combo box.
*
* @param popupAbove true display popup above the combo box,
* false display popup below the combo box.
*/
public void setPopupAbove(boolean popupAbove)
{
this.popupAbove = popupAbove;
}
/**
* Determine if the popup might be displayed wider than the combo box
*
* @return the popupWider value
*/
public boolean isPopupWider()
{
return popupWider;
}
/**
* Change the width of the popup to be the greater of the width of the
* combo box or the preferred width of the popup. Normally the popup width
* is always the same size as the combo box width.
*
* @param popupWider true adjust the width as required.
*/
public void setPopupWider(boolean popupWider)
{
this.popupWider = popupWider;
}
/**
* Determine if the horizontal scroll bar might be required for the popup
*
* @return the scrollBarRequired value
*/
public boolean isScrollBarRequired()
{
return scrollBarRequired;
}
/**
* For some reason the default implementation of the popup removes the
* horizontal scrollBar from the popup scroll pane which can result in
* the truncation of the rendered items in the popop. Adding a scrollBar
* back to the scrollPane will allow horizontal scrolling if necessary.
*
* @param scrollBarRequired true add horizontal scrollBar to scrollPane
* false remove the horizontal scrollBar
*/
public void setScrollBarRequired(boolean scrollBarRequired)
{
this.scrollBarRequired = scrollBarRequired;
}
/**
* Alter the bounds of the popup just before it is made visible.
*/
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e)
{
JComboBox comboBox = (JComboBox)e.getSource();
if (comboBox.getItemCount() == 0) return;
final Object child = comboBox.getAccessibleContext().getAccessibleChild(0);
if (child instanceof BasicComboPopup)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
customizePopup((BasicComboPopup)child);
}
});
}
}
protected void customizePopup(BasicComboPopup popup)
{
scrollPane = getScrollPane(popup);
if (popupWider)
popupWider( popup );
checkHorizontalScrollBar( popup );
// For some reason in JDK7 the popup will not display at its preferred
// width unless its location has been changed from its default
// (ie. for normal "pop down" shift the popup and reset)
Component comboBox = popup.getInvoker();
Point location = comboBox.getLocationOnScreen();
if (popupAbove)
{
int height = popup.getPreferredSize().height;
popup.setLocation(location.x, location.y - height);
}
else
{
int height = comboBox.getPreferredSize().height;
popup.setLocation(location.x, location.y + height - 1);
popup.setLocation(location.x, location.y + height);
}
}
/*
* Adjust the width of the scrollpane used by the popup
*/
protected void popupWider(BasicComboPopup popup)
{
JList list = popup.getList();
// Determine the maximimum width to use:
// a) determine the popup preferred width
// b) limit width to the maximum if specified
// c) ensure width is not less than the scroll pane width
int popupWidth = list.getPreferredSize().width
+ 5 // make sure horizontal scrollbar doesn't appear
+ getScrollBarWidth(popup, scrollPane);
if (maximumWidth != -1)
{
popupWidth = Math.min(popupWidth, maximumWidth);
}
Dimension scrollPaneSize = scrollPane.getPreferredSize();
popupWidth = Math.max(popupWidth, scrollPaneSize.width);
// Adjust the width
scrollPaneSize.width = popupWidth;
scrollPane.setPreferredSize(scrollPaneSize);
scrollPane.setMaximumSize(scrollPaneSize);
}
/*
* This method is called every time:
* - to make sure the viewport is returned to its default position
* - to remove the horizontal scrollbar when it is not wanted
*/
private void checkHorizontalScrollBar(BasicComboPopup popup)
{
// Reset the viewport to the left
JViewport viewport = scrollPane.getViewport();
Point p = viewport.getViewPosition();
p.x = 0;
viewport.setViewPosition( p );
// Remove the scrollbar so it is never painted
if (! scrollBarRequired)
{
scrollPane.setHorizontalScrollBar( null );
return;
}
// Make sure a horizontal scrollbar exists in the scrollpane
JScrollBar horizontal = scrollPane.getHorizontalScrollBar();
if (horizontal == null)
{
horizontal = new JScrollBar(JScrollBar.HORIZONTAL);
scrollPane.setHorizontalScrollBar( horizontal );
scrollPane.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
}
// Potentially increase height of scroll pane to display the scrollbar
if (horizontalScrollBarWillBeVisible(popup, scrollPane))
{
Dimension scrollPaneSize = scrollPane.getPreferredSize();
scrollPaneSize.height += horizontal.getPreferredSize().height;
scrollPane.setPreferredSize(scrollPaneSize);
scrollPane.setMaximumSize(scrollPaneSize);
scrollPane.revalidate();
}
}
/*
* Get the scroll pane used by the popup so its bounds can be adjusted
*/
protected JScrollPane getScrollPane(BasicComboPopup popup)
{
JList list = popup.getList();
Container c = SwingUtilities.getAncestorOfClass(JScrollPane.class, list);
return (JScrollPane)c;
}
/*
* I can't find any property on the scrollBar to determine if it will be
* displayed or not so use brute force to determine this.
*/
protected int getScrollBarWidth(BasicComboPopup popup, JScrollPane scrollPane)
{
int scrollBarWidth = 0;
JComboBox comboBox = (JComboBox)popup.getInvoker();
if (comboBox.getItemCount() > comboBox.getMaximumRowCount())
{
JScrollBar vertical = scrollPane.getVerticalScrollBar();
scrollBarWidth = vertical.getPreferredSize().width;
}
return scrollBarWidth;
}
/*
* I can't find any property on the scrollBar to determine if it will be
* displayed or not so use brute force to determine this.
*/
protected boolean horizontalScrollBarWillBeVisible(BasicComboPopup popup, JScrollPane scrollPane)
{
JList list = popup.getList();
int scrollBarWidth = getScrollBarWidth(popup, scrollPane);
int popupWidth = list.getPreferredSize().width + scrollBarWidth;
return popupWidth > scrollPane.getPreferredSize().width;
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
{
// In its normal state the scrollpane does not have a scrollbar
if (scrollPane != null)
{
scrollPane.setHorizontalScrollBar( null );
}
}
}