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.CoolBar; 14 15 import org.eclipse.swt.SWT; 16 import org.eclipse.swt.SWTException; 17 import org.eclipse.swt.graphics.Color; 18 import org.eclipse.swt.graphics.Cursor; 19 import org.eclipse.swt.graphics.GC; 20 import org.eclipse.swt.graphics.Point; 21 import org.eclipse.swt.graphics.Rectangle; 22 import org.eclipse.swt.widgets.Composite; 23 import org.eclipse.swt.widgets.CoolItem; 24 import org.eclipse.swt.widgets.Event; 25 import org.eclipse.swt.widgets.Control; 26 import org.eclipse.swt.widgets.Listener; 27 28 import java.lang.all; 29 30 /** 31 * Instances of this class provide an area for dynamically 32 * positioning the items they contain. 33 * <p> 34 * The item children that may be added to instances of this class 35 * must be of type <code>CoolItem</code>. 36 * </p><p> 37 * Note that although this class is a subclass of <code>Composite</code>, 38 * it does not make sense to add <code>Control</code> children to it, 39 * or set a layout on it. 40 * </p><p> 41 * <dl> 42 * <dt><b>Styles:</b></dt> 43 * <dd>FLAT, HORIZONTAL, VERTICAL</dd> 44 * <dt><b>Events:</b></dt> 45 * <dd>(none)</dd> 46 * </dl> 47 * </p><p> 48 * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified. 49 * </p><p> 50 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 51 * </p> 52 * 53 * @see <a href="http://www.eclipse.org/swt/snippets/#coolbar">CoolBar snippets</a> 54 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 55 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 56 */ 57 public class CoolBar : Composite { 58 59 alias Composite.computeSize computeSize; 60 alias Composite.setCursor setCursor; 61 62 CoolItem[][] items; 63 CoolItem[] originalItems; 64 Cursor hoverCursor, dragCursor, cursor; 65 CoolItem dragging = null; 66 int mouseXOffset, itemXOffset; 67 bool isLocked = false; 68 bool inDispose = false; 69 static const int ROW_SPACING = 2; 70 static const int CLICK_DISTANCE = 3; 71 static const int DEFAULT_COOLBAR_WIDTH = 0; 72 static const int DEFAULT_COOLBAR_HEIGHT = 0; 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 99 * @see Widget#checkSubclass 100 * @see Widget#getStyle 101 */ 102 public this (Composite parent, int style) { 103 super (parent, checkStyle(style)); 104 if ((style & SWT.VERTICAL) !is 0) { 105 this.style |= SWT.VERTICAL; 106 hoverCursor = new Cursor(display, SWT.CURSOR_SIZENS); 107 } else { 108 this.style |= SWT.HORIZONTAL; 109 hoverCursor = new Cursor(display, SWT.CURSOR_SIZEWE); 110 } 111 dragCursor = new Cursor(display, SWT.CURSOR_SIZEALL); 112 Listener listener = new class () Listener { 113 public void handleEvent(Event event) { 114 switch (event.type) { 115 case SWT.Dispose: onDispose(event); break; 116 case SWT.MouseDown: onMouseDown(event); break; 117 case SWT.MouseExit: onMouseExit(); break; 118 case SWT.MouseMove: onMouseMove(event); break; 119 case SWT.MouseUp: onMouseUp(event); break; 120 case SWT.MouseDoubleClick: onMouseDoubleClick(event); break; 121 case SWT.Paint: onPaint(event); break; 122 case SWT.Resize: onResize(); break; 123 default: 124 } 125 } 126 }; 127 int[] events = [ 128 SWT.Dispose, 129 SWT.MouseDown, 130 SWT.MouseExit, 131 SWT.MouseMove, 132 SWT.MouseUp, 133 SWT.MouseDoubleClick, 134 SWT.Paint, 135 SWT.Resize 136 ]; 137 for (int i = 0; i < events.length; i++) { 138 addListener(events[i], listener); 139 } 140 } 141 static int checkStyle (int style) { 142 style |= SWT.NO_FOCUS; 143 return (style | SWT.NO_REDRAW_RESIZE) & ~(SWT.V_SCROLL | SWT.H_SCROLL); 144 } 145 void _setCursor (Cursor cursor) { 146 if (this.cursor !is null) return; 147 super.setCursor (cursor); 148 } 149 protected override void checkSubclass () { 150 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 151 } 152 153 public override Point computeSize (int wHint, int hHint, bool changed) { 154 checkWidget(); 155 int width = 0, height = 0; 156 wrapItems((style & SWT.VERTICAL) !is 0 ? hHint : wHint); 157 bool flat = (style & SWT.FLAT) !is 0; 158 for (int row = 0; row < items.length; row++) { 159 int rowWidth = 0, rowHeight = 0; 160 for (int i = 0; i < items[row].length; i++) { 161 CoolItem item = items[row][i]; 162 rowWidth += item.preferredWidth; 163 rowHeight = Math.max(rowHeight, item.preferredHeight); 164 } 165 height += rowHeight; 166 if (!flat && row > 0) height += ROW_SPACING; 167 width = Math.max(width, rowWidth); 168 } 169 wrapItems(getWidth()); 170 if (width is 0) width = DEFAULT_COOLBAR_WIDTH; 171 if (height is 0) height = DEFAULT_COOLBAR_HEIGHT; 172 if (wHint !is SWT.DEFAULT) width = wHint; 173 if (hHint !is SWT.DEFAULT) height = hHint; 174 Rectangle trim = computeTrim(0, 0, width, height); 175 return fixPoint(trim.width, trim.height); 176 } 177 CoolItem getGrabbedItem(int x, int y) { 178 for (int row = 0; row < items.length; row++) { 179 for (int i = 0; i < items[row].length; i++) { 180 CoolItem item = items[row][i]; 181 Rectangle bounds = item.internalGetBounds(); 182 bounds.width = CoolItem.MINIMUM_WIDTH; 183 if (bounds.x > x) break; 184 if (bounds.y > y) return null; 185 if (bounds.contains(x, y)) { 186 return item; 187 } 188 } 189 } 190 return null; 191 } 192 /** 193 * Returns the item that is currently displayed at the given, 194 * zero-relative index. Throws an exception if the index is 195 * out of range. 196 * 197 * @param index the visual index of the item to return 198 * @return the item at the given visual index 199 * 200 * @exception IllegalArgumentException <ul> 201 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 202 * </ul> 203 * @exception SWTException <ul> 204 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 205 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 206 * </ul> 207 */ 208 public CoolItem getItem (int index) { 209 checkWidget(); 210 if (index < 0) error (SWT.ERROR_INVALID_RANGE); 211 for (int row = 0; row < items.length; row++) { 212 if (items[row].length > index) { 213 return items[row][index]; 214 } else { 215 index -= items[row].length; 216 } 217 } 218 error (SWT.ERROR_INVALID_RANGE); 219 return null; 220 } 221 /** 222 * Returns the number of items contained in the receiver. 223 * 224 * @return the number of items 225 * 226 * @exception SWTException <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 int getItemCount () { 232 checkWidget(); 233 return cast(int)/*64bit*/originalItems.length; 234 } 235 /** 236 * Returns an array of <code>CoolItem</code>s in the order 237 * in which they are currently being displayed. 238 * <p> 239 * Note: This is not the actual structure used by the receiver 240 * to maintain its list of items, so modifying the array will 241 * not affect the receiver. 242 * </p> 243 * 244 * @return the receiver's items in their current visual order 245 * 246 * @exception SWTException <ul> 247 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 248 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 249 * </ul> 250 */ 251 public CoolItem [] getItems () { 252 checkWidget(); 253 CoolItem [] result = new CoolItem [getItemCount()]; 254 int offset = 0; 255 for (int row = 0; row < items.length; row++) { 256 System.arraycopy(items[row], 0, result, offset, items[row].length); 257 offset += items[row].length; 258 } 259 return result; 260 } 261 Point findItem (CoolItem item) { 262 for (int row = 0; row < items.length; row++) { 263 for (int i = 0; i < items[row].length; i++) { 264 if (items[row][i] ==/*eq*/ item ) return new Point(i, row); 265 } 266 } 267 return new Point(-1, -1); 268 } 269 void fixEvent (Event event) { 270 if ((style & SWT.VERTICAL) !is 0) { 271 int tmp = event.x; 272 event.x = event.y; 273 event.y = tmp; 274 } 275 } 276 Rectangle fixRectangle (int x, int y, int width, int height) { 277 if ((style & SWT.VERTICAL) !is 0) { 278 return new Rectangle(y, x, height, width); 279 } 280 return new Rectangle(x, y, width, height); 281 } 282 Point fixPoint (int x, int y) { 283 if ((style & SWT.VERTICAL) !is 0) { 284 return new Point(y, x); 285 } 286 return new Point(x, y); 287 } 288 /** 289 * Searches the receiver's items in the order they are currently 290 * being displayed, starting at the first item (index 0), until 291 * an item is found that is equal to the argument, and returns 292 * the index of that item. If no item is found, returns -1. 293 * 294 * @param item the search item 295 * @return the visual order index of the search item, or -1 if the item is not found 296 * 297 * @exception IllegalArgumentException <ul> 298 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 299 * <li>ERROR_INVALID_ARGUMENT - if the item is disposed</li> 300 * </ul> 301 * @exception SWTException <ul> 302 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 303 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 304 * </ul> 305 */ 306 public int indexOf (CoolItem item) { 307 checkWidget(); 308 if (item is null) error (SWT.ERROR_NULL_ARGUMENT); 309 if (item.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); 310 int answer = 0; 311 for (int row = 0; row < items.length; row++) { 312 for (int i = 0; i < items[row].length; i++) { 313 if (items[row][i] ==/*eq*/ item) { 314 return answer; 315 } else { 316 answer++; 317 } 318 } 319 } 320 return -1; 321 } 322 /** 323 * Insert the item into the row. Adjust the x and width values 324 * appropriately. 325 */ 326 void insertItemIntoRow(CoolItem item, int rowIndex, int x_root) { 327 int barWidth = getWidth(); 328 int rowY = items[rowIndex][0].internalGetBounds().y; 329 int x = Math.max(0, Math.abs(x_root - toDisplay(new Point(0, 0)).x)); 330 331 /* Find the insertion index and add the item. */ 332 int index; 333 for (index = 0; index < items[rowIndex].length; index++) { 334 if (x < items[rowIndex][index].internalGetBounds().x) break; 335 } 336 if (index is 0) { 337 item.wrap = true; 338 items[rowIndex][0].wrap = false; 339 } 340 ptrdiff_t oldLength = items[rowIndex].length; 341 CoolItem[] newRow = new CoolItem[oldLength + 1]; 342 System.arraycopy(items[rowIndex], 0, newRow, 0, index); 343 newRow[index] = item; 344 System.arraycopy(items[rowIndex], index, newRow, index + 1, oldLength - index); 345 items[rowIndex] = newRow; 346 347 /* Adjust the width of the item to the left. */ 348 if (index > 0) { 349 CoolItem left = items[rowIndex][index - 1]; 350 Rectangle leftBounds = left.internalGetBounds(); 351 int newWidth = x - leftBounds.x; 352 if (newWidth < left.internalGetMinimumWidth()) { 353 x += left.internalGetMinimumWidth() - newWidth; 354 newWidth = left.internalGetMinimumWidth(); 355 } 356 left.setBounds(leftBounds.x, leftBounds.y, newWidth, leftBounds.height); 357 left.requestedWidth = newWidth; 358 } 359 360 /* Set the item's bounds. */ 361 int width = 0, height = item.internalGetBounds().height; 362 if (index < items[rowIndex].length - 1) { 363 CoolItem right = items[rowIndex][index + 1]; 364 width = right.internalGetBounds().x - x; 365 if (width < right.internalGetMinimumWidth()) { 366 moveRight(right, right.internalGetMinimumWidth() - width); 367 width = right.internalGetBounds().x - x; 368 } 369 item.setBounds(x, rowY, width, height); 370 if (width < item.internalGetMinimumWidth()) moveLeft(item, item.internalGetMinimumWidth() - width); 371 } else { 372 width = Math.max(item.internalGetMinimumWidth(), barWidth - x); 373 item.setBounds(x, rowY, width, height); 374 if (x + width > barWidth) moveLeft(item, x + width - barWidth); 375 } 376 Rectangle bounds = item.internalGetBounds(); 377 item.requestedWidth = bounds.width; 378 internalRedraw(bounds.x, bounds.y, item.internalGetMinimumWidth(), bounds.height); 379 } 380 void internalRedraw (int x, int y, int width, int height) { 381 if ((style & SWT.VERTICAL) !is 0) { 382 redraw (y, x, height, width, false); 383 } else { 384 redraw (x, y, width, height, false); 385 } 386 } 387 void createItem (CoolItem item, int index) { 388 ptrdiff_t itemCount = getItemCount(), row = 0; 389 if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); 390 if (items.length is 0) { 391 items = new CoolItem[][]( 1,1 ); 392 items[0][0] = item; 393 } else { 394 ptrdiff_t i = index; 395 /* find the row to insert into */ 396 if (index < itemCount) { 397 while (i > items[row].length) { 398 i -= items[row].length; 399 row++; 400 } 401 } else { 402 row = items.length - 1; 403 i = items[row].length; 404 } 405 406 // Set the last item in the row to the preferred size 407 // and add the new one just to it's right 408 ptrdiff_t lastIndex = items[row].length - 1; 409 CoolItem lastItem = items[row][lastIndex]; 410 if (lastItem.ideal) { 411 Rectangle bounds = lastItem.internalGetBounds(); 412 bounds.width = lastItem.preferredWidth; 413 bounds.height = lastItem.preferredHeight; 414 lastItem.requestedWidth = lastItem.preferredWidth; 415 lastItem.setBounds(bounds.x, bounds.y, bounds.width, bounds.height); 416 } 417 if (i is 0) { 418 item.wrap = true; 419 items[row][0].wrap = false; 420 } 421 ptrdiff_t oldLength = items[row].length; 422 CoolItem[] newRow = new CoolItem[oldLength + 1]; 423 System.arraycopy(items[row], 0, newRow, 0, i); 424 newRow[i] = item; 425 System.arraycopy(items[row], i, newRow, i + 1, oldLength - i); 426 items[row] = newRow; 427 } 428 item.requestedWidth = CoolItem.MINIMUM_WIDTH; 429 430 ptrdiff_t length = originalItems.length; 431 CoolItem [] newOriginals = new CoolItem [length + 1]; 432 System.arraycopy (originalItems, 0, newOriginals, 0, index); 433 System.arraycopy (originalItems, index, newOriginals, index + 1, length - index); 434 newOriginals [index] = item; 435 originalItems = newOriginals; 436 layoutItems(); 437 438 } 439 void destroyItem(CoolItem item) { 440 if (inDispose) return; 441 int row = findItem(item).y; 442 if (row is -1) return; 443 Rectangle bounds = item.internalGetBounds(); 444 removeItemFromRow(item, row, true); 445 446 int index = 0; 447 while (index < originalItems.length) { 448 if (originalItems [index] is item) break; 449 index++; 450 } 451 ptrdiff_t length = originalItems.length - 1; 452 CoolItem [] newOriginals = new CoolItem [length]; 453 System.arraycopy (originalItems, 0, newOriginals, 0, index); 454 System.arraycopy (originalItems, index + 1, newOriginals, index, length - index); 455 originalItems = newOriginals; 456 457 internalRedraw(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH, bounds.height); 458 relayout(); 459 } 460 void moveDown(CoolItem item, int x_root) { 461 int oldRowIndex = findItem(item).y; 462 bool resize = false; 463 if (items[oldRowIndex].length is 1) { 464 resize = true; 465 /* If this is the only item in the bottom row, don't move it. */ 466 if (oldRowIndex is items.length - 1) return; 467 } 468 int newRowIndex = (items[oldRowIndex].length is 1) ? oldRowIndex : oldRowIndex + 1; 469 removeItemFromRow(item, oldRowIndex, false); 470 Rectangle old = item.internalGetBounds(); 471 internalRedraw(old.x, old.y, CoolItem.MINIMUM_WIDTH, old.height); 472 if (newRowIndex is items.length) { 473 /* Create a new bottom row for the item. */ 474 CoolItem[][] newRows = new CoolItem[][](items.length + 1); 475 SimpleType!(CoolItem[]).arraycopy(items, 0, newRows, 0, items.length); 476 ptrdiff_t row = items.length; 477 newRows[row] = new CoolItem[1]; 478 newRows[row][0] = item; 479 items = newRows; 480 resize = true; 481 item.wrap = true; 482 } else { 483 insertItemIntoRow(item, newRowIndex, x_root); 484 } 485 if (resize) { 486 relayout(); 487 } else { 488 layoutItems(); 489 } 490 } 491 void moveLeft(CoolItem item, int pixels) { 492 Point point = findItem(item); 493 int row = point.y; 494 int index = point.x; 495 if (index is 0) return; 496 Rectangle bounds = item.internalGetBounds(); 497 int minSpaceOnLeft = 0; 498 for (int i = 0; i < index; i++) { 499 minSpaceOnLeft += items[row][i].internalGetMinimumWidth(); 500 } 501 int x = Math.max(minSpaceOnLeft, bounds.x - pixels); 502 CoolItem left = items[row][index - 1]; 503 Rectangle leftBounds = left.internalGetBounds(); 504 if (leftBounds.x + left.internalGetMinimumWidth() > x) { 505 int shift = leftBounds.x + left.internalGetMinimumWidth() - x; 506 moveLeft(left, shift); 507 leftBounds = left.internalGetBounds(); 508 } 509 int leftWidth = Math.max(left.internalGetMinimumWidth(), leftBounds.width - pixels); 510 left.setBounds(leftBounds.x, leftBounds.y, leftWidth, leftBounds.height); 511 left.requestedWidth = leftWidth; 512 int width = bounds.width + (bounds.x - x); 513 item.setBounds(x, bounds.y, width, bounds.height); 514 item.requestedWidth = width; 515 516 int damagedWidth = bounds.x - x + CoolItem.MINIMUM_WIDTH; 517 if (damagedWidth > CoolItem.MINIMUM_WIDTH) { 518 internalRedraw(x, bounds.y, damagedWidth, bounds.height); 519 } 520 } 521 void moveRight(CoolItem item, int pixels) { 522 Point point = findItem(item); 523 int row = point.y; 524 int index = point.x; 525 if (index is 0) return; 526 Rectangle bounds = item.internalGetBounds(); 527 int minSpaceOnRight = 0; 528 for (int i = index; i < items[row].length; i++) { 529 minSpaceOnRight += items[row][i].internalGetMinimumWidth(); 530 } 531 int max = getWidth() - minSpaceOnRight; 532 int x = Math.min(max, bounds.x + pixels); 533 int width = 0; 534 if (index + 1 is items[row].length) { 535 width = getWidth() - x; 536 } else { 537 CoolItem right = items[row][index + 1]; 538 Rectangle rightBounds = right.internalGetBounds(); 539 if (x + item.internalGetMinimumWidth() > rightBounds.x) { 540 int shift = x + item.internalGetMinimumWidth() - rightBounds.x; 541 moveRight(right, shift); 542 rightBounds = right.internalGetBounds(); 543 } 544 width = rightBounds.x - x; 545 } 546 item.setBounds(x, bounds.y, width, bounds.height); 547 item.requestedWidth = width; 548 CoolItem left = items[row][index - 1]; 549 Rectangle leftBounds = left.internalGetBounds(); 550 int leftWidth = x - leftBounds.x; 551 left.setBounds(leftBounds.x, leftBounds.y, leftWidth, leftBounds.height); 552 left.requestedWidth = leftWidth; 553 554 int damagedWidth = x - bounds.x + CoolItem.MINIMUM_WIDTH + CoolItem.MARGIN_WIDTH; 555 if (x - bounds.x > 0) { 556 internalRedraw(bounds.x - CoolItem.MARGIN_WIDTH, bounds.y, damagedWidth, bounds.height); 557 } 558 } 559 void moveUp(CoolItem item, int x_root) { 560 Point point = findItem(item); 561 int oldRowIndex = point.y; 562 bool resize = false; 563 if (items[oldRowIndex].length is 1) { 564 resize = true; 565 /* If this is the only item in the top row, don't move it. */ 566 if (oldRowIndex is 0) return; 567 } 568 removeItemFromRow(item, oldRowIndex, false); 569 Rectangle old = item.internalGetBounds(); 570 internalRedraw(old.x, old.y, CoolItem.MINIMUM_WIDTH, old.height); 571 int newRowIndex = Math.max(0, oldRowIndex - 1); 572 if (oldRowIndex is 0) { 573 /* Create a new top row for the item. */ 574 CoolItem[][] newRows = new CoolItem[][]( items.length + 1 ); 575 SimpleType!(CoolItem[]).arraycopy(items, 0, newRows, 1, items.length); 576 newRows[0] = new CoolItem[1]; 577 newRows[0][0] = item; 578 items = newRows; 579 resize = true; 580 item.wrap = true; 581 } else { 582 insertItemIntoRow(item, newRowIndex, x_root); 583 } 584 if (resize) { 585 relayout(); 586 } else { 587 layoutItems(); 588 } 589 } 590 void onDispose(Event event) { 591 /* 592 * Usually when an item is disposed, destroyItem will change the size of the items array 593 * and reset the bounds of all the remaining cool items. 594 * Since the whole cool bar is being disposed, this is not necessary. For speed 595 * the inDispose flag is used to skip over this part of the item dispose. 596 */ 597 if (inDispose) return; 598 inDispose = true; 599 notifyListeners(SWT.Dispose, event); 600 event.type = SWT.None; 601 for (int i = 0; i < items.length; i++) { 602 for (int j = 0; j < items[i].length; j++) { 603 items[i][j].dispose(); 604 } 605 } 606 hoverCursor.dispose(); 607 dragCursor.dispose(); 608 cursor = null; 609 } 610 void onMouseDown(Event event) { 611 if (isLocked || event.button !is 1) return; 612 fixEvent(event); 613 dragging = getGrabbedItem(event.x, event.y); 614 if (dragging !is null) { 615 mouseXOffset = event.x; 616 itemXOffset = mouseXOffset - dragging.internalGetBounds().x; 617 _setCursor(dragCursor); 618 } 619 fixEvent(event); 620 } 621 void onMouseExit() { 622 if (dragging is null) _setCursor(null); 623 } 624 void onMouseMove(Event event) { 625 if (isLocked) return; 626 fixEvent(event); 627 CoolItem grabbed = getGrabbedItem(event.x, event.y); 628 if (dragging !is null) { 629 int left_root = toDisplay(new Point(event.x - itemXOffset, event.y)).x; 630 Rectangle bounds = dragging.internalGetBounds(); 631 if (event.y < bounds.y) { 632 moveUp(dragging, left_root); 633 } else if (event.y > bounds.y + bounds.height){ 634 moveDown(dragging, left_root); 635 } else if (event.x < mouseXOffset) { 636 int distance = Math.min(mouseXOffset, bounds.x + itemXOffset) - event.x; 637 if (distance > 0) moveLeft(dragging, distance); 638 } else if (event.x > mouseXOffset) { 639 int distance = event.x - Math.max(mouseXOffset, bounds.x + itemXOffset); 640 if (distance > 0) moveRight(dragging, distance); 641 } 642 mouseXOffset = event.x; 643 } else { 644 if (grabbed !is null) { 645 _setCursor(hoverCursor); 646 } else { 647 _setCursor(null); 648 } 649 } 650 fixEvent(event); 651 } 652 void onMouseUp(Event event) { 653 _setCursor(null); 654 dragging = null; 655 } 656 void onMouseDoubleClick(Event event) { 657 if (isLocked) return; 658 dragging = null; 659 fixEvent(event); 660 CoolItem target = getGrabbedItem(event.x, event.y); 661 if (target is null) { 662 _setCursor(null); 663 } else { 664 Point location = findItem(target); 665 int row = location.y; 666 int index = location.x; 667 if (items[row].length > 1) { 668 Rectangle bounds = target.internalGetBounds(); 669 int maxSize = getWidth (); 670 for (int i = 0; i < items[row].length; i++) { 671 if (i !is index) { 672 maxSize -= items[row][i].internalGetMinimumWidth(); 673 } 674 } 675 if (bounds.width is maxSize) { 676 /* The item is at its maximum width. It should be resized to its minimum width. */ 677 int distance = bounds.width - target.internalGetMinimumWidth(); 678 if (index + 1 < items[row].length) { 679 /* There is an item to the right. Maximize it. */ 680 CoolItem right = items[row][index + 1]; 681 moveLeft(right, distance); 682 } else { 683 /* There is no item to the right. Move the item all the way right. */ 684 moveRight(target, distance); 685 } 686 } else if (bounds.width < target.preferredWidth) { 687 /* The item is less than its preferredWidth. Resize to preferredWidth. */ 688 int distance = target.preferredWidth - bounds.width; 689 if (index + 1 < items[row].length) { 690 CoolItem right = items[row][index + 1]; 691 moveRight(right, distance); 692 distance = target.preferredWidth - target.internalGetBounds().width; 693 } 694 if (distance > 0) { 695 moveLeft(target, distance); 696 } 697 } else { 698 /* The item is at its minimum width. Maximize it. */ 699 for (int i = 0; i < items[row].length; i++) { 700 if (i !is index) { 701 CoolItem item = items[row][i]; 702 item.requestedWidth = Math.max(item.internalGetMinimumWidth(), CoolItem.MINIMUM_WIDTH); 703 } 704 } 705 target.requestedWidth = maxSize; 706 layoutItems(); 707 } 708 _setCursor(hoverCursor); 709 } 710 } 711 fixEvent(event); 712 } 713 void onPaint(Event event) { 714 GC gc = event.gc; 715 if (items.length is 0) return; 716 Color shadowColor = display.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW); 717 Color highlightColor = display.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW); 718 bool vertical = (style & SWT.VERTICAL) !is 0; 719 bool flat = (style & SWT.FLAT) !is 0; 720 int stopX = getWidth(); 721 Rectangle rect; 722 Rectangle clipping = gc.getClipping(); 723 for (int row = 0; row < items.length; row++) { 724 Rectangle bounds = new Rectangle(0, 0, 0, 0); 725 for (int i = 0; i < items[row].length; i++) { 726 bounds = items[row][i].internalGetBounds(); 727 rect = fixRectangle(bounds.x, bounds.y, bounds.width, bounds.height); 728 if (!clipping.intersects(rect)) continue; 729 bool nativeGripper = false; 730 731 /* Draw gripper. */ 732 if (!isLocked) { 733 rect = fixRectangle(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH, bounds.height); 734 if (!flat) nativeGripper = drawGripper(rect.x, rect.y, rect.width, rect.height, vertical); 735 if (!nativeGripper) { 736 int grabberTrim = 2; 737 int grabberHeight = bounds.height - (2 * grabberTrim) - 1; 738 gc.setForeground(shadowColor); 739 rect = fixRectangle( 740 bounds.x + CoolItem.MARGIN_WIDTH, 741 bounds.y + grabberTrim, 742 2, 743 grabberHeight); 744 gc.drawRectangle(rect); 745 gc.setForeground(highlightColor); 746 rect = fixRectangle( 747 bounds.x + CoolItem.MARGIN_WIDTH, 748 bounds.y + grabberTrim + 1, 749 bounds.x + CoolItem.MARGIN_WIDTH, 750 bounds.y + grabberTrim + grabberHeight - 1); 751 gc.drawLine(rect.x, rect.y, rect.width, rect.height); 752 rect = fixRectangle( 753 bounds.x + CoolItem.MARGIN_WIDTH, 754 bounds.y + grabberTrim, 755 bounds.x + CoolItem.MARGIN_WIDTH + 1, 756 bounds.y + grabberTrim); 757 gc.drawLine(rect.x, rect.y, rect.width, rect.height); 758 } 759 } 760 761 /* Draw separator. */ 762 if (!flat && !nativeGripper && i !is 0) { 763 gc.setForeground(shadowColor); 764 rect = fixRectangle(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height - 1); 765 gc.drawLine(rect.x, rect.y, rect.width, rect.height); 766 gc.setForeground(highlightColor); 767 rect = fixRectangle(bounds.x + 1, bounds.y, bounds.x + 1, bounds.y + bounds.height - 1); 768 gc.drawLine(rect.x, rect.y, rect.width, rect.height); 769 } 770 } 771 if (!flat && row + 1 < items.length) { 772 /* Draw row separator. */ 773 int separatorY = bounds.y + bounds.height; 774 gc.setForeground(shadowColor); 775 rect = fixRectangle(0, separatorY, stopX, separatorY); 776 gc.drawLine(rect.x, rect.y, rect.width, rect.height); 777 gc.setForeground(highlightColor); 778 rect = fixRectangle(0, separatorY + 1, stopX, separatorY + 1); 779 gc.drawLine(rect.x, rect.y, rect.width, rect.height); 780 } 781 } 782 } 783 void onResize () { 784 layoutItems (); 785 } 786 override void removeControl (Control control) { 787 super.removeControl (control); 788 CoolItem [] items = getItems (); 789 for (int i=0; i<items.length; i++) { 790 CoolItem item = items [i]; 791 if (item.control is control) item.setControl (null); 792 } 793 } 794 /** 795 * Remove the item from the row. Adjust the x and width values 796 * appropriately. 797 */ 798 void removeItemFromRow(CoolItem item, int rowIndex, bool disposed) { 799 int index = findItem(item).x; 800 ptrdiff_t newLength = items[rowIndex].length - 1; 801 Rectangle itemBounds = item.internalGetBounds(); 802 item.wrap = false; 803 if (newLength > 0) { 804 CoolItem[] newRow = new CoolItem[newLength]; 805 System.arraycopy(items[rowIndex], 0, newRow, 0, index); 806 System.arraycopy(items[rowIndex], index + 1, newRow, index, newRow.length - index); 807 items[rowIndex] = newRow; 808 items[rowIndex][0].wrap = true; 809 } else { 810 CoolItem[][] newRows = new CoolItem[][]( items.length - 1 ); 811 SimpleType!(CoolItem[]).arraycopy(items, 0, newRows, 0, rowIndex); 812 SimpleType!(CoolItem[]).arraycopy(items, rowIndex + 1, newRows, rowIndex, newRows.length - rowIndex); 813 items = newRows; 814 return; 815 } 816 if (!disposed) { 817 if (index is 0) { 818 CoolItem first = items[rowIndex][0]; 819 Rectangle bounds = first.internalGetBounds(); 820 int width = bounds.x + bounds.width; 821 first.setBounds(0, bounds.y, width, bounds.height); 822 first.requestedWidth = width; 823 internalRedraw(bounds.x, bounds.y, CoolItem.MINIMUM_WIDTH, bounds.height); 824 } else { 825 CoolItem previous = items[rowIndex][index - 1]; 826 Rectangle bounds = previous.internalGetBounds(); 827 int width = bounds.width + itemBounds.width; 828 previous.setBounds(bounds.x, bounds.y, width, bounds.height); 829 previous.requestedWidth = width; 830 } 831 } 832 } 833 /** 834 * Return the height of the bar after it has 835 * been properly laid out for the given width. 836 */ 837 int layoutItems () { 838 int y = 0, width; 839 if ((style&SWT.VERTICAL) !is 0) { 840 width = getClientArea().height; 841 } else { 842 width = getClientArea().width; 843 } 844 wrapItems(width); 845 int rowSpacing = (style & SWT.FLAT) !is 0 ? 0 : ROW_SPACING; 846 for (int row = 0; row < items.length; row++) { 847 ptrdiff_t count = items[row].length; 848 int x = 0; 849 850 /* determine the height and the available width for the row */ 851 int rowHeight = 0; 852 int available = width; 853 for (int i = 0; i < count; i++) { 854 CoolItem item = items[row][i]; 855 rowHeight = Math.max(rowHeight, item.internalGetBounds().height); 856 available -= item.internalGetMinimumWidth(); 857 } 858 if (row > 0) y += rowSpacing; 859 860 /* lay the items out */ 861 for (int i = 0; i < count; i++) { 862 CoolItem child = items[row][i]; 863 int newWidth = available + child.internalGetMinimumWidth(); 864 if (i + 1 < count) { 865 newWidth = Math.min(newWidth, child.requestedWidth); 866 available -= (newWidth - child.internalGetMinimumWidth()); 867 } 868 Rectangle oldBounds = child.internalGetBounds(); 869 Rectangle newBounds = new Rectangle(x, y, newWidth, rowHeight); 870 if ( oldBounds !=/*eq*/ newBounds) { 871 child.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height); 872 Rectangle damage = new Rectangle(0, 0, 0, 0); 873 /* Cases are in descending order from most area to redraw to least. */ 874 if (oldBounds.y !is newBounds.y) { 875 damage = newBounds; 876 damage.add(oldBounds); 877 /* Redraw the row separator as well. */ 878 damage.y -= rowSpacing; 879 damage.height += 2 * rowSpacing; 880 } else if (oldBounds.height !is newBounds.height) { 881 /* 882 * Draw from the bottom of the gripper to the bottom of the new area. 883 * (Bottom of the gripper is -3 from the bottom of the item). 884 */ 885 damage.y = newBounds.y + Math.min(oldBounds.height, newBounds.height) - 3; 886 damage.height = newBounds.y + newBounds.height + rowSpacing; 887 damage.x = oldBounds.x - CoolItem.MARGIN_WIDTH; 888 damage.width = oldBounds.width + CoolItem.MARGIN_WIDTH; 889 } else if (oldBounds.x !is newBounds.x) { 890 /* Redraw only the difference between the separators. */ 891 damage.x = Math.min(oldBounds.x, newBounds.x); 892 damage.width = Math.abs(oldBounds.x - newBounds.x) + CoolItem.MINIMUM_WIDTH; 893 damage.y = oldBounds.y; 894 damage.height = oldBounds.height; 895 } 896 internalRedraw(damage.x, damage.y, damage.width, damage.height); 897 } 898 x += newWidth; 899 } 900 y += rowHeight; 901 } 902 return y; 903 } 904 void relayout() { 905 Point size = getSize(); 906 int height = layoutItems(); 907 if ((style & SWT.VERTICAL) !is 0) { 908 Rectangle trim = computeTrim (0, 0, height, 0); 909 if (height !is size.x) super.setSize(trim.width, size.y); 910 } else { 911 Rectangle trim = computeTrim (0, 0, 0, height); 912 if (height !is size.y) super.setSize(size.x, trim.height); 913 } 914 } 915 /** 916 * Returns an array of zero-relative ints that map 917 * the creation order of the receiver's items to the 918 * order in which they are currently being displayed. 919 * <p> 920 * Specifically, the indices of the returned array represent 921 * the current visual order of the items, and the contents 922 * of the array represent the creation order of the items. 923 * </p><p> 924 * Note: This is not the actual structure used by the receiver 925 * to maintain its list of items, so modifying the array will 926 * not affect the receiver. 927 * </p> 928 * 929 * @return the current visual order of the receiver's items 930 * 931 * @exception SWTException <ul> 932 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 933 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 934 * </ul> 935 */ 936 public int[] getItemOrder () { 937 checkWidget (); 938 int count = getItemCount (); 939 int [] indices = new int [count]; 940 count = 0; 941 for (int i = 0; i < items.length; i++) { 942 for (int j = 0; j < items[i].length; j++) { 943 CoolItem item = items[i][j]; 944 int index = 0; 945 while (index<originalItems.length) { 946 if (originalItems [index] is item) break; 947 index++; 948 } 949 if (index is originalItems.length) error (SWT.ERROR_CANNOT_GET_ITEM); 950 indices [count++] = index; 951 } 952 } 953 return indices; 954 } 955 void setItemOrder (int[] itemOrder) { 956 // SWT extension: allow null for zero length string 957 //if (itemOrder is null) error(SWT.ERROR_NULL_ARGUMENT); 958 ptrdiff_t count = originalItems.length; 959 if (itemOrder.length !is count) error(SWT.ERROR_INVALID_ARGUMENT); 960 961 /* Ensure that itemOrder does not contain any duplicates. */ 962 bool [] set = new bool [count]; 963 for (int i = 0; i < set.length; i++) set [i] = false; 964 for (int i = 0; i < itemOrder.length; i++) { 965 if (itemOrder [i] < 0 || itemOrder [i] >= count) error (SWT.ERROR_INVALID_ARGUMENT); 966 if (set [itemOrder [i]]) error (SWT.ERROR_INVALID_ARGUMENT); 967 set [itemOrder [i]] = true; 968 } 969 970 CoolItem[] row = new CoolItem[count]; 971 for (int i = 0; i < count; i++) { 972 row[i] = originalItems[itemOrder[i]]; 973 } 974 items = new CoolItem[][](1,count); 975 items[0] = row; 976 } 977 /** 978 * Returns an array of points whose x and y coordinates describe 979 * the widths and heights (respectively) of the items in the receiver 980 * in the order in which they are currently being displayed. 981 * 982 * @return the receiver's item sizes in their current visual order 983 * 984 * @exception SWTException <ul> 985 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 986 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 987 * </ul> 988 */ 989 public Point[] getItemSizes () { 990 checkWidget(); 991 CoolItem[] items = getItems(); 992 Point[] sizes = new Point[items.length]; 993 for (int i = 0; i < items.length; i++) { 994 sizes[i] = items[i].getSize(); 995 } 996 return sizes; 997 } 998 void setItemSizes (Point[] sizes) { 999 // SWT extension: allow null for zero length string 1000 //if (sizes is null) error(SWT.ERROR_NULL_ARGUMENT); 1001 CoolItem[] items = getItems(); 1002 if (sizes.length !is items.length) error(SWT.ERROR_INVALID_ARGUMENT); 1003 for (int i = 0; i < items.length; i++) { 1004 items[i].setSize(sizes[i]); 1005 } 1006 } 1007 /** 1008 * Returns whether or not the receiver is 'locked'. When a coolbar 1009 * is locked, its items cannot be repositioned. 1010 * 1011 * @return true if the coolbar is locked, false otherwise 1012 * 1013 * @exception SWTException <ul> 1014 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1015 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1016 * </ul> 1017 * 1018 * @since 2.0 1019 */ 1020 public bool getLocked () { 1021 checkWidget (); 1022 return isLocked; 1023 } 1024 int getWidth () { 1025 if ((style & SWT.VERTICAL) !is 0) return getSize().y; 1026 return getSize().x; 1027 } 1028 /** 1029 * Returns an array of ints that describe the zero-relative 1030 * indices of any item(s) in the receiver that will begin on 1031 * a new row. The 0th visible item always begins the first row, 1032 * therefore it does not count as a wrap index. 1033 * 1034 * @return an array containing the receiver's wrap indices, or an empty array if all items are in one row 1035 * 1036 * @exception SWTException <ul> 1037 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1038 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1039 * </ul> 1040 */ 1041 public int[] getWrapIndices () { 1042 checkWidget(); 1043 if (items.length <= 1) return null; 1044 int[] wrapIndices = new int[]( items.length - 1 ); 1045 ptrdiff_t i = 0, nextWrap = items[0].length; 1046 for (int row = 1; row < items.length; row++) { 1047 if (items[row][0].wrap) wrapIndices[i++] = cast(int)/*64bit*/nextWrap; 1048 nextWrap += items[row].length; 1049 } 1050 if (i !is wrapIndices.length) { 1051 int[] tmp = new int[i]; 1052 System.arraycopy(wrapIndices, 0, tmp, 0, i); 1053 return tmp; 1054 } 1055 return wrapIndices; 1056 } 1057 /** 1058 * Sets whether or not the receiver is 'locked'. When a coolbar 1059 * is locked, its items cannot be repositioned. 1060 * 1061 * @param locked lock the coolbar if true, otherwise unlock the coolbar 1062 * 1063 * @exception SWTException <ul> 1064 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1065 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1066 * </ul> 1067 * 1068 * @since 2.0 1069 */ 1070 public void setLocked (bool locked) { 1071 checkWidget (); 1072 if (isLocked !is locked) { 1073 redraw(); 1074 } 1075 isLocked = locked; 1076 1077 } 1078 /** 1079 * Sets the indices of all item(s) in the receiver that will 1080 * begin on a new row. The indices are given in the order in 1081 * which they are currently being displayed. The 0th item 1082 * always begins the first row, therefore it does not count 1083 * as a wrap index. If indices is null or empty, the items 1084 * will be placed on one line. 1085 * 1086 * @param indices an array of wrap indices, or null 1087 * 1088 * @exception SWTException <ul> 1089 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1090 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1091 * </ul> 1092 */ 1093 public void setWrapIndices (int[] indices) { 1094 checkWidget(); 1095 if (indices is null) indices = new int[0]; 1096 ptrdiff_t count = originalItems.length; 1097 for (int i=0; i<indices.length; i++) { 1098 if (indices[i] < 0 || indices[i] >= count) { 1099 error (SWT.ERROR_INVALID_ARGUMENT); 1100 } 1101 } 1102 for (int i=0; i<originalItems.length; i++) { 1103 originalItems[i].wrap = false; 1104 } 1105 for (int i=0; i<indices.length; i++) { 1106 int index = indices[i]; 1107 for (int row = 0; row < items.length; row++) { 1108 if (items[row].length > index) { 1109 items[row][index].wrap = true; 1110 break; 1111 } else { 1112 index -= items[row].length; 1113 } 1114 } 1115 } 1116 relayout(); 1117 } 1118 public override void setCursor (Cursor cursor) { 1119 checkWidget (); 1120 super.setCursor (this.cursor = cursor); 1121 } 1122 /** 1123 * Sets the receiver's item order, wrap indices, and item sizes 1124 * all at once. This method is typically used to restore the 1125 * displayed state of the receiver to a previously stored state. 1126 * <p> 1127 * The item order is the order in which the items in the receiver 1128 * should be displayed, given in terms of the zero-relative ordering 1129 * of when the items were added. 1130 * </p><p> 1131 * The wrap indices are the indices of all item(s) in the receiver 1132 * that will begin on a new row. The indices are given in the order 1133 * specified by the item order. The 0th item always begins the first 1134 * row, therefore it does not count as a wrap index. If wrap indices 1135 * is null or empty, the items will be placed on one line. 1136 * </p><p> 1137 * The sizes are specified in an array of points whose x and y 1138 * coordinates describe the new widths and heights (respectively) 1139 * of the receiver's items in the order specified by the item order. 1140 * </p> 1141 * 1142 * @param itemOrder an array of indices that describe the new order to display the items in 1143 * @param wrapIndices an array of wrap indices, or null 1144 * @param sizes an array containing the new sizes for each of the receiver's items in visual order 1145 * 1146 * @exception SWTException <ul> 1147 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1148 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1149 * </ul> 1150 * @exception IllegalArgumentException <ul> 1151 * <li>ERROR_INVALID_ARGUMENT - if item order or sizes is not the same length as the number of items</li> 1152 * </ul> 1153 */ 1154 public void setItemLayout (int[] itemOrder, int[] wrapIndices, Point[] sizes) { 1155 checkWidget(); 1156 setItemOrder(itemOrder); 1157 setWrapIndices(wrapIndices); 1158 setItemSizes(sizes); 1159 relayout(); 1160 } 1161 void wrapItems (int maxWidth) { 1162 ptrdiff_t itemCount = originalItems.length; 1163 if (itemCount < 2) return; 1164 CoolItem[] itemsVisual = new CoolItem[itemCount]; 1165 int start = 0; 1166 for (int row = 0; row < items.length; row++) { 1167 System.arraycopy(items[row], 0, itemsVisual, start, items[row].length); 1168 start += items[row].length; 1169 } 1170 CoolItem[][] newItems = new CoolItem[][](itemCount); 1171 int rowCount = 0, rowWidth = 0; 1172 start = 0; 1173 for (int i = 0; i < itemCount; i++) { 1174 CoolItem item = itemsVisual[i]; 1175 int itemWidth = item.internalGetMinimumWidth(); 1176 if ((i > 0 && item.wrap) || (maxWidth !is SWT.DEFAULT && rowWidth + itemWidth > maxWidth)) { 1177 if (i is start) { 1178 newItems[rowCount] = new CoolItem[1]; 1179 newItems[rowCount][0] = item; 1180 start = i + 1; 1181 rowWidth = 0; 1182 } else { 1183 int count = i - start; 1184 newItems[rowCount] = new CoolItem[count]; 1185 System.arraycopy(itemsVisual, start, newItems[rowCount], 0, count); 1186 start = i; 1187 rowWidth = itemWidth; 1188 } 1189 rowCount++; 1190 } else { 1191 rowWidth += itemWidth; 1192 } 1193 } 1194 if (start < itemCount) { 1195 ptrdiff_t count = itemCount - start; 1196 newItems[rowCount] = new CoolItem[count]; 1197 System.arraycopy(itemsVisual, start, newItems[rowCount], 0, count); 1198 rowCount++; 1199 } 1200 if (newItems.length !is rowCount) { 1201 CoolItem[][] tmp = new CoolItem[][](rowCount); 1202 SimpleType!(CoolItem[]).arraycopy(newItems, 0, tmp, 0, rowCount); 1203 items = tmp; 1204 } else { 1205 items = newItems; 1206 } 1207 } 1208 }