ecere/ECON: Fixed ECON parser expecting members in specific order
[sdk] / ecere / src / sys / Date.ec
1 namespace sys;
2
3 import "i18n"
4 import "System"
5 import "CalendarControl"
6
7 #define ISLEAP(y) (!((y)%4) && (((y) % 100) || (!((y)% 400))))
8
9 /*static */Array<const String> enLongDaysNames
10 { [
11    "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
12 ] };
13 /*static */Array<const String> enLongMonthsNames
14 { [
15    "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
16 ] };
17
18 /*static */Array<const String> enShortDaysNames
19 { [
20    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
21 ] };
22 /*static */Array<const String> enShortMonthsNames
23 { [
24    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
25 ] };
26
27 /*static */Array<const String> longDaysNames
28 { [
29    $"Sunday", $"Monday", $"Tuesday", $"Wednesday", $"Thursday", $"Friday", $"Saturday"
30 ] };
31 /*static */Array<const String> longMonthsNames
32 { [
33    $"January", $"February", $"March", $"April", $"LongMonthNames"."May", $"June", $"July", $"August", $"September", $"October", $"November", $"December"
34 ] };
35
36 /*static */Array<const String> shortDaysNames
37 { [
38    $"Sun", $"Mon", $"Tue", $"Wed", $"Thu", $"Fri", $"Sat"
39 ] };
40 /*static */Array<const String> shortMonthsNames
41 { [
42    $"Jan", $"Feb", $"Mar", $"Apr", $"ShortMonthNames"."May", $"Jun", $"Jul", $"Aug", $"Sep", $"Oct", $"Nov", $"Dec"
43 ] };
44
45 default:
46 extern int __ecereVMethodID_class_OnGetString;
47 public:
48
49 public enum Month
50 {
51    january, february, march, april, may, june, july, august, september, october, november, december;
52
53    const char * OnGetString(char * string, void * fieldData, bool * needClass)
54    {
55       Month m = this;
56       if(m >= january && m <= december)
57       {
58          if(!needClass || !*needClass)
59             return longMonthsNames[m];
60          else
61             return class::OnGetString(string, fieldData, needClass);
62       }
63       return null;
64    }
65
66    bool OnGetDataFromString(const char * string)
67    {
68       if(string)
69       {
70          Month m;
71          for(m = 0; m<=december; m++)
72          {
73             if(!strcmpi(shortMonthsNames[m], string)  || !strcmpi(longMonthsNames[m], string) ||
74                !strcmpi(enShortMonthsNames[m], string) || !strcmpi(enLongMonthsNames[m], string))
75             {
76                this = m;
77                return true;
78             }
79          }
80       }
81       return false;
82    }
83 };
84
85 default:
86 extern int __ecereVMethodID_class_OnGetString;
87 private:
88
89 public struct Date
90 {
91    int year;
92    Month month;
93    int day;
94
95    int OnCompare(Date b)
96    {
97       if(year > b.year) return 1;
98       if(year < b.year) return -1;
99       if(month > b.month) return 1;
100       if(month < b.month) return -1;
101       if(day > b.day) return 1;
102       if(day < b.day) return -1;
103       return 0;
104    }
105
106    const char * OnGetString(char * stringOutput, void * fieldData, bool * needClass)
107    {
108       if(stringOutput)
109       {
110          if(day && year)
111             sprintf(stringOutput, "%s, %s %2d, %d",
112                longDaysNames[dayOfTheWeek], longMonthsNames[month], day, year);
113          else
114             stringOutput[0] = 0;
115       }
116       return stringOutput;
117    }
118
119    const char * OnGetStringEn(char * stringOutput, void * fieldData, bool * needClass)
120    {
121       if(stringOutput)
122       {
123          if(day && year)
124             sprintf(stringOutput, "%s, %s %2d, %d",
125                enLongDaysNames[dayOfTheWeek], enLongMonthsNames[month], day, year);
126          else
127             stringOutput[0] = 0;
128       }
129       return stringOutput;
130    }
131
132    bool OnGetDataFromString(const char * string)
133    {
134       char value[256];
135       bool gotAlphaMonth = false;
136       DateTime time;
137       int year, day;
138       Month month = 0;
139       int numerics[3];
140       int len[3];
141       int count = 0;
142       time.GetLocalTime();
143
144       if(!strcmpi(string, "today") || !strcmpi(string, $"today") ||
145          !strcmpi(string, "now") || !strcmpi(string, $"now") ||
146          !strcmpi(string, "tomorrow") || !strcmpi(string, $"tomorrow") ||
147          !strcmpi(string, "yesterday") || !strcmpi(string, $"yesterday"))
148       {
149          SecSince1970 weWant;
150          if(!strcmpi(string, "tomorrow") || !strcmpi(string, $"tomorrow"))
151          {
152             weWant = (SecSince1970)time + 24 * 60 * 60;
153             time = (DateTime)weWant;
154          }
155          else if(!strcmpi(string, "yesterday") || !strcmpi(string, $"yesterday"))
156          {
157             weWant = (SecSince1970)time - 24 * 60 * 60;
158             time = (DateTime)weWant;
159          }
160          this.year = time.year;
161          this.month = time.month;
162          this.day = time.day;
163          return true;
164       }
165
166       if(string)
167       {
168          if(!string[0])
169          {
170             this.year = 0;
171             this.month = 0;
172             this.day = 0;
173             return true;
174          }
175          while(GetAlNum(&string, value, sizeof(value)))
176          {
177             int numeric = atoi(value);
178             int c;
179             bool isAlpha = false;
180             unichar ch;
181             int nb;
182             for(c = 0; (ch = UTF8GetChar(value + c, &nb)) && !isAlpha; c += nb)
183             {
184                // if(isalpha(value[c])) isAlpha = true;
185                if(CharMatchCategories(ch, letters)) isAlpha = true;
186             }
187
188             if(isAlpha)
189             {
190                Month m;
191                for(m = 0; m<Month::enumSize; m++)
192                   if(!strcmpi(shortMonthsNames[m], value)  || !strcmpi(longMonthsNames[m], value) ||
193                      !strcmpi(enShortMonthsNames[m], value) || !strcmpi(enLongMonthsNames[m], value))
194                   {
195                      month = m;
196                      gotAlphaMonth = true;
197                      break;
198                   }
199             }
200             else if(count < 3)
201             {
202                len[count] = strlen(value);
203                numerics[count++] = numeric;
204             }
205          }
206       }
207       if(count + gotAlphaMonth >= 3)
208       {
209          int yearLen;
210          if(!gotAlphaMonth)
211          {
212             if(numerics[1] >= 1 && numerics[1] <= 12 &&
213                numerics[2] >= 1 && numerics[2] <= 31)
214             {
215                year = numerics[0]; yearLen = len[0];
216                month = (Month)(numerics[1] - 1);
217                day = numerics[2];
218             }
219             else if(numerics[1] >= 1 && numerics[1] <= 31 &&
220                     numerics[2] >= 1 && numerics[2] <= 12)
221             {
222                year = numerics[0]; yearLen = len[0];
223                day = numerics[1];
224                month = (Month)(numerics[2] - 1);
225             }
226             /*
227             else if(numerics[0] >= 1 && numerics[0] <= 31 &&
228                     numerics[1] >= 1 && numerics[1] <= 12)
229             {
230                day = numerics[0];
231                month = (Month)(numerics[1] - 1);
232                year = numerics[2]; yearLen = len[2];
233             }
234             else if(numerics[0] >= 1 && numerics[0] <= 12 &&
235                     numerics[1] >= 1 && numerics[1] <= 31)
236             {
237                month = (Month)(numerics[0] - 1);
238                day = numerics[1];
239                year = numerics[2]; yearLen = len[2];
240             }*/
241             // FLIPPED THESE
242             else if(numerics[0] >= 1 && numerics[0] <= 12 &&
243                     numerics[1] >= 1 && numerics[1] <= 31)
244             {
245                month = (Month)(numerics[0] - 1);
246                day = numerics[1];
247                year = numerics[2]; yearLen = len[2];
248             }
249             else if(numerics[0] >= 1 && numerics[0] <= 31 &&
250                     numerics[1] >= 1 && numerics[1] <= 12)
251             {
252                day = numerics[0];
253                month = (Month)(numerics[1] - 1);
254                year = numerics[2]; yearLen = len[2];
255             }
256             else
257                return false;
258          }
259          else
260          {
261             if(numerics[0] < 1 || numerics[0] > 31 || (len[0] == 2 && numerics[0] < 10))
262             {
263                year = numerics[0]; yearLen = len[0];
264                day = numerics[1];
265             }
266             else if(numerics[0] >= 1 && numerics[0] <= 31)
267             {
268                day = numerics[0];
269                year = numerics[1]; yearLen = len[1];
270             }
271             else if(numerics[1] >= 1 && numerics[1] <= 31)
272             {
273                year = numerics[0]; yearLen = len[0];
274                day = numerics[1];
275             }
276             else
277                return false;
278          }
279
280          if(year < 100 && yearLen == 2)
281          {
282             year += (time.year / 100) * 100;
283             if(Abs(year - time.year) >= 50)
284                year -= 100;
285          }
286       }
287       else if(count >= 2 && !gotAlphaMonth)
288       {
289          // No Year Specified
290          year = time.year;
291          if(numerics[0] >= 1 && numerics[0] <= 12 &&
292             numerics[1] >= 1 && numerics[1] <= 31)
293          {
294             month = (Month)(numerics[0] - 1);
295             day = numerics[1];
296          }
297          else if(numerics[0] >= 1 && numerics[0] <= 31 &&
298                  numerics[1] >= 1 && numerics[1] <= 12)
299          {
300             day = numerics[0];
301             month = (Month)(numerics[1] - 1);
302          }
303          else
304             return false;
305       }
306       else if(count >= 1 && gotAlphaMonth)
307       {
308          year = time.year;
309          day = numerics[0];
310       }
311       else
312          return false;
313
314       if(day > monthLengths[ISLEAP(year)][month])
315          return false;
316
317       this.year = year;
318       this.month = month;
319       this.day = day;
320
321       return true;
322    }
323
324    property DayOfTheWeek dayOfTheWeek
325    {
326       get
327       {
328          DayOfTheWeek dayOfTheWeek;
329          int a4 = (year       / 4) - !(year       & 3);
330          int b4 = (EPOCH_YEAR / 4) - !(EPOCH_YEAR & 3);
331          int a100 = a4 / 25 - (a4 % 25 < 0);
332          int b100 = b4 / 25 - (b4 % 25 < 0);
333          int a400 = a100 / 4;
334          int b400 = b100 / 4;
335          int leapDays = (a4 - b4) - (a100 - b100) + (a400 - b400);
336          int days = 365 * (year - EPOCH_YEAR) + leapDays;
337          days += daysInAYearBeforeMonth[ISLEAP(year)][month] + day - 1;
338          dayOfTheWeek = (EPOCH_WEEKDAY + days) % 7;
339          if(dayOfTheWeek < 0)
340             dayOfTheWeek += 7;
341          return dayOfTheWeek;
342       }
343    }
344
345    bool OnSaveEdit(DropBox dropBox, void * object)
346    {
347       return dropBox.Save();
348    }
349
350    Window OnEdit(DataBox dataBox, Window obsolete, int x, int y, int w, int h, void * fieldData)
351    {
352       const char * string = "";
353       DateDropBox comboBox
354       {
355          dataBox,
356          editText = true;
357          anchor = { 0, 0, 0, 0 };
358          borderStyle = 0;
359          hotKey = f2;
360       };
361
362       if(day || year || month)
363       {
364          char tempString[MAX_F_STRING] = "";
365          bool needClass = false;
366          const char * result = OnGetString(tempString, null, &needClass);
367          if(result) string = result;
368
369          comboBox.calendar.dateValue = this;
370          comboBox.calendar.shownMonth = month;
371          comboBox.calendar.shownYear = year;
372       }
373       else
374       {
375          DateTime now;
376          now.GetLocalTime();
377          comboBox.calendar.dateValue.year = now.year;
378          comboBox.calendar.dateValue.month = (Month)now.month;
379          comboBox.calendar.dateValue.day = now.day;
380          comboBox.calendar.shownMonth = (Month)now.month;
381          comboBox.calendar.shownYear = now.year;
382       }
383
384       comboBox.contents = string;
385       comboBox.Create();
386       if(!dataBox.active)
387          comboBox.contents = string;
388       return comboBox;
389    }
390 };
391
392 class DateDropBox : DropBox
393 {
394    bool dateModified;
395    void EditNotifyUpdate(EditBox editBox)
396    {
397       if(pullDown)
398          dateModified = true;
399    }
400
401    bool OnPostCreate()
402    {
403       editBox.NotifyUpdate = EditNotifyUpdate;
404       return true;
405    }
406    bool OnKeyDown(Key key, unichar ch)
407    {
408       SmartKey sKey = (SmartKey)key;
409       if(pullDown && sKey == enter)
410       {
411          // Because we can still edit the date text while the calendar is dropped, enter on the date box should validate it
412          NotifyTextEntry(master, this, contents, true);
413          ((DataBox)master).SetData(&calendar.dateValue, false);
414          ((DataBox)master).Refresh();
415          return false;
416       }
417       return DropBox::OnKeyDown(key, ch);
418    }
419    CalendarControl calendar
420    {
421       master = this, autoCreate = false;
422       interim = true;
423
424       bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
425       {
426          if(!active)
427          {
428             DateDropBox dropBox = (DateDropBox)master;
429             Destroy(0);
430             ((DataBox)dropBox.master).Refresh();
431             *goOnWithActivation = false;
432          }
433          return true;
434       }
435
436       void NotifyChanged(CalendarControl calendarControl, bool close)
437       {
438          incref this;
439          if(dateModified)
440          {
441             Date date = calendar.dateValue;
442             dateModified = false;
443             if(date.OnGetDataFromString(contents))
444             {
445                if(date.year || date.month || date.day)
446                {
447                   calendar.dateValue = date;
448                   calendar.shownMonth = date.month;
449                   calendar.shownYear = date.year;
450                   dateModified = true;
451                }
452             }
453          }
454          if(!dateModified)
455             NotifyTextEntry(master, this, null, false);
456          dateModified = false;
457          //((DataBox)master).SetData(&calendar.dateValue, false);
458          if(close)
459          {
460             OnKeyDown(enter, 0);
461             // TESTING THIS HERE:
462             ((DataBox)master).Refresh();
463          }
464          delete this;
465       }
466    };
467
468    Window OnDropDown()
469    {
470       editBox.Activate();
471       if(contents[0])
472          NotifyTextEntry(master, this, contents, true);
473       calendar.Create();
474       return calendar;
475    }
476
477    void OnCloseDropDown(Window dateEditor)
478    {
479       master.Update(null);
480       calendar.Destroy(0);
481    }
482
483    bool DataBox::NotifyTextEntry(DateDropBox dropBox, const char * string, bool save)
484    {
485       CalendarControl calendar = (CalendarControl)dropBox.calendar;
486       Date date = calendar.dateValue;
487       Date * dataBoxDate = (Date *)data;
488
489       if(save)
490       {
491          if(date.OnGetDataFromString(string))
492          {
493             // TESTING THIS COMMENTED OUT HERE: (Not good -- was modifying on drop down!)
494             // With it not commented out, a current date in a SavingDataBox always shows blank
495             if(date.year != calendar.dateValue.year || date.year != dataBoxDate->year ||
496                date.month != calendar.dateValue.month || date.month != dataBoxDate->month ||
497                date.day != calendar.dateValue.day || date.day != dataBoxDate->day)
498                SetData(&date, false);
499             if(date.year || date.month || date.day)
500             {
501                calendar.dateValue = date;
502                calendar.shownMonth = date.month;
503                calendar.shownYear = date.year;
504             }
505             if(calendar.created)
506             {
507                calendar.Destroy(0);
508                calendar.Create();
509             }
510          }
511       }
512       {
513          char tempString[1024] = "";
514          bool needClass = false;
515          // char * string = date.OnGetString(tempString, null, &needClass);
516          const char * string = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &date, tempString, null, &needClass);
517          dropBox.contents = string;
518       }
519       return true;
520    }
521 };
522
523 public class RepButton : Button
524 {
525 public:
526    bool pressing;
527    isRemote = true;
528    inactive = true;
529
530    property Seconds delay { set { timer2.delay = value; } }
531    property Seconds delay0 { set { timer.delay = value; } }
532
533    bool OnKeyHit(Key key, unichar ch)
534    {
535       if(key == hotKey)
536       {
537          return NotifyClicked(master, this, 0, 0, key.modifiers);
538          // return false;
539       }
540       return true;
541    }
542
543    bool OnKeyDown(Key key, unichar ch)
544    {
545       if(key == hotKey)
546       {
547          /*return */NotifyPushed(master, this, 0,0, key.modifiers);
548          return false; //true;
549       }
550       return true;
551    }
552
553    bool OnKeyUp(Key key, unichar ch)
554    {
555       if(key == hotKey)
556       {
557          return NotifyReleased(master, this, 0,0, key.modifiers);
558          // return false;
559       }
560       return true;
561    }
562
563    bool NotifyPushed(RepButton button, int x, int y, Modifiers mods)
564    {
565       button.pressing = true;
566       button.NotifyClicked(this, button, x, y, mods);
567       button.timer.Start();
568       return true;
569    }
570
571    bool NotifyMouseLeave(RepButton button, Modifiers mods)
572    {
573       button.timer.Stop();
574       button.timer2.Stop();
575       return true;
576    }
577
578    bool NotifyReleased(RepButton button, int x, int y, Modifiers mods)
579    {
580       button.pressing = false;
581       button.NotifyMouseLeave(this, button, mods);
582       return false;
583    }
584
585    bool NotifyMouseOver(RepButton button, int x, int y, Modifiers mods)
586    {
587       if(button.pressing)
588          button.timer2.Start();
589       return true;
590    }
591
592    Timer timer
593    {
594       this, delay = 0.5;
595
596       bool DelayExpired()
597       {
598          timer.Stop();
599          timer2.Start();
600          timer2.DelayExpired(this);
601          return true;
602       }
603    };
604    Timer timer2
605    {
606       this, delay = 0.1;
607       bool DelayExpired()
608       {
609          NotifyClicked(master, this, 0, 0, 0);
610          return true;
611       }
612    };
613 }