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.graphics.Region;
14 
15 import java.lang.all;
16 
17 
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.graphics.Resource;
20 import org.eclipse.swt.graphics.Point;
21 import org.eclipse.swt.graphics.Rectangle;
22 import org.eclipse.swt.graphics.Device;
23 import org.eclipse.swt.internal.gtk.OS;
24 
25 
26 /**
27  * Instances of this class represent areas of an x-y coordinate
28  * system that are aggregates of the areas covered by a number
29  * of polygons.
30  * <p>
31  * Application code must explicitly invoke the <code>Region.dispose()</code>
32  * method to release the operating system resources managed by each instance
33  * when those instances are no longer required.
34  * </p>
35  * 
36  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: GraphicsExample</a>
37  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
38  */
39 public final class Region : Resource {
40     /**
41      * the OS resource for the region
42      * (Warning: This field is platform dependent)
43      * <p>
44      * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
45      * public API. It is marked public only so that it can be shared
46      * within the packages provided by SWT. It is not available on all
47      * platforms and should never be accessed from application code.
48      * </p>
49      */
50     public GdkRegion* handle;
51 
52 /**
53  * Constructs a new empty region.
54  *
55  * @exception SWTError <ul>
56  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for region creation</li>
57  * </ul>
58  */
59 public this() {
60     this(null);
61 }
62 
63 /**
64  * Constructs a new empty region.
65  * <p>
66  * You must dispose the region when it is no longer required.
67  * </p>
68  *
69  * @param device the device on which to allocate the region
70  *
71  * @exception IllegalArgumentException <ul>
72  *    <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
73  * </ul>
74  * @exception SWTError <ul>
75  *    <li>ERROR_NO_HANDLES if a handle could not be obtained for region creation</li>
76  * </ul>
77  *
78  * @see #dispose
79  *
80  * @since 3.0
81  */
82 public this(Device device) {
83     super(device);
84     handle = OS.gdk_region_new();
85     if (handle is null) SWT.error(SWT.ERROR_NO_HANDLES);
86     init_();
87 }
88 
89 this(Device device, GdkRegion* handle) {
90     super(device);
91     this.handle = handle;
92 }
93 
94 /**
95  * Adds the given polygon to the collection of polygons
96  * the receiver maintains to describe its area.
97  *
98  * @param pointArray points that describe the polygon to merge with the receiver
99  *
100  * @exception IllegalArgumentException <ul>
101  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
102  * </ul>
103  * @exception SWTException <ul>
104  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
105  * </ul>
106  *
107  * @since 3.0
108 *
109  */
110 public void add (int[] pointArray) {
111     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
112     if (pointArray is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
113     /*
114     * Bug in GTK. If gdk_region_polygon() is called with one point,
115     * it segment faults. The fix is to make sure that it is called
116     * with enough points for a polygon.
117     */
118     if (pointArray.length < 6) return;
119     auto polyRgn = OS.gdk_region_polygon(cast(GdkPoint*)pointArray.ptr,
120                       cast(int)/*64bit*/pointArray.length / 2, OS.GDK_EVEN_ODD_RULE);
121     OS.gdk_region_union(handle, polyRgn);
122     OS.gdk_region_destroy(polyRgn);
123 }
124 
125 /**
126  * Adds the given rectangle to the collection of polygons
127  * the receiver maintains to describe its area.
128  *
129  * @param rect the rectangle to merge with the receiver
130  *
131  * @exception IllegalArgumentException <ul>
132  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
133  *    <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li>
134  * </ul>
135  * @exception SWTException <ul>
136  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
137  * </ul>
138  */
139 public void add(Rectangle rect) {
140     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
141     if (rect is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
142     add (rect.x, rect.y, rect.width, rect.height);
143 }
144 
145 /**
146  * Adds the given rectangle to the collection of polygons
147  * the receiver maintains to describe its area.
148  *
149  * @param x the x coordinate of the rectangle
150  * @param y the y coordinate of the rectangle
151  * @param width the width coordinate of the rectangle
152  * @param height the height coordinate of the rectangle
153  *
154  * @exception IllegalArgumentException <ul>
155  *    <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li>
156  * </ul>
157  * @exception SWTException <ul>
158  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
159  * </ul>
160  *
161  * @since 3.1
162  */
163 public void add(int x, int y, int width, int height) {
164     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
165     if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
166     GdkRectangle* gdkRect = new GdkRectangle();
167     gdkRect.x = x;
168     gdkRect.y = y;
169     gdkRect.width = width;
170     gdkRect.height = height;
171     OS.gdk_region_union_with_rect(handle, gdkRect);
172 }
173 
174 /**
175  * Adds all of the polygons which make up the area covered
176  * by the argument to the collection of polygons the receiver
177  * maintains to describe its area.
178  *
179  * @param region the region to merge
180  *
181  * @exception IllegalArgumentException <ul>
182  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
183  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
184  * </ul>
185  * @exception SWTException <ul>
186  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
187  * </ul>
188  */
189 public void add(Region region) {
190     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
191     if (region is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
192     if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
193     OS.gdk_region_union(handle, region.handle);
194 }
195 
196 /**
197  * Returns <code>true</code> if the point specified by the
198  * arguments is inside the area specified by the receiver,
199  * and <code>false</code> otherwise.
200  *
201  * @param x the x coordinate of the point to test for containment
202  * @param y the y coordinate of the point to test for containment
203  * @return <code>true</code> if the region contains the point and <code>false</code> otherwise
204  *
205  * @exception SWTException <ul>
206  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
207  * </ul>
208  */
209 public bool contains(int x, int y) {
210     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
211     return cast(bool)OS.gdk_region_point_in(handle, x, y);
212 }
213 
214 /**
215  * Returns <code>true</code> if the given point is inside the
216  * area specified by the receiver, and <code>false</code>
217  * otherwise.
218  *
219  * @param pt the point to test for containment
220  * @return <code>true</code> if the region contains the point and <code>false</code> otherwise
221  *
222  * @exception IllegalArgumentException <ul>
223  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
224  * </ul>
225  * @exception SWTException <ul>
226  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
227  * </ul>
228  */
229 public bool contains(Point pt) {
230     if (pt is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
231     return contains(pt.x, pt.y);
232 }
233 
234 override
235 void destroy() {
236     OS.gdk_region_destroy(handle);
237     handle = null;
238 }
239 
240 /**
241  * Compares the argument to the receiver, and returns true
242  * if they represent the <em>same</em> object using a class
243  * specific comparison.
244  *
245  * @param object the object to compare with this object
246  * @return <code>true</code> if the object is the same as this object and <code>false</code> otherwise
247  *
248  * @see #hashCode
249  */
250 public override equals_t opEquals(Object object) {
251     if (this is object) return true;
252     if ( auto region = cast(Region)object ){
253         return handle is region.handle;
254     }
255     return false;
256 }
257 
258 /**
259  * Returns a rectangle which represents the rectangular
260  * union of the collection of polygons the receiver
261  * maintains to describe its area.
262  *
263  * @return a bounding rectangle for the region
264  *
265  * @exception SWTException <ul>
266  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
267  * </ul>
268  *
269  * @see Rectangle#union
270  */
271 public Rectangle getBounds() {
272     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
273     GdkRectangle* gdkRect = new GdkRectangle();
274     OS.gdk_region_get_clipbox(handle, gdkRect);
275     return new Rectangle(gdkRect.x, gdkRect.y, gdkRect.width, gdkRect.height);
276 }
277 
278 public static Region gtk_new(Device device, GdkRegion* handle) {
279     return new Region(device, handle);
280 }
281 
282 /**
283  * Returns an integer hash code for the receiver. Any two
284  * objects that return <code>true</code> when passed to
285  * <code>equals</code> must return the same value for this
286  * method.
287  *
288  * @return the receiver's hash
289  *
290  * @see #equals
291  */
292 public override hash_t toHash() {
293     return cast(hash_t)handle;
294 }
295 
296 /**
297  * Intersects the given rectangle to the collection of polygons
298  * the receiver maintains to describe its area.
299  *
300  * @param rect the rectangle to intersect with the receiver
301  *
302  * @exception IllegalArgumentException <ul>
303  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
304  *    <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li>
305  * </ul>
306  * @exception SWTException <ul>
307  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
308  * </ul>
309  *
310  * @since 3.0
311  */
312 public void intersect(Rectangle rect) {
313     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
314     if (rect is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
315     intersect (rect.x, rect.y, rect.width, rect.height);
316 }
317 
318 /**
319  * Intersects the given rectangle to the collection of polygons
320  * the receiver maintains to describe its area.
321  *
322  * @param x the x coordinate of the rectangle
323  * @param y the y coordinate of the rectangle
324  * @param width the width coordinate of the rectangle
325  * @param height the height coordinate of the rectangle
326  *
327  * @exception IllegalArgumentException <ul>
328  *    <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li>
329  * </ul>
330  * @exception SWTException <ul>
331  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
332  * </ul>
333  *
334  * @since 3.1
335  */
336 public void intersect(int x, int y, int width, int height) {
337     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
338     if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
339     GdkRectangle* gdkRect = new GdkRectangle();
340     gdkRect.x = x;
341     gdkRect.y = y;
342     gdkRect.width = width;
343     gdkRect.height = height;
344     auto rectRgn = OS.gdk_region_rectangle(gdkRect);
345     OS.gdk_region_intersect(handle, rectRgn);
346     OS.gdk_region_destroy(rectRgn);
347 }
348 
349 /**
350  * Intersects all of the polygons which make up the area covered
351  * by the argument to the collection of polygons the receiver
352  * maintains to describe its area.
353  *
354  * @param region the region to intersect
355  *
356  * @exception IllegalArgumentException <ul>
357  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
358  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
359  * </ul>
360  * @exception SWTException <ul>
361  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
362  * </ul>
363  *
364  * @since 3.0
365  */
366 public void intersect(Region region) {
367     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
368     if (region is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
369     if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
370     OS.gdk_region_intersect(handle, region.handle);
371 }
372 
373 /**
374  * Returns <code>true</code> if the rectangle described by the
375  * arguments intersects with any of the polygons the receiver
376  * maintains to describe its area, and <code>false</code> otherwise.
377  *
378  * @param x the x coordinate of the origin of the rectangle
379  * @param y the y coordinate of the origin of the rectangle
380  * @param width the width of the rectangle
381  * @param height the height of the rectangle
382  * @return <code>true</code> if the rectangle intersects with the receiver, and <code>false</code> otherwise
383  *
384  * @exception SWTException <ul>
385  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
386  * </ul>
387  *
388  * @see Rectangle#intersects(Rectangle)
389  */
390 public bool intersects (int x, int y, int width, int height) {
391     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
392     GdkRectangle* gdkRect = new GdkRectangle();
393     gdkRect.x = x;
394     gdkRect.y = y;
395     gdkRect.width = width;
396     gdkRect.height = height;
397     return OS.gdk_region_rect_in(handle, gdkRect) !is OS.GDK_OVERLAP_RECTANGLE_OUT;
398 }
399 /**
400  * Returns <code>true</code> if the given rectangle intersects
401  * with any of the polygons the receiver maintains to describe
402  * its area and <code>false</code> otherwise.
403  *
404  * @param rect the rectangle to test for intersection
405  * @return <code>true</code> if the rectangle intersects with the receiver, and <code>false</code> otherwise
406  *
407  * @exception IllegalArgumentException <ul>
408  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
409  * </ul>
410  * @exception SWTException <ul>
411  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
412  * </ul>
413  *
414  * @see Rectangle#intersects(Rectangle)
415  */
416 public bool intersects(Rectangle rect) {
417     if (rect is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
418     return intersects(rect.x, rect.y, rect.width, rect.height);
419 }
420 
421 /**
422  * Returns <code>true</code> if the region has been disposed,
423  * and <code>false</code> otherwise.
424  * <p>
425  * This method gets the dispose state for the region.
426  * When a region has been disposed, it is an error to
427  * invoke any other method using the region.
428  *
429  * @return <code>true</code> when the region is disposed, and <code>false</code> otherwise
430  */
431 public override bool isDisposed() {
432     return handle is null;
433 }
434 
435 /**
436  * Returns <code>true</code> if the receiver does not cover any
437  * area in the (x, y) coordinate plane, and <code>false</code> if
438  * the receiver does cover some area in the plane.
439  *
440  * @return <code>true</code> if the receiver is empty, and <code>false</code> otherwise
441  *
442  * @exception SWTException <ul>
443  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
444  * </ul>
445  */
446 public bool isEmpty() {
447     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
448     return cast(bool)OS.gdk_region_empty(handle);
449 }
450 
451 /**
452  * Subtracts the given polygon from the collection of polygons
453  * the receiver maintains to describe its area.
454  *
455  * @param pointArray points that describe the polygon to merge with the receiver
456  *
457  * @exception IllegalArgumentException <ul>
458  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
459  * </ul>
460  * @exception SWTException <ul>
461  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
462  * </ul>
463  *
464  * @since 3.0
465  */
466 public void subtract (int[] pointArray) {
467     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
468     if (pointArray is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
469     /*
470     * Bug in GTK. If gdk_region_polygon() is called with one point,
471     * it segment faults. The fix is to make sure that it is called
472     * with enough points for a polygon.
473     */
474     if (pointArray.length < 6) return;
475     auto polyRgn = OS.gdk_region_polygon( cast(GdkPoint*)pointArray.ptr,
476                       cast(int)/*64bit*/pointArray.length / 2, OS.GDK_EVEN_ODD_RULE);
477     OS.gdk_region_subtract(handle, polyRgn);
478     OS.gdk_region_destroy(polyRgn);
479 }
480 
481 /**
482  * Subtracts the given rectangle from the collection of polygons
483  * the receiver maintains to describe its area.
484  *
485  * @param rect the rectangle to subtract from the receiver
486  *
487  * @exception IllegalArgumentException <ul>
488  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
489  *    <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li>
490  * </ul>
491  * @exception SWTException <ul>
492  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
493  * </ul>
494  *
495  * @since 3.0
496  */
497 public void subtract(Rectangle rect) {
498     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
499     if (rect is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
500     subtract (rect.x, rect.y, rect.width, rect.height);
501 }
502 
503 /**
504  * Subtracts the given rectangle from the collection of polygons
505  * the receiver maintains to describe its area.
506  *
507  * @param x the x coordinate of the rectangle
508  * @param y the y coordinate of the rectangle
509  * @param width the width coordinate of the rectangle
510  * @param height the height coordinate of the rectangle
511  *
512  * @exception IllegalArgumentException <ul>
513  *    <li>ERROR_INVALID_ARGUMENT - if the rectangle's width or height is negative</li>
514  * </ul>
515  * @exception SWTException <ul>
516  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
517  * </ul>
518  *
519  * @since 3.1
520  */
521 public void subtract(int x, int y, int width, int height) {
522     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
523     if (width < 0 || height < 0) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
524     GdkRectangle* gdkRect = new GdkRectangle();
525     gdkRect.x = x;
526     gdkRect.y = y;
527     gdkRect.width = width;
528     gdkRect.height = height;
529     auto rectRgn = OS.gdk_region_rectangle(gdkRect);
530     OS.gdk_region_subtract(handle, rectRgn);
531     OS.gdk_region_destroy(rectRgn);
532 }
533 
534 /**
535  * Subtracts all of the polygons which make up the area covered
536  * by the argument from the collection of polygons the receiver
537  * maintains to describe its area.
538  *
539  * @param region the region to subtract
540  *
541  * @exception IllegalArgumentException <ul>
542  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
543  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
544  * </ul>
545  * @exception SWTException <ul>
546  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
547  * </ul>
548  *
549  * @since 3.0
550  */
551 public void subtract(Region region) {
552     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
553     if (region is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
554     if (region.isDisposed()) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
555     OS.gdk_region_subtract(handle, region.handle);
556 }
557 
558 /**
559  * Translate all of the polygons the receiver maintains to describe
560  * its area by the specified point.
561  *
562  * @param x the x coordinate of the point to translate
563  * @param y the y coordinate of the point to translate
564  *
565  * @exception SWTException <ul>
566  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
567  * </ul>
568  *
569  * @since 3.1
570  */
571 public void translate (int x, int y) {
572     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
573     OS.gdk_region_offset (handle, x, y);
574 }
575 
576 /**
577  * Translate all of the polygons the receiver maintains to describe
578  * its area by the specified point.
579  *
580  * @param pt the point to translate
581  *
582  * @exception IllegalArgumentException <ul>
583  *    <li>ERROR_NULL_ARGUMENT - if the argument is null</li>
584  * </ul>
585  * @exception SWTException <ul>
586  *    <li>ERROR_GRAPHIC_DISPOSED - if the receiver has been disposed</li>
587  * </ul>
588  *
589  * @since 3.1
590  */
591 public void translate (Point pt) {
592     if (isDisposed()) SWT.error(SWT.ERROR_GRAPHIC_DISPOSED);
593     if (pt is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
594     translate (pt.x, pt.y);
595 }
596 
597 /**
598  * Returns a string containing a concise, human-readable
599  * description of the receiver.
600  *
601  * @return a string representation of the receiver
602  */
603 public override String toString () {
604     if (isDisposed()) return "Region {*DISPOSED*}";
605     return Format( "Region {{{}}", handle );
606 }
607 }