root/dwt/graphics/Device.d

Revision 246:fd9c62a2998e, 31.1 kB (checked in by Frank Benoit <benoit@tionex.de>, 6 months ago)

Updater SWT 3.4M7 to 3.4

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.graphics.Device;
14
15 import dwt.DWT;
16 import dwt.DWTException;
17 import dwt.internal.gdip.Gdip;
18 import dwt.internal.win32.OS;
19
20 import dwt.graphics.Cursor;
21 import dwt.graphics.GC;
22 import dwt.graphics.Image;
23 import dwt.graphics.Path;
24 import dwt.graphics.Pattern;
25 import dwt.graphics.Region;
26 import dwt.graphics.TextLayout;
27 import dwt.graphics.Transform;
28 import dwt.graphics.Drawable;
29 import dwt.graphics.DeviceData;
30 import dwt.graphics.Rectangle;
31 import dwt.graphics.FontData;
32 import dwt.graphics.Font;
33 import dwt.graphics.GCData;
34 import dwt.graphics.Color;
35
36 import dwt.dwthelper.Runnable;
37 import dwt.dwthelper.System;
38
39 import dwt.dwthelper.utils;
40 import tango.core.Exception;
41 import tango.util.Convert;
42 import tango.io.Stdout;
43
44 /**
45  * This class is the abstract superclass of all device objects,
46  * such as the Display device and the Printer device. Devices
47  * can have a graphics context (GC) created for them, and they
48  * can be drawn on by sending messages to the associated GC.
49  *
50  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
51  */
52 public abstract class Device : Drawable {
53
54     private struct CallbackData {
55         Device device;
56         bool scalable;
57     }
58
59     /* Debugging */
60     public static bool DEBUG = true;
61     bool debug_;
62     bool tracking;
63     Exception [] errors;
64     Object [] objects;
65     Object trackingLock;
66
67     /**
68      * Palette
69      * (Warning: This field is platform dependent)
70      * <p>
71      * <b>IMPORTANT:</b> This field is <em>not</em> part of the DWT
72      * public API. It is marked public only so that it can be shared
73      * within the packages provided by DWT. It is not available on all
74      * platforms and should never be accessed from application code.
75      * </p>
76      */
77     public HPALETTE hPalette;
78     int [] colorRefCount;
79
80     /* System Font */
81     Font systemFont;
82
83     /* Font Enumeration */
84     int nFonts = 256;
85     LOGFONT* [] logFonts;
86     TEXTMETRIC metrics;
87     int[] pixels;
88
89     /* Scripts */
90     SCRIPT_PROPERTIES*[] scripts;
91
92     /* Advanced Graphics */
93     ULONG_PTR gdipToken;
94
95     bool disposed;
96
97     /*
98     * TEMPORARY CODE. When a graphics object is
99     * created and the device parameter is null,
100     * the current Display is used. This presents
101     * a problem because DWT graphics does not
102     * reference classes in DWT widgets. The correct
103     * fix is to remove this feature. Unfortunately,
104     * too many application programs rely on this
105     * feature.
106     *
107     * This code will be removed in the future.
108     */
109     protected static Device CurrentDevice;
110     protected static Runnable DeviceFinder;
111
112
113 /*
114 * TEMPORARY CODE.
115 */
116 static synchronized Device getDevice () {
117     if (DeviceFinder !is null) DeviceFinder.run();
118     Device device = CurrentDevice;
119     CurrentDevice = null;
120     return device;
121 }
122
123 /**
124  * Constructs a new instance of this class.
125  * <p>
126  * You must dispose the device when it is no longer required.
127  * </p>
128  *
129  * @see #create
130  * @see #init
131  *
132  * @since 3.1
133  */
134 public this() {
135     this(null);
136 }
137
138 /**
139  * Constructs a new instance of this class.
140  * <p>
141  * You must dispose the device when it is no longer required.
142  * </p>
143  *
144  * @param data the DeviceData which describes the receiver
145  *
146  * @see #create
147  * @see #init
148  * @see DeviceData
149  */
150 public this(DeviceData data) {
151     synchronized (Device.classinfo) {
152         debug_ = DEBUG;
153         tracking = DEBUG;
154         if (data !is null) {
155             debug_ = data.debug_;
156             tracking = data.tracking;
157         }
158         if (tracking) {
159             errors = new Exception [128];
160             objects = new Object [128];
161             trackingLock = new Object ();
162         }
163         create (data);
164         init_ ();
165         gdipToken = 0;
166     }
167 }
168
169 /**
170  * Throws an <code>DWTException</code> if the receiver can not
171  * be accessed by the caller. This may include both checks on
172  * the state of the receiver and more generally on the entire
173  * execution context. This method <em>should</em> be called by
174  * device implementors to enforce the standard DWT invariants.
175  * <p>
176  * Currently, it is an error to invoke any method (other than
177  * <code>isDisposed()</code> and <code>dispose()</code>) on a
178  * device that has had its <code>dispose()</code> method called.
179  * </p><p>
180  * In future releases of DWT, there may be more or fewer error
181  * checks and exceptions may be thrown for different reasons.
182  * <p>
183  *
184  * @exception DWTException <ul>
185  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
186  * </ul>
187  */
188 protected void checkDevice () {
189     if (disposed) DWT.error(DWT.ERROR_DEVICE_DISPOSED);
190 }
191
192 void checkGDIP() {
193     if (gdipToken) return;
194     int oldErrorMode = 0;
195     static if (!OS.IsWinCE) oldErrorMode = OS.SetErrorMode (OS.SEM_FAILCRITICALERRORS);
196     try {
197         ULONG_PTR token;
198         GdiplusStartupInput input;
199         input.GdiplusVersion = 1;
200         if (Gdip.GdiplusStartup ( &token, &input, null ) is 0) {
201             gdipToken = token;
202         }
203     } catch (Exception t) {
204         DWT.error (DWT.ERROR_NO_GRAPHICS_LIBRARY, t, " [GDI+ is required]"); //$NON-NLS-1$
205     } finally {
206         if (!OS.IsWinCE) OS.SetErrorMode (oldErrorMode);
207     }
208 }
209
210 /**
211  * Creates the device in the operating system.  If the device
212  * does not have a handle, this method may do nothing depending
213  * on the device.
214  * <p>
215  * This method is called before <code>init</code>.
216  * </p><p>
217  * Subclasses are supposed to reimplement this method and not
218  * call the <code>super</code> implementation.
219  * </p>
220  *
221  * @param data the DeviceData which describes the receiver
222  *
223  * @see #init
224  */
225 protected void create (DeviceData data) {
226 }
227
228 int computePixels(float height) {
229     auto hDC = internal_new_GC (null);
230     int pixels = -cast(int)(0.5f + (height * OS.GetDeviceCaps(hDC, OS.LOGPIXELSY) / 72f));
231     internal_dispose_GC (hDC, null);
232     return pixels;
233 }
234
235 float computePoints(LOGFONT* logFont, HFONT hFont) {
236     auto hDC = internal_new_GC (null);
237     int logPixelsY = OS.GetDeviceCaps(hDC, OS.LOGPIXELSY);
238     int pixels = 0;
239     if (logFont.lfHeight > 0) {
240         /*
241          * Feature in Windows. If the lfHeight of the LOGFONT structure
242          * is positive, the lfHeight measures the height of the entire
243          * cell, including internal leading, in logical units. Since the
244          * height of a font in points does not include the internal leading,
245          * we must subtract the internal leading, which requires a TEXTMETRIC.
246          */
247         auto oldFont = OS.SelectObject(hDC, hFont);
248         TEXTMETRIC* lptm = new TEXTMETRIC();
249         OS.GetTextMetrics(hDC, lptm);
250         OS.SelectObject(hDC, oldFont);
251         pixels = logFont.lfHeight - lptm.tmInternalLeading;
252     } else {
253         pixels = -logFont.lfHeight;
254     }
255     internal_dispose_GC (hDC, null);
256     return pixels * 72f / logPixelsY;
257 }
258
259 /**
260  * Destroys the device in the operating system and releases
261  * the device's handle.  If the device does not have a handle,
262  * this method may do nothing depending on the device.
263  * <p>
264  * This method is called after <code>release</code>.
265  * </p><p>
266  * Subclasses are supposed to reimplement this method and not
267  * call the <code>super</code> implementation.
268  * </p>
269  *
270  * @see #dispose
271  * @see #release
272  */
273 protected void destroy () {
274 }
275
276 /**
277  * Disposes of the operating system resources associated with
278  * the receiver. After this method has been invoked, the receiver
279  * will answer <code>true</code> when sent the message
280  * <code>isDisposed()</code>.
281  *
282  * @see #release
283  * @see #destroy
284  * @see #checkDevice
285  */
286 public void dispose () {
287     synchronized (Device.classinfo) {
288         if (isDisposed()) return;
289         checkDevice ();
290         release ();
291         destroy ();
292         disposed = true;
293         if (tracking) {
294             synchronized (trackingLock) {
295                 printErrors ();
296                 objects = null;
297                 errors = null;
298                 trackingLock = null;
299             }
300         }
301     }
302 }
303
304 void dispose_Object (Object object) {
305     synchronized (trackingLock) {
306         for (int i=0; i<objects.length; i++) {
307             if (objects [i] is object) {
308                 objects [i] = null;
309                 errors [i] = null;
310                 return;
311             }
312         }
313     }
314 }
315
316 static extern(Windows) int EnumFontFamFunc (ENUMLOGFONT* lpelfe, NEWTEXTMETRIC* lpntme, int FontType, int lParam) {
317     auto cbdata = cast(CallbackData*)lParam;
318     return cbdata.device.EnumFontFamProc( lpelfe, lpntme, FontType, cbdata.scalable );
319 }
320
321 int EnumFontFamProc (ENUMLOGFONT* lpelfe, NEWTEXTMETRIC* lpntme, DWORD FontType, bool scalable) {
322     bool isScalable = (FontType & OS.RASTER_FONTTYPE) is 0;
323     if (isScalable is scalable) {
324         /* Add the log font to the list of log fonts */
325         if (nFonts is logFonts.length) {
326             LOGFONT* [] newLogFonts = new LOGFONT* [logFonts.length + 128];
327             SimpleType!(LOGFONT*).arraycopy (logFonts, 0, newLogFonts, 0, nFonts);
328             logFonts = newLogFonts;
329             int[] newPixels = new int[newLogFonts.length];
330             System.arraycopy (pixels, 0, newPixels, 0, nFonts);
331             pixels = newPixels;
332         }
333         LOGFONT* logFont = logFonts [nFonts];
334         if (logFont is null) logFont = new LOGFONT ();
335         *logFont = *cast(LOGFONT*)lpelfe;
336         logFonts [nFonts] = logFont;
337         if (logFont.lfHeight > 0) {
338             /*
339              * Feature in Windows. If the lfHeight of the LOGFONT structure
340              * is positive, the lfHeight measures the height of the entire
341              * cell, including internal leading, in logical units. Since the
342              * height of a font in points does not include the internal leading,
343              * we must subtract the internal leading, which requires a TEXTMETRIC,
344              * which in turn requires font creation.
345              */
346             metrics = *cast(TEXTMETRIC*)lpntme;
347             pixels[nFonts] = logFont.lfHeight - metrics.tmInternalLeading;
348         } else {
349             pixels[nFonts] = -logFont.lfHeight;
350         }
351         nFonts++;
352     }
353     return 1;
354 }
355
356 /**
357  * Returns a rectangle describing the receiver's size and location.
358  *
359  * @return the bounding rectangle
360  *
361  * @exception DWTException <ul>
362  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
363  * </ul>
364  */
365 public Rectangle getBounds () {
366     checkDevice ();
367     auto hDC = internal_new_GC (null);
368     int width = OS.GetDeviceCaps (hDC, OS.HORZRES);
369     int height = OS.GetDeviceCaps (hDC, OS.VERTRES);
370     internal_dispose_GC (hDC, null);
371     return new Rectangle (0, 0, width, height);
372 }
373
374 /**
375  * Returns a <code>DeviceData</code> based on the receiver.
376  * Modifications made to this <code>DeviceData</code> will not
377  * affect the receiver.
378  *
379  * @return a <code>DeviceData</code> containing the device's data and attributes
380  *
381  * @exception DWTException <ul>
382  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
383  * </ul>
384  *
385  * @see DeviceData
386  */
387 public DeviceData getDeviceData () {
388     checkDevice();
389     DeviceData data = new DeviceData ();
390     data.debug_ = debug_;
391     data.tracking = tracking;
392     if (tracking) {
393         synchronized (trackingLock) {
394             int count = 0, length = objects.length;
395             for (int i=0; i<length; i++) {
396                 if (objects [i] !is null) count++;
397             }
398             int index = 0;
399             data.objects = new Object [count];
400             data.errors = new Exception [count];
401             for (int i=0; i<length; i++) {
402                 if (objects [i] !is null) {
403                     data.objects [index] = objects [i];
404                     data.errors [index] = errors [i];
405                     index++;
406                 }
407             }
408         }
409     } else {
410         data.objects = new Object [0];
411         data.errors = new Exception [0];
412     }
413     return data;
414 }
415
416 /**
417  * Returns a rectangle which describes the area of the
418  * receiver which is capable of displaying data.
419  *
420  * @return the client area
421  *
422  * @exception DWTException <ul>
423  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
424  * </ul>
425  *
426  * @see #getBounds
427  */
428 public Rectangle getClientArea () {
429     return getBounds ();
430 }
431
432 /**
433  * Returns the bit depth of the screen, which is the number of
434  * bits it takes to represent the number of unique colors that
435  * the screen is currently capable of displaying. This number
436  * will typically be one of 1, 8, 15, 16, 24 or 32.
437  *
438  * @return the depth of the screen
439  *
440  * @exception DWTException <ul>
441  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
442  * </ul>
443  */
444 public int getDepth () {
445     checkDevice ();
446     auto hDC = internal_new_GC (null);
447     int bits = OS.GetDeviceCaps (hDC, OS.BITSPIXEL);
448     int planes = OS.GetDeviceCaps (hDC, OS.PLANES);
449     internal_dispose_GC (hDC, null);
450     return bits * planes;
451 }
452
453 /**
454  * Returns a point whose x coordinate is the horizontal
455  * dots per inch of the display, and whose y coordinate
456  * is the vertical dots per inch of the display.
457  *
458  * @return the horizontal and vertical DPI
459  *
460  * @exception DWTException <ul>
461  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
462  * </ul>
463  */
464 public Point getDPI () {
465     checkDevice ();
466     auto hDC = internal_new_GC (null);
467     int dpiX = OS.GetDeviceCaps (hDC, OS.LOGPIXELSX);
468     int dpiY = OS.GetDeviceCaps (hDC, OS.LOGPIXELSY);
469     internal_dispose_GC (hDC, null);
470     return new Point (dpiX, dpiY);
471 }
472
473 /**
474  * Returns <code>FontData</code> objects which describe
475  * the fonts that match the given arguments. If the
476  * <code>faceName</code> is null, all fonts will be returned.
477  *
478  * @param faceName the name of the font to look for, or null
479  * @param scalable if true only scalable fonts are returned, otherwise only non-scalable fonts are returned.
480  * @return the matching font data
481  *
482  * @exception DWTException <ul>
483  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
484  * </ul>
485  */
486 public FontData [] getFontList (String faceName, bool scalable) {
487     checkDevice ();
488
489     /* Create the callback */
490     CallbackData cbdata;
491     cbdata.device = this;
492
493     /* Initialize the instance variables */
494     metrics = TEXTMETRIC.init;
495     pixels = new int[nFonts];
496     logFonts = new LOGFONT* [nFonts];
497     for (int i=0; i<logFonts.length; i++) {
498         logFonts [i] = new LOGFONT();
499     }
500     nFonts = 0;
501
502     /* Enumerate */
503     int offset = 0;
504     auto hDC = internal_new_GC (null);
505     if (faceName is null) {
506         /* The user did not specify a face name, so they want all versions of all available face names */
507         cbdata.scalable = scalable;
508         OS.EnumFontFamilies (hDC, null, &EnumFontFamFunc, cast(int)&cbdata );
509
510         /**
511          * For bitmapped fonts, EnumFontFamilies only enumerates once for each font, regardless
512          * of how many styles are available. If the user wants bitmapped fonts, enumerate on
513          * each face name now.
514          */
515         offset = nFonts;
516         for (int i=0; i<offset; i++) {
517             LOGFONT* lf = logFonts [i];
518             /**
519              * Bug in Windows 98. When EnumFontFamiliesEx is called with a specified face name, it
520              * should enumerate for each available style of that font. Instead, it only enumerates
521              * once. The fix is to call EnumFontFamilies, which works as expected.
522              */
523             cbdata.scalable = scalable;
524             static if (OS.IsUnicode) {
525                 OS.EnumFontFamiliesW (hDC, (cast(LOGFONTW*)lf).lfFaceName.ptr, &EnumFontFamFunc, cast(int)&cbdata);
526             } else {
527                 OS.EnumFontFamiliesA (hDC, (cast(LOGFONTA*)lf).lfFaceName.ptr, &EnumFontFamFunc, cast(int)&cbdata);
528             }
529         }
530     } else {
531         /* Use the character encoding for the default locale */
532         /**
533          * Bug in Windows 98. When EnumFontFamiliesEx is called with a specified face name, it
534          * should enumerate for each available style of that font. Instead, it only enumerates
535          * once. The fix is to call EnumFontFamilies, which works as expected.
536          */
537         cbdata.scalable = scalable;
538         OS.EnumFontFamilies (hDC, .StrToTCHARz(faceName), &EnumFontFamFunc, cast(int)&cbdata);
539     }
540     int logPixelsY = OS.GetDeviceCaps(hDC, OS.LOGPIXELSY);
541     internal_dispose_GC (hDC, null);
542
543     /* Create the fontData from the logfonts */
544     int count = 0;
545     FontData [] result = new FontData [nFonts - offset];
546     for (int i=offset; i<nFonts; i++) {
547         FontData fd = FontData.win32_new (logFonts [i], pixels [i] * 72f / logPixelsY);
548         int j;
549         for (j = 0; j < count; j++) {
550             if (fd == result [j]) break;
551         }
552         if (j is count) result [count++] = fd;
553     }
554     if (count !is result.length) {
555         FontData [] newResult = new FontData [count];
556         System.arraycopy (result, 0, newResult, 0, count);
557         result = newResult;
558     }
559
560     /* Clean up */
561     logFonts = null;
562     pixels = null;
563     metrics = TEXTMETRIC.init;
564     return result;
565 }
566
567 String getLastError () {
568     int error = OS.GetLastError();
569     if (error is 0) return ""; //$NON-NLS-1$
570     return " [GetLastError=0x" ~ .toHex(error) ~ "]"; //$NON-NLS-1$ //$NON-NLS-2$
571 }
572
573 String getLastErrorText () {
574     int error = OS.GetLastError();
575     if (error is 0) return ""; //$NON-NLS-1$
576     TCHAR* buffer = null;
577     int dwFlags = OS.FORMAT_MESSAGE_ALLOCATE_BUFFER | OS.FORMAT_MESSAGE_FROM_SYSTEM | OS.FORMAT_MESSAGE_IGNORE_INSERTS;
578     int length = OS.FormatMessage(dwFlags, null, error, OS.LANG_USER_DEFAULT, cast(TCHAR*)&buffer, 0, null);
579     String errorNum = ("[GetLastError=") ~ .toHex(error) ~ "] ";
580     if (length == 0) return errorNum;
581     String buffer1 = .TCHARzToStr(buffer, length);
582     if ( *buffer != 0)
583         OS.LocalFree(cast(HLOCAL)buffer);
584     return errorNum ~ buffer1;
585 }
586
587 /**
588  * Returns the matching standard color for the given
589  * constant, which should be one of the color constants
590  * specified in class <code>DWT</code>. Any value other
591  * than one of the DWT color constants which is passed
592  * in will result in the color black. This color should
593  * not be freed because it was allocated by the system,
594  * not the application.
595  *
596  * @param id the color constant
597  * @return the matching color
598  *
599  * @exception DWTException <ul>
600  *    <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
601  * </ul>
602  *
603  * @see DWT
604  */
605 public Color getSystemColor (int id) {
606     checkDevice ();
607     int pixel = 0x00000000;
608     switch (id) {
609         case DWT.COLOR_WHITE:               pixel = 0x00FFFFFF;  break;
610         case DWT.COLOR_BLACK:               pixel = 0x00000000;  break;
611         case DWT.COLOR_RED:                 pixel = 0x000000FF;  break;
612         case DWT.COLOR_DARK_RED:            pixel = 0x00000080;  break;
613         case DWT.COLOR_GREEN:               pixel = 0x0000FF00;  break;
614         case DWT.COLOR_DARK_GREEN:          pixel = 0x00008000;  break;
615         case DWT.COLOR_YELLOW:              pixel = 0x0000FFFF;  break;
616         case DWT.COLOR_DARK_YELLOW:         pixel = 0x00008080;  break;
617         case DWT.COLOR_BLUE:                pixel = 0x00FF0000;  break;
618         case DWT.COLOR_DARK_BLUE:           pixel = 0x00800000;  break;
619         case DWT.COLOR_MAGENTA:             pixel = 0x00FF00FF;  break;
620         case DWT.COLOR_DARK_MAGENTA:        pixel = 0x00800080;  break;
621         case DWT.COLOR_CYAN:                pixel = 0x00FFFF00;  break;
622         case DWT.COLOR_DARK_CYAN:           pixel = 0x00808000;  break;
623         case DWT.COLOR_GRAY:                pixel = 0x00C0C0C0;  break;
624         case DWT.COLOR_DARK_GRAY:           pixel = 0x00808080;  break;
625         default:
626     }
627     return Color.win32_new (this, pixel);
628 }
629
630 /**
631  * Returns a reasonable font for applications to use.
632  * On some platforms, this will match the "default font"
633  * or "system font" if such can be found.  This font
634  * should not be freed because it was allocated by the
635  * system, not the application.
636  * <p>
637  * Typically, applications which want the default look
638  * should simply not set the font on the widgets they
639  * create. Widgets are always created with the correct
640  * default font for the class of user-interface component
641  * they represent.
642  * </p>
643  *
644  * @return a font
645  *
646  * @exception