/*
@VaadinAddonLicenseForJavaFiles@
*/
package com.spaceapplications.vaadin.addon.eventtimeline.gwt.client;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* VEventTimelineBandArea.
*
* @author Thomas Neidhart / Space Applications Services NV/SA
* @author Florian Pirchner (add / remove bands)
*/
public class VEventTimelineBandArea extends VerticalPanel implements
MouseOverHandler, MouseOutHandler {
private final VEventTimelineWidget timelineWidget;
private HandlerRegistration mouseOverReg, mouseOutReg;
// Band captions
private final List<Integer> bandMinimumHeights = new ArrayList<Integer>();
private final List<VEventTimelineBand> allBands = new ArrayList<VEventTimelineBand>();
private final List<VEventTimelineBand> visibleBands = new ArrayList<VEventTimelineBand>();
private int pageSize = -1;
private int pageNumber;
private boolean bandSelectionEnabled;
public VEventTimelineBandArea(VEventTimelineWidget tw) {
timelineWidget = tw;
}
@Override
protected void onLoad() {
super.onLoad();
mouseOverReg = addDomHandler(this, MouseOverEvent.getType());
mouseOutReg = addDomHandler(this, MouseOutEvent.getType());
}
@Override
protected void onUnload() {
super.onUnload();
if (mouseOverReg != null) {
mouseOverReg.removeHandler();
mouseOverReg = null;
}
if (mouseOutReg != null) {
mouseOutReg.removeHandler();
mouseOutReg = null;
}
}
@Override
public void onMouseOut(MouseOutEvent event) {
for (Widget w : getChildren()) {
((VEventTimelineBand) w).disableAdjuster();
}
}
@Override
public void onMouseOver(MouseOverEvent event) {
for (Widget w : getChildren()) {
((VEventTimelineBand) w).enableAdjuster();
}
}
/**
* Is called to add an event band. Do not miss to navigate to the requested
* page. For performance issues this is not done automatically.
*
* @param id
* @param caption
*/
public void addBand(int id, String caption) {
VEventTimelineBand band = new VEventTimelineBand(id, caption, this);
allBands.add(band);
bandMinimumHeights.add(20);
}
/**
* Returns the page number that contains the given band. If the band is not
* part of the available bands then -1 is returned.
*
* @param band
* @return
*/
protected int getContainingPage(VEventTimelineBand band) {
int indexOf = allBands.indexOf(band);
if (indexOf < 0) {
return -1;
}
if (this.pageSize <= 0) {
return 0;
}
return indexOf / pageSize;
}
/**
* Requests the build of the visible page.
*
* @param firstBandInPageIndex
* The first band in the page. The value is the index of the band
* in allBands
* @param pageSize
* The number of event bands visible in the band area
*/
protected void requestBuildPage(int firstBandInPageIndex, int pageSize) {
// remove all bands
for (VEventTimelineBand band : visibleBands) {
remove(band);
band.setHeight("0px");
}
visibleBands.clear();
if (pageSize > 0) {
int height = calcHeight(pageSize);
for (int i = 0; i < pageSize; i++) {
int currentBandIndex = firstBandInPageIndex + i;
if (currentBandIndex > allBands.size() - 1) {
// end of allBands reached -> leave
break;
}
VEventTimelineBand band = allBands.get(currentBandIndex);
band.setHeight(height + "px");
band.setWidth(100 + "px");
// add the band
add(band);
visibleBands.add(band);
band.updateBandAdjuster();
}
} else {
int height = calcHeight(allBands.size());
for (VEventTimelineBand band : allBands) {
band.setHeight(height + "px");
band.setWidth(100 + "px");
// add the band
add(band);
visibleBands.add(band);
band.updateBandAdjuster();
}
}
}
/**
* Applies the bound to the event bands by calculating height and width.
*
* @param pageSize
* The number of visible event bands
*/
protected int calcHeight(int pageSize) {
int bandHeight = timelineWidget.getBandHeight();
// only adjust height if no specific height is configured
if (bandHeight == -1) {
int calcBase = pageSize <= 0 ? allBands.size() : pageSize;
if (calcBase > 0) {
bandHeight = (getParent().getOffsetHeight()
- timelineWidget.getBrowserHeight() - 16) / calcBase;
}
}
return bandHeight;
}
/**
* Is called to remove the event band with the given id.
*
* @param id
*/
public void removeBand(int id) {
int index = -1;
VEventTimelineBand band = null;
for (VEventTimelineBand temp : allBands) {
if (temp.getId() == id) {
index = allBands.indexOf(temp);
band = temp;
break;
}
}
if (index >= 0) {
bandMinimumHeights.remove(index);
}
// remove the band
if (visibleBands.contains(band)) {
int page = getContainingPage(band);
allBands.remove(band);
remove(band);
visibleBands.remove(band);
// test if current page would be empty since removed was first
// element of last page
if (page > 0) {
int firstBandInPage = calcFirstBandInPage(page);
if (firstBandInPage >= allBands.size()) {
page--;
}
}
// if the page is valid, navigate to it
if (page >= 0) {
setVisiblePage(page);
}
} else {
allBands.remove(band);
}
}
/**
* Returns the number of bands.
*
* @return
*/
public int getBandCount() {
return allBands.size();
}
/**
* Returns all available bands.
*
* @return the allBands
*/
public List<VEventTimelineBand> getAllBands() {
return Collections.unmodifiableList(allBands);
}
/**
* Returns all visible bands.
*
* @return the visibleBands
*/
public List<VEventTimelineBand> getVisibleBands() {
return Collections.unmodifiableList(visibleBands);
}
/**
* Returns the height of the band.
*
* @param bandId
* @return
*/
public int getBandHeight(int bandId) {
VEventTimelineBand result = getBandById(bandId);
return result != null ? result.getHeight() : 0;
}
/**
* Returns the band by its id or <code>null</code> if the band does not
* exist.
*
* @param bandId
* @return
*/
protected VEventTimelineBand getBandById(int bandId) {
VEventTimelineBand result = null;
for (VEventTimelineBand band : allBands) {
if (band.getId() == bandId) {
result = band;
break;
}
}
return result;
}
public boolean requestResize(int bandId, int oldHeight, int adjustment) {
int minimumHeight = bandMinimumHeights.get(bandId);
int newHeight = oldHeight + adjustment;
if (newHeight < minimumHeight) {
return false;
}
// maximum height of the parent widget
int maxHeight = getParent().getOffsetHeight() - timelineWidget.getBrowserHeight() - 16;
int totalHeight = 0;
for (int idx = 0; idx < getWidgetCount(); idx++) {
VEventTimelineBand w = (VEventTimelineBand) getWidget(idx);
if (idx != bandId) {
totalHeight += w.getHeight();
} else {
totalHeight += newHeight;
}
}
if (totalHeight > maxHeight) {
newHeight -= totalHeight - maxHeight;
if (newHeight < oldHeight) {
return false;
}
}
getWidget(bandId).setHeight(newHeight + "px");
for (Widget w : getChildren()) {
((VEventTimelineBand) w).updateBandAdjuster();
}
return true;
}
public void redraw() {
timelineWidget.redrawDisplay();
}
/**
* The maximum number of event bands shown in the band area.<br/>
* If a value lower equal 0 is set, an unlimited number of bands can be
* added.
*
* @param pageSize
*/
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
setVisiblePage(0);
}
/**
* Returns the maximum number of event bands shown in the band area. If a
* value lower equal 0 is returned, an unlimited number of bands can be
* added.
*
* @return the pageSize
*/
public int getPageSize() {
return pageSize;
}
/**
* Returns the number of event band pages available.
*
* @return
*/
public int getPageCount() {
if (allBands.size() == 0) {
return 0;
}
int pageSize = this.pageSize;
if (pageSize <= 0) {
pageSize = allBands.size();
}
BigDecimal bd = new BigDecimal((double) allBands.size() / pageSize);
return bd.setScale(0, RoundingMode.CEILING).intValue();
}
/**
* Returns the number of the current page. The first page is 0.
*
* @return the pageNumber
*/
public int getVisiblePage() {
return pageNumber;
}
/**
* Sets the number of the current visible page<br/>
* If the given number is > the maximum available page, the last page will
* be shown.<br/>
* The first page is 0.
*/
public void setVisiblePage(int pageNumber) {
if (pageNumber < 0 || getPageCount() == 0) {
this.pageNumber = 0;
} else {
if (pageNumber > getPageCount() - 1) {
this.pageNumber = getPageCount() - 1;
} else {
this.pageNumber = pageNumber;
}
}
navigateToPage(pageNumber);
}
/**
* Navigates to the page with the given number.
*
* @param pageNumber
*/
public void navigateToPage(int pageNumber) {
if (pageNumber < 0) {
return;
}
int firstBandInPage = calcFirstBandInPage(pageNumber);
requestBuildPage(firstBandInPage, pageSize);
}
/**
* Calculates the index of the first band in the page.
*
* @param pageNumber
* @return
*/
protected int calcFirstBandInPage(int pageNumber) {
int firstBandInPage = 0;
if (pageSize > 0) {
firstBandInPage = pageNumber * pageSize;
}
return firstBandInPage;
}
/**
* Navigates to the band with the given id.
*
* @param bandId
*/
public void navigateToBand(int bandId) {
VEventTimelineBand band = getBandById(bandId);
if (band != null) {
int containingPage = getContainingPage(band);
if (containingPage >= 0) {
setVisiblePage(containingPage);
}
}
}
/**
* Returns true, if the band is in the visible portion of the UI.
*
* @param bandId
* @return true if the band is visible
*/
public boolean isBandVisible(int bandId) {
VEventTimelineBand band = getBandById(bandId);
if (band != null) {
int containingPage = getContainingPage(band);
return containingPage == getVisiblePage();
}
return false;
}
/**
* Is called if the band with the given id was selected.
*
* @param bandId
*/
public void bandSelected(int bandId) {
// band selection not enabled
if (!bandSelectionEnabled) {
return;
}
// nothing to do if the band is already selected
if (isBandSelected(bandId)) {
return;
}
if (!isBandVisible(bandId)) {
navigateToBand(bandId);
}
for (VEventTimelineBand band : allBands) {
if (band.getId() == bandId) {
band.setSelected(true);
} else {
band.setSelected(false);
}
}
timelineWidget.fireBandSelected(bandId);
}
/**
* Is called to deselect all bands.
*/
public void deselectBands() {
for (VEventTimelineBand band : allBands) {
if (band.isSelected()) {
band.setSelected(false);
}
}
}
/**
* Returns true, if the band with the given id is selected.
*
* @param bandId
* @return
*/
private boolean isBandSelected(int bandId) {
VEventTimelineBand band = getBandById(bandId);
return band != null && band.isSelected();
}
/**
* Sets whether the band selection is enabled or not.
*
* @param bandSelectionEnabled
*/
public void setBandSelectionEnabled(boolean bandSelectionEnabled) {
this.bandSelectionEnabled = bandSelectionEnabled;
if (!bandSelectionEnabled) {
// deselect all bands
deselectBands();
}
}
/**
* Returns true, if the band selection is enabled. False otherwise.
*
* @return
*/
public boolean isBandSelectionEnabled() {
return bandSelectionEnabled;
}
}