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.widgets.Canvas; 14 15 import java.lang.all; 16 import org.eclipse.swt.graphics.Point; 17 import org.eclipse.swt.internal.gtk.OS; 18 import org.eclipse.swt.SWT; 19 import org.eclipse.swt.widgets.Composite; 20 import org.eclipse.swt.widgets.Caret; 21 import org.eclipse.swt.widgets.Control; 22 import org.eclipse.swt.widgets.IME; 23 import org.eclipse.swt.graphics.GC; 24 import org.eclipse.swt.graphics.Rectangle; 25 import org.eclipse.swt.graphics.Font; 26 27 /** 28 * Instances of this class provide a surface for drawing 29 * arbitrary graphics. 30 * <dl> 31 * <dt><b>Styles:</b></dt> 32 * <dd>(none)</dd> 33 * <dt><b>Events:</b></dt> 34 * <dd>(none)</dd> 35 * </dl> 36 * <p> 37 * This class may be subclassed by custom control implementors 38 * who are building controls that are <em>not</em> constructed 39 * from aggregates of other controls. That is, they are either 40 * painted using SWT graphics calls or are handled by native 41 * methods. 42 * </p> 43 * 44 * @see Composite 45 * @see <a href="http://www.eclipse.org/swt/snippets/#canvas">Canvas snippets</a> 46 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 47 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 48 */ 49 public class Canvas : Composite { 50 51 alias Composite.setBounds setBounds; 52 53 Caret caret; 54 IME ime; 55 56 this () {} 57 58 /** 59 * Constructs a new instance of this class given its parent 60 * and a style value describing its behavior and appearance. 61 * <p> 62 * The style value is either one of the style constants defined in 63 * class <code>SWT</code> which is applicable to instances of this 64 * class, or must be built by <em>bitwise OR</em>'ing together 65 * (that is, using the <code>int</code> "|" operator) two or more 66 * of those <code>SWT</code> style constants. The class description 67 * lists the style constants that are applicable to the class. 68 * Style bits are also inherited from superclasses. 69 * </p> 70 * 71 * @param parent a composite control which will be the parent of the new instance (cannot be null) 72 * @param style the style of control to construct 73 * 74 * @exception IllegalArgumentException <ul> 75 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 76 * </ul> 77 * @exception SWTException <ul> 78 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 79 * </ul> 80 * 81 * @see SWT 82 * @see Widget#checkSubclass 83 * @see Widget#getStyle 84 */ 85 public this (Composite parent, int style) { 86 super (parent, checkStyle (style)); 87 } 88 89 /** 90 * Fills the interior of the rectangle specified by the arguments, 91 * with the receiver's background. 92 * 93 * @param gc the gc where the rectangle is to be filled 94 * @param x the x coordinate of the rectangle to be filled 95 * @param y the y coordinate of the rectangle to be filled 96 * @param width the width of the rectangle to be filled 97 * @param height the height of the rectangle to be filled 98 * 99 * @exception IllegalArgumentException <ul> 100 * <li>ERROR_NULL_ARGUMENT - if the gc is null</li> 101 * <li>ERROR_INVALID_ARGUMENT - if the gc has been disposed</li> 102 * </ul> 103 * @exception SWTException <ul> 104 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 105 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 106 * </ul> 107 * 108 * @since 3.2 109 */ 110 override public void drawBackground (GC gc, int x, int y, int width, int height) { 111 checkWidget (); 112 if (gc is null) error (SWT.ERROR_NULL_ARGUMENT); 113 if (gc.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); 114 super.drawBackground (gc, x, y, width, height); 115 } 116 117 /** 118 * Returns the caret. 119 * <p> 120 * The caret for the control is automatically hidden 121 * and shown when the control is painted or resized, 122 * when focus is gained or lost and when an the control 123 * is scrolled. To avoid drawing on top of the caret, 124 * the programmer must hide and show the caret when 125 * drawing in the window any other time. 126 * </p> 127 * 128 * @return the caret for the receiver, may be null 129 * 130 * @exception SWTException <ul> 131 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 132 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 133 * </ul> 134 */ 135 public Caret getCaret () { 136 checkWidget(); 137 return caret; 138 } 139 140 override Point getIMCaretPos () { 141 if (caret is null) return super.getIMCaretPos (); 142 return new Point (caret.x, caret.y); 143 } 144 145 /** 146 * Returns the IME. 147 * 148 * @return the IME 149 * 150 * @exception SWTException <ul> 151 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 152 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 153 * </ul> 154 * 155 * @since 3.4 156 */ 157 public IME getIME () { 158 checkWidget (); 159 return ime; 160 } 161 162 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* event) { 163 if (ime !is null) { 164 auto result = ime.gtk_button_press_event (widget, event); 165 if (result !is 0) return result; 166 } 167 return super.gtk_button_press_event (widget, event); 168 } 169 170 override int gtk_commit (GtkIMContext* imcontext, char* text) { 171 if (ime !is null) { 172 auto result = ime.gtk_commit (imcontext, text); 173 if (result !is 0) return result; 174 } 175 return super.gtk_commit (imcontext, text); 176 } 177 178 override int gtk_expose_event (GtkWidget* widget, GdkEventExpose* event) { 179 if ((state & OBSCURED) !is 0) return 0; 180 bool isFocus = caret !is null && caret.isFocusCaret (); 181 if (isFocus) caret.killFocus (); 182 auto result = super.gtk_expose_event (widget, event); 183 if (isFocus) caret.setFocus (); 184 return result; 185 } 186 187 override int gtk_focus_in_event (GtkWidget* widget, GdkEventFocus* event) { 188 auto result = super.gtk_focus_in_event (widget, event); 189 if (caret !is null) caret.setFocus (); 190 return result; 191 } 192 193 override int gtk_focus_out_event (GtkWidget* widget, GdkEventFocus* event) { 194 auto result = super.gtk_focus_out_event (widget, event); 195 if (caret !is null) caret.killFocus (); 196 return result; 197 } 198 199 override int gtk_preedit_changed (GtkIMContext* imcontext) { 200 if (ime !is null) { 201 auto result = ime.gtk_preedit_changed (imcontext); 202 if (result !is 0) return result; 203 } 204 return super.gtk_preedit_changed (imcontext); 205 } 206 207 override void redrawWidget (int x, int y, int width, int height, bool redrawAll, bool all, bool trim) { 208 bool isFocus = caret !is null && caret.isFocusCaret (); 209 if (isFocus) caret.killFocus (); 210 super.redrawWidget (x, y, width, height, redrawAll, all, trim); 211 if (isFocus) caret.setFocus (); 212 } 213 214 override void releaseChildren (bool destroy) { 215 if (caret !is null) { 216 caret.release (false); 217 caret = null; 218 } 219 if (ime !is null) { 220 ime.release (false); 221 ime = null; 222 } 223 super.releaseChildren (destroy); 224 } 225 226 /** 227 * Scrolls a rectangular area of the receiver by first copying 228 * the source area to the destination and then causing the area 229 * of the source which is not covered by the destination to 230 * be repainted. Children that intersect the rectangle are 231 * optionally moved during the operation. In addition, outstanding 232 * paint events are flushed before the source area is copied to 233 * ensure that the contents of the canvas are drawn correctly. 234 * 235 * @param destX the x coordinate of the destination 236 * @param destY the y coordinate of the destination 237 * @param x the x coordinate of the source 238 * @param y the y coordinate of the source 239 * @param width the width of the area 240 * @param height the height of the area 241 * @param all <code>true</code>if children should be scrolled, and <code>false</code> otherwise 242 * 243 * @exception SWTException <ul> 244 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 245 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 246 * </ul> 247 */ 248 public void scroll (int destX, int destY, int x, int y, int width, int height, bool all) { 249 checkWidget(); 250 if (width <= 0 || height <= 0) return; 251 if ((style & SWT.MIRRORED) !is 0) { 252 int clientWidth = getClientWidth (); 253 x = clientWidth - width - x; 254 destX = clientWidth - width - destX; 255 } 256 int deltaX = destX - x, deltaY = destY - y; 257 if (deltaX is 0 && deltaY is 0) return; 258 if (!isVisible ()) return; 259 bool isFocus = caret !is null && caret.isFocusCaret (); 260 if (isFocus) caret.killFocus (); 261 auto window = paintWindow (); 262 auto visibleRegion = OS.gdk_drawable_get_visible_region (window); 263 GdkRectangle* srcRect = new GdkRectangle (); 264 srcRect.x = x; 265 srcRect.y = y; 266 srcRect.width = width; 267 srcRect.height = height; 268 auto copyRegion = OS.gdk_region_rectangle (srcRect); 269 OS.gdk_region_intersect(copyRegion, visibleRegion); 270 auto invalidateRegion = OS.gdk_region_rectangle (srcRect); 271 OS.gdk_region_subtract (invalidateRegion, visibleRegion); 272 OS.gdk_region_offset (invalidateRegion, deltaX, deltaY); 273 GdkRectangle* copyRect = new GdkRectangle(); 274 OS.gdk_region_get_clipbox (copyRegion, copyRect); 275 if (copyRect.width !is 0 && copyRect.height !is 0) { 276 update (); 277 } 278 Control control = findBackgroundControl (); 279 if (control is null) control = this; 280 if (control.backgroundImage !is null) { 281 redrawWidget (x, y, width, height, false, false, false); 282 redrawWidget (destX, destY, width, height, false, false, false); 283 } else { 284 // GC gc = new GC (this); 285 // gc.copyArea (x, y, width, height, destX, destY); 286 // gc.dispose (); 287 auto gdkGC = OS.gdk_gc_new (window); 288 OS.gdk_gc_set_exposures (gdkGC, true); 289 OS.gdk_draw_drawable (window, gdkGC, window, copyRect.x, copyRect.y, copyRect.x + deltaX, copyRect.y + deltaY, copyRect.width, copyRect.height); 290 OS.g_object_unref (gdkGC); 291 bool disjoint = (destX + width < x) || (x + width < destX) || (destY + height < y) || (y + height < destY); 292 if (disjoint) { 293 GdkRectangle* rect = new GdkRectangle (); 294 rect.x = x; 295 rect.y = y; 296 rect.width = width; 297 rect.height = height; 298 OS.gdk_region_union_with_rect (invalidateRegion, rect); 299 } else { 300 GdkRectangle* rect = new GdkRectangle (); 301 if (deltaX !is 0) { 302 int newX = destX - deltaX; 303 if (deltaX < 0) newX = destX + width; 304 rect.x = newX; 305 rect.y = y; 306 rect.width = Math.abs(deltaX); 307 rect.height = height; 308 OS.gdk_region_union_with_rect (invalidateRegion, rect); 309 } 310 if (deltaY !is 0) { 311 int newY = destY - deltaY; 312 if (deltaY < 0) newY = destY + height; 313 rect.x = x; 314 rect.y = newY; 315 rect.width = width; 316 rect.height = Math.abs(deltaY); 317 OS.gdk_region_union_with_rect (invalidateRegion, rect); 318 } 319 } 320 OS.gdk_window_invalidate_region(window, invalidateRegion, all); 321 OS.gdk_region_destroy (visibleRegion); 322 OS.gdk_region_destroy (copyRegion); 323 OS.gdk_region_destroy (invalidateRegion); 324 } 325 if (all) { 326 Control [] children = _getChildren (); 327 for (int i=0; i<children.length; i++) { 328 Control child = children [i]; 329 Rectangle rect = child.getBounds (); 330 if (Math.min(x + width, rect.x + rect.width) >= Math.max (x, rect.x) && 331 Math.min(y + height, rect.y + rect.height) >= Math.max (y, rect.y)) { 332 child.setLocation (rect.x + deltaX, rect.y + deltaY); 333 } 334 } 335 } 336 if (isFocus) caret.setFocus (); 337 } 338 339 override int setBounds (int x, int y, int width, int height, bool move, bool resize) { 340 bool isFocus = caret !is null && caret.isFocusCaret (); 341 if (isFocus) caret.killFocus (); 342 int result = super.setBounds (x, y, width, height, move, resize); 343 if (isFocus) caret.setFocus (); 344 return result; 345 } 346 347 /** 348 * Sets the receiver's caret. 349 * <p> 350 * The caret for the control is automatically hidden 351 * and shown when the control is painted or resized, 352 * when focus is gained or lost and when an the control 353 * is scrolled. To avoid drawing on top of the caret, 354 * the programmer must hide and show the caret when 355 * drawing in the window any other time. 356 * </p> 357 * @param caret the new caret for the receiver, may be null 358 * 359 * @exception IllegalArgumentException <ul> 360 * <li>ERROR_INVALID_ARGUMENT - if the caret has been disposed</li> 361 * </ul> 362 * @exception SWTException <ul> 363 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 364 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 365 * </ul> 366 */ 367 public void setCaret (Caret caret) { 368 checkWidget(); 369 Caret newCaret = caret; 370 Caret oldCaret = this.caret; 371 this.caret = newCaret; 372 if (hasFocus ()) { 373 if (oldCaret !is null) oldCaret.killFocus (); 374 if (newCaret !is null) { 375 if (newCaret.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); 376 newCaret.setFocus (); 377 } 378 } 379 } 380 381 override public void setFont (Font font) { 382 checkWidget(); 383 if (caret !is null) caret.setFont (font); 384 super.setFont (font); 385 } 386 387 /** 388 * Sets the receiver's IME. 389 * 390 * @param ime the new IME for the receiver, may be null 391 * 392 * @exception IllegalArgumentException <ul> 393 * <li>ERROR_INVALID_ARGUMENT - if the IME has been disposed</li> 394 * </ul> 395 * @exception SWTException <ul> 396 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 397 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 398 * </ul> 399 * 400 * @since 3.4 401 */ 402 public void setIME (IME ime) { 403 checkWidget (); 404 if (ime !is null && ime.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); 405 this.ime = ime; 406 } 407 408 void updateCaret () { 409 auto imHandle = imHandle (); 410 if (imHandle is null) return; 411 GdkRectangle* rect = new GdkRectangle (); 412 rect.x = caret.x; 413 rect.y = caret.y; 414 rect.width = caret.width; 415 rect.height = caret.height; 416 OS.gtk_im_context_set_cursor_location (imHandle, rect); 417 } 418 419 }