/******************************************************************************* * Copyright (c) 2006-2007 Nicolas Richeton. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors : * Nicolas Richeton (nicolas.richeton@gmail.com) - initial API and implementation *******************************************************************************/ package org.eclipse.nebula.widgets.gallery; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Item; /** * <p> * Instances of this class represent a selectable user interface object that * represents an item in a gallery. * </p> * * <p> * NOTE: THIS WIDGET AND ITS API ARE STILL UNDER DEVELOPMENT. * </p> * * @see Gallery * * @author Nicolas Richeton (nicolas.richeton@gmail.com) * @contributor Peter Centgraf (bugs 212071, 212073) * @contributor Berthold Daum (bug 306144 - selection tuning) */ public class GalleryItem extends Item { private static final String EMPTY_STRING = ""; //$NON-NLS-1$ private String[] text = { EMPTY_STRING, EMPTY_STRING, EMPTY_STRING }; // This is managed by the Gallery /** * Children of this item. Only used when groups are enabled. */ protected GalleryItem[] items = null; /** * Bounds of this items in the current Gallery. * * X and Y values are used for vertical or horizontal offset depending on * the Gallery settings. Only used when groups are enabled. * * Width and height */ // protected Rectangle bounds = new Rectangle(0, 0, 0, 0); protected int x = 0; protected int y = 0; /** * Size of the group, including its title. */ protected int width = 0; protected int height = 0; protected int marginBottom = 0; protected int hCount = 0; protected int vCount = 0; /** * Last result of indexOf( GalleryItem). Used for optimisation. */ protected int lastIndexOf = 0; /** * True if the Gallery was created wih SWT.VIRTUAL */ private boolean virtualGallery; private Gallery parent; private GalleryItem parentItem; /** * Selection bit flags. Each 'int' contains flags for 32 items. */ protected int[] selectionFlags = null; protected Font font; protected Color foreground, background; private boolean ultraLazyDummy = false; /** * */ private boolean expanded; protected boolean isUltraLazyDummy() { return ultraLazyDummy; } protected void setUltraLazyDummy(boolean ultraLazyDummy) { this.ultraLazyDummy = ultraLazyDummy; } public Gallery getParent() { return parent; } protected void setParent(Gallery parent) { this.parent = parent; } public GalleryItem getParentItem() { return parentItem; } protected void setParentItem(GalleryItem parentItem) { this.parentItem = parentItem; } public GalleryItem(Gallery parent, int style) { this(parent, style, -1, true); } public GalleryItem(Gallery parent, int style, int index) { this(parent, style, index, true); } public GalleryItem(GalleryItem parent, int style) { this(parent, style, -1, true); } public GalleryItem(GalleryItem parent, int style, int index) { this(parent, style, index, true); } protected GalleryItem(GalleryItem parent, int style, int index, boolean create) { super(parent, style); this.parent = parent.parent; this.parentItem = parent; if ((parent.getStyle() & SWT.VIRTUAL) > 0) { virtualGallery = true; } if (create) parent.addItem(this, index); } protected GalleryItem(Gallery parent, int style, int index, boolean create) { super(parent, style); this.parent = parent; this.parentItem = null; if ((parent.getStyle() & SWT.VIRTUAL) > 0) { virtualGallery = true; } if (create) parent.addItem(this, index); } protected void addItem(GalleryItem item, int position) { if (position != -1 && (position < 0 || position > getItemCount())) { throw new IllegalArgumentException("ERROR_INVALID_RANGE"); //$NON-NLS-1$ } _addItem(item, position); } private void _addItem(GalleryItem item, int position) { // TODO: ensure that there was no item at this position before using // this item in virtual mode // Insert item items = (GalleryItem[]) parent._arrayAddItem(items, item, position); // Update Gallery parent.updateStructuralValues(null, false); parent.updateScrollBarsProperties(); } /** * Returns the number of items contained in the receiver that are direct * item children of the receiver. * * @return */ public int getItemCount() { if (items == null) return 0; return items.length; } /** * Only work when the table was created with SWT.VIRTUAL * * @param itemCount */ public void setItemCount(int count) { if (count == 0) { // No items items = null; } else { // At least one item, create a new array and copy data from the // old one. GalleryItem[] newItems = new GalleryItem[count]; if (items != null) { System.arraycopy(items, 0, newItems, 0, Math.min(count, items.length)); } items = newItems; } } /** * Searches the receiver's list starting at the first item (index 0) until * an item is found that is equal to the argument, and returns the index of * that item. <br/> * If SWT.VIRTUAL is used and the item has not been used yet, the item is * created and a SWT.SetData event is fired. * * @param index * : index of the item. * @return : the GalleryItem or null if index is out of bounds */ public GalleryItem getItem(int index) { checkWidget(); return parent._getItem(this, index); } public GalleryItem[] getItems() { checkWidget(); if (items == null) return new GalleryItem[0]; GalleryItem[] itemsLocal = new GalleryItem[this.items.length]; System.arraycopy(items, 0, itemsLocal, 0, this.items.length); return itemsLocal; } /** * Returns the index of childItem within this item or -1 if childItem is not * found. The search is only one level deep. * * @param childItem * @return */ public int indexOf(GalleryItem childItem) { checkWidget(); return parent._indexOf(this, childItem); } public void setImage(Image image) { super.setImage(image); parent.redraw(this); } /** * Returns true if the receiver is expanded, and false otherwise. * * @return */ public boolean isExpanded() { return expanded; } /** * Sets the expanded state of the receiver. * * @param expanded */ public void setExpanded(boolean expanded) { checkWidget(); _setExpanded(expanded, true); } public void _setExpanded(boolean expanded, boolean redraw) { this.expanded = expanded; parent.updateStructuralValues(this, false); parent.updateScrollBarsProperties(); if (redraw) { parent.redraw(); } } /** * @deprecated * @return */ public String getDescription() { return getText(1); } /** * @param description * @deprecated */ public void setDescription(String description) { setText(1, description); } /** * Deselect all children of this item */ public void deselectAll() { checkWidget(); _deselectAll(); parent.redraw(); } protected void _deselectAll() { // Deselect groups // We could set selectionFlags to null, but we rather set all values to // 0 to redure garbage collection. On each iteration, we deselect 32 // items. if (selectionFlags != null) for (int i = 0; i < selectionFlags.length; i++) selectionFlags[i] = 0; if (items == null) return; // Deselect group content. for (int i = 0; i < items.length; i++) { if (items[i] != null) items[i]._deselectAll(); } } protected void _addSelection(GalleryItem item) { // Deselect all items is multi selection is disabled if (!parent.multi) { _deselectAll(); } if (item.getParentItem() == this) { int index = indexOf(item); // Divide position by 32 to get selection bloc for this item. int n = index >> 5; if (selectionFlags == null) { // Create selectionFlag array // Add 31 before dividing by 32 to ensure at least one 'int' is // created if size < 32. selectionFlags = new int[(items.length + 31) >> 5]; } else if (n >= selectionFlags.length) { // Expand selectionArray int[] oldFlags = selectionFlags; selectionFlags = new int[n + 1]; System.arraycopy(oldFlags, 0, selectionFlags, 0, oldFlags.length); } // Get flag position in the 32 bit block and ensure is selected. selectionFlags[n] |= 1 << (index & 0x1f); } } protected boolean isSelected(GalleryItem item) { if (item == null) return false; if (item.getParentItem() == this) { if (selectionFlags == null) return false; int index = indexOf(item); int n = index >> 5; if (n >= selectionFlags.length) return false; int flags = selectionFlags[n]; return flags != 0 && (flags & 1 << (index & 0x1f)) != 0; } return false; } protected void select(int from, int to) { if (Gallery.DEBUG) System.out.println("GalleryItem.select( " + from + "," + to + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ for (int i = from; i <= to; i++) { GalleryItem item = getItem(i); parent._addSelection(item); item._selectAll(); } } /** * Return the current bounds of the item. This method may return negative * values if it is not visible. * * @return */ public Rectangle getBounds() { // The y coords is relative to the client area because it may return // wrong values // on win32 when using the scroll bars. Instead, I use the absolute // position and make it relative using the current translation. if (parent.isVertical()) { return new Rectangle(x, y - parent.translate, width, height); } return new Rectangle(x - parent.translate, y, width, height); } public Font getFont() { return getFont(false); } public Font getFont(boolean itemOnly) { checkWidget(); if (itemOnly) { return font; } // Let the renderer decide the color. if (parent.getGroupRenderer() != null) { return parent.getGroupRenderer().getFont(this); } // Default SWT behavior if no renderer. return font != null ? font : parent.getFont(); } public void setFont(Font font) { checkWidget(); if (font != null && font.isDisposed()) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } this.font = font; this.parent.redraw(this); } /** * Returns the receiver's foreground color. * * @return The foreground color * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * */ public Color getForeground() { return getForeground(false); } /** * Returns the receiver's foreground color. * * @param itemOnly * If TRUE, does not try to use renderer or parent widget to * guess the real foreground color. Note : FALSE is the default * behavior. * * @return The foreground color * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * */ public Color getForeground(boolean itemOnly) { checkWidget(); if (itemOnly) { return this.foreground; } // Let the renderer decide the color. if (parent.getGroupRenderer() != null) { return parent.getGroupRenderer().getForeground(this); } // Default SWT behavior if no renderer. return foreground != null ? foreground : parent.getForeground(); } /** * Sets the receiver's foreground color to the color specified by the * argument, or to the default system color for the item if the argument is * null. * * @param color * The new color (or null) * * @exception IllegalArgumentException * <ul> * <li>ERROR_INVALID_ARGUMENT - if the argument has been * disposed</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * */ public void setForeground(Color foreground) { checkWidget(); if (foreground != null && foreground.isDisposed()) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } this.foreground = foreground; this.parent.redraw(this); } /** * Returns the receiver's background color. * * @return The background color * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * */ public Color getBackground() { return getBackground(false); } /** * Returns the receiver's background color. * * @param itemOnly * If TRUE, does not try to use renderer or parent widget to * guess the real background color. Note : FALSE is the default * behavior. * * @return The background color * * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * */ public Color getBackground(boolean itemOnly) { checkWidget(); // If itemOnly, return this item's color attribute. if (itemOnly) { return this.background; } // Let the renderer decide the color. if (parent.getGroupRenderer() != null) { return parent.getGroupRenderer().getBackground(this); } // Default SWT behavior if no renderer. return background != null ? background : parent.getBackground(); } /** * Sets the receiver's background color to the color specified by the * argument, or to the default system color for the item if the argument is * null. * * @param color * The new color (or null) * * @exception IllegalArgumentException * <ul> * <li>ERROR_INVALID_ARGUMENT - if the argument has been * disposed</li> * </ul> * @exception SWTException * <ul> * <li>ERROR_WIDGET_DISPOSED - if the receiver has been * disposed</li> * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the * thread that created the receiver</li> * </ul> * */ public void setBackground(Color background) { checkWidget(); if (background != null && background.isDisposed()) { SWT.error(SWT.ERROR_INVALID_ARGUMENT); } this.background = background; this.parent.redraw(this); } /** * Reset item values to defaults. */ public void clear() { checkWidget(); // Clear all attributes text[0] = EMPTY_STRING; text[1] = EMPTY_STRING; text[2] = EMPTY_STRING; super.setImage(null); this.font = null; background = null; foreground = null; // Force redraw this.parent.redraw(this); } public void clearAll() { clearAll(false); } public void clearAll(boolean all) { checkWidget(); if (items == null) return; if (virtualGallery) { items = new GalleryItem[items.length]; } else { for (int i = 0; i < items.length; i++) { if (items[i] != null) { if (all) { items[i].clearAll(true); } items[i].clear(); } } } } /** * Selects all of the items in the receiver. */ public void selectAll() { checkWidget(); _selectAll(); parent.redraw(); } protected void _selectAll() { select(0, this.getItemCount() - 1); } public void remove(int index) { checkWidget(); parent._remove(this, index); parent.updateStructuralValues(null, false); parent.updateScrollBarsProperties(); parent.redraw(); } public void remove(GalleryItem item) { remove(indexOf(item)); } /** * Disposes the gallery Item. This method is call directly by gallery and * should not be used by a client */ protected void _dispose() { removeFromParent(); _disposeChildren(); super.dispose(); } protected void _disposeChildren() { if (items != null) { while (items != null) { if (items[items.length - 1] != null) { items[items.length - 1]._dispose(); } else { // This is an uninitialized item, just remove the slot parent._remove(this, items.length - 1); } } } } protected void removeFromParent() { if (parentItem != null) { int index = parent._indexOf(parentItem, this); parent._remove(parentItem, index); } else { int index = parent._indexOf(this); parent._remove(index); } } public void dispose() { checkWidget(); removeFromParent(); _disposeChildren(); super.dispose(); parent.updateStructuralValues(null, false); parent.updateScrollBarsProperties(); parent.redraw(); } public void setText(String string) { setText(0, string); } public void setText(int index, String string) { checkWidget(); if (string == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); text[index] = string; parent.redraw(this); } public String getText() { return getText(0); } public String getText(int index) { checkWidget(); return text[index]; } }