net.cbtltd.client.widget.calendar.CalendarWidget.java Source code

Java tutorial

Introduction

Here is the source code for net.cbtltd.client.widget.calendar.CalendarWidget.java

Source

/**
 * @author   bookingnet
 * @
 * @version   4.0.0
 */
package net.cbtltd.client.widget.calendar;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;

import net.cbtltd.client.Component;
import net.cbtltd.client.Components;
import net.cbtltd.client.GuardedRequest;
import net.cbtltd.client.HasComponents;
import net.cbtltd.client.field.AbstractField;
import net.cbtltd.client.field.ListField;
import net.cbtltd.client.resource.Hosts;
import net.cbtltd.client.widget.NameIdWidgetItems;
import net.cbtltd.client.widget.RazorWidget;
import net.cbtltd.shared.Error;
import net.cbtltd.shared.JSONRequest;
import net.cbtltd.shared.License;
import net.cbtltd.shared.NameId;
import net.cbtltd.shared.NameIdAction;
import net.cbtltd.shared.Reservation;
import net.cbtltd.shared.Service;
import net.cbtltd.shared.Table;
import net.cbtltd.shared.Time;
import net.cbtltd.shared.reservation.AvailableItem;
import net.cbtltd.shared.reservation.AvailableWidget;

import com.allen_sauer.gwt.log.client.Log;
import com.bookingnet.config.RazorConfig;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.HasChangeHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.jsonp.client.JsonpRequestBuilder;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/** The Class CalendarWidget is to display availability in a calendar format via a widget that can be hosted by a web page. */
public class CalendarWidget extends VerticalPanel implements ChangeHandler, HasComponents {

    private static final CalendarConstants CONSTANTS = GWT.create(CalendarConstants.class);
    private static final Hosts HOSTS = GWT.create(Hosts.class);
    private static final DateTimeFormat EF = DateTimeFormat.getFormat("EEE");
    private static final DateTimeFormat MF = DateTimeFormat.getFormat("MMMM yyyy");
    private static final DateTimeFormat DNF = DateTimeFormat.getFormat("d");
    private static final Image loader = new Image(AbstractField.BUNDLE.loader());
    private final Navigator navigator = new Navigator();
    private static ListField productField;
    private Grid daysGrid;
    private Date displayedMonth;
    private boolean rpc;
    private String pos;
    private static final Components COMPONENTS = new Components();

    /* (non-Javadoc)
     * @see net.cbtltd.client.HasComponents#addComponent(net.cbtltd.client.Component)
     */
    public void addComponent(Component component) {
        COMPONENTS.add(component);
    }

    /* (non-Javadoc)
     * @see net.cbtltd.client.HasComponents#onRefresh()
     */
    public void onRefresh() {
        COMPONENTS.onRefresh();
    }

    /* (non-Javadoc)
     * @see net.cbtltd.client.HasComponents#hasChanged()
     */
    public boolean hasChanged() {
        return COMPONENTS.hasChanged();
    }

    /* (non-Javadoc)
     * @see net.cbtltd.client.HasComponents#onReset(java.lang.String)
     */
    public void onReset(String state) {
        COMPONENTS.onReset();
    }

    /* (non-Javadoc)
     * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent)
     */
    public void onClick(ClickEvent click) {
    }

    /* A timer to schedule periodic refreshes og the widget. */
    private final Timer refreshTimer = new Timer() {
        public void run() {
            getCalendar();
        }
    };

    /**
     * Instantiates a new calendar widget.
     * If GWT RPC is used the widget must be in an iFrame in the host HTML page to prevent cross-site problems.
     * If JSONP is used the widget JavaScript must be served from the same site as the host HTML page.
     *
     * @param rpc is true if GWT RPC is to be used, else JSONP is used.
     * @param pos the point of sale code of the organization hosting the widget.
     * @param productid the ID of the product to be displayed.
     */
    public CalendarWidget(boolean rpc, String pos, String productid) {
        try {
            if (productid == null || productid.isEmpty()) {
                throw new RuntimeException(Error.product_id.getMessage());
            }
            this.rpc = rpc;
            this.pos = pos;
            this.setStylePrimaryName("CalendarWidget");
            int tab = 0;

            //-----------------------------------------------
            // Product field
            //-----------------------------------------------
            productField = new ListField(this, null, new NameIdAction(Service.PRODUCT), CONSTANTS.productLabel(),
                    false, tab++);
            productField.setFieldStyle("Field");
            productField.setLabelStyle("Label");
            productField.setAllOrganizations(true);
            productField.setIds(NameId.getCdlist(productid));
            productField.setVisible(productid.split(",").length > 1); // multiple properties
            productField.setHelpText(CONSTANTS.productHelp());
            //this.add(productField);

            loader.setVisible(false);
            HorizontalPanel bar = new HorizontalPanel();
            bar.add(productField);
            bar.add(loader);
            this.add(bar);

            //-----------------------------------------------
            // Calendar grid
            //-----------------------------------------------
            daysGrid = new Grid(7, 7);
            drawWeekLine(this);
            drawDayGrid(this);

            this.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
            navigator.addChangeHandler(this);
            navigator.setAbsolute(true);
            this.add(navigator);

            this.add(RazorWidget.getHome());

            getProduct(productid);
            setDisplayedMonth(RazorWidget.getDate());
            refreshTimer.cancel();
            refreshTimer.schedule(RazorWidget.delay);
        } catch (Throwable x) {
            x.printStackTrace();
            throw new RuntimeException(Error.widget_calendar.getMessage() + " " + x.getMessage());
        }
    }

    /* The action to refresh the widget for the selected product and target month. */
    private void getCalendar() {
        refreshTimer.cancel();
        refreshTimer.schedule(30000);

        if (rpc) {
            calendarWidget.execute();
        } else {
            getJsonpCalendar();
        }
    }

    /* The RPC action to refresh the widget for the selected product and target month. */
    private GuardedRequest<Table<AvailableItem>> calendarWidget = new GuardedRequest<Table<AvailableItem>>() {
        protected boolean error() {
            return productField.noValue() || displayedMonth == null;
        }

        protected void send() {
            super.send(new AvailableWidget(productField.getValue(), getMonthFirstDay(),
                    Time.addDuration(getMonthFirstDay(), 60, Time.DAY)));
        }

        protected void receive(Table<AvailableItem> response) {
            renderAvailableItems(response);
        }
    };

    /*
     * Removes the state styles from the calendar.
     *
     * @param formatter the state style formatter.
     * @param i the row.
     * @param j the column.
     */
    private void removeStyles(CellFormatter formatter, int i, int j) {
        formatter.removeStyleName(i, j, Reservation.State.Arrived.name());
        formatter.removeStyleName(i, j, Reservation.State.Briefed.name());
        formatter.removeStyleName(i, j, Reservation.State.Closed.name());
        formatter.removeStyleName(i, j, Reservation.State.Confirmed.name());
        formatter.removeStyleName(i, j, Reservation.State.Departed.name());
        formatter.removeStyleName(i, j, Reservation.State.FullyPaid.name());
        formatter.removeStyleName(i, j, Reservation.State.PreDeparture.name());
        formatter.removeStyleName(i, j, Reservation.State.Provisional.name());
        formatter.removeStyleName(i, j, Reservation.State.Reserved.name());
    }

    /*
     * Removes the styles from the calendar.
     *
     * @param formatter the state style formatter.
     */
    private void removeStyles(CellFormatter formatter) {
        for (int i = 0; i < 7; i++) {
            for (int j = 0; j < 7; j++) {
                removeStyles(formatter, i, j);
            }
        }
    }

    /*
     * Render the colour of the reserved cells from a table of available item objects.
     * 
     * @param response the table of available items.
     */
    /**
     * Render available items.
     *
     * @param response the response
     */
    private void renderAvailableItems(Table<AvailableItem> response) {
        Log.debug("renderAvailableItems " + response);
        CellFormatter formatter = daysGrid.getCellFormatter();
        removeStyles(formatter);
        ArrayList<AvailableItem> items = response == null ? null : response.getValue();
        if (items == null || items.isEmpty()) {
            return;
        }
        Collections.sort(items);
        Date cursor = getDaysGridOrigin(displayedMonth);
        int i = 0;
        int j = 0;
        for (AvailableItem item : items) {
            Date start = Time.getDateClient(item.getFromdate());
            Date end = Time.getDateClient(item.getTodate());
            if (start.after(Time.addDuration(cursor, 48, Time.DAY))) {
                return;
            }
            if (!end.before(cursor)) {
                while (cursor.before(start)) {
                    cursor = Time.addDuration(cursor, 1, Time.DAY);
                    j++;
                    if (j >= 7) {
                        i++;
                        j = 0;
                    }
                    if (i > 6) {
                        return;
                    }
                }
                while (cursor.before(end)) {
                    formatter.addStyleName(i, j, item.getState());
                    cursor = Time.addDuration(cursor, 1, Time.DAY);
                    j++;
                    if (j >= 7) {
                        i++;
                        j = 0;
                    }
                    if (i > 6) {
                        return;
                    }
                }
            }
        }
    }

    /* The JSONP action to refresh the widget for the selected product and target month. */
    private void getJsonpCalendar() {

        if (productField.noValue() || displayedMonth == null) {
            return;
        }

        JsonpRequestBuilder jsonp = new JsonpRequestBuilder();

        String url = HOSTS.jsonUrl() + "?service=" + JSONRequest.CALENDAR + "&pos=" + pos + "&productid="
                + productField.getValue() + "&date=" + RazorWidget.DF.format(getDaysGridOrigin(displayedMonth));

        loader.setVisible(true);
        jsonp.requestObject(url, new AsyncCallback<CalendarWidgetItems>() {

            @Override
            public void onFailure(Throwable x) {
                loader.setVisible(false);
                throw new RuntimeException(Error.widget_calendar.getMessage() + " " + x.getMessage());
            }

            @Override
            public void onSuccess(CalendarWidgetItems response) {
                loader.setVisible(false);
                renderCalendarWidgetItems(response);
            }
        });
    }

    /*
     * Render the colour of the reserved cells from JSON calendar widget items.
     * 
     * @param response the table of available items.
     */
    private void renderCalendarWidgetItems(CalendarWidgetItems response) {
        CellFormatter formatter = daysGrid.getCellFormatter();
        removeStyles(formatter);
        if (response.getItems() == null || response.getItems().length() == 0) {
            return;
        }
        int row = 0;
        int col = 0;
        Date cursor = getDaysGridOrigin(displayedMonth);
        for (int index = 0; index < response.getItems().length(); index++) {
            CalendarWidgetItem item = response.getItems().get(index);
            Date date = RazorWidget.DF.parse(item.getDate());
            while (cursor.before(date)) {
                //removeStyles(formatter, row, col);
                cursor = Time.addDuration(cursor, 1, Time.DAY);
                col++;
                if (col >= 7) {
                    row++;
                    col = 0;
                }
                if (row > 6) {
                    return;
                }
            }
            formatter.addStyleName(row, col,
                    item.getState() == null ? Reservation.State.Confirmed.name() : item.getState());
            cursor = Time.addDuration(cursor, 1, Time.DAY);
            col++;
            if (col >= 7) {
                row++;
                col = 0;
            }
            if (row > 6) {
                return;
            }
        }
    }

    /* 
     * The request callback to get product name ID pairs.
     * 
     *  @param productid the ID of the selected product.
     */
    private void getProduct(String productid) {
        if (rpc) {
            productField.onRefresh();
        } else {
            getJsonpProductNameIds(productid);
        }
    }

    /* 
     * The JSONP request callback to get product name ID pairs.
     * 
     *  @param productid the ID of the selected product.
     */
    private void getJsonpProductNameIds(String productid) {

        JsonpRequestBuilder jsonp = new JsonpRequestBuilder();
        String url = HOSTS.jsonUrl() + "?service=" + JSONRequest.NAMEID + "&pos=" + pos + "&model="
                + NameId.Type.Product.name() + "&id=" + productid;

        loader.setVisible(true);
        jsonp.requestObject(url, new AsyncCallback<NameIdWidgetItems>() {

            @Override
            public void onFailure(Throwable x) {
                loader.setVisible(false);
                throw new RuntimeException(Error.nameid_json.getMessage() + " " + x.getMessage());
            }

            @Override
            public void onSuccess(NameIdWidgetItems response) {
                loader.setVisible(false);
                if (response != null && response.getItems() != null && response.getItems().length() > 0) {
                    ArrayList<NameId> items = new ArrayList<NameId>();
                    for (int index = 0; index < response.getItems().length(); index++) {
                        items.add(new NameId(response.getItems().get(index).getName(),
                                response.getItems().get(index).getId()));
                    }
                    productField.setItems(items);
                }
            }
        });
    }

    /* Sets a date in the month to be displayed. */
    private void setDisplayedMonth(Date displayedMonth) {
        this.displayedMonth = displayedMonth;
        displayMonth();
    }

    /**
     * The navigator change handler.
     *
     * @param change the change
     */
    public void onChange(ChangeEvent change) {
        if (productField.sent(change)) {
            getCalendar();
        } else if (navigator.sent(change)) {
            int index = navigator.getIndex();
            if (index == 0) {
                changeMonth(-12);
            } else if (index == 1) {
                changeMonth(-1);
            } else if (index == 2) {
                changeMonth(1);
            } else if (index == 3) {
                changeMonth(12);
            } else {
                Window.alert("No Month");
            }
        }
    }

    /*
     * Changes the displayed month.
     * 
     * @param months to add to the displayed month
     */
    private void changeMonth(int months) {
        displayedMonth = addMonths(this.displayedMonth, months);
        displayMonth();
    }

    /* Displays the current month. */
    private void displayMonth() {
        if (this.displayedMonth == null) {
            this.displayedMonth = new Date();
        }
        navigator.setLabel(MF.format(this.displayedMonth));
        this.drawDaysGridContent(this.displayedMonth);
        getCalendar();
    }

    /*
     * Draws the week line.
     * 
     * @param panel on which to draw.
     */
    private void drawWeekLine(Panel panel) {
        Grid weekLine = new Grid(1, 7);
        weekLine.setStyleName("WeekLine");
        Date weekFirstday = getWeekFirstDay();
        for (int i = 0; i < 7; i++) {
            weekLine.setText(0, i, EF.format(addDays(weekFirstday, i)));
        }
        panel.add(weekLine);
    }

    /*
     * Draws the day grid.
     * 
     * @param panel on which to draw.
     */
    private void drawDayGrid(Panel panel) {
        // for clickable cells
        //      this.daysGrid.addTableListener(new TableListener() {
        //         public void onCellClicked(SourcesTableEvents sender, int row, int cell) {
        //            Date date = addDays(getDaysGridOrigin(displayedMonth), row * 7 + cell);
        //            cellClicked(date, row, cell);
        //         };
        //      });
        daysGrid.setStyleName("DayGrid");
        panel.add(daysGrid);
    }

    /*
     * Draws the days into the days grid. Days drawn are the days of the displayed month
     * and few days after and before the displayed month.
     * 
     * @param displayedMonth the date in the displayed month.
     */
    private void drawDaysGridContent(Date displayedMonth) {
        CellFormatter formatter = daysGrid.getCellFormatter();
        Date cursor = this.getDaysGridOrigin(displayedMonth);
        for (int i = 0; i < 7; i++) {
            for (int j = 0; j < 7; j++) {
                daysGrid.setText(i, j, DNF.format(cursor));
                removeStyleNames(i, j, formatter);
                if (areEquals(new Date(), cursor)) {
                    if (displayedMonth.getMonth() == cursor.getMonth()) {
                        formatter.addStyleName(i, j, "CurrentMonthSelected");
                    } else {
                        formatter.addStyleName(i, j, "DaySelected");
                    }
                } else if (cursor.before(new Date())) {
                    formatter.addStyleName(i, j, "DayPast");
                }
                //            else if (isInWeekEnd(cursor)) {
                //               if (displayedMonth.getMonth() == cursor.getMonth())   {formatter.addStyleName(i, j, "availableWidgetCurrentMonthWeekEnd());}
                //               else {formatter.addStyleName(i, j, "weekEnd());}
                //            }
                else if (displayedMonth.getMonth() == cursor.getMonth()) {
                    formatter.addStyleName(i, j, "CurrentMonthOtherDay");
                } else {
                    formatter.addStyleName(i, j, "DayOther");
                }
                cursor = addDays(cursor, 1);
            }
        }
    }

    /*
     * Removes the cell style names.
     * 
     * @param row the row of the cell to change.
     * @param col the column of the cell to change.
     * @formatter row the cell formatter to use.
     */
    private void removeStyleNames(int row, int col, CellFormatter formatter) {
        formatter.removeStyleName(row, col, "DayPast");
        formatter.removeStyleName(row, col, "DaySelected");
        formatter.removeStyleName(row, col, "CurrentMonthSelected");
        formatter.removeStyleName(row, col, "DayOther");
        formatter.removeStyleName(row, col, "CurrentMonthOtherDay");
        formatter.removeStyleName(row, col, "DayNotAvailable");
    }

    /*
     * Returns the first day to display. If the month first day is after the 5th
     * day of the week, it return the first day of the week. Else, it returns
     * the first day of the week before.
     * 
     * @param displayedMonth the currently displayed month.
     * 
     * @return the first day to display in the grid.
     */
    private Date getDaysGridOrigin(Date displayedMonth) {
        int currentYear = displayedMonth.getYear();
        int currentMonth = displayedMonth.getMonth();
        CellFormatter cfJours = daysGrid.getCellFormatter();
        Date monthFirstDay = new Date(currentYear, currentMonth, 1);
        int indice = getWeekDayIndex(monthFirstDay);
        Date origineTableau;
        if (indice > 4) {
            origineTableau = getWeekFirstDay(monthFirstDay);
        } else {
            origineTableau = getWeekFirstDay(addDays(monthFirstDay, -7));
        }
        return origineTableau;
    }

    /*
     * Adds the specified number of days to the date.
     * 
     * @param date the date to add to.
     * @param days the specified number of days.
     * @return the new date.
     */
    private static Date addDays(Date date, int days) {
        return new Date(date.getYear(), date.getMonth(), date.getDate() + days);
    }

    /*
     * Adds the specified number of months to the date.
     * 
     * @param date the date to add to.
     * @param months the specified number of months.
     * @return the new date.
     */
    private static Date addMonths(Date date, int months) {
        return new Date(date.getYear(), date.getMonth() + months, date.getDate());
    }

    /*
     * Checks if two dates are the same.
     * 
     * @param one the first date.
     * @param another the other date.
     * @return true if the dates are the same.
     */
    private static boolean areEquals(Date one, Date another) {
        return one.getDate() == another.getDate() && one.getMonth() == another.getMonth()
                && one.getYear() == another.getYear();
    }

    /*
     * Returns the first date of the currently displayed month.
     * 
     * @return the first day of the month.
     */
    private Date getMonthFirstDay() {
        return getMonthFirstDay(displayedMonth);
    }

    /*
     * Returns the first date of a month containing the specified date.
     * 
     * @param date the specified date.
     * @return the first day of the month.
     */
    private static Date getMonthFirstDay(Date date) {
        Date current = date;
        while (current.getDate() != 1) {
            current = new Date(current.getYear(), current.getMonth(), current.getDate() - 1);
        }
        return current;
    }

    /*
     * Returns the index of the specified day in the week.
     * Example : sunday = 0, monday = 1 .... which depends on the locale.
     * 
     * @param day the specified day in the week.
     * @return the index of the day
     */
    private static int getWeekDayIndex(Date day) {
        //DateLocale locale = (DateLocale) GWT.create(DateLocale.class);
        //int[] daysOrder = locale.getDAY_ORDER();
        int[] daysOrder = { 1, 2, 3, 4, 5, 6, 0 };
        int dayIndex = day.getDay();
        for (int i = 0; i < 7; i++) {
            if (dayIndex == daysOrder[i]) {
                return i;
            }
        }
        return -1;
    }

    /*
     * Returns the first day of the current week.
     * 
     * @return date of the first day
     */
    private static Date getWeekFirstDay() {
        return getWeekFirstDay(new Date());
    }

    /*
     * Returns the first day of the week containing the specified date.
     * 
     * @param date the specified date.
     * @return date of the first day of the week.
     */
    private static Date getWeekFirstDay(Date date) {
        Date current = date;
        //DateLocale local = (DateLocale) GWT.create(DateLocale.class);
        int[] daysOrder = { 1, 2, 3, 4, 5, 6, 0 };
        //int firstDay = local.getDAY_ORDER()[0];
        int firstDay = daysOrder[0];
        while (current.getDay() != firstDay) {
            current = new Date(current.getYear(), current.getMonth(), current.getDate() - 1);
        }
        return current;
    }

    /* The Inner Class Navigator provides the buttons and label to navigate forward and back in time. */
    private class Navigator extends Composite implements ClickHandler, HasChangeHandlers {

        private final FlowPanel panel = new FlowPanel();
        private final Label first = new Label("<<");
        private final Label last = new Label(">>");
        private final Label next = new Label(">");
        private final Label prev = new Label("<");
        private final Label label = new Label();
        private boolean absolute = false;
        private boolean enabled = true;
        private int index;
        private int end;

        /** Instantiates a new navigator. */
        public Navigator() {
            initWidget(panel);

            final HorizontalPanel navigator = new HorizontalPanel();
            panel.add(navigator);

            next.addClickHandler(this);
            next.addStyleName("Button");

            prev.addClickHandler(this);
            prev.addStyleName("Button");

            last.addClickHandler(this);
            last.addStyleName("Button");

            first.addClickHandler(this);
            first.addStyleName("Button");

            label.addStyleName("Control");
            label.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);

            navigator.add(first);
            navigator.add(prev);
            navigator.add(label);
            navigator.add(next);
            navigator.add(last);
        }

        /**
         * Adds the specified change handler.
         *
         * @param handler to be added.
         * @return the handler registration
         */
        public HandlerRegistration addChangeHandler(ChangeHandler handler) {
            return addDomHandler(handler, ChangeEvent.getType());
        }

        /**
         * Fires a change event.
         * 
         * @param sender of the event.
         */
        protected void fireChange(Widget sender) {
            NativeEvent nativeEvent = Document.get().createChangeEvent();
            DomEvent.fireNativeEvent(nativeEvent, sender);
        }

        /**
         * Checks if this widget sent the change event.
         * 
         * @param event when changed.
         * @return true, if this widget sent the change event.
         */
        public boolean sent(ChangeEvent event) {
            return this == event.getSource();
        }

        /**
         * Handles the clicking of any of the buttons in the navigator as follows:
         * >> button sets the offset to max(end - limit, start)
         * > button sets the offset to max (offset + limit, end)
         * < button sets the offset to min (offset - limit, start)
         * << button sets the offset to start
         * Fires a change event on any change in offset.
         *
         * @param event when clicked
         */
        @Override
        public void onClick(ClickEvent event) {
            if (!enabled) {
                return;
            } else if (event.getSource() == first) {
                index = 0;
            } else if (event.getSource() == prev) {
                index = (absolute ? 1 : (index <= 0) ? 0 : index - 1);
            } else if (event.getSource() == next) {
                index = (absolute ? 2 : (index == end) ? end : index + 1);
            } else if (event.getSource() == last) {
                index = absolute ? 3 : end;
            }
            setLabel();
            fireChange(this);
        }

        /**
         * Sets the absolute.
         *
         * @param absolute the new absolute
         */
        public void setAbsolute(boolean absolute) {
            this.absolute = absolute;
        }

        /**
         * Sets the label.
         */
        private void setLabel() {
            label.setText("" + (index + 1) + " of " + (end + 1));
        }

        /**
         * Sets the label.
         *
         * @param text the new label
         */
        public void setLabel(String text) {
            label.setText(text);
        }

        /**
         * Gets the index.
         *
         * @return the index
         */
        public int getIndex() {
            return index;
        }
    }
}