|
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) 2010, 2012, Oracle and/or its affiliates. All rights reserved. |
|
24 */ |
|
25 |
|
26 package com.oracle.solaris.vp.panel.swing.control; |
|
27 |
|
28 import java.awt.*; |
|
29 import java.awt.event.*; |
|
30 import java.io.IOException; |
|
31 import java.util.*; |
|
32 import java.util.List; |
|
33 import javax.swing.*; |
|
34 import com.oracle.solaris.vp.panel.common.*; |
|
35 import com.oracle.solaris.vp.panel.common.action.ActionException; |
|
36 import com.oracle.solaris.vp.panel.common.control.*; |
|
37 import com.oracle.solaris.vp.panel.common.model.PanelDescriptor; |
|
38 import com.oracle.solaris.vp.panel.common.view.*; |
|
39 import com.oracle.solaris.vp.panel.swing.view.SwingBusyIndicator; |
|
40 import com.oracle.solaris.vp.util.misc.finder.Finder; |
|
41 import com.oracle.solaris.vp.util.misc.IconUtil; |
|
42 import com.oracle.solaris.vp.util.swing.*; |
|
43 import com.oracle.solaris.vp.util.swing.glass.*; |
|
44 |
|
45 public class WindowControl<P extends PanelDescriptor, C extends Window> |
|
46 extends SwingControl<P, C> implements HasBusyIndicator { |
|
47 |
|
48 private static final String RAD_FMRI = "system/rad"; |
|
49 |
|
50 // |
|
51 // Inner classes |
|
52 // |
|
53 |
|
54 private static class BusyGlassPaneIndicator extends BusyGlassPane |
|
55 implements SwingBusyIndicator { |
|
56 |
|
57 @Override |
|
58 public boolean isBusyIndicatorDisplayed() { |
|
59 return isShowing(); |
|
60 } |
|
61 |
|
62 @Override |
|
63 public void setBusyIndicatorDisplayed(final boolean displayed) { |
|
64 GUIUtil.invokeAndWait( |
|
65 new Runnable() { |
|
66 @Override |
|
67 public void run() { |
|
68 getParent().setVisible(displayed); |
|
69 } |
|
70 }); |
|
71 } |
|
72 } |
|
73 |
|
74 // |
|
75 // Instance data |
|
76 // |
|
77 |
|
78 // The Control from which the window title is derived |
|
79 private Control titleControl; |
|
80 |
|
81 // The Control from which the window icons are derived |
|
82 private Control iconControl; |
|
83 |
|
84 private WindowListener windowClosingListener = |
|
85 new WindowAdapter() { |
|
86 @Override |
|
87 public void windowClosing(WindowEvent e) { |
|
88 WindowControl.this.windowClosing(); |
|
89 } |
|
90 }; |
|
91 |
|
92 private BusyGlassPaneIndicator busy; |
|
93 |
|
94 // Listener to hide the visual "flicker" that could occur when |
|
95 // navigating between distant Controls within this window |
|
96 private NavigationListener noFlicker = |
|
97 new NavigationListener() { |
|
98 @Override |
|
99 public void navigationStarted(final NavigationStartEvent e) { |
|
100 setGlassPaneVisible(true); |
|
101 } |
|
102 |
|
103 @Override |
|
104 public void navigationStopped(NavigationStopEvent e) { |
|
105 setGlassPaneVisible(false); |
|
106 } |
|
107 |
|
108 private void setGlassPaneVisible(final boolean visible) { |
|
109 final Window window = getComponent(); |
|
110 if (window instanceof RootPaneContainer) { |
|
111 GUIUtil.invokeAndWait( |
|
112 new Runnable() { |
|
113 @Override |
|
114 public void run() { |
|
115 ((RootPaneContainer)window).getGlassPane(). |
|
116 setVisible(visible); |
|
117 } |
|
118 }); |
|
119 } |
|
120 } |
|
121 }; |
|
122 |
|
123 private ConnectionListener connListener = |
|
124 new ConnectionListener() { |
|
125 @Override |
|
126 public void connectionChanged(ConnectionEvent event) { |
|
127 busy.setBusyIndicatorDisplayed(false); |
|
128 } |
|
129 |
|
130 @Override |
|
131 public void connectionFailed(ConnectionEvent event) { |
|
132 // Prevent interaction with GUI until connection is repaired |
|
133 busy.setMessage(Finder.getString("connection.failed.message", |
|
134 event.getConnectionInfo().getHost(), RAD_FMRI)); |
|
135 |
|
136 Action quit = |
|
137 new AbstractAction(Finder.getString( |
|
138 "connection.failed.button.quit")) { |
|
139 |
|
140 @Override |
|
141 public void actionPerformed(ActionEvent event) { |
|
142 getNavigator().asyncExec( |
|
143 new Runnable() { |
|
144 @Override |
|
145 public void run() { |
|
146 try { |
|
147 getClientContext().closeInstance( |
|
148 true); |
|
149 } catch (ActionException ignore) { |
|
150 } |
|
151 } |
|
152 }); |
|
153 } |
|
154 }; |
|
155 |
|
156 Action reconnect = |
|
157 new AbstractAction(Finder.getString( |
|
158 "connection.failed.button.reconnect")) { |
|
159 |
|
160 @Override |
|
161 public void actionPerformed(ActionEvent event) { |
|
162 getNavigator().asyncExec( |
|
163 new Runnable() { |
|
164 @Override |
|
165 public void run() { |
|
166 try { |
|
167 getClientContext().login(null, |
|
168 false); |
|
169 } catch (ActionException ignore) { |
|
170 } |
|
171 } |
|
172 }); |
|
173 } |
|
174 }; |
|
175 |
|
176 busy.setActions(quit, reconnect); |
|
177 busy.setDelay(0); |
|
178 busy.setBusyIndicatorDisplayed(true); |
|
179 } |
|
180 }; |
|
181 |
|
182 // |
|
183 // Constructors |
|
184 // |
|
185 |
|
186 public WindowControl(String id, String name, P descriptor) { |
|
187 super(id, name, descriptor); |
|
188 } |
|
189 |
|
190 // |
|
191 // HasBusyIndicator methods |
|
192 // |
|
193 |
|
194 @Override |
|
195 public BusyIndicator getBusyIndicator() { |
|
196 return busy; |
|
197 } |
|
198 |
|
199 // |
|
200 // Control methods |
|
201 // |
|
202 |
|
203 @Override |
|
204 public void descendantStarted(Control[] path) { |
|
205 super.descendantStarted(path); |
|
206 |
|
207 Control control = path[path.length - 1]; |
|
208 controlStarted(control); |
|
209 } |
|
210 |
|
211 @Override |
|
212 public void descendantStopped(Control[] path) { |
|
213 super.descendantStopped(path); |
|
214 |
|
215 Control control = path[path.length - 1]; |
|
216 controlStopped(control); |
|
217 } |
|
218 |
|
219 @Override |
|
220 public Navigable[] getForwardingPath(boolean childStopped) { |
|
221 ensureChildrenCreated(); |
|
222 |
|
223 if (childStopped) { |
|
224 return new Navigable[] {Navigator.PARENT_NAVIGABLE}; |
|
225 } |
|
226 |
|
227 if (children.size() > 0) { |
|
228 // Automatically view first child |
|
229 return new Navigable[] {children.get(0)}; |
|
230 } |
|
231 |
|
232 return super.getForwardingPath(childStopped); |
|
233 } |
|
234 |
|
235 @Override |
|
236 public void start(Navigator navigator, Map<String, String> parameters) |
|
237 throws NavigationAbortedException, InvalidParameterException, |
|
238 NavigationFailedException { |
|
239 |
|
240 super.start(navigator, parameters); |
|
241 |
|
242 Window window = getComponent(); |
|
243 window.addWindowListener(windowClosingListener); |
|
244 |
|
245 // Wait until the navigation is complete, then show the window (as long |
|
246 // as this Control is still started) |
|
247 getNavigator().asyncExec( |
|
248 new Runnable() { |
|
249 @Override |
|
250 public void run() { |
|
251 if (isStarted()) { |
|
252 ConnectionInfo info = getPanelDescriptor(). |
|
253 getClientContext().getConnectionInfo(); |
|
254 |
|
255 try { |
|
256 // Test connection. |
|
257 info.getConnector(). |
|
258 getMBeanServerConnection(); |
|
259 } catch (IOException e) { |
|
260 // Show indicator for broken connection. |
|
261 connListener.connectionFailed( |
|
262 new ConnectionEvent(this, info)); |
|
263 } |
|
264 |
|
265 // Use event thread in case setVisible blocks |
|
266 EventQueue.invokeLater( |
|
267 new Runnable() { |
|
268 @Override |
|
269 public void run() { |
|
270 setVisible(); |
|
271 } |
|
272 }); |
|
273 } |
|
274 } |
|
275 }); |
|
276 |
|
277 controlStarted(this); |
|
278 |
|
279 navigator.addNavigationListener(noFlicker); |
|
280 noFlicker.navigationStarted(null); |
|
281 |
|
282 getPanelDescriptor().getClientContext().addConnectionListener( |
|
283 connListener); |
|
284 } |
|
285 |
|
286 @Override |
|
287 public void stop(boolean isCancel) throws NavigationAbortedException { |
|
288 Navigator navigator = getNavigator(); |
|
289 super.stop(isCancel); |
|
290 |
|
291 Window window = getComponent(); |
|
292 window.removeWindowListener(windowClosingListener); |
|
293 window.setVisible(false); |
|
294 |
|
295 controlStopped(this); |
|
296 |
|
297 noFlicker.navigationStopped(null); |
|
298 navigator.removeNavigationListener(noFlicker); |
|
299 |
|
300 getPanelDescriptor().getClientContext().removeConnectionListener( |
|
301 connListener); |
|
302 } |
|
303 |
|
304 // |
|
305 // SwingControl methods |
|
306 // |
|
307 |
|
308 @Override |
|
309 protected void configComponent(C comp) { |
|
310 super.configComponent(comp); |
|
311 |
|
312 Window window = getComponent(); |
|
313 if (window instanceof RootPaneContainer) { |
|
314 LayeredGlassPane layered = new LayeredGlassPane(); |
|
315 |
|
316 busy = new BusyGlassPaneIndicator(); |
|
317 layered.add(busy); |
|
318 |
|
319 ImageCaptureGlassPane capture = new ImageCaptureGlassPane(); |
|
320 layered.add(capture); |
|
321 |
|
322 ((RootPaneContainer)window).setGlassPane(layered); |
|
323 } |
|
324 } |
|
325 |
|
326 // |
|
327 // WindowControl methods |
|
328 // |
|
329 |
|
330 /** |
|
331 * Gets the title of this {@code Window}, if supported. This default |
|
332 * implementation returns {@code null}. |
|
333 * |
|
334 * @return the title, or {@code null} if this {@code WindowControl}'s |
|
335 * {@code Window} does not support a title |
|
336 */ |
|
337 protected String getWindowTitle() { |
|
338 return null; |
|
339 } |
|
340 |
|
341 /** |
|
342 * Shows the window. This method is called on the AWT event thread, after |
|
343 * navigation has completed. |
|
344 */ |
|
345 protected void setVisible() { |
|
346 getComponent().setVisible(true); |
|
347 } |
|
348 |
|
349 /** |
|
350 * Sets the title of this {@code Window}, if supported. This default |
|
351 * implementation does nothing. |
|
352 * |
|
353 * @param title |
|
354 * the title, or {@code null} if this {@code WindowControl}'s |
|
355 * {@code Window} does not support a title |
|
356 */ |
|
357 protected void setWindowTitle(String title) { |
|
358 } |
|
359 |
|
360 /** |
|
361 * Called on the AWT event thread when the user closes the window directly. |
|
362 * This default implementation attempts to navigate upward one level in the |
|
363 * navigation stack. |
|
364 */ |
|
365 protected void windowClosing() { |
|
366 getNavigator().goToAsync(false, this, Navigator.PARENT_NAVIGABLE); |
|
367 } |
|
368 |
|
369 // |
|
370 // Private methods |
|
371 // |
|
372 |
|
373 private void controlStarted(Control control) { |
|
374 String title = getWindowTitle(); |
|
375 if (title == null || title.isEmpty()) { |
|
376 String name = control.getName(); |
|
377 if (name != null) { |
|
378 titleControl = control; |
|
379 setWindowTitle(name); |
|
380 } |
|
381 } |
|
382 |
|
383 Window window = getComponent(); |
|
384 List<? extends Image> images = window.getIconImages(); |
|
385 if (images == null || images.isEmpty()) { |
|
386 if (control instanceof HasIcons) { |
|
387 List<? extends Icon> icons = ((HasIcons)control).getIcons(); |
|
388 if (icons != null && !icons.isEmpty()) { |
|
389 iconControl = control; |
|
390 window.setIconImages(IconUtil.toImages(icons)); |
|
391 } |
|
392 } |
|
393 } |
|
394 } |
|
395 |
|
396 private void controlStopped(Control control) { |
|
397 if (titleControl == control) { |
|
398 setWindowTitle(null); |
|
399 titleControl = null; |
|
400 } |
|
401 |
|
402 if (iconControl == control) { |
|
403 getComponent().setIconImages(null); |
|
404 iconControl = null; |
|
405 } |
|
406 } |
|
407 } |