|
1 /* |
|
2 * CDDL HEADER START |
|
3 * |
|
4 * The contents of this file are subject to the terms of the |
|
5 * Common Development and Distribution License (the "License"). |
|
6 * You may not use this file except in compliance with the License. |
|
7 * |
|
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
|
9 * or http://www.opensolaris.org/os/licensing. |
|
10 * See the License for the specific language governing permissions |
|
11 * and limitations under the License. |
|
12 * |
|
13 * When distributing Covered Code, include this CDDL HEADER in each |
|
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
|
15 * If applicable, add the following below this CDDL HEADER, with the |
|
16 * fields enclosed by brackets "[]" replaced with your own identifying |
|
17 * information: Portions Copyright [yyyy] [name of copyright owner] |
|
18 * |
|
19 * CDDL HEADER END |
|
20 */ |
|
21 |
|
22 /* |
|
23 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
|
24 */ |
|
25 |
|
26 package com.oracle.solaris.vp.panel.swing.view; |
|
27 |
|
28 import java.awt.*; |
|
29 import java.awt.event.*; |
|
30 import java.util.*; |
|
31 import java.util.List; |
|
32 import javax.swing.*; |
|
33 import javax.swing.border.Border; |
|
34 import javax.swing.event.*; |
|
35 import com.oracle.solaris.vp.panel.common.control.*; |
|
36 import com.oracle.solaris.vp.util.misc.finder.MessageFinder; |
|
37 import com.oracle.solaris.vp.util.misc.NameValue; |
|
38 import com.oracle.solaris.vp.util.swing.*; |
|
39 |
|
40 @SuppressWarnings({"serial"}) |
|
41 public class ControlBreadCrumbs extends BreadCrumbs<List<Control>> { |
|
42 // |
|
43 // Static data |
|
44 // |
|
45 |
|
46 public static final int BUTTON_MARGIN = 4; |
|
47 |
|
48 // |
|
49 // Instance data |
|
50 // |
|
51 |
|
52 private JLabel lastBreadCrumb = createLabel(true); |
|
53 private PopupList iPopup; |
|
54 |
|
55 // |
|
56 // BreadCrumbs methods |
|
57 // |
|
58 |
|
59 @Override |
|
60 protected Component createBreadCrumb(int index) { |
|
61 List<Control> list = getElementAt(index); |
|
62 final Control control = list.get(0); |
|
63 String text = toString(list); |
|
64 |
|
65 if (index == getModel().getSize() - 1) { |
|
66 lastBreadCrumb.setText(text); |
|
67 return lastBreadCrumb; |
|
68 } |
|
69 |
|
70 JButton b = createButton(); |
|
71 b.setText(text); |
|
72 |
|
73 b.addActionListener( |
|
74 new ActionListener() { |
|
75 @Override |
|
76 public void actionPerformed(ActionEvent e) { |
|
77 // Launch navigation on non-event thread |
|
78 control.getNavigator().goToAsync(false, control); |
|
79 } |
|
80 }); |
|
81 |
|
82 return b; |
|
83 } |
|
84 |
|
85 @Override |
|
86 protected Component createHiddenBreadCrumbIndicator() { |
|
87 String pkg = BreadCrumbs.class.getPackage().getName(); |
|
88 String ellipsis = new MessageFinder().getStringFromPackage( |
|
89 pkg, "breadcrumb.hidden.text"); |
|
90 |
|
91 JButton b = createButton(); |
|
92 b.setText(ellipsis); |
|
93 b.setIcon(ArrowIcon.LEFT); |
|
94 b.setHorizontalTextPosition(SwingConstants.LEFT); |
|
95 |
|
96 iPopup = new PopupList(b); |
|
97 iPopup.showOnDownArrowKey(); |
|
98 iPopup.showOnMousePress(); |
|
99 |
|
100 // Navigate to Navigable selected in list |
|
101 iPopup.addItemListener( |
|
102 new ItemListener() { |
|
103 @Override |
|
104 public void itemStateChanged(ItemEvent e) { |
|
105 if (e.getStateChange() == ItemEvent.SELECTED) { |
|
106 @SuppressWarnings({"unchecked"}) |
|
107 NameValue<Control> nv = (NameValue<Control>)e.getItem(); |
|
108 |
|
109 final Control control = nv.getValue(); |
|
110 |
|
111 // Launch navigation on non-event thread |
|
112 control.getNavigator().goToAsync(false, control); |
|
113 } |
|
114 } |
|
115 }); |
|
116 |
|
117 return b; |
|
118 } |
|
119 |
|
120 @Override |
|
121 protected Component createSeparator(int index) { |
|
122 class Tuple { |
|
123 public Control control; |
|
124 public Navigable navigable; |
|
125 |
|
126 public Tuple(Control control, Navigable navigable) { |
|
127 this.control = control; |
|
128 this.navigable = navigable; |
|
129 } |
|
130 } |
|
131 |
|
132 Control nextControl = getElementAt(index).get(0); |
|
133 NameValue<Tuple> nextNav = null; |
|
134 |
|
135 NameValueComboBoxModel<Tuple> model = |
|
136 new NameValueComboBoxModel<Tuple>(); |
|
137 |
|
138 List<Control> previous = getElementAt(index - 1); |
|
139 for (Control control : previous) { |
|
140 for (Navigable navigable : control.getBrowsable()) { |
|
141 String name = navigable.getName(); |
|
142 if (name != null) { |
|
143 NameValue<Tuple> nv = new NameValue<Tuple>( |
|
144 name, new Tuple(control, navigable)); |
|
145 |
|
146 model.addElement(nv); |
|
147 |
|
148 if (nextNav == null && |
|
149 Navigable.Util.equals(navigable, nextControl)) { |
|
150 nextNav = nv; |
|
151 } |
|
152 } |
|
153 } |
|
154 } |
|
155 |
|
156 if (model.getSize() <= 1) { |
|
157 JLabel l = createLabel(false); |
|
158 l.setIcon(ArrowIcon.RIGHT); |
|
159 return l; |
|
160 } |
|
161 |
|
162 JButton b = createButton(); |
|
163 b.setIcon(ArrowIcon.RIGHT); |
|
164 |
|
165 final NameValue<Tuple> fNextNav = nextNav; |
|
166 PopupList popup = new PopupList(b, model); |
|
167 popup.showOnDownArrowKey(); |
|
168 popup.showOnMousePress(); |
|
169 |
|
170 final JList list = popup.getPopup().getList(); |
|
171 |
|
172 // Select the current Control in list when it is shown |
|
173 popup.getPopup().addPopupMenuListener( |
|
174 new PopupMenuListener() { |
|
175 @Override |
|
176 public void popupMenuCanceled(PopupMenuEvent e) { |
|
177 } |
|
178 |
|
179 @Override |
|
180 public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { |
|
181 } |
|
182 |
|
183 @Override |
|
184 public void popupMenuWillBecomeVisible(PopupMenuEvent e) { |
|
185 list.setSelectedValue(fNextNav, true); |
|
186 } |
|
187 }); |
|
188 |
|
189 // Embolden the current Control in list |
|
190 final ListCellRenderer renderer = list.getCellRenderer(); |
|
191 list.setCellRenderer( |
|
192 new ListCellRenderer() { |
|
193 @Override |
|
194 public Component getListCellRendererComponent( |
|
195 JList list, Object value, int index, boolean isSelected, |
|
196 boolean cellHasFocus) { |
|
197 |
|
198 Component c = renderer.getListCellRendererComponent( |
|
199 list, value, index, isSelected, cellHasFocus); |
|
200 |
|
201 if (value == fNextNav) { |
|
202 Font f = c.getFont().deriveFont(Font.BOLD); |
|
203 c.setFont(f); |
|
204 } |
|
205 |
|
206 return c; |
|
207 } |
|
208 }); |
|
209 |
|
210 // Navigate to Navigable selected in list |
|
211 popup.addItemListener( |
|
212 new ItemListener() { |
|
213 @Override |
|
214 public void itemStateChanged(ItemEvent e) { |
|
215 if (e.getStateChange() == ItemEvent.SELECTED) { |
|
216 @SuppressWarnings({"unchecked"}) |
|
217 NameValue<Tuple> nv = (NameValue<Tuple>)e.getItem(); |
|
218 Tuple tuple = nv.getValue(); |
|
219 |
|
220 // Launch navigation on non-event thread |
|
221 tuple.control.getNavigator().goToAsync(false, |
|
222 tuple.control, tuple.navigable); |
|
223 } |
|
224 } |
|
225 }); |
|
226 |
|
227 return b; |
|
228 } |
|
229 |
|
230 @Override |
|
231 protected void setFirstVisibleIndex(int index) { |
|
232 if (index != getFirstVisibleIndex()) { |
|
233 super.setFirstVisibleIndex(index); |
|
234 |
|
235 if (index != 0) { |
|
236 NameValueComboBoxModel<Control> model = |
|
237 new NameValueComboBoxModel<Control>(); |
|
238 |
|
239 for (int i = index - 1; i >= 0; i--) { |
|
240 Control control = getElementAt(i).get(0); |
|
241 |
|
242 NameValue<Control> nv = |
|
243 new NameValue<Control>(control.getName(), control); |
|
244 |
|
245 model.addElement(nv); |
|
246 } |
|
247 |
|
248 iPopup.setModel(model); |
|
249 } |
|
250 } |
|
251 } |
|
252 |
|
253 @Override |
|
254 public String toString(List<Control> list) { |
|
255 return list.get(0).getName(); |
|
256 } |
|
257 |
|
258 // |
|
259 // ControlBreadCrumbs methods |
|
260 // |
|
261 |
|
262 /** |
|
263 * Collate the given address into {@code List}s (stacking {@link Control}s |
|
264 * with {@code null} names into the previous {@link Control}'s {@code |
|
265 * List}), as expected by the model. |
|
266 * |
|
267 * @param path |
|
268 * an active navigation path |
|
269 */ |
|
270 public void setAddress(Collection<Control> path) { |
|
271 List<List<Control>> collated; |
|
272 if (path == null) { |
|
273 collated = Collections.emptyList(); |
|
274 } else { |
|
275 collated = new LinkedList<List<Control>>(); |
|
276 List<Control> list = null; |
|
277 for (Control control : path) { |
|
278 if (control.getName() != null) { |
|
279 list = new LinkedList<Control>(); |
|
280 collated.add(list); |
|
281 } |
|
282 if (list != null) { |
|
283 list.add(control); |
|
284 } |
|
285 } |
|
286 } |
|
287 |
|
288 clear(); |
|
289 |
|
290 for (List<Control> list : collated) { |
|
291 push(list); |
|
292 } |
|
293 } |
|
294 |
|
295 // |
|
296 // Private methods |
|
297 // |
|
298 |
|
299 private JButton createButton() { |
|
300 JButton b = new JButton(); |
|
301 GUIUtil.setHorizontalMargin(b, BUTTON_MARGIN); |
|
302 |
|
303 new RolloverHandler(b); |
|
304 return b; |
|
305 } |
|
306 |
|
307 private JLabel createLabel(boolean last) { |
|
308 JLabel label = new JLabel(); |
|
309 |
|
310 // Yes, this is hackish, but |
|
311 // UIManager.getBorder("Button.border").getBorderInsets(...) is not |
|
312 // reliable |
|
313 Insets insets = createButton().getInsets(); |
|
314 |
|
315 Border border = BorderFactory.createEmptyBorder( |
|
316 0, insets.left, 0, last ? 0 : insets.right); |
|
317 |
|
318 label.setBorder(border); |
|
319 |
|
320 // Prevent clicks from passing through to text field |
|
321 label.addMouseListener(new MouseAdapter() {}); |
|
322 |
|
323 return label; |
|
324 } |
|
325 } |