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 }