View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.eclipse.ui.dialogs;
17  
18  import org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  import org.argeo.eclipse.ui.EclipseUiException;
21  import org.eclipse.swt.SWT;
22  import org.eclipse.swt.events.FocusEvent;
23  import org.eclipse.swt.events.FocusListener;
24  import org.eclipse.swt.events.ShellAdapter;
25  import org.eclipse.swt.events.ShellEvent;
26  import org.eclipse.swt.graphics.Point;
27  import org.eclipse.swt.graphics.Rectangle;
28  import org.eclipse.swt.layout.GridData;
29  import org.eclipse.swt.layout.GridLayout;
30  import org.eclipse.swt.widgets.Composite;
31  import org.eclipse.swt.widgets.Control;
32  import org.eclipse.swt.widgets.Display;
33  import org.eclipse.swt.widgets.Shell;
34  
35  /** Generic lightweight dialog, not based on JFace. */
36  public class LightweightDialog {
37  	private final static Log log = LogFactory.getLog(LightweightDialog.class);
38  
39  	// must be the same value as org.eclipse.jface.window.Window#OK
40  	public final static int OK = 0;
41  	// must be the same value as org.eclipse.jface.window.Window#CANCEL
42  	public final static int CANCEL = 1;
43  
44  	private Shell parentShell;
45  	private Shell backgroundShell;
46  	private Shell foregoundShell;
47  
48  	private Integer returnCode = null;
49  	private boolean block = true;
50  
51  	private String title;
52  
53  	/** Tries to find a display */
54  	private static Display getDisplay() {
55  		try {
56  			Display display = Display.getCurrent();
57  			if (display != null)
58  				return display;
59  			else
60  				return Display.getDefault();
61  		} catch (Exception e) {
62  			return Display.getCurrent();
63  		}
64  	}
65  
66  	public LightweightDialog(Shell parentShell) {
67  		this.parentShell = parentShell;
68  	}
69  
70  	public int open() {
71  		if (foregoundShell != null)
72  			throw new EclipseUiException("There is already a shell");
73  		backgroundShell = new Shell(parentShell, SWT.ON_TOP);
74  		backgroundShell.setFullScreen(true);
75  		// if (parentShell != null) {
76  		// backgroundShell.setBounds(parentShell.getBounds());
77  		// } else
78  		// backgroundShell.setMaximized(true);
79  		backgroundShell.setAlpha(128);
80  		backgroundShell.setBackground(getDisplay().getSystemColor(SWT.COLOR_BLACK));
81  		foregoundShell = new Shell(backgroundShell, SWT.NO_TRIM | SWT.ON_TOP);
82  		if (title != null)
83  			setTitle(title);
84  		foregoundShell.setLayout(new GridLayout());
85  		foregoundShell.setSize(getInitialSize());
86  		createDialogArea(foregoundShell);
87  		// shell.pack();
88  		// shell.layout();
89  
90  		Rectangle shellBounds = parentShell != null ? parentShell.getBounds() : Display.getCurrent().getBounds();// RAP
91  		Point dialogSize = foregoundShell.getSize();
92  		int x = shellBounds.x + (shellBounds.width - dialogSize.x) / 2;
93  		int y = shellBounds.y + (shellBounds.height - dialogSize.y) / 2;
94  		foregoundShell.setLocation(x, y);
95  
96  		foregoundShell.addShellListener(new ShellAdapter() {
97  			private static final long serialVersionUID = -2701270481953688763L;
98  
99  			@Override
100 			public void shellDeactivated(ShellEvent e) {
101 				if (hasChildShells())
102 					return;
103 				if (returnCode == null)// not yet closed
104 					closeShell(CANCEL);
105 			}
106 
107 			@Override
108 			public void shellClosed(ShellEvent e) {
109 				notifyClose();
110 			}
111 
112 		});
113 
114 		backgroundShell.open();
115 		foregoundShell.open();
116 		// after the foreground shell has been opened
117 		backgroundShell.addFocusListener(new FocusListener() {
118 			private static final long serialVersionUID = 3137408447474661070L;
119 
120 			@Override
121 			public void focusLost(FocusEvent event) {
122 			}
123 
124 			@Override
125 			public void focusGained(FocusEvent event) {
126 				if (hasChildShells())
127 					return;
128 				if (returnCode == null)// not yet closed
129 					closeShell(CANCEL);
130 			}
131 		});
132 
133 		if (block) {
134 			try {
135 				runEventLoop(foregoundShell);
136 			} catch (ThreadDeath t) {
137 				returnCode = CANCEL;
138 				if (log.isTraceEnabled())
139 					log.error("Thread death, canceling dialog", t);
140 			} catch (Throwable t) {
141 				returnCode = CANCEL;
142 				log.error("Cannot open blocking lightweight dialog", t);
143 			}
144 		}
145 		if (returnCode == null)
146 			returnCode = OK;
147 		return returnCode;
148 	}
149 
150 	private boolean hasChildShells() {
151 		if (foregoundShell == null)
152 			return false;
153 		return foregoundShell.getShells().length != 0;
154 	}
155 
156 	// public synchronized int openAndWait() {
157 	// open();
158 	// while (returnCode == null)
159 	// try {
160 	// wait(100);
161 	// } catch (InterruptedException e) {
162 	// // silent
163 	// }
164 	// return returnCode;
165 	// }
166 
167 	private synchronized void notifyClose() {
168 		if (returnCode == null)
169 			returnCode = CANCEL;
170 		notifyAll();
171 	}
172 
173 	protected void closeShell(int returnCode) {
174 		this.returnCode = returnCode;
175 		if (CANCEL == returnCode)
176 			onCancel();
177 		if (foregoundShell != null && !foregoundShell.isDisposed()) {
178 			foregoundShell.close();
179 			foregoundShell.dispose();
180 			foregoundShell = null;
181 		}
182 
183 		if (backgroundShell != null && !backgroundShell.isDisposed()) {
184 			backgroundShell.close();
185 			backgroundShell.dispose();
186 		}
187 	}
188 
189 	protected Point getInitialSize() {
190 		// if (exception != null)
191 		// return new Point(800, 600);
192 		// else
193 		return new Point(600, 400);
194 	}
195 
196 	protected Control createDialogArea(Composite parent) {
197 		Composite dialogarea = new Composite(parent, SWT.NONE);
198 		dialogarea.setLayout(new GridLayout());
199 		dialogarea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
200 		return dialogarea;
201 	}
202 
203 	protected Shell getBackgroundShell() {
204 		return backgroundShell;
205 	}
206 
207 	protected Shell getForegoundShell() {
208 		return foregoundShell;
209 	}
210 
211 	public void setBlockOnOpen(boolean shouldBlock) {
212 		block = shouldBlock;
213 	}
214 
215 	public void pack() {
216 		foregoundShell.pack();
217 	}
218 
219 	private void runEventLoop(Shell loopShell) {
220 		Display display;
221 		if (foregoundShell == null) {
222 			display = Display.getCurrent();
223 		} else {
224 			display = loopShell.getDisplay();
225 		}
226 
227 		while (loopShell != null && !loopShell.isDisposed()) {
228 			try {
229 				if (!display.readAndDispatch()) {
230 					display.sleep();
231 				}
232 			} catch (Throwable e) {
233 				handleException(e);
234 			}
235 		}
236 		if (!display.isDisposed())
237 			display.update();
238 	}
239 
240 	protected void handleException(Throwable t) {
241 		if (t instanceof ThreadDeath) {
242 			// Don't catch ThreadDeath as this is a normal occurrence when
243 			// the thread dies
244 			throw (ThreadDeath) t;
245 		}
246 		// Try to keep running.
247 		t.printStackTrace();
248 	}
249 
250 	/** @return false, if the dialog should not be closed. */
251 	protected boolean onCancel() {
252 		return true;
253 	}
254 
255 	public void setTitle(String title) {
256 		this.title = title;
257 		if (title != null && getForegoundShell() != null)
258 			getForegoundShell().setText(title);
259 	}
260 
261 	public Integer getReturnCode() {
262 		return returnCode;
263 	}
264 
265 }