1/*******************************************************************************
2 * Copyright (c) 2011 Google, Inc.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *    Google, Inc. - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.wb.internal.core.utils.ui.dialogs;
12
13import org.eclipse.jface.dialogs.Dialog;
14import org.eclipse.jface.dialogs.IDialogSettings;
15import org.eclipse.swt.SWT;
16import org.eclipse.swt.events.ControlEvent;
17import org.eclipse.swt.events.ControlListener;
18import org.eclipse.swt.graphics.Point;
19import org.eclipse.swt.graphics.Rectangle;
20import org.eclipse.swt.widgets.Shell;
21import org.eclipse.ui.plugin.AbstractUIPlugin;
22
23/**
24 * {@link Dialog} that remembers location/size between usage sessions.
25 *
26 * @author scheglov_ke
27 * @coverage core.ui
28 */
29public abstract class ResizableDialog extends Dialog {
30  /**
31   * Key for accessing {@link Dialog} from its {@link Shell}.
32   */
33  public static final String KEY_DIALOG = "KEY_DIALOG";
34  ////////////////////////////////////////////////////////////////////////////
35  //
36  // Internal constants
37  //
38  ////////////////////////////////////////////////////////////////////////////
39  private static final String X = "x";
40  private static final String Y = "y";
41  private static final String WIDTH = "width";
42  private static final String HEIGHT = "height";
43  ////////////////////////////////////////////////////////////////////////////
44  //
45  // Instance fields
46  //
47  ////////////////////////////////////////////////////////////////////////////
48  private final AbstractUIPlugin m_plugin;
49
50  ////////////////////////////////////////////////////////////////////////////
51  //
52  // Constructor
53  //
54  ////////////////////////////////////////////////////////////////////////////
55  public ResizableDialog(Shell parentShell, AbstractUIPlugin plugin) {
56    super(parentShell);
57    m_plugin = plugin;
58    setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX);
59  }
60
61  ////////////////////////////////////////////////////////////////////////////
62  //
63  // Size
64  //
65  ////////////////////////////////////////////////////////////////////////////
66  @Override
67  protected Point getInitialSize() {
68    // track the current dialog bounds
69    installDialogBoundsTracker();
70    // answer the size from the previous incarnation
71    Point defaultSize = getDefaultSize();
72    if ((getShellStyle() & SWT.RESIZE) != 0) {
73      Rectangle oldBounds = loadBounds();
74      if (oldBounds != null) {
75        Rectangle displayBounds = getShell().getDisplay().getBounds();
76        int width = Math.min(displayBounds.width, Math.max(oldBounds.width, defaultSize.x));
77        int height = Math.min(displayBounds.height, Math.max(oldBounds.height, defaultSize.y));
78        return new Point(width, height);
79      }
80    }
81    // use default size
82    return defaultSize;
83  }
84
85  /**
86   * @return the default size of dialog.
87   */
88  protected Point getDefaultSize() {
89    return super.getInitialSize();
90  }
91
92  ////////////////////////////////////////////////////////////////////////////
93  //
94  // Location
95  //
96  ////////////////////////////////////////////////////////////////////////////
97  @Override
98  protected Point getInitialLocation(Point initialSize) {
99    Rectangle windowBounds;
100    {
101      Shell windowShell = m_plugin.getWorkbench().getActiveWorkbenchWindow().getShell();
102      windowBounds = windowShell.getBounds();
103    }
104    // answer the location from the previous incarnation
105    Rectangle bounds = loadBounds();
106    if (bounds != null) {
107      int x = bounds.x;
108      int y = bounds.y;
109      int maxX = windowBounds.x + windowBounds.width - initialSize.x;
110      int maxY = windowBounds.y + windowBounds.height - initialSize.y;
111      if (x > maxX) {
112        x = maxX;
113      }
114      if (y > maxY) {
115        y = maxY;
116      }
117      if (x < windowBounds.x) {
118        x = windowBounds.x;
119      }
120      if (y < windowBounds.y) {
121        y = windowBounds.y;
122      }
123      return new Point(x, y);
124    }
125    // default location - centered on workbench window
126    int x = windowBounds.x + (windowBounds.width - initialSize.x) / 2;
127    int y = windowBounds.y + (windowBounds.height - initialSize.y) / 2;
128    return new Point(x, y);
129  }
130
131  ////////////////////////////////////////////////////////////////////////////
132  //
133  // Bounds
134  //
135  ////////////////////////////////////////////////////////////////////////////
136  /**
137   * Loads bounds from {@link IDialogSettings}.
138   */
139  private Rectangle loadBounds() {
140    IDialogSettings settings = getDialogSettings();
141    try {
142      return new Rectangle(settings.getInt(X),
143          settings.getInt(Y),
144          settings.getInt(WIDTH),
145          settings.getInt(HEIGHT));
146    } catch (NumberFormatException e) {
147      return null;
148    }
149  }
150
151  /**
152   * Saves bounds to {@link IDialogSettings}.
153   */
154  private void saveBounds(Rectangle bounds) {
155    IDialogSettings settings = getDialogSettings();
156    settings.put(X, bounds.x);
157    settings.put(Y, bounds.y);
158    settings.put(WIDTH, bounds.width);
159    settings.put(HEIGHT, bounds.height);
160  }
161
162  /**
163   * @return the {@link IDialogSettings} for this dialog with this type.
164   */
165  protected IDialogSettings getDialogSettings() {
166    IDialogSettings settings = m_plugin.getDialogSettings();
167    String sectionName = getDialogSettingsSectionName();
168    if (settings.getSection(sectionName) == null) {
169      return settings.addNewSection(sectionName);
170    }
171    return settings.getSection(sectionName);
172  }
173
174  /**
175   * @return the name of section for dialog specific bounds. By default uses name of {@link Class},
176   *         but if same dialog is used for displaying different content, then may be overridden.
177   */
178  protected String getDialogSettingsSectionName() {
179    return getClass().getName();
180  }
181
182  ////////////////////////////////////////////////////////////////////////////
183  //
184  // Size tracking
185  //
186  ////////////////////////////////////////////////////////////////////////////
187  protected Rectangle cachedBounds;
188
189  private void installDialogBoundsTracker() {
190    getShell().addControlListener(new ControlListener() {
191      public void controlMoved(ControlEvent e) {
192        cachedBounds = getShell().getBounds();
193      }
194
195      public void controlResized(ControlEvent e) {
196        cachedBounds = getShell().getBounds();
197      }
198    });
199  }
200
201  @Override
202  public boolean close() {
203    boolean shellMaximized = getShell().getMaximized();
204    boolean closed = super.close();
205    if (closed && !shellMaximized && cachedBounds != null) {
206      saveBounds(cachedBounds);
207    }
208    return closed;
209  }
210
211  ////////////////////////////////////////////////////////////////////////////
212  //
213  // Shell
214  //
215  ////////////////////////////////////////////////////////////////////////////
216  @Override
217  protected void configureShell(Shell newShell) {
218    super.configureShell(newShell);
219    newShell.setData(KEY_DIALOG, this);
220  }
221}
222