/******************************************************************************* * Copyright (c) 2006-2009 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 * Tom Schindl (tom.schindl@bestsolution.at) - fix for bug 174933 *******************************************************************************/ package org.eclipse.nebula.widgets.gallery; import java.lang.reflect.Array; import java.util.ArrayList; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.TreeListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Item; import org.eclipse.swt.widgets.ScrollBar; import org.eclipse.swt.widgets.TypedListener; /** * <p> * SWT Widget that displays an image gallery<br/> * see http://www.eclipse.org/nebula/widgets/gallery/gallery.php<br/> * This widget requires jdk-1.4+ * </p> * <p> * Style <code>VIRTUAL</code> is used to create a <code>Gallery</code> whose * <code>GalleryItem</code>s are to be populated by the client on an on-demand * basis instead of up-front. This can provide significant performance * improvements for galleries that are very large or for which * <code>GalleryItem</code> population is expensive (for example, retrieving * values from an external source). * </p> * <p> * Here is an example of using a <code>Gallery</code> with style * <code>VIRTUAL</code>: <code><pre> * final Gallery gallery = new Gallery(parent, SWT.VIRTUAL | V_SCROLL | SWT.BORDER); * gallery.setGroupRenderer(new DefaultGalleryGroupRenderer()); * gallery.setItemRenderer(new DefaultGalleryItemRenderer()); * gallery.setItemCount(1000000); * gallery.addListener(SWT.SetData, new Listener() { * public void handleEvent(Event event) { * GalleryItem item = (GalleryItem) event.item; * int index = gallery.indexOf(item); * item.setText("Item " + index); * System.out.println(item.getText()); * } * }); * </pre></code> * </p> * <p> * <dl> * <dt><b>Styles:</b></dt> * <dd>SINGLE, MULTI, VIRTUAL, V_SCROLL, H_SCROLL</dd> * </dl> * </p> * <p> * Note: Only one of the styles SINGLE and MULTI may be specified. * </p> * <p> * Note: Only one of the styles V_SCROLL and H_SCROLL may be specified. * </p> * <p> * <dl> * <dt><b>Events:</b></dt> * <dd>Selection, DefaultSelection, SetData, PaintItem</dd> * </dl> * </p> * <p> * NOTE: THIS WIDGET AND ITS API ARE STILL UNDER DEVELOPMENT. * </p> * * @author Nicolas Richeton (nicolas.richeton@gmail.com) * @contributor Peter Centgraf (bugs 212071, 212073) * @contributor Robert Handschmann (bug 215817) * @contributor Berthold Daum (bug 306144 - selection tuning) */ public class Gallery extends Canvas { private static final String BUG_PLATFORM_LINUX_GTK_174932 = "gtk"; //$NON-NLS-1$ /** * Used to enable debug logging in the Gallery widget. */ protected static boolean DEBUG = false; GalleryItem[] items = null; private GalleryItem[] selection = null; /** * Selection bit flags. Each 'int' contains flags for 32 items. */ protected int[] selectionFlags = null; /** * Virtual mode flag. */ boolean virtual = false; /** * Ultra virtual : non visible groups are not initialized. */ boolean virtualGroups = false; boolean virtualGroupsCompatibilityMode = false; int virtualGroupDefaultItemCount = 10; /** * Scrolling direction flag. True : V_SCROLL, false : H_SCROLL. */ boolean vertical = true; /** * Multi-selection flag */ boolean multi = false; /** * Image quality : interpolation */ int interpolation = SWT.HIGH; /** * Image quality : antialias */ int antialias = SWT.ON; // Internals private int gHeight = 0; private int gWidth = 0; int lastIndexOf = 0; /** * Keeps track of the last selected item. This is necessary to support * "Shift+Mouse button" where we have to select all items between the * previous and the current item and keyboard navigation. */ protected GalleryItem lastSingleClick = null; /** * Current translation (scroll bar position). Can be used by renderer during * paint. */ protected int translate = 0; /** * Low quality on user action : decrease drawing quality on scrolling or * resize. (faster) */ boolean lowQualityOnUserAction = false; protected int lastTranslateValue = 0; protected int lastControlWidth = 0; protected int lastControlHeight = 0; protected int lastContentHeight = 0; protected int lastContentWidth = 0; protected int higherQualityDelay = 500; /** * Keep track of processing the current mouse event. */ private boolean mouseClickHandled = false; /** * Background color, if Control#getBackground() is not used. * * @see Gallery#useControlColors */ private Color backgroundColor; /** * Foreground color, if Control#getForeground() is not used. * * @see Gallery#useControlColors */ private Color foregroundColor; /** * If set to true, the gallery will get colors from parent Control. This may * generate more objects and slightly slow down the application. See Bug * 279822 : https://bugs.eclipse.org/bugs/show_bug.cgi?id=279822 */ private boolean useControlColors = false; AbstractGalleryItemRenderer itemRenderer; AbstractGalleryGroupRenderer groupRenderer; /** * Return the number of root-level items in the receiver. Does not include * children. * * @return */ public int getItemCount() { checkWidget(); if (items == null) return 0; return items.length; } /** * Sets the number of root-level items contained in the receiver. Only work * in VIRTUAL mode. * * @return */ public void setItemCount(int count) { checkWidget(); if (DEBUG) System.out.println("setCount" + count); //$NON-NLS-1$ 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; } updateStructuralValues(null, false); this.updateScrollBarsProperties(); redraw(); } /** * Get current item renderer * * @return */ public AbstractGalleryItemRenderer getItemRenderer() { checkWidget(); return itemRenderer; } /** * Set item receiver. Usually, this does not trigger gallery update. redraw * must be called right after setGroupRenderer to reflect this change. * * @param itemRenderer */ public void setItemRenderer(AbstractGalleryItemRenderer itemRenderer) { checkWidget(); this.itemRenderer = itemRenderer; if (this.itemRenderer != null) this.itemRenderer.setGallery(this); redraw(); } /** * Adds the listener to the collection of listeners who will be notified * when the receiver's selection changes, by sending it one of the messages * defined in the <code>SelectionListener</code> interface. * <p> * When <code>widgetSelected</code> is called, the item field of the event * object is valid. * </p> * * @param listener * the listener which should be notified * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</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> * * @see SelectionListener * @see #removeSelectionListener * @see SelectionEvent */ public void addSelectionListener(SelectionListener listener) { checkWidget(); if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); TypedListener typedListener = new TypedListener(listener); addListener(SWT.Selection, typedListener); addListener(SWT.DefaultSelection, typedListener); } /** * Removes the listener from the collection of listeners who will be * notified when the receiver's selection changes. * * @param listener * the listener which should no longer be notified * * @exception IllegalArgumentException * <ul> * <li>ERROR_NULL_ARGUMENT - if the listener is null</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> * * @see SelectionListener * @see #addSelectionListener(SelectionListener) */ public void removeSelectionListener(SelectionListener listener) { checkWidget(); removeListener(SWT.Selection, listener); removeListener(SWT.DefaultSelection, listener); } /** * Removes the listener from the collection of listeners who will be * notified when items in the receiver are expanded or collapsed. * * @param listener */ public void removeTreeListener(SelectionListener listener) { checkWidget(); removeListener(SWT.Expand, listener); } /** * Adds the listener to the collection of listeners who will be notified * when an item in the receiver is expanded or collapsed by sending it one * of the messages defined in the TreeListener interface. * * @param listener */ public void addTreeListener(TreeListener listener) { checkWidget(); if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); addListener(SWT.Expand, new TypedListener(listener)); } /** * Send SWT.PaintItem for one item. * * @param item * @param index * @param gc * @param x * @param y */ protected void sendPaintItemEvent(Item item, int index, GC gc, int x, int y, int width, int height) { Event e = new Event(); e.item = item; e.type = SWT.PaintItem; e.index = index; // TODO: Does clipping need to be set ? // gc.setClipping(x, y, width, height); e.gc = gc; e.x = x; e.y = y; e.width = width; e.height = height; this.notifyListeners(SWT.PaintItem, e); } /** * #see {@link #setLowQualityOnUserAction(boolean)} * * @return */ public boolean isLowQualityOnUserAction() { return lowQualityOnUserAction; } /** * If set to true, the gallery will disable antialiasing and interpolation * while the user is resizing or scrolling the gallery. This enables faster * redraws at the cost of lower image quality. When every redraw is finished * a last one will be issued using the default (higher) quality. * * @see #setHigherQualityDelay(int) * @param lowQualityOnUserAction */ public void setLowQualityOnUserAction(boolean lowQualityOnUserAction) { this.lowQualityOnUserAction = lowQualityOnUserAction; } /** * @see #setHigherQualityDelay(int) * @return */ public int getHigherQualityDelay() { return higherQualityDelay; } /** * Set the delay after the last user action before the redraw at higher * quality is triggered * * @see #setLowQualityOnUserAction(boolean) * @param higherQualityDelay */ public void setHigherQualityDelay(int higherQualityDelay) { this.higherQualityDelay = higherQualityDelay; } /** * @see #setInterpolation(int) * @return */ public int getInterpolation() { return interpolation; } /** * Sets the gallery's interpolation setting to the parameter, which must be * one of <code>SWT.DEFAULT</code>, <code>SWT.NONE</code>, * <code>SWT.LOW</code> or <code>SWT.HIGH</code>. * * @param interpolation */ public void setInterpolation(int interpolation) { this.interpolation = interpolation; } /** * @see #setAntialias(int) * @return */ public int getAntialias() { return antialias; } /** * * Sets the gallery's anti-aliasing value to the parameter, which must be * one of <code>SWT.DEFAULT</code>, <code>SWT.OFF</code> or * <code>SWT.ON</code>. * * @param antialias */ public void setAntialias(int antialias) { this.antialias = antialias; } /** * Send a selection event for a gallery item * * @param item */ protected void notifySelectionListeners(GalleryItem item, int index, boolean isDefault) { Event e = new Event(); e.widget = this; e.item = item; if (item != null) { e.data = item.getData(); } // TODO: report index // e.index = index; try { if (isDefault) { notifyListeners(SWT.DefaultSelection, e); } else { notifyListeners(SWT.Selection, e); } } catch (RuntimeException ex) { ex.printStackTrace(); } } /** * Send an Expand event for a GalleryItem * * @param item * @param index */ protected void notifyTreeListeners(GalleryItem item, boolean state) { Event e = new Event(); e.widget = this; e.item = item; if (item != null) { e.data = item.getData(); } // TODO: report index // e.index = index; try { notifyListeners(SWT.Expand, e); } catch (RuntimeException ex) { ex.printStackTrace(); } } /** * Create a Gallery * * * @param parent * @param style * - SWT.VIRTUAL switches in virtual mode. <br/> * SWT.V_SCROLL add vertical slider and switches to vertical * mode. <br/> * SWT.H_SCROLL add horizontal slider and switches to horizontal * mode. <br/> * if both V_SCROLL and H_SCROLL are specified, the gallery is in * vertical mode by default. Mode can be changed afterward using * setVertical<br/> * SWT.MULTI allows only several items to be selected at the same * time. */ public Gallery(Composite parent, int style) { super(parent, style | SWT.DOUBLE_BUFFERED); virtual = (style & SWT.VIRTUAL) > 0; vertical = (style & SWT.V_SCROLL) > 0; multi = (style & SWT.MULTI) > 0; setForeground(getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND)); // Add listeners : redraws, mouse and keyboard _addDisposeListeners(); _addResizeListeners(); _addPaintListeners(); _addScrollBarsListeners(); _addMouseListeners(); _addKeyListeners(); // Set defaults _setDefaultRenderers(); // Layout updateStructuralValues(null, false); updateScrollBarsProperties(); redraw(); } private void _setDefaultRenderers() { // Group renderer DefaultGalleryGroupRenderer gr = new DefaultGalleryGroupRenderer(); gr.setMinMargin(2); gr.setItemHeight(56); gr.setItemWidth(72); gr.setAutoMargin(true); gr.setGallery(this); this.groupRenderer = gr; // Item renderer this.itemRenderer = new DefaultGalleryItemRenderer(); itemRenderer.setGallery(this); } /** * Add internal dispose listeners to this gallery. */ private void _addDisposeListeners() { this.addDisposeListener(new DisposeListener() { public void widgetDisposed(DisposeEvent e) { onDispose(); } }); } /** * Add internal paint listeners to this gallery. */ private void _addPaintListeners() { addPaintListener(new PaintListener() { public void paintControl(PaintEvent event) { onPaint(event.gc); } }); } /** * Add internal resize listeners to this gallery. */ private void _addResizeListeners() { addControlListener(new ControlAdapter() { public void controlResized(ControlEvent event) { updateStructuralValues(null, true); updateScrollBarsProperties(); redraw(); } }); } /** * Add internal scrollbars listeners to this gallery. */ private void _addScrollBarsListeners() { // Vertical bar ScrollBar verticalBar = getVerticalBar(); if (verticalBar != null) { verticalBar.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { if (vertical) scrollVertical(); } }); } // Horizontal bar ScrollBar horizontalBar = getHorizontalBar(); if (horizontalBar != null) { horizontalBar.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { if (!vertical) scrollHorizontal(); } }); } } private void _addKeyListeners() { this.addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { switch (e.keyCode) { case SWT.ARROW_LEFT: case SWT.ARROW_RIGHT: case SWT.ARROW_UP: case SWT.ARROW_DOWN: case SWT.PAGE_UP: case SWT.PAGE_DOWN: case SWT.HOME: case SWT.END: GalleryItem newItem = groupRenderer .getNextItem(lastSingleClick, e.keyCode); if (newItem != null) { _deselectAll(false); setSelected(newItem, true, true); lastSingleClick = newItem; _showItem(newItem); redraw(); } break; case SWT.CR: GalleryItem[] selection = getSelection(); GalleryItem item = null; if (selection != null && selection.length > 0) { item = selection[0]; } notifySelectionListeners(item, 0, true); break; } } public void keyReleased(KeyEvent e) { // Nothing yet. } }); } /** * Scroll the Gallery in order to make 'item' visible. * * @param item * Item to show */ public void showItem(GalleryItem item) { this.checkWidget(); this._showItem(item); } void _showItem(GalleryItem item) { int y; int height; Rectangle rect = groupRenderer.getSize(item); if (rect == null) { return; } if (vertical) { y = rect.y; height = rect.height; if (y < translate) { translate = y; } else if (translate + this.getClientArea().height < y + height) { translate = y + height - this.getClientArea().height; } } else { y = rect.x; height = rect.width; if (y < translate) { translate = y; } else if (translate + this.getClientArea().width < y + height) { translate = y + height - this.getClientArea().width; } } this.updateScrollBarsProperties(); redraw(); } /** * Add internal mouse listeners to this gallery. */ private void _addMouseListeners() { addMouseListener(new MouseListener() { public void mouseDoubleClick(MouseEvent e) { onMouseDoubleClick(e); } public void mouseDown(MouseEvent e) { onMouseDown(e); } public void mouseUp(MouseEvent e) { onMouseUp(e); } }); } private void select(int from, int to) { for (int i = from; i <= to; i++) { GalleryItem item = getItem(i); this._addSelection(item); item._selectAll(); } } private void select(GalleryItem from, GalleryItem to) { GalleryItem fromParent = from.getParentItem(); GalleryItem toParent = to.getParentItem(); if (fromParent == toParent) { if (fromParent == null) { int fromIndex = indexOf(from); int toIndex = indexOf(to); select(fromIndex, toIndex); } else { int fromIndex = fromParent.indexOf(from); int toIndex = toParent.indexOf(to); fromParent.select(fromIndex, toIndex); } } else { int fromParentIndex = indexOf(fromParent); int toParentIndex = indexOf(toParent); int fromIndex = fromParent.indexOf(from); int toIndex = toParent.indexOf(to); fromParent.select(fromIndex, fromParent.getItemCount() - 1); for (int i = fromParentIndex + 1; i < toParentIndex; i++) { getItem(i)._selectAll(); } toParent.select(0, toIndex); } this.notifySelectionListeners(to, indexOf(to), false); redraw(); } private boolean getOrder(GalleryItem before, GalleryItem after) { if (before == null || after == null) return true; GalleryItem newParent = before.getParentItem(); GalleryItem oldParent = after.getParentItem(); int beforeParentIndex = indexOf(newParent); int afterParentIndex = indexOf(oldParent); if (newParent == oldParent) { int newParentIndex; int oldParentIndex; if (newParent == null) { newParentIndex = indexOf(before); oldParentIndex = indexOf(after); } else { newParentIndex = newParent.indexOf(before); oldParentIndex = newParent.indexOf(after); } return (newParentIndex < oldParentIndex); } return beforeParentIndex < afterParentIndex; } /** * Toggle item selection status * * @param item * Item which state is to be changed. * @param selected * true is the item is now selected, false if it is now * unselected. * @param notifyListeners * If true, a selection event will be sent to all the current * selection listeners. * */ protected void setSelected(GalleryItem item, boolean selected, boolean notifyListeners) { if (selected) { if (!isSelected(item)) { _addSelection(item); } } else { if (isSelected(item)) { _removeSelection(item); } } // Notify listeners if necessary. if (notifyListeners) { GalleryItem notifiedItem = null; if (item != null && selected) { notifiedItem = item; } else { if (selection != null && selection.length > 0) { notifiedItem = selection[selection.length - 1]; } } int index = -1; if (notifiedItem != null) { index = indexOf(notifiedItem); } notifySelectionListeners(notifiedItem, index, false); } } protected void _addSelection(GalleryItem item) { if (item == null) return; if (this.isSelected(item)) return; // Deselect all items is multi selection is disabled if (!multi) { _deselectAll(false); } if (item.getParentItem() != null) { item.getParentItem()._addSelection(item); } else { 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); } if (selection == null) { selection = new GalleryItem[1]; } else { GalleryItem[] oldSelection = selection; selection = new GalleryItem[oldSelection.length + 1]; System.arraycopy(oldSelection, 0, selection, 0, oldSelection.length); } selection[selection.length - 1] = item; } private void _removeSelection(GalleryItem item) { if (item.getParentItem() == null) { int index = _indexOf(item); selectionFlags[index >> 5] &= ~(1 << (index & 0x1f)); } else _removeSelection(item.getParentItem(), item); int index = _arrayIndexOf(selection, item); if (index == -1) return; selection = (GalleryItem[]) _arrayRemoveItem(selection, index); } protected void _removeSelection(GalleryItem parent, GalleryItem item) { int index = _indexOf(parent, item); parent.selectionFlags[index >> 5] &= ~(1 << (index & 0x1f)); } protected boolean isSelected(GalleryItem item) { if (item == null) return false; if (item.getParentItem() != null) { return item.getParentItem().isSelected(item); } 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; } /** * Deselects all items. */ public void deselectAll() { checkWidget(); _deselectAll(false); redraw(); } /** * Deselects all items and send selection event depending on parameter. * * @param notifyListeners * If true, a selection event will be sent to all the current * selection listeners. */ protected void _deselectAll(boolean notifyListeners) { if (DEBUG) System.out.println("clear"); //$NON-NLS-1$ this.selection = null; // 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; for (int i = 0; i < items.length; i++) { if (items[i] != null) items[i]._deselectAll(); } // Notify listeners if necessary. if (notifyListeners) notifySelectionListeners(null, -1, false); } void onMouseDoubleClick(MouseEvent e) { if (DEBUG) System.out.println("Mouse Double Click"); //$NON-NLS-1$ GalleryItem item = getItem(new Point(e.x, e.y)); if (item != null) { notifySelectionListeners(item, 0, true); } mouseClickHandled = true; } void onMouseUp(MouseEvent e) { if (DEBUG) System.out.println("onMouseUp"); //$NON-NLS-1$ if (mouseClickHandled) { if (DEBUG) { System.out.println("onMouseUp : mouse event already handled"); //$NON-NLS-1$ } return; } if (e.button == 1) { GalleryItem item = getItem(new Point(e.x, e.y)); if (item == null) return; if ((e.stateMask & SWT.MOD1) > 0) { onMouseHandleLeftMod1(e, item, false, true); } else if ((e.stateMask & SWT.SHIFT) > 0) { onMouseHandleLeftShift(e, item, false, true); } else { onMouseHandleLeft(e, item, false, true); } } } /** * Clean up the Gallery and renderers on dispose. */ void onDispose() { // Remove items if not Virtual. if (!virtual) removeAll(); // Dispose renderers if (itemRenderer != null) itemRenderer.dispose(); if (groupRenderer != null) groupRenderer.dispose(); } void onMouseDown(MouseEvent e) { if (DEBUG) System.out.println("Mouse down "); //$NON-NLS-1$ mouseClickHandled = false; if (!_mouseDown(e)) { // Stop handling as requested by the group renderer mouseClickHandled = true; return; } GalleryItem item = getItem(new Point(e.x, e.y)); if (e.button == 1) { if (item == null) { _deselectAll(true); redraw(); mouseClickHandled = true; lastSingleClick = null; } else { if ((e.stateMask & SWT.MOD1) > 0) { onMouseHandleLeftMod1(e, item, true, false); } else if ((e.stateMask & SWT.SHIFT) > 0) { onMouseHandleLeftShift(e, item, true, false); } else { onMouseHandleLeft(e, item, true, false); } } } else if (e.button == 3) { onMouseHandleRight(e, item, true, false); } } private void onMouseHandleLeftMod1(MouseEvent e, GalleryItem item, boolean down, boolean up) { if (up) { // if (lastSingleClick != null) { if (item != null) { if (DEBUG) System.out.println("setSelected : inverse"); //$NON-NLS-1$ setSelected(item, !isSelected(item), true); lastSingleClick = item; redraw(); } // } } } private void onMouseHandleLeftShift(MouseEvent e, GalleryItem item, boolean down, boolean up) { if (up) { if (lastSingleClick != null) { _deselectAll(false); if (getOrder(item, lastSingleClick)) select(item, lastSingleClick); else select(lastSingleClick, item); } } } void onMouseHandleLeft(MouseEvent e, GalleryItem item, boolean down, boolean up) { if (down) { if (!isSelected(item)) { _deselectAll(false); if (DEBUG) System.out.println("setSelected"); //$NON-NLS-1$ setSelected(item, true, true); lastSingleClick = item; redraw(); mouseClickHandled = true; } } else if (up) { if (item == null) { _deselectAll(true); } else { if (DEBUG) System.out.println("setSelected"); //$NON-NLS-1$ _deselectAll(false); setSelected(item, true, lastSingleClick != item); lastSingleClick = item; } redraw(); } } /** * Handle right click. * * @param e * @param item * : The item which is under the cursor or null * @param down * @param up */ void onMouseHandleRight(MouseEvent e, GalleryItem item, boolean down, boolean up) { if (down) { if (DEBUG) System.out.println("right click"); //$NON-NLS-1$ if (item != null && !isSelected(item)) { _deselectAll(false); setSelected(item, true, true); redraw(); mouseClickHandled = true; } } } void onPaint(GC gc) { if (DEBUG) System.out.println("paint"); //$NON-NLS-1$ boolean lowQualityPaint = lowQualityOnUserAction && (translate != lastTranslateValue || (lastControlWidth != getSize().x || lastControlHeight != getSize().y || lastContentHeight != this.gHeight || lastContentWidth != this.gWidth)); try { // Linux-GTK Bug 174932 if (!SWT.getPlatform().equals(BUG_PLATFORM_LINUX_GTK_174932)) { gc.setAdvanced(true); } if (gc.getAdvanced()) { if (lowQualityPaint) { gc.setAntialias(SWT.OFF); gc.setInterpolation(SWT.OFF); } else { gc.setAntialias(antialias); gc.setInterpolation(interpolation); } } // End of Bug 174932 Rectangle clipping = gc.getClipping(); gc.setBackground(this.getBackground()); drawBackground(gc, clipping.x, clipping.y, clipping.width, clipping.height); int[] indexes = getVisibleItems(clipping); if (indexes != null && indexes.length > 0) { // Call preDraw for optimization if (groupRenderer != null) groupRenderer.preDraw(gc); if (itemRenderer != null) itemRenderer.preDraw(gc); for (int i = indexes.length - 1; i >= 0; i--) { if (DEBUG) System.out.println("Drawing group " + indexes[i]); //$NON-NLS-1$ _drawGroup(gc, indexes[i]); } // Call postDraw for optimization / cleanup if (groupRenderer != null) groupRenderer.postDraw(gc); if (itemRenderer != null) itemRenderer.postDraw(gc); } } catch (Exception e) { // We can't let onPaint throw an exception because unexpected // results may occur in SWT. e.printStackTrace(); } // When lowQualityOnUserAction is enabled, keep last state and wait // before updating with a higher quality if (lowQualityOnUserAction) { lastTranslateValue = translate; lastControlWidth = getSize().x; lastControlHeight = getSize().y; lastContentHeight = gHeight; lastContentWidth = gWidth; if (lowQualityPaint) { // Calling timerExec with the same object just delays the // execution (doesn't run twice) Display.getCurrent().timerExec(higherQualityDelay, redrawTimer); } } } RedrawTimer redrawTimer = new RedrawTimer(); protected class RedrawTimer implements Runnable { public void run() { redraw(); } } private int[] getVisibleItems(Rectangle clipping) { if (items == null) return null; int start = vertical ? (clipping.y + translate) : (clipping.x + translate); int end = vertical ? (clipping.y + clipping.height + translate) : (clipping.x + clipping.width + translate); ArrayList al = new ArrayList(); int index = 0; GalleryItem item = null; while (index < items.length) { if (virtualGroups) { item = _getItem(index, false); } else { item = _getItem(index); } if ((vertical ? item.y : item.x) > end) break; if ((vertical ? (item.y + item.height) : (item.x + item.width)) >= start) al.add(new Integer(index)); index++; } int[] result = new int[al.size()]; for (int i = 0; i < al.size(); i++) result[i] = ((Integer) al.get(i)).intValue(); return result; } /** * Refresh item by firering SWT.SetData. * <p> * Currently not implemented. * </p> * * @param index */ public void refresh(int index) { checkWidget(); if (index < getItemCount()) { // TODO: refresh } } /** * Redraw the item given as parameter. * * @param item */ public void redraw(GalleryItem item) { checkWidget(); // Redraw only the item's bounds Rectangle bounds = item.getBounds(); redraw(bounds.x, bounds.y, bounds.width, bounds.height, true); } /** * Handle the drawing of root items * * @param gc * @param index */ private void _drawGroup(GC gc, int index) { GalleryItem item = null; if (virtualGroups) { // Ultra virtual : when a group is about to be drawn, initialize it // and update gallery layout according to the new size item = _getItem(index, false); if (item.isUltraLazyDummy()) { boolean updateLocation = (vertical && item.y < translate) || (!vertical && item.x < translate); int oldSize = item.height; item = _getItem(index, true); // Compatibility mode : ensure all previous items are already // initialized if (virtualGroupsCompatibilityMode) { for (int i = 0; i < index; i++) { _getItem(i); } } updateStructuralValues(item, false); if (DEBUG) { System.out.println("old" + oldSize + " new " + item.height //$NON-NLS-1$//$NON-NLS-2$ + " translate " + translate); //$NON-NLS-1$ } if (updateLocation) { translate += (item.height - oldSize); if (DEBUG) { System.out.println("updated to : " + translate); //$NON-NLS-1$ } } this.updateScrollBarsProperties(); redraw(); } } else { // Default behavior : get the item with no special handling. item = getItem(index); } if (item == null) return; // update item attributes this.groupRenderer.setExpanded(item.isExpanded()); // Drawing area int x = this.vertical ? item.x : item.x - this.translate; int y = this.vertical ? item.y - translate : item.y; Rectangle clipping = gc.getClipping(); Rectangle previousClipping = new Rectangle(clipping.x, clipping.y, clipping.width, clipping.height); clipping.intersect(new Rectangle(x, y, item.width, item.height)); gc.setClipping(clipping); // Draw group this.groupRenderer.draw(gc, item, x, y, clipping.x, clipping.y, clipping.width, clipping.height); gc.setClipping(previousClipping); } /** * If table is virtual and item at pos i has not been set, call the callback * listener to set its value. * * @return */ private void updateItem(GalleryItem parentItem, int i, boolean create) { if (virtual) { GalleryItem galleryItem; if (parentItem == null) { // Parent is the Gallery widget galleryItem = items[i]; if (galleryItem == null || (virtualGroups && galleryItem.isUltraLazyDummy() && create)) { if (DEBUG) { System.out.println("Virtual/creating item "); //$NON-NLS-1$ } galleryItem = new GalleryItem(this, SWT.NONE, i, false); items[i] = galleryItem; if (virtualGroups && !create) { galleryItem.setItemCount(virtualGroupDefaultItemCount); galleryItem.setUltraLazyDummy(true); galleryItem.setExpanded(true); } else { setData(galleryItem, i); } } } else { // Parent is another GalleryItem galleryItem = parentItem.items[i]; if (galleryItem == null) { if (DEBUG) { System.out.println("Virtual/creating item "); //$NON-NLS-1$ } galleryItem = new GalleryItem(parentItem, SWT.NONE, i, false); parentItem.items[i] = galleryItem; setData(galleryItem, i); } } } } /** * Sends SWT.SetData event. Used if SWT.VIRTUAL * * @param galleryItem * @param index */ protected void setData(GalleryItem galleryItem, int index) { Item item = galleryItem; Event e = new Event(); e.item = item; e.type = SWT.SetData; e.index = index; this.notifyListeners(SWT.SetData, e); } /** * Recalculate structural values using the group renderer<br> * Gallery and item size will be updated. * * @param keepLocation * if true, the current scrollbars position ratio is saved and * restored even if the gallery size has changed. (Visible items * stay visible) * @deprecated Use {@link #updateStructuralValues(GalleryItem,boolean)} * instead */ protected void updateStructuralValues(boolean keepLocation) { updateStructuralValues(null, keepLocation); } /** * Recalculate structural values using the group renderer<br> * Gallery and item size will be updated. * * @param changedGroup * the group that was modified since the last layout. If the * group renderer or more that one group have changed, use null * as parameter (full update) * @param keepLocation * if true, the current scrollbars position ratio is saved and * restored even if the gallery size has changed. (Visible items * stay visible) */ protected void updateStructuralValues(GalleryItem changedGroup, boolean keepLocation) { if (DEBUG) { System.out.println("Client Area : " + this.getClientArea().x + " " //$NON-NLS-1$//$NON-NLS-2$ + this.getClientArea().y + " " + this.getClientArea().width //$NON-NLS-1$ + " " + this.getClientArea().height); //$NON-NLS-1$ } Rectangle area = this.getClientArea(); float pos = 0; if (vertical) { if (gHeight > 0 && keepLocation) pos = (float) (translate + 0.5 * area.height) / gHeight; gWidth = area.width; gHeight = calculateSize(changedGroup); if (keepLocation) translate = (int) (gHeight * pos - 0.5 * area.height); } else { if (gWidth > 0 && keepLocation) pos = (float) (translate + 0.5 * area.width) / gWidth; gWidth = calculateSize(changedGroup); gHeight = area.height; if (keepLocation) translate = (int) (gWidth * pos - 0.5 * area.width); } validateTranslation(); if (DEBUG) { System.out.println("Content Size : " + gWidth + " " + gHeight); //$NON-NLS-1$//$NON-NLS-2$ } } /** * Calculate full height (or width) of the Gallery. The group renderer is * used to calculate the size of each group. * * @return */ private int calculateSize(GalleryItem onlyUpdateGroup) { if (groupRenderer == null) return 0; groupRenderer.preLayout(null); int currentHeight = 0; int mainItemCount = getItemCount(); for (int i = 0; i < mainItemCount; i++) { GalleryItem item = null; if (virtualGroups) { item = this._getItem(i, false); } else { item = this._getItem(i); } if (onlyUpdateGroup != null && !onlyUpdateGroup.equals(item)) { // Item has not changed : no layout. if (vertical) { item.y = currentHeight; item.x = getClientArea().x; currentHeight += item.height; } else { item.y = getClientArea().y; item.x = currentHeight; currentHeight += item.width; } } else { this.groupRenderer.setExpanded(item.isExpanded()); // TODO: Not used ATM // int groupItemCount = item.getItemCount(); if (vertical) { item.y = currentHeight; item.x = getClientArea().x; item.width = getClientArea().width; item.height = -1; this.groupRenderer.layout(null, item); currentHeight += item.height; } else { item.y = getClientArea().y; item.x = currentHeight; item.width = -1; item.height = getClientArea().height; this.groupRenderer.layout(null, item); currentHeight += item.width; } // Unused ? // Point s = this.getSize(item.hCount, item.vCount, itemSizeX, // itemSizeY, userMargin, realMargin); // item.height = s.y; } } groupRenderer.postLayout(null); return currentHeight; } /** * Move the scrollbar to reflect the current visible items position. <br/> * The bar which is moved depends of the current gallery scrolling : * vertical or horizontal. * */ protected void updateScrollBarsProperties() { if (vertical) { updateScrollBarProperties(getVerticalBar(), getClientArea().height, gHeight); } else { updateScrollBarProperties(getHorizontalBar(), getClientArea().width, gWidth); } } /** * Move the scrollbar to reflect the current visible items position. * * @param bar * - the scroll bar to move * @param clientSize * - Client (visible) area size * @param totalSize * - Total Size */ private void updateScrollBarProperties(ScrollBar bar, int clientSize, int totalSize) { if (bar == null) return; bar.setMinimum(0); bar.setPageIncrement(clientSize); bar.setMaximum(totalSize); bar.setThumb(clientSize); // Let the group renderer use a custom increment value. if (groupRenderer != null) bar.setIncrement(groupRenderer.getScrollBarIncrement()); if (totalSize > clientSize) { if (DEBUG) System.out.println("Enabling scrollbar"); //$NON-NLS-1$ bar.setEnabled(true); bar.setVisible(true); bar.setSelection(translate); // Ensure that translate has a valid value. validateTranslation(); } else { if (DEBUG) System.out.println("Disabling scrollbar"); //$NON-NLS-1$ bar.setEnabled(false); bar.setVisible(false); bar.setSelection(0); translate = 0; } } /** * Check the current translation value. Must be > 0 and < gallery * size.<br/> * Invalid values are fixed. */ private void validateTranslation() { Rectangle area = this.getClientArea(); // Ensure that translate has a valid value. int totalSize = 0; int clientSize = 0; // Fix negative values if (translate < 0) translate = 0; // Get size depending on vertical setting. if (vertical) { totalSize = gHeight; clientSize = area.height; } else { totalSize = gWidth; clientSize = area.width; } if (totalSize > clientSize) { // Fix translate too big. if (translate + clientSize > totalSize) { translate = totalSize - clientSize; } } else { translate = 0; } } protected void scrollVertical() { int areaHeight = getClientArea().height; if (gHeight > areaHeight) { // image is higher than client area ScrollBar bar = getVerticalBar(); scroll(0, translate - bar.getSelection(), 0, 0, getClientArea().width, areaHeight, false); translate = bar.getSelection(); } else { translate = 0; } } protected void scrollHorizontal() { int areaWidth = getClientArea().width; if (gWidth > areaWidth) { // image is higher than client area ScrollBar bar = getHorizontalBar(); scroll(translate - bar.getSelection(), 0, 0, 0, areaWidth, getClientArea().height, false); translate = bar.getSelection(); } else { translate = 0; } } 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) { // Insert item items = (GalleryItem[]) _arrayAddItem(items, item, position); // Update Gallery updateStructuralValues(null, false); updateScrollBarsProperties(); } /** * Get the item at index.<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 _getItem(index); } /** * This method is used by items to implement getItem( index ) * * @param parent * @param index * @return */ protected GalleryItem _getItem(GalleryItem parent, int index) { if (index < parent.getItemCount()) { // Refresh item if it is not set yet updateItem(parent, index, true); if (parent.items == null) { return null; } else { return parent.items[index]; } } return null; } /** * Get the item at index.<br/> * If SWT.VIRTUAL is used and the item has not been used yet, the item is * created and a SWT.SetData is fired.<br/> * * This is the internal implementation of this method : checkWidget() is not * used. * * @param index * @return The item at 'index' (not null) */ protected GalleryItem _getItem(int index) { return _getItem(index, true); } /** * Get the item at 'index'.<br/> * If SWT.VIRTUAL is used, 'create' is true and the item has not been used * yet, the item is created and a SWT.SetData is fired.<br/> * * @param index * @param create * @return The item at 'index' or null if there was no item and 'create' was * false. */ protected GalleryItem _getItem(int index, boolean create) { if (index < getItemCount()) { updateItem(null, index, create); return items[index]; } return null; } /** * Forward the mouseDown event to the corresponding group according to the * mouse position. * * @param event * The original MouseEvent * @return true if Gallery should continue standard mouse click handling */ protected boolean _mouseDown(MouseEvent event) { if (DEBUG) System.out.println("getitem " + event.x + " " + event.y); //$NON-NLS-1$//$NON-NLS-2$ GalleryItem group = this._getGroup(new Point(event.x, event.y)); if (group != null) { int pos = vertical ? (event.y + translate) : (event.x + translate); try { return groupRenderer.mouseDown(group, event, new Point( vertical ? event.x : pos, vertical ? pos : event.y)); } catch (Exception e) { // We can't let 3rd party groupRenderer#mouseDown throw an // exception because unexpected results may occur in SWT. e.printStackTrace(); } } return true; } /** * Get item at pixel position * * @param coords * @return GalleryItem or null */ public GalleryItem getItem(Point coords) { checkWidget(); if (DEBUG) System.out.println("getitem " + coords.x + " " + coords.y); //$NON-NLS-1$ //$NON-NLS-2$ int pos = vertical ? (coords.y + translate) : (coords.x + translate); GalleryItem group = this._getGroup(coords); if (group != null) return groupRenderer.getItem(group, new Point( vertical ? coords.x : pos, vertical ? pos : coords.y)); return null; } /** * Get group at pixel position * * @param coords * @return GalleryItem or null */ private GalleryItem _getGroup(Point coords) { // If there is no item in the gallery, return asap if (items == null) return null; int pos = vertical ? (coords.y + translate) : (coords.x + translate); int index = 0; GalleryItem item = null; while (index < items.length) { item = getItem(index); if ((vertical ? item.y : item.x) > pos) break; if ((vertical ? (item.y + item.height) : (item.x + item.width)) >= pos) return item; index++; } return null; } /** * <p> * Get group at pixel position (relative to client area). * </p> * <p> * This is an experimental API which is exposing an internal method, it may * become deprecated at some point. * </p> * * @param coords * @return */ public GalleryItem getGroup(Point coords) { checkWidget(); return _getGroup(coords); } // TODO: Not used ATM // private void clear() { // checkWidget(); // if (virtual) { // setItemCount(0); // } else { // items = null; // } // // updateStructuralValues(true); // updateScrollBarsProperties(); // } /** * Clear all Gallery items.<br/> * * * If the Gallery is virtual, the item count is not reseted and all items * will be created again at their first use.<br/> * * @param all * If true, all children will be cleared. Only groups are cleared * otherwise. */ public void clearAll(boolean all) { checkWidget(); if (items == null) return; if (virtual) { items = new GalleryItem[items.length]; } else { for (int i = 0; i < items.length; i++) { if (items[i] != null) { if (all) { items[i].clearAll(true); } else { items[i].clear(); } } } } // TODO: I'm clearing selection here // but we have to check that Table has the same behavior this._deselectAll(false); updateStructuralValues(null, false); updateScrollBarsProperties(); redraw(); } /** * Clear all GalleryGroups */ public void clearAll() { clearAll(false); } /** * Clear one item.<br/> * * @param index */ public void clear(int index) { clear(index, false); } /** * Clear one item and all its children if 'all' is true * * @param index * @param all */ public void clear(int index, boolean all) { checkWidget(); // Item is already cleared, return immediately. if (items[index] == null) return; if (virtual) { // Clear item items[index] = null; // In virtual mode, clearing an item can change its content, so // force content update updateStructuralValues(null, false); updateScrollBarsProperties(); } else { // Reset item items[index].clear(); if (all) { items[index].clearAll(true); } } redraw(); } /** * Returns the index of a GalleryItem. * * @param parentItem * @param item * @return */ public int indexOf(GalleryItem item) { checkWidget(); if (item == null) { SWT.error(SWT.ERROR_NULL_ARGUMENT); // SWT.error throws an exception } if (item.getParentItem() == null) { return _indexOf(item); } return _indexOf(item.getParentItem(), item); } /** * Returns the index of a GalleryItem when it is a root Item * * @param parentItem * @param item * @return */ protected int _indexOf(GalleryItem item) { int itemCount = getItemCount(); if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); if (1 <= lastIndexOf && lastIndexOf < itemCount - 1) { if (items[lastIndexOf] == item) return lastIndexOf; if (items[lastIndexOf + 1] == item) return ++lastIndexOf; if (items[lastIndexOf - 1] == item) return --lastIndexOf; } if (lastIndexOf < itemCount / 2) { for (int i = 0; i < itemCount; i++) { if (items[i] == item) return lastIndexOf = i; } } else { for (int i = itemCount - 1; i >= 0; --i) { if (items[i] == item) return lastIndexOf = i; } } return -1; } /** * Returns the index of a GalleryItem when it is not a root Item * * @param parentItem * @param item * @return */ protected int _indexOf(GalleryItem parentItem, GalleryItem item) { int itemCount = parentItem.getItemCount(); if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); if (1 <= parentItem.lastIndexOf && parentItem.lastIndexOf < itemCount - 1) { if (parentItem.items[parentItem.lastIndexOf] == item) return parentItem.lastIndexOf; if (parentItem.items[parentItem.lastIndexOf + 1] == item) return ++parentItem.lastIndexOf; if (parentItem.items[parentItem.lastIndexOf - 1] == item) return --parentItem.lastIndexOf; } if (parentItem.lastIndexOf < itemCount / 2) { for (int i = 0; i < itemCount; i++) { if (parentItem.items[i] == item) return parentItem.lastIndexOf = i; } } else { for (int i = itemCount - 1; i >= 0; --i) { if (parentItem.items[i] == item) return parentItem.lastIndexOf = i; } } return -1; } 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; } /** * @see Gallery#setUseControlColors(boolean) * @return true if Gallery uses parent colors. */ public boolean isUseControlColors() { return useControlColors; } /** * Set useControlColors to true in order to get colors from parent Control * (SWT default). This may generate more objects on painting and slightly * slow down the application. See Bug 279822 : * https://bugs.eclipse.org/bugs/show_bug.cgi?id=279822 * * If enabled, you'll get new Color objects each time you call getXXXColor() * on Gallery or GalleryItem. * * Default is false : colors are stored locally in Gallery, and you'll get * the same object each time you call getXXXColor() on Gallery * orGalleryItem. The Gallery may not catch color changes on parent control. * * @param useControlColors */ public void setUseControlColors(boolean useControlColors) { this.useControlColors = useControlColors; } /* * (non-Javadoc) * * @see org.eclipse.swt.widgets.Control#getBackground() */ public Color getBackground() { return getBackground(false); } /** * Returns the receiver's background color. * * @param galleryOnly * If TRUE, does not try to parent widget or Display defaults to * guess the real background color. Note : FALSE is the default * behavior. * * @return The background color or null if galleryOnly was used and the * gallery has not foreground color set. * * @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 galleryOnly) { if (galleryOnly) { return this.backgroundColor; } if (useControlColors) { return super.getBackground(); } return backgroundColor != null ? backgroundColor : getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND); } /* * (non-Javadoc) * * @see org.eclipse.swt.widgets.Control#getForeground() */ public Color getForeground() { return getForeground(false); } /** * Returns the receiver's foreground color. * * @param galleryOnly * If TRUE, does not try to parent widget or Display defaults to * guess the real foreground color. Note : FALSE is the default * behavior. * * @return The foreground color or null if galleryOnly was used and the * gallery has not foreground color set. * * @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 galleryOnly) { if (galleryOnly) { return this.foregroundColor; } if (useControlColors) { return super.getForeground(); } return foregroundColor != null ? foregroundColor : getDisplay().getSystemColor(SWT.COLOR_LIST_FOREGROUND); } /* * (non-Javadoc) * * @see * org.eclipse.swt.widgets.Control#setBackground(org.eclipse.swt.graphics * .Color) */ public void setBackground(Color color) { // Cache color locally if (!useControlColors) { this.backgroundColor = color; } // Always call Control#setBackground(); Additionally, this will trigger // a redraw. super.setBackground(color); } /* * (non-Javadoc) * * @see * org.eclipse.swt.widgets.Control#setForeground(org.eclipse.swt.graphics * .Color) */ public void setForeground(Color color) { // Cache color locally if (!useControlColors) { this.foregroundColor = color; } // Always call Control#setForeground(); Additionally, this will trigger // a redraw. super.setForeground(color); } /** * Checks if the Gallery was created with SWT.V_SCROLL (ie has a vertical * scroll bar). * * @return true if the gallery has the SWT.V_SCROLL style. */ public boolean isVertical() { checkWidget(); return vertical; } /** * @deprecated * @param vertical */ public void setVertical(boolean vertical) { checkWidget(); this.vertical = vertical; this.updateStructuralValues(null, true); redraw(); } public AbstractGalleryGroupRenderer getGroupRenderer() { return groupRenderer; } public void setGroupRenderer(AbstractGalleryGroupRenderer groupRenderer) { this.groupRenderer = groupRenderer; if (this.groupRenderer != null) { this.groupRenderer.setGallery(this); } this.updateStructuralValues(null, true); this.updateScrollBarsProperties(); this.redraw(); } public GalleryItem[] getSelection() { if (selection == null) { return new GalleryItem[0]; } return selection; } public int getSelectionCount() { if (selection == null) return 0; return selection.length; } /** * Selects all of the items in the receiver. */ public void selectAll() { checkWidget(); _selectAll(); redraw(); } protected void _selectAll() { select(0, this.getItemCount() - 1); } public void setSelection(GalleryItem[] items) { checkWidget(); _deselectAll(false); for (int i = 0; i < items.length; i++) { this.setSelected(items[i], true, false); // Ensure item is visible _showItem(items[i]); // Simulate mouse click to enable keyboard navigation lastSingleClick = items[i]; } redraw(); } public void removeAll() { checkWidget(); if (items != null) { // Clear items GalleryItem[] tmpArray = new GalleryItem[items.length]; System.arraycopy(items, 0, tmpArray, 0, items.length); for (int i = 0; i < tmpArray.length; i++) { // Dispose items if not virtual // if (!virtual) { if (tmpArray[i] != null) { tmpArray[i]._dispose(); } // } } } } public void remove(int index) { checkWidget(); _remove(index); updateStructuralValues(null, false); updateScrollBarsProperties(); redraw(); } public void remove(GalleryItem item) { if (item.getParentItem() == null) { remove(indexOf(item)); } else { item.getParentItem().remove(item); } } protected void _remove(int index) { // if (!virtual) { if (isSelected(items[index])) { setSelected(items[index], false, false); } this.items = (GalleryItem[]) this._arrayRemoveItem(this.items, index); // if( virtual) // itemCount--; // } } protected void _remove(GalleryItem parent, int index) { if (isSelected(parent.items[index])) { setSelected(parent.items[index], false, false); } parent.items = (GalleryItem[]) this._arrayRemoveItem(parent.items, index); } protected Object[] _arrayRemoveItem(Object[] array, int index) { if (array == null) return null; if (array.length == 1 && index == 0) return null; Object[] newArray = (Object[]) Array.newInstance( array.getClass().getComponentType(), array.length - 1); if (index > 0) System.arraycopy(array, 0, newArray, 0, index); if (index + 1 < array.length) System.arraycopy(array, index + 1, newArray, index, newArray.length - index); return newArray; } /** * Adds an item to an array. * * @param array * @param object * @param index * : if index == -1, item is added at the end of the array. * @return */ protected Object[] _arrayAddItem(Object[] array, Object object, int index) { // Get current array length int length = 0; if (array != null) length = array.length; // Create new array Object[] newArray = (Object[]) Array.newInstance(object.getClass(), length + 1); if (array != null) System.arraycopy(array, 0, newArray, 0, length); if (index != -1) { // Move all items for (int i = newArray.length - 2; i >= index; i--) { if (i >= 0) { newArray[i + 1] = newArray[i]; } } // Insert item at index newArray[index] = object; } else { // Insert item at the end newArray[newArray.length - 1] = object; } return newArray; } protected int _arrayIndexOf(int[] array, int value) { if (array == null) return -1; for (int i = array.length - 1; i >= 0; --i) { if (array[i] == value) { return i; } } return -1; } protected int _arrayIndexOf(Object[] array, Object value) { if (array == null) return -1; for (int i = array.length - 1; i >= 0; --i) { if (array[i] == value) { return i; } } return -1; } protected int[] _arrayRemoveItem(int[] array, int index) { if (array == null) return null; if (array.length == 1 && index == 0) return null; int[] newArray = new int[array.length - 1]; if (index > 0) System.arraycopy(array, 0, newArray, 0, index); if (index + 1 < array.length) System.arraycopy(array, index + 1, newArray, index, newArray.length - index); return newArray; } public void _setGalleryItems(GalleryItem[] items) { this.items = items; } /** * @see #setVirtualGroups(boolean) * * @return */ public boolean isVirtualGroups() { return virtualGroups; } /** * Enable virtual groups * * <p> * When a gallery has the SWT.VIRTUAL flag, only items are initialized on * display. All groups need to be initialized from the beginning to * calculate the total size of the content. * </p> * * <p> * Virtual groups enable creating groups AND items lazily at the cost of a * poor approximation of the total size of the content. * </p> * * <p> * While a group isn't initialized, the item count defined as default item * count is used. * </p> * * <p> * When a group comes into view, it is initialized using the setData event, * and the size of the gallery content is updated to match the real value. * </p> * * <p> * From the developer point of view, virtual groups uses exactly the same * code as the standard virtual mode of SWT. * </p> * * <p> * This mode can create visual glitches with code that automatically scrolls * the widget such as SAT Smooth Scrolling. In that case, you can enable the * compatibility mode which is little less lazy that the default virtual * groups, but still better than the standard virtual mode * </p> * * @see #setVirtualGroupDefaultItemCount(int) * @see #setVirtualGroupsCompatibilityMode(boolean) * * @param virtualGroups */ public void setVirtualGroups(boolean virtualGroups) { this.virtualGroups = virtualGroups; } /** * @see #setVirtualGroupDefaultItemCount(int) * @return */ public int getVirtualGroupDefaultItemCount() { return virtualGroupDefaultItemCount; } /** * @see #setVirtualGroupsCompatibilityMode(boolean) * @return */ public boolean isVirtualGroupsCompatibilityMode() { return virtualGroupsCompatibilityMode; } /** * Enable the compatibility workaround for problems with the ultra virtual * mode. * * @see #setVirtualGroups(boolean) * @param compatibilityMode */ public void setVirtualGroupsCompatibilityMode(boolean compatibilityMode) { this.virtualGroupsCompatibilityMode = compatibilityMode; } /** * Set the item count used when a group is not yet initialized (with virtual * groups). Since the virtual groups make the size of the gallery change * while scrolling, a fine tuned item count can improve the accuracy of the * slider. * * @see #setVirtualGroups(boolean) * * @param defaultItemCount */ public void setVirtualGroupDefaultItemCount(int defaultItemCount) { this.virtualGroupDefaultItemCount = defaultItemCount; } }