/*
* 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.HashMap;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.PushButton;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* A control panel for the slide show.
*
* <p>The control panel consists of up to
* four buttons: back, gallery, play, next and an optional progress bar.
* In addition to that the control panel can "swallow" a film strip which
* will be placed in the middle between the gallery and the play button.
*
* @author eckhart
*
*/
public class ControlPanel extends Composite implements ClickHandler,
SlideshowListener, ResizeListener, AttachmentListener, SlideshowControl {
/** Constants (bit values) for the control panel buttons */
// public static final int BEGIN = 1, BACK = 2, HOME = 4, PLAY = 8, NEXT = 16, END = 32;
public static final int BACK = 1;
/** Constants (bit values) for the control panel buttons */
// public static final int BEGIN = 1, BACK = 2, HOME = 4, PLAY = 8, NEXT = 16, END = 32;
public static final int HOME = 2;
/** Constants (bit values) for the control panel buttons */
// public static final int BEGIN = 1, BACK = 2, HOME = 4, PLAY = 8, NEXT = 16, END = 32;
public static final int NEXT = 8;
/** Constants (bit values) for the control panel buttons */
// public static final int BEGIN = 1, BACK = 2, HOME = 4, PLAY = 8, NEXT = 16, END = 32;
public static final int PLAY = 4;
/** an array of the possible pixel sizes of the buttons */
protected static final int[] ICON_SIZES = { 16, 24, 32, 48, 64 };
/**
* the base names (without size appendices, e.g. "_32" for the 32 pixel size
* version of a button) of image files of the control buttons
*/
// protected static final String[] BASE_NAMES = { "begin", "begin_down",
// "back", "back_down", "gallery", "gallery_down", "play", "pause",
// "next", "next_down", "end", "end_down" };
protected static final String[] BASE_NAMES = { "back", "back_down",
"gallery", "gallery_down", "play", "pause", "next", "next_down" };
/** a map that contains for each possible pixel size of the buttons another
* map which maps the base name of the button to its image object
*/
static HashMap<Integer, HashMap<String, Image>> iconSets;
static {
iconSets = new HashMap<Integer, HashMap<String, Image>>();
for (int i: ICON_SIZES)
iconSets.put(i, ControlPanel.loadImageSet(i));
}
/**
* Loads a set of icons for the control buttons corresponding to the given
* <code>size</code>. The icons must be stored in the "icons" sub-directory
* and be named appropriately, e.g. "next_down_24.png" is the icon with an
* edge size of 24 pixels for the "next" button when it is pressed.
* @param size the size of the icons.
* @return a map where the respective images are indexed by their
* name (without suffix or size indicator, e.g. a proper
* index would be "next_down" but not "next_down_24.png"
*/
protected static HashMap<String, Image> loadImageSet(int size) {
HashMap<String, Image> is = new HashMap<String, Image>();
String suffix = "_" + String.valueOf(size) + ".png";
for (String bn: BASE_NAMES) {
String name = "icons/" + bn + suffix;
Image img = new Image(name);
// img.addStyleName("icon");
is.put(bn, img);
}
return is;
}
private ClickHandler homeButtonHandler;
private HandlerRegistration homeButtonRegistration;
private HashMap<String, Image> icons;
private Filmstrip filmstrip;
private ProgressBar progress;
private PushButton back, home, next; // begin and end are not needed any more
private ToggleButton play;
private SimplePanel wrapper;
private Slideshow slideshow;
private Tooltip backTooltip, homeTooltip,
playPauseTooltip, nextTooltip;
private int buttonSize;
// private boolean hasProgressBar = true;
private boolean autoResize = true;
private int buttonsEnabled = NEXT|HOME|PLAY|BACK;
private int buttonsShown = NEXT|HOME|PLAY|BACK;
/**
* The constructor of class <code>ControlPanel</code>. The control panel
* is initialized with a slide show. By default it features a progress bar
* and all buttons are visible and enabled.
*
* @param slideshow the slide show to which this control panel shall be
* attached
*/
public ControlPanel(Slideshow slideshow) {
this.slideshow = slideshow;
this.slideshow.addSlideshowListener(this);
this.slideshow.getImagePanel().addAttachmentListener(this);
wrapper = new SimplePanel();
wrapper.setStyleName("controlPanel");
buttonSize = ICON_SIZES[0];
icons = iconSets.get(buttonSize);
// beginTooltip = new Tooltip(I18N.i18n.begin());
// endTooltip = new Tooltip(I18N.i18n.end());
backTooltip = new Tooltip(I18N.i18n.back());
nextTooltip = new Tooltip(I18N.i18n.next());
homeTooltip = new Tooltip(I18N.i18n.home());
playPauseTooltip = new Tooltip(I18N.i18n.playPause());
composePanel();
initWidget(wrapper);
}
// /**
// * Allows automatic adjustment of the size of the control panel if the size
// * of the image panel changes.
// */
// public void allowAutoResize() {
// autoResize = true;
// }
//
// /**
// * Disallows automatic adjustment of the size of the control panel if the size
// * of the image panel changes.
// */
// public void disallowAutoResize() {
// autoResize = false;
// }
/**
* Returns the size of the control buttons as integer value. The size is
* automatically adjusted by the <code>onResized</code> method. It is
* guaranteed to have a value greater or equal than 8 and smaller or equal
* than 128, but usually it is one of the values: 16,24,32,40,48,64.
* @return the edge size of the control buttons in pixel.
*/
public int getCrtlBtnSize() {
return buttonSize;
}
/**
* Returns true if the control panel has swallowed (i.e. contains) a
* film strip.
* @return true, if control panel contains a film strip
*/
public boolean hasFilmStrip() {
return filmstrip != null;
}
// /**
// * Returns the "swallowed" film strip or <code>null</code> if the control
// * panel does not contain a film strip.
// * @return reference to the film strip of the panel or <code>null</code>
// */
// public Filmstrip getFilmstrip() {
// return filmstrip;
// }
// /**
// * Hides the progress bar.
// */
// public void hideProgressBar() {
// if (hasProgressBar) {
// hasProgressBar = false;
// composePanel();
// }
// }
/* (non-Javadoc)
* @see com.google.gwt.user.client.ui.ClickListener.onClick()
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onClick(com.google.gwt.event.dom.client.ClickEvent)
*/
@Override
public void onClick(ClickEvent event) {
Object sender = event.getSource();
if (sender == back) {
slideshow.stop();
slideshow.back();
} else if (sender == next) {
slideshow.stop();
slideshow.next();
// } else if (sender == begin) {
// slideshow.stop();
// slideshow.show(0);
// } else if (sender == end) {
// slideshow.stop();
// slideshow.show(slideshow.size()-1);
} else if (sender == play) {
if (play.isDown()) {
slideshow.next();
slideshow.start();
} else {
slideshow.stop();
}
} // else { // sender == gallery
// }
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowListener.onFade()
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onFade()
*/
@Override
public void onFade() {
progress.setValue(slideshow.getCurrentSlide()+1);
if (filmstrip != null) filmstrip.focusImage(slideshow.getCurrentSlide());
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.AttachmentListener#onLoad(com.google.gwt.user.client.ui.Widget)
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onLoad(com.google.gwt.user.client.ui.Widget)
*/
@Override
public void onLoad(Widget sender) {
// beginTooltip.setMaxAppearances(2);
// endTooltip.setMaxAppearances(2);
backTooltip.setMaxAppearances(2);
nextTooltip.setMaxAppearances(2);
homeTooltip.setMaxAppearances(4);
playPauseTooltip.setMaxAppearances(3);
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.ResizeListener.onResized()
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onResized()
*/
@Override
public void onResized() {
if (autoResize) {
// final int[][] resolutions = {{640,480}, {800, 600}, {1024,768},
// {1280, 1024}, {1680, 1050}};
final int[][] resolutions = {{320,240}, {640, 480}, {1024, 600},
{1440, 1050}, {1920, 1200}};
final int[] btnSizes = {16, 24, 32, 40, 48, 64, 64};
int w = slideshow.getImagePanel().getOffsetWidth();
int h = Toolbox.getOffsetHeight(slideshow.getImagePanel());
// GWT.log("ControlPanel.onResized: panel dimensions: "+String.valueOf(w)+"x"+String.valueOf(h));
int i;
for (i = 0; i < resolutions.length; i++) {
int res[] = resolutions[i];
if (w < res[0] || h < res[1]) break;
}
if (filmstrip != null) i++; // don't remove this line or switching from normal ctrl panel to filmstrip might get problems!
// GWT.log("ControlPanel.onResized: ButtonSize: "+String.valueOf(btnSizes[i]));
setCtrlBtnSizePx(btnSizes[i]);
}
if (filmstrip != null) filmstrip.onResized();
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowListener.onShow()
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onShow(int)
*/
@Override
public void onShow(int slideNr) {
progress.setValue(slideNr+1);
if (filmstrip != null) {
filmstrip.focusImage(slideNr);
}
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowListener.onStart()
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onStart()
*/
@Override
public void onStart() {
if (!play.isDown()) {
play.setDown(true);
}
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowListener.onStop()
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onStop()
*/
@Override
public void onStop() {
if (play.isDown()) {
play.setDown(false);
}
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.AttachmentListener#onUnload(com.google.gwt.user.client.ui.Widget)
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#onUnload(com.google.gwt.user.client.ui.Widget)
*/
@Override
public void onUnload(Widget sender) {
// beginTooltip.hide();
// endTooltip.hide();
backTooltip.hide();
nextTooltip.hide();
homeTooltip.hide();
playPauseTooltip.hide();
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.ResizeListener.prepareResized()
*/
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#prepareResized()
*/
@Override
public void prepareResized() {
if (filmstrip != null) filmstrip.prepareResized();
}
// /**
// * Determines which buttons are enabled on the panel. If the play button is
// * disabled, the slide show will be stopped in case it was running.
// *
// * @param buttonFlags the bit set of the buttons that are to be enabled
// */
// public void setButtonEnabled(int buttonFlags) {
// buttonsEnabled = buttonFlags;
// if ((buttonsEnabled & PLAY) == 0) slideshow.stop();
// enableOrDisableButtons();
// }
// /**
// * Determines which buttons are shown on the panel. If the play button is
// * hidden the slide show will be stopped in case it was running.
// *
// * @param buttonFlags the bit set of the buttons that are to be shown
// */
// public void setButtonShow(int buttonFlags) {
// boolean redoPanel;
// if (buttonsShown != buttonFlags) {
// redoPanel = true;
// } else {
// redoPanel = false;
// }
// buttonsShown = buttonFlags;
// if ((buttonsShown & PLAY) == 0) slideshow.stop();
// if (redoPanel) composePanel();
// }
/**
* Sets the size of the control buttons. If the new size is widely different
* form the old one, this involves picking a fresh set of icons for the
* control buttons.
*
* @param size the edge size of each control button in pixel.
*/
public void setCtrlBtnSizePx(int size) {
assert size >= 16 && size <= 128;
if (buttonSize == size) return;
buttonSize = size;
HashMap<String, Image> match = iconSets.get(ICON_SIZES[ICON_SIZES.length-1]);
for (int i: ICON_SIZES) {
if (size <= i) {
match = iconSets.get(i);
break;
}
}
// need to set sizes for images that are still loading
// using im.getHeight() to query sizes is a really bad idea before image
// has been fully loaded!
for (Image im: match.values()) im.setPixelSize(size/2, size); // size/2 only works with the grey icons set! for the colored icon set use size * 3 / 4
if (match != icons || filmstrip != null) {
icons = match;
// wrapper.remove(panel);
composePanel();
}
}
/* (non-Javadoc)
* @see de.eckhartarnold.client.SlideshowControl#setHomeButtonListener(com.google.gwt.event.dom.client.ClickHandler)
*/
@Override
public void setHomeButtonListener(ClickHandler handler) {
if (home != null) {
if (homeButtonRegistration != null) {
homeButtonRegistration.removeHandler();
}
homeButtonRegistration = home.addClickHandler(handler);
}
homeButtonHandler = handler;
}
// /**
// * Shows the progress bar.
// */
// public void showProgressBar() {
// if (!hasProgressBar) {
// hasProgressBar = true;
// composePanel();
// }
// }
/**
* Swallows a {@link Filmstrip} widget. The film strip will then become part
* of the control panel. The control panel will automatically be resized so
* that it covers the full width of the browser window (or the parent widget).
* @param filmstrip the film strip widget
* @param selectableImages true, if the images of the film strip shall be
* made selectable so that by clicking on one them
* the slide show jumps to that image
*/
public void swallowFilmstrip(Filmstrip filmstrip, boolean selectableImages) {
if (filmstrip != null && selectableImages) {
filmstrip.setPickImageCallback(new Filmstrip.IPickImage() {
public void onPickImage(int imageNr) {
slideshow.stop();
slideshow.show(imageNr);
}
});
}
if (this.filmstrip != filmstrip) {
this.filmstrip = filmstrip;
composePanel();
}
}
/**
* (Re-)creates the button panel using the images stored in the
* <code>icons</code> map as symbols on the buttons.
*/
protected void composePanel() {
VerticalPanel vpanel = new VerticalPanel();
HorizontalPanel buttonPanel = new HorizontalPanel();
buttonPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
// progress bar
progress = new ProgressBar(slideshow.size());
int btnSize;
if (filmstrip != null) btnSize = buttonSize / 2;
else btnSize = buttonSize;
if (btnSize >= 24) progress.setLabelingType(ProgressBar.VALUE_LABEL);
else progress.setLabelingType(ProgressBar.NO_LABEL);
if (btnSize <= 32) progress.getFrame().addStyleDependentName("thin");
if(btnSize > 48) progress.getBar().addStyleDependentName("16px");
else if (btnSize > 32) progress.getBar().addStyleDependentName("12px");
else if (btnSize >= 28) progress.getBar().addStyleDependentName("10px");
else if (btnSize >= 24) progress.getBar().addStyleDependentName("9px");
else if (btnSize >= 20) progress.getBar().addStyleDependentName("4px");
else progress.getBar().addStyleDependentName("3px");
int value = slideshow.getCurrentSlide();
if (value >= 0) progress.setValue(value+1);
// button panel
// begin = new PushButton(icons.get("begin"), icons.get("begin_down"), this);
// Tooltip.addToWidget(beginTooltip, begin);
back = new PushButton(icons.get("back"), icons.get("back_down"), this);
Tooltip.addToWidget(backTooltip, back);
if (homeButtonHandler != null) {
home = new PushButton(icons.get("gallery"), icons.get("gallery_down"),
homeButtonHandler);
} else {
home = new PushButton(icons.get("gallery"),
icons.get("gallery_down"));
}
Tooltip.addToWidget(homeTooltip, home);
play = new ToggleButton(icons.get("play"), icons.get("pause"), this);
Tooltip.addToWidget(playPauseTooltip, play);
if (slideshow.isRunning()) play.setDown(true);
next = new PushButton(icons.get("next"), icons.get("next_down"), this);
Tooltip.addToWidget(nextTooltip, next);
// end = new PushButton(icons.get("end"), icons.get("end_down"), this);
// Tooltip.addToWidget(endTooltip, end);
// if ((buttonsShown & BEGIN) != 0) buttonPanel.add(begin);
if ((buttonsShown & BACK) != 0) buttonPanel.add(back);
if ((buttonsShown & HOME) != 0) buttonPanel.add(home);
if (filmstrip != null) {
filmstrip.setHeight(buttonSize*2+"px");
vpanel.add(filmstrip);
vpanel.add(progress);
vpanel.setWidth("100%");
buttonPanel.add(vpanel);
buttonPanel.setCellWidth(vpanel, "100%");
buttonPanel.addStyleName("controlFilmstripBackground");
addButtonStyles("controlFilmstripButton");
} else {
buttonPanel.addStyleName("controlPanelBackground");
addButtonStyles("controlPanelButton");
}
if ((buttonsShown & PLAY) != 0) buttonPanel.add(play);
if ((buttonsShown & NEXT) != 0) buttonPanel.add(next);
// if ((buttonsShown & END) != 0) buttonPanel.add(end);
enableOrDisableButtons();
if (filmstrip != null) {
wrapper.setWidget(buttonPanel);
} else {
vpanel.add(buttonPanel);
vpanel.add(progress);
wrapper.setWidget(vpanel);
}
}
/**
* Transfers the state of the <code>buttonsEnabled</code> flags which
* can be set with method <code>setButtonEnabled</code> to the button
* widgets.
*/
protected void enableOrDisableButtons() {
// begin.setEnabled((buttonsEnabled & BEGIN) != 0);
back.setEnabled((buttonsEnabled & BACK) != 0);
home.setEnabled((buttonsEnabled & HOME) != 0);
play.setEnabled((buttonsEnabled & PLAY) != 0);
next.setEnabled((buttonsEnabled & NEXT) != 0);
// end.setEnabled((buttonsEnabled & END) != 0);
}
/**
* Sets the stylenames for all buttons and the panel with the
* addStyleName method.
* @param buttonStyle css style for the buttons
*/
private void addButtonStyles(String buttonStyle) {
// begin.addStyleName(buttonStyle);
back.addStyleName(buttonStyle);
home.addStyleName(buttonStyle);
play.addStyleName(buttonStyle);
next.addStyleName(buttonStyle);
// end.addStyleName(buttonStyle);
}
}