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.Sash; 14 15 import java.lang.all; 16 17 18 19 import org.eclipse.swt.SWT; 20 import org.eclipse.swt.SWTException; 21 import org.eclipse.swt.events.SelectionEvent; 22 import org.eclipse.swt.events.SelectionListener; 23 import org.eclipse.swt.graphics.Point; 24 import org.eclipse.swt.internal.gtk.OS; 25 26 import org.eclipse.swt.widgets.Control; 27 import org.eclipse.swt.widgets.Composite; 28 import org.eclipse.swt.widgets.TypedListener; 29 import org.eclipse.swt.widgets.Event; 30 31 /** 32 * Instances of the receiver represent a selectable user interface object 33 * that allows the user to drag a rubber banded outline of the sash within 34 * the parent control. 35 * <dl> 36 * <dt><b>Styles:</b></dt> 37 * <dd>HORIZONTAL, VERTICAL, SMOOTH</dd> 38 * <dt><b>Events:</b></dt> 39 * <dd>Selection</dd> 40 * </dl> 41 * <p> 42 * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. 43 * </p><p> 44 * IMPORTANT: This class is intended to be subclassed <em>only</em> 45 * within the SWT implementation. 46 * </p> 47 * 48 * @see <a href="http://www.eclipse.org/swt/snippets/#sash">Sash snippets</a> 49 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 50 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 51 */ 52 public class Sash : Control { 53 54 alias Control.computeSize computeSize; 55 alias Control.setCursor setCursor; 56 57 bool dragging; 58 int startX, startY, lastX, lastY; 59 GtkWidget* defaultCursor; 60 61 private const static int INCREMENT = 1; 62 private const static int PAGE_INCREMENT = 9; 63 64 /** 65 * Constructs a new instance of this class given its parent 66 * and a style value describing its behavior and appearance. 67 * <p> 68 * The style value is either one of the style constants defined in 69 * class <code>SWT</code> which is applicable to instances of this 70 * class, or must be built by <em>bitwise OR</em>'ing together 71 * (that is, using the <code>int</code> "|" operator) two or more 72 * of those <code>SWT</code> style constants. The class description 73 * lists the style constants that are applicable to the class. 74 * Style bits are also inherited from superclasses. 75 * </p> 76 * 77 * @param parent a composite control which will be the parent of the new instance (cannot be null) 78 * @param style the style of control to construct 79 * 80 * @exception IllegalArgumentException <ul> 81 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 82 * </ul> 83 * @exception SWTException <ul> 84 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 85 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 86 * </ul> 87 * 88 * @see SWT#HORIZONTAL 89 * @see SWT#VERTICAL 90 * @see Widget#checkSubclass 91 * @see Widget#getStyle 92 */ 93 public this (Composite parent, int style) { 94 super (parent, checkStyle (style)); 95 } 96 97 /** 98 * Adds the listener to the collection of listeners who will 99 * be notified when the control is selected by the user, by sending 100 * it one of the messages defined in the <code>SelectionListener</code> 101 * interface. 102 * <p> 103 * When <code>widgetSelected</code> is called, the x, y, width, and height fields of the event object are valid. 104 * If the receiver is being dragged, the event object detail field contains the value <code>SWT.DRAG</code>. 105 * <code>widgetDefaultSelected</code> is not called. 106 * </p> 107 * 108 * @param listener the listener which should be notified when the control is selected by the user 109 * 110 * @exception IllegalArgumentException <ul> 111 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 112 * </ul> 113 * @exception SWTException <ul> 114 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 115 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 116 * </ul> 117 * 118 * @see SelectionListener 119 * @see #removeSelectionListener 120 * @see SelectionEvent 121 */ 122 public void addSelectionListener (SelectionListener listener) { 123 checkWidget (); 124 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 125 TypedListener typedListener = new TypedListener (listener); 126 addListener (SWT.Selection,typedListener); 127 addListener (SWT.DefaultSelection,typedListener); 128 } 129 130 static int checkStyle (int style) { 131 return checkBits (style, SWT.HORIZONTAL, SWT.VERTICAL, 0, 0, 0, 0); 132 } 133 134 public override Point computeSize (int wHint, int hHint, bool changed) { 135 checkWidget (); 136 if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0; 137 if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0; 138 int border = getBorderWidth (); 139 int width = border * 2, height = border * 2; 140 if ((style & SWT.HORIZONTAL) !is 0) { 141 width += DEFAULT_WIDTH; height += 3; 142 } else { 143 width += 3; height += DEFAULT_HEIGHT; 144 } 145 if (wHint !is SWT.DEFAULT) width = wHint + (border * 2); 146 if (hHint !is SWT.DEFAULT) height = hHint + (border * 2); 147 return new Point (width, height); 148 } 149 150 override void createHandle (int index) { 151 state |= HANDLE | THEME_BACKGROUND; 152 handle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 153 if (handle is null) error (SWT.ERROR_NO_HANDLES); 154 OS.gtk_fixed_set_has_window (handle, true); 155 OS.GTK_WIDGET_SET_FLAGS (handle, OS.GTK_CAN_FOCUS); 156 int type = (style & SWT.VERTICAL) !is 0 ? OS.GDK_SB_H_DOUBLE_ARROW : OS.GDK_SB_V_DOUBLE_ARROW; 157 defaultCursor = cast(GtkWidget*)OS.gdk_cursor_new (type); 158 } 159 160 void drawBand (int x, int y, int width, int height) { 161 if ((style & SWT.SMOOTH) !is 0) return; 162 auto window = OS.GTK_WIDGET_WINDOW (parent.paintHandle()); 163 if (window is null) return; 164 char [] bits = cast(String)[ cast(byte)-86, 85, -86, 85, -86, 85, -86, 85 ]; 165 auto stipplePixmap = OS.gdk_bitmap_create_from_data (cast(GdkDrawable*)window, bits.ptr, 8, 8); 166 auto gc = OS.gdk_gc_new (window); 167 auto colormap = OS.gdk_colormap_get_system(); 168 GdkColor color; 169 OS.gdk_color_white (colormap, &color); 170 OS.gdk_gc_set_foreground (gc, &color); 171 OS.gdk_gc_set_stipple (gc, stipplePixmap); 172 OS.gdk_gc_set_subwindow (gc, OS.GDK_INCLUDE_INFERIORS); 173 OS.gdk_gc_set_fill (gc, OS.GDK_STIPPLED); 174 OS.gdk_gc_set_function (gc, OS.GDK_XOR); 175 OS.gdk_draw_rectangle (window, gc, 1, x, y, width, height); 176 OS.g_object_unref (stipplePixmap); 177 OS.g_object_unref (gc); 178 } 179 180 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 181 auto result = super.gtk_button_press_event (widget, gdkEvent); 182 if (result !is 0) return result; 183 int button = gdkEvent.button; 184 if (button !is 1) return 0; 185 if (gdkEvent.type is OS.GDK_2BUTTON_PRESS) return 0; 186 if (gdkEvent.type is OS.GDK_3BUTTON_PRESS) return 0; 187 auto window = OS.GTK_WIDGET_WINDOW (widget); 188 int origin_x, origin_y; 189 OS.gdk_window_get_origin (window, &origin_x, &origin_y); 190 startX = cast(int) (gdkEvent.x_root - origin_x ); 191 startY = cast(int) (gdkEvent.y_root - origin_y ); 192 int x = OS.GTK_WIDGET_X (handle); 193 int y = OS.GTK_WIDGET_Y (handle); 194 int width = OS.GTK_WIDGET_WIDTH (handle); 195 int height = OS.GTK_WIDGET_HEIGHT (handle); 196 lastX = x; 197 lastY = y; 198 Event event = new Event (); 199 event.time = gdkEvent.time; 200 event.x = lastX; 201 event.y = lastY; 202 event.width = width; 203 event.height = height; 204 if ((style & SWT.SMOOTH) is 0) { 205 event.detail = SWT.DRAG; 206 } 207 if ((parent.style & SWT.MIRRORED) !is 0) event.x = parent.getClientWidth () - width - event.x; 208 sendEvent (SWT.Selection, event); 209 if (isDisposed ()) return 0; 210 if (event.doit) { 211 dragging = true; 212 lastX = event.x; 213 lastY = event.y; 214 if ((parent.style & SWT.MIRRORED) !is 0) lastX = parent.getClientWidth () - width - lastX; 215 parent.update (true, (style & SWT.SMOOTH) is 0); 216 drawBand (lastX, event.y, width, height); 217 if ((style & SWT.SMOOTH) !is 0) { 218 setBounds (event.x, event.y, width, height); 219 // widget could be disposed at this point 220 } 221 } 222 return result; 223 } 224 225 override int gtk_button_release_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 226 auto result = super.gtk_button_release_event (widget, gdkEvent); 227 if (result !is 0) return result; 228 int button = gdkEvent.button; 229 if (button !is 1) return 0; 230 if (!dragging) return 0; 231 dragging = false; 232 int width = OS.GTK_WIDGET_WIDTH (handle); 233 int height = OS.GTK_WIDGET_HEIGHT (handle); 234 Event event = new Event (); 235 event.time = gdkEvent.time; 236 event.x = lastX; 237 event.y = lastY; 238 event.width = width; 239 event.height = height; 240 drawBand (lastX, lastY, width, height); 241 if ((parent.style & SWT.MIRRORED) !is 0) event.x = parent.getClientWidth () - width - event.x; 242 sendEvent (SWT.Selection, event); 243 if (isDisposed ()) return result; 244 if (event.doit) { 245 if ((style & SWT.SMOOTH) !is 0) { 246 setBounds (event.x, event.y, width, height); 247 // widget could be disposed at this point 248 } 249 } 250 return result; 251 } 252 253 override int gtk_focus_in_event (GtkWidget* widget, GdkEventFocus* event) { 254 auto result = super.gtk_focus_in_event (widget, event); 255 if (result !is 0) return result; 256 // widget could be disposed at this point 257 if (handle !is null) { 258 lastX = OS.GTK_WIDGET_X (handle); 259 lastY = OS.GTK_WIDGET_Y (handle); 260 } 261 return 0; 262 } 263 264 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* gdkEvent) { 265 auto result = super.gtk_key_press_event (widget, gdkEvent); 266 if (result !is 0) return result; 267 int keyval = gdkEvent.keyval; 268 switch (keyval) { 269 case OS.GDK_Left: 270 case OS.GDK_Right: 271 case OS.GDK_Up: 272 case OS.GDK_Down: 273 int xChange = 0, yChange = 0; 274 int stepSize = PAGE_INCREMENT; 275 if ((gdkEvent.state & OS.GDK_CONTROL_MASK) !is 0) stepSize = INCREMENT; 276 if ((style & SWT.VERTICAL) !is 0) { 277 if (keyval is OS.GDK_Up || keyval is OS.GDK_Down) break; 278 xChange = keyval is OS.GDK_Left ? -stepSize : stepSize; 279 } else { 280 if (keyval is OS.GDK_Left ||keyval is OS.GDK_Right) break; 281 yChange = keyval is OS.GDK_Up ? -stepSize : stepSize; 282 } 283 284 int width = OS.GTK_WIDGET_WIDTH (handle); 285 int height = OS.GTK_WIDGET_HEIGHT (handle); 286 int parentBorder = 0; 287 int parentWidth = OS.GTK_WIDGET_WIDTH (parent.handle); 288 int parentHeight = OS.GTK_WIDGET_HEIGHT (parent.handle); 289 int newX = lastX, newY = lastY; 290 if ((style & SWT.VERTICAL) !is 0) { 291 newX = Math.min (Math.max (0, lastX + xChange - parentBorder - startX), parentWidth - width); 292 } else { 293 newY = Math.min (Math.max (0, lastY + yChange - parentBorder - startY), parentHeight - height); 294 } 295 if (newX is lastX && newY is lastY) return result; 296 297 /* Ensure that the pointer image does not change */ 298 auto window = OS.GTK_WIDGET_WINDOW (handle); 299 int grabMask = OS.GDK_POINTER_MOTION_MASK | OS.GDK_BUTTON_RELEASE_MASK; 300 auto gdkCursor = cursor !is null ? cursor.handle : cast(GdkCursor*)defaultCursor; 301 int ptrGrabResult = OS.gdk_pointer_grab (window, false, grabMask, window, gdkCursor, OS.GDK_CURRENT_TIME); 302 303 /* The event must be sent because its doit flag is used. */ 304 Event event = new Event (); 305 event.time = gdkEvent.time; 306 event.x = newX; 307 event.y = newY; 308 event.width = width; 309 event.height = height; 310 if ((parent.style & SWT.MIRRORED) !is 0) event.x = parent.getClientWidth () - width - event.x; 311 sendEvent (SWT.Selection, event); 312 if (ptrGrabResult is OS.GDK_GRAB_SUCCESS) OS.gdk_pointer_ungrab (OS.GDK_CURRENT_TIME); 313 if (isDisposed ()) break; 314 315 if (event.doit) { 316 lastX = event.x; 317 lastY = event.y; 318 if ((parent.style & SWT.MIRRORED) !is 0) lastX = parent.getClientWidth () - width - lastX; 319 if ((style & SWT.SMOOTH) !is 0) { 320 setBounds (event.x, event.y, width, height); 321 if (isDisposed ()) break; 322 } 323 int cursorX = event.x, cursorY = event.y; 324 if ((style & SWT.VERTICAL) !is 0) { 325 cursorY += height / 2; 326 } else { 327 cursorX += width / 2; 328 } 329 display.setCursorLocation (parent.toDisplay (cursorX, cursorY)); 330 } 331 break; 332 default: 333 } 334 335 return result; 336 } 337 338 override int gtk_motion_notify_event (GtkWidget* widget, GdkEventMotion* gdkEvent) { 339 auto result = super.gtk_motion_notify_event (widget, gdkEvent); 340 if (result !is 0) return result; 341 if (!dragging) return 0; 342 int eventX, eventY, eventState; 343 if (gdkEvent.is_hint !is 0) { 344 int pointer_x, pointer_y, mask; 345 OS.gdk_window_get_pointer (gdkEvent.window, &pointer_x, &pointer_y, &mask); 346 eventX = pointer_x ; 347 eventY = pointer_y ; 348 eventState = mask ; 349 } else { 350 int origin_x, origin_y; 351 OS.gdk_window_get_origin (gdkEvent.window, &origin_x, &origin_y); 352 eventX = cast(int)(gdkEvent.x_root - origin_x); 353 eventY = cast(int)(gdkEvent.y_root - origin_y); 354 eventState = gdkEvent.state; 355 } 356 if ((eventState & OS.GDK_BUTTON1_MASK) is 0) return 0; 357 int x = OS.GTK_WIDGET_X (handle); 358 int y = OS.GTK_WIDGET_Y (handle); 359 int width = OS.GTK_WIDGET_WIDTH (handle); 360 int height = OS.GTK_WIDGET_HEIGHT (handle); 361 int parentBorder = 0; 362 int parentWidth = OS.GTK_WIDGET_WIDTH (parent.handle); 363 int parentHeight = OS.GTK_WIDGET_HEIGHT (parent.handle); 364 int newX = lastX, newY = lastY; 365 if ((style & SWT.VERTICAL) !is 0) { 366 newX = Math.min (Math.max (0, eventX + x - startX - parentBorder), parentWidth - width); 367 } else { 368 newY = Math.min (Math.max (0, eventY + y - startY - parentBorder), parentHeight - height); 369 } 370 if (newX is lastX && newY is lastY) return 0; 371 drawBand (lastX, lastY, width, height); 372 373 Event event = new Event (); 374 event.time = gdkEvent.time; 375 event.x = newX; 376 event.y = newY; 377 event.width = width; 378 event.height = height; 379 if ((style & SWT.SMOOTH) is 0) { 380 event.detail = SWT.DRAG; 381 } 382 if ((parent.style & SWT.MIRRORED) !is 0) event.x = parent.getClientWidth() - width - event.x; 383 sendEvent (SWT.Selection, event); 384 if (isDisposed ()) return 0; 385 if (event.doit) { 386 lastX = event.x; 387 lastY = event.y; 388 if ((parent.style & SWT.MIRRORED) !is 0) lastX = parent.getClientWidth () - width - lastX; 389 } 390 parent.update (true, (style & SWT.SMOOTH) is 0); 391 drawBand (lastX, lastY, width, height); 392 if ((style & SWT.SMOOTH) !is 0) { 393 setBounds (event.x, lastY, width, height); 394 // widget could be disposed at this point 395 } 396 return result; 397 } 398 399 override int gtk_realize (GtkWidget* widget) { 400 gtk_setCursor (cursor !is null ? cursor.handle : null); 401 return super.gtk_realize (widget); 402 } 403 404 override void hookEvents () { 405 super.hookEvents (); 406 OS.gtk_widget_add_events (handle, OS.GDK_POINTER_MOTION_HINT_MASK); 407 } 408 409 override void releaseWidget () { 410 super.releaseWidget (); 411 if (defaultCursor !is null) OS.gdk_cursor_destroy (cast(GdkCursor*)defaultCursor); 412 defaultCursor = null; 413 } 414 415 /** 416 * Removes the listener from the collection of listeners who will 417 * be notified when the control is selected by the user. 418 * 419 * @param listener the listener which should no longer be notified 420 * 421 * @exception IllegalArgumentException <ul> 422 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 423 * </ul> 424 * @exception SWTException <ul> 425 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 426 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 427 * </ul> 428 * 429 * @see SelectionListener 430 * @see #addSelectionListener 431 */ 432 public void removeSelectionListener(SelectionListener listener) { 433 checkWidget(); 434 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 435 if (eventTable is null) return; 436 eventTable.unhook (SWT.Selection, listener); 437 eventTable.unhook (SWT.DefaultSelection,listener); 438 } 439 440 override void gtk_setCursor (GdkCursor* cursor) { 441 super.gtk_setCursor (cursor !is null ? cursor : cast(GdkCursor*)defaultCursor); 442 } 443 444 override int traversalCode (int key, GdkEventKey* event) { 445 return 0; 446 } 447 448 }