Java tutorial
/* * Copyright 2008 Eckhart Arnold (eckhart_arnold@hotmail.com). * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package de.eckhartarnold.client; import java.util.HashSet; import com.google.gwt.animation.client.Animation; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.MouseOverEvent; import com.google.gwt.event.dom.client.MouseOverHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; /** * Displays a row of thumbnail images as if * on a film strip. * * @author ecki * */ public class Filmstrip extends Composite implements ResizeListener { /** * An interface that encapsulates a callback method which is called when * the user clicks on an image of the film strip. */ public interface IPickImage { /** * Called when the user clicks on a certain image on the film strip. * @param imageNr the number of the image that has been clicked on by * the user. */ void onPickImage(int imageNr); } private class Sliding extends Animation { private int displacement, current; public boolean isComplete() { return current == 0; } public void onComplete() { if (current != 0) { current = 0; redraw(0); } thumbnails.get(cursor).setStylePrimaryName("filmstripHighlighted"); } public void onUpdate(double progress) { int pos = (int) ((1.0 - progress) * displacement); if (Math.abs(pos - current) >= 10) { // do not take every small step! current = pos; redraw(current); } } public void setDisplacement(int displacement) { cancel(); current = -1; this.displacement = displacement; } } private int width, height = -1; private int borderSize = 2; // should be the same as specified in the css-file!!! private int cursor = 0; private int filmstripBorderSize = 0; // should be the same as specified in the css-file!!! private boolean isLoaded = false; // true, if the filmstrip is visible private int slidingDuration = 200; private HashSet<Image> visible; private IPickImage pickImageCallback; private final SimplePanel envelope; private final AbsolutePanel panel; private final Sliding sliding = new Sliding(); private final Thumbnails thumbnails; /** * The constructor of class <code>Filmstrip</code> * @param collection the image collection info from which to construct the * film strip */ public Filmstrip(ImageCollectionInfo collection) { this(new Thumbnails(collection)); } /** * The constructor of class <code>Filmstrip</code> * @param thumbnailImages the thumb nail images to be displayed on the * film strip */ public Filmstrip(Thumbnails thumbnailImages) { this.thumbnails = thumbnailImages; envelope = new SimplePanel(); envelope.addStyleName("filmstripEnvelope"); panel = new AbsolutePanel(); panel.addStyleName("filmstripPanel"); envelope.setWidget(panel); initWidget(envelope); // sinkEvents(Event.ONMOUSEWHEEL); ClickHandler imageClickHandler = new ClickHandler() { public void onClick(ClickEvent event) { Widget sender = (Widget) event.getSource(); if (pickImageCallback != null) { pickImageCallback.onPickImage(thumbnails.indexOf((Image) sender)); } } }; MouseDownHandler imageMouseDownHandler = new MouseDownHandler() { public void onMouseDown(MouseDownEvent event) { Widget sender = (Widget) event.getSource(); if (pickImageCallback != null && sender != thumbnails.get(cursor)) { sender.addStyleName("filmstripPressed"); } } }; MouseOverHandler imageMouseOverHandler = new MouseOverHandler() { public void onMouseOver(MouseOverEvent event) { Widget sender = (Widget) event.getSource(); if (pickImageCallback != null && sender != thumbnails.get(cursor)) { sender.addStyleName("filmstripTouched"); } } }; MouseOutHandler imageMouseOutHandler = new MouseOutHandler() { public void onMouseOut(MouseOutEvent event) { Widget sender = (Widget) event.getSource(); if (pickImageCallback != null && sender != thumbnails.get(cursor)) { sender.removeStyleName("filmstripTouched"); sender.removeStyleName("filmstripPressed"); } } }; MouseUpHandler imageMouseUpHandler = new MouseUpHandler() { public void onMouseUp(MouseUpEvent event) { Widget sender = (Widget) event.getSource(); if (pickImageCallback != null && sender != thumbnails.get(cursor)) { sender.removeStyleName("filmstripPressed"); } } }; for (int i = 0; i < thumbnails.size(); i++) { Image img = thumbnails.get(i); if (i == cursor) img.setStyleName("filmstripHighlighted"); else img.setStyleName("filmstrip"); img.addClickHandler(imageClickHandler); img.addMouseDownHandler(imageMouseDownHandler); img.addMouseOverHandler(imageMouseOverHandler); img.addMouseOutHandler(imageMouseOutHandler); img.addMouseUpHandler(imageMouseUpHandler); } visible = new HashSet<Image>(); } /** * Returns the duration in milliseconds for sliding from one image * to the next. * @return Duration in milliseconds */ public int getSlidingDuration() { return slidingDuration; } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.Composite#onBrowserEvent(com.google.gwt.user.client.Event) */ /* @Override public void onBrowserEvent(Event event) { switch (event.getTypeInt()) { case Event.ONMOUSEWHEEL: } }*/ /* (non-Javadoc) * @see de.eckhartarnold.client.ResizeListener#onResized() */ public void onResized() { //if (!loaded) return; // envelope.clear(); assert envelope.getWidget() == null : "prepareResized() must be called before onResized()!"; int height = Toolbox.getOffsetHeight(envelope); int width = envelope.getOffsetWidth(); envelope.setWidget(panel); if (width == this.width && height == this.height) return; panel.setPixelSize(width - filmstripBorderSize * 2, height); if (width != this.width) this.width = width; if (height != this.height && height != 0) { this.height = height; thumbnails.adjustToHeight(height - 2 * borderSize); } redraw(0); } /** * Removes the {@link AbsolutePanel} so that the image panels size * can be determined correctly by the browser before calling * <code>onResized</code>. The <code>AbsolutePanel</code> * is restored by method <code>onResized</code>. This method therefore * should always be used in conjunction with the method * <code>onResized</code>. */ public void prepareResized() { panel.setSize("100%", "100%"); // required for Internet Explorer 7 compatibility! envelope.clear(); } /** * Puts the focus on the given image, i.e. the film strip will be * scrolled so that the focused image appears in the center. It's * frame will be highlighted. * * @param imageNr */ public void focusImage(int imageNr) { assert imageNr >= 0 && imageNr < thumbnails.size(); if (imageNr != cursor || !sliding.isComplete()) { sliding.cancel(); thumbnails.get(cursor).setStylePrimaryName("filmstrip"); if (slidingDuration > 0 && isLoaded) { sliding.setDisplacement(displacement(imageNr)); int duration = slidingDuration * Math.min(10, Math.abs(imageNr - cursor)); cursor = imageNr; sliding.run(duration); } else { cursor = imageNr; thumbnails.get(cursor).setStylePrimaryName("filmstripHighlighted"); if (height > 0 && isLoaded) redraw(0); } } } /** * Sets the callback that is called when the user selects an image by * clicking on a thumbnail or by turning the mouse wheel while the mouse * pointer is located over the preview filmstrip. * * @param callback an Interface of the respective "callback" interface * or <code>null</code>. */ public void setPickImageCallback(IPickImage callback) { pickImageCallback = callback; if (callback != null) { for (int i = 0; i < thumbnails.size(); i++) { Image img = thumbnails.get(i); img.addStyleDependentName("selectable"); } } else { for (int i = 0; i < thumbnails.size(); i++) { Image img = thumbnails.get(i); img.removeStyleDependentName("selectable"); } } } /** * Sets the duration in milliseconds for sliding from one image * to the next. * @param duration Duration in milliseconds */ public void setSlidingDuration(int duration) { slidingDuration = duration; } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.Widget#onLoad() */ @Override protected void onLoad() { if (envelope.getWidget() != null) { prepareResized(); onResized(); } // else prepare resize has already been called! isLoaded = true; redraw(0); } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.Widget#onUnload() */ @Override protected void onUnload() { isLoaded = false; } /** * Moves a thumbnail image to the given position within the widget's * absolute panel. Automatically adds an image to the widget's panel * if it was not visible before, or removes it, if it is not visible * any more (due to its position lying outside the panel's boundaries). * * @param imgIndex the index number of the thumbnail image * @param left the position of the image on the absolute panel */ private void move(int imgIndex, int left) { Image img = thumbnails.get(imgIndex); int imgWidth = thumbnails.imageSize(imgIndex)[0]; if (visible.contains(img)) { if (left < this.width && left + imgWidth > 0) { panel.setWidgetPosition(img, left, 0); } else { panel.remove(img); visible.remove(img); } img.removeStyleName("filmstripTouched"); img.removeStyleName("filmstripPressed"); } else { if (left < this.width && left + imgWidth > 0) { panel.add(img, left, 0); visible.add(img); } } } /** * Determines the displacement of the currently focused slide to the * given slide. * @param slideNr the number of the slide in relation to which the * displacement shall be calculated, if the slide * <code>slideNr</code> were focused and the focused * slide somewhere either to the left or to the * right of it. * @return the displacement */ private int displacement(int slideNr) { if (slideNr == cursor) return 0; int delta = 0; delta += thumbnails.imageSize(cursor)[0] / 2; delta += thumbnails.imageSize(slideNr)[0] / 2; if (slideNr > cursor) { for (int i = cursor + 1; i < slideNr; i++) { delta += thumbnails.imageSize(i)[0]; } return delta; } else { for (int i = slideNr + 1; i < cursor; i++) { delta += thumbnails.imageSize(i)[0]; } return -delta; } } /** * Redraws the film strip. if parameter <code>displacement</code> is * unequal zero, the row of images will be displaced by a certain number of * pixels. This is used to implement an animated change of the selected * image changes. * * @param displacement the number of pixels by which the thumbnails shall be * displaced. (0 means no displacement, a negative * number results in a displacement to the left a * positive number in a displacement to the right.) */ private void redraw(int displacement) { int c1, c2; int left, right; int center = width / 2 + displacement; int w = thumbnails.imageSize(cursor)[0]; move(cursor, center - w / 2); c1 = cursor - 1; c2 = cursor + 1; left = center - w / 2 - borderSize; right = center + w / 2 + borderSize; // while ((right < width && c2 < thumbnails.size()) || // (left >= 0 && c1 >= 0)) { while (c1 >= 0 || c2 < thumbnails.size()) { if (c1 >= 0) { left -= thumbnails.imageSize(c1)[0] + 2 * borderSize; move(c1, left + borderSize); c1--; } if (c2 < thumbnails.size()) { move(c2, right + borderSize); right += thumbnails.imageSize(c2)[0] + 2 * borderSize; c2++; } } } }