Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/swt/widgets/CoolBar.java


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