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 *******************************************************************************/ 11 package org.eclipse.wb.internal.core.utils.ui.dialogs; 12 13 import org.eclipse.jface.dialogs.Dialog; 14 import org.eclipse.jface.dialogs.IDialogSettings; 15 import org.eclipse.swt.SWT; 16 import org.eclipse.swt.events.ControlEvent; 17 import org.eclipse.swt.events.ControlListener; 18 import org.eclipse.swt.graphics.Point; 19 import org.eclipse.swt.graphics.Rectangle; 20 import org.eclipse.swt.widgets.Shell; 21 import 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 */ 29 public 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 //////////////////////////////////////////////////////////////////////////// ResizableDialog(Shell parentShell, AbstractUIPlugin plugin)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 getInitialSize()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 */ getDefaultSize()88 protected Point getDefaultSize() { 89 return super.getInitialSize(); 90 } 91 92 //////////////////////////////////////////////////////////////////////////// 93 // 94 // Location 95 // 96 //////////////////////////////////////////////////////////////////////////// 97 @Override getInitialLocation(Point initialSize)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 */ loadBounds()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 */ saveBounds(Rectangle bounds)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 */ getDialogSettings()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 */ getDialogSettingsSectionName()178 protected String getDialogSettingsSectionName() { 179 return getClass().getName(); 180 } 181 182 //////////////////////////////////////////////////////////////////////////// 183 // 184 // Size tracking 185 // 186 //////////////////////////////////////////////////////////////////////////// 187 protected Rectangle cachedBounds; 188 installDialogBoundsTracker()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 close()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 configureShell(Shell newShell)217 protected void configureShell(Shell newShell) { 218 super.configureShell(newShell); 219 newShell.setData(KEY_DIALOG, this); 220 } 221 } 222