• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
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 
17 package com.android.sdkuilib.ui;
18 
19 import com.android.sdklib.SdkConstants;
20 
21 import org.eclipse.swt.SWT;
22 import org.eclipse.swt.events.DisposeEvent;
23 import org.eclipse.swt.events.DisposeListener;
24 import org.eclipse.swt.graphics.Point;
25 import org.eclipse.swt.graphics.Rectangle;
26 import org.eclipse.swt.widgets.Dialog;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.swt.widgets.Shell;
29 
30 import java.util.HashMap;
31 import java.util.Map;
32 
33 /**
34  * A base class for an SWT Dialog.
35  * <p/>
36  * The base class offers the following goodies: <br/>
37  * - Dialog is automatically centered on its parent. <br/>
38  * - Dialog size is reused during the session. <br/>
39  * - A simple API with an {@link #open()} method that returns a boolean. <br/>
40  * <p/>
41  * A typical usage is:
42  * <pre>
43  *   MyDialog extends SwtBaseDialog { ... }
44  *   MyDialog d = new MyDialog(parentShell, "My Dialog Title");
45  *   if (d.open()) {
46  *      ...do something like refresh parent list view
47  *   }
48  * </pre>
49  * We also have a JFace-base {@link GridDialog}.
50  * The JFace dialog is good when you just want a typical OK/Cancel layout with the
51  * buttons all managed for you.
52  * This SWT base dialog has little decoration.
53  * It's up to you to manage whatever buttons you want, if any.
54  */
55 public abstract class SwtBaseDialog extends Dialog {
56 
57     /**
58      * Min Y location for dialog. Need to deal with the menu bar on mac os.
59      */
60     private final static int MIN_Y =
61         SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN ? 20 : 0;
62 
63     /** Last dialog size for this session, different for each dialog class. */
64     private static Map<Class<?>, Point> sLastSizeMap = new HashMap<Class<?>, Point>();
65 
66     private volatile boolean mQuitRequested = false;
67     private boolean mReturnValue;
68     private Shell mShell;
69 
70     /**
71      * Create the dialog.
72      *
73      * @param parent The parent's shell
74      * @param title The dialog title. Can be null.
75      */
SwtBaseDialog(Shell parent, int swtStyle, String title)76     public SwtBaseDialog(Shell parent, int swtStyle, String title) {
77         super(parent, swtStyle);
78         if (title != null) {
79             setText(title);
80         }
81     }
82 
83     /**
84      * Open the dialog.
85      *
86      * @return The last value set using {@link #setReturnValue(boolean)} or false by default.
87      */
open()88     public boolean open() {
89         if (!mQuitRequested) {
90             createShell();
91         }
92         if (!mQuitRequested) {
93             createContents();
94         }
95         if (!mQuitRequested) {
96             positionShell();
97         }
98         if (!mQuitRequested) {
99             postCreate();
100         }
101         if (!mQuitRequested) {
102             mShell.open();
103             mShell.layout();
104             eventLoop();
105         }
106 
107         return mReturnValue;
108     }
109 
110     /**
111      * Creates the shell for this dialog.
112      * The default shell has a size of 450x300, which is also its minimum size.
113      * You might want to override these values.
114      * <p/>
115      * Called before {@link #createContents()}.
116      */
createShell()117     protected void createShell() {
118         mShell = new Shell(getParent(), SWT.DIALOG_TRIM | SWT.RESIZE | SWT.APPLICATION_MODAL);
119         mShell.setMinimumSize(new Point(450, 300));
120         mShell.setSize(450, 300);
121         if (getText() != null) {
122             mShell.setText(getText());
123         }
124         mShell.addDisposeListener(new DisposeListener() {
125             @Override
126             public void widgetDisposed(DisposeEvent e) {
127                 saveSize();
128             }
129         });
130     }
131 
132     /**
133      * Creates the content and attaches it to the current shell (cf. {@link #getShell()}).
134      * <p/>
135      * Derived classes should consider creating the UI here and initializing their
136      * state in {@link #postCreate()}.
137      */
createContents()138     protected abstract void createContents();
139 
140     /**
141      * Called after {@link #createContents()} and after {@link #positionShell()}
142      * just before the dialog is actually shown on screen.
143      * <p/>
144      * Derived classes should consider creating the UI in {@link #createContents()} and
145      * initialize it here.
146      */
postCreate()147     protected abstract void postCreate();
148 
149     /**
150      * Run the event loop.
151      * This is called from {@link #open()} after {@link #postCreate()} and
152      * after the window has been shown on screen.
153      * Derived classes might want to use this as a place to start automated
154      * tasks that will update the UI.
155      */
eventLoop()156     protected void eventLoop() {
157         Display display = getParent().getDisplay();
158         while (!mQuitRequested && !mShell.isDisposed()) {
159             if (!display.readAndDispatch()) {
160                 display.sleep();
161             }
162         }
163     }
164 
165     /**
166      * Returns the current value that {@link #open()} will return to the caller.
167      * Default is false.
168      */
getReturnValue()169     protected boolean getReturnValue() {
170         return mReturnValue;
171     }
172 
173     /**
174      * Sets the value that {@link #open()} will return to the caller.
175      * @param returnValue The new value to be returned by {@link #open()}.
176      */
setReturnValue(boolean returnValue)177     protected void setReturnValue(boolean returnValue) {
178         mReturnValue = returnValue;
179     }
180 
181     /**
182      * Returns the shell created by {@link #createShell()}.
183      * @return The current {@link Shell}.
184      */
getShell()185     protected Shell getShell() {
186         return mShell;
187     }
188 
189     /**
190      * Saves the dialog size and close the dialog.
191      * The {@link #open()} method will given return value (see {@link #setReturnValue(boolean)}.
192      * <p/>
193      * It's safe to call this method before the shell is initialized,
194      * in which case the dialog will close as soon as possible.
195      */
close()196     protected void close() {
197         if (mShell != null && !mShell.isDisposed()) {
198             saveSize();
199             getShell().close();
200         }
201         mQuitRequested = true;
202     }
203 
204     //-------
205 
206     /**
207      * Centers the dialog in its parent shell.
208      */
positionShell()209     private void positionShell() {
210         // Centers the dialog in its parent shell
211         Shell child = mShell;
212         Shell parent = getParent();
213         if (child != null && parent != null) {
214             // get the parent client area with a location relative to the display
215             Rectangle parentArea = parent.getClientArea();
216             Point parentLoc = parent.getLocation();
217             int px = parentLoc.x;
218             int py = parentLoc.y;
219             int pw = parentArea.width;
220             int ph = parentArea.height;
221 
222             // Reuse the last size if there's one, otherwise use the default
223             Point childSize = sLastSizeMap.get(this.getClass());
224             if (childSize == null) {
225                 childSize = child.getSize();
226             }
227             int cw = childSize.x;
228             int ch = childSize.y;
229 
230             int x = px + (pw - cw) / 2;
231             if (x < 0) x = 0;
232 
233             int y = py + (ph - ch) / 2;
234             if (y < MIN_Y) y = MIN_Y;
235 
236             child.setLocation(x, y);
237             child.setSize(cw, ch);
238         }
239     }
240 
saveSize()241     private void saveSize() {
242         if (mShell != null && !mShell.isDisposed()) {
243             sLastSizeMap.put(this.getClass(), mShell.getSize());
244         }
245     }
246 
247 }
248