root/dwt/widgets/Scrollable.d

Revision 246:fd9c62a2998e, 18.9 kB (checked in by Frank Benoit <benoit@tionex.de>, 5 months ago)

Updater SWT 3.4M7 to 3.4

Line 
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 dwt.widgets.Scrollable;
14
15 import dwt.DWT;
16 import dwt.DWTException;
17
18 import dwt.graphics.Rectangle;
19
20 import dwt.internal.win32.OS;
21
22 import dwt.widgets.Composite;
23 import dwt.widgets.Control;
24 import dwt.widgets.Decorations;
25 import dwt.widgets.Event;
26 import dwt.widgets.ScrollBar;
27 import dwt.widgets.Widget;
28
29 import dwt.dwthelper.utils;
30 //import tango.util.log.Trace;
31 void trc( long line ){
32     //Trace.formatln( "Scrollable {}", line );
33 }
34
35 /**
36  * This class is the abstract superclass of all classes which
37  * represent controls that have standard scroll bars.
38  * <dl>
39  * <dt><b>Styles:</b></dt>
40  * <dd>H_SCROLL, V_SCROLL</dd>
41  * <dt><b>Events:</b>
42  * <dd>(none)</dd>
43  * </dl>
44  * <p>
45  * IMPORTANT: This class is intended to be subclassed <em>only</em>
46  * within the DWT implementation.
47  * </p>
48  *
49  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
50  */
51
52 public abstract class Scrollable : Control {
53
54     alias Control.windowProc windowProc;
55
56     ScrollBar horizontalBar, verticalBar;
57
58 /**
59  * Prevents uninitialized instances from being created outside the package.
60  */
61 this () {
62 }
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>DWT</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>DWT</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 DWTException <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 DWT#H_SCROLL
89  * @see DWT#V_SCROLL
90  * @see Widget#checkSubclass
91  * @see Widget#getStyle
92  */
93 public this (Composite parent, int style) {
94     super (parent, style);
95 }
96
97 override int callWindowProc (HWND hwnd, int msg, int wParam, int lParam) {
98     if (handle is null) return 0;
99     return OS.DefWindowProc (hwnd, msg, wParam, lParam);
100 }
101
102 /**
103  * Given a desired <em>client area</em> for the receiver
104  * (as described by the arguments), returns the bounding
105  * rectangle which would be required to produce that client
106  * area.
107  * <p>
108  * In other words, it returns a rectangle such that, if the
109  * receiver's bounds were set to that rectangle, the area
110  * of the receiver which is capable of displaying data
111  * (that is, not covered by the "trimmings") would be the
112  * rectangle described by the arguments (relative to the
113  * receiver's parent).
114  * </p>
115  *
116  * @param x the desired x coordinate of the client area
117  * @param y the desired y coordinate of the client area
118  * @param width the desired width of the client area
119  * @param height the desired height of the client area
120  * @return the required bounds to produce the given client area
121  *
122  * @exception DWTException <ul>
123  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
124  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
125  * </ul>
126  *
127  * @see #getClientArea
128  */
129 public Rectangle computeTrim (int x, int y, int width, int height) {
130     checkWidget ();
131     auto scrolledHandle_ = scrolledHandle ();
132     RECT rect;
133     OS.SetRect (&rect, x, y, x + width, y + height);
134     int bits1 = OS.GetWindowLong (scrolledHandle_, OS.GWL_STYLE);
135     int bits2 = OS.GetWindowLong (scrolledHandle_, OS.GWL_EXSTYLE);
136     OS.AdjustWindowRectEx (&rect, bits1, false, bits2);
137     if (horizontalBar !is null) rect.bottom += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
138     if (verticalBar !is null) rect.right += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
139     int nWidth = rect.right - rect.left, nHeight = rect.bottom - rect.top;
140     return new Rectangle (rect.left, rect.top, nWidth, nHeight);
141 }
142
143 ScrollBar createScrollBar (int type) {
144     ScrollBar bar = new ScrollBar (this, type);
145     if ((state & CANVAS) !is 0) {
146         bar.setMaximum (100);
147         bar.setThumb (10);
148     }
149     return bar;
150 }
151
152 override void createWidget () {
153     super.createWidget ();
154     if ((style & DWT.H_SCROLL) !is 0) horizontalBar = createScrollBar (DWT.H_SCROLL);
155     if ((style & DWT.V_SCROLL) !is 0) verticalBar = createScrollBar (DWT.V_SCROLL);
156 }
157
158 void destroyScrollBar (int type) {
159     auto hwnd = scrolledHandle ();
160     int bits = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
161     if ((type & DWT.HORIZONTAL) !is 0) {
162         style &= ~DWT.H_SCROLL;
163         bits &= ~OS.WS_HSCROLL;
164     }
165     if ((type & DWT.VERTICAL) !is 0) {
166         style &= ~DWT.V_SCROLL;
167         bits &= ~OS.WS_VSCROLL;
168     }
169     OS.SetWindowLong (hwnd, OS.GWL_STYLE, bits);
170 }
171
172 /**
173  * Returns a rectangle which describes the area of the
174  * receiver which is capable of displaying data (that is,
175  * not covered by the "trimmings").
176  *
177  * @return the client area
178  *
179  * @exception DWTException <ul>
180  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
181  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
182  * </ul>
183  *
184  * @see #computeTrim
185  */
186 public Rectangle getClientArea () {
187     checkWidget ();
188     forceResize ();
189     RECT rect;
190     auto scrolledHandle_ = scrolledHandle ();
191     OS.GetClientRect (scrolledHandle_, &rect);
192     int x = rect.left, y = rect.top;
193     int width = rect.right - rect.left;
194     int height = rect.bottom - rect.top;
195     if (scrolledHandle_ !is handle) {
196         OS.GetClientRect (handle, &rect);
197         OS.MapWindowPoints(handle, scrolledHandle_, cast(POINT*)&rect, 2);
198         x = -rect.left;
199         y = -rect.top;
200     }
201     return new Rectangle (x, y, width, height);
202 }
203
204 /**
205  * Returns the receiver's horizontal scroll bar if it has
206  * one, and null if it does not.
207  *
208  * @return the horizontal scroll bar (or null)
209  *
210  * @exception DWTException <ul>
211  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
212  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
213  * </ul>
214  */
215 public ScrollBar getHorizontalBar () {
216     checkWidget ();
217     return horizontalBar;
218 }
219
220 /**
221  * Returns the receiver's vertical scroll bar if it has
222  * one, and null if it does not.
223  *
224  * @return the vertical scroll bar (or null)
225  *
226  * @exception DWTException <ul>
227  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
228  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
229  * </ul>
230  */
231 public ScrollBar getVerticalBar () {
232     checkWidget ();
233     return verticalBar;
234 }
235
236 override void releaseChildren (bool destroy) {
237     if (horizontalBar !is null) {
238         horizontalBar.release (false);
239         horizontalBar = null;
240     }
241     if (verticalBar !is null) {
242         verticalBar.release (false);
243         verticalBar = null;
244     }
245     super.releaseChildren (destroy);
246 }
247
248 HANDLE scrolledHandle () {
249     return handle;
250 }
251
252 override int widgetExtStyle () {
253     return super.widgetExtStyle ();
254     /*
255     * This code is intentionally commented.  In future,
256     * we may wish to support different standard Windows
257     * edge styles.  The issue here is that not all of
258     * these styles are available on the other platforms
259     * this would need to be a hint.
260     */
261 //  if ((style & DWT.BORDER) !is 0) return OS.WS_EX_CLIENTEDGE;
262 //  if ((style & DWT.SHADOW_IN) !is 0) return OS.WS_EX_STATICEDGE;
263 //  return super.widgetExtStyle ();
264 }
265
266 override int widgetStyle () {
267     int bits = super.widgetStyle () | OS.WS_TABSTOP;
268     if ((style & DWT.H_SCROLL) !is 0) bits |= OS.WS_HSCROLL;
269     if ((style & DWT.V_SCROLL) !is 0) bits |= OS.WS_VSCROLL;
270     return bits;
271 }
272
273 override String windowClass () {
274     return display.windowClass();
275 }
276
277 override int windowProc () {
278     return display.windowProc;
279 }
280
281 override LRESULT WM_HSCROLL (int wParam, int lParam) {
282 trc(__LINE__);
283     LRESULT result = super.WM_HSCROLL (wParam, lParam);
284     if (result !is null) return result;
285
286     /*
287     * Bug on WinCE.  lParam should be NULL when the message is not sent
288     * by a scroll bar control, but it contains the handle to the window.
289     * When the message is sent by a scroll bar control, it correctly
290     * contains the handle to the scroll bar.  The fix is to check for
291     * both.
292     */
293     if (horizontalBar !is null && (lParam is 0 || lParam is cast(int)handle)) {
294         return wmScroll (horizontalBar, (state & CANVAS) !is 0, handle, OS.WM_HSCROLL, wParam, lParam);
295     }
296     return result;
297 }
298
299 override LRESULT WM_MOUSEWHEEL (int wParam, int lParam) {
300 trc(__LINE__);
301     int scrollRemainder = display.scrollRemainder;
302     LRESULT result = super.WM_MOUSEWHEEL (wParam, lParam);
303     if (result !is null) return result;
304     /*
305     * Translate WM_MOUSEWHEEL to WM_VSCROLL or WM_HSCROLL.
306     */
307     if ((state & CANVAS) !is 0) {
308         if ((wParam & (OS.MK_SHIFT | OS.MK_CONTROL)) !is 0) return result;
309         bool vertical = verticalBar !is null && verticalBar.getEnabled ();
310         bool horizontal = horizontalBar !is null && horizontalBar.getEnabled ();
311         int msg = vertical ? OS.WM_VSCROLL : horizontal ? OS.WM_HSCROLL : 0;
312         if (msg is 0) return result;
313         int linesToScroll;
314         OS.SystemParametersInfo (OS.SPI_GETWHEELSCROLLLINES, 0, &linesToScroll, 0);
315         int delta = OS.GET_WHEEL_DELTA_WPARAM (wParam);
316         bool pageScroll = linesToScroll is OS.WHEEL_PAGESCROLL;
317         if (!OS.IsWinCE && OS.WIN32_VERSION >= OS.VERSION (6, 0)) {
318             ScrollBar bar = vertical ? verticalBar : horizontalBar;
319             SCROLLINFO info;
320             info.cbSize = SCROLLINFO.sizeof;
321             info.fMask = OS.SIF_POS;
322             OS.GetScrollInfo (handle, bar.scrollBarType (), &info);
323             if (vertical && !pageScroll) delta *= linesToScroll;
324             int increment = pageScroll ? bar.getPageIncrement () : bar.getIncrement ();
325             info.nPos -=  increment * delta / OS.WHEEL_DELTA;
326             OS.SetScrollInfo (handle, bar.scrollBarType (), &info, true);
327             OS.SendMessage (handle, msg, OS.SB_THUMBPOSITION, 0);
328         } else {
329             int code = 0;
330             if (pageScroll) {
331                 code = delta < 0 ? OS.SB_PAGEDOWN : OS.SB_PAGEUP;
332             } else {
333                 code = delta < 0 ? OS.SB_LINEDOWN : OS.SB_LINEUP;
334                 if (msg is OS.WM_VSCROLL) delta *= linesToScroll;
335             }
336             /* Check if the delta and the remainder have the same direction (sign) */
337             if ((delta ^ scrollRemainder) >= 0) delta += scrollRemainder;
338             int count = Math.abs (delta) / OS.WHEEL_DELTA;
339             for (int i=0; i<count; i++) {
340                 OS.SendMessage (handle, msg, code, 0);
341             }
342         }
343         return LRESULT.ZERO;
344     }
345
346     /*
347     * When the native widget scrolls inside WM_MOUSEWHEEL, it
348     * may or may not send a WM_VSCROLL or WM_HSCROLL to do the
349     * actual scrolling.  This depends on the implementation of
350     * each native widget.  In order to ensure that application
351     * code is notified when the scroll bar moves, compare the
352     * scroll bar position before and after the WM_MOUSEWHEEL.
353     * If the native control sends a WM_VSCROLL or WM_HSCROLL,
354     * then the application has already been notified.  If not
355     * explicitly send the event.
356     */
357     int vPosition = verticalBar is null ? 0 : verticalBar.getSelection ();
358     int hPosition = horizontalBar is null ? 0 : horizontalBar.getSelection ();
359     int /*long*/ code = callWindowProc (handle, OS.WM_MOUSEWHEEL, wParam, lParam);
360     if (verticalBar !is null) {
361         int position = verticalBar.getSelection ();
362         if (position !is vPosition) {
363             Event event = new Event ();
364             event.detail = position < vPosition ? DWT.PAGE_UP : DWT.PAGE_DOWN;
365             verticalBar.sendEvent (DWT.Selection, event);
366         }
367     }
368     if (horizontalBar !is null) {
369         int position = horizontalBar.getSelection ();
370         if (position !is hPosition) {
371             Event event = new Event ();
372             event.detail = position < hPosition ? DWT.PAGE_UP : DWT.PAGE_DOWN;
373             horizontalBar.sendEvent (DWT.Selection, event);
374         }
375     }
376     return new LRESULT (code);
377 }
378
379 override LRESULT WM_SIZE (int wParam, int lParam) {
380 trc(__LINE__);
381     int /*long*/ code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam);
382     super.WM_SIZE (wParam, lParam);
383     // widget may be disposed at this point
384     if (code is 0) return LRESULT.ZERO;
385     return new LRESULT (code);
386 }
387
388 override LRESULT WM_VSCROLL (int wParam, int lParam) {
389 trc(__LINE__);
390     LRESULT result = super.WM_VSCROLL (wParam, lParam);
391     if (result !is null) return result;
392     /*
393     * Bug on WinCE.  lParam should be NULL when the message is not sent
394     * by a scroll bar control, but it contains the handle to the window.
395     * When the message is sent by a scroll bar control, it correctly
396     * contains the handle to the scroll bar.  The fix is to check for
397     * both.
398     */
399     if (verticalBar !is null && (lParam is 0 || lParam is cast(int)handle)) {
400         return wmScroll (verticalBar, (state & CANVAS) !is 0, handle, OS.WM_VSCROLL, wParam, lParam);
401     }
402     return result;
403 }
404
405 override LRESULT wmNCPaint (HWND hwnd, int /*long*/ wParam, int /*long*/ lParam) {
406     LRESULT result = super.wmNCPaint (hwnd, wParam, lParam);
407     if (result !is null) return result;
408     /*
409     * Bug in Windows.  On XP only (not Vista), Windows sometimes
410     * does not redraw the bottom right corner of a window that
411     * has scroll bars, causing pixel corruption.  The fix is to
412     * always draw the corner.
413     */
414     if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
415         if (!OS.IsWinCE && OS.WIN32_VERSION < OS.VERSION (6, 0)) {
416             int bits1 = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
417             if ((bits1 & (OS.WS_HSCROLL | OS.WS_VSCROLL)) !is 0) {
418                 RECT windowRect;
419                 OS.GetWindowRect (hwnd, &windowRect);
420                 RECT trimRect;
421                 int bits2 = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
422                 OS.AdjustWindowRectEx (&trimRect, bits1, false, bits2);
423                 bool hVisible = false, vVisible = false;
424                 SCROLLBARINFO psbi;
425                 psbi.cbSize = SCROLLBARINFO.sizeof;
426                 if (OS.GetScrollBarInfo (hwnd, OS.OBJID_HSCROLL, &psbi)) {
427                     hVisible = (psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) is 0;
428                 }
429                 if (OS.GetScrollBarInfo (hwnd, OS.OBJID_VSCROLL, &psbi)) {
430                     vVisible = (psbi.rgstate [0] & OS.STATE_SYSTEM_INVISIBLE) is 0;
431                 }
432                 RECT cornerRect;
433                 cornerRect.right = windowRect.right - windowRect.left - trimRect.right;
434                 cornerRect.bottom = windowRect.bottom - windowRect.top - trimRect.bottom;
435                 cornerRect.left = cornerRect.right - (hVisible ? OS.GetSystemMetrics (OS.SM_CXVSCROLL) : 0);
436                 cornerRect.top = cornerRect.bottom - (vVisible ? OS.GetSystemMetrics (OS.SM_CYHSCROLL) : 0);
437                 if (cornerRect.left !is cornerRect.right && cornerRect.top !is cornerRect.bottom) {
438                     auto hDC = OS.GetWindowDC (hwnd);
439                     OS.FillRect (hDC, &cornerRect, cast(HANDLE)( OS.COLOR_BTNFACE + 1));
440                     Decorations shell = menuShell ();
441                     if ((shell.style & DWT.RESIZE) !is 0) {
442                         auto hwndScroll = shell.scrolledHandle ();
443                         bool drawGripper = hwnd is hwndScroll;
444                         if (!drawGripper) {
445                             RECT shellRect;
446                             OS.GetClientRect (hwndScroll, &shellRect);
447                             OS.MapWindowPoints (hwndScroll, null, cast(POINT*)&shellRect, 2);
448                             drawGripper = shellRect.right is windowRect.right && shellRect.bottom is windowRect.bottom;
449                         }
450                         if (drawGripper) {
451                             OS.DrawThemeBackground (display.hScrollBarTheme(), hDC, OS.SBP_SIZEBOX, 0, &cornerRect, null);
452                         }
453                     }
454                     OS.ReleaseDC (hwnd, hDC);
455                 }
456             }
457         }
458     }
459     return result;
460 }
461
462 LRESULT wmScroll (ScrollBar bar, bool update, HWND hwnd, int msg, int /*long*/ wParam, int /*long*/ lParam) {
463     LRESULT result = null;
464     if (update) {
465         int type = msg is OS.WM_HSCROLL ? OS.SB_HORZ : OS.SB_VERT;
466         SCROLLINFO info;
467         info.cbSize = SCROLLINFO.sizeof;
468         info.fMask = OS.SIF_TRACKPOS | OS.SIF_POS | OS.SIF_RANGE;
469         OS.GetScrollInfo (hwnd, type, &info);
470         info.fMask = OS.SIF_POS;
471         int code = OS.LOWORD (wParam);
472         switch (code) {
473             case OS.SB_ENDSCROLL:  return null;
474             case OS.SB_THUMBPOSITION:
475             case OS.SB_THUMBTRACK:
476                 /*
477                 * Note: On WinCE, the value in SB_THUMBPOSITION is relative to nMin.
478                 * Same for SB_THUMBPOSITION 'except' for the very first thumb track
479                 * message which has the actual value of nMin. This is a problem when
480                 * nMin is not zero.
481                 */
482                 info.nPos = info.nTrackPos;
483                 break;
484             case OS.SB_TOP:
485                 info.nPos = info.nMin;
486                 break;
487             case OS.SB_BOTTOM:
488                 info.nPos = info.nMax;
489                 break;
490             case OS.SB_LINEDOWN:
491                 info.nPos += bar.getIncrement ();
492                 break;
493             case OS.SB_LINEUP:
494                 int increment = bar.getIncrement ();
495                 info.nPos = Math.max (info.nMin, info.nPos - increment);
496                 break;
497             case OS.SB_PAGEDOWN:
498                 info.nPos += bar.getPageIncrement ();
499                 break;
500             case OS.SB_PAGEUP:
501                 int pageIncrement = bar.getPageIncrement ();
502                 info.nPos = Math.max (info.nMin, info.nPos - pageIncrement);
503                 break;
504             default:
505         }
506         OS.SetScrollInfo (hwnd, type, &info, true);
507     } else {
508         int /*long*/ code = callWindowProc (hwnd, msg, wParam, lParam);
509         result = code is 0 ? LRESULT.ZERO : new LRESULT (code);
510     }
511     bar.wmScrollChild (wParam, lParam);
512     return result;
513 }
514
515 }
Note: See TracBrowser for help on using the browser.