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.ToolBar; 14 15 import java.lang.all; 16 17 18 19 import org.eclipse.swt.SWT; 20 import org.eclipse.swt.SWTException; 21 import org.eclipse.swt.graphics.Point; 22 import org.eclipse.swt.internal.gtk.OS; 23 import org.eclipse.swt.widgets.ToolItem; 24 import org.eclipse.swt.widgets.Composite; 25 import org.eclipse.swt.widgets.ImageList; 26 import org.eclipse.swt.widgets.Shell; 27 import org.eclipse.swt.widgets.Decorations; 28 import org.eclipse.swt.widgets.Control; 29 import org.eclipse.swt.widgets.Menu; 30 import org.eclipse.swt.widgets.Widget; 31 import org.eclipse.swt.widgets.Event; 32 33 /** 34 * Instances of this class support the layout of selectable 35 * tool bar items. 36 * <p> 37 * The item children that may be added to instances of this class 38 * must be of type <code>ToolItem</code>. 39 * </p><p> 40 * Note that although this class is a subclass of <code>Composite</code>, 41 * it does not make sense to add <code>Control</code> children to it, 42 * or set a layout on it. 43 * </p><p> 44 * <dl> 45 * <dt><b>Styles:</b></dt> 46 * <dd>FLAT, WRAP, RIGHT, HORIZONTAL, VERTICAL, SHADOW_OUT</dd> 47 * <dt><b>Events:</b></dt> 48 * <dd>(none)</dd> 49 * </dl> 50 * <p> 51 * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. 52 * </p><p> 53 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 54 * </p> 55 * 56 * @see <a href="http://www.eclipse.org/swt/snippets/#toolbar">ToolBar, ToolItem snippets</a> 57 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 58 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 59 */ 60 public class ToolBar : Composite { 61 62 alias Composite.computeSize computeSize; 63 alias Composite.createHandle createHandle; 64 alias Composite.forceFocus forceFocus; 65 alias Composite.mnemonicHit mnemonicHit; 66 alias Composite.mnemonicMatch mnemonicMatch; 67 alias Composite.setBounds setBounds; 68 alias Composite.setForegroundColor setForegroundColor; 69 alias Composite.setToolTipText setToolTipText; 70 71 ToolItem lastFocus; 72 ImageList imageList; 73 74 /** 75 * Constructs a new instance of this class given its parent 76 * and a style value describing its behavior and appearance. 77 * <p> 78 * The style value is either one of the style constants defined in 79 * class <code>SWT</code> which is applicable to instances of this 80 * class, or must be built by <em>bitwise OR</em>'ing together 81 * (that is, using the <code>int</code> "|" operator) two or more 82 * of those <code>SWT</code> style constants. The class description 83 * lists the style constants that are applicable to the class. 84 * Style bits are also inherited from superclasses. 85 * </p> 86 * 87 * @param parent a composite control which will be the parent of the new instance (cannot be null) 88 * @param style the style of control to construct 89 * 90 * @exception IllegalArgumentException <ul> 91 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 92 * </ul> 93 * @exception SWTException <ul> 94 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 95 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 96 * </ul> 97 * 98 * @see SWT#FLAT 99 * @see SWT#WRAP 100 * @see SWT#RIGHT 101 * @see SWT#HORIZONTAL 102 * @see SWT#SHADOW_OUT 103 * @see SWT#VERTICAL 104 * @see Widget#checkSubclass() 105 * @see Widget#getStyle() 106 */ 107 public this (Composite parent, int style) { 108 super (parent, checkStyle (style)); 109 /* 110 * Ensure that either of HORIZONTAL or VERTICAL is set. 111 * NOTE: HORIZONTAL and VERTICAL have the same values 112 * as H_SCROLL and V_SCROLL so it is necessary to first 113 * clear these bits to avoid scroll bars and then reset 114 * the bits using the original style supplied by the 115 * programmer. 116 */ 117 if ((style & SWT.VERTICAL) !is 0) { 118 this.style |= SWT.VERTICAL; 119 } else { 120 this.style |= SWT.HORIZONTAL; 121 } 122 int orientation = (style & SWT.VERTICAL) !is 0 ? OS.GTK_ORIENTATION_VERTICAL : OS.GTK_ORIENTATION_HORIZONTAL; 123 OS.gtk_toolbar_set_orientation (handle, orientation); 124 } 125 126 static int checkStyle (int style) { 127 /* 128 * Feature in GTK. It is not possible to create 129 * a toolbar that wraps. Therefore, no matter what 130 * style bits are specified, clear the WRAP bits so 131 * that the style matches the behavior. 132 */ 133 if ((style & SWT.WRAP) !is 0) style &= ~SWT.WRAP; 134 /* 135 * Even though it is legal to create this widget 136 * with scroll bars, they serve no useful purpose 137 * because they do not automatically scroll the 138 * widget's client area. The fix is to clear 139 * the SWT style. 140 */ 141 return style & ~(SWT.H_SCROLL | SWT.V_SCROLL); 142 } 143 144 protected override void checkSubclass () { 145 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 146 } 147 148 override void createHandle (int index) { 149 state |= HANDLE | THEME_BACKGROUND; 150 fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 151 if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES); 152 OS.gtk_fixed_set_has_window (fixedHandle, true); 153 handle = cast(GtkWidget*)OS.gtk_toolbar_new (); 154 if (handle is null) error (SWT.ERROR_NO_HANDLES); 155 OS.gtk_container_add (fixedHandle, handle); 156 if ((style & SWT.FLAT) !is 0) { 157 OS.gtk_widget_set_name (handle, "swt-toolbar-flat".ptr ); 158 } 159 } 160 161 public override Point computeSize (int wHint, int hHint, bool changed) { 162 checkWidget (); 163 if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0; 164 if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0; 165 return computeNativeSize (handle, wHint, hHint, changed); 166 } 167 168 override GtkWidget* eventHandle () { 169 return fixedHandle; 170 } 171 172 override GtkWidget* enterExitHandle() { 173 return handle; 174 } 175 176 override void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) { 177 super.fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus); 178 ToolItem [] items = getItems (); 179 if (toolTipText is null) { 180 for (int i = 0; i < items.length; i++) { 181 ToolItem item = items [i]; 182 if (item.toolTipText !is null) { 183 item.setToolTipText(oldShell, null); 184 item.setToolTipText(newShell, item.toolTipText); 185 } 186 } 187 } 188 } 189 190 override bool forceFocus (GtkWidget* focusHandle) { 191 if (lastFocus !is null && lastFocus.setFocus ()) return true; 192 ToolItem [] items = getItems (); 193 for (int i = 0; i < items.length; i++) { 194 ToolItem item = items [i]; 195 if (item.setFocus ()) return true; 196 } 197 return super.forceFocus (focusHandle); 198 } 199 200 /** 201 * Returns the item at the given, zero-relative index in the 202 * receiver. Throws an exception if the index is out of range. 203 * 204 * @param index the index of the item to return 205 * @return the item at the given index 206 * 207 * @exception IllegalArgumentException <ul> 208 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 209 * </ul> 210 * @exception SWTException <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 ToolItem getItem (int index) { 216 checkWidget(); 217 if (!(0 <= index && index < getItemCount())) error (SWT.ERROR_INVALID_RANGE); 218 return getItems()[index]; 219 } 220 221 /** 222 * Returns the item at the given point in the receiver 223 * or null if no such item exists. The point is in the 224 * coordinate system of the receiver. 225 * 226 * @param point the point used to locate the item 227 * @return the item at the given point 228 * 229 * @exception IllegalArgumentException <ul> 230 * <li>ERROR_NULL_ARGUMENT - if the point is null</li> 231 * </ul> 232 * @exception SWTException <ul> 233 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 234 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 235 * </ul> 236 */ 237 public ToolItem getItem (Point point) { 238 checkWidget(); 239 if (point is null) error (SWT.ERROR_NULL_ARGUMENT); 240 ToolItem[] items = getItems(); 241 for (int i=0; i<items.length; i++) { 242 if (items[i].getBounds().contains(point)) return items[i]; 243 } 244 return null; 245 } 246 247 /** 248 * Returns the number of items contained in the receiver. 249 * 250 * @return the number of items 251 * 252 * @exception SWTException <ul> 253 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 254 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 255 * </ul> 256 */ 257 public int getItemCount () { 258 checkWidget(); 259 auto list = OS.gtk_container_get_children (handle); 260 if (list is null) return 0; 261 int itemCount = OS.g_list_length (list); 262 OS.g_list_free (list); 263 return itemCount; 264 } 265 266 /** 267 * Returns an array of <code>ToolItem</code>s which are the items 268 * in the receiver. 269 * <p> 270 * Note: This is not the actual structure used by the receiver 271 * to maintain its list of items, so modifying the array will 272 * not affect the receiver. 273 * </p> 274 * 275 * @return the items in the receiver 276 * 277 * @exception SWTException <ul> 278 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 279 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 280 * </ul> 281 */ 282 public ToolItem [] getItems () { 283 checkWidget(); 284 auto list = OS.gtk_container_get_children (handle); 285 if (list is null) return new ToolItem [0]; 286 int count = OS.g_list_length (list); 287 ToolItem [] result = new ToolItem [count]; 288 for (int i=0; i<count; i++) { 289 auto data = OS.g_list_nth_data (list, i); 290 Widget widget = display.getWidget (cast(GtkWidget*)data); 291 result [i] = cast(ToolItem) widget; 292 } 293 OS.g_list_free (list); 294 return result; 295 } 296 297 /** 298 * Returns the number of rows in the receiver. When 299 * the receiver has the <code>WRAP</code> style, the 300 * number of rows can be greater than one. Otherwise, 301 * the number of rows is always one. 302 * 303 * @return the number of items 304 * 305 * @exception SWTException <ul> 306 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 307 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 308 * </ul> 309 */ 310 public int getRowCount () { 311 checkWidget(); 312 /* On GTK, toolbars cannot wrap */ 313 return 1; 314 } 315 316 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* gdkEvent) { 317 if (!hasFocus ()) return 0; 318 auto result = super.gtk_key_press_event (widget, gdkEvent); 319 if (result !is 0) return result; 320 ToolItem [] items = getItems (); 321 ptrdiff_t length = items.length; 322 ptrdiff_t index = 0; 323 while (index < length) { 324 if (items [index].hasFocus ()) break; 325 index++; 326 } 327 bool next = false; 328 switch (gdkEvent.keyval) { 329 case OS.GDK_Up: 330 case OS.GDK_Left: next = false; break; 331 case OS.GDK_Down: { 332 if (0 <= index && index < length) { 333 ToolItem item = items [index]; 334 if ((item.style & SWT.DROP_DOWN) !is 0) { 335 Event event = new Event (); 336 event.detail = SWT.ARROW; 337 auto topHandle = item.topHandle (); 338 event.x = OS.GTK_WIDGET_X (topHandle); 339 event.y = (OS.GTK_WIDGET_Y (topHandle) + OS.GTK_WIDGET_HEIGHT (topHandle)); 340 if ((style & SWT.MIRRORED) !is 0) event.x = getClientWidth() - OS.GTK_WIDGET_WIDTH(topHandle) - event.x; 341 item.postEvent (SWT.Selection, event); 342 return result; 343 } 344 } 345 goto case OS.GDK_Right; 346 } 347 case OS.GDK_Right: next = true; break; 348 default: return result; 349 } 350 if ((style & SWT.MIRRORED) !is 0) next= !next; 351 ptrdiff_t start = index, offset = next ? 1 : -1; 352 while ((index = (index + offset + length) % length) !is start) { 353 ToolItem item = items [index]; 354 if (item.setFocus ()) return result; 355 } 356 return result; 357 } 358 359 override bool hasFocus () { 360 ToolItem [] items = getItems (); 361 for (int i=0; i<items.length; i++) { 362 ToolItem item = items [i]; 363 if (item.hasFocus ()) return true; 364 } 365 return super.hasFocus(); 366 } 367 368 /** 369 * Searches the receiver's list starting at the first item 370 * (index 0) until an item is found that is equal to the 371 * argument, and returns the index of that item. If no item 372 * is found, returns -1. 373 * 374 * @param item the search item 375 * @return the index of the item 376 * 377 * @exception IllegalArgumentException <ul> 378 * <li>ERROR_NULL_ARGUMENT - if the tool item is null</li> 379 * <li>ERROR_INVALID_ARGUMENT - if the tool item has been disposed</li> 380 * </ul> 381 * @exception SWTException <ul> 382 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 383 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 384 * </ul> 385 */ 386 public int indexOf (ToolItem item) { 387 checkWidget(); 388 if (item is null) error (SWT.ERROR_NULL_ARGUMENT); 389 ToolItem [] items = getItems (); 390 for (int i=0; i<items.length; i++) { 391 if (item is items[i]) return i; 392 } 393 return -1; 394 } 395 396 override bool mnemonicHit (wchar key) { 397 ToolItem [] items = getItems (); 398 for (int i=0; i<items.length; i++) { 399 auto labelHandle = items [i].labelHandle; 400 if (labelHandle !is null && mnemonicHit (labelHandle, key)) return true; 401 } 402 return false; 403 } 404 405 override bool mnemonicMatch (wchar key) { 406 ToolItem [] items = getItems (); 407 for (int i=0; i<items.length; i++) { 408 auto labelHandle = items [i].labelHandle; 409 if (labelHandle !is null && mnemonicMatch (labelHandle, key)) return true; 410 } 411 return false; 412 } 413 414 void relayout () { 415 ToolItem [] items = getItems (); 416 for (int i=0; i<items.length; i++) { 417 ToolItem item = items [i]; 418 if (item !is null) item.resizeControl (); 419 } 420 } 421 422 override void releaseChildren (bool destroy) { 423 ToolItem [] items = getItems (); 424 for (int i=0; i<items.length; i++) { 425 ToolItem item = items [i]; 426 if (item !is null && !item.isDisposed ()) { 427 item.release (false); 428 } 429 } 430 super.releaseChildren (destroy); 431 } 432 433 override void releaseWidget () { 434 super.releaseWidget (); 435 if (imageList !is null) imageList.dispose (); 436 imageList = null; 437 } 438 439 override void removeControl (Control control) { 440 super.removeControl (control); 441 ToolItem [] items = getItems (); 442 for (int i=0; i<items.length; i++) { 443 ToolItem item = items [i]; 444 if (item.control is control) item.setControl (null); 445 } 446 } 447 448 override int setBounds (int x, int y, int width, int height, bool move, bool resize) { 449 int result = super.setBounds (x, y, width, height, move, resize); 450 if ((result & RESIZED) !is 0) relayout (); 451 return result; 452 } 453 454 override void setFontDescription (PangoFontDescription* font) { 455 super.setFontDescription (font); 456 ToolItem [] items = getItems (); 457 for (int i = 0; i < items.length; i++) { 458 items[i].setFontDescription (font); 459 } 460 relayout (); 461 } 462 463 override void setForegroundColor (GdkColor* color) { 464 super.setForegroundColor (color); 465 ToolItem [] items = getItems (); 466 for (int i = 0; i < items.length; i++) { 467 items[i].setForegroundColor (color); 468 } 469 } 470 471 public override void setToolTipText (String string) { 472 checkWidget(); 473 super.setToolTipText (string); 474 Shell shell = _getShell (); 475 ToolItem [] items = getItems (); 476 for (int i = 0; i < items.length; i++) { 477 String newString = string !is null ? null : items [i].toolTipText; 478 shell.setToolTipText (items [i].handle, newString); 479 } 480 } 481 482 }