/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.rrm.ehour.ui.common.panel.calendar;
import net.rrm.ehour.config.EhourConfig;
import net.rrm.ehour.data.DateRange;
import net.rrm.ehour.domain.User;
import net.rrm.ehour.ui.common.decorator.LoadingSpinnerDecorator;
import net.rrm.ehour.ui.common.event.AjaxEvent;
import net.rrm.ehour.ui.common.event.EventPublisher;
import net.rrm.ehour.ui.common.event.PayloadAjaxEvent;
import net.rrm.ehour.ui.common.formguard.GuardFormAjaxCallListener;
import net.rrm.ehour.ui.common.model.DateModel;
import net.rrm.ehour.ui.common.panel.sidepanel.SidePanel;
import net.rrm.ehour.ui.common.session.EhourWebSession;
import net.rrm.ehour.ui.common.util.HtmlUtil;
import net.rrm.ehour.util.DateUtil;
import org.apache.wicket.AttributeModifier;
import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.attributes.AjaxRequestAttributes;
import org.apache.wicket.ajax.attributes.IAjaxCallListener;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.PropertyModel;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.DAY_OF_WEEK;
/**
* Navigation Calendar
*/
public class CalendarPanel extends SidePanel {
private static final long serialVersionUID = -7777893083323915299L;
private WebMarkupContainer calendarFrame;
private User user;
private boolean fireWeekClicks;
private DateRange highlightWeekStartingAt;
private EhourConfig config;
private CalendarWeekFactory calendarWeekFactory;
public CalendarPanel(String id, User user) {
this(id, user, true);
}
public CalendarPanel(String id, User user, boolean allowWeekClicks) {
super(id);
calendarWeekFactory = new CalendarWeekFactory();
setOutputMarkupId(true);
config = EhourWebSession.getEhourConfig();
this.user = user;
fireWeekClicks = allowWeekClicks;
calendarFrame = getFrame();
add(calendarFrame);
buildCalendar(calendarFrame);
}
private void buildCalendar(WebMarkupContainer parent) {
// first get the data
Calendar month = EhourWebSession.getSession().getNavCalendar();
List<CalendarWeek> weeks = calendarWeekFactory.createWeeks(config.getFirstDayOfWeek(), user.getUserId(), ((GregorianCalendar) month.clone()));
// set month label
parent.add(new Label("currentMonth", new DateModel(month, EhourWebSession.getEhourConfig(), DateModel.DATESTYLE_MONTHONLY)));
// previous & next month links
parent.add(createPreviousMonthLink("previousMonthLink"));
parent.add(createNextMonthLink("nextMonthLink"));
// content
addCalendarWeeks(parent, weeks);
}
public void refreshCalendar(AjaxRequestTarget target) {
WebMarkupContainer replacementFrame = getFrame();
buildCalendar(replacementFrame);
calendarFrame.replaceWith(replacementFrame);
calendarFrame = replacementFrame;
target.add(replacementFrame);
}
private WebMarkupContainer getFrame() {
WebMarkupContainer calendarFrame = new WebMarkupContainer("calendarFrame");
calendarFrame.setOutputMarkupId(true);
return calendarFrame;
}
private AjaxLink<Void> createNextMonthLink(String id) {
AjaxLink<Void> nextMonthLink = new ChangeMonthLink(id, 1);
return nextMonthLink;
}
private AjaxLink<Void> createPreviousMonthLink(String id) {
AjaxLink<Void> previousMonthLink = new ChangeMonthLink(id, -1);
return previousMonthLink;
}
@SuppressWarnings("serial")
private void addCalendarWeeks(WebMarkupContainer container, final List<CalendarWeek> weeks) {
ListView<CalendarWeek> view = new ListView<CalendarWeek>("weeks", weeks) {
public void populateItem(final ListItem<CalendarWeek> item) {
CalendarWeek week = item.getModelObject();
Calendar renderDate = (Calendar) week.getWeekStart().clone();
boolean isCurrentWeek = highlightWeekStartingAt != null && DateUtil.isDateWithinRange(week.getWeekStart().getTime(), highlightWeekStartingAt);
for (int dayInWeek = 1; dayInWeek <= 7; dayInWeek++) {
boolean weekend = DateUtil.isWeekend(renderDate);
int currentDay = renderDate.get(DAY_OF_WEEK);
CalendarDay day = week.getDay(currentDay);
Label label = createLabel(dayInWeek, week, day, weekend);
item.add(label);
renderDate.add(Calendar.DATE, 1);
}
item.setOutputMarkupId(true);
if (fireWeekClicks) {
fireWeekClicks(item, week, isCurrentWeek);
} else {
item.add(AttributeModifier.replace("class", "CalendarWeek other"));
item.add(AttributeModifier.replace("style", "cursor:default"));
}
if (item.getIndex() == weeks.size() - 1) {
item.add(AttributeModifier.append("class", "LastWeek"));
}
}
private void fireWeekClicks(ListItem<CalendarWeek> item, CalendarWeek week, boolean isCurrentWeek) {
if (isCurrentWeek) {
item.add(AttributeModifier.replace("class", "CalendarWeek selectedWeek"));
} else {
item.add(AttributeModifier.replace("class", "CalendarWeek other"));
item.add(new WeekClick("onclick", week.getWeek(), week.getYear()));
}
}
private Label createLabel(int dayOfWeek, CalendarWeek week, CalendarDay day, boolean weekend) {
String id = "day" + dayOfWeek;
// when day is null the date is in the next/previous month
Label label = day == null ? HtmlUtil.getNbspLabel(id) : new Label(id, new PropertyModel<Integer>(day, "monthDay"));
// determine css class
StringBuilder cssClass = new StringBuilder(weekend ? "WeekendDay" : "");
// booked days are bold
if (day != null && day.isBooked()) {
cssClass.append(" Booked");
}
// first day doesn't have margin-left
if (dayOfWeek == 1) {
cssClass.append(" FirstDay");
}
if (dayOfWeek == 7) {
if (week.getLocation() == ElementLocation.LAST) {
cssClass.append(" LastDayOfLastRow");
}
}
label.add(AttributeModifier.replace("class", cssClass.toString()));
return label;
}
};
container.add(view);
}
/**
* Changes the month
*
* @author Thies
*/
private class ChangeMonthLink extends AjaxLink<Void> {
private static final long serialVersionUID = 1L;
private int monthChange;
public ChangeMonthLink(String id, int monthChange) {
super(id);
this.monthChange = monthChange;
}
@Override
public void onClick(AjaxRequestTarget target) {
EhourWebSession session = EhourWebSession.getSession();
Calendar month = session.getNavCalendar();
month.add(Calendar.MONTH, monthChange);
month.set(DAY_OF_MONTH, 1);
session.setNavCalendar(month);
// do it before it gets replaced, otherwise getPage is null due to new instantiation of links
EventPublisher.publishAjaxEvent(ChangeMonthLink.this, new AjaxEvent(CalendarAjaxEventType.MONTH_CHANGE));
refreshCalendar(target);
this.setEnabled(false);
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
List<IAjaxCallListener> listeners = attributes.getAjaxCallListeners();
listeners.add(new GuardFormAjaxCallListener());
listeners.add(new LoadingSpinnerDecorator());
}
}
private class WeekClick extends AjaxEventBehavior {
private static final long serialVersionUID = 9164386260367481606L;
private int week, year;
public WeekClick(String id, int week, int year) {
super(id);
this.week = week;
this.year = year;
}
@Override
protected void onEvent(AjaxRequestTarget target) {
EhourWebSession session = EhourWebSession.getSession();
Calendar cal = DateUtil.getCalendar(session.getEhourConfig());
cal.set(Calendar.YEAR, year);
cal.set(Calendar.WEEK_OF_YEAR, week);
DateUtil.dayOfWeekFix(cal);
cal.set(DAY_OF_WEEK, config.getFirstDayOfWeek());
session.setNavCalendar(cal);
EventPublisher.publishAjaxEvent(CalendarPanel.this, new PayloadAjaxEvent<>(CalendarAjaxEventType.WEEK_CLICK, cal));
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
List<IAjaxCallListener> listeners = attributes.getAjaxCallListeners();
listeners.add(new GuardFormAjaxCallListener());
listeners.add(new LoadingSpinnerDecorator());
}
}
public void setHighlightWeekStartingAt(DateRange highlightWeekStartingAt) {
this.highlightWeekStartingAt = highlightWeekStartingAt;
}
}