root/dwt/widgets/Decorations.d

Revision 320:da968414c383, 62.0 kB (checked in by Frank Benoit <benoit@tionex.de>, 1 month ago)

Merge changes SWT 3.4.1

Line 
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 dwt.widgets.Decorations;
14
15
16 import dwt.DWT;
17 import dwt.DWTException;
18 import dwt.graphics.Image;
19 import dwt.graphics.ImageData;
20 import dwt.graphics.Point;
21 import dwt.graphics.Rectangle;
22 import dwt.internal.win32.OS;
23
24 import dwt.widgets.Canvas;
25 import dwt.widgets.Menu;
26 import dwt.widgets.Control;
27 import dwt.widgets.Button;
28 import dwt.widgets.Composite;
29 import dwt.widgets.Event;
30 import dwt.widgets.Shell;
31 import dwt.widgets.MenuItem;
32 import dwt.widgets.Display;
33
34 import dwt.dwthelper.utils;
35
36 /**
37  * Instances of this class provide the appearance and
38  * behavior of <code>Shells</code>, but are not top
39  * level shells or dialogs. Class <code>Shell</code>
40  * shares a significant amount of code with this class,
41  * and is a subclass.
42  * <p>
43  * IMPORTANT: This class was intended to be abstract and
44  * should <em>never</em> be referenced or instantiated.
45  * Instead, the class <code>Shell</code> should be used.
46  * </p>
47  * <p>
48  * Instances are always displayed in one of the maximized,
49  * minimized or normal states:
50  * <ul>
51  * <li>
52  * When an instance is marked as <em>maximized</em>, the
53  * window manager will typically resize it to fill the
54  * entire visible area of the display, and the instance
55  * is usually put in a state where it can not be resized
56  * (even if it has style <code>RESIZE</code>) until it is
57  * no longer maximized.
58  * </li><li>
59  * When an instance is in the <em>normal</em> state (neither
60  * maximized or minimized), its appearance is controlled by
61  * the style constants which were specified when it was created
62  * and the restrictions of the window manager (see below).
63  * </li><li>
64  * When an instance has been marked as <em>minimized</em>,
65  * its contents (client area) will usually not be visible,
66  * and depending on the window manager, it may be
67  * "iconified" (that is, replaced on the desktop by a small
68  * simplified representation of itself), relocated to a
69  * distinguished area of the screen, or hidden. Combinations
70  * of these changes are also possible.
71  * </li>
72  * </ul>
73  * </p>
74  * Note: The styles supported by this class must be treated
75  * as <em>HINT</em>s, since the window manager for the
76  * desktop on which the instance is visible has ultimate
77  * control over the appearance and behavior of decorations.
78  * For example, some window managers only support resizable
79  * windows and will always assume the RESIZE style, even if
80  * it is not set.
81  * <dl>
82  * <dt><b>Styles:</b></dt>
83  * <dd>BORDER, CLOSE, MIN, MAX, NO_TRIM, RESIZE, TITLE, ON_TOP, TOOL</dd>
84  * <dt><b>Events:</b></dt>
85  * <dd>(none)</dd>
86  * </dl>
87  * Class <code>DWT</code> provides two "convenience constants"
88  * for the most commonly required style combinations:
89  * <dl>
90  * <dt><code>SHELL_TRIM</code></dt>
91  * <dd>
92  * the result of combining the constants which are required
93  * to produce a typical application top level shell: (that
94  * is, <code>CLOSE | TITLE | MIN | MAX | RESIZE</code>)
95  * </dd>
96  * <dt><code>DIALOG_TRIM</code></dt>
97  * <dd>
98  * the result of combining the constants which are required
99  * to produce a typical application dialog shell: (that
100  * is, <code>TITLE | CLOSE | BORDER</code>)
101  * </dd>
102  * </dl>
103  * <p>
104  * IMPORTANT: This class is intended to be subclassed <em>only</em>
105  * within the DWT implementation.
106  * </p>
107  *
108  * @see #getMinimized
109  * @see #getMaximized
110  * @see Shell
111  * @see DWT
112  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
113  */
114
115 public class Decorations : Canvas {
116
117     alias Canvas.setBounds setBounds;
118     alias Canvas.setParent setParent;
119     alias Canvas.setSavedFocus setSavedFocus;
120     alias Canvas.sort sort;
121     alias Canvas.windowProc windowProc;
122
123     Image image, smallImage, largeImage;
124     Image [] images;
125     Menu menuBar;
126     Menu [] menus;
127     Control savedFocus;
128     Button defaultButton, saveDefault;
129     int swFlags;
130     HACCEL hAccel;
131     int nAccel;
132     bool moved, resized, opened;
133     int oldX = OS.CW_USEDEFAULT, oldY = OS.CW_USEDEFAULT;
134     int oldWidth = OS.CW_USEDEFAULT, oldHeight = OS.CW_USEDEFAULT;
135
136 /**
137  * Prevents uninitialized instances from being created outside the package.
138  */
139 this () {
140 }
141
142 /**
143  * Constructs a new instance of this class given its parent
144  * and a style value describing its behavior and appearance.
145  * <p>
146  * The style value is either one of the style constants defined in
147  * class <code>DWT</code> which is applicable to instances of this
148  * class, or must be built by <em>bitwise OR</em>'ing together
149  * (that is, using the <code>int</code> "|" operator) two or more
150  * of those <code>DWT</code> style constants. The class description
151  * lists the style constants that are applicable to the class.
152  * Style bits are also inherited from superclasses.
153  * </p>
154  *
155  * @param parent a composite control which will be the parent of the new instance (cannot be null)
156  * @param style the style of control to construct
157  *
158  * @exception IllegalArgumentException <ul>
159  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
160  * </ul>
161  * @exception DWTException <ul>
162  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
163  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
164  * </ul>
165  *
166  * @see DWT#BORDER
167  * @see DWT#CLOSE
168  * @see DWT#MIN
169  * @see DWT#MAX
170  * @see DWT#RESIZE
171  * @see DWT#TITLE
172  * @see DWT#NO_TRIM
173  * @see DWT#SHELL_TRIM
174  * @see DWT#DIALOG_TRIM
175  * @see DWT#ON_TOP
176  * @see DWT#TOOL
177  * @see Widget#checkSubclass
178  * @see Widget#getStyle
179  */
180 public this (Composite parent, int style) {
181     super (parent, checkStyle (style));
182 }
183
184 void _setMaximized (bool maximized) {
185     swFlags = maximized ? OS.SW_SHOWMAXIMIZED : OS.SW_RESTORE;
186     static if (OS.IsWinCE) {
187         /*
188         * Note: WinCE does not support SW_SHOWMAXIMIZED and SW_RESTORE. The
189         * workaround is to resize the window to fit the parent client area.
190         */
191         if (maximized) {
192             RECT rect;
193             OS.SystemParametersInfo (OS.SPI_GETWORKAREA, 0, &rect, 0);
194             int width = rect.right - rect.left, height = rect.bottom - rect.top;
195             if (OS.IsPPC) {
196                 /* Leave space for the menu bar */
197                 if (menuBar !is null) {
198                     auto hwndCB = menuBar.hwndCB;
199                     RECT rectCB;
200                     OS.GetWindowRect (hwndCB, &rectCB);
201                     height -= rectCB.bottom - rectCB.top;
202                 }
203             }
204             int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
205             SetWindowPos (handle, null, rect.left, rect.top, width, height, flags);
206         }
207     } else {
208         if (!OS.IsWindowVisible (handle)) return;
209         if (maximized is OS.IsZoomed (handle)) return;
210         OS.ShowWindow (handle, swFlags);
211         OS.UpdateWindow (handle);
212     }
213 }
214
215 void _setMinimized (bool minimized) {
216     static if (OS.IsWinCE) return;
217     swFlags = minimized ? OS.SW_SHOWMINNOACTIVE : OS.SW_RESTORE;
218     if (!OS.IsWindowVisible (handle)) return;
219     if (minimized is OS.IsIconic (handle)) return;
220     int flags = swFlags;
221     if (flags is OS.SW_SHOWMINNOACTIVE && handle is OS.GetActiveWindow ()) {
222         flags = OS.SW_MINIMIZE;
223     }
224     OS.ShowWindow (handle, flags);
225     OS.UpdateWindow (handle);
226 }
227
228 void addMenu (Menu menu) {
229     if (menus is null) menus = new Menu [4];
230     for (int i=0; i<menus.length; i++) {
231         if (menus [i] is null) {
232             menus [i] = menu;
233             return;
234         }
235     }
236     Menu [] newMenus = new Menu [menus.length + 4];
237     newMenus [menus.length] = menu;
238     System.arraycopy (menus, 0, newMenus, 0, menus.length);
239     menus = newMenus;
240 }
241
242 void bringToTop () {
243     /*
244     * This code is intentionally commented.  On some platforms,
245     * the ON_TOP style creates a shell that will stay on top
246     * of every other shell on the desktop.  Using SetWindowPos ()
247     * with HWND_TOP caused problems on Windows 98 so this code is
248     * commented out until this functionality is specified and
249     * the problems are fixed.
250     */
251 //  if ((style & DWT.ON_TOP) !is 0) {
252 //      int flags = OS.SWP_NOSIZE | OS.SWP_NOMOVE | OS.SWP_NOACTIVATE;
253 //      OS.SetWindowPos (handle, OS.HWND_TOP, 0, 0, 0, 0, flags);
254 //  } else {
255         OS.BringWindowToTop (handle);
256         // widget could be disposed at this point
257 //  }
258 }
259
260 static int checkStyle (int style) {
261     if ((style & DWT.NO_TRIM) !is 0) {
262         style &= ~(DWT.CLOSE | DWT.TITLE | DWT.MIN | DWT.MAX | DWT.RESIZE | DWT.BORDER);
263     }
264     static if (OS.IsWinCE) {
265         /*
266         * Feature in WinCE PPC.  WS_MINIMIZEBOX or WS_MAXIMIZEBOX
267         * are not supposed to be used.  If they are, the result
268         * is a button which does not repaint correctly.  The fix
269         * is to remove this style.
270         */
271         if ((style & DWT.MIN) !is 0) style &= ~DWT.MIN;
272         if ((style & DWT.MAX) !is 0) style &= ~DWT.MAX;
273         return style;
274     }
275     if ((style & (DWT.MENU | DWT.MIN | DWT.MAX | DWT.CLOSE)) !is 0) {
276         style |= DWT.TITLE;
277     }
278
279     /*
280     * If either WS_MINIMIZEBOX or WS_MAXIMIZEBOX are set,
281     * we must also set WS_SYSMENU or the buttons will not
282     * appear.
283     */
284     if ((style & (DWT.MIN | DWT.MAX)) !is 0) style |= DWT.CLOSE;
285
286     /*
287     * Both WS_SYSMENU and WS_CAPTION must be set in order
288     * to for the system menu to appear.
289     */
290     if ((style & DWT.CLOSE) !is 0) style |= DWT.TITLE;
291
292     /*
293     * Bug in Windows.  The WS_CAPTION style must be
294     * set when the window is resizable or it does not
295     * draw properly.
296     */
297     /*
298     * This code is intentionally commented.  It seems
299     * that this problem originally in Windows 3.11,
300     * has been fixed in later versions.  Because the
301     * exact nature of the drawing problem is unknown,
302     * keep the commented code around in case it comes
303     * back.
304     */
305 //  if ((style & DWT.RESIZE) !is 0) style |= DWT.TITLE;
306
307     return style;
308 }
309
310 override void checkBorder () {
311     /* Do nothing */
312 }
313
314 void checkComposited (Composite parent) {
315     /* Do nothing */
316 }
317
318 override void checkOpened () {
319     if (!opened) resized = false;
320 }
321
322 override protected void checkSubclass () {
323     if (!isValidSubclass ()) error (DWT.ERROR_INVALID_SUBCLASS);
324 }
325
326 override override int callWindowProc (HWND hwnd, int msg, int wParam, int lParam) {
327     if (handle is null) return 0;
328     return OS.DefMDIChildProc (hwnd, msg, wParam, lParam);
329 }
330
331 void closeWidget () {
332     Event event = new Event ();
333     sendEvent (DWT.Close, event);
334     if (event.doit && !isDisposed ()) dispose ();
335 }
336
337 int compare (ImageData data1, ImageData data2, int width, int height, int depth) {
338     int value1 = Math.abs (data1.width - width), value2 = Math.abs (data2.width - width);
339     if (value1 is value2) {
340         int transparent1 = data1.getTransparencyType ();
341         int transparent2 = data2.getTransparencyType ();
342         if (transparent1 is transparent2) {
343             if (data1.depth is data2.depth) return 0;
344             return data1.depth > data2.depth && data1.depth <= depth ? -1 : 1;
345         }
346         if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (5, 1)) {
347             if (transparent1 is DWT.TRANSPARENCY_ALPHA) return -1;
348             if (transparent2 is DWT.TRANSPARENCY_ALPHA) return 1;
349         }
350         if (transparent1 is DWT.TRANSPARENCY_MASK) return -1;
351         if (transparent2 is DWT.TRANSPARENCY_MASK) return 1;
352         if (transparent1 is DWT.TRANSPARENCY_PIXEL) return -1;
353         if (transparent2 is DWT.TRANSPARENCY_PIXEL) return 1;
354         return 0;
355     }
356     return value1 < value2 ? -1 : 1;
357 }
358
359 override Control computeTabGroup () {
360     return this;
361 }
362
363 override Control computeTabRoot () {
364     return this;
365 }
366
367 override public Rectangle computeTrim (int x, int y, int width, int height) {
368     checkWidget ();
369
370     /* Get the size of the trimmings */
371     RECT rect;
372     OS.SetRect (&rect, x, y, x + width, y + height);
373     int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
374     int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
375     bool hasMenu = OS.IsWinCE ? false : OS.GetMenu (handle) !is null;
376     OS.AdjustWindowRectEx (&rect, bits1, hasMenu, bits2);
377
378     /* Get the size of the scroll bars */
379     if (horizontalBar !is null) rect.bottom += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
380     if (verticalBar !is null) rect.right += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
381
382     /* Compute the height of the menu bar */
383     if (hasMenu) {
384         RECT testRect;
385         OS.SetRect (&testRect, 0, 0, rect.right - rect.left, rect.bottom - rect.top);
386         OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, &testRect);
387         while ((testRect.bottom - testRect.top) < height) {
388             if (testRect.bottom - testRect.top is 0) break;
389             rect.top -= OS.GetSystemMetrics (OS.SM_CYMENU) - OS.GetSystemMetrics (OS.SM_CYBORDER);
390             OS.SetRect (&testRect, 0, 0, rect.right - rect.left, rect.bottom - rect.top);
391             OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, &testRect);
392         }
393     }
394     return new Rectangle (rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
395 }
396
397 void createAccelerators () {
398     hAccel = null;
399     nAccel = 0;
400     int maxAccel = 0;
401     MenuItem [] items = display.items;
402     if (menuBar is null || items is null) {
403         if (!OS.IsPPC) return;
404         maxAccel = 1;
405     } else {
406         maxAccel = OS.IsPPC ? items.length + 1 : items.length;
407     }
408     ACCEL accel;
409     byte [] buffer1 = new byte [ACCEL.sizeof];
410     byte [] buffer2 = new byte [maxAccel * ACCEL.sizeof];
411     if (menuBar !is null && items !is null) {
412         for (int i=0; i<items.length; i++) {
413             MenuItem item = items [i];
414             if (item !is null && item.accelerator !is 0) {
415                 Menu menu = item.parent;
416                 if (menu.parent is this) {
417                     while (menu !is null && menu !is menuBar) {
418                         menu = menu.getParentMenu ();
419                     }
420                     if (menu is menuBar && item.fillAccel (&accel)) {
421                         *cast(ACCEL*)buffer1.ptr = accel;
422                         //OS.MoveMemory (buffer1, accel, ACCEL.sizeof);
423                         System.arraycopy (buffer1, 0, buffer2, nAccel * ACCEL.sizeof, ACCEL.sizeof);
424                         nAccel++;
425                     }
426                 }
427             }
428         }
429     }
430     if (OS.IsPPC) {
431         /*
432         * Note on WinCE PPC.  Close the shell when user taps CTRL-Q.
433         * IDOK represents the "Done Button" which also closes the shell.
434         */
435         accel.fVirt = cast(byte) (OS.FVIRTKEY | OS.FCONTROL);
436         accel.key = cast(short) 'Q';
437         accel.cmd = cast(short) OS.IDOK;
438         *cast(ACCEL*)buffer1.ptr = accel;
439         //OS.MoveMemory (buffer1, accel, ACCEL.sizeof);
440         System.arraycopy (buffer1, 0, buffer2, nAccel * ACCEL.sizeof, ACCEL.sizeof);
441         nAccel++;
442     }
443     if (nAccel !is 0) hAccel = OS.CreateAcceleratorTable ( cast(ACCEL*)buffer2.ptr, nAccel);
444 }
445
446 override void createHandle () {
447     super.createHandle ();
448     if (parent !is null || ((style & DWT.TOOL) !is 0)) {
449         setParent ();
450         setSystemMenu ();
451     }
452 }
453
454 override void createWidget () {
455     super.createWidget ();
456     swFlags = OS.IsWinCE ? OS.SW_SHOWMAXIMIZED : OS.SW_SHOWNOACTIVATE;
457     hAccel = cast(HACCEL)-1;
458 }
459
460 void destroyAccelerators () {
461     if (hAccel !is null && hAccel !is cast(HACCEL)-1) OS.DestroyAcceleratorTable (hAccel);
462     hAccel = cast(HACCEL)-1;
463 }
464
465 override public void dispose () {
466     if (isDisposed()) return;
467     if (!isValidThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS);
468     if (!(cast(Shell)this)) {
469         if (!traverseDecorations (true)) {
470             Shell shell = getShell ();
471             shell.setFocus ();
472         }
473         setVisible (false);
474     }
475     super.dispose ();
476 }
477
478 Menu findMenu (HMENU hMenu) {
479     if (menus is null) return null;
480     for (int i=0; i<menus.length; i++) {
481         Menu menu = menus [i];
482         if (menu !is null && hMenu is menu.handle) return menu;
483     }
484     return null;
485 }
486
487 void fixDecorations (Decorations newDecorations, Control control, Menu [] menus) {
488     if (this is newDecorations) return;
489     if (control is savedFocus) savedFocus = null;
490     if (control is defaultButton) defaultButton = null;
491     if (control is saveDefault) saveDefault = null;
492     if (menus is null) return;
493     Menu menu = control.menu;
494     if (menu !is null) {
495         int index = 0;
496         while (index <menus.length) {
497             if (menus [index] is menu) {
498                 control.setMenu (null);
499                 return;
500             }
501             index++;
502         }
503         menu.fixMenus (newDecorations);
504         destroyAccelerators ();
505         newDecorations.destroyAccelerators ();
506     }
507 }
508
509 override public Rectangle getBounds () {
510     checkWidget ();
511     static if (!OS.IsWinCE) {
512         if (OS.IsIconic (handle)) {
513             WINDOWPLACEMENT lpwndpl;
514             lpwndpl.length = WINDOWPLACEMENT.sizeof;
515             OS.GetWindowPlacement (handle, &lpwndpl);
516             int width = lpwndpl.rcNormalPosition.right - lpwndpl.rcNormalPosition.left;
517             int height = lpwndpl.rcNormalPosition.bottom - lpwndpl.rcNormalPosition.top;
518             return new Rectangle (lpwndpl.rcNormalPosition.left, lpwndpl.rcNormalPosition.top, width, height);
519         }
520     }
521     return super.getBounds ();
522 }
523
524 override public Rectangle getClientArea () {
525     checkWidget ();
526     /*
527     * Note: The CommandBar is part of the client area,
528     * not the trim.  Applications don't expect this so
529     * subtract the height of the CommandBar.
530     */
531     static if (OS.IsHPC) {
532         Rectangle rect = super.getClientArea ();
533         if (menuBar !is null) {
534             auto hwndCB = menuBar.hwndCB;
535             int height = OS.CommandBar_Height (hwndCB);
536             rect.y += height;
537             rect.height = Math.max (0, rect.height - height);
538         }
539         return rect;
540     }
541     static if (!OS.IsWinCE) {
542         if (OS.IsIconic (handle)) {
543             WINDOWPLACEMENT lpwndpl;
544             lpwndpl.length = WINDOWPLACEMENT.sizeof;
545             OS.GetWindowPlacement (handle, &lpwndpl);
546             int width = lpwndpl.rcNormalPosition.right - lpwndpl.rcNormalPosition.left;
547             int height = lpwndpl.rcNormalPosition.bottom - lpwndpl.rcNormalPosition.top;
548             /*
549             * Feature in Windows.  For some reason WM_NCCALCSIZE does
550             * not compute the client area when the window is minimized.
551             * The fix is to compute it using AdjustWindowRectEx() and
552             * GetSystemMetrics().
553             *
554             * NOTE: This code fails to compute the correct client area
555             * for a minimized window where the menu bar would wrap were
556             * the window restored.  There is no fix for this problem at
557             * this time.
558             */
559             if (horizontalBar !is null) width -= OS.GetSystemMetrics (OS.SM_CYHSCROLL);
560             if (verticalBar !is null) height -= OS.GetSystemMetrics (OS.SM_CXVSCROLL);
561             RECT rect;
562             int bits1 = OS.GetWindowLong (handle, OS.GWL_STYLE);
563             int bits2 = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
564             bool hasMenu = OS.IsWinCE ? false : OS.GetMenu (handle) !is null;
565             OS.AdjustWindowRectEx (&rect, bits1, hasMenu, bits2);
566             width = Math.max (0, width - (rect.right - rect.left));
567             height = Math.max (0, height - (