1 /********************************************************************************
2 * *
3 * B a s e C a l e n d a r W i d g e t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 2006,2020 by Sander Jansen. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as published by *
10 * the Free Software Foundation; either version 3 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this program. If not, see <http://www.gnu.org/licenses/> *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "fxascii.h"
26 #include "fxkeys.h"
27 #include "FXArray.h"
28 #include "FXHash.h"
29 #include "FXMutex.h"
30 #include "FXStream.h"
31 #include "FXString.h"
32 #include "FXColors.h"
33 #include "FXSize.h"
34 #include "FXPoint.h"
35 #include "FXObjectList.h"
36 #include "FXRectangle.h"
37 #include "FXStringDictionary.h"
38 #include "FXSettings.h"
39 #include "FXRegistry.h"
40 #include "FXEvent.h"
41 #include "FXWindow.h"
42 #include "FXDCWindow.h"
43 #include "FXApp.h"
44 #include "FXFont.h"
45 #include "FXDate.h"
46 #include "FXCalendarView.h"
47
48 /*
49 Notes:
50
51 - ISO8601 weeknumbers are implemented for weeks starting at monday and ending at sunday
52 */
53
54 #define SELECT_MASK (CALENDAR_SINGLESELECT|CALENDAR_BROWSESELECT)
55 #define CALENDAR_MASK (CALENDAR_SINGLESELECT|CALENDAR_BROWSESELECT|CALENDAR_WEEKNUMBERS|CALENDAR_STATIC|CALENDAR_HIDEOTHER)
56
57 using namespace FX;
58
59 /*******************************************************************************/
60
61 namespace FX {
62
63
64 // Map
65 FXDEFMAP(FXCalendarView) FXCalendarViewMap[]={
66 FXMAPFUNC(SEL_PAINT,0,FXCalendarView::onPaint),
67 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXCalendarView::onLeftBtnPress),
68 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXCalendarView::onLeftBtnRelease),
69 FXMAPFUNC(SEL_KEYPRESS,0,FXCalendarView::onKeyPress),
70 FXMAPFUNC(SEL_FOCUSIN,0,FXCalendarView::onFocusIn),
71 FXMAPFUNC(SEL_FOCUSOUT,0,FXCalendarView::onFocusOut),
72 FXMAPFUNC(SEL_CLICKED,0,FXCalendarView::onClicked),
73 FXMAPFUNC(SEL_DOUBLECLICKED,0,FXCalendarView::onDoubleClicked),
74 FXMAPFUNC(SEL_TRIPLECLICKED,0,FXCalendarView::onTripleClicked),
75 FXMAPFUNC(SEL_COMMAND,0,FXCalendarView::onCommand),
76 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETVALUE,FXCalendarView::onCmdSetValue),
77 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETINTVALUE,FXCalendarView::onCmdSetIntValue),
78 FXMAPFUNC(SEL_COMMAND,FXWindow::ID_GETINTVALUE,FXCalendarView::onCmdGetIntValue),
79 };
80
81
82 // Implementation
FXIMPLEMENT(FXCalendarView,FXWindow,FXCalendarViewMap,ARRAYNUMBER (FXCalendarViewMap))83 FXIMPLEMENT(FXCalendarView,FXWindow,FXCalendarViewMap,ARRAYNUMBER(FXCalendarViewMap))
84
85
86 // For serialization
87 FXCalendarView::FXCalendarView(){
88 flags|=FLAG_SHOWN|FLAG_ENABLED;
89 font=(FXFont*)-1L;
90 }
91
92
93 // Construct and initialize
FXCalendarView(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h)94 FXCalendarView::FXCalendarView(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXWindow(p,opts,x,y,w,h){
95 flags|=FLAG_SHOWN|FLAG_ENABLED;
96 font=getApp()->getNormalFont();
97 target=tgt;
98 message=sel;
99 has_selection=false;
100 selected=current=FXDate::localDate();
101 firstday=FXDate::Sun; // Sunday
102
103 // Setup Default Colors
104 todayColor=FXRGB(0,180,0);
105 titleColor=getApp()->getBackColor();
106 titleBackColor=getApp()->getShadowColor();
107 dayColor=getApp()->getForeColor();
108 otherDayColor=getApp()->getShadowColor();
109 weekendColor=FXRGB(180,0,0);
110 otherWeekendColor=makeHiliteColor(FXRGB(200,100,100));
111 backColor=getApp()->getBackColor();
112
113 // Update for date
114 updateview(false);
115 }
116
117
118 // Create X window
create()119 void FXCalendarView::create(){
120 FXWindow::create();
121 font->create();
122 }
123
124
125 // Detach window
detach()126 void FXCalendarView::detach(){
127 FXWindow::detach();
128 font->detach();
129 }
130
131
132 // Enable the window
enable()133 void FXCalendarView::enable(){
134 if(!(flags&FLAG_ENABLED)){
135 FXWindow::enable();
136 update();
137 }
138 }
139
140
141 // Disable the window
disable()142 void FXCalendarView::disable(){
143 if(flags&FLAG_ENABLED){
144 FXWindow::disable();
145 update();
146 }
147 }
148
149
150 // Set the Calendar Style
setCalendarStyle(FXuint style)151 void FXCalendarView::setCalendarStyle(FXuint style){
152 FXuint opts=((style^options)&CALENDAR_MASK)^options;
153 if(options!=opts){
154 options=opts;
155 recalc();
156 layout();
157 update();
158 }
159 }
160
161
162 // Get the Calendar Style
getCalendarStyle() const163 FXuint FXCalendarView::getCalendarStyle() const{
164 return (options&CALENDAR_MASK);
165 }
166
167
168 // Set the display color of titles
setTitleColor(FXColor clr)169 void FXCalendarView::setTitleColor(FXColor clr){
170 if(titleColor!=clr){
171 titleColor=clr;
172 update();
173 }
174 }
175
176
177 // Set the display background color of titles
setTitleBackColor(FXColor clr)178 void FXCalendarView::setTitleBackColor(FXColor clr){
179 if(titleBackColor!=clr){
180 titleBackColor=clr;
181 update();
182 }
183 }
184
185
186 // Set the display color of non-weekend days
setDayColor(FXColor clr)187 void FXCalendarView::setDayColor(FXColor clr){
188 if(dayColor!=clr){
189 dayColor=clr;
190 update();
191 }
192 }
193
194
195 // Set the display color of non-weekend days not in the current month
setOtherDayColor(FXColor clr)196 void FXCalendarView::setOtherDayColor(FXColor clr){
197 if(otherDayColor!=clr){
198 otherDayColor=clr;
199 update();
200 }
201 }
202
203
204 // Set the display color of today
setTodayColor(FXColor clr)205 void FXCalendarView::setTodayColor(FXColor clr){
206 if(todayColor!=clr){
207 todayColor=clr;
208 update();
209 }
210 }
211
212
213 // Set the display color of days in the weekend
setWeekendColor(FXColor clr)214 void FXCalendarView::setWeekendColor(FXColor clr){
215 if(weekendColor!=clr){
216 weekendColor=clr;
217 update();
218 }
219 }
220
221
222 // Set the display color of days in the weekend not in the current month
setOtherWeekendColor(FXColor clr)223 void FXCalendarView::setOtherWeekendColor(FXColor clr){
224 if(otherWeekendColor!=clr){
225 otherWeekendColor=clr;
226 update();
227 }
228 }
229
230
231 // Compute default width
getDefaultWidth()232 FXint FXCalendarView::getDefaultWidth(){
233 FXint cw=0,tw,i;
234 FXString text;
235 for(i=0; i<7; i++){
236 text=tr(FXDate::dayNameShort(i));
237 tw=font->getTextWidth(text);
238 if(tw>cw) cw=tw;
239 }
240 if(options&CALENDAR_WEEKNUMBERS){
241 text=tr("Wk");
242 tw=font->getTextWidth(text);
243 if(tw>cw) cw=tw;
244 return (cw+4)*8;
245 }
246 return (cw+4)*7;
247 }
248
249
250 // Compute default height
getDefaultHeight()251 FXint FXCalendarView::getDefaultHeight(){
252 return (font->getFontHeight()+4)*7;
253 }
254
255
256 // Into focus chain
setFocus()257 void FXCalendarView::setFocus(){
258 FXWindow::setFocus();
259 setDefault(true);
260 flags&=~FLAG_UPDATE;
261 }
262
263
264 // Out of focus chain
killFocus()265 void FXCalendarView::killFocus(){
266 FXWindow::killFocus();
267 setDefault(maybe);
268 flags|=FLAG_UPDATE;
269 }
270
271
272 // Set focus on given date
moveFocus(FXDate f)273 void FXCalendarView::moveFocus(FXDate f){
274 if((options&CALENDAR_STATIC) && f.month()!=month) return;
275 setCurrentDate(f,true);
276 }
277
278
279 // Get date at x,y if any
getDateAt(FXint x,FXint y,FXDate & date) const280 FXbool FXCalendarView::getDateAt(FXint x,FXint y,FXDate& date) const {
281 FXint ncols=(options&CALENDAR_WEEKNUMBERS)?8:7;
282 FXint nrows=7;
283 FXint cw=width/ncols;
284 FXint ch=height/nrows;
285 FXint cwr=width%ncols;
286 FXint chr=height%nrows;
287 FXint col=0,row=0,xx=0,yy=0;
288 FXint columnwidth[8];
289 FXint rowheight[7];
290 FXint i,e;
291
292 // Calculate column widths
293 for(i=e=0; i<ncols; i++){
294 columnwidth[i]=cw;
295 e+=cwr;
296 if(e>=ncols){ columnwidth[i]++; e-=ncols; }
297 }
298
299 // Calculate row heights
300 for(i=e=0; i<nrows; i++){
301 rowheight[i]=ch;
302 e+=chr;
303 if(e>=nrows){ rowheight[i]++; e-=nrows; }
304 }
305
306 // Filter out obvious cases (clicked on headers)
307 if(y<rowheight[0]) return false;
308 if(options&CALENDAR_WEEKNUMBERS && x<columnwidth[0]) return false;
309
310 // Determine where we clicked
311 while(xx<x && col<ncols) xx+=columnwidth[col++];
312 while(yy<y && row<nrows) yy+=rowheight[row++];
313 if(options&CALENDAR_WEEKNUMBERS) col-=1;
314 col-=1;
315 row-=2;
316 date=ds+(row*7)+col;
317 return true;
318 }
319
320
321 // Redraw widget
onPaint(FXObject *,FXSelector,void * ptr)322 long FXCalendarView::onPaint(FXObject*,FXSelector,void* ptr){
323 FXDate now=FXDate::localDate();
324 FXint coloffset=(options&CALENDAR_WEEKNUMBERS)?1:0;
325 FXbool displayselected=(has_selection || ((options&SELECT_MASK)==CALENDAR_BROWSESELECT));
326 FXint ncols=(options&CALENDAR_WEEKNUMBERS)?8:7;
327 FXint nrows=7;
328 FXint cw=width/ncols;
329 FXint ch=height/nrows;
330 FXint cwr=width%ncols;
331 FXint chr=height%nrows;
332 FXint textwidth=font->getTextWidth("88",2);
333 FXint fontheight=font->getFontHeight();
334 FXint i=0,c=0,r=0,e=0;
335 FXint xx=0,yy=0;
336 FXint columnwidth[8]; // Width of each column
337 FXint columnoffset[8]; // Text Offset in each column
338 FXint rowheight[7]; // Height of each column
339 FXString text;
340 FXDate pd=ds;
341
342 // The acual painting
343 FXDCWindow dc(this,(FXEvent*)ptr);
344
345 // Calculate column widths
346 for(i=0,e=0; i<ncols; i++){
347 columnwidth[i]=cw;
348 e+=cwr;
349 if(e>=ncols){ columnwidth[i]++; e-=ncols; }
350 columnoffset[i]=(columnwidth[i]-textwidth)/2;
351 }
352
353 // Calculate row heights
354 for(i=0,e=0; i<nrows; i++){
355 rowheight[i]=ch;
356 e+=chr;
357 if(e>=nrows){ rowheight[i]++; e-=nrows; }
358 }
359
360 // Paint backgrounds
361 if(isEnabled()){
362 dc.setForeground(backColor);
363 }
364 else{
365 dc.setForeground(getApp()->getBaseColor());
366 }
367
368 dc.fillRectangle(0,rowheight[0],width,height-rowheight[0]);
369 dc.setForeground(titleBackColor);
370 dc.fillRectangle(0,0,width,rowheight[0]);
371
372 // Paint separator between week numbers and days
373 if(options&CALENDAR_WEEKNUMBERS) {
374 dc.setForeground(getApp()->getForeColor());
375 dc.fillRectangle(columnwidth[0]-1,0,1,height);
376 }
377
378 // Paint header
379 dc.setForeground(titleColor);
380 dc.setFont(font);
381
382 // Week number caption
383 if(options&CALENDAR_WEEKNUMBERS){
384 text=tr("Wk");
385 dc.drawText((columnwidth[0]-font->getTextWidth(text))/2,(rowheight[0]-fontheight)/2+font->getFontAscent(),text);
386 xx+=columnwidth[0];
387 }
388
389 // Day of week captions
390 for(c=firstday,i=coloffset; c<firstday+7; c++,i++){
391 text=tr(FXDate::dayNameShort(c%7));
392 if(c%7==0 || c%7==6){
393 dc.setForeground(weekendColor);
394 }
395 else{
396 dc.setForeground(titleColor);
397 }
398 dc.drawText(xx+(columnwidth[i]-font->getTextWidth(text))/2,(rowheight[0]-fontheight)/2+font->getFontAscent(),text);
399 xx+=columnwidth[i];
400 }
401
402 // Next row
403 yy+=rowheight[0];
404
405 // Paint days
406 dc.setForeground(getApp()->getForeColor());
407 for(r=1; r<nrows; r++){
408 xx=0;
409
410 if(options&CALENDAR_WEEKNUMBERS){
411 text.fromInt(pd.weekOfYear());
412 dc.setForeground(dayColor);
413
414 // Draw Text
415 dc.drawText(xx+columnwidth[0]-font->getTextWidth(text)-columnoffset[0],yy+(rowheight[r]-fontheight)/2+font->getFontAscent(),text);
416 xx+=columnwidth[0];
417 }
418
419
420 for(c=coloffset; c<ncols; c++){
421 if((options&CALENDAR_HIDEOTHER) && pd.month()!=month){
422 ++pd; // Next day
423 xx+=columnwidth[c]; // Next column
424 continue;
425 }
426
427 text.fromInt(pd.day());
428
429 // Determine drawing colors
430 if(displayselected && selected==pd){
431 if(isEnabled()){
432 dc.setForeground(getApp()->getSelbackColor());
433 }
434 else{
435 dc.setForeground(getApp()->getShadowColor());
436 }
437 dc.fillRectangle(xx,yy,columnwidth[c],rowheight[r]);
438 dc.setForeground(getApp()->getSelforeColor());
439 }
440 else{
441 if(pd==now){
442 dc.setForeground(todayColor);
443 }
444 else{
445 if(pd.dayOfWeek()==0 || pd.dayOfWeek()==6){
446 if(pd.month()==month)
447 dc.setForeground(weekendColor);
448 else
449 dc.setForeground(otherWeekendColor);
450 }
451 else{
452 if(pd.month()==month)
453 dc.setForeground(dayColor);
454 else
455 dc.setForeground(otherDayColor);
456 }
457 }
458 }
459
460 // Draw Text
461 dc.drawText(xx+columnwidth[c]-font->getTextWidth(text)-columnoffset[c],yy+(rowheight[r]-fontheight)/2+font->getFontAscent(),text);
462
463 if(hasFocus() && current==pd){
464 dc.drawFocusRectangle(xx,yy,columnwidth[c],rowheight[r]);
465 }
466
467 ++pd; // Next day
468 xx+=columnwidth[c]; // Next column
469 }
470
471 // Next Row
472 yy+=rowheight[r];
473 }
474 return 1;
475 }
476
477
478
479 // Pressed left button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)480 long FXCalendarView::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
481 FXEvent* ev=(FXEvent*)ptr;
482 flags&=~FLAG_TIP;
483 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
484 if(isEnabled()){
485 grab();
486 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
487 flags&=~FLAG_UPDATE;
488 FXDate cd;
489 if(getDateAt(ev->click_x,ev->click_y,cd)){
490 if(((options&CALENDAR_STATIC) || (options&CALENDAR_HIDEOTHER)) && cd.month()!=month){
491 flags|=FLAG_PRESSED;
492 return 1;
493 }
494 setCurrentDate(cd,true);
495 if((options&SELECT_MASK)==CALENDAR_SINGLESELECT){
496 if(has_selection && current==selected)
497 state=true;
498 else
499 state=false;
500 selectDate(cd,true);
501 }
502 }
503 flags|=FLAG_PRESSED;
504 return 1;
505 }
506 return 0;
507 }
508
509
510 // If window can have focus
canFocus() const511 FXbool FXCalendarView::canFocus() const {
512 return true;
513 }
514
515
516 // Gained focus
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)517 long FXCalendarView::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
518 FXWindow::onFocusIn(sender,sel,ptr);
519 markdirty(current);
520 return 1;
521 }
522
523
524 // Lost focus
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)525 long FXCalendarView::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
526 FXWindow::onFocusOut(sender,sel,ptr);
527 markdirty(current);
528 return 1;
529 }
530
531
532 // Released left button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)533 long FXCalendarView::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
534 FXEvent* event=(FXEvent*)ptr;
535 if(isEnabled()){
536 ungrab();
537 flags|=FLAG_UPDATE;
538 flags&=~FLAG_PRESSED;
539 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
540
541 if((options&SELECT_MASK)==CALENDAR_SINGLESELECT){
542 if(state){
543 killSelection(true);
544 }
545 }
546
547 // Generate clicked callbacks
548 if(event->click_count==1){
549 handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXuval)current.getJulian());
550 }
551 else if(event->click_count==2){
552 handle(this,FXSEL(SEL_DOUBLECLICKED,0),(void*)(FXuval)current.getJulian());
553 }
554 else if(event->click_count==3){
555 handle(this,FXSEL(SEL_TRIPLECLICKED,0),(void*)(FXuval)current.getJulian());
556 }
557
558 handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXuval)current.getJulian());
559 return 1;
560 }
561 return 0;
562 }
563
564
565 // Command message
onCommand(FXObject *,FXSelector,void * ptr)566 long FXCalendarView::onCommand(FXObject*,FXSelector,void* ptr){
567 return target && target->tryHandle(this,FXSEL(SEL_COMMAND,message),ptr);
568 }
569
570
571 // Clicked in list
onClicked(FXObject *,FXSelector,void * ptr)572 long FXCalendarView::onClicked(FXObject*,FXSelector,void* ptr){
573 return target && target->tryHandle(this,FXSEL(SEL_CLICKED,message),ptr);
574 }
575
576
577 // Double clicked in list; ptr may or may not point to an item
onDoubleClicked(FXObject *,FXSelector,void * ptr)578 long FXCalendarView::onDoubleClicked(FXObject*,FXSelector,void* ptr){
579 return target && target->tryHandle(this,FXSEL(SEL_DOUBLECLICKED,message),ptr);
580 }
581
582
583 // Triple clicked in list; ptr may or may not point to an item
onTripleClicked(FXObject *,FXSelector,void * ptr)584 long FXCalendarView::onTripleClicked(FXObject*,FXSelector,void* ptr){
585 return target && target->tryHandle(this,FXSEL(SEL_TRIPLECLICKED,message),ptr);
586 }
587
588
589 // Update value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)590 long FXCalendarView::onCmdSetValue(FXObject*,FXSelector,void* ptr){
591 setCurrentDate(FXDate((FXuint)(FXuval)ptr));
592 return 1;
593 }
594
595
596 // Obtain value from list
onCmdGetIntValue(FXObject *,FXSelector,void * ptr)597 long FXCalendarView::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){
598 *((FXuint*)ptr)=getCurrentDate().getJulian();
599 return 1;
600 }
601
602
603 // Update value from a message
onCmdSetIntValue(FXObject *,FXSelector,void * ptr)604 long FXCalendarView::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){
605 setCurrentDate(FXDate(*((FXuint*)ptr)));
606 return 1;
607 }
608
609
610 // Key pressed
onKeyPress(FXObject * sender,FXSelector sel,void * ptr)611 long FXCalendarView::onKeyPress(FXObject*sender,FXSelector sel,void* ptr){
612 FXEvent* event=(FXEvent*)ptr;
613 FXDate date=current;
614 flags&=~FLAG_TIP;
615
616 // Bounce to focus widget
617 if(getFocus() && getFocus()->handle(sender,sel,ptr)) return 1;
618
619 if(!isEnabled()) return 0;
620
621 // Try target first
622 if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
623
624 // Eat keystroke
625 switch(event->code){
626 case KEY_Up:
627 case KEY_KP_Up:
628 moveFocus(current-7);
629 return 1;
630 break;
631 case KEY_Down:
632 case KEY_KP_Down:
633 moveFocus(current+7);
634 return 1;
635 break;
636 case KEY_Right:
637 case KEY_KP_Right:
638 moveFocus(current+1);
639 return 1;
640 break;
641 case KEY_Left:
642 case KEY_KP_Left:
643 moveFocus(current-1);
644 return 1;
645 break;
646 case KEY_space:
647 case KEY_Return:
648 case KEY_KP_Enter:
649 if(has_selection && current==selected)
650 killSelection(true);
651 else
652 selectDate(current);
653 return 1;
654 break;
655 case KEY_Page_Down:
656 case KEY_KP_Page_Down:
657 date.addMonths(1);
658 setCurrentDate(date,true);
659 return 1;
660 break;
661 case KEY_Page_Up:
662 case KEY_KP_Page_Up:
663 date.addMonths(-1);
664 setCurrentDate(date,true);
665 return 1;
666 break;
667 case KEY_Home:
668 case KEY_KP_Home:
669 setCurrentDate(FXDate::localDate(),true);
670 return 1;
671 break;
672 case KEY_End:
673 case KEY_KP_End:
674 if(has_selection)
675 setCurrentDate(selected,true);
676 else
677 getApp()->beep();
678 return 1;
679 break;
680 }
681 return 0;
682 }
683
684
685 // Mark as dirty
markdirty(FXDate d)686 void FXCalendarView::markdirty(FXDate d){
687 if(xid){
688 FXint ncols = ((options&CALENDAR_WEEKNUMBERS) ? 8 : 7);
689 FXint nrows = 7;
690 FXint cw = width / ncols;
691 FXint ch = height / nrows;
692 FXint cwr = width % ncols;
693 FXint chr = height % nrows;
694 FXint days = d-ds;
695 FXint col = (days%7) + ((options&CALENDAR_WEEKNUMBERS) ? 1 : 0);
696 FXint row = (days/7) + 1;
697 FXint xx=0,yy=0,ww=0,hh=0,i=0,e=0;
698
699 // Calculate xx
700 for(i=0,e=0;i<col;i++){
701 xx+=cw;
702 e+=cwr;
703 if(e>=ncols){ xx++; e-=ncols; }
704 }
705
706 // Calculate ww
707 ww=cw;
708 e+=cwr;
709 if(e>=ncols){ ww++; e-=ncols; }
710
711 // Calculate yy
712 for(i=0,e=0;i<row;i++){
713 yy+=ch;
714 e+=chr;
715 if(e>=nrows){ yy++; e-=nrows; }
716 }
717
718 // Calculate hh
719 hh=ch;
720 e+=chr;
721 if(e>=nrows){ hh++; e-=nrows; }
722
723 update(xx,yy,ww,hh);
724 }
725 }
726
727
728 // Update view
updateview(FXbool notify)729 void FXCalendarView::updateview(FXbool notify){
730 FXDate ms=(current-(current.day()-1));
731 month=current.month();
732 ds=ms-(7-(firstday-ms.dayOfWeek()))%7;
733 if(target && notify) target->tryHandle(this,FXSEL(SEL_REPLACED,message),(void*)(FXival)current.getJulian());
734 }
735
736
737 // Set the current month; current day will be properly updated for the choosen month
setCurrentMonth(FXint m,FXbool notify)738 void FXCalendarView::setCurrentMonth(FXint m,FXbool notify){
739 if(m<1 || m>12){ fxerror("%s::setCurrentMonth: Invalid month argument.\n",getClassName()); }
740 if(month!=m){
741
742 // Set a new current date
743 FXint day=current.day();
744 current.setDate(current.year(),m,1);
745 if(current.daysInMonth()>day)
746 current.setDate(current.year(),m,day);
747 else
748 current.setDate(current.year(),m,current.daysInMonth());
749
750 // Update the GUI and do the same as setCurrentDate would do.
751 // Don't call setCurrentDate since it wouldn't update the complete view.
752 updateview();
753 update(0,0,width,height);
754
755 // Notify item change
756 if(notify && target){ target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current.getJulian()); }
757
758 // In browse select mode, select this item
759 if((options&SELECT_MASK)==CALENDAR_BROWSESELECT){
760 selectDate(current,notify);
761 }
762 }
763 }
764
765
766 // Get the selected date, if any
getSelectedDate(FXDate & date) const767 FXbool FXCalendarView::getSelectedDate(FXDate& date) const {
768 if(((options&SELECT_MASK)==CALENDAR_SINGLESELECT)){
769 if(has_selection){
770 date=selected;
771 return true;
772 }
773 return false;
774 }
775 else{
776 date=selected;
777 return true;
778 }
779 }
780
781
782 // Select Date
selectDate(FXDate d,FXbool notify)783 void FXCalendarView::selectDate(FXDate d,FXbool notify){
784 if(d!=selected || !has_selection){
785
786 has_selection=true;
787
788 // Within the current month view
789 if((selected>=ds) && (selected<=(ds+41))){
790 markdirty(selected);
791 }
792
793 // Within the current month view
794 if((d.month()==month) && (d.year()==selected.year())){
795 selected=d;
796 markdirty(selected);
797 }
798 else{
799 selected=d;
800 updateview();
801 update(0,0,width,height);
802 }
803
804 if(notify && target){ target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)selected.getJulian()); }
805 }
806 }
807
808
809 // Deselect Date
killSelection(FXbool notify)810 void FXCalendarView::killSelection(FXbool notify){
811 if(((options&SELECT_MASK)==CALENDAR_SINGLESELECT) && has_selection){
812 has_selection=false;
813 if(notify && target){ target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)selected.getJulian()); }
814 markdirty(selected);
815 }
816 }
817
818
819 // Set Date
setCurrentDate(FXDate d,FXbool notify)820 void FXCalendarView::setCurrentDate(FXDate d,FXbool notify){
821 if(d!=current){
822
823 // Update View and repaint everyting
824 if(d<ds || d>(ds+41) || (((options&CALENDAR_HIDEOTHER) || ((options&SELECT_MASK)==CALENDAR_BROWSESELECT)) && d.month()!=month)){
825 current=d;
826 updateview();
827 update(0,0,width,height);
828 }
829
830 // Within the current month view. just mark fields used dirty
831 else{
832 markdirty(current);
833 markdirty(d);
834 current=d;
835 }
836
837 // Notify item change
838 if(notify && target){ target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current.getJulian()); }
839 }
840
841 // In browse select mode, select this item
842 if((options&SELECT_MASK)==CALENDAR_BROWSESELECT){
843 selectDate(current,notify);
844 }
845 }
846
847
848 // Set the first day of the week [0...6]
setFirstDay(FXint d)849 void FXCalendarView::setFirstDay(FXint d){
850 if(d<0 || d>6){ fxerror("%s::setFirstDay: invalid day argument.\n",getClassName()); }
851 if(firstday!=d){
852 firstday=d;
853 updateview();
854 update();
855 }
856 }
857
858
859 // Change the font
setFont(FXFont * fnt)860 void FXCalendarView::setFont(FXFont *fnt){
861 if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); }
862 if(font!=fnt){
863 font=fnt;
864 recalc();
865 update();
866 }
867 }
868
869
870 // Destroy it
~FXCalendarView()871 FXCalendarView::~FXCalendarView(){
872 font=(FXFont*)-1L;
873 }
874
875 }
876
877