You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VCalendarPanel.java 55KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.Date;
  6. import java.util.List;
  7. import com.google.gwt.dom.client.Node;
  8. import com.google.gwt.event.dom.client.BlurEvent;
  9. import com.google.gwt.event.dom.client.BlurHandler;
  10. import com.google.gwt.event.dom.client.ChangeEvent;
  11. import com.google.gwt.event.dom.client.ChangeHandler;
  12. import com.google.gwt.event.dom.client.ClickEvent;
  13. import com.google.gwt.event.dom.client.ClickHandler;
  14. import com.google.gwt.event.dom.client.DomEvent;
  15. import com.google.gwt.event.dom.client.FocusEvent;
  16. import com.google.gwt.event.dom.client.FocusHandler;
  17. import com.google.gwt.event.dom.client.KeyCodes;
  18. import com.google.gwt.event.dom.client.KeyDownEvent;
  19. import com.google.gwt.event.dom.client.KeyDownHandler;
  20. import com.google.gwt.event.dom.client.KeyPressEvent;
  21. import com.google.gwt.event.dom.client.KeyPressHandler;
  22. import com.google.gwt.event.dom.client.MouseDownEvent;
  23. import com.google.gwt.event.dom.client.MouseDownHandler;
  24. import com.google.gwt.event.dom.client.MouseOutEvent;
  25. import com.google.gwt.event.dom.client.MouseOutHandler;
  26. import com.google.gwt.event.dom.client.MouseUpEvent;
  27. import com.google.gwt.event.dom.client.MouseUpHandler;
  28. import com.google.gwt.user.client.Command;
  29. import com.google.gwt.user.client.DeferredCommand;
  30. import com.google.gwt.user.client.Timer;
  31. import com.google.gwt.user.client.ui.Button;
  32. import com.google.gwt.user.client.ui.FlexTable;
  33. import com.google.gwt.user.client.ui.FlowPanel;
  34. import com.google.gwt.user.client.ui.InlineHTML;
  35. import com.google.gwt.user.client.ui.ListBox;
  36. import com.google.gwt.user.client.ui.Widget;
  37. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  38. import com.vaadin.terminal.gwt.client.BrowserInfo;
  39. import com.vaadin.terminal.gwt.client.DateTimeService;
  40. @SuppressWarnings("deprecation")
  41. public class VCalendarPanel extends FocusableFlexTable implements
  42. KeyDownHandler, KeyPressHandler, MouseOutHandler, MouseDownHandler,
  43. MouseUpHandler, BlurHandler, FocusHandler {
  44. public interface SubmitListener {
  45. /**
  46. * Called when calendar user triggers a submitting operation in calendar
  47. * panel. Eg. clicking on day or hitting enter.
  48. */
  49. void onSubmit();
  50. /**
  51. * On eg. ESC key.
  52. */
  53. void onCancel();
  54. }
  55. /**
  56. * Blur listener that listens to blur event from the panel
  57. */
  58. public interface FocusOutListener {
  59. /**
  60. * @return true if the calendar panel is not used after focus moves out
  61. */
  62. boolean onFocusOut(DomEvent event);
  63. }
  64. /**
  65. * Dispatches an event when the panel changes its _focused_ value.
  66. */
  67. public interface ValueChangeListener {
  68. /**
  69. *
  70. * @return true if the calendar panel will not be used anymore
  71. */
  72. void changed(Date date);
  73. }
  74. /**
  75. * Dispatches an event when the panel when time is changed
  76. */
  77. public interface TimeChangeListener {
  78. void changed(int hour, int min, int sec, int msec);
  79. }
  80. /**
  81. * Represents a Date button in the calendar
  82. */
  83. private class VEventButton extends Button {
  84. public VEventButton() {
  85. addMouseDownHandler(VCalendarPanel.this);
  86. addMouseOutHandler(VCalendarPanel.this);
  87. addMouseUpHandler(VCalendarPanel.this);
  88. }
  89. }
  90. private static final String CN_FOCUSED = "focused";
  91. private static final String CN_TODAY = "today";
  92. private static final String CN_SELECTED = "selected";
  93. /**
  94. * Represents a click handler for when a user selects a value by using the
  95. * mouse
  96. */
  97. private ClickHandler dayClickHandler = new ClickHandler() {
  98. /*
  99. * (non-Javadoc)
  100. *
  101. * @see
  102. * com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt
  103. * .event.dom.client.ClickEvent)
  104. */
  105. public void onClick(ClickEvent event) {
  106. Day day = (Day) event.getSource();
  107. focusDay(day.getDay());
  108. selectFocused();
  109. onSubmit();
  110. }
  111. };
  112. private VEventButton prevYear;
  113. private VEventButton nextYear;
  114. private VEventButton prevMonth;
  115. private VEventButton nextMonth;
  116. private VTime time;
  117. private FlexTable days = new FlexTable();
  118. /* Needed to identify resolution changes */
  119. private int oldResolution = 0;
  120. private int resolution = VDateField.RESOLUTION_YEAR;
  121. private int focusedRow;
  122. private int focusedColumn;
  123. private Timer mouseTimer;
  124. private Date value;
  125. private boolean enabled = true;
  126. private boolean readonly = false;
  127. private DateTimeService dateTimeService;
  128. private boolean showISOWeekNumbers;
  129. private Date focusedDate;
  130. private Day selectedDay;
  131. private Day focusedDay;
  132. private FocusOutListener focusOutListener;
  133. private SubmitListener submitListener;
  134. private ValueChangeListener valueChangeListener;
  135. private TimeChangeListener timeChangeListener;
  136. private boolean hasFocus = false;
  137. public VCalendarPanel() {
  138. setStyleName(VDateField.CLASSNAME + "-calendarpanel");
  139. /*
  140. * Firefox auto-repeat works correctly only if we use a key press
  141. * handler, other browsers handle it correctly when using a key down
  142. * handler
  143. */
  144. if (BrowserInfo.get().isGecko()) {
  145. addKeyPressHandler(this);
  146. } else {
  147. addKeyDownHandler(this);
  148. }
  149. addFocusHandler(this);
  150. addBlurHandler(this);
  151. }
  152. /**
  153. * Sets the focus to given day of current time. Used when moving in the
  154. * calender with the keyboard.
  155. *
  156. * @param day
  157. * The day number from by Date.getDate()
  158. */
  159. private void focusDay(int day) {
  160. // Only used when calender body is present
  161. if (resolution > VDateField.RESOLUTION_MONTH) {
  162. if (focusedDay != null)
  163. focusedDay.removeStyleDependentName(CN_FOCUSED);
  164. if (day > 0 && focusedDate != null) {
  165. focusedDate.setDate(day);
  166. int rowCount = days.getRowCount();
  167. for (int i = 0; i < rowCount; i++) {
  168. int cellCount = days.getCellCount(i);
  169. for (int j = 0; j < cellCount; j++) {
  170. Widget widget = days.getWidget(i, j);
  171. if (widget != null && widget instanceof Day) {
  172. Day curday = (Day) widget;
  173. if (curday.getDay() == day) {
  174. curday.addStyleDependentName(CN_FOCUSED);
  175. focusedDay = curday;
  176. focusedColumn = j;
  177. focusedRow = i;
  178. return;
  179. }
  180. }
  181. }
  182. }
  183. }
  184. }
  185. }
  186. /**
  187. * Sets the selection hightlight to a given date of current time
  188. *
  189. * @param day
  190. */
  191. private void selectDate(int day) {
  192. value.setDate(day);
  193. if (selectedDay != null)
  194. selectedDay.removeStyleDependentName(CN_SELECTED);
  195. int rowCount = days.getRowCount();
  196. for (int i = 0; i < rowCount; i++) {
  197. int cellCount = days.getCellCount(i);
  198. for (int j = 0; j < cellCount; j++) {
  199. Widget widget = days.getWidget(i, j);
  200. if (widget != null && widget instanceof Day) {
  201. Day curday = (Day) widget;
  202. if (curday.getDay() == day) {
  203. curday.addStyleDependentName(CN_SELECTED);
  204. selectedDay = curday;
  205. return;
  206. }
  207. }
  208. }
  209. }
  210. }
  211. /**
  212. * Updates year, month, day from focusedDate to value
  213. */
  214. private void selectFocused() {
  215. if (focusedDate != null) {
  216. int changedFields = 0;
  217. if (value.getYear() != focusedDate.getYear()) {
  218. value.setYear(focusedDate.getYear());
  219. changedFields += VDateField.RESOLUTION_YEAR;
  220. }
  221. if (value.getMonth() != focusedDate.getMonth()) {
  222. value.setMonth(focusedDate.getMonth());
  223. changedFields += VDateField.RESOLUTION_MONTH;
  224. }
  225. if (value.getDate() != focusedDate.getDate()) {
  226. value.setDate(focusedDate.getDate());
  227. changedFields += VDateField.RESOLUTION_DAY;
  228. }
  229. selectDate(focusedDate.getDate());
  230. } else {
  231. ApplicationConnection.getConsole().log(
  232. "Trying to select a the focused date which is NULL!");
  233. }
  234. }
  235. protected boolean onValueChange() {
  236. return false;
  237. }
  238. private int getResolution() {
  239. return resolution;
  240. }
  241. public void setResolution(int resolution) {
  242. if (resolution != this.resolution) {
  243. this.oldResolution = this.resolution;
  244. this.resolution = resolution;
  245. }
  246. }
  247. private boolean isReadonly() {
  248. return readonly;
  249. }
  250. private boolean isEnabled() {
  251. return enabled;
  252. }
  253. private void clearCalendarBody(boolean remove) {
  254. if (!remove) {
  255. // Leave the cells in place but clear their contents
  256. // This has the side effect of ensuring that the calendar always
  257. // contain 7 rows.
  258. for (int row = 1; row < 7; row++) {
  259. for (int col = 0; col < 8; col++) {
  260. days.setHTML(row, col, "&nbsp;");
  261. }
  262. }
  263. } else if (getRowCount() > 1) {
  264. removeRow(1);
  265. days.clear();
  266. }
  267. }
  268. /**
  269. * Builds the top buttons and current month and year header.
  270. *
  271. * @param forceRedraw
  272. * Forces the builder to recreate the instances of the buttons.
  273. * @param needsMonth
  274. * Should the month buttons be visible?
  275. */
  276. private void buildCalendarHeader(boolean forceRedraw, boolean needsMonth) {
  277. if (forceRedraw) {
  278. if (prevMonth == null) {
  279. getFlexCellFormatter().setStyleName(0, 0,
  280. VDateField.CLASSNAME + "-calendarpanel-prevyear");
  281. getFlexCellFormatter().setStyleName(0, 4,
  282. VDateField.CLASSNAME + "-calendarpanel-nextyear");
  283. getFlexCellFormatter().setStyleName(0, 3,
  284. VDateField.CLASSNAME + "-calendarpanel-nextmonth");
  285. getFlexCellFormatter().setStyleName(0, 1,
  286. VDateField.CLASSNAME + "-calendarpanel-prevmonth");
  287. getRowFormatter().addStyleName(0,
  288. VDateField.CLASSNAME + "-calendarpanel-header");
  289. prevYear = new VEventButton();
  290. prevYear.setHTML("&laquo;");
  291. prevYear.setStyleName("v-button-prevyear");
  292. prevYear.setTabIndex(-1);
  293. nextYear = new VEventButton();
  294. nextYear.setHTML("&raquo;");
  295. nextYear.setStyleName("v-button-nextyear");
  296. nextYear.setTabIndex(-1);
  297. setWidget(0, 0, prevYear);
  298. setWidget(0, 4, nextYear);
  299. if (needsMonth) {
  300. prevMonth = new VEventButton();
  301. prevMonth.setHTML("&lsaquo;");
  302. prevMonth.setStyleName("v-button-prevmonth");
  303. prevMonth.setTabIndex(-1);
  304. nextMonth = new VEventButton();
  305. nextMonth.setHTML("&rsaquo;");
  306. nextMonth.setStyleName("v-button-nextmonth");
  307. nextMonth.setTabIndex(-1);
  308. setWidget(0, 3, nextMonth);
  309. setWidget(0, 1, prevMonth);
  310. }
  311. } else if (!needsMonth) {
  312. // Remove month traverse buttons
  313. remove(prevMonth);
  314. remove(nextMonth);
  315. prevMonth = null;
  316. nextMonth = null;
  317. }
  318. }
  319. final String monthName = needsMonth ? getDateTimeService().getMonth(
  320. focusedDate.getMonth()) : "";
  321. final int year = focusedDate.getYear() + 1900;
  322. getFlexCellFormatter().setStyleName(0, 2,
  323. VDateField.CLASSNAME + "-calendarpanel-month");
  324. setHTML(0, 2, "<span class=\"" + VDateField.CLASSNAME
  325. + "-calendarpanel-month\">" + monthName + " " + year
  326. + "</span>");
  327. }
  328. private DateTimeService getDateTimeService() {
  329. return dateTimeService;
  330. }
  331. public void setDateTimeService(DateTimeService dateTimeService) {
  332. this.dateTimeService = dateTimeService;
  333. }
  334. /**
  335. * Returns whether ISO 8601 week numbers should be shown in the value
  336. * selector or not. ISO 8601 defines that a week always starts with a Monday
  337. * so the week numbers are only shown if this is the case.
  338. *
  339. * @return true if week number should be shown, false otherwise
  340. */
  341. public boolean isShowISOWeekNumbers() {
  342. return showISOWeekNumbers;
  343. }
  344. public void setShowISOWeekNumbers(boolean showISOWeekNumbers) {
  345. this.showISOWeekNumbers = showISOWeekNumbers;
  346. }
  347. /**
  348. * Builds the day and time selectors of the calendar.
  349. */
  350. private void buildCalendarBody() {
  351. final int weekColumn = 0;
  352. final int firstWeekdayColumn = 1;
  353. final int headerRow = 0;
  354. setWidget(1, 0, days);
  355. setCellPadding(0);
  356. setCellSpacing(0);
  357. getFlexCellFormatter().setColSpan(1, 0, 5);
  358. getFlexCellFormatter().setStyleName(1, 0,
  359. VDateField.CLASSNAME + "-calendarpanel-body");
  360. days.getFlexCellFormatter().setStyleName(headerRow, weekColumn,
  361. "v-week");
  362. days.setHTML(headerRow, weekColumn, "<strong></strong>");
  363. // Hide the week column if week numbers are not to be displayed.
  364. days.getFlexCellFormatter().setVisible(headerRow, weekColumn,
  365. isShowISOWeekNumbers());
  366. days.getRowFormatter().setStyleName(headerRow,
  367. VDateField.CLASSNAME + "-calendarpanel-weekdays");
  368. if (isShowISOWeekNumbers()) {
  369. days.getFlexCellFormatter().setStyleName(headerRow, weekColumn,
  370. "v-first");
  371. days.getFlexCellFormatter().setStyleName(headerRow,
  372. firstWeekdayColumn, "");
  373. days.getRowFormatter().addStyleName(headerRow,
  374. VDateField.CLASSNAME + "-calendarpanel-weeknumbers");
  375. } else {
  376. days.getFlexCellFormatter().setStyleName(headerRow, weekColumn, "");
  377. days.getFlexCellFormatter().setStyleName(headerRow,
  378. firstWeekdayColumn, "v-first");
  379. }
  380. days.getFlexCellFormatter().setStyleName(headerRow,
  381. firstWeekdayColumn + 6, "v-last");
  382. // Print weekday names
  383. final int firstDay = getDateTimeService().getFirstDayOfWeek();
  384. for (int i = 0; i < 7; i++) {
  385. int day = i + firstDay;
  386. if (day > 6) {
  387. day = 0;
  388. }
  389. if (getResolution() > VDateField.RESOLUTION_MONTH) {
  390. days.setHTML(headerRow, firstWeekdayColumn + i, "<strong>"
  391. + getDateTimeService().getShortDay(day) + "</strong>");
  392. } else {
  393. days.setHTML(headerRow, firstWeekdayColumn + i, "");
  394. }
  395. }
  396. // The day of month that is selected, -1 if no day of this month is
  397. // selected (i.e, showing another month/year than selected or nothing is
  398. // selected)
  399. int dayOfMonthSelected = -1;
  400. // The day of month that is today, -1 if no day of this month is today
  401. // (i.e., showing another month/year than current)
  402. int dayOfMonthToday = -1;
  403. boolean initiallyNull = value == null;
  404. if (!initiallyNull && value.getMonth() == focusedDate.getMonth()
  405. && value.getYear() == focusedDate.getYear()) {
  406. dayOfMonthSelected = value.getDate();
  407. }
  408. final Date today = new Date();
  409. if (today.getMonth() == focusedDate.getMonth()
  410. && today.getYear() == focusedDate.getYear()) {
  411. dayOfMonthToday = today.getDate();
  412. }
  413. final int startWeekDay = getDateTimeService().getStartWeekDay(
  414. focusedDate);
  415. final int daysInMonth = DateTimeService
  416. .getNumberOfDaysInMonth(focusedDate);
  417. int dayCount = 0;
  418. final Date curr = new Date(focusedDate.getTime());
  419. // No month has more than 6 weeks so 6 is a safe maximum for rows.
  420. for (int weekOfMonth = 1; weekOfMonth < 7; weekOfMonth++) {
  421. boolean weekNumberProcessed[] = new boolean[] { false, false,
  422. false, false, false, false, false };
  423. for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
  424. if (!(weekOfMonth == 1 && dayOfWeek < startWeekDay)) {
  425. if (dayCount >= daysInMonth) {
  426. // All days printed and we are done
  427. break;
  428. }
  429. final int dayOfMonth = ++dayCount;
  430. curr.setDate(dayCount);
  431. // Actually write the day of month
  432. Day day = new Day(dayOfMonth);
  433. if (dayOfMonthSelected == dayOfMonth) {
  434. day.addStyleDependentName(CN_SELECTED);
  435. selectedDay = day;
  436. }
  437. if (dayOfMonthToday == dayOfMonth) {
  438. day.addStyleDependentName(CN_TODAY);
  439. }
  440. if (dayOfMonth == focusedDate.getDate()) {
  441. focusedDay = day;
  442. focusedRow = weekOfMonth;
  443. focusedColumn = firstWeekdayColumn + dayOfWeek;
  444. if (hasFocus) {
  445. day.addStyleDependentName(CN_FOCUSED);
  446. }
  447. }
  448. days.setWidget(weekOfMonth, firstWeekdayColumn + dayOfWeek,
  449. day);
  450. // ISO week numbers if requested
  451. if (!weekNumberProcessed[weekOfMonth]) {
  452. days.getCellFormatter().setVisible(weekOfMonth,
  453. weekColumn, isShowISOWeekNumbers());
  454. if (isShowISOWeekNumbers()) {
  455. final String baseCssClass = VDateField.CLASSNAME
  456. + "-calendarpanel-weeknumber";
  457. String weekCssClass = baseCssClass;
  458. int weekNumber = DateTimeService
  459. .getISOWeekNumber(curr);
  460. days.setHTML(weekOfMonth, 0, "<span class=\""
  461. + weekCssClass + "\"" + ">" + weekNumber
  462. + "</span>");
  463. weekNumberProcessed[weekOfMonth] = true;
  464. }
  465. }
  466. }
  467. }
  468. }
  469. }
  470. /**
  471. * Do we need the time selector
  472. *
  473. * @return True if it is required
  474. */
  475. private boolean isTimeSelectorNeeded() {
  476. return getResolution() > VDateField.RESOLUTION_DAY;
  477. }
  478. /**
  479. * Updates the calendar and text field with the selected dates.
  480. */
  481. public void renderCalendar() {
  482. if (focusedDate == null) {
  483. focusedDate = new Date();
  484. }
  485. if (getResolution() <= VDateField.RESOLUTION_MONTH
  486. && valueChangeListener != null) {
  487. valueChangeListener.changed(focusedDate);
  488. }
  489. Date start = new Date();
  490. final boolean needsMonth = getResolution() > VDateField.RESOLUTION_YEAR;
  491. boolean needsBody = getResolution() >= VDateField.RESOLUTION_DAY;
  492. buildCalendarHeader(true, needsMonth);
  493. clearCalendarBody(!needsBody);
  494. if (needsBody) {
  495. buildCalendarBody();
  496. }
  497. if (isTimeSelectorNeeded()
  498. && (time == null || this.resolution != this.oldResolution)) {
  499. time = new VTime();
  500. setWidget(2, 0, time);
  501. getFlexCellFormatter().setColSpan(2, 0, 5);
  502. getFlexCellFormatter().setStyleName(2, 0,
  503. VDateField.CLASSNAME + "-calendarpanel-time");
  504. this.oldResolution = this.resolution;
  505. } else if (isTimeSelectorNeeded()) {
  506. time.updateTimes();
  507. } else if (time != null) {
  508. remove(time);
  509. }
  510. Date end = new Date();
  511. ApplicationConnection.getConsole().error(
  512. "Rendering calendar panel for(ms) "
  513. + (end.getTime() - start.getTime()));
  514. }
  515. /**
  516. * Selects the next month
  517. */
  518. private void focusNextMonth() {
  519. int currentMonth = focusedDate.getMonth();
  520. focusedDate.setMonth(currentMonth + 1);
  521. int requestedMonth = (currentMonth + 1) % 12;
  522. /*
  523. * If the selected value was e.g. 31.3 the new value would be 31.4 but
  524. * this value is invalid so the new value will be 1.5. This is taken
  525. * care of by decreasing the value until we have the correct month.
  526. */
  527. while (focusedDate.getMonth() != requestedMonth) {
  528. focusedDate.setDate(focusedDate.getDate() - 1);
  529. }
  530. renderCalendar();
  531. }
  532. /**
  533. * Selects the previous month
  534. */
  535. private void focusPreviousMonth() {
  536. int currentMonth = focusedDate.getMonth();
  537. focusedDate.setMonth(currentMonth - 1);
  538. /*
  539. * If the selected value was e.g. 31.12 the new value would be 31.11 but
  540. * this value is invalid so the new value will be 1.12. This is taken
  541. * care of by decreasing the value until we have the correct month.
  542. */
  543. while (focusedDate.getMonth() == currentMonth) {
  544. focusedDate.setDate(focusedDate.getDate() - 1);
  545. }
  546. renderCalendar();
  547. }
  548. /**
  549. * Selects the previous year
  550. */
  551. private void focusPreviousYear(int years) {
  552. focusedDate.setYear(focusedDate.getYear() - years);
  553. renderCalendar();
  554. }
  555. /**
  556. * Selects the next year
  557. */
  558. private void focusNextYear(int years) {
  559. focusedDate.setYear(focusedDate.getYear() + years);
  560. renderCalendar();
  561. }
  562. /**
  563. * Handles a user click on the component
  564. *
  565. * @param sender
  566. * The component that was clicked
  567. * @param updateVariable
  568. * Should the value field be updated
  569. *
  570. */
  571. private void processClickEvent(Widget sender) {
  572. if (!isEnabled() || isReadonly()) {
  573. return;
  574. }
  575. if (sender == prevYear) {
  576. focusPreviousYear(1);
  577. } else if (sender == nextYear) {
  578. focusNextYear(1);
  579. } else if (sender == prevMonth) {
  580. focusPreviousMonth();
  581. } else if (sender == nextMonth) {
  582. focusNextMonth();
  583. }
  584. }
  585. public interface CalendarEntrySource {
  586. public List getEntries(Date date, int resolution);
  587. }
  588. /*
  589. * (non-Javadoc)
  590. *
  591. * @see
  592. * com.google.gwt.event.dom.client.KeyDownHandler#onKeyDown(com.google.gwt
  593. * .event.dom.client.KeyDownEvent)
  594. */
  595. public void onKeyDown(KeyDownEvent event) {
  596. handleKeyPress(event);
  597. }
  598. /*
  599. * (non-Javadoc)
  600. *
  601. * @see
  602. * com.google.gwt.event.dom.client.KeyPressHandler#onKeyPress(com.google
  603. * .gwt.event.dom.client.KeyPressEvent)
  604. */
  605. public void onKeyPress(KeyPressEvent event) {
  606. handleKeyPress(event);
  607. }
  608. /**
  609. * Handles the keypress from both the onKeyPress event and the onKeyDown
  610. * event
  611. *
  612. * @param event
  613. * The keydown/keypress event
  614. */
  615. private void handleKeyPress(DomEvent event) {
  616. if (time != null
  617. && time.getElement().isOrHasChild(
  618. (Node) event.getNativeEvent().getEventTarget().cast())) {
  619. int nativeKeyCode = event.getNativeEvent().getKeyCode();
  620. if (nativeKeyCode == getSelectKey()) {
  621. ApplicationConnection.getConsole().log(
  622. "keydown on listselects"
  623. + event.getNativeEvent().getKeyCode());
  624. onSubmit(); // submit happens if enter key hit down on listboxes
  625. event.preventDefault();
  626. event.stopPropagation();
  627. }
  628. return;
  629. }
  630. // Check tabs
  631. int keycode = event.getNativeEvent().getKeyCode();
  632. if (keycode == KeyCodes.KEY_TAB && event.getNativeEvent().getShiftKey()) {
  633. if (onTabOut(event)) {
  634. return;
  635. }
  636. }
  637. // Handle the navigation
  638. if (handleNavigation(keycode, event.getNativeEvent().getCtrlKey()
  639. || event.getNativeEvent().getMetaKey(), event.getNativeEvent()
  640. .getShiftKey())) {
  641. event.preventDefault();
  642. }
  643. }
  644. /**
  645. * Notifies submit-listeners of a submit event
  646. */
  647. private void onSubmit() {
  648. if (getSubmitListener() != null) {
  649. getSubmitListener().onSubmit();
  650. }
  651. }
  652. /**
  653. * Notifies submit-listeners of a cancel event
  654. */
  655. private void onCancel() {
  656. if (getSubmitListener() != null) {
  657. getSubmitListener().onCancel();
  658. }
  659. }
  660. /**
  661. * Handles the keyboard navigation when the resolution is set to years.
  662. *
  663. * @param keycode
  664. * The keycode to process
  665. * @param ctrl
  666. * Is ctrl pressed?
  667. * @param shift
  668. * is shift pressed
  669. * @return Returns true if the keycode was processed, else false
  670. */
  671. protected boolean handleNavigationYearMode(int keycode, boolean ctrl,
  672. boolean shift) {
  673. // Ctrl and Shift selection not supported
  674. if (ctrl || shift) {
  675. return false;
  676. }
  677. else if (keycode == getPreviousKey()) {
  678. focusNextYear(10); // Add 10 years
  679. return true;
  680. }
  681. else if (keycode == getForwardKey()) {
  682. focusNextYear(1); // Add 1 year
  683. return true;
  684. }
  685. else if (keycode == getNextKey()) {
  686. focusPreviousYear(10); // Subtract 10 years
  687. return true;
  688. }
  689. else if (keycode == getBackwardKey()) {
  690. focusPreviousYear(1); // Subtract 1 year
  691. return true;
  692. } else if (keycode == getSelectKey()) {
  693. value = (Date) focusedDate.clone();
  694. onSubmit();
  695. return true;
  696. } else if (keycode == getResetKey()) {
  697. // Restore showing value the selected value
  698. focusedDate.setTime(value.getTime());
  699. renderCalendar();
  700. return true;
  701. } else if (keycode == getCloseKey()) {
  702. // TODO fire listener, on users responsibility??
  703. return true;
  704. }
  705. return false;
  706. }
  707. /**
  708. * Handle the keyboard navigation when the resolution is set to MONTH
  709. *
  710. * @param keycode
  711. * The keycode to handle
  712. * @param ctrl
  713. * Was the ctrl key pressed?
  714. * @param shift
  715. * Was the shift key pressed?
  716. * @return
  717. */
  718. protected boolean handleNavigationMonthMode(int keycode, boolean ctrl,
  719. boolean shift) {
  720. // Ctrl selection not supported
  721. if (ctrl) {
  722. return false;
  723. } else if (keycode == getPreviousKey()) {
  724. focusNextYear(1); // Add 1 year
  725. return true;
  726. } else if (keycode == getForwardKey()) {
  727. focusNextMonth(); // Add 1 month
  728. return true;
  729. } else if (keycode == getNextKey()) {
  730. focusPreviousYear(1); // Subtract 1 year
  731. return true;
  732. } else if (keycode == getBackwardKey()) {
  733. focusPreviousMonth(); // Subtract 1 month
  734. return true;
  735. } else if (keycode == getSelectKey()) {
  736. value = (Date) focusedDate.clone();
  737. onSubmit();
  738. return true;
  739. } else if (keycode == getResetKey()) {
  740. // Restore showing value the selected value
  741. focusedDate.setTime(value.getTime());
  742. renderCalendar();
  743. return true;
  744. } else if (keycode == getCloseKey() || keycode == KeyCodes.KEY_TAB) {
  745. // TODO fire close event
  746. return true;
  747. }
  748. return false;
  749. }
  750. /**
  751. * Handle keyboard navigation what the resolution is set to DAY
  752. *
  753. * @param keycode
  754. * The keycode to handle
  755. * @param ctrl
  756. * Was the ctrl key pressed?
  757. * @param shift
  758. * Was the shift key pressed?
  759. * @return Return true if the key press was handled by the method, else
  760. * return false.
  761. */
  762. protected boolean handleNavigationDayMode(int keycode, boolean ctrl,
  763. boolean shift) {
  764. // Ctrl key is not in use
  765. if (ctrl) {
  766. return false;
  767. }
  768. /*
  769. * Jumps to the next day.
  770. */
  771. if (keycode == getForwardKey() && !shift) {
  772. // Calculate new showing value
  773. Date newCurrentDate = (Date) focusedDate.clone();
  774. newCurrentDate.setDate(newCurrentDate.getDate() + 1);
  775. if (newCurrentDate.getMonth() == focusedDate.getMonth()) {
  776. // Month did not change, only move the selection
  777. focusDay(focusedDate.getDate() + 1);
  778. } else {
  779. // If the month changed we need to re-render the calendar
  780. focusedDate.setDate(focusedDate.getDate() + 1);
  781. renderCalendar();
  782. }
  783. return true;
  784. /*
  785. * Jumps to the previous day
  786. */
  787. } else if (keycode == getBackwardKey() && !shift) {
  788. // Calculate new showing value
  789. Date newCurrentDate = (Date) focusedDate.clone();
  790. newCurrentDate.setDate(newCurrentDate.getDate() - 1);
  791. if (newCurrentDate.getMonth() == focusedDate.getMonth()) {
  792. // Month did not change, only move the selection
  793. focusDay(focusedDate.getDate() - 1);
  794. } else {
  795. // If the month changed we need to re-render the calendar
  796. focusedDate.setDate(focusedDate.getDate() - 1);
  797. renderCalendar();
  798. }
  799. return true;
  800. /*
  801. * Jumps one week back in the calendar
  802. */
  803. } else if (keycode == getPreviousKey() && !shift) {
  804. // Calculate new showing value
  805. Date newCurrentDate = (Date) focusedDate.clone();
  806. newCurrentDate.setDate(newCurrentDate.getDate() - 7);
  807. if (newCurrentDate.getMonth() == focusedDate.getMonth()
  808. && focusedRow > 1) {
  809. // Month did not change, only move the selection
  810. focusDay(focusedDate.getDate() - 7);
  811. } else {
  812. // If the month changed we need to re-render the calendar
  813. focusedDate.setDate(focusedDate.getDate() - 7);
  814. renderCalendar();
  815. }
  816. return true;
  817. /*
  818. * Jumps one week forward in the calendar
  819. */
  820. } else if (keycode == getNextKey() && !ctrl && !shift) {
  821. // Calculate new showing value
  822. Date newCurrentDate = (Date) focusedDate.clone();
  823. newCurrentDate.setDate(newCurrentDate.getDate() + 7);
  824. if (newCurrentDate.getMonth() == focusedDate.getMonth()) {
  825. // Month did not change, only move the selection
  826. focusDay(focusedDate.getDate() + 7);
  827. } else {
  828. // If the month changed we need to re-render the calendar
  829. focusedDate.setDate(focusedDate.getDate() + 7);
  830. renderCalendar();
  831. }
  832. return true;
  833. /*
  834. * Selects the value that is chosen
  835. */
  836. } else if (keycode == getSelectKey() && !shift) {
  837. selectFocused();
  838. onSubmit(); // submit
  839. return true;
  840. } else if (keycode == getCloseKey()) {
  841. onCancel();
  842. // TODO close event
  843. return true;
  844. /*
  845. * Jumps to the next month
  846. */
  847. } else if (shift && keycode == getForwardKey()) {
  848. focusNextMonth();
  849. return true;
  850. /*
  851. * Jumps to the previous month
  852. */
  853. } else if (shift && keycode == getBackwardKey()) {
  854. focusPreviousMonth();
  855. return true;
  856. /*
  857. * Jumps to the next year
  858. */
  859. } else if (shift && keycode == getPreviousKey()) {
  860. focusNextYear(1);
  861. return true;
  862. /*
  863. * Jumps to the previous year
  864. */
  865. } else if (shift && keycode == getNextKey()) {
  866. focusPreviousYear(1);
  867. return true;
  868. /*
  869. * Resets the selection
  870. */
  871. } else if (keycode == getResetKey() && !shift) {
  872. // Restore showing value the selected value
  873. focusedDate.setTime(value.getTime());
  874. renderCalendar();
  875. return true;
  876. }
  877. return false;
  878. }
  879. /**
  880. * Handles the keyboard navigation
  881. *
  882. * @param keycode
  883. * The key code that was pressed
  884. * @param ctrl
  885. * Was the ctrl key pressed
  886. * @param shift
  887. * Was the shift key pressed
  888. * @return Return true if key press was handled by the component, else
  889. * return false
  890. */
  891. protected boolean handleNavigation(int keycode, boolean ctrl, boolean shift) {
  892. if (!isEnabled() || isReadonly()) {
  893. return false;
  894. }
  895. else if (resolution == VDateField.RESOLUTION_YEAR) {
  896. return handleNavigationYearMode(keycode, ctrl, shift);
  897. }
  898. else if (resolution == VDateField.RESOLUTION_MONTH) {
  899. return handleNavigationMonthMode(keycode, ctrl, shift);
  900. }
  901. else if (resolution == VDateField.RESOLUTION_DAY) {
  902. return handleNavigationDayMode(keycode, ctrl, shift);
  903. }
  904. else {
  905. return handleNavigationDayMode(keycode, ctrl, shift);
  906. }
  907. }
  908. /**
  909. * Returns the reset key which will reset the calendar to the previous
  910. * selection. By default this is backspace but it can be overriden to change
  911. * the key to whatever you want.
  912. *
  913. * @return
  914. */
  915. protected int getResetKey() {
  916. return KeyCodes.KEY_BACKSPACE;
  917. }
  918. /**
  919. * Returns the select key which selects the value. By default this is the
  920. * enter key but it can be changed to whatever you like by overriding this
  921. * method.
  922. *
  923. * @return
  924. */
  925. protected int getSelectKey() {
  926. return KeyCodes.KEY_ENTER;
  927. }
  928. /**
  929. * Returns the key that closes the popup window if this is a VPopopCalendar.
  930. * Else this does nothing. By default this is the Escape key but you can
  931. * change the key to whatever you want by overriding this method.
  932. *
  933. * @return
  934. */
  935. protected int getCloseKey() {
  936. return KeyCodes.KEY_ESCAPE;
  937. }
  938. /**
  939. * The key that selects the next day in the calendar. By default this is the
  940. * right arrow key but by overriding this method it can be changed to
  941. * whatever you like.
  942. *
  943. * @return
  944. */
  945. protected int getForwardKey() {
  946. return KeyCodes.KEY_RIGHT;
  947. }
  948. /**
  949. * The key that selects the previous day in the calendar. By default this is
  950. * the left arrow key but by overriding this method it can be changed to
  951. * whatever you like.
  952. *
  953. * @return
  954. */
  955. protected int getBackwardKey() {
  956. return KeyCodes.KEY_LEFT;
  957. }
  958. /**
  959. * The key that selects the next week in the calendar. By default this is
  960. * the down arrow key but by overriding this method it can be changed to
  961. * whatever you like.
  962. *
  963. * @return
  964. */
  965. protected int getNextKey() {
  966. return KeyCodes.KEY_DOWN;
  967. }
  968. /**
  969. * The key that selects the previous week in the calendar. By default this
  970. * is the up arrow key but by overriding this method it can be changed to
  971. * whatever you like.
  972. *
  973. * @return
  974. */
  975. protected int getPreviousKey() {
  976. return KeyCodes.KEY_UP;
  977. }
  978. /*
  979. * (non-Javadoc)
  980. *
  981. * @see
  982. * com.google.gwt.event.dom.client.MouseOutHandler#onMouseOut(com.google
  983. * .gwt.event.dom.client.MouseOutEvent)
  984. */
  985. public void onMouseOut(MouseOutEvent event) {
  986. if (mouseTimer != null) {
  987. mouseTimer.cancel();
  988. }
  989. }
  990. /*
  991. * (non-Javadoc)
  992. *
  993. * @see
  994. * com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google
  995. * .gwt.event.dom.client.MouseDownEvent)
  996. */
  997. public void onMouseDown(MouseDownEvent event) {
  998. // Allow user to click-n-hold for fast-forward or fast-rewind.
  999. // Timer is first used for a 500ms delay after mousedown. After that has
  1000. // elapsed, another timer is triggered to go off every 150ms. Both
  1001. // timers are cancelled on mouseup or mouseout.
  1002. if (event.getSource() instanceof VEventButton) {
  1003. final Widget sender = (Widget) event.getSource();
  1004. processClickEvent(sender);
  1005. mouseTimer = new Timer() {
  1006. @Override
  1007. public void run() {
  1008. mouseTimer = new Timer() {
  1009. @Override
  1010. public void run() {
  1011. processClickEvent(sender);
  1012. }
  1013. };
  1014. mouseTimer.scheduleRepeating(150);
  1015. }
  1016. };
  1017. mouseTimer.schedule(500);
  1018. }
  1019. }
  1020. /*
  1021. * (non-Javadoc)
  1022. *
  1023. * @see
  1024. * com.google.gwt.event.dom.client.MouseUpHandler#onMouseUp(com.google.gwt
  1025. * .event.dom.client.MouseUpEvent)
  1026. */
  1027. public void onMouseUp(MouseUpEvent event) {
  1028. if (mouseTimer != null) {
  1029. mouseTimer.cancel();
  1030. }
  1031. }
  1032. /**
  1033. * Sets the data of the Panel.
  1034. *
  1035. * @param currentDate
  1036. * The date to set
  1037. */
  1038. public void setDate(Date currentDate) {
  1039. // Check that we are not re-rendering a already active date if
  1040. if (currentDate == value && currentDate != null) {
  1041. return;
  1042. }
  1043. Date oldValue = value;
  1044. value = currentDate;
  1045. if (focusedDate == null && value != null) {
  1046. focusedDate = (Date) value.clone();
  1047. }
  1048. // Re-render calendar if the month or year has changed
  1049. if (oldValue == null || value == null
  1050. || oldValue.getYear() != value.getYear()
  1051. || oldValue.getMonth() != value.getMonth()) {
  1052. renderCalendar();
  1053. } else {
  1054. focusDay(currentDate.getDate());
  1055. selectFocused();
  1056. }
  1057. if (!hasFocus)
  1058. focusDay(-1);
  1059. }
  1060. /**
  1061. * TimeSelector is a widget consisting of list boxes that modifie the Date
  1062. * object that is given for.
  1063. *
  1064. */
  1065. public class VTime extends FlowPanel implements ChangeHandler {
  1066. private ListBox hours;
  1067. private ListBox mins;
  1068. private ListBox sec;
  1069. private ListBox msec;
  1070. private ListBox ampm;
  1071. private ListBox lastField;
  1072. /**
  1073. * Constructor
  1074. */
  1075. public VTime() {
  1076. super();
  1077. setStyleName(VDateField.CLASSNAME + "-time");
  1078. buildTime();
  1079. }
  1080. private ListBox createListBox() {
  1081. ListBox lb = new ListBox();
  1082. lb.setStyleName(VNativeSelect.CLASSNAME);
  1083. lb.addChangeHandler(this);
  1084. lb.addBlurHandler(VCalendarPanel.this);
  1085. lb.addFocusHandler(VCalendarPanel.this);
  1086. return lb;
  1087. }
  1088. /**
  1089. * Constructs the ListBoxes and updates their value
  1090. *
  1091. * @param redraw
  1092. * Should new instances of the listboxes be created
  1093. */
  1094. private void buildTime() {
  1095. clear();
  1096. hours = createListBox();
  1097. if (getDateTimeService().isTwelveHourClock()) {
  1098. hours.addItem("12");
  1099. for (int i = 1; i < 12; i++) {
  1100. hours.addItem((i < 10) ? "0" + i : "" + i);
  1101. }
  1102. } else {
  1103. for (int i = 0; i < 24; i++) {
  1104. hours.addItem((i < 10) ? "0" + i : "" + i);
  1105. }
  1106. }
  1107. hours.addChangeHandler(this);
  1108. if (getDateTimeService().isTwelveHourClock()) {
  1109. ampm = createListBox();
  1110. final String[] ampmText = getDateTimeService().getAmPmStrings();
  1111. ampm.addItem(ampmText[0]);
  1112. ampm.addItem(ampmText[1]);
  1113. ampm.addChangeHandler(this);
  1114. }
  1115. if (getResolution() >= VDateField.RESOLUTION_MIN) {
  1116. mins = createListBox();
  1117. for (int i = 0; i < 60; i++) {
  1118. mins.addItem((i < 10) ? "0" + i : "" + i);
  1119. }
  1120. mins.addChangeHandler(this);
  1121. }
  1122. if (getResolution() >= VDateField.RESOLUTION_SEC) {
  1123. sec = createListBox();
  1124. for (int i = 0; i < 60; i++) {
  1125. sec.addItem((i < 10) ? "0" + i : "" + i);
  1126. }
  1127. sec.addChangeHandler(this);
  1128. }
  1129. if (getResolution() == VDateField.RESOLUTION_MSEC) {
  1130. msec = createListBox();
  1131. for (int i = 0; i < 1000; i++) {
  1132. if (i < 10) {
  1133. msec.addItem("00" + i);
  1134. } else if (i < 100) {
  1135. msec.addItem("0" + i);
  1136. } else {
  1137. msec.addItem("" + i);
  1138. }
  1139. }
  1140. msec.addChangeHandler(this);
  1141. }
  1142. final String delimiter = getDateTimeService().getClockDelimeter();
  1143. if (isReadonly()) {
  1144. int h = 0;
  1145. if (value != null) {
  1146. h = value.getHours();
  1147. }
  1148. if (getDateTimeService().isTwelveHourClock()) {
  1149. h -= h < 12 ? 0 : 12;
  1150. }
  1151. add(new VLabel(h < 10 ? "0" + h : "" + h));
  1152. } else {
  1153. add(hours);
  1154. lastField = hours;
  1155. }
  1156. if (getResolution() >= VDateField.RESOLUTION_MIN) {
  1157. add(new VLabel(delimiter));
  1158. if (isReadonly()) {
  1159. final int m = mins.getSelectedIndex();
  1160. add(new VLabel(m < 10 ? "0" + m : "" + m));
  1161. } else {
  1162. add(mins);
  1163. lastField = mins;
  1164. }
  1165. }
  1166. if (getResolution() >= VDateField.RESOLUTION_SEC) {
  1167. add(new VLabel(delimiter));
  1168. if (isReadonly()) {
  1169. final int s = sec.getSelectedIndex();
  1170. add(new VLabel(s < 10 ? "0" + s : "" + s));
  1171. } else {
  1172. add(sec);
  1173. lastField = sec;
  1174. }
  1175. }
  1176. if (getResolution() == VDateField.RESOLUTION_MSEC) {
  1177. add(new VLabel("."));
  1178. if (isReadonly()) {
  1179. final int m = getMilliseconds();
  1180. final String ms = m < 100 ? "0" + m : "" + m;
  1181. add(new VLabel(m < 10 ? "0" + ms : ms));
  1182. } else {
  1183. add(msec);
  1184. lastField = msec;
  1185. }
  1186. }
  1187. if (getResolution() == VDateField.RESOLUTION_HOUR) {
  1188. add(new VLabel(delimiter + "00")); // o'clock
  1189. }
  1190. if (getDateTimeService().isTwelveHourClock()) {
  1191. add(new VLabel("&nbsp;"));
  1192. if (isReadonly()) {
  1193. int i = 0;
  1194. if (value != null) {
  1195. i = (value.getHours() < 12) ? 0 : 1;
  1196. }
  1197. add(new VLabel(ampm.getItemText(i)));
  1198. } else {
  1199. add(ampm);
  1200. lastField = ampm;
  1201. }
  1202. }
  1203. if (isReadonly()) {
  1204. return;
  1205. }
  1206. // Update times
  1207. updateTimes();
  1208. ListBox lastDropDown = (ListBox) getWidget(getWidgetCount() - 1);
  1209. lastDropDown.addKeyDownHandler(new KeyDownHandler() {
  1210. public void onKeyDown(KeyDownEvent event) {
  1211. boolean shiftKey = event.getNativeEvent().getShiftKey();
  1212. if (shiftKey) {
  1213. return;
  1214. } else {
  1215. int nativeKeyCode = event.getNativeKeyCode();
  1216. if (nativeKeyCode == KeyCodes.KEY_TAB) {
  1217. onTabOut(event);
  1218. }
  1219. }
  1220. }
  1221. });
  1222. }
  1223. /**
  1224. * Updates the valus to correspond to the values in value
  1225. */
  1226. public void updateTimes() {
  1227. boolean selected = true;
  1228. if (value == null) {
  1229. value = new Date();
  1230. selected = false;
  1231. }
  1232. if (getDateTimeService().isTwelveHourClock()) {
  1233. int h = value.getHours();
  1234. ampm.setSelectedIndex(h < 12 ? 0 : 1);
  1235. h -= ampm.getSelectedIndex() * 12;
  1236. hours.setSelectedIndex(h);
  1237. } else {
  1238. hours.setSelectedIndex(value.getHours());
  1239. }
  1240. if (getResolution() >= VDateField.RESOLUTION_MIN) {
  1241. mins.setSelectedIndex(value.getMinutes());
  1242. }
  1243. if (getResolution() >= VDateField.RESOLUTION_SEC) {
  1244. sec.setSelectedIndex(value.getSeconds());
  1245. }
  1246. if (getResolution() == VDateField.RESOLUTION_MSEC) {
  1247. if (selected) {
  1248. msec.setSelectedIndex(getMilliseconds());
  1249. } else {
  1250. msec.setSelectedIndex(0);
  1251. }
  1252. }
  1253. if (getDateTimeService().isTwelveHourClock()) {
  1254. ampm.setSelectedIndex(value.getHours() < 12 ? 0 : 1);
  1255. }
  1256. hours.setEnabled(isEnabled());
  1257. if (mins != null) {
  1258. mins.setEnabled(isEnabled());
  1259. }
  1260. if (sec != null) {
  1261. sec.setEnabled(isEnabled());
  1262. }
  1263. if (msec != null) {
  1264. msec.setEnabled(isEnabled());
  1265. }
  1266. if (ampm != null) {
  1267. ampm.setEnabled(isEnabled());
  1268. }
  1269. }
  1270. private int getMilliseconds() {
  1271. return DateTimeService.getMilliseconds(value);
  1272. }
  1273. private DateTimeService getDateTimeService() {
  1274. if (dateTimeService == null) {
  1275. dateTimeService = new DateTimeService();
  1276. }
  1277. return dateTimeService;
  1278. }
  1279. /*
  1280. * (non-Javadoc) VT
  1281. *
  1282. * @see
  1283. * com.google.gwt.event.dom.client.ChangeHandler#onChange(com.google.gwt
  1284. * .event.dom.client.ChangeEvent)
  1285. */
  1286. public void onChange(ChangeEvent event) {
  1287. /*
  1288. * Value from dropdowns gets always set for the value. Like year and
  1289. * month when resolution is month or year.
  1290. */
  1291. if (event.getSource() == hours) {
  1292. int h = hours.getSelectedIndex();
  1293. if (getDateTimeService().isTwelveHourClock()) {
  1294. h = h + ampm.getSelectedIndex() * 12;
  1295. }
  1296. value.setHours(h);
  1297. if (timeChangeListener != null) {
  1298. timeChangeListener.changed(h, value.getMinutes(),
  1299. value.getSeconds(),
  1300. DateTimeService.getMilliseconds(value));
  1301. }
  1302. event.preventDefault();
  1303. event.stopPropagation();
  1304. } else if (event.getSource() == mins) {
  1305. final int m = mins.getSelectedIndex();
  1306. value.setMinutes(m);
  1307. if (timeChangeListener != null) {
  1308. timeChangeListener.changed(value.getHours(), m,
  1309. value.getSeconds(),
  1310. DateTimeService.getMilliseconds(value));
  1311. }
  1312. event.preventDefault();
  1313. event.stopPropagation();
  1314. } else if (event.getSource() == sec) {
  1315. final int s = sec.getSelectedIndex();
  1316. value.setSeconds(s);
  1317. if (timeChangeListener != null) {
  1318. timeChangeListener.changed(value.getHours(),
  1319. value.getMinutes(), s,
  1320. DateTimeService.getMilliseconds(value));
  1321. }
  1322. event.preventDefault();
  1323. event.stopPropagation();
  1324. } else if (event.getSource() == msec) {
  1325. final int ms = msec.getSelectedIndex();
  1326. DateTimeService.setMilliseconds(value, ms);
  1327. if (timeChangeListener != null) {
  1328. timeChangeListener.changed(value.getHours(),
  1329. value.getMinutes(), value.getSeconds(), ms);
  1330. }
  1331. event.preventDefault();
  1332. event.stopPropagation();
  1333. } else if (event.getSource() == ampm) {
  1334. final int h = hours.getSelectedIndex()
  1335. + (ampm.getSelectedIndex() * 12);
  1336. value.setHours(h);
  1337. if (timeChangeListener != null) {
  1338. timeChangeListener.changed(h, value.getMinutes(),
  1339. value.getSeconds(),
  1340. DateTimeService.getMilliseconds(value));
  1341. }
  1342. event.preventDefault();
  1343. event.stopPropagation();
  1344. }
  1345. }
  1346. }
  1347. private class Day extends InlineHTML {
  1348. private static final String BASECLASS = VDateField.CLASSNAME
  1349. + "-calendarpanel-day";
  1350. private final int day;
  1351. Day(int dayOfMonth) {
  1352. super("" + dayOfMonth);
  1353. setStyleName(BASECLASS);
  1354. day = dayOfMonth;
  1355. addClickHandler(dayClickHandler);
  1356. }
  1357. public int getDay() {
  1358. return day;
  1359. }
  1360. }
  1361. public Date getDate() {
  1362. return value;
  1363. }
  1364. /**
  1365. * If true should be returned if the panel will not be used after this
  1366. * event.
  1367. *
  1368. * @param event
  1369. * @return
  1370. */
  1371. protected boolean onTabOut(DomEvent event) {
  1372. if (focusOutListener != null) {
  1373. return focusOutListener.onFocusOut(event);
  1374. }
  1375. return false;
  1376. }
  1377. /**
  1378. * A focus out listener is triggered when the panel loosed focus. This can
  1379. * happen either after a user clicks outside the panel or tabs out.
  1380. *
  1381. * @param listener
  1382. * The listener to trigger
  1383. */
  1384. public void setFocusOutListener(FocusOutListener listener) {
  1385. focusOutListener = listener;
  1386. }
  1387. /**
  1388. * The submit listener is called when the user selects a value from the
  1389. * calender either by clicking the day or selects it by keyboard.
  1390. *
  1391. * @param submitListener
  1392. * The listener to trigger
  1393. */
  1394. public void setSubmitListener(SubmitListener submitListener) {
  1395. this.submitListener = submitListener;
  1396. }
  1397. /**
  1398. * The value change listener is triggered when the focused date changes by
  1399. * user either clicking on a new date or by using the keyboard.
  1400. *
  1401. * @param listener
  1402. * The listener to trigger
  1403. */
  1404. public void setValueChangeListener(ValueChangeListener listener) {
  1405. this.valueChangeListener = listener;
  1406. }
  1407. /**
  1408. * The time change listener is triggered when the user changes the time.
  1409. *
  1410. * @param listener
  1411. */
  1412. public void setTimeChangeListener(TimeChangeListener listener) {
  1413. this.timeChangeListener = listener;
  1414. }
  1415. /**
  1416. * Returns the submit listener that listens to selection made from the panel
  1417. *
  1418. * @return The listener or NULL if no listener has been set
  1419. */
  1420. public SubmitListener getSubmitListener() {
  1421. return submitListener;
  1422. }
  1423. /*
  1424. * (non-Javadoc)
  1425. *
  1426. * @see
  1427. * com.google.gwt.event.dom.client.BlurHandler#onBlur(com.google.gwt.event
  1428. * .dom.client.BlurEvent)
  1429. */
  1430. public void onBlur(final BlurEvent event) {
  1431. if (isAttached()) {
  1432. DeferredCommand.addCommand(new Command() {
  1433. public void execute() {
  1434. if (!hasFocus) {
  1435. onTabOut(event);
  1436. }
  1437. }
  1438. });
  1439. }
  1440. if (event.getSource() instanceof VCalendarPanel) {
  1441. hasFocus = false;
  1442. focusDay(-1);
  1443. }
  1444. }
  1445. /*
  1446. * (non-Javadoc)
  1447. *
  1448. * @see
  1449. * com.google.gwt.event.dom.client.FocusHandler#onFocus(com.google.gwt.event
  1450. * .dom.client.FocusEvent)
  1451. */
  1452. public void onFocus(FocusEvent event) {
  1453. if (event.getSource() instanceof VCalendarPanel) {
  1454. hasFocus = true;
  1455. // Focuses the current day if the calendar shows the days
  1456. focusDay(focusedDay.getDay());
  1457. }
  1458. }
  1459. }