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.Scrollable; 14 15 import java.lang.all; 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.internal.gtk.OS; 19 import org.eclipse.swt.graphics.Rectangle; 20 import org.eclipse.swt.widgets.Composite; 21 import org.eclipse.swt.widgets.Control; 22 import org.eclipse.swt.widgets.Display; 23 import org.eclipse.swt.widgets.ScrollBar; 24 import org.eclipse.swt.widgets.Widget; 25 26 version(Tango){ 27 import tango.stdc..string; 28 } else { // Phobos 29 } 30 31 /** 32 * This class is the abstract superclass of all classes which 33 * represent controls that have standard scroll bars. 34 * <dl> 35 * <dt><b>Styles:</b></dt> 36 * <dd>H_SCROLL, V_SCROLL</dd> 37 * <dt><b>Events:</b> 38 * <dd>(none)</dd> 39 * </dl> 40 * <p> 41 * IMPORTANT: This class is intended to be subclassed <em>only</em> 42 * within the SWT implementation. 43 * </p> 44 * 45 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 46 */ 47 public abstract class Scrollable : Control { 48 GtkWidget* scrolledHandle; 49 ScrollBar horizontalBar, verticalBar; 50 51 /** 52 * Prevents uninitialized instances from being created outside the package. 53 */ 54 this () {} 55 56 /** 57 * Constructs a new instance of this class given its parent 58 * and a style value describing its behavior and appearance. 59 * <p> 60 * The style value is either one of the style constants defined in 61 * class <code>SWT</code> which is applicable to instances of this 62 * class, or must be built by <em>bitwise OR</em>'ing together 63 * (that is, using the <code>int</code> "|" operator) two or more 64 * of those <code>SWT</code> style constants. The class description 65 * lists the style constants that are applicable to the class. 66 * Style bits are also inherited from superclasses. 67 * </p> 68 * 69 * @param parent a composite control which will be the parent of the new instance (cannot be null) 70 * @param style the style of control to construct 71 * 72 * @exception IllegalArgumentException <ul> 73 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 74 * </ul> 75 * @exception SWTException <ul> 76 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 77 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 78 * </ul> 79 * 80 * @see SWT#H_SCROLL 81 * @see SWT#V_SCROLL 82 * @see Widget#checkSubclass 83 * @see Widget#getStyle 84 */ 85 public this (Composite parent, int style) { 86 super (parent, style); 87 } 88 89 GtkWidget* clientHandle () { 90 return handle; 91 } 92 93 /** 94 * Given a desired <em>client area</em> for the receiver 95 * (as described by the arguments), returns the bounding 96 * rectangle which would be required to produce that client 97 * area. 98 * <p> 99 * In other words, it returns a rectangle such that, if the 100 * receiver's bounds were set to that rectangle, the area 101 * of the receiver which is capable of displaying data 102 * (that is, not covered by the "trimmings") would be the 103 * rectangle described by the arguments (relative to the 104 * receiver's parent). 105 * </p> 106 * 107 * @param x the desired x coordinate of the client area 108 * @param y the desired y coordinate of the client area 109 * @param width the desired width of the client area 110 * @param height the desired height of the client area 111 * @return the required bounds to produce the given client area 112 * 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 #getClientArea 119 */ 120 public Rectangle computeTrim (int x, int y, int width, int height) { 121 checkWidget(); 122 int border = 0; 123 if (fixedHandle !is null) border += OS.gtk_container_get_border_width (cast(GtkContainer*)fixedHandle); 124 if (scrolledHandle !is null) border += OS.gtk_container_get_border_width (cast(GtkContainer*)scrolledHandle); 125 int trimX = x - border, trimY = y - border; 126 int trimWidth = width + (border * 2), trimHeight = height + (border * 2); 127 trimHeight += hScrollBarWidth (); 128 trimWidth += vScrollBarWidth (); 129 if (scrolledHandle !is null) { 130 if (OS.gtk_scrolled_window_get_shadow_type (cast(GtkScrolledWindow*)scrolledHandle) !is OS.GTK_SHADOW_NONE) { 131 auto style = OS.gtk_widget_get_style (cast(GtkWidget*)scrolledHandle); 132 int xthickness = OS.gtk_style_get_xthickness (style); 133 int ythickness = OS.gtk_style_get_ythickness (style); 134 trimX -= xthickness; 135 trimY -= ythickness; 136 trimWidth += xthickness * 2; 137 trimHeight += ythickness * 2; 138 } 139 } 140 return new Rectangle (trimX, trimY, trimWidth, trimHeight); 141 } 142 143 ScrollBar createScrollBar (int style) { 144 if (scrolledHandle is null) return null; 145 ScrollBar bar = new ScrollBar (); 146 bar.parent = this; 147 bar.style = style; 148 bar.display = display; 149 bar.state |= HANDLE; 150 if ((style & SWT.H_SCROLL) !is 0) { 151 bar.handle = OS.GTK_SCROLLED_WINDOW_HSCROLLBAR (cast(GtkScrolledWindow*)scrolledHandle); 152 bar.adjustmentHandle = OS.gtk_scrolled_window_get_hadjustment (cast(GtkScrolledWindow*)scrolledHandle); 153 } else { 154 bar.handle = OS.GTK_SCROLLED_WINDOW_VSCROLLBAR (cast(GtkScrolledWindow*)scrolledHandle); 155 bar.adjustmentHandle = OS.gtk_scrolled_window_get_vadjustment (cast(GtkScrolledWindow*)scrolledHandle); 156 } 157 bar.setOrientation(); 158 bar.hookEvents (); 159 bar.register (); 160 return bar; 161 } 162 163 override void createWidget (int index) { 164 super.createWidget (index); 165 if ((style & SWT.H_SCROLL) !is 0) horizontalBar = createScrollBar (SWT.H_SCROLL); 166 if ((style & SWT.V_SCROLL) !is 0) verticalBar = createScrollBar (SWT.V_SCROLL); 167 } 168 169 override void deregister () { 170 super.deregister (); 171 if (scrolledHandle !is null) display.removeWidget (cast(GtkWidget*)scrolledHandle); 172 } 173 174 void destroyScrollBar (ScrollBar bar) { 175 setScrollBarVisible (bar, false); 176 //This code is intentionally commented 177 //bar.destroyHandle (); 178 } 179 180 public override int getBorderWidth () { 181 checkWidget(); 182 int border = 0; 183 if (fixedHandle !is null) border += OS.gtk_container_get_border_width (cast(GtkContainer*)fixedHandle); 184 if (scrolledHandle !is null) { 185 border += OS.gtk_container_get_border_width (cast(GtkContainer*)scrolledHandle); 186 if (OS.gtk_scrolled_window_get_shadow_type (cast(GtkScrolledWindow*)scrolledHandle) !is OS.GTK_SHADOW_NONE) { 187 border += OS.gtk_style_get_xthickness (OS.gtk_widget_get_style (cast(GtkWidget*)scrolledHandle)); 188 } 189 } 190 return border; 191 } 192 193 /** 194 * Returns a rectangle which describes the area of the 195 * receiver which is capable of displaying data (that is, 196 * not covered by the "trimmings"). 197 * 198 * @return the client area 199 * 200 * @exception SWTException <ul> 201 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 202 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 203 * </ul> 204 * 205 * @see #computeTrim 206 */ 207 public Rectangle getClientArea () { 208 checkWidget (); 209 forceResize (); 210 auto clientHandle = clientHandle (); 211 int x = OS.GTK_WIDGET_X (clientHandle); 212 int y = OS.GTK_WIDGET_Y (clientHandle); 213 int width = (state & ZERO_WIDTH) !is 0 ? 0 : OS.GTK_WIDGET_WIDTH (clientHandle); 214 int height = (state & ZERO_HEIGHT) !is 0 ? 0 : OS.GTK_WIDGET_HEIGHT (clientHandle); 215 return new Rectangle (x, y, width, height); 216 } 217 /** 218 * Returns the receiver's horizontal scroll bar if it has 219 * one, and null if it does not. 220 * 221 * @return the horizontal scroll bar (or null) 222 * 223 * @exception SWTException <ul> 224 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 225 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 226 * </ul> 227 */ 228 public ScrollBar getHorizontalBar () { 229 checkWidget (); 230 return horizontalBar; 231 } 232 /** 233 * Returns the receiver's vertical scroll bar if it has 234 * one, and null if it does not. 235 * 236 * @return the vertical scroll bar (or null) 237 * 238 * @exception SWTException <ul> 239 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 240 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 241 * </ul> 242 */ 243 public ScrollBar getVerticalBar () { 244 checkWidget (); 245 return verticalBar; 246 } 247 248 override int gtk_scroll_event (GtkWidget* widget, GdkEventScroll* eventPtr) { 249 auto result = super.gtk_scroll_event (widget, eventPtr); 250 251 /* 252 * Feature in GTK. Scrolled windows do not scroll if the scrollbars 253 * are hidden. This is not a bug, but is inconsistent with other platforms. 254 * The fix is to set the adjustment values directly. 255 */ 256 if ((state & CANVAS) !is 0) { 257 ScrollBar scrollBar; 258 GdkEventScroll* gdkEvent = new GdkEventScroll (); 259 OS.memmove (gdkEvent, eventPtr, GdkEventScroll.sizeof); 260 if (gdkEvent.direction is OS.GDK_SCROLL_UP || gdkEvent.direction is OS.GDK_SCROLL_DOWN) { 261 scrollBar = verticalBar; 262 } else { 263 scrollBar = horizontalBar; 264 } 265 if (scrollBar !is null && !OS.GTK_WIDGET_VISIBLE (scrollBar.handle) && scrollBar.getEnabled()) { 266 GtkAdjustment* adjustment = scrollBar.adjustmentHandle; 267 /* Calculate wheel delta to match GTK+ 2.4 and higher */ 268 int wheel_delta = cast(int) Math.pow(adjustment.page_size, 2.0 / 3.0); 269 if (gdkEvent.direction is OS.GDK_SCROLL_UP || gdkEvent.direction is OS.GDK_SCROLL_LEFT) 270 wheel_delta = -wheel_delta; 271 int value = cast(int) Math.max(adjustment.lower, 272 Math.min(adjustment.upper - adjustment.page_size, adjustment.value + wheel_delta)); 273 OS.gtk_adjustment_set_value (scrollBar.adjustmentHandle, value); 274 return 1; 275 } 276 } 277 return result; 278 } 279 280 int hScrollBarWidth() { 281 if (horizontalBar is null) return 0; 282 auto hBarHandle = OS.GTK_SCROLLED_WINDOW_HSCROLLBAR(cast(GtkScrolledWindow*)scrolledHandle); 283 if (hBarHandle is null) return 0; 284 GtkRequisition requisition; 285 OS.gtk_widget_size_request(cast(GtkWidget*)hBarHandle, &requisition); 286 int spacing = OS.GTK_SCROLLED_WINDOW_SCROLLBAR_SPACING(cast(GtkScrolledWindow*)scrolledHandle); 287 return requisition.height + spacing; 288 } 289 290 override bool sendLeaveNotify () { 291 return scrolledHandle !is null; 292 } 293 294 override void setOrientation () { 295 super.setOrientation (); 296 if ((style & SWT.RIGHT_TO_LEFT) !is 0) { 297 if (scrolledHandle !is null) { 298 OS.gtk_widget_set_direction (cast(GtkWidget*)scrolledHandle, OS.GTK_TEXT_DIR_RTL); 299 } 300 } 301 } 302 303 bool setScrollBarVisible (ScrollBar bar, bool visible) { 304 if (scrolledHandle is null) return false; 305 int hsp, vsp; 306 OS.gtk_scrolled_window_get_policy (cast(GtkScrolledWindow*)scrolledHandle, &hsp, &vsp); 307 int policy = visible ? OS.GTK_POLICY_ALWAYS : OS.GTK_POLICY_NEVER; 308 if ((bar.style & SWT.HORIZONTAL) !is 0) { 309 if (hsp is policy) return false; 310 hsp = policy; 311 } else { 312 if (vsp is policy) return false; 313 vsp = policy; 314 } 315 OS.gtk_scrolled_window_set_policy (cast(GtkScrolledWindow*)scrolledHandle, hsp, vsp); 316 return true; 317 } 318 319 void redrawBackgroundImage () { 320 } 321 322 override void redrawWidget (int x, int y, int width, int height, bool redrawAll, bool all, bool trim) { 323 super.redrawWidget (x, y, width, height, redrawAll, all, trim); 324 if ((OS.GTK_WIDGET_FLAGS (handle) & OS.GTK_REALIZED) is 0) return; 325 if (!trim) return; 326 auto topHandle_ = topHandle (), paintHandle_ = paintHandle (); 327 if (topHandle_ is paintHandle_) return; 328 auto window = OS.GTK_WIDGET_WINDOW (topHandle_); 329 GdkRectangle* rect = new GdkRectangle (); 330 if (redrawAll) { 331 rect.width = OS.GTK_WIDGET_WIDTH (topHandle_); 332 rect.height = OS.GTK_WIDGET_HEIGHT (topHandle_); 333 } else { 334 int destX, destY; 335 OS.gtk_widget_translate_coordinates (cast(GtkWidget*)paintHandle_, topHandle_, x, y, &destX, &destY); 336 rect.x = destX; 337 rect.y = destY; 338 rect.width = width; 339 rect.height = height; 340 } 341 OS.gdk_window_invalidate_rect (window, rect, all); 342 } 343 344 override void register () { 345 super.register (); 346 if (scrolledHandle !is null) display.addWidget (cast(GtkWidget*)scrolledHandle, this); 347 } 348 349 override void releaseHandle () { 350 super.releaseHandle (); 351 scrolledHandle = null; 352 } 353 354 override void releaseChildren (bool destroy) { 355 if (horizontalBar !is null) { 356 horizontalBar.release (false); 357 horizontalBar = null; 358 } 359 if (verticalBar !is null) { 360 verticalBar.release (false); 361 verticalBar = null; 362 } 363 super.releaseChildren (destroy); 364 } 365 366 override void resizeHandle (int width, int height) { 367 if (fixedHandle !is null) OS.gtk_widget_set_size_request (cast(GtkWidget*)fixedHandle, width, height); 368 OS.gtk_widget_set_size_request (scrolledHandle !is null ? cast(GtkWidget*)scrolledHandle : handle, width, height); 369 } 370 371 override void showWidget () { 372 super.showWidget (); 373 if (scrolledHandle !is null) OS.gtk_widget_show (cast(GtkWidget*)scrolledHandle); 374 } 375 376 override GtkWidget* topHandle () { 377 if (fixedHandle !is null) return fixedHandle; 378 if (scrolledHandle !is null) return scrolledHandle; 379 return super.topHandle (); 380 } 381 382 void updateScrollBarValue (ScrollBar bar) { 383 redrawBackgroundImage (); 384 } 385 386 int vScrollBarWidth() { 387 if (verticalBar is null) return 0; 388 auto vBarHandle = OS.GTK_SCROLLED_WINDOW_VSCROLLBAR(cast(GtkScrolledWindow*)scrolledHandle); 389 if (vBarHandle is null) return 0; 390 GtkRequisition requisition; 391 OS.gtk_widget_size_request (cast(GtkWidget*)vBarHandle, &requisition); 392 int spacing = OS.GTK_SCROLLED_WINDOW_SCROLLBAR_SPACING(cast(GtkScrolledWindow*)scrolledHandle); 393 return requisition.width + spacing; 394 } 395 }