2 * Jexer - Java Text User Interface
4 * The MIT License (MIT)
6 * Copyright (C) 2019 Kevin Lamonte
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
26 * @author Kevin Lamonte [kevin.lamonte@gmail.com]
31 import java
.util
.Calendar
;
32 import java
.util
.GregorianCalendar
;
34 import jexer
.bits
.CellAttributes
;
35 import jexer
.bits
.GraphicsChars
;
36 import jexer
.bits
.StringUtils
;
37 import jexer
.event
.TKeypressEvent
;
38 import jexer
.event
.TMouseEvent
;
39 import static jexer
.TKeypress
.*;
42 * TCalendar is a date picker widget.
44 public class TCalendar
extends TWidget
{
46 // ------------------------------------------------------------------------
47 // Variables --------------------------------------------------------------
48 // ------------------------------------------------------------------------
51 * The calendar being displayed.
53 private GregorianCalendar displayCalendar
= new GregorianCalendar();
56 * The calendar with the selected day.
58 private GregorianCalendar calendar
= new GregorianCalendar();
61 * The action to perform when the user changes the value of the calendar.
63 private TAction updateAction
= null;
65 // ------------------------------------------------------------------------
66 // Constructors -----------------------------------------------------------
67 // ------------------------------------------------------------------------
72 * @param parent parent widget
73 * @param x column relative to parent
74 * @param y row relative to parent
75 * @param updateAction action to call when the user changes the value of
78 public TCalendar(final TWidget parent
, final int x
, final int y
,
79 final TAction updateAction
) {
81 // Set parent and window
82 super(parent
, x
, y
, 28, 8);
84 this.updateAction
= updateAction
;
87 // ------------------------------------------------------------------------
88 // Event handlers ---------------------------------------------------------
89 // ------------------------------------------------------------------------
92 * Returns true if the mouse is currently on the left arrow.
94 * @param mouse mouse event
95 * @return true if the mouse is currently on the left arrow
97 private boolean mouseOnLeftArrow(final TMouseEvent mouse
) {
98 if ((mouse
.getY() == 0)
99 && (mouse
.getX() == 1)
107 * Returns true if the mouse is currently on the right arrow.
109 * @param mouse mouse event
110 * @return true if the mouse is currently on the right arrow
112 private boolean mouseOnRightArrow(final TMouseEvent mouse
) {
113 if ((mouse
.getY() == 0)
114 && (mouse
.getX() == getWidth() - 2)
122 * Handle mouse down clicks.
124 * @param mouse mouse button down event
127 public void onMouseDown(final TMouseEvent mouse
) {
128 if ((mouseOnLeftArrow(mouse
)) && (mouse
.isMouse1())) {
129 displayCalendar
.add(Calendar
.MONTH
, -1);
130 } else if ((mouseOnRightArrow(mouse
)) && (mouse
.isMouse1())) {
131 displayCalendar
.add(Calendar
.MONTH
, 1);
132 } else if (mouse
.isMouse1()) {
133 // Find the day this might correspond to, and set it.
134 int index
= (mouse
.getY() - 2) * 7 + (mouse
.getX() / 4) + 1;
135 // System.err.println("index: " + index);
137 int lastDayNumber
= displayCalendar
.getActualMaximum(
138 Calendar
.DAY_OF_MONTH
);
139 GregorianCalendar firstOfMonth
= new GregorianCalendar();
140 firstOfMonth
.setTimeInMillis(displayCalendar
.getTimeInMillis());
141 firstOfMonth
.set(Calendar
.DAY_OF_MONTH
, 1);
142 int dayOf1st
= firstOfMonth
.get(Calendar
.DAY_OF_WEEK
) - 1;
143 // System.err.println("dayOf1st: " + dayOf1st);
145 int day
= index
- dayOf1st
;
146 // System.err.println("day: " + day);
148 if ((day
< 1) || (day
> lastDayNumber
)) {
151 calendar
.setTimeInMillis(displayCalendar
.getTimeInMillis());
152 calendar
.set(Calendar
.DAY_OF_MONTH
, day
);
157 * Handle mouse double click.
159 * @param mouse mouse double click event
162 public void onMouseDoubleClick(final TMouseEvent mouse
) {
163 if (updateAction
!= null) {
164 updateAction
.DO(this);
171 * @param keypress keystroke event
174 public void onKeypress(final TKeypressEvent keypress
) {
177 if (keypress
.equals(kbUp
)) {
179 } else if (keypress
.equals(kbDown
)) {
181 } else if (keypress
.equals(kbLeft
)) {
183 } else if (keypress
.equals(kbRight
)) {
185 } else if (keypress
.equals(kbEnter
)) {
186 if (updateAction
!= null) {
187 updateAction
.DO(this);
191 // Pass to parent for the things we don't care about.
192 super.onKeypress(keypress
);
196 if (increment
!= 0) {
197 calendar
.add(Calendar
.DAY_OF_YEAR
, increment
);
199 if ((displayCalendar
.get(Calendar
.MONTH
) != calendar
.get(
201 || (displayCalendar
.get(Calendar
.YEAR
) != calendar
.get(
205 displayCalendar
.add(Calendar
.MONTH
, -1);
207 displayCalendar
.add(Calendar
.MONTH
, 1);
214 // ------------------------------------------------------------------------
215 // TWidget ----------------------------------------------------------------
216 // ------------------------------------------------------------------------
219 * Draw the combobox down arrow.
223 CellAttributes backgroundColor
= getTheme().getColor(
224 "tcalendar.background");
225 CellAttributes dayColor
= getTheme().getColor(
227 CellAttributes selectedDayColor
= getTheme().getColor(
228 "tcalendar.day.selected");
229 CellAttributes arrowColor
= getTheme().getColor(
231 CellAttributes titleColor
= getTheme().getColor(
234 // Fill in the interior background
235 for (int i
= 0; i
< getHeight(); i
++) {
236 hLineXY(0, i
, getWidth(), ' ', backgroundColor
);
240 String title
= String
.format("%tB %tY", displayCalendar
,
242 // This particular title is always single-width (see format string
243 // above), but for completeness let's treat it the same as every
244 // other window title string.
245 int titleLeft
= (getWidth() - StringUtils
.width(title
) - 2) / 2;
246 putCharXY(titleLeft
, 0, ' ', titleColor
);
247 putStringXY(titleLeft
+ 1, 0, title
, titleColor
);
248 putCharXY(titleLeft
+ StringUtils
.width(title
) + 1, 0, ' ',
252 putCharXY(1, 0, GraphicsChars
.LEFTARROW
, arrowColor
);
253 putCharXY(getWidth() - 2, 0, GraphicsChars
.RIGHTARROW
,
257 * Now draw out the days.
259 putStringXY(0, 1, " S M T W T F S ", dayColor
);
260 int lastDayNumber
= displayCalendar
.getActualMaximum(
261 Calendar
.DAY_OF_MONTH
);
262 GregorianCalendar firstOfMonth
= new GregorianCalendar();
263 firstOfMonth
.setTimeInMillis(displayCalendar
.getTimeInMillis());
264 firstOfMonth
.set(Calendar
.DAY_OF_MONTH
, 1);
265 int dayOf1st
= firstOfMonth
.get(Calendar
.DAY_OF_WEEK
) - 1;
266 int dayColumn
= dayOf1st
* 4;
270 while (dayOfMonth
<= lastDayNumber
) {
271 if (dayColumn
== 4 * 7) {
275 if ((dayOfMonth
== calendar
.get(Calendar
.DAY_OF_MONTH
))
276 && (displayCalendar
.get(Calendar
.MONTH
) == calendar
.get(
278 && (displayCalendar
.get(Calendar
.YEAR
) == calendar
.get(
281 putStringXY(dayColumn
, row
,
282 String
.format(" %2d ", dayOfMonth
), selectedDayColor
);
284 putStringXY(dayColumn
, row
,
285 String
.format(" %2d ", dayOfMonth
), dayColor
);
293 // ------------------------------------------------------------------------
294 // TCalendar --------------------------------------------------------------
295 // ------------------------------------------------------------------------
298 * Get calendar value.
300 * @return the current calendar value (clone instance)
302 public Calendar
getValue() {
303 return (Calendar
) calendar
.clone();
307 * Set calendar value.
309 * @param calendar the new value to use
311 public final void setValue(final Calendar calendar
) {
312 this.calendar
.setTimeInMillis(calendar
.getTimeInMillis());
316 * Set calendar value.
318 * @param millis the millis to set to
320 public final void setValue(final long millis
) {
321 this.calendar
.setTimeInMillis(millis
);