package fr.lteconsulting.hexa.client.ui.widget; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.HasValueChangeHandlers; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.Widget; import fr.lteconsulting.hexa.client.calendar.Calendar; import fr.lteconsulting.hexa.client.calendar.CalendarPeriod; import fr.lteconsulting.hexa.client.calendar.PeriodBoundaries; import fr.lteconsulting.hexa.client.calendar.Tree; import fr.lteconsulting.hexa.client.common.HexaDate; import fr.lteconsulting.hexa.client.css.HexaCss; public class DatePicker extends Widget implements HasValueChangeHandlers<HexaDate> { public final static Css CSS = GWT.create( Css.class ); public interface Css extends HexaCss { String hexaDatePicker(); String previousMonth(); String nextMonth(); String selectable(); String unavailable(); String headerRow(); String row(); String today(); String button(); } // TODO // Localization of day names and start of the week day Tree available = null; HexaDate currentlyDisplayedMonth; public void setAvailablePeriod( String availablePeriod ) { this.available = Calendar.get().Parse( availablePeriod ); PeriodBoundaries boundaries = new PeriodBoundaries(); available.getFlat().GetBoundaries( boundaries ); setCurrentMonth( new HexaDate( boundaries.from ) ); } public void setCurrentMonth( HexaDate selectedMonth ) { if( selectedMonth == null || !selectedMonth.isValid() ) selectedMonth = new HexaDate(); CalendarPeriod availablePeriods = available != null ? available.getFlat() : Calendar.get().Parse( "a" ).getFlat(); int today = new HexaDate().toInt(); HexaDate firstDayOfTheMonth = new HexaDate( selectedMonth.getYear(), selectedMonth.getMonth(), 1 ); currentlyDisplayedMonth = firstDayOfTheMonth; int shift = -nativeToEuropean( firstDayOfTheMonth.getDay() ); HexaDate firstDisplayedDay = firstDayOfTheMonth.addDays( shift ); HexaDate currentDay = new HexaDate( firstDisplayedDay.getYear(), firstDisplayedDay.getMonth(), firstDisplayedDay.getDate() ); StringBuilder sb = new StringBuilder(); sb.append( "<div class='" + CSS.headerRow() + "'><button class='" + CSS.previousMonth() + " " + CSS.button() + "'><</button>" ); sb.append( "<div>" + HexaDate.MonthNames[selectedMonth.getMonth()] + " " + selectedMonth.getHumanYear() + "</div>" ); sb.append( "<button class='" + CSS.nextMonth() + " " + CSS.button() + "'>></button></div>" ); sb.append( "<div class='" + CSS.row() + "'>" ); for( int i = 0; i < 7; i++ ) sb.append( "<div>" + HexaDate.DayNames[europeanToNative( i )].substring( 0, 1 ) + "</div>" ); sb.append( "</div>" ); for( int r = 0; r < 6; r++ ) { sb.append( "<div class='" + CSS.row() + "'>" ); for( int i = 0; i < 7; i++ ) { boolean isDayAvailable = availablePeriods.IsInside( currentDay.getString() ); String className = isDayAvailable ? CSS.selectable() : CSS.unavailable(); if( today == currentDay.toInt() ) className += " " + CSS.today(); sb.append( "<div class='" + className + "'" + (isDayAvailable ? " data='" + currentDay.toInt() + "'" : "") + ">" + currentDay.getDate() + "</div>" ); currentDay = currentDay.addDays( 1 ); } sb.append( "</div>" ); } getElement().setInnerHTML( sb.toString() ); } public DatePicker() { DivElement root = Document.get().createDivElement(); setElement( root ); setStylePrimaryName( CSS.hexaDatePicker() ); setCurrentMonth( new HexaDate() ); addDomHandler( new ClickHandler() { @Override public void onClick( ClickEvent event ) { Element target = Element.as( event.getNativeEvent().getEventTarget() ); while( target != null && target != getElement() ) { String data = target.getAttribute( "data" ); if( data != null && !data.isEmpty() ) { HexaDate date = new HexaDate( Integer.parseInt( data ) ); ValueChangeEvent.fire( DatePicker.this, date ); return; } else if( target.hasClassName( CSS.previousMonth() ) ) { setCurrentMonth( currentlyDisplayedMonth.addDays( -5 ) ); return; } else if( target.hasClassName( CSS.nextMonth() ) ) { int nbDaysInMonth = HexaDate.GetDaysInMonth( currentlyDisplayedMonth.getYear(), currentlyDisplayedMonth.getMonth() ); setCurrentMonth( currentlyDisplayedMonth.addDays( nbDaysInMonth ) ); return; } target = target.getParentElement(); } } }, ClickEvent.getType() ); } private int nativeToEuropean( int day ) { return (day + 6) % 7; } private int europeanToNative( int day ) { return (day + 1) % 7; } @Override public HandlerRegistration addValueChangeHandler( ValueChangeHandler<HexaDate> handler ) { return addHandler( handler, ValueChangeEvent.getType() ); } }