Commit | Line | Data |
---|---|---|
051e2913 KL |
1 | /* |
2 | * Jexer - Java Text User Interface | |
3 | * | |
4 | * The MIT License (MIT) | |
5 | * | |
a69ed767 | 6 | * Copyright (C) 2019 Kevin Lamonte |
051e2913 KL |
7 | * |
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: | |
14 | * | |
15 | * The above copyright notice and this permission notice shall be included in | |
16 | * all copies or substantial portions of the Software. | |
17 | * | |
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. | |
25 | * | |
26 | * @author Kevin Lamonte [kevin.lamonte@gmail.com] | |
27 | * @version 1 | |
28 | */ | |
29 | package jexer; | |
30 | ||
31 | import java.util.Calendar; | |
32 | import java.util.GregorianCalendar; | |
33 | ||
34 | import jexer.bits.CellAttributes; | |
35 | import jexer.bits.GraphicsChars; | |
36 | import jexer.event.TKeypressEvent; | |
37 | import jexer.event.TMouseEvent; | |
38 | import static jexer.TKeypress.*; | |
39 | ||
40 | /** | |
41 | * TCalendar is a date picker widget. | |
42 | */ | |
43 | public class TCalendar extends TWidget { | |
44 | ||
45 | // ------------------------------------------------------------------------ | |
46 | // Variables -------------------------------------------------------------- | |
47 | // ------------------------------------------------------------------------ | |
48 | ||
49 | /** | |
50 | * The calendar being displayed. | |
51 | */ | |
52 | private GregorianCalendar displayCalendar = new GregorianCalendar(); | |
53 | ||
54 | /** | |
55 | * The calendar with the selected day. | |
56 | */ | |
57 | private GregorianCalendar calendar = new GregorianCalendar(); | |
58 | ||
59 | /** | |
60 | * The action to perform when the user changes the value of the calendar. | |
61 | */ | |
62 | private TAction updateAction = null; | |
63 | ||
64 | // ------------------------------------------------------------------------ | |
65 | // Constructors ----------------------------------------------------------- | |
66 | // ------------------------------------------------------------------------ | |
67 | ||
68 | /** | |
69 | * Public constructor. | |
70 | * | |
71 | * @param parent parent widget | |
72 | * @param x column relative to parent | |
73 | * @param y row relative to parent | |
74 | * @param updateAction action to call when the user changes the value of | |
75 | * the calendar | |
76 | */ | |
77 | public TCalendar(final TWidget parent, final int x, final int y, | |
78 | final TAction updateAction) { | |
79 | ||
80 | // Set parent and window | |
81 | super(parent, x, y, 28, 8); | |
82 | ||
83 | this.updateAction = updateAction; | |
84 | } | |
85 | ||
86 | // ------------------------------------------------------------------------ | |
87 | // Event handlers --------------------------------------------------------- | |
88 | // ------------------------------------------------------------------------ | |
89 | ||
90 | /** | |
91 | * Returns true if the mouse is currently on the left arrow. | |
92 | * | |
93 | * @param mouse mouse event | |
94 | * @return true if the mouse is currently on the left arrow | |
95 | */ | |
96 | private boolean mouseOnLeftArrow(final TMouseEvent mouse) { | |
97 | if ((mouse.getY() == 0) | |
98 | && (mouse.getX() == 1) | |
99 | ) { | |
100 | return true; | |
101 | } | |
102 | return false; | |
103 | } | |
104 | ||
105 | /** | |
106 | * Returns true if the mouse is currently on the right arrow. | |
107 | * | |
108 | * @param mouse mouse event | |
109 | * @return true if the mouse is currently on the right arrow | |
110 | */ | |
111 | private boolean mouseOnRightArrow(final TMouseEvent mouse) { | |
112 | if ((mouse.getY() == 0) | |
113 | && (mouse.getX() == getWidth() - 2) | |
114 | ) { | |
115 | return true; | |
116 | } | |
117 | return false; | |
118 | } | |
119 | ||
120 | /** | |
121 | * Handle mouse down clicks. | |
122 | * | |
123 | * @param mouse mouse button down event | |
124 | */ | |
125 | @Override | |
126 | public void onMouseDown(final TMouseEvent mouse) { | |
127 | if ((mouseOnLeftArrow(mouse)) && (mouse.isMouse1())) { | |
128 | displayCalendar.add(Calendar.MONTH, -1); | |
129 | } else if ((mouseOnRightArrow(mouse)) && (mouse.isMouse1())) { | |
130 | displayCalendar.add(Calendar.MONTH, 1); | |
131 | } else if (mouse.isMouse1()) { | |
132 | // Find the day this might correspond to, and set it. | |
133 | int index = (mouse.getY() - 2) * 7 + (mouse.getX() / 4) + 1; | |
134 | // System.err.println("index: " + index); | |
135 | ||
136 | int lastDayNumber = displayCalendar.getActualMaximum( | |
137 | Calendar.DAY_OF_MONTH); | |
138 | GregorianCalendar firstOfMonth = new GregorianCalendar(); | |
139 | firstOfMonth.setTimeInMillis(displayCalendar.getTimeInMillis()); | |
140 | firstOfMonth.set(Calendar.DAY_OF_MONTH, 1); | |
141 | int dayOf1st = firstOfMonth.get(Calendar.DAY_OF_WEEK) - 1; | |
142 | // System.err.println("dayOf1st: " + dayOf1st); | |
143 | ||
144 | int day = index - dayOf1st; | |
145 | // System.err.println("day: " + day); | |
146 | ||
147 | if ((day < 1) || (day > lastDayNumber)) { | |
148 | return; | |
149 | } | |
150 | calendar.setTimeInMillis(displayCalendar.getTimeInMillis()); | |
151 | calendar.set(Calendar.DAY_OF_MONTH, day); | |
152 | } | |
153 | } | |
154 | ||
155 | /** | |
156 | * Handle mouse double click. | |
157 | * | |
158 | * @param mouse mouse double click event | |
159 | */ | |
160 | @Override | |
161 | public void onMouseDoubleClick(final TMouseEvent mouse) { | |
162 | if (updateAction != null) { | |
163 | updateAction.DO(); | |
164 | } | |
165 | } | |
166 | ||
167 | /** | |
168 | * Handle keystrokes. | |
169 | * | |
170 | * @param keypress keystroke event | |
171 | */ | |
172 | @Override | |
173 | public void onKeypress(final TKeypressEvent keypress) { | |
174 | int increment = 0; | |
175 | ||
176 | if (keypress.equals(kbUp)) { | |
177 | increment = -7; | |
178 | } else if (keypress.equals(kbDown)) { | |
179 | increment = 7; | |
180 | } else if (keypress.equals(kbLeft)) { | |
181 | increment = -1; | |
182 | } else if (keypress.equals(kbRight)) { | |
183 | increment = 1; | |
184 | } else if (keypress.equals(kbEnter)) { | |
185 | if (updateAction != null) { | |
186 | updateAction.DO(); | |
187 | } | |
188 | return; | |
189 | } else { | |
190 | // Pass to parent for the things we don't care about. | |
191 | super.onKeypress(keypress); | |
192 | return; | |
193 | } | |
194 | ||
195 | if (increment != 0) { | |
196 | calendar.add(Calendar.DAY_OF_YEAR, increment); | |
197 | ||
198 | if ((displayCalendar.get(Calendar.MONTH) != calendar.get( | |
199 | Calendar.MONTH)) | |
200 | || (displayCalendar.get(Calendar.YEAR) != calendar.get( | |
201 | Calendar.YEAR)) | |
202 | ) { | |
203 | if (increment < 0) { | |
204 | displayCalendar.add(Calendar.MONTH, -1); | |
205 | } else { | |
206 | displayCalendar.add(Calendar.MONTH, 1); | |
207 | } | |
208 | } | |
209 | } | |
210 | ||
211 | } | |
212 | ||
213 | // ------------------------------------------------------------------------ | |
214 | // TWidget ---------------------------------------------------------------- | |
215 | // ------------------------------------------------------------------------ | |
216 | ||
217 | /** | |
218 | * Draw the combobox down arrow. | |
219 | */ | |
220 | @Override | |
221 | public void draw() { | |
222 | CellAttributes backgroundColor = getTheme().getColor( | |
223 | "tcalendar.background"); | |
224 | CellAttributes dayColor = getTheme().getColor( | |
225 | "tcalendar.day"); | |
226 | CellAttributes selectedDayColor = getTheme().getColor( | |
227 | "tcalendar.day.selected"); | |
228 | CellAttributes arrowColor = getTheme().getColor( | |
229 | "tcalendar.arrow"); | |
230 | CellAttributes titleColor = getTheme().getColor( | |
231 | "tcalendar.title"); | |
232 | ||
233 | // Fill in the interior background | |
234 | for (int i = 0; i < getHeight(); i++) { | |
a69ed767 | 235 | hLineXY(0, i, getWidth(), ' ', backgroundColor); |
051e2913 KL |
236 | } |
237 | ||
238 | // Draw the title | |
239 | String title = String.format("%tB %tY", displayCalendar, | |
240 | displayCalendar); | |
241 | int titleLeft = (getWidth() - title.length() - 2) / 2; | |
a69ed767 KL |
242 | putCharXY(titleLeft, 0, ' ', titleColor); |
243 | putStringXY(titleLeft + 1, 0, title, titleColor); | |
244 | putCharXY(titleLeft + title.length() + 1, 0, ' ', | |
051e2913 KL |
245 | titleColor); |
246 | ||
247 | // Arrows | |
a69ed767 KL |
248 | putCharXY(1, 0, GraphicsChars.LEFTARROW, arrowColor); |
249 | putCharXY(getWidth() - 2, 0, GraphicsChars.RIGHTARROW, | |
051e2913 KL |
250 | arrowColor); |
251 | ||
252 | /* | |
253 | * Now draw out the days. | |
254 | */ | |
a69ed767 | 255 | putStringXY(0, 1, " S M T W T F S ", dayColor); |
051e2913 KL |
256 | int lastDayNumber = displayCalendar.getActualMaximum( |
257 | Calendar.DAY_OF_MONTH); | |
258 | GregorianCalendar firstOfMonth = new GregorianCalendar(); | |
259 | firstOfMonth.setTimeInMillis(displayCalendar.getTimeInMillis()); | |
260 | firstOfMonth.set(Calendar.DAY_OF_MONTH, 1); | |
261 | int dayOf1st = firstOfMonth.get(Calendar.DAY_OF_WEEK) - 1; | |
262 | int dayColumn = dayOf1st * 4; | |
263 | int row = 2; | |
264 | ||
265 | int dayOfMonth = 1; | |
266 | while (dayOfMonth <= lastDayNumber) { | |
267 | if (dayColumn == 4 * 7) { | |
268 | dayColumn = 0; | |
269 | row++; | |
270 | } | |
271 | if ((dayOfMonth == calendar.get(Calendar.DAY_OF_MONTH)) | |
272 | && (displayCalendar.get(Calendar.MONTH) == calendar.get( | |
273 | Calendar.MONTH)) | |
274 | && (displayCalendar.get(Calendar.YEAR) == calendar.get( | |
275 | Calendar.YEAR)) | |
276 | ) { | |
a69ed767 | 277 | putStringXY(dayColumn, row, |
051e2913 KL |
278 | String.format(" %2d ", dayOfMonth), selectedDayColor); |
279 | } else { | |
a69ed767 | 280 | putStringXY(dayColumn, row, |
051e2913 KL |
281 | String.format(" %2d ", dayOfMonth), dayColor); |
282 | } | |
283 | dayColumn += 4; | |
284 | dayOfMonth++; | |
285 | } | |
286 | ||
287 | } | |
288 | ||
289 | // ------------------------------------------------------------------------ | |
290 | // TCalendar -------------------------------------------------------------- | |
291 | // ------------------------------------------------------------------------ | |
292 | ||
293 | /** | |
294 | * Get calendar value. | |
295 | * | |
296 | * @return the current calendar value (clone instance) | |
297 | */ | |
298 | public Calendar getValue() { | |
299 | return (Calendar) calendar.clone(); | |
300 | } | |
301 | ||
302 | /** | |
303 | * Set calendar value. | |
304 | * | |
305 | * @param calendar the new value to use | |
306 | */ | |
307 | public final void setValue(final Calendar calendar) { | |
308 | this.calendar.setTimeInMillis(calendar.getTimeInMillis()); | |
309 | } | |
310 | ||
311 | /** | |
312 | * Set calendar value. | |
313 | * | |
314 | * @param millis the millis to set to | |
315 | */ | |
316 | public final void setValue(final long millis) { | |
317 | this.calendar.setTimeInMillis(millis); | |
318 | } | |
319 | ||
320 | } |