1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
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  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.custom.ControlEditor;
14 
15 import java.lang.all;
16 
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.graphics.Rectangle;
19 import org.eclipse.swt.widgets.Composite;
20 import org.eclipse.swt.widgets.Control;
21 import org.eclipse.swt.widgets.Event;
22 import org.eclipse.swt.widgets.Listener;
23 import org.eclipse.swt.widgets.ScrollBar;
24 
25 /**
26 *
27 * A ControlEditor is a manager for a Control that appears above a composite and tracks with the
28 * moving and resizing of that composite.  It can be used to display one control above
29 * another control.  This could be used when editing a control that does not have editing
30 * capabilities by using a text editor or for launching a dialog by placing a button
31 * above a control.
32 *
33 * <p> Here is an example of using a ControlEditor:
34 *
35 * <code><pre>
36 * Canvas canvas = new Canvas(shell, SWT.BORDER);
37 * canvas.setBounds(10, 10, 300, 300);
38 * Color color = new Color(null, 255, 0, 0);
39 * canvas.setBackground(color);
40 * ControlEditor editor = new ControlEditor (canvas);
41 * // The editor will be a button in the bottom right corner of the canvas.
42 * // When selected, it will launch a Color dialog that will change the background
43 * // of the canvas.
44 * Button button = new Button(canvas, SWT.PUSH);
45 * button.setText("Select Color...");
46 * button.addSelectionListener (new SelectionAdapter() {
47 *   public void widgetSelected(SelectionEvent e) {
48 *       ColorDialog dialog = new ColorDialog(shell);
49 *       dialog.open();
50 *       RGB rgb = dialog.getRGB();
51 *       if (rgb !is null) {
52 *           if (color !is null) color.dispose();
53 *           color = new Color(null, rgb);
54 *           canvas.setBackground(color);
55 *       }
56 *
57 *   }
58 * });
59 *
60 * editor.horizontalAlignment = SWT.RIGHT;
61 * editor.verticalAlignment = SWT.BOTTOM;
62 * editor.grabHorizontal = false;
63 * editor.grabVertical = false;
64 * Point size = button.computeSize(SWT.DEFAULT, SWT.DEFAULT);
65 * editor.minimumWidth = size.x;
66 * editor.minimumHeight = size.y;
67 * editor.setEditor (button);
68 * </pre></code>
69 *
70 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
71 */
72 public class ControlEditor {
73 
74     /**
75     * Specifies how the editor should be aligned relative to the control.  Allowed values
76     * are SWT.LEFT, SWT.RIGHT and SWT.CENTER.  The default value is SWT.CENTER.
77     */
78     public int horizontalAlignment = SWT.CENTER;
79 
80     /**
81     * Specifies whether the editor should be sized to use the entire width of the control.
82     * True means resize the editor to the same width as the cell.  False means do not adjust
83     * the width of the editor.  The default value is false.
84     */
85     public bool grabHorizontal = false;
86 
87     /**
88     * Specifies the minimum width the editor can have.  This is used in association with
89     * a true value of grabHorizontal.  If the cell becomes smaller than the minimumWidth, the
90     * editor will not made smaller than the minimum width value.  The default value is 0.
91     */
92     public int minimumWidth = 0;
93 
94     /**
95     * Specifies how the editor should be aligned relative to the control.  Allowed values
96     * are SWT.TOP, SWT.BOTTOM and SWT.CENTER.  The default value is SWT.CENTER.
97     */
98     public int verticalAlignment = SWT.CENTER;
99 
100     /**
101     * Specifies whether the editor should be sized to use the entire height of the control.
102     * True means resize the editor to the same height as the underlying control.  False means do not adjust
103     * the height of the editor. The default value is false.
104     */
105     public bool grabVertical = false;
106 
107     /**
108     * Specifies the minimum height the editor can have.  This is used in association with
109     * a true value of grabVertical.  If the control becomes smaller than the minimumHeight, the
110     * editor will not made smaller than the minimum height value.  The default value is 0.
111     */
112     public int minimumHeight = 0;
113 
114     Composite parent;
115     Control editor;
116     private bool hadFocus;
117     private Listener controlListener;
118     private Listener scrollbarListener;
119 
120     private const static int [] EVENTS = [SWT.KeyDown, SWT.KeyUp, SWT.MouseDown, SWT.MouseUp, SWT.Resize];
121 /**
122 * Creates a ControlEditor for the specified Composite.
123 *
124 * @param parent the Composite above which this editor will be displayed
125 *
126 */
127 public this (Composite parent) {
128     this.parent = parent;
129 
130     controlListener = new class() Listener {
131         public void handleEvent(Event e) {
132             layout ();
133         }
134     };
135     for (int i=0; i<EVENTS.length; i++) {
136         parent.addListener (EVENTS [i], controlListener);
137     }
138 
139     scrollbarListener = new class() Listener {
140         public void handleEvent(Event e) {
141             scroll (e);
142         }
143     };
144     ScrollBar hBar = parent.getHorizontalBar ();
145     if (hBar !is null) hBar.addListener (SWT.Selection, scrollbarListener);
146     ScrollBar vBar = parent.getVerticalBar ();
147     if (vBar !is null) vBar.addListener (SWT.Selection, scrollbarListener);
148 }
149 Rectangle computeBounds () {
150     Rectangle clientArea = parent.getClientArea();
151     Rectangle editorRect = new Rectangle(clientArea.x, clientArea.y, minimumWidth, minimumHeight);
152 
153     if (grabHorizontal)
154         editorRect.width = Math.max(clientArea.width, minimumWidth);
155 
156     if (grabVertical)
157         editorRect.height = Math.max(clientArea.height, minimumHeight);
158 
159     switch (horizontalAlignment) {
160         case SWT.RIGHT:
161             editorRect.x += clientArea.width - editorRect.width;
162             break;
163         case SWT.LEFT:
164             // do nothing - clientArea.x is the right answer
165             break;
166         default:
167             // default is CENTER
168             editorRect.x += (clientArea.width - editorRect.width)/2;
169     }
170 
171     switch (verticalAlignment) {
172         case SWT.BOTTOM:
173             editorRect.y += clientArea.height - editorRect.height;
174             break;
175         case SWT.TOP:
176             // do nothing - clientArea.y is the right answer
177             break;
178         default :
179             // default is CENTER
180             editorRect.y += (clientArea.height - editorRect.height)/2;
181     }
182 
183 
184     return editorRect;
185 
186 }
187 /**
188  * Removes all associations between the Editor and the underlying composite.  The
189  * composite and the editor Control are <b>not</b> disposed.
190  */
191 public void dispose () {
192     if (parent !is null && !parent.isDisposed()) {
193         for (int i=0; i<EVENTS.length; i++) {
194             parent.removeListener (EVENTS [i], controlListener);
195         }
196         ScrollBar hBar = parent.getHorizontalBar ();
197         if (hBar !is null) hBar.removeListener (SWT.Selection, scrollbarListener);
198         ScrollBar vBar = parent.getVerticalBar ();
199         if (vBar !is null) vBar.removeListener (SWT.Selection, scrollbarListener);
200     }
201 
202     parent = null;
203     editor = null;
204     hadFocus = false;
205     controlListener = null;
206     scrollbarListener = null;
207 }
208 /**
209 * Returns the Control that is displayed above the composite being edited.
210 *
211 * @return the Control that is displayed above the composite being edited
212 */
213 public Control getEditor () {
214     return editor;
215 }
216 /**
217  * Lays out the control within the underlying composite.  This
218  * method should be called after changing one or more fields to
219  * force the Editor to resize.
220  *
221  * @since 2.1
222  */
223 public void layout () {
224     if (editor is null || editor.isDisposed()) return;
225     if (editor.getVisible ()) {
226         hadFocus = editor.isFocusControl();
227     } // this doesn't work because
228       // resizing the column takes the focus away
229       // before we get here
230     editor.setBounds (computeBounds ());
231     if (hadFocus) {
232         if (editor is null || editor.isDisposed()) return;
233         editor.setFocus ();
234     }
235 }
236 void scroll (Event e) {
237     if (editor is null || editor.isDisposed()) return;
238     layout();
239 }
240 /**
241 * Specify the Control that is to be displayed.
242 *
243 * <p>Note: The Control provided as the editor <b>must</b> be created with its parent
244 * being the Composite specified in the ControlEditor constructor.
245 *
246 * @param editor the Control that is displayed above the composite being edited
247 */
248 public void setEditor (Control editor) {
249 
250     if (editor is null) {
251         // this is the case where the caller is setting the editor to be blank
252         // set all the values accordingly
253         this.editor = null;
254         return;
255     }
256 
257     this.editor = editor;
258     layout();
259     if (this.editor is null || this.editor.isDisposed()) return;
260     editor.setVisible(true);
261 }
262 }