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