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.ExpandBar; 14 15 16 import org.eclipse.swt.SWT; 17 import org.eclipse.swt.SWTException; 18 import org.eclipse.swt.events.ExpandAdapter; 19 import org.eclipse.swt.events.ExpandEvent; 20 import org.eclipse.swt.events.ExpandListener; 21 import org.eclipse.swt.graphics.FontMetrics; 22 import org.eclipse.swt.graphics.GC; 23 import org.eclipse.swt.graphics.GCData; 24 import org.eclipse.swt.graphics.Point; 25 import org.eclipse.swt.internal.gtk.OS; 26 import org.eclipse.swt.widgets.ExpandItem; 27 import org.eclipse.swt.widgets.Composite; 28 import org.eclipse.swt.widgets.ScrollBar; 29 import org.eclipse.swt.widgets.TypedListener; 30 import org.eclipse.swt.widgets.Event; 31 import org.eclipse.swt.widgets.Control; 32 import java.lang.all; 33 34 /** 35 * Instances of this class support the layout of selectable 36 * expand bar items. 37 * <p> 38 * The item children that may be added to instances of this class 39 * must be of type <code>ExpandItem</code>. 40 * </p><p> 41 * <dl> 42 * <dt><b>Styles:</b></dt> 43 * <dd>V_SCROLL</dd> 44 * <dt><b>Events:</b></dt> 45 * <dd>Expand, Collapse</dd> 46 * </dl> 47 * </p><p> 48 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 49 * </p> 50 * 51 * @see ExpandItem 52 * @see ExpandEvent 53 * @see ExpandListener 54 * @see ExpandAdapter 55 * @see <a href="http://www.eclipse.org/swt/snippets/#expandbar">ExpandBar snippets</a> 56 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 57 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 58 * 59 * @since 3.2 60 */ 61 public class ExpandBar : Composite { 62 63 alias Composite.computeSize computeSize; 64 alias Composite.createHandle createHandle; 65 alias Composite.forceFocus forceFocus; 66 alias Composite.setBounds setBounds; 67 alias Composite.setForegroundColor setForegroundColor; 68 69 ExpandItem [] items; 70 ExpandItem lastFocus; 71 int itemCount; 72 int spacing; 73 int yCurrentScroll; 74 75 /** 76 * Constructs a new instance of this class given its parent 77 * and a style value describing its behavior and appearance. 78 * <p> 79 * The style value is either one of the style constants defined in 80 * class <code>SWT</code> which is applicable to instances of this 81 * class, or must be built by <em>bitwise OR</em>'ing together 82 * (that is, using the <code>int</code> "|" operator) two or more 83 * of those <code>SWT</code> style constants. The class description 84 * lists the style constants that are applicable to the class. 85 * Style bits are also inherited from superclasses. 86 * </p> 87 * 88 * @param parent a composite control which will be the parent of the new instance (cannot be null) 89 * @param style the style of control to construct 90 * 91 * @exception IllegalArgumentException <ul> 92 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 93 * </ul> 94 * @exception SWTException <ul> 95 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 96 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 97 * </ul> 98 * 99 * @see Widget#checkSubclass 100 * @see Widget#getStyle 101 */ 102 public this (Composite parent, int style) { 103 super (parent, style); 104 } 105 106 /** 107 * Adds the listener to the collection of listeners who will 108 * be notified when an item in the receiver is expanded or collapsed 109 * by sending it one of the messages defined in the <code>ExpandListener</code> 110 * interface. 111 * 112 * @param listener the listener which should be notified 113 * 114 * @exception IllegalArgumentException <ul> 115 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 116 * </ul> 117 * @exception SWTException <ul> 118 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 119 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 120 * </ul> 121 * 122 * @see ExpandListener 123 * @see #removeExpandListener 124 */ 125 public void addExpandListener (ExpandListener listener) { 126 checkWidget (); 127 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 128 TypedListener typedListener = new TypedListener (listener); 129 addListener (SWT.Expand, typedListener); 130 addListener (SWT.Collapse, typedListener); 131 } 132 133 protected override void checkSubclass () { 134 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 135 } 136 137 public override Point computeSize (int wHint, int hHint, bool changed) { 138 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 139 if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0; 140 if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0; 141 Point size = computeNativeSize (handle, wHint, hHint, changed); 142 int border = OS.gtk_container_get_border_width (handle); 143 size.x += 2 * border; 144 size.y += 2 * border; 145 return size; 146 } else { 147 int height = 0, width = 0; 148 if (wHint is SWT.DEFAULT || hHint is SWT.DEFAULT) { 149 if (itemCount > 0) { 150 height += spacing; 151 GC gc = new GC (this); 152 for (int i = 0; i < itemCount; i++) { 153 ExpandItem item = items [i]; 154 height += item.getHeaderHeight (); 155 if (item.expanded) height += item.height; 156 height += spacing; 157 width = Math.max (width, item.getPreferredWidth (gc)); 158 } 159 gc.dispose (); 160 } 161 } 162 if (width is 0) width = DEFAULT_WIDTH; 163 if (height is 0) height = DEFAULT_HEIGHT; 164 if (wHint !is SWT.DEFAULT) width = wHint; 165 if (hHint !is SWT.DEFAULT) height = hHint; 166 return new Point (width, height); 167 } 168 } 169 170 override void createHandle (int index) { 171 state |= HANDLE; 172 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 173 fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 174 if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES); 175 OS.gtk_fixed_set_has_window (fixedHandle, true); 176 handle = cast(GtkWidget*)OS.gtk_vbox_new (false, 0); 177 if (handle is null) error (SWT.ERROR_NO_HANDLES); 178 if ((style & SWT.V_SCROLL) !is 0) { 179 scrolledHandle = cast(GtkWidget*)OS.gtk_scrolled_window_new (null, null); 180 if (scrolledHandle is null) error (SWT.ERROR_NO_HANDLES); 181 int vsp = (style & SWT.V_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER; 182 OS.gtk_scrolled_window_set_policy (scrolledHandle, OS.GTK_POLICY_NEVER, vsp); 183 OS.gtk_container_add (fixedHandle, scrolledHandle); 184 OS.gtk_scrolled_window_add_with_viewport (scrolledHandle, handle); 185 } else { 186 OS.gtk_container_add (fixedHandle, handle); 187 } 188 OS.gtk_container_set_border_width (handle, 0); 189 } else { 190 auto topHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 191 if (topHandle is null) error (SWT.ERROR_NO_HANDLES); 192 OS.gtk_fixed_set_has_window (topHandle, true); 193 if ((style & SWT.V_SCROLL) !is 0) { 194 fixedHandle = topHandle; 195 scrolledHandle = cast(GtkWidget*)OS.gtk_scrolled_window_new (null, null); 196 if (scrolledHandle is null) error (SWT.ERROR_NO_HANDLES); 197 handle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 198 if (handle is null) error (SWT.ERROR_NO_HANDLES); 199 OS.gtk_fixed_set_has_window (handle, true); 200 OS.gtk_container_add (fixedHandle, scrolledHandle); 201 202 /* 203 * Force the scrolledWindow to have a single child that is 204 * not scrolled automatically. Calling gtk_container_add() 205 * seems to add the child correctly but cause a warning. 206 */ 207 bool warnings = display.getWarnings (); 208 display.setWarnings (false); 209 OS.gtk_container_add (scrolledHandle, handle); 210 display.setWarnings (warnings); 211 } else { 212 handle = topHandle; 213 } 214 OS.GTK_WIDGET_SET_FLAGS (handle, OS.GTK_CAN_FOCUS); 215 } 216 } 217 218 void createItem (ExpandItem item, int style, int index) { 219 if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); 220 if (itemCount is items.length) { 221 ExpandItem [] newItems = new ExpandItem [itemCount + 4]; 222 System.arraycopy (items, 0, newItems, 0, items.length); 223 items = newItems; 224 } 225 System.arraycopy (items, index, items, index + 1, itemCount - index); 226 items [index] = item; 227 itemCount++; 228 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 229 if (lastFocus is null) lastFocus = item; 230 } 231 item.width = Math.max (0, getClientArea ().width - spacing * 2); 232 layoutItems (index, true); 233 } 234 235 override void createWidget (int index) { 236 super.createWidget (index); 237 items = new ExpandItem [4]; 238 } 239 240 void destroyItem (ExpandItem item) { 241 int index = 0; 242 while (index < itemCount) { 243 if (items [index] is item) break; 244 index++; 245 } 246 if (index is itemCount) return; 247 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 248 if (item is lastFocus) { 249 int focusIndex = index > 0 ? index - 1 : 1; 250 if (focusIndex < itemCount) { 251 lastFocus = items [focusIndex]; 252 lastFocus.redraw (); 253 } else { 254 lastFocus = null; 255 } 256 } 257 } 258 System.arraycopy (items, index + 1, items, index, --itemCount - index); 259 items [itemCount] = null; 260 item.redraw (); 261 layoutItems (index, true); 262 } 263 264 override GtkWidget* eventHandle () { 265 return OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0) ? fixedHandle : handle; 266 } 267 268 override bool forceFocus (GtkWidget* focusHandle) { 269 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 270 if (lastFocus !is null && lastFocus.setFocus ()) return true; 271 for (int i = 0; i < itemCount; i++) { 272 ExpandItem item = items [i]; 273 if (item.setFocus ()) return true; 274 } 275 } 276 return super.forceFocus (focusHandle); 277 } 278 279 override bool hasFocus () { 280 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 281 for (int i=0; i<itemCount; i++) { 282 ExpandItem item = items [i]; 283 if (item.hasFocus ()) return true; 284 } 285 } 286 return super.hasFocus(); 287 } 288 289 int getBandHeight () { 290 if (font is null) return ExpandItem.CHEVRON_SIZE; 291 GC gc = new GC (this); 292 FontMetrics metrics = gc.getFontMetrics (); 293 gc.dispose (); 294 return Math.max (ExpandItem.CHEVRON_SIZE, metrics.getHeight ()); 295 } 296 297 override GdkColor* getForegroundColor () { 298 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 299 if ((state & FOREGROUND) is 0) { 300 return display.getSystemColor (SWT.COLOR_TITLE_FOREGROUND).handle; 301 } 302 } 303 return super.getForegroundColor (); 304 } 305 306 /** 307 * Returns the item at the given, zero-relative index in the 308 * receiver. Throws an exception if the index is out of range. 309 * 310 * @param index the index of the item to return 311 * @return the item at the given index 312 * 313 * @exception IllegalArgumentException <ul> 314 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 315 * </ul> 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> 319 * </ul> 320 */ 321 public ExpandItem getItem (int index) { 322 checkWidget(); 323 if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); 324 return items [index]; 325 } 326 327 /** 328 * Returns the number of items contained in the receiver. 329 * 330 * @return the number of items 331 * 332 * @exception SWTException <ul> 333 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 334 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 335 * </ul> 336 */ 337 public int getItemCount () { 338 checkWidget(); 339 return itemCount; 340 } 341 342 /** 343 * Returns an array of <code>ExpandItem</code>s which are the items 344 * in the receiver. 345 * <p> 346 * Note: This is not the actual structure used by the receiver 347 * to maintain its list of items, so modifying the array will 348 * not affect the receiver. 349 * </p> 350 * 351 * @return the items in the receiver 352 * 353 * @exception SWTException <ul> 354 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 355 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 356 * </ul> 357 */ 358 public ExpandItem [] getItems () { 359 checkWidget (); 360 ExpandItem [] result = new ExpandItem [itemCount]; 361 System.arraycopy (items, 0, result, 0, itemCount); 362 return result; 363 } 364 365 /** 366 * Returns the receiver's spacing. 367 * 368 * @return the spacing 369 * 370 * @exception SWTException <ul> 371 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 372 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 373 * </ul> 374 */ 375 public int getSpacing () { 376 checkWidget (); 377 return spacing; 378 } 379 380 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 381 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 382 int x = cast(int)gdkEvent.x; 383 int y = cast(int)gdkEvent.y; 384 for (int i = 0; i < itemCount; i++) { 385 ExpandItem item = items[i]; 386 bool hover = item.x <= x && x < (item.x + item.width) && item.y <= y && y < (item.y + getBandHeight ()); 387 if (hover && item !is lastFocus) { 388 lastFocus.redraw (); 389 lastFocus = item; 390 lastFocus.redraw (); 391 forceFocus (); 392 break; 393 } 394 } 395 } 396 return super.gtk_button_press_event (widget, gdkEvent); 397 } 398 399 override int gtk_button_release_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 400 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 401 if (lastFocus !is null) { 402 int x = cast(int)gdkEvent.x; 403 int y = cast(int)gdkEvent.y; 404 bool hover = lastFocus.x <= x && x < (lastFocus.x + lastFocus.width) && lastFocus.y <= y && y < (lastFocus.y + getBandHeight ()); 405 if (hover) { 406 Event ev = new Event (); 407 ev.item = lastFocus; 408 notifyListeners (lastFocus.expanded ? SWT.Collapse : SWT.Expand, ev); 409 lastFocus.expanded = !lastFocus.expanded; 410 showItem (lastFocus); 411 } 412 } 413 } 414 return super.gtk_button_release_event (widget, gdkEvent); 415 } 416 417 override int gtk_expose_event (GtkWidget* widget, GdkEventExpose* gdkEvent) { 418 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 419 GCData data = new GCData (); 420 data.damageRgn = gdkEvent.region; 421 GC gc = GC.gtk_new (this, data); 422 OS.gdk_gc_set_clip_region (gc.handle, gdkEvent.region); 423 bool hasFocus = isFocusControl (); 424 for (int i = 0; i < itemCount; i++) { 425 ExpandItem item = items [i]; 426 item.drawItem (gc, hasFocus && item is lastFocus); 427 } 428 gc.dispose (); 429 } 430 return super.gtk_expose_event (widget, gdkEvent); 431 } 432 433 override int gtk_focus_in_event (GtkWidget* widget, GdkEventFocus* event) { 434 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 435 if (lastFocus !is null) lastFocus.redraw (); 436 } 437 return super.gtk_focus_in_event(widget, event); 438 } 439 440 override int gtk_focus_out_event (GtkWidget* widget, GdkEventFocus* event) { 441 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 442 if (lastFocus !is null) lastFocus.redraw (); 443 } 444 return super.gtk_focus_out_event (widget, event); 445 } 446 447 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* gdkEvent) { 448 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 449 if (!hasFocus ()) return 0; 450 auto result = super.gtk_key_press_event (widget, gdkEvent); 451 if (result !is 0) return result; 452 int index = 0; 453 while (index < itemCount) { 454 if (items [index].hasFocus ()) break; 455 index++; 456 } 457 bool next = false; 458 switch (gdkEvent.keyval) { 459 case OS.GDK_Up: 460 case OS.GDK_Left: next = false; break; 461 case OS.GDK_Down: 462 case OS.GDK_Right: next = true; break; 463 default: return result; 464 } 465 int start = index, offset = next ? 1 : -1; 466 while ((index = (index + offset + itemCount) % itemCount) !is start) { 467 ExpandItem item = items [index]; 468 if (item.setFocus ()) return result; 469 } 470 return result; 471 } else { 472 if (lastFocus !is null) { 473 switch (gdkEvent.keyval) { 474 case OS.GDK_Return: 475 case OS.GDK_space: 476 Event ev = new Event (); 477 ev.item = lastFocus; 478 sendEvent (lastFocus.expanded ? SWT.Collapse :SWT.Expand, ev); 479 lastFocus.expanded = !lastFocus.expanded; 480 showItem (lastFocus); 481 break; 482 case OS.GDK_Up: 483 case OS.GDK_KP_Up: { 484 int focusIndex = indexOf (lastFocus); 485 if (focusIndex > 0) { 486 lastFocus.redraw (); 487 lastFocus = items [focusIndex - 1]; 488 lastFocus.redraw (); 489 } 490 break; 491 } 492 case OS.GDK_Down: 493 case OS.GDK_KP_Down: { 494 int focusIndex = indexOf (lastFocus); 495 if (focusIndex < itemCount - 1) { 496 lastFocus.redraw (); 497 lastFocus = items [focusIndex + 1]; 498 lastFocus.redraw (); 499 } 500 break; 501 } 502 default: 503 } 504 } 505 } 506 return super.gtk_key_press_event (widget, gdkEvent); 507 } 508 509 /** 510 * Searches the receiver's list starting at the first item 511 * (index 0) until an item is found that is equal to the 512 * argument, and returns the index of that item. If no item 513 * is found, returns -1. 514 * 515 * @param item the search item 516 * @return the index of the item 517 * 518 * @exception IllegalArgumentException <ul> 519 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 520 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> 521 * </ul> 522 * @exception SWTException <ul> 523 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 524 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 525 * </ul> 526 */ 527 public int indexOf (ExpandItem item) { 528 checkWidget(); 529 if (item is null) error (SWT.ERROR_NULL_ARGUMENT); 530 for (int i = 0; i < itemCount; i++) { 531 if (items [i] is item) return i; 532 } 533 return -1; 534 } 535 536 void layoutItems (int index, bool setScrollbar_) { 537 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 538 for (int i = 0; i < itemCount; i++) { 539 ExpandItem item = items [i]; 540 if (item !is null) item.resizeControl (yCurrentScroll); 541 } 542 } else { 543 if (index < itemCount) { 544 int y = spacing - yCurrentScroll; 545 for (int i = 0; i < index; i++) { 546 ExpandItem item = items [i]; 547 if (item.expanded) y += item.height; 548 y += item.getHeaderHeight() + spacing; 549 } 550 for (int i = index; i < itemCount; i++) { 551 ExpandItem item = items [i]; 552 item.setBounds (spacing, y, 0, 0, true, false); 553 if (item.expanded) y += item.height; 554 y += item.getHeaderHeight() + spacing; 555 } 556 } 557 if (setScrollbar_) setScrollbar (); 558 } 559 } 560 561 override GtkWidget* parentingHandle () { 562 return OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0) ? fixedHandle : handle; 563 } 564 565 override void releaseChildren (bool destroy) { 566 for (int i = 0; i < itemCount; i++) { 567 ExpandItem item = items [i]; 568 if (item !is null && !item.isDisposed ()) { 569 item.release (false); 570 } 571 } 572 super.releaseChildren (destroy); 573 } 574 575 /** 576 * Removes the listener from the collection of listeners who will 577 * be notified when items in the receiver are expanded or collapsed. 578 * 579 * @param listener the listener which should no longer be notified 580 * 581 * @exception IllegalArgumentException <ul> 582 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 583 * </ul> 584 * @exception SWTException <ul> 585 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 586 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 587 * </ul> 588 * 589 * @see ExpandListener 590 * @see #addExpandListener 591 */ 592 public void removeExpandListener (ExpandListener listener) { 593 checkWidget (); 594 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 595 if (eventTable is null) return; 596 eventTable.unhook (SWT.Expand, listener); 597 eventTable.unhook (SWT.Collapse, listener); 598 } 599 600 override int setBounds (int x, int y, int width, int height, bool move, bool resize) { 601 int result = super.setBounds (x, y, width, height, move, resize); 602 if (OS.GTK_VERSION < OS.buildVERSION (2, 4, 0)) { 603 if (resize) { 604 if ((style & SWT.V_SCROLL) !is 0) { 605 setScrollbar (); 606 } else { 607 for (int i = 0; i < itemCount; i++) { 608 ExpandItem item = items [i]; 609 int newWidth = Math.max (0, getClientArea ().width - spacing * 2); 610 if (item.width !is newWidth) { 611 item.setBounds (0, 0, newWidth, item.height, false, true); 612 } 613 } 614 } 615 } 616 } 617 return result; 618 } 619 620 override void setFontDescription (PangoFontDescription* font) { 621 super.setFontDescription (font); 622 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 623 for (int i = 0; i < itemCount; i++) { 624 items[i].setFontDescription (font); 625 } 626 layoutItems (0, true); 627 } 628 } 629 630 override void setForegroundColor (GdkColor* color) { 631 super.setForegroundColor (color); 632 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 633 for (int i = 0; i < itemCount; i++) { 634 items[i].setForegroundColor (color); 635 } 636 } 637 } 638 639 void setScrollbar () { 640 if (itemCount is 0) return; 641 if ((style & SWT.V_SCROLL) is 0) return; 642 int height = getClientArea ().height; 643 ExpandItem item = items [itemCount - 1]; 644 int maxHeight = item.y + getBandHeight () + spacing; 645 if (item.expanded) maxHeight += item.height; 646 auto adjustmentHandle = OS.gtk_scrolled_window_get_vadjustment (scrolledHandle); 647 yCurrentScroll = cast(int)adjustmentHandle.value; 648 649 //claim bottom free space 650 if (yCurrentScroll > 0 && height > maxHeight) { 651 yCurrentScroll = Math.max (0, yCurrentScroll + maxHeight - height); 652 layoutItems (0, false); 653 } 654 maxHeight += yCurrentScroll; 655 adjustmentHandle.value = Math.min (yCurrentScroll, maxHeight); 656 adjustmentHandle.upper = maxHeight; 657 adjustmentHandle.page_size = height; 658 OS.gtk_adjustment_changed (adjustmentHandle); 659 int policy = maxHeight > height ? OS.GTK_POLICY_ALWAYS : OS.GTK_POLICY_NEVER; 660 OS.gtk_scrolled_window_set_policy (scrolledHandle, OS.GTK_POLICY_NEVER, policy); 661 int width = OS.GTK_WIDGET_WIDTH (fixedHandle) - spacing * 2; 662 if (policy is OS.GTK_POLICY_ALWAYS) { 663 auto vHandle = OS.GTK_SCROLLED_WINDOW_VSCROLLBAR (scrolledHandle); 664 GtkRequisition requisition; 665 OS.gtk_widget_size_request (vHandle, &requisition); 666 width -= requisition.width; 667 } 668 width = Math.max (0, width); 669 for (int i = 0; i < itemCount; i++) { 670 ExpandItem item2 = items[i]; 671 item2.setBounds (0, 0, width, item2.height, false, true); 672 } 673 } 674 675 /** 676 * Sets the receiver's spacing. Spacing specifies the number of pixels allocated around 677 * each item. 678 * 679 * @param spacing the spacing around each item 680 * 681 * @exception SWTException <ul> 682 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 683 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 684 * </ul> 685 */ 686 public void setSpacing (int spacing) { 687 checkWidget (); 688 if (spacing < 0) return; 689 if (spacing is this.spacing) return; 690 this.spacing = spacing; 691 if (OS.GTK_VERSION >= OS.buildVERSION (2, 4, 0)) { 692 OS.gtk_box_set_spacing (handle, spacing); 693 OS.gtk_container_set_border_width (handle, spacing); 694 } else { 695 if ((style & SWT.V_SCROLL) is 0) { 696 int width = Math.max (0, getClientArea ().width - spacing * 2); 697 for (int i = 0; i < itemCount; i++) { 698 ExpandItem item = items [i]; 699 if (item.width !is width) item.setBounds (0, 0, width, item.height, false, true); 700 } 701 } 702 layoutItems (0, true); 703 redraw (); 704 } 705 } 706 707 void showItem (ExpandItem item) { 708 Control control = item.control; 709 if (control !is null && !control.isDisposed ()) { 710 control.setVisible (item.expanded); 711 } 712 item.redraw (); 713 int index = indexOf (item); 714 layoutItems (index + 1, true); 715 } 716 717 override void updateScrollBarValue (ScrollBar bar) { 718 yCurrentScroll = bar.getSelection(); 719 layoutItems (0, false); 720 } 721 }