1 /*******************************************************************************
2 * Copyright (c) 2000, 2013 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.widgets;
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.graphics.*;
18 import org.eclipse.swt.internal.*;
19 import org.eclipse.swt.internal.win32.*;
22 * Instances of this class represent a selectable user interface object
23 * that represents a expandable item in a expand bar.
25 * <dt><b>Styles:</b></dt>
27 * <dt><b>Events:</b></dt>
31 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
35 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
38 * @noextend This class is not intended to be subclassed by clients.
40 public class ExpandItem extends Item {
43 boolean expanded, hover;
44 int x, y, width, height;
45 int imageHeight, imageWidth;
46 static final int TEXT_INSET = 6;
47 static final int BORDER = 1;
48 static final int CHEVRON_SIZE = 24;
51 * Constructs a new instance of this class given its parent
52 * and a style value describing its behavior and appearance.
54 * The style value is either one of the style constants defined in
55 * class <code>SWT</code> which is applicable to instances of this
56 * class, or must be built by <em>bitwise OR</em>'ing together
57 * (that is, using the <code>int</code> "|" operator) two or more
58 * of those <code>SWT</code> style constants. The class description
59 * lists the style constants that are applicable to the class.
60 * Style bits are also inherited from superclasses.
63 * @param parent a composite control which will be the parent of the new instance (cannot be null)
64 * @param style the style of control to construct
66 * @exception IllegalArgumentException <ul>
67 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
69 * @exception SWTException <ul>
70 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
71 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
74 * @see Widget#checkSubclass
75 * @see Widget#getStyle
77 public ExpandItem (ExpandBar parent, int style) {
78 this (parent, style, checkNull (parent).getItemCount ());
82 * Constructs a new instance of this class given its parent, a
83 * style value describing its behavior and appearance, and the index
84 * at which to place it in the items maintained by its parent.
86 * The style value is either one of the style constants defined in
87 * class <code>SWT</code> which is applicable to instances of this
88 * class, or must be built by <em>bitwise OR</em>'ing together
89 * (that is, using the <code>int</code> "|" operator) two or more
90 * of those <code>SWT</code> style constants. The class description
91 * lists the style constants that are applicable to the class.
92 * Style bits are also inherited from superclasses.
95 * @param parent a composite control which will be the parent of the new instance (cannot be null)
96 * @param style the style of control to construct
97 * @param index the zero-relative index to store the receiver in its parent
99 * @exception IllegalArgumentException <ul>
100 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
101 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
103 * @exception SWTException <ul>
104 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
105 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
108 * @see Widget#checkSubclass
109 * @see Widget#getStyle
111 public ExpandItem (ExpandBar parent, int style, int index) {
112 super (parent, style);
113 this.parent = parent;
114 parent.createItem (this, style, index);
117 static ExpandBar checkNull (ExpandBar control) {
118 if (control == null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
122 private void drawChevron (long hDC, RECT rect) {
123 long oldBrush = OS.SelectObject (hDC, OS.GetSysColorBrush (OS.COLOR_BTNFACE));
124 OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
125 OS.SelectObject (hDC, oldBrush);
130 long hPen = OS.CreatePen (OS.PS_SOLID, 1, parent.getForegroundPixel ());
131 long oldPen = OS.SelectObject (hDC, hPen);
132 int [] polyline1, polyline2;
134 int px = rect.left + 5;
135 int py = rect.top + 7;
136 polyline1 = new int [] {
137 px,py, px+1,py, px+1,py-1, px+2,py-1, px+2,py-2, px+3,py-2, px+3,py-3,
138 px+3,py-2, px+4,py-2, px+4,py-1, px+5,py-1, px+5,py, px+7,py};
140 polyline2 = new int [] {
141 px,py, px+1,py, px+1,py-1, px+2,py-1, px+2,py-2, px+3,py-2, px+3,py-3,
142 px+3,py-2, px+4,py-2, px+4,py-1, px+5,py-1, px+5,py, px+7,py};
144 int px = rect.left + 5;
145 int py = rect.top + 4;
146 polyline1 = new int[] {
147 px,py, px+1,py, px+1,py+1, px+2,py+1, px+2,py+2, px+3,py+2, px+3,py+3,
148 px+3,py+2, px+4,py+2, px+4,py+1, px+5,py+1, px+5,py, px+7,py};
150 polyline2 = new int [] {
151 px,py, px+1,py, px+1,py+1, px+2,py+1, px+2,py+2, px+3,py+2, px+3,py+3,
152 px+3,py+2, px+4,py+2, px+4,py+1, px+5,py+1, px+5,py, px+7,py};
154 OS.Polyline (hDC, polyline1, polyline1.length / 2);
155 OS.Polyline (hDC, polyline2, polyline2.length / 2);
157 long whitePen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_3DHILIGHT));
158 long darkGrayPen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_3DSHADOW));
159 OS.SelectObject (hDC, whitePen);
161 rect.left, rect.bottom,
163 rect.right, rect.top};
164 OS.Polyline (hDC, points1, points1.length / 2);
165 OS.SelectObject (hDC, darkGrayPen);
167 rect.right, rect.top,
168 rect.right, rect.bottom,
169 rect.left, rect.bottom};
170 OS.Polyline (hDC, points2, points2.length / 2);
171 OS.SelectObject (hDC, oldPen);
172 OS.DeleteObject (whitePen);
173 OS.DeleteObject (darkGrayPen);
175 OS.SelectObject (hDC, oldPen);
177 OS.DeleteObject (hPen);
180 void drawItem (GC gc, long hTheme, RECT clipRect, boolean drawFocus) {
181 long hDC = gc.handle;
182 int headerHeight = parent.getBandHeight ();
183 RECT rect = new RECT ();
184 OS.SetRect (rect, x, y, x + width, y + headerHeight);
186 OS.DrawThemeBackground (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, rect, clipRect);
188 long oldBrush = OS.SelectObject (hDC, OS.GetSysColorBrush (OS.COLOR_BTNFACE));
189 OS.PatBlt (hDC, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, OS.PATCOPY);
190 OS.SelectObject (hDC, oldBrush);
193 rect.left += ExpandItem.TEXT_INSET;
194 if (imageHeight > headerHeight) {
195 gc.drawImage (image, DPIUtil.autoScaleDown(rect.left), DPIUtil.autoScaleDown(rect.top + headerHeight - imageHeight));
197 gc.drawImage (image, DPIUtil.autoScaleDown(rect.left), DPIUtil.autoScaleDown(rect.top + (headerHeight - imageHeight) / 2));
199 rect.left += imageWidth;
201 if (text.length () > 0) {
202 rect.left += ExpandItem.TEXT_INSET;
204 if ((style & SWT.FLIP_TEXT_DIRECTION) != 0) {
205 int bits = OS.GetWindowLong (parent.handle, OS.GWL_EXSTYLE);
206 if ((bits & OS.WS_EX_LAYOUTRTL) != 0) {
207 buffer = (LRE + text).toCharArray ();
209 buffer = (RLE + text).toCharArray ();
213 buffer = text.toCharArray ();
216 OS.DrawThemeText (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, buffer, buffer.length, OS.DT_VCENTER | OS.DT_SINGLELINE, 0, rect);
218 int oldBkMode = OS.SetBkMode (hDC, OS.TRANSPARENT);
219 OS.DrawText (hDC, buffer, buffer.length, rect, OS.DT_VCENTER | OS.DT_SINGLELINE);
220 OS.SetBkMode (hDC, oldBkMode);
223 int chevronSize = ExpandItem.CHEVRON_SIZE;
224 rect.left = rect.right - chevronSize;
225 rect.top = y + (headerHeight - chevronSize) / 2;
226 rect.bottom = rect.top + chevronSize;
228 int partID = expanded ? OS.EBP_NORMALGROUPCOLLAPSE : OS.EBP_NORMALGROUPEXPAND;
229 int stateID = hover ? OS.EBNGC_HOT : OS.EBNGC_NORMAL;
230 OS.DrawThemeBackground (hTheme, hDC, partID, stateID, rect, clipRect);
232 drawChevron (hDC, rect);
235 OS.SetRect (rect, x + 1, y + 1, x + width - 2, y + headerHeight - 2);
236 OS.DrawFocusRect (hDC, rect);
239 if (!parent.isAppThemed ()) {
240 long pen = OS.CreatePen (OS.PS_SOLID, 1, OS.GetSysColor (OS.COLOR_BTNFACE));
241 long oldPen = OS.SelectObject (hDC, pen);
244 x, y + headerHeight + height,
245 x + width - 1, y + headerHeight + height,
246 x + width - 1, y + headerHeight - 1};
247 OS.Polyline (hDC, points, points.length / 2);
248 OS.SelectObject (hDC, oldPen);
249 OS.DeleteObject (pen);
255 void destroyWidget () {
256 parent.destroyItem (this);
261 * Returns the control that is shown when the item is expanded.
262 * If no control has been set, return <code>null</code>.
264 * @return the control
266 * @exception SWTException <ul>
267 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
268 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
271 public Control getControl () {
277 * Returns <code>true</code> if the receiver is expanded,
278 * and false otherwise.
280 * @return the expanded state
282 * @exception SWTException <ul>
283 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
284 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
287 public boolean getExpanded () {
293 * Returns the height of the receiver's header
295 * @return the height of the header
297 * @exception SWTException <ul>
298 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
299 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
302 public int getHeaderHeight () {
304 return DPIUtil.autoScaleDown(getHeaderHeightInPixels());
307 int getHeaderHeightInPixels () {
308 return Math.max (parent.getBandHeight (), imageHeight);
312 * Gets the height of the receiver.
316 * @exception SWTException <ul>
317 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
318 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
321 public int getHeight () {
323 return DPIUtil.autoScaleDown(getHeightInPixels());
326 int getHeightInPixels () {
331 * Returns the receiver's parent, which must be a <code>ExpandBar</code>.
333 * @return the receiver's parent
335 * @exception SWTException <ul>
336 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
337 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
340 public ExpandBar getParent () {
345 int getPreferredWidth (long hTheme, long hDC) {
346 int width = ExpandItem.TEXT_INSET * 2 + ExpandItem.CHEVRON_SIZE;
348 width += ExpandItem.TEXT_INSET + imageWidth;
350 if (text.length() > 0) {
351 RECT rect = new RECT ();
352 char [] buffer = text.toCharArray ();
354 OS.GetThemeTextExtent (hTheme, hDC, OS.EBP_NORMALGROUPHEAD, 0, buffer, buffer.length, OS.DT_SINGLELINE, null, rect);
356 OS.DrawText (hDC, buffer, buffer.length, rect, OS.DT_CALCRECT);
358 width += (rect.right - rect.left);
363 boolean isHover (int x, int y) {
364 int bandHeight = parent.getBandHeight ();
365 return this.x < x && x < (this.x + width) && this.y < y && y < (this.y + bandHeight);
368 void redraw (boolean all) {
369 long parentHandle = parent.handle;
370 int headerHeight = parent.getBandHeight ();
371 RECT rect = new RECT ();
372 int left = all ? x : x + width - headerHeight;
373 OS.SetRect (rect, left, y, x + width, y + headerHeight);
374 OS.InvalidateRect (parentHandle, rect, true);
375 if (imageHeight > headerHeight) {
376 OS.SetRect (rect, x + ExpandItem.TEXT_INSET, y + headerHeight - imageHeight, x + ExpandItem.TEXT_INSET + imageWidth, y);
377 OS.InvalidateRect (parentHandle, rect, true);
379 if (!parent.isAppThemed ()) {
380 OS.SetRect (rect, x, y + headerHeight, x + width, y + headerHeight + height + 1);
381 OS.InvalidateRect (parentHandle, rect, true);
386 void releaseHandle () {
387 super.releaseHandle ();
392 void releaseWidget () {
393 super.releaseWidget ();
397 void setBoundsInPixels (int x, int y, int width, int height, boolean move, boolean size) {
399 int headerHeight = parent.getBandHeight ();
401 if (imageHeight > headerHeight) {
402 y += (imageHeight - headerHeight);
410 this.height = height;
413 if (control != null && !control.isDisposed ()) {
414 if (!parent.isAppThemed ()) {
416 width = Math.max (0, width - BORDER * 2);
417 height = Math.max (0, height - BORDER);
419 if (move && size) control.setBoundsInPixels (x, y + headerHeight, width, height);
420 if (move && !size) control.setLocationInPixels (x, y + headerHeight);
421 if (!move && size) control.setSizeInPixels (width, height);
426 * Sets the control that is shown when the item is expanded.
428 * @param control the new control (or null)
430 * @exception IllegalArgumentException <ul>
431 * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
432 * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
434 * @exception SWTException <ul>
435 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
436 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
439 public void setControl (Control control) {
441 if (control != null) {
442 if (control.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
443 if (control.parent != parent) error (SWT.ERROR_INVALID_PARENT);
445 this.control = control;
446 if (control != null) {
447 int headerHeight = parent.getBandHeight ();
448 control.setVisible (expanded);
449 if (!parent.isAppThemed ()) {
450 int width = Math.max (0, this.width - BORDER * 2);
451 int height = Math.max (0, this.height - BORDER);
452 control.setBoundsInPixels (x + BORDER, y + headerHeight, width, height);
454 control.setBoundsInPixels (x, y + headerHeight, width, height);
460 * Sets the expanded state of the receiver.
462 * @param expanded the new expanded state
464 * @exception SWTException <ul>
465 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
466 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
469 public void setExpanded (boolean expanded) {
471 this.expanded = expanded;
472 parent.showItem (this);
476 * Sets the height of the receiver. This is height of the item when it is expanded,
477 * excluding the height of the header.
479 * @param height the new height
481 * @exception SWTException <ul>
482 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
483 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
486 public void setHeight (int height) {
488 setHeightInPixels(DPIUtil.autoScaleUp(height));
491 void setHeightInPixels (int height) {
492 if (height < 0) return;
493 setBoundsInPixels (0, 0, width, height, false, true);
494 if (expanded) parent.layoutItems (parent.indexOf (this) + 1, true);
498 public void setImage (Image image) {
499 super.setImage (image);
500 int oldImageHeight = imageHeight;
502 Rectangle bounds = image.getBoundsInPixels ();
503 imageHeight = bounds.height;
504 imageWidth = bounds.width;
506 imageHeight = imageWidth = 0;
508 if (oldImageHeight != imageHeight) {
509 parent.layoutItems (parent.indexOf (this), true);
516 public void setText (String string) {
517 super.setText (string);
518 if ((state & HAS_AUTO_DIRECTION) != 0) {
519 updateTextDirection (AUTO_TEXT_DIRECTION);