|
1 /******************************************************************************* |
|
2 * Copyright (c) 2000, 2007 IBM Corporation and others. |
|
3 * Portion Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). |
|
4 * All rights reserved. This program and the accompanying materials |
|
5 * are made available under the terms of the Eclipse Public License v1.0 |
|
6 * which accompanies this distribution, and is available at |
|
7 * http://www.eclipse.org/legal/epl-v10.html |
|
8 * |
|
9 * Contributors: |
|
10 * IBM Corporation - initial API and implementation |
|
11 * Nokia Corporation - Qt implementation |
|
12 *******************************************************************************/ |
|
13 package org.eclipse.swt.widgets; |
|
14 |
|
15 import org.eclipse.swt.SWT; |
|
16 import org.eclipse.swt.graphics.Point; |
|
17 import org.eclipse.swt.graphics.Rectangle; |
|
18 import org.eclipse.swt.internal.qt.OS; |
|
19 import org.eclipse.swt.internal.qt.WidgetConstant; |
|
20 import org.eclipse.swt.internal.qt.WidgetState; |
|
21 |
|
22 /** |
|
23 * This class is the abstract superclass of all classes which represent controls |
|
24 * that have standard scroll bars. |
|
25 * <dl> |
|
26 * <dt><b>Styles:</b></dt> |
|
27 * <dd>H_SCROLL, V_SCROLL</dd> |
|
28 * <dt><b>Events:</b> |
|
29 * <dd>(none)</dd> |
|
30 * </dl> |
|
31 * <p> |
|
32 * IMPORTANT: This class is intended to be subclassed <em>only</em> within the |
|
33 * SWT implementation. |
|
34 * </p> |
|
35 */ |
|
36 public abstract class Scrollable extends Control { |
|
37 ScrollBar horizontalBar; |
|
38 ScrollBar verticalBar; |
|
39 |
|
40 /* |
|
41 * Handle of the native object implementing QAbstractScrollArea for the |
|
42 * widget. May be 0 because not all Scrollable objects derive from |
|
43 * QAbstractScrollArea in native side. |
|
44 * Must be checked if this is valid before using! |
|
45 */ |
|
46 int scrollAreaHandle; |
|
47 |
|
48 /** |
|
49 * Prevents uninitialized instances from being created outside the package. |
|
50 */ |
|
51 Scrollable() { |
|
52 } |
|
53 |
|
54 /** |
|
55 * Constructs a new instance of this class given its parent and a style |
|
56 * value describing its behavior and appearance. |
|
57 * <p> |
|
58 * The style value is either one of the style constants defined in class |
|
59 * <code>SWT</code> which is applicable to instances of this class, or must |
|
60 * be built by <em>bitwise OR</em>'ing together (that is, using the |
|
61 * <code>int</code> "|" operator) two or more of those <code>SWT</code> |
|
62 * style constants. The class description lists the style constants that are |
|
63 * applicable to the class. Style bits are also inherited from superclasses. |
|
64 * </p> |
|
65 * |
|
66 * @param parent |
|
67 * a composite control which will be the parent of the new |
|
68 * instance (cannot be null) |
|
69 * @param style |
|
70 * the style of control to construct |
|
71 * |
|
72 * @exception IllegalArgumentException |
|
73 * <ul> |
|
74 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
|
75 * </ul> |
|
76 * @exception SWTException |
|
77 * <ul> |
|
78 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the |
|
79 * thread that created the parent</li> |
|
80 * <li>ERROR_INVALID_SUBCLASS - if this class is not an |
|
81 * allowed subclass</li> |
|
82 * </ul> |
|
83 * |
|
84 * @see SWT#H_SCROLL |
|
85 * @see SWT#V_SCROLL |
|
86 * @see Widget#checkSubclass |
|
87 * @see Widget#getStyle |
|
88 */ |
|
89 public Scrollable(Composite parent, int style) { |
|
90 this(parent, style, 0, null, false); |
|
91 } |
|
92 |
|
93 /** |
|
94 * <p> |
|
95 * <b>IMPORTANT:</b> This constructor is <em>not</em> part of the SWT |
|
96 * public API. It should never be referenced from application code. |
|
97 * </p> |
|
98 */ |
|
99 protected Scrollable(Composite parent, int style, int extraStyle, Object packageProxy, |
|
100 boolean isExtended) { |
|
101 super(parent, style, extraStyle, packageProxy, isExtended); |
|
102 } |
|
103 |
|
104 int clientHandle() { |
|
105 return handle; |
|
106 } |
|
107 |
|
108 public Point computeSize(int wHint, int hHint, boolean changed) { |
|
109 checkWidget(); |
|
110 |
|
111 if (scrollAreaHandle == 0) { |
|
112 return super.computeSize(wHint, hHint, changed); |
|
113 } |
|
114 |
|
115 if (changed) { |
|
116 OS.QWidget_updateGeometry(topHandle); |
|
117 } |
|
118 |
|
119 Point preferredSize = (packageProxy != null ? |
|
120 packageProxy.getPreferredClientAreaSize() : getPreferredClientAreaSize_pp()); |
|
121 Rectangle trim = computeTrim(0, 0, preferredSize.x, preferredSize.y); |
|
122 Point size = new Point(trim.width, trim.height); |
|
123 |
|
124 if (wHint != SWT.DEFAULT) { |
|
125 size.x = wHint + trim.width - preferredSize.x; |
|
126 } |
|
127 if (hHint != SWT.DEFAULT) { |
|
128 size.y = hHint + trim.height - preferredSize.y; |
|
129 } |
|
130 |
|
131 return size; |
|
132 } |
|
133 |
|
134 /** |
|
135 * Given a desired <em>client area</em> for the receiver (as described by |
|
136 * the arguments), returns the bounding rectangle which would be required to |
|
137 * produce that client area. |
|
138 * <p> |
|
139 * In other words, it returns a rectangle such that, if the receiver's |
|
140 * bounds were set to that rectangle, the area of the receiver which is |
|
141 * capable of displaying data (that is, not covered by the "trimmings") |
|
142 * would be the rectangle described by the arguments (relative to the |
|
143 * receiver's parent). |
|
144 * </p> |
|
145 * |
|
146 * @param x |
|
147 * the desired x coordinate of the client area |
|
148 * @param y |
|
149 * the desired y coordinate of the client area |
|
150 * @param width |
|
151 * the desired width of the client area |
|
152 * @param height |
|
153 * the desired height of the client area |
|
154 * @return the required bounds to produce the given client area |
|
155 * |
|
156 * @exception SWTException |
|
157 * <ul> |
|
158 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been |
|
159 * disposed</li> |
|
160 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the |
|
161 * thread that created the receiver</li> |
|
162 * </ul> |
|
163 * |
|
164 * @see #getClientArea |
|
165 */ |
|
166 public Rectangle computeTrim(int x, int y, int width, int height) { |
|
167 checkWidget(); |
|
168 int border = getBorderWidth(); |
|
169 x -= border; |
|
170 y -= border; |
|
171 width += 2 * border; |
|
172 height += 2 * border; |
|
173 |
|
174 ScrollBar vBar = getVerticalBar(); |
|
175 if (vBar != null) { |
|
176 int barWidth = vBar.getSize().x; |
|
177 width += barWidth; |
|
178 } |
|
179 |
|
180 ScrollBar hBar = getHorizontalBar(); |
|
181 if (hBar != null) { |
|
182 height += hBar.getSize().y; |
|
183 } |
|
184 |
|
185 return new Rectangle(x, y, width, height); |
|
186 } |
|
187 |
|
188 |
|
189 final ScrollBar createScrollBar(int style) { |
|
190 if ((style & SWT.H_SCROLL) != 0) { |
|
191 setHBarPolicy(true); |
|
192 } else { |
|
193 setVBarPolicy(true); |
|
194 } |
|
195 return new ScrollBar(this, style); |
|
196 } |
|
197 |
|
198 void createWidget(int index) { |
|
199 super.createWidget(index); |
|
200 // If topHandle wasn't set by a subclass |
|
201 if(topHandle == 0) { |
|
202 // If scrollAreaHandle was set by a subclass then use that as the |
|
203 // topHandle by default |
|
204 if(scrollAreaHandle != 0) { |
|
205 topHandle = scrollAreaHandle; |
|
206 } |
|
207 } |
|
208 // Same for frameHandle |
|
209 if(frameHandle == 0) { |
|
210 if(scrollAreaHandle != 0) { |
|
211 frameHandle = scrollAreaHandle; |
|
212 } |
|
213 } |
|
214 createBars(); |
|
215 } |
|
216 |
|
217 private void createBars() { |
|
218 if (scrollAreaHandle != 0) { |
|
219 if (((style & SWT.H_SCROLL) != 0) && (state & WidgetState.EMBEDDED_SCROLLBARS) == 0) { |
|
220 if (horizontalBar == null) { |
|
221 horizontalBar = createScrollBar(SWT.H_SCROLL); |
|
222 } |
|
223 } else { |
|
224 setHBarPolicy(false); |
|
225 } |
|
226 |
|
227 if (((style & SWT.V_SCROLL) != 0) && (state & WidgetState.EMBEDDED_SCROLLBARS) == 0) { |
|
228 if (verticalBar == null) { |
|
229 verticalBar = createScrollBar(SWT.V_SCROLL); |
|
230 } |
|
231 } else { |
|
232 setVBarPolicy(false); |
|
233 } |
|
234 } |
|
235 } |
|
236 |
|
237 /** |
|
238 * Returns a rectangle which describes the area of the receiver which is |
|
239 * capable of displaying data (that is, not covered by the "trimmings"). |
|
240 * |
|
241 * @return the client area |
|
242 * |
|
243 * @exception SWTException |
|
244 * <ul> |
|
245 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been |
|
246 * disposed</li> |
|
247 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the |
|
248 * thread that created the receiver</li> |
|
249 * </ul> |
|
250 * |
|
251 * @see #computeTrim |
|
252 */ |
|
253 public Rectangle getClientArea() { |
|
254 checkWidget(); |
|
255 if(!isVisible()) updateQLayouts(); |
|
256 |
|
257 // QWidget::rect() always returns (0,0) as top left. |
|
258 // It is ok to use (0,0) as the client area top left because native viewport widget |
|
259 // is the parent widget of the Scrollable's children and viewport does have any trimmings. |
|
260 // E.g. implementations of Layout.layout(Composite, boolean) read the client |
|
261 // area top left when setting bounds for Composite's children. |
|
262 Rectangle clientArea = OS.QWidget_rect(handle); |
|
263 |
|
264 // Null size, invisible SortedList gets negative viewPort. Qt layouts suspected. |
|
265 if (clientArea.width < 0) clientArea.width = 0; |
|
266 if (clientArea.height < 0) clientArea.height = 0; |
|
267 |
|
268 return clientArea; |
|
269 } |
|
270 |
|
271 /* |
|
272 * There's a problem that Qt doesn't apply the layouts or size policies when |
|
273 * widgets are not visible. The purpose of this method is to force Qt to |
|
274 * update the layouts e.g. when getClientArea is called before widget is |
|
275 * shown. Without this getClientArea won't return correct values. |
|
276 */ |
|
277 void updateQLayouts() { |
|
278 if(parent != null) parent.updateQLayouts(); |
|
279 updateLayoutOfQWidget(topHandle); |
|
280 if(scrollAreaHandle != 0 && scrollAreaHandle != topHandle) |
|
281 updateLayoutOfQWidget(scrollAreaHandle); |
|
282 if(handle != scrollAreaHandle) |
|
283 updateLayoutOfQWidget(handle); |
|
284 |
|
285 // There's no public way to directly make QAbstractScrollArea execute its |
|
286 // layout calculation code. Corner widget functionality can be used to |
|
287 // invoke it indirectly if there's no corner widget. I.e. this has nothing |
|
288 // to do with corner widget - the purpose is just to call layout calculation |
|
289 // code by any means available. |
|
290 if(scrollAreaHandle != 0) { |
|
291 int cornerWidget = OS.QAbstractScrollArea_cornerWidget(scrollAreaHandle); |
|
292 if(cornerWidget == 0) OS.QAbstractScrollArea_setCornerWidget(scrollAreaHandle, 0); |
|
293 } |
|
294 } |
|
295 |
|
296 void updateLayoutOfQWidget(int handle) { |
|
297 int layout = OS.QWidget_layout(handle); |
|
298 if(layout != 0) { |
|
299 OS.QLayout_activate(layout); |
|
300 OS.QLayout_update(layout); |
|
301 } |
|
302 } |
|
303 |
|
304 /** |
|
305 * Returns the receiver's horizontal scroll bar if it has one, and null if |
|
306 * it does not. |
|
307 * |
|
308 * @return the horizontal scroll bar (or null) |
|
309 * |
|
310 * @exception SWTException |
|
311 * <ul> |
|
312 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been |
|
313 * disposed</li> |
|
314 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the |
|
315 * thread that created the receiver</li> |
|
316 * </ul> |
|
317 */ |
|
318 public ScrollBar getHorizontalBar() { |
|
319 checkWidget(); |
|
320 return horizontalBar; |
|
321 } |
|
322 |
|
323 /** |
|
324 * Returns the receiver's vertical scroll bar if it has one, and null if it |
|
325 * does not. |
|
326 * |
|
327 * @return the vertical scroll bar (or null) |
|
328 * |
|
329 * @exception SWTException |
|
330 * <ul> |
|
331 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been |
|
332 * disposed</li> |
|
333 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the |
|
334 * thread that created the receiver</li> |
|
335 * </ul> |
|
336 */ |
|
337 public ScrollBar getVerticalBar() { |
|
338 checkWidget(); |
|
339 return verticalBar; |
|
340 } |
|
341 |
|
342 /* |
|
343 * Gets the preferred size of the widget excluding trimings. |
|
344 */ |
|
345 Point getPreferredClientAreaSize_pp() { |
|
346 /* |
|
347 * Here, the handle is supposed to be a view port's handle. If not, |
|
348 * overwrite this method in the subclass |
|
349 */ |
|
350 if (handle == 0) |
|
351 return new Point(WidgetConstant.DEFAULT_WIDTH, WidgetConstant.DEFAULT_HEIGHT); |
|
352 /* |
|
353 /* It seems that next line always returns an invalid size since there is no associated layout for viewport widget. |
|
354 /* So the function ends up with returning a default size |
|
355 */ |
|
356 Point size = OS.QWidget_sizeHint(handle); |
|
357 if (size.x < 0) |
|
358 size.x = WidgetConstant.DEFAULT_WIDTH; |
|
359 if (size.y < 0) |
|
360 size.y = WidgetConstant.DEFAULT_HEIGHT; |
|
361 return size; |
|
362 } |
|
363 |
|
364 public void setData(String key, Object value) { |
|
365 super.setData(key, value); |
|
366 |
|
367 if (key.equals(WidgetConstant.SET_EMBEDDED_SCROLLBARS_STATE_KEY)) { |
|
368 if ((state & WidgetState.EMBEDDED_SCROLLBARS) != 0) { |
|
369 disposeBars(); |
|
370 releaseBars(); |
|
371 } else { |
|
372 createBars(); |
|
373 } |
|
374 } |
|
375 } |
|
376 |
|
377 private void disposeBars() { |
|
378 if (horizontalBar != null && !horizontalBar.isDisposed()) { |
|
379 horizontalBar.dispose(); |
|
380 } |
|
381 if (verticalBar != null && !verticalBar.isDisposed()) { |
|
382 verticalBar.dispose(); |
|
383 } |
|
384 } |
|
385 |
|
386 void releaseChildren_pp(boolean destroy) { |
|
387 releaseBars(); |
|
388 super.releaseChildren_pp(destroy); |
|
389 } |
|
390 |
|
391 private void releaseBars() { |
|
392 if (horizontalBar != null) { |
|
393 horizontalBar.release(false); |
|
394 horizontalBar = null; |
|
395 } |
|
396 if (verticalBar != null) { |
|
397 verticalBar.release(false); |
|
398 verticalBar = null; |
|
399 } |
|
400 } |
|
401 |
|
402 void deregister_pp() { |
|
403 super.deregister_pp(); |
|
404 display.removeWidget(scrollAreaHandle); |
|
405 } |
|
406 |
|
407 void register_pp() { |
|
408 super.register_pp(); |
|
409 display.addWidget(scrollAreaHandle, this); |
|
410 } |
|
411 |
|
412 void releaseHandle_pp() { |
|
413 scrollAreaHandle = 0; |
|
414 super.releaseHandle_pp(); |
|
415 } |
|
416 |
|
417 void setOrientation(int handle, int orientation) { |
|
418 checkWidget(); |
|
419 int flags = SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT; |
|
420 if ((orientation & flags) == 0 || (orientation & flags) == flags) |
|
421 return; |
|
422 |
|
423 style &= ~flags; |
|
424 style |= orientation & flags; |
|
425 |
|
426 OS.QWidget_setLayoutDirection(handle, |
|
427 orientation == SWT.LEFT_TO_RIGHT ? OS.QT_LEFTTORIGHT |
|
428 : OS.QT_RIGHTTOLEFT); |
|
429 } |
|
430 |
|
431 void setHBarPolicy(boolean status) { |
|
432 if (status) |
|
433 OS.QAbstractScrollArea_setHorizontalScrollBarPolicy(scrollAreaHandle, |
|
434 OS.QT_SCROLLBARALWAYSON); |
|
435 else |
|
436 OS.QAbstractScrollArea_setHorizontalScrollBarPolicy(scrollAreaHandle, |
|
437 OS.QT_SCROLLBARALWAYSOFF); |
|
438 } |
|
439 |
|
440 void setVBarPolicy(boolean status) { |
|
441 if (status) |
|
442 OS.QAbstractScrollArea_setVerticalScrollBarPolicy(scrollAreaHandle, |
|
443 OS.QT_SCROLLBARALWAYSON); |
|
444 else |
|
445 OS.QAbstractScrollArea_setVerticalScrollBarPolicy(scrollAreaHandle, |
|
446 OS.QT_SCROLLBARALWAYSOFF); |
|
447 } |
|
448 |
|
449 } |