Enhance Calendar Widget
[platform/core/csapi/tizenfx.git] / src / ElmSharp / ElmSharp / Calendar.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Collections.Generic;
19 using System.Linq;
20 using System.Runtime.InteropServices;
21
22 namespace ElmSharp
23 {
24     /// <summary>
25     /// Enumeration for event periodicity, used to define if a mark should be repeated beyond event's day. It's set when a mark is added.
26     /// </summary>
27     public enum CalendarMarkRepeatType
28     {
29         /// <summary>
30         /// Default value. Marks will be displayed only on event day.
31         /// </summary>
32         Unique,
33
34         /// <summary>
35         /// Marks will be displayed every day after event day.
36         /// </summary>
37         Daily,
38
39         /// <summary>
40         /// Marks will be displayed every week after event day.
41         /// </summary>
42         Weekly,
43
44         /// <summary>
45         /// Marks will be displayed every month day that coincides to event day.
46         /// </summary>
47         Monthly,
48
49         /// <summary>
50         /// Marks will be displayed every year that coincides to event day.
51         /// </summary>
52         Annually,
53
54         /// <summary>
55         /// Marks will be displayed every last day of month after event day.
56         /// </summary>
57         LastDayOfMonth
58     }
59
60     /// <summary>
61     /// Enumeration for the mode, which determine how user could select a day.
62     /// </summary>
63     public enum CalendarSelectMode
64     {
65         /// <summary>
66         /// Default value. a day is always selected.
67         /// </summary>
68         Default,
69
70         /// <summary>
71         /// A day is always selected.
72         /// </summary>
73         Always,
74
75         /// <summary>
76         /// None of the days can be selected.
77         /// </summary>
78         None,
79
80         /// <summary>
81         /// User may have selected a day or not.
82         /// </summary>
83         OnDemand
84     }
85
86     [Flags]
87     public enum CalendarSelectable
88     {
89         None = 0,
90         Year = 1 << 0,
91         Month = 1 << 1,
92         Day = 1 << 2
93     }
94
95     /// <summary>
96     /// The CalendarMark is a Item for marking a Calendar's type,date and repeat type.
97     /// </summary>
98     public class CalendarMark
99     {
100         internal IntPtr Handle;
101
102         /// <summary>
103         /// A string used to define the type of mark.
104         /// </summary>
105         public string Type;
106
107         /// <summary>
108         /// A time struct to represent the date of inclusion of the mark.
109         /// </summary>
110         public DateTime Date;
111
112         /// <summary>
113         /// Repeat the event following this periodicity.
114         /// </summary>
115         public CalendarMarkRepeatType Repeat;
116
117         /// <summary>
118         /// Creates and initializes a new instance of the CalendarMark class.
119         /// </summary>
120         /// <param name="type">Type of mark</param>
121         /// <param name="date">Date of inclusion of the mark</param>
122         /// <param name="repeat">Repeat type</param>
123         public CalendarMark(string type, DateTime date, CalendarMarkRepeatType repeat)
124         {
125             Handle = IntPtr.Zero;
126             Type = type;
127             Date = date;
128             Repeat = repeat;
129         }
130     }
131
132     /// <summary>
133     /// The Calendar is a widget that helps applications to flexibly display a calender with day of the week, date, year and month.
134     /// </summary>
135     public class Calendar : Layout
136     {
137         SmartEvent _changed;
138         DateTime _cacheSelectedDate;
139         SmartEvent _displayedMonthChanged;
140         int _cacheDisplayedMonth;
141
142         Interop.Elementary.Elm_Calendar_Format_Cb _calendarFormat;
143         DateFormatDelegate _dateFormatDelegate = null;
144
145         /// <summary>
146         /// Creates and initializes a new instance of the Calendar class.
147         /// </summary>
148         /// <param name="parent">
149         /// The EvasObject to which the new Calendar will be attached as a child.
150         /// </param>
151         public Calendar(EvasObject parent) : base(parent)
152         {
153             _changed = new SmartEvent(this, this.RealHandle, "changed");
154             _changed.On += (sender, e) =>
155             {
156                 DateTime selectedDate = SelectedDate;
157                 DateChanged?.Invoke(this, new DateChangedEventArgs(_cacheSelectedDate, selectedDate));
158                 _cacheSelectedDate = selectedDate;
159             };
160
161             _displayedMonthChanged = new SmartEvent(this, this.RealHandle, "display,changed");
162             _displayedMonthChanged.On += (sender, e) =>
163             {
164                 int currentDisplayedMonth = DisplayedTime.Month;
165                 DisplayedMonthChanged?.Invoke(this, new DisplayedMonthChangedEventArgs(_cacheDisplayedMonth, currentDisplayedMonth));
166                 _cacheDisplayedMonth = currentDisplayedMonth;
167             };
168
169             _calendarFormat = (t) => { return _dateFormatDelegate(t); };
170         }
171
172         /// <summary>
173         /// DateChanged will be triggered when the date in the calendar is changed.
174         /// </summary>
175         public event EventHandler<DateChangedEventArgs> DateChanged;
176
177         /// <summary>
178         /// DisplayedMonthChanged will be triggered when the current month displayed in the calendar is changed.
179         /// </summary>
180         public event EventHandler<DisplayedMonthChangedEventArgs> DisplayedMonthChanged;
181
182         /// <summary>
183         /// This delegate type is used to format the string that will be used to display month and year.
184         /// </summary>
185         /// <param name="time">DateTime</param>
186         /// <returns></returns>
187         public delegate string DateFormatDelegate(DateTime time);
188
189         /// <summary>
190         /// Sets or gets the minimum for year.
191         /// </summary>
192         public int MinimumYear
193         {
194             get
195             {
196                 int minimumYear;
197                 int unused;
198                 Interop.Elementary.elm_calendar_min_max_year_get(RealHandle, out minimumYear, out unused);
199                 return minimumYear;
200             }
201             set
202             {
203                 int maximumYear;
204                 int unused;
205                 Interop.Elementary.elm_calendar_min_max_year_get(RealHandle, out unused, out maximumYear);
206                 if (maximumYear < 1902)
207                 {
208                     maximumYear = DateTime.MaxValue.Year;
209                 }
210                 Interop.Elementary.elm_calendar_min_max_year_set(RealHandle, value, maximumYear);
211             }
212         }
213
214         /// <summary>
215         /// Sets or gets the maximum for the year.
216         /// </summary>
217         public int MaximumYear
218         {
219             get
220             {
221                 int maximumYear;
222                 int unused;
223                 Interop.Elementary.elm_calendar_min_max_year_get(RealHandle, out unused, out maximumYear);
224                 return maximumYear;
225             }
226             set
227             {
228                 int minimumYear;
229                 int unused;
230                 Interop.Elementary.elm_calendar_min_max_year_get(RealHandle, out minimumYear, out unused);
231                 Interop.Elementary.elm_calendar_min_max_year_set(RealHandle, minimumYear, value);
232             }
233         }
234
235         /// <summary>
236         /// Sets or gets the first day of week, who are used on Calendar.
237         /// </summary>
238         public DateTime DisplayedTime
239         {
240             get
241             {
242                 var tm = new Interop.Libc.SystemTime();
243                 Interop.Elementary.elm_calendar_displayed_time_get(RealHandle, out tm);
244                 ///TODO
245                 ///If the defect is fixed, it will be removed.
246                 var daysInMonth = DateTime.DaysInMonth(tm.tm_year + 1900, tm.tm_mon + 1);
247                 var day = tm.tm_mday;
248
249                 if (day > daysInMonth)
250                 {
251                     day = daysInMonth;
252                 }
253
254                 DateTime date = new DateTime(tm.tm_year + 1900, tm.tm_mon + 1, day, tm.tm_hour, tm.tm_min, tm.tm_sec);
255
256                 return date;
257             }
258         }
259
260         /// <summary>
261         /// Sets or gets the first day of week, who are used on Calendar.
262         /// </summary>
263         public DayOfWeek FirstDayOfWeek
264         {
265             get
266             {
267                 return (DayOfWeek)Interop.Elementary.elm_calendar_first_day_of_week_get(RealHandle);
268             }
269             set
270             {
271                 Interop.Elementary.elm_calendar_first_day_of_week_set(RealHandle, (int)value);
272             }
273         }
274
275         /// <summary>
276         /// Sets or gets the weekdays names to be displayed by the Calendar.
277         /// </summary>
278         /// <remarks>
279         /// The usage should be like this;
280         /// List<string> weekDayNames = new List<string>() { "S", "M", "T", "W", "T", "F", "S" };
281         /// Calendar.WeekDayNames = weekDayNames;
282         /// </remarks>
283         public IReadOnlyList<string> WeekDayNames
284         {
285             get
286             {
287                 IntPtr stringArrayPtr = Interop.Elementary.elm_calendar_weekdays_names_get(RealHandle);
288                 string[] stringArray;
289                 IntPtrToStringArray(stringArrayPtr, 7, out stringArray);
290                 return stringArray;
291             }
292             set
293             {
294                 if (value != null && value.Count == 7)
295                 {
296                     Interop.Elementary.elm_calendar_weekdays_names_set(RealHandle, value.ToArray());
297                 }
298             }
299         }
300
301         /// <summary>
302         /// Sets or gets the selected date.
303         /// </summary>
304         /// <remarks>
305         /// Selected date changes when the user goes to next/previous month or select a day pressing over it on calendar.
306         /// </remarks>
307         public DateTime SelectedDate
308         {
309             get
310             {
311                 var tm = new Interop.Libc.SystemTime();
312                 Interop.Elementary.elm_calendar_selected_time_get(RealHandle, ref tm);
313                 if (tm.tm_year == 0 && tm.tm_mon == 0 && tm.tm_mday == 0)
314                 {
315                     return DateTime.Now;
316                 }
317                 return tm;
318             }
319             set
320             {
321                 Interop.Libc.SystemTime tm = value;
322                 Interop.Elementary.elm_calendar_selected_time_set(RealHandle, ref tm);
323                 _cacheSelectedDate = value;
324             }
325         }
326
327         /// <summary>
328         /// Sets or gets the interval on time updates for an user mouse button
329         /// hold on calendar widgets' month/year selection.
330         /// </summary>
331         public double Interval
332         {
333             get
334             {
335                 return Interop.Elementary.elm_calendar_interval_get(RealHandle);
336             }
337             set
338             {
339                 Interop.Elementary.elm_calendar_interval_set(RealHandle, value);
340             }
341         }
342
343         /// <summary>
344         /// Gets or sets the select day mode used.
345         /// </summary>
346         public CalendarSelectMode SelectMode
347         {
348             get
349             {
350                 return (CalendarSelectMode)Interop.Elementary.elm_calendar_select_mode_get(RealHandle);
351             }
352             set
353             {
354                 Interop.Elementary.elm_calendar_select_mode_set(RealHandle, (Interop.Elementary.Elm_Calendar_Select_Mode)value);
355             }
356         }
357
358         /// <summary>
359         /// Gets or sets fields of a datetime will be taken into account, when SelectedDate set is invoked.
360         /// </summary>
361         public CalendarSelectable Selectable
362         {
363             get
364             {
365                 return (CalendarSelectable)Interop.Elementary.elm_calendar_selectable_get(RealHandle);
366             }
367             set
368             {
369                 Interop.Elementary.elm_calendar_selectable_set(RealHandle, (int)value);
370             }
371         }
372
373         /// <summary>
374         /// Gets or sets date format the string that will be used to display month and year.
375         /// By default it uses strftime with "%B %Y" format string.
376         /// It should allocate the memory that will be used by the string, that will be freed by the widget after usage.A pointer to the string and a pointer to the time struct will be provided.
377         /// </summary>
378         public DateFormatDelegate DateFormat
379         {
380             get
381             {
382                 return _dateFormatDelegate;
383             }
384             set
385             {
386                 _dateFormatDelegate = value;
387                 if (value != null)
388                 {
389                     Interop.Elementary.elm_calendar_format_function_set(RealHandle, _calendarFormat);
390                 }
391                 else
392                 {
393                     Interop.Elementary.elm_calendar_format_function_set(RealHandle, null);
394                 }
395             }
396         }
397
398         /// <summary>
399         /// Add a new mark to the calendar.
400         /// </summary>
401         /// <param name="type">A string used to define the type of mark. It will be emitted to the theme, that should display a related modification on these days representation.</param>
402         /// <param name="date">A time struct to represent the date of inclusion of the mark. For marks that repeats it will just be displayed after the inclusion date in the calendar.</param>
403         /// <param name="repeat">Repeat the event following this periodicity. Can be a unique mark (that don't repeat), daily, weekly, monthly or annually.</param>
404         /// <returns>Item for a calendar mark.</returns>
405         public CalendarMark AddMark(string type, DateTime date, CalendarMarkRepeatType repeat)
406         {
407             CalendarMark mark = new CalendarMark(type, date, repeat);
408             Interop.Libc.SystemTime tm = date;
409             IntPtr nativeHandle = Interop.Elementary.elm_calendar_mark_add(RealHandle, type, ref tm, (Interop.Elementary.Elm_Calendar_Mark_Repeat_Type)repeat);
410             mark.Handle = nativeHandle;
411
412             return mark;
413         }
414
415         /// <summary>
416         /// Delete mark from the calendar.
417         /// </summary>
418         /// <param name="mark">Item for a calendar mark</param>
419         public void DeleteMark(CalendarMark mark)
420         {
421             Interop.Elementary.elm_calendar_mark_del(mark.Handle);
422         }
423
424         /// <summary>
425         /// Draw calendar marks.
426         /// </summary>
427         public void DrawMarks()
428         {
429             Interop.Elementary.elm_calendar_marks_draw(RealHandle);
430         }
431
432         /// <summary>
433         /// Remove all calendar's marks.
434         /// </summary>
435         public void ClearMarks()
436         {
437             Interop.Elementary.elm_calendar_marks_clear(RealHandle);
438         }
439
440         protected override IntPtr CreateHandle(EvasObject parent)
441         {
442             IntPtr handle = Interop.Elementary.elm_layout_add(parent.Handle);
443             Interop.Elementary.elm_layout_theme_set(handle, "layout", "elm_widget", "default");
444
445             RealHandle = Interop.Elementary.elm_calendar_add(handle);
446             Interop.Elementary.elm_object_part_content_set(handle, "elm.swallow.content", RealHandle);
447
448             return handle;
449         }
450
451         static void IntPtrToStringArray(IntPtr unmanagedArray, int size, out string[] managedArray)
452         {
453             managedArray = new string[size];
454             IntPtr[] IntPtrArray = new IntPtr[size];
455
456             Marshal.Copy(unmanagedArray, IntPtrArray, 0, size);
457
458             for (int iterator = 0; iterator < size; iterator++)
459             {
460                 managedArray[iterator] = Marshal.PtrToStringAnsi(IntPtrArray[iterator]);
461             }
462         }
463     }
464 }