Add Elementary Calendar Widget
authorBruno Dilly <bdilly@profusion.mobi>
Mon, 23 Aug 2010 18:45:36 +0000 (18:45 +0000)
committerBruno Dilly <bdilly@profusion.mobi>
Mon, 23 Aug 2010 18:45:36 +0000 (18:45 +0000)
A calendar is a widget that allows the user to select a date. It has
support to adding marks (holidays and checks by default). The calendar
is displayed month at a time.

Weekdays names and the function used to format month and year to
be displayed can be set, giving more flexibility to this widget.

SVN revision: 51584

data/themes/default.edc
src/lib/Elementary.h.in
src/lib/Makefile.am
src/lib/elm_calendar.c [new file with mode: 0644]

index ba6b03dedb7083c4beb0525019ba1987ea4f7dcf..6f7aced68240ad946139fb034ff282ac2b3d0351 100644 (file)
@@ -24930,4 +24930,722 @@ collections {
         }
      }
   }
-}
+
+/////////////////////////////////////////////////////////////////////////////
+// CALENDAR
+/////////////////////////////////////////////////////////////////////////////
+#define CH(_pos) \
+        part { name: "ch_"#_pos".base"; \
+           type: RECT; \
+           description { state: "default" 0.0; \
+              rel1 { \
+                 relative: (_pos % 7 * 7 / 8 / 6) 0; \
+                 to: "header"; \
+              } \
+              rel2 { \
+                 relative: (_pos % 7 * 7 / 8 / 6 + 1 / 8) 1; \
+                 to: "header"; \
+              } \
+               color: 0 0 0 0; \
+               visible: 0; \
+           } \
+        } \
+         part { name: "ch_"#_pos".text"; \
+            type: TEXT; \
+            effect: SOFT_SHADOW; \
+            mouse_events: 0; \
+            scale: 1; \
+           clip_to: "ch_"#_pos".clipper"; \
+            description { \
+               state: "default" 0.0; \
+               rel1.to: "ch_"#_pos".base"; \
+               rel2.to: "ch_"#_pos".base"; \
+               color: 0 0 0 255; \
+               color3: 0 0 0 0; \
+               text { \
+                  font: "Sans"; \
+                  size: 10; \
+                  min: 1 1; \
+                  align: 0.5 0.5; \
+               } \
+            } \
+        } \
+        part { name: "ch_"#_pos".clipper"; \
+           type: RECT; \
+           description { state: "default" 0.0; \
+              rel1.to: "ch_"#_pos".base"; \
+              rel2.to: "ch_"#_pos".base"; \
+           } \
+        }
+
+#define CIT(_pos) \
+        part { name: "cit_"#_pos".rect"; \
+           type: RECT; \
+           description { state: "default" 0.0; \
+              rel1 { \
+                 relative: (_pos % 7 * 7 / 8 / 6) \
+                           (_pos / 8 / 5 - _pos % 7 / 8 / 5); \
+                 to: "base"; \
+              } \
+              rel2 { \
+                 relative: (_pos % 7 * 7 / 8 / 6 + 1 / 8) \
+                           (_pos / 8 / 5 - _pos % 7 / 8 / 5 + 1 / 8); \
+                 to: "base"; \
+              } \
+               color: 0 0 0 0; \
+               visible: 0; \
+           } \
+        } \
+         part { \
+            name: "cit_"#_pos".event"; \
+            type: RECT; \
+            repeat_events: 1; \
+            description { \
+               rel1.to: "cit_"#_pos".rect"; \
+               rel2.to: "cit_"#_pos".rect"; \
+               state: "default" 0.0; \
+               color: 0 0 0 0; \
+            } \
+         } \
+         part { name: "cit_"#_pos".shelf"; \
+           type: RECT; \
+           mouse_events: 0; \
+           description { state: "default" 0.0; \
+               rel1 { \
+                  to: "cit_"#_pos".bg"; \
+                  offset: -1 -1; \
+               } \
+               rel2 { \
+                  to: "cit_"#_pos".bg"; \
+               } \
+               color: 200 200 200 255; \
+           } \
+        } \
+         part { name: "cit_"#_pos".hd"; \
+           type: RECT; \
+           mouse_events: 0; \
+           description { state: "default" 0.0; \
+               rel1 { \
+                  to: "cit_"#_pos".bg"; \
+                  offset: -1 -1; \
+               } \
+               rel2 { \
+                  to: "cit_"#_pos".bg"; \
+               } \
+               visible: 0; \
+               color: 160 0 0 255; \
+           } \
+           description { state: "visible" 0.0; \
+               inherit: "default" 0.0; \
+               visible: 1; \
+            } \
+        } \
+         part { \
+            name: "cit_"#_pos".base_sh"; \
+            mouse_events: 0; \
+            description { \
+               state: "default" 0.0; \
+               align: 0.0 0.0; \
+               min: 0 1; \
+               rel1 { \
+                  to: "cit_"#_pos".base"; \
+                  relative: 0.0 1.0; \
+                  offset: 0 0; \
+               } \
+               rel2 { \
+                  to: "cit_"#_pos".base"; \
+                  relative: 1.0 1.05; \
+                  offset: -1 0; \
+               } \
+               image { \
+                  normal: "ilist_item_shadow.png"; \
+               } \
+               fill.smooth: 0; \
+            } \
+         } \
+         part { \
+            name: "cit_"#_pos".base"; \
+            mouse_events: 0; \
+            description { \
+               state: "default" 0.0; \
+               rel1.to: "cit_"#_pos".rect"; \
+               rel2.to: "cit_"#_pos".rect"; \
+               rel2.offset: -1 -1; \
+               image { \
+                  normal: "ilist_1.png"; \
+                  border: 2 2 2 2; \
+               } \
+               fill.smooth: 0; \
+            } \
+            description { \
+               state: "today" 0.0; \
+               inherit: "default" 0.0; \
+               image.normal: "ilist_2.png"; \
+               color: 240 240 240 255; \
+            } \
+         } \
+         part { name: "cit_"#_pos".bg"; \
+            mouse_events: 0; \
+            description { state: "default" 0.0; \
+               visible: 0; \
+               color: 255 255 255 0; \
+               rel1 { \
+                  to: "cit_"#_pos".rect"; \
+                  relative: 0.0 0.0; \
+               } \
+               rel2 { \
+                  to: "cit_"#_pos".rect"; \
+                  relative: 1.0 1.0; \
+                  offset: -1 -1; \
+               } \
+               image { \
+                  normal: "bt_sm_base1.png"; \
+                  border: 6 6 6 6; \
+               } \
+               image.middle: SOLID; \
+            } \
+            description { state: "selected" 0.0; \
+               inherit: "default" 0.0; \
+               visible: 1; \
+               color: 255 255 255 255; \
+            } \
+         } \
+         part { name: "cit_"#_pos".text"; \
+            type: TEXT; \
+            effect: SOFT_SHADOW; \
+            mouse_events: 0; \
+            scale: 1; \
+            description { \
+               state: "default" 0.0; \
+               rel1.to: "cit_"#_pos".bg"; \
+               rel2.to: "cit_"#_pos".bg"; \
+               color: 0 0 0 255; \
+               color3: 0 0 0 0; \
+               text { \
+                  font: "Sans"; \
+                  size: 10; \
+                  min: 1 1; \
+                  align: 0.5 0.5; \
+               } \
+            } \
+            description { state: "selected" 0.0; \
+               inherit: "default" 0.0; \
+               color: 224 224 224 255; \
+               color3: 0 0 0 64; \
+            } \
+         } \
+         part { name: "cit_"#_pos".fg1"; \
+            mouse_events: 0; \
+            description { state: "default" 0.0; \
+               visible: 0; \
+               color: 255 255 255 0; \
+               rel1.to: "cit_"#_pos".bg"; \
+               rel2.relative: 1.0 0.5; \
+               rel2.to: "cit_"#_pos".bg"; \
+               image { \
+                  normal: "bt_sm_hilight.png"; \
+                  border: 6 6 6 0; \
+               } \
+            } \
+            description { state: "selected" 0.0; \
+               inherit: "default" 0.0; \
+               visible: 1; \
+               color: 255 255 255 255; \
+            } \
+         } \
+         part { name: "cit_"#_pos".fg2"; \
+            mouse_events: 0; \
+            description { state: "default" 0.0; \
+               visible: 0; \
+               color: 255 255 255 0; \
+               rel1.to: "cit_"#_pos".bg"; \
+               rel2.to: "cit_"#_pos".bg"; \
+               image { \
+                  normal: "bt_sm_shine.png"; \
+                  border: 6 6 6 0; \
+               } \
+            } \
+            description { state: "selected" 0.0; \
+               inherit: "default" 0.0; \
+               visible: 1; \
+               color: 255 255 255 255; \
+            } \
+         } \
+         part { name: "cit_"#_pos".check"; \
+           mouse_events: 0; \
+           description { state: "default" 0.0; \
+               rel1 { \
+                  to: "cit_"#_pos".bg"; \
+                  relative: 0.7 0.6; \
+                  offset: 1 1; \
+               } \
+               rel2 { \
+                  to: "cit_"#_pos".bg"; \
+                  relative: 1.1 1.2; \
+                  offset: -2 -2; \
+               } \
+               aspect: 1 1; \
+               visible: 0; \
+               color: 255 0 0 255; \
+               image.normal: "check.png"; \
+           } \
+           description { state: "visible" 0.0; \
+               inherit: "default" 0.0; \
+               visible: 1; \
+            } \
+        } \
+      programs { \
+         program { \
+            name:    "cit_"#_pos".go_active"; \
+            signal:  "cit_"#_pos",selected"; \
+            source:  "elm"; \
+            action:  STATE_SET "selected" 0.0; \
+            target:  "cit_"#_pos".bg"; \
+            target:  "cit_"#_pos".fg1"; \
+            target:  "cit_"#_pos".fg2"; \
+            target:  "cit_"#_pos".text"; \
+         } \
+         program { \
+            name:    "cit_"#_pos".go_passive"; \
+            signal:  "cit_"#_pos",unselected"; \
+            source:  "elm"; \
+            action:  STATE_SET "default" 0.0; \
+            target:  "cit_"#_pos".bg"; \
+            target:  "cit_"#_pos".fg1"; \
+            target:  "cit_"#_pos".fg2"; \
+            target:  "cit_"#_pos".text"; \
+         } \
+         program { \
+            name:    "cit_"#_pos".is_today"; \
+            signal:  "cit_"#_pos",today"; \
+            source:  "elm"; \
+            action:  STATE_SET "today" 0.0; \
+            target: "cit_"#_pos".base"; \
+         } \
+         program { \
+            name:    "cit_"#_pos".not_today"; \
+            signal:  "cit_"#_pos",not_today"; \
+            source:  "elm"; \
+            action:  STATE_SET "default" 0.0; \
+            target: "cit_"#_pos".base"; \
+         } \
+         program { \
+            source: "cit_"#_pos".clicked"; \
+            signal: "mouse,clicked,1"; \
+            source: "cit_"#_pos".event"; \
+           action: SIGNAL_EMIT "elm,action,selected" #_pos; \
+         } \
+         program { \
+            name:    "cit_"#_pos".clear"; \
+            signal:  "cit_"#_pos",clear"; \
+            source:  "elm"; \
+            action:  STATE_SET "default" 0.0; \
+            target: "cit_"#_pos".check"; \
+            target: "cit_"#_pos".hd"; \
+         } \
+         program { \
+            name:    "cit_"#_pos".checked"; \
+            signal:  "cit_"#_pos",checked"; \
+            source:  "elm"; \
+            action:  STATE_SET "visible" 0.0; \
+            target: "cit_"#_pos".check"; \
+         } \
+         program { \
+            name:    "cit_"#_pos".holiday"; \
+            signal:  "cit_"#_pos",holiday"; \
+            source:  "elm"; \
+            action:  STATE_SET "visible" 0.0; \
+            target: "cit_"#_pos".hd"; \
+         } \
+      }
+
+   group { name: "elm/calendar/base/default";
+       images {
+           image: "shelf_inset.png" COMP;
+           image: "bt_base1.png" COMP;
+           image: "bt_hilight.png" COMP;
+           image: "bt_shine.png" COMP;
+           image: "bt_glow.png" COMP;
+           image: "bt_dis_base.png" COMP;
+           image: "bt_dis_hilight.png" COMP;
+           image: "sp_bt_l.png" COMP;
+           image: "sp_bt_r.png" COMP;
+           image: "bt_sm_base1.png" COMP;
+           image: "bt_sm_shine.png" COMP;
+           image: "bt_sm_hilight.png" COMP;
+           image: "ilist_1.png" COMP;
+           image: "ilist_2.png" COMP;
+           image: "ilist_item_shadow.png" COMP;
+           image: "check.png" COMP;
+       }
+       parts {
+           part { name: "bg";
+               type: RECT;
+               description { state: "default" 0.0;
+                   min: 0 30;
+                   rel1.offset: 1 1;
+                   rel2.offset: -2 -2;
+                   color: 255 255 255 0;
+                   align: 0.0 0.5;
+               }
+           }
+           part { name: "spinner-base";
+               type: RECT;
+               mouse_events: 0;
+               description { state: "default" 0.0;
+                   min: 24 24;
+                   max: 999999 24;
+                   rel1.to: "bg";
+                  rel1.offset: 6 6;
+                   rel2.to: "bg";
+                  rel2.offset: -7 -7;
+                   color: 255 255 255 0;
+                   align: 0.0 0.0;
+               }
+           }
+           part { name: "conf_over_spinner";
+               mouse_events:  0;
+               description { state: "default" 0.0;
+                   rel1.to: "spinner-base";
+                  rel1.offset: -3 -3;
+                   rel2.to: "spinner-base";
+                  rel2.offset: 2 2;
+                   image {
+                       normal: "shelf_inset.png";
+                       border: 7 7 7 7;
+                       middle: 0;
+                   }
+                   fill.smooth : 0;
+               }
+           }
+           part { name: "table-base";
+               type: RECT;
+               mouse_events: 0;
+               description { state: "default" 0.0;
+                   min: 256 220;
+                   rel1.to_x: "bg";
+                   rel1.to_y: "spinner-base";
+                  rel1.offset: 6 6;
+                  rel1.relative: 0 1;
+                   rel2.to: "bg";
+                  rel2.offset: -7 -7;
+                   color: 255 255 255 0;
+               }
+           }
+           part { name: "conf_over_table";
+               mouse_events:  0;
+               description { state: "default" 0.0;
+                   rel1.to: "table-base";
+                  rel1.offset: -3 -3;
+                   rel2.to: "table-base";
+                  rel2.offset: 2 2;
+                   image {
+                       normal: "shelf_inset.png";
+                       border: 7 7 7 7;
+                       middle: 0;
+                   }
+                   fill.smooth : 0;
+               }
+           }
+           part { name: "header";
+               type: RECT;
+               mouse_events: 0;
+               description { state: "default" 0.0;
+                   rel1.to: "table-base";
+                  rel1.relative: 0 0;
+                   rel2.to: "table-base";
+                  rel2.relative: 1 0.1;
+                   color: 255 255 255 0;
+               }
+           }
+           part { name: "base";
+               type: RECT;
+               mouse_events: 0;
+               description { state: "default" 0.0;
+                   rel1.to_x: "table-base";
+                   rel1.to_y: "header";
+                  rel1.relative: 0 1;
+                  rel1.offset: 3 0;
+                   rel2.to: "table-base";
+                  rel2.offset: -3 0;
+                   color: 255 255 255 0;
+               }
+           }
+           part { name: "left_bt";
+               mouse_events:  1;
+               description { state: "default" 0.0;
+                   rel1 { to: "spinner-base";
+                       offset: 2 2;
+                   }
+                   rel2 { to: "spinner-base";
+                       offset: -3 -3;
+                   }
+                   align: 0.0 0.5;
+                   min: 24 24;
+                   max: 24 24;
+                   fixed: 1 1;
+                   image {
+                       normal: "bt_base1.png";
+                       border: 6 6 6 6;
+                   }
+                   fill.smooth : 0;
+               }
+               description { state: "clicked" 0.0;
+                   inherit: "default" 0.0;
+                   image.normal: "bt_base1.png";
+                   image.middle: SOLID;
+               }
+           }
+           part { name: "left_over1";
+               mouse_events: 0;
+               description { state: "default" 0.0;
+                   rel1.to: "left_bt";
+                   rel2 { to: "left_bt";
+                       relative: 1.0 0.5;
+                   }
+                   image {
+                       normal: "bt_hilight.png";
+                       border: 7 7 7 0;
+                   }
+               }
+           }
+           part { name: "left_over2";
+               mouse_events: 1;
+               repeat_events: 1;
+               description { state: "default" 0.0;
+                   rel1.to: "left_bt";
+                   rel2.to: "left_bt";
+                   image {
+                       normal: "bt_shine.png";
+                       border: 7 7 7 7;
+                   }
+               }
+           }
+           part { name: "left_over3";
+               mouse_events: 1;
+               repeat_events: 1;
+               description { state: "default" 0.0;
+                   color: 255 255 255 0;
+                   rel1.to: "left_bt";
+                   rel2.to: "left_bt";
+                   image {
+                       normal: "bt_glow.png";
+                       border: 12 12 12 12;
+                   }
+                   fill.smooth : 0;
+               }
+               description { state: "clicked" 0.0;
+                   inherit:  "default" 0.0;
+                   visible: 1;
+                   color: 255 255 255 255;
+               }
+           }
+           part { name: "right_bt";
+               mouse_events:  1;
+               description { state: "default" 0.0;
+                   rel1 { to: "spinner-base";
+                       offset: -27 3;
+                   }
+                   rel2 { to: "spinner-base";
+                       offset: -3 -3;
+                   }
+                   align: 1.0 0.5;
+                   min: 24 24;
+                   max: 24 24;
+                   fixed: 1 1;
+                   image {
+                       normal: "bt_base1.png";
+                       border: 5 5 4 12;
+                   }
+                   fill.smooth : 0;
+               }
+               description { state: "clicked" 0.0;
+                   inherit: "default" 0.0;
+                   image.normal: "bt_base1.png";
+                   image.middle: SOLID;
+               }
+           }
+           part { name: "right_over1";
+               mouse_events: 0;
+               description { state: "default" 0.0;
+                   rel1.to: "right_bt";
+                   rel2 { to: "right_bt";
+                       relative: 1.0 0.5;
+                   }
+                   image {
+                       normal: "bt_hilight.png";
+                       border: 7 7 7 0;
+                   }
+               }
+           }
+           part { name: "right_over2";
+               mouse_events: 1;
+               repeat_events: 1;
+               description { state: "default" 0.0;
+                   rel1.to: "right_bt";
+                   rel2.to: "right_bt";
+                   image {
+                       normal: "bt_shine.png";
+                       border: 7 7 7 7;
+                   }
+               }
+           }
+           part { name: "right_over3";
+               mouse_events: 1;
+               repeat_events: 1;
+               description { state: "default" 0.0;
+                   color: 255 255 255 0;
+                   rel1.to: "right_bt";
+                   rel2.to: "right_bt";
+                   image {
+                       normal: "bt_glow.png";
+                       border: 12 12 12 12;
+                   }
+                   fill.smooth : 0;
+               }
+               description { state: "clicked" 0.0;
+                   inherit:  "default" 0.0;
+                   visible: 1;
+                   color: 255 255 255 255;
+               }
+           }
+           part { name: "left_bt_icon";
+               repeat_events: 1;
+               description { state: "default" 0.0;
+                   rel1.to: "left_bt";
+                   rel2.to: "left_bt";
+                   align: 0.5 0.5;
+                   min: 16 16;
+                   max: 16 16;
+                   image.normal: "sp_bt_l.png";
+               }
+           }
+           part { name: "right_bt_icon";
+               repeat_events: 1;
+               description { state: "default" 0.0;
+                   rel1.to: "right_bt";
+                   rel2.to: "right_bt";
+                   align: 0.5 0.5;
+                   min: 16 16;
+                   max: 16 16;
+                   image.normal: "sp_bt_r.png";
+               }
+           }
+           part { name: "month_text";
+               type: TEXT;
+               mouse_events: 0;
+               scale: 1;
+               description { state: "default" 0.0;
+                   align: 0 0.5;
+                   fixed: 1 1;
+                   rel1 { relative: 1.0 0.0;
+                       offset: 3 2;
+                       to: "left_bt";
+                       to_y: "spinner-base";
+                   }
+                   rel2 { relative: 0.0 1.0;
+                       offset: -3 -2;
+                       to_x: "right_bt";
+                       to_y: "spinner-base";
+                   }
+                   color: 0 0 0 255;
+                   text {
+                       font: "Sans,Edje-Vera";
+                       size: 12;
+                       min: 1 1;
+                       align: 0.5 0.5;
+                   }
+               }
+           }
+          CH(0)   CH(1)   CH(2)   CH(3)   CH(4)   CH(5)   CH(6)
+          CIT(0)  CIT(1)  CIT(2)  CIT(3)  CIT(4)  CIT(5)  CIT(6)
+          CIT(7)  CIT(8)  CIT(9)  CIT(10) CIT(11) CIT(12) CIT(13)
+          CIT(14) CIT(15) CIT(16) CIT(17) CIT(18) CIT(19) CIT(20)
+          CIT(21) CIT(22) CIT(23) CIT(24) CIT(25) CIT(26) CIT(27)
+          CIT(28) CIT(29) CIT(30) CIT(31) CIT(32) CIT(33) CIT(34)
+          CIT(35) CIT(36) CIT(37) CIT(38) CIT(39) CIT(40) CIT(41)
+       }
+       programs {
+           program { name: "dec_start";
+               signal: "mouse,down,1";
+               source: "left_bt";
+               action: SIGNAL_EMIT "elm,action,decrement,start" "";
+           }
+           program { name: "dec_stop";
+               signal: "mouse,up,1";
+               source: "left_bt";
+               action: SIGNAL_EMIT "elm,action,stop" "";
+           }
+           program { name: "inc_start";
+               signal: "mouse,down,1";
+               source: "right_bt";
+               action: SIGNAL_EMIT "elm,action,increment,start" "";
+           }
+           program { name: "inc_stop";
+               signal: "mouse,up,1";
+               source: "right_bt";
+               action: SIGNAL_EMIT "elm,action,stop" "";
+           }
+           program {
+               name:   "left_bt_click";
+               signal: "mouse,down,1";
+               source: "left_over2";
+               action: STATE_SET "clicked" 0.0;
+               target: "left_bt";
+           }
+           program {
+               name:   "left_bt_unclick";
+               signal: "mouse,up,1";
+               source: "left_over2";
+               action: STATE_SET "default" 0.0;
+               target: "left_bt";
+           }
+           program {
+               name:   "left_bt_click2";
+               signal: "mouse,down,1";
+               source: "left_over3";
+               action: STATE_SET "clicked" 0.0;
+               target: "left_over3";
+           }
+           program {
+               name:   "left_bt_unclick2";
+               signal: "mouse,up,1";
+               source: "left_over3";
+               action: STATE_SET "default" 0.0;
+               transition: DECELERATE 0.5;
+               target: "left_over3";
+           }
+           program {
+               name:   "right_bt_click";
+               signal: "mouse,down,1";
+               source: "right_over2";
+               action: STATE_SET "clicked" 0.0;
+               target: "right_bt";
+           }
+           program {
+               name:   "right_bt_unclick";
+               signal: "mouse,up,1";
+               source: "right_over2";
+               action: STATE_SET "default" 0.0;
+               target: "right_bt";
+           }
+           program {
+               name:   "right_bt_click2";
+               signal: "mouse,down,1";
+               source: "right_over3";
+               action: STATE_SET "clicked" 0.0;
+               target: "right_over3";
+           }
+           program {
+               name:   "right_bt_unclick2";
+               signal: "mouse,up,1";
+               source: "right_over3";
+               action: STATE_SET "default" 0.0;
+               transition: DECELERATE 0.5;
+               target: "right_over3";
+           }
+       }
+   }
+
+#undef CIT
+#undef CH
index a39b14b485426e1f433323ea625c26f7964084cf..37a8c693b4a88ac3b1e4f7d821f09cff1d49bfa6 100644 (file)
@@ -1612,6 +1612,31 @@ extern "C" {
    EAPI Eina_Bool elm_animator_operating_get(Elm_Animator *animator);
    EAPI unsigned int elm_animator_repeat_get(Elm_Animator *animator);
 
+   /* calendar */
+   typedef enum {ELM_CALENDAR_UNIQUE, ELM_CALENDAR_DAILY, ELM_CALENDAR_WEEKLY, ELM_CALENDAR_MONTHLY, ELM_CALENDAR_ANNUALLY} Elm_Calendar_Mark_Repeat;
+   typedef struct _Elm_Calendar_Mark Elm_Calendar_Mark;
+
+   EAPI Evas_Object *elm_calendar_add(Evas_Object *parent);
+   EAPI const char **elm_calendar_weekdays_names_get(const Evas_Object *obj);
+   EAPI void elm_calendar_weekdays_names_set(Evas_Object *obj, const char *weekdays[]);
+   EAPI double elm_calendar_interval_get(const Evas_Object *obj);
+   EAPI void elm_calendar_interval_set(Evas_Object *obj, double interval);
+   EAPI void elm_calendar_min_max_year_get(const Evas_Object *obj, int *min, int *max);
+   EAPI void elm_calendar_min_max_year_set(Evas_Object *obj, int min, int max);
+   EAPI Eina_Bool elm_calendar_day_selection_enabled_get(const Evas_Object *obj);
+   EAPI void elm_calendar_day_selection_enabled_set(Evas_Object *obj, Eina_Bool enabled);
+   EAPI Eina_Bool elm_calendar_selected_time_get(const Evas_Object *obj, struct tm *selected_time);
+   EAPI void elm_calendar_selected_time_set(Evas_Object *obj, struct tm *selected_time);
+   EAPI void elm_calendar_format_function_set(Evas_Object *obj, char * (*format_function) (struct tm *stime));
+   EAPI Elm_Calendar_Mark *elm_calendar_mark_add(Evas_Object *obj, const char *mark_type, struct tm *mark_time, Elm_Calendar_Mark_Repeat repeat);
+   EAPI void elm_calendar_mark_del(Elm_Calendar_Mark *mark);
+   EAPI void elm_calendar_marks_clear(Evas_Object *obj);
+   EAPI const Eina_List *elm_calendar_marks_get(const Evas_Object *obj);
+   EAPI void elm_calendar_marks_draw(Evas_Object *obj);
+   /* smart callbacks called:
+    * changed - emitted when the user select a day or change the displayed
+    * month.
+    */
 
 #ifdef __cplusplus
 }
index ce96d9c3738711e3a529369442ce71bf2c9a539d..0dd72c41e20c2417024e0a4b8f11d4de7e919cb4 100644 (file)
@@ -78,6 +78,7 @@ elm_mapbuf.c \
 elm_thumb.c \
 elm_config.c \
 elm_animator.c \
+elm_calendar.c \
 \
 elc_anchorblock.c \
 elc_anchorview.c \
diff --git a/src/lib/elm_calendar.c b/src/lib/elm_calendar.c
new file mode 100644 (file)
index 0000000..c6a3c03
--- /dev/null
@@ -0,0 +1,1013 @@
+#include <Elementary.h>
+#include "elm_priv.h"
+
+/**
+ * @defgroup Calendar
+ *
+ * A calendar is a widget that allows the user to select a date. It has
+ * support to adding check marks (holidays and checks by default). The calendar
+ * is displayed one month at a time.
+ *
+ * Weekday names and the function used to format month and year to
+ * be displayed can be set, giving more flexibility to this widget.
+ *
+ * Signals that you can add callbacks for are:
+ *
+ * changed - emitted when the user selects a day or changes the displayed
+ * month, what actually changes the selected day as well.
+ */
+typedef struct _Widget_Data Widget_Data;
+
+struct _Widget_Data
+{
+   Evas_Object *calendar;
+   Eina_List *marks;
+   double interval, first_interval;
+   int year_min, year_max, spin_speed;
+   int today_it, selected_it, first_day_it;
+   Ecore_Timer *spin, *update_timer;
+   char * (*format_func) (struct tm *stime);
+   const char *weekdays[7];
+   struct tm current_time, selected_time;
+   Eina_Bool selection_enabled : 1;
+};
+
+struct _Elm_Calendar_Mark
+{
+   Evas_Object *obj;
+   Eina_List *node;
+   struct tm mark_time;
+   const char *mark_type;
+   Elm_Calendar_Mark_Repeat repeat;
+};
+
+static const char *widtype = NULL;
+
+static const char *_days_abbrev[] =
+{
+   "Sun", "Mon", "Tue", "Wed",
+   "Thu", "Fri", "Sat"
+};
+
+static int _days_in_month[2][12] =
+{
+   {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+   {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
+};
+
+static Elm_Calendar_Mark *
+_mark_new(Evas_Object *obj, const char *mark_type, struct tm *mark_time, Elm_Calendar_Mark_Repeat repeat)
+{
+   Widget_Data *wd = elm_widget_data_get(obj);
+   Elm_Calendar_Mark *mark;
+
+   if (!wd) return NULL;
+   mark = calloc(1, sizeof(Elm_Calendar_Mark));
+   if (!mark) return NULL;
+   mark->obj = obj;
+   mark->mark_type = eina_stringshare_add(mark_type);
+   mark->mark_time = *mark_time;
+   mark->repeat = repeat;
+   return mark;
+}
+
+static inline void
+_mark_free(Elm_Calendar_Mark *mark)
+{
+   eina_stringshare_del(mark->mark_type);
+   free(mark);
+}
+
+static void
+_sizing_eval(Evas_Object *obj)
+{
+   Widget_Data *wd = elm_widget_data_get(obj);
+   Evas_Coord minw = -1, minh = -1;
+   if (!wd) return;
+   elm_coords_finger_size_adjust(8, &minw, 7, &minh);
+   edje_object_size_min_restricted_calc(wd->calendar, &minw, &minh, minw, minh);
+   evas_object_size_hint_min_set(obj, minw, minh);
+   evas_object_size_hint_max_set(obj, -1, -1);
+}
+
+static inline int
+_maxdays_get(struct tm *time)
+{
+   int month, year;
+
+   month = time->tm_mon;
+   year = time->tm_year + 1900;
+
+   return _days_in_month[(!(year % 4) && (!(year % 4) || (year % 100)))][month];
+}
+
+static inline void
+_unselect(Widget_Data *wd, int selected)
+{
+   char emission[18];
+   snprintf(emission, sizeof(emission), "cit_%i,unselected", selected);
+   edje_object_signal_emit(wd->calendar, emission, "elm");
+}
+
+static inline void
+_select(Widget_Data *wd, int selected)
+{
+   char emission[16];
+   snprintf(emission, sizeof(emission), "cit_%i,selected", selected);
+   edje_object_signal_emit(wd->calendar, emission, "elm");
+}
+
+static inline void
+_not_today(Widget_Data *wd)
+{
+   char emission[17];
+   snprintf(emission, sizeof(emission), "cit_%i,not_today", wd->today_it);
+   edje_object_signal_emit(wd->calendar, emission, "elm");
+   wd->today_it = -1;
+}
+
+static inline void
+_today(Widget_Data *wd, int it)
+{
+   char emission[13];
+   snprintf(emission, sizeof(emission), "cit_%i,today", it);
+   edje_object_signal_emit(wd->calendar, emission, "elm");
+   wd->today_it = it;
+}
+
+static char *
+_format_month_year(struct tm *stime)
+{
+   char buf[32];
+   if (!strftime(buf, sizeof(buf), "%B %Y", stime)) return NULL;
+   return strdup(buf);
+}
+
+static inline void
+_cit_mark(Evas_Object *cal, int cit, const char *mtype)
+{
+   char sign[64];
+   snprintf(sign, sizeof(sign), "cit_%i,%s", cit, mtype);
+   edje_object_signal_emit(cal, sign, "elm");
+}
+
+static inline int
+_weekday_get(int first_week_day, int day)
+{
+   return (day + first_week_day - 1) % 7;
+}
+
+static void
+_populate(Evas_Object *obj)
+{
+   int maxdays, day, mon, year, i;
+   Elm_Calendar_Mark *mark;
+   char part[12], day_s[3];
+   struct tm first_day;
+   Eina_List *l;
+   char *buf;
+   Widget_Data *wd = elm_widget_data_get(obj);
+
+   if (!wd) return;
+
+   if (wd->today_it > 0) _not_today(wd);
+
+   maxdays = _maxdays_get(&wd->selected_time);
+   mon = wd->selected_time.tm_mon;
+   year = wd->selected_time.tm_year;
+
+   /* Set selected month */
+   buf = wd->format_func(&wd->selected_time);
+   if (buf) {
+     edje_object_part_text_set(wd->calendar, "month_text", buf);
+     free(buf);
+   }
+   else
+     edje_object_part_text_set(wd->calendar, "month_text", "");
+
+   /* Set days */
+   day = 0;
+   first_day = wd->selected_time;
+   first_day.tm_mday = 1;
+   mktime(&first_day);
+
+   for (i = 0; i < 42; i++)
+     {
+       if ((!day) && (i == first_day.tm_wday))
+         {
+            day = 1;
+            wd->first_day_it = i;
+         }
+
+       if ((day == wd->current_time.tm_mday)
+             && (mon == wd->current_time.tm_mon)
+             && (year == wd->current_time.tm_year))
+         _today(wd, i);
+
+       if (day == wd->selected_time.tm_mday)
+         {
+            if ((wd->selected_it > -1) && (wd->selected_it != i))
+              _unselect(wd, wd->selected_it);
+
+            if (wd->selection_enabled) _select(wd, i);
+
+            wd->selected_it = i;
+         }
+
+       if (day && (day <= maxdays))
+         snprintf(day_s, sizeof(day_s), "%d", day++);
+       else
+            day_s[0] = 0;
+
+       snprintf(part, sizeof(part), "cit_%i.text", i);
+       edje_object_part_text_set(wd->calendar, part, day_s);
+       /* Clear previous marks */
+       _cit_mark(wd->calendar, i, "clear");
+     }
+
+   /* Set marks */
+   EINA_LIST_FOREACH(wd->marks, l, mark)
+     {
+       struct tm *mtime = &mark->mark_time;
+       int mon = wd->selected_time.tm_mon;
+       int year = wd->selected_time.tm_year;
+       int mday_it = mtime->tm_mday + wd->first_day_it - 1;
+
+       switch (mark->repeat)
+         {
+          case ELM_CALENDAR_UNIQUE:
+             if ((mtime->tm_mon == mon) && (mtime->tm_year == year))
+               _cit_mark(wd->calendar, mday_it, mark->mark_type);
+             break;
+          case ELM_CALENDAR_DAILY:
+             if (((mtime->tm_year == year) && (mtime->tm_mon < mon)) ||
+                   (mtime->tm_year < year))
+               day = 1;
+             else if ((mtime->tm_year == year) && (mtime->tm_mon == mon))
+               day = mtime->tm_mday;
+             else
+               break;
+             for (; day <= maxdays; day++)
+               _cit_mark(wd->calendar, day + wd->first_day_it - 1,
+                     mark->mark_type);
+             break;
+          case ELM_CALENDAR_WEEKLY:
+             if (((mtime->tm_year == year) && (mtime->tm_mon < mon)) ||
+                   (mtime->tm_year < year))
+               day = 1;
+             else if ((mtime->tm_year == year) && (mtime->tm_mon == mon))
+               day = mtime->tm_mday;
+             else
+               break;
+             for (; day <= maxdays; day++)
+               if (mtime->tm_wday == _weekday_get(wd->first_day_it, day))
+                 _cit_mark(wd->calendar, day + wd->first_day_it - 1,
+                       mark->mark_type);
+             break;
+          case ELM_CALENDAR_MONTHLY:
+             if (((mtime->tm_year < year) ||
+                      ((mtime->tm_year == year) && (mtime->tm_mon <= mon))) &&
+                   (mtime->tm_mday <= maxdays))
+               _cit_mark(wd->calendar, mday_it, mark->mark_type);
+             break;
+          case ELM_CALENDAR_ANNUALLY:
+             if ((mtime->tm_year <= year) && (mtime->tm_mon == mon) &&
+                   (mtime->tm_mday <= maxdays))
+               _cit_mark(wd->calendar, mday_it, mark->mark_type);
+             break;
+         }
+     }
+}
+
+static void
+_set_headers(Evas_Object *obj)
+{
+   static char part[] = "ch_0.text";
+   int i;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+
+   for (i = 0; i < 7; i++)
+     {
+       part[3] = i + '0';
+       edje_object_part_text_set(wd->calendar, part, wd->weekdays[i]);
+     }
+}
+
+static void
+_del_hook(Evas_Object *obj)
+{
+   int i;
+   Elm_Calendar_Mark *mark;
+   Widget_Data *wd = elm_widget_data_get(obj);
+
+   if (!wd) return;
+
+   if (wd->spin) ecore_timer_del(wd->spin);
+   if (wd->update_timer) ecore_timer_del(wd->update_timer);
+
+   if (wd->marks)
+       EINA_LIST_FREE(wd->marks, mark)
+       {
+           _mark_free(mark);
+       }
+
+   for (i = 0; i < 7; i++)
+     eina_stringshare_del(wd->weekdays[i]);
+
+   free(wd);
+}
+
+static void
+_theme_hook(Evas_Object *obj)
+{
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   _elm_theme_object_set(obj, wd->calendar, "calendar", "base",
+        elm_widget_style_get(obj));
+   _set_headers(obj);
+   _populate(obj);
+   edje_object_message_signal_process(wd->calendar);
+   edje_object_scale_set(wd->calendar,
+        elm_widget_scale_get(obj) * _elm_config->scale);
+   _sizing_eval(obj);
+}
+
+static void
+_signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
+{
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   edje_object_signal_emit(wd->calendar, emission, source);
+}
+
+static void
+_signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
+{
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   edje_object_signal_callback_add(wd->calendar, emission,
+        source, func_cb, data);
+}
+
+static void *
+_signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source))
+{
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return NULL;
+   return edje_object_signal_callback_del(wd->calendar, emission, source,
+        func_cb);
+}
+
+/* Set correct tm_wday and tm_yday after other fields changes*/
+static inline void
+_fix_selected_time(Widget_Data *wd)
+{
+   mktime(&wd->selected_time);
+}
+
+static Eina_Bool
+_update_month(Evas_Object *obj, int delta)
+{
+   struct tm time_check;
+   int maxdays;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return EINA_FALSE;
+
+   /* check if it's a valid time. for 32 bits, year greater than 2037 is not */
+   time_check = wd->selected_time;
+   time_check.tm_mon += delta;
+   if (mktime(&time_check) == -1)
+     return EINA_FALSE;
+
+   wd->selected_time.tm_mon += delta;
+   if (wd->selected_time.tm_mon < 0)
+     {
+       if (wd->selected_time.tm_year == wd->year_min)
+         {
+            wd->selected_time.tm_mon++;
+            return EINA_FALSE;
+         }
+       wd->selected_time.tm_mon = 11;
+       wd->selected_time.tm_year--;
+     }
+   else if (wd->selected_time.tm_mon > 11)
+     {
+       if (wd->selected_time.tm_year == wd->year_max)
+         {
+            wd->selected_time.tm_mon--;
+            return EINA_FALSE;
+         }
+       wd->selected_time.tm_mon = 0;
+       wd->selected_time.tm_year++;
+     }
+
+   maxdays = _maxdays_get(&wd->selected_time);
+   if (wd->selected_time.tm_mday > maxdays)
+     wd->selected_time.tm_mday = maxdays;
+
+   _fix_selected_time(wd);
+   evas_object_smart_callback_call(obj, "changed", NULL);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_spin_value(void *data)
+{
+   Widget_Data *wd = elm_widget_data_get(data);
+   if (!wd) return ECORE_CALLBACK_CANCEL;
+   if (_update_month(data, wd->spin_speed)) _populate(data);
+   wd->interval = wd->interval / 1.05;
+   ecore_timer_interval_set(wd->spin, wd->interval);
+   return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_button_inc_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
+{
+   Widget_Data *wd = elm_widget_data_get(data);
+   if (!wd) return;
+   wd->interval = wd->first_interval;
+   wd->spin_speed = 1;
+   if (wd->spin) ecore_timer_del(wd->spin);
+   wd->spin = ecore_timer_add(wd->interval, _spin_value, data);
+   _spin_value(data);
+}
+
+static void
+_button_dec_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
+{
+   Widget_Data *wd = elm_widget_data_get(data);
+   if (!wd) return;
+   wd->interval = wd->first_interval;
+   wd->spin_speed = -1;
+   if (wd->spin) ecore_timer_del(wd->spin);
+   wd->spin = ecore_timer_add(wd->interval, _spin_value, data);
+   _spin_value(data);
+}
+
+static void
+_button_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
+{
+   Widget_Data *wd = elm_widget_data_get(data);
+   if (!wd) return;
+   wd->interval = wd->first_interval;
+   if (wd->spin) ecore_timer_del(wd->spin);
+   wd->spin = NULL;
+}
+
+static int
+_get_item_day(Evas_Object *obj, int selected_it)
+{
+   int day;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return 0;
+
+   day = selected_it - wd->first_day_it + 1;
+   if ((day < 0) || (day > _maxdays_get(&wd->selected_time)))
+     return 0;
+
+   return day;
+}
+
+static void
+_day_selected(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source)
+{
+   int sel_it, day;
+   Widget_Data *wd = elm_widget_data_get(data);
+   if ((!wd) || (!wd->selection_enabled))
+      return;
+   sel_it = atoi(source);
+   day = _get_item_day(data, sel_it);
+   if (!day)
+     return;
+   _unselect(wd, wd->selected_it);
+   wd->selected_it = sel_it;
+   wd->selected_time.tm_mday = day;
+   _select(wd, wd->selected_it);
+   _fix_selected_time(wd);
+   evas_object_smart_callback_call(data, "changed", NULL);
+}
+
+static inline int
+_time_to_next_day(struct tm *t)
+{
+  return (((24 - t->tm_hour) * 60) - t->tm_min) * 60 - t->tm_sec;
+}
+
+static Eina_Bool
+_update_cur_date(void *data)
+{
+   time_t current_time;
+   int t, day;
+   Widget_Data *wd = elm_widget_data_get(data);
+   if (!wd) return ECORE_CALLBACK_RENEW;
+
+   if (wd->today_it > 0) _not_today(wd);
+
+   current_time = time(NULL);
+   localtime_r(&current_time, &wd->current_time);
+   t = _time_to_next_day(&wd->current_time);
+   ecore_timer_interval_set(wd->update_timer, t);
+
+   if ((wd->current_time.tm_mon != wd->selected_time.tm_mon) ||
+        (wd->current_time.tm_year!= wd->selected_time.tm_year))
+     return ECORE_CALLBACK_RENEW;
+
+   day = wd->current_time.tm_mday + wd->first_day_it - 1;
+   _today(wd, day);
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+/**
+ * Add a new calendar to the parent
+ *
+ * @param parent The parent object
+ * @return The new object or NULL if it cannot be created
+ *
+ * @ingroup Calendar
+ */
+EAPI Evas_Object *
+elm_calendar_add(Evas_Object *parent)
+{
+   time_t current_time;
+   Evas_Object *obj;
+   Widget_Data *wd;
+   int i, t;
+   Evas *e;
+
+   wd = ELM_NEW(Widget_Data);
+   e = evas_object_evas_get(parent);
+   obj = elm_widget_add(e);
+   ELM_SET_WIDTYPE(widtype, "calendar");
+   elm_widget_type_set(obj, "calendar");
+   elm_widget_sub_object_add(parent, obj);
+   elm_widget_data_set(obj, wd);
+   elm_widget_del_hook_set(obj, _del_hook);
+   elm_widget_theme_hook_set(obj, _theme_hook);
+   elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
+   elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
+   elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
+
+   wd->first_interval = 0.85;
+   wd->year_min = 2;
+   wd->year_max = -1;
+   wd->today_it = -1;
+   wd->selected_it = -1;
+   wd->first_day_it = -1;
+   wd->selection_enabled = EINA_TRUE;
+   wd->format_func = _format_month_year;
+   wd->marks = NULL;
+
+   wd->calendar = edje_object_add(e);
+   _elm_theme_object_set(obj, wd->calendar, "calendar", "base", "default");
+   elm_widget_resize_object_set(obj, wd->calendar);
+
+   edje_object_signal_callback_add(wd->calendar, "elm,action,increment,start",
+                                   "*", _button_inc_start, obj);
+   edje_object_signal_callback_add(wd->calendar, "elm,action,decrement,start",
+                                   "*", _button_dec_start, obj);
+   edje_object_signal_callback_add(wd->calendar, "elm,action,stop",
+                                   "*", _button_stop, obj);
+   edje_object_signal_callback_add(wd->calendar, "elm,action,selected",
+                                   "*", _day_selected, obj);
+
+   for (i = 0; i < 7; i++)
+     wd->weekdays[i] = eina_stringshare_add(_days_abbrev[i]);
+
+   current_time = time(NULL);
+   localtime_r(&current_time, &wd->selected_time);
+   wd->current_time = wd->selected_time;
+   t = _time_to_next_day(&wd->current_time);
+   wd->update_timer = ecore_timer_add(t, _update_cur_date, obj);
+
+   _set_headers(obj);
+   _populate(obj);
+   _sizing_eval(obj);
+   return obj;
+}
+
+/**
+ * Set weekdays names to display in the calendar.
+ *
+ * By default, the following abbreviations are displayed:
+ * "Sun, Mon, Tue, Wed, Thu, Fri, Sat"
+ * The first string should be related to Sunday, the second to Monday...
+ *
+ * The usage should be like this:
+ * @code
+ *   const char *weekdays[] =
+ *   {
+ *      "Sunday", "Monday", "Tuesday", "Wednesday",
+ *      "Thursday", "Friday", "Saturday"
+ *   };
+ *   elm_calendar_weekdays_names_set(calendar, weekdays);
+ * @endcode
+ *
+ * @param obj The calendar object
+ * @param weedays Array of seven strings to be used as weekday names.
+ * Warning: it must have 7 elements, or it will access invalid memory.
+ * The strings must be NULL terminated ('@\0').
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_weekdays_names_set(Evas_Object *obj, const char *weekdays[])
+{
+   int i;
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+
+   for (i = 0; i < 7; i++)
+     {
+        eina_stringshare_replace(&wd->weekdays[i], weekdays[i]);
+     }
+   _set_headers(obj);
+}
+
+/**
+ * Get weekdays names displayed in the calendar.
+ *
+ * By default, the following abbreviations are displayed:
+ * "Sun, Mon, Tue, Wed, Thu, Fri, Sat"
+ * The first string is related to Sunday, the second to Monday...
+ *
+ * @param obj The calendar object
+ * @return Array of seven strings to used as weekday names.
+ *
+ * @ingroup Calendar
+ */
+EAPI const char **
+elm_calendar_weekdays_names_get(const Evas_Object *obj)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype) NULL;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return NULL;
+   return wd->weekdays;
+}
+
+/**
+ * Set the interval for the calendar
+ *
+ * The interval value is decreased while the user increments or decrements
+ * the calendar value. The next interval value is the previous interval / 1.05,
+ * so it speed up a bit. Default value is 0.85 seconds.
+ *
+ * @param obj The calendar object
+ * @param interval The interval value in seconds
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_interval_set(Evas_Object *obj, double interval)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   wd->first_interval = interval;
+}
+
+/**
+ * Get the interval of the calendar
+ *
+ * The interval value is decreased while the user increments or decrements
+ * the calendar value. The next interval value is the previous interval / 1.05,
+ * so it speed up a bit. Default value is 0.85 seconds.
+ *
+ * @param obj The calendar object
+ * @return The value of the first interval in seconds
+ *
+ * @ingroup Calendar
+ */
+EAPI double
+elm_calendar_interval_get(const Evas_Object *obj)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return 0.0;
+   return wd->first_interval;
+}
+
+/**
+ * Set the minimum and maximum values for the year
+ *
+ * Maximum must be greater than minimum, except if you don't wan't to set
+ * maximum year.
+ * Default values are 1902 and -1.
+ *
+ * If the maximum year is a negative value, it will be limited depending of the
+ * platform architecture (2037 for 32 bits);
+ *
+ * @param obj The calendar object
+ * @param min The minimum year, greater than 1901;
+ * @param max The maximum year;
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_min_max_year_set(Evas_Object *obj, int min, int max)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   min -= 1900;
+   max -= 1900;
+   if ((wd->year_min == min) && (wd->year_max == max)) return;
+   wd->year_min = min > 2 ? min : 2;
+   if (wd->selected_time.tm_year > wd->year_max)
+     wd->selected_time.tm_year = wd->year_max;
+   if (wd->selected_time.tm_year < wd->year_min)
+     wd->selected_time.tm_year = wd->year_min;
+   _fix_selected_time(wd);
+   _populate(obj);
+}
+
+/**
+ * Get the minimum and maximum values for the year
+ *
+ * Default values are 1902 and -1.
+ *
+ * If the maximum year is a negative value, it will be limited depending of the
+ * platform architecture (2037 for 32 bits);
+ *
+ * @param obj The calendar object
+ * @param min The minimum year
+ * @param max The maximum year
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_min_max_year_get(const Evas_Object *obj, int *min, int *max)
+{
+   if (min) *min = 0;
+   if (max) *max = 0;
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   if (min) *min = wd->year_min + 1900;
+   if (max) *max = wd->year_max + 1900;
+}
+
+/**
+ * Enable or disable day selection
+ *
+ * Enabled by default. If disabled, the user can select months, but not days.
+ * It should be used if you won't need such selection for the widget usage.
+ *
+ * @param obj The calendar object
+ * @param enabled Boolean to enable (true) or disable (false) day selection
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_day_selection_enabled_set(Evas_Object *obj, Eina_Bool enabled)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   wd->selection_enabled = enabled;
+   if (enabled)
+     _select(wd, wd->selected_it);
+   else
+     _unselect(wd, wd->selected_it);
+}
+
+/**
+ * Get day selection state
+ *
+ * Enabled by default. If disabled, the user can select months, but not days.
+ * It should be used if you won't need such selection for the widget usage.
+ *
+ * @param obj The calendar object
+ * @return True if day selection is enabled, or false otherwise. It will
+ * return false if it can't get widget data.
+ *
+ * @ingroup Calendar
+ */
+EAPI Eina_Bool
+elm_calendar_day_selection_enabled_get(const Evas_Object *obj)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return EINA_FALSE;
+   return wd->selection_enabled;
+}
+
+/**
+ * Set selected time
+ *
+ * Set the time selected, changing the displayed month if needed.
+ * Selected time changes when the user changes the month or select a day.
+ *
+ * @param obj The calendar object
+ * @param selected_time A tm struct to represent the selected date
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_selected_time_set(Evas_Object *obj, struct tm *selected_time)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   wd->selected_time = *selected_time;
+   _populate(obj);
+   return;
+}
+
+/**
+ * Get selected time
+ *
+ * Get the time selected by the user.
+ * Selected time changes when the user changes the month or select a day.
+ *
+ * @param obj The calendar object
+ * @param selected_time A tm struct to represent the selected date
+ * @return It will return false if it can't get widget data, or true otherwise
+ *
+ * @ingroup Calendar
+ */
+EAPI Eina_Bool
+elm_calendar_selected_time_get(const Evas_Object *obj, struct tm *selected_time)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return EINA_FALSE;
+   if (selected_time) *selected_time = wd->selected_time;
+   return EINA_TRUE;
+}
+
+/**
+ * Set a function to format the string that will be used to display
+ * month - year
+ *
+ * By default it uses strftime with "%B %Y" format string.
+ * 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.
+ *
+ * Example:
+ * @code
+ * static char *
+ * _format_month_year(struct tm *stime)
+ * {
+ *    char buf[32];
+ *    if (!strftime(buf, sizeof(buf), "%B %Y", stime)) return NULL;
+ *    return strdup(buf);
+ * }
+ * elm_calendar_format_function_set(calendar, _format_month_year);
+ * @endcode
+ *
+ * @param obj The calendar object
+ * @param format_function Function to set the month-year string given
+ * the selected date
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_format_function_set(Evas_Object *obj, char * (*format_function) (struct tm *stime))
+{
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   wd->format_func = format_function;
+}
+
+/**
+ * Add a new mark to the calendar
+ *
+ * Add a mark that will be drawn in the calendar respecting the insertion time
+ * and periodicity. It will emit the type as signal to the widget theme.
+ * By default, it supports "holiday" and "checked", but it can be extended.
+ *
+ * It won't immediately update the calendar, drawing the marks. For this, call
+ * elm_calendar_marks_draw().
+ *
+ * Example
+ * @code
+ * struct tm selected_time;
+ * time_t current_time;
+ *
+ * current_time = time(NULL) + 5 * 84600;
+ * localtime_r(&current_time, &selected_time);
+ * elm_calendar_mark_add(cal, "holiday", selected_time, ELM_CALENDAR_ANNUALLY);
+ *
+ * current_time = time(NULL) + 1 * 84600;
+ * localtime_r(&current_time, &selected_time);
+ * elm_calendar_mark_add(cal, "checked", selected_time, ELM_CALENDAR_UNIQUE);
+ *
+ * elm_calendar_marks_draw(cal);
+ * @endcode
+ *
+ * @param obj The calendar object
+ * @param mark_type A string used to define the type of mark. It will be
+ * emmitted to the theme, that should display a related modification on these
+ * days representation.
+ * @param mark_time 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 repeat Repeat the event following this periodicity. Can be a unique
+ * mark (that don't repeat), daily, weekly, monthly or annually.
+ *
+ * @return The created mark or NULL upon failure
+ *
+ * @ingroup Calendar
+ */
+EAPI Elm_Calendar_Mark *
+elm_calendar_mark_add(Evas_Object *obj, const char *mark_type, struct tm *mark_time, Elm_Calendar_Mark_Repeat repeat)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype) NULL;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   Elm_Calendar_Mark *mark;
+   if (!wd) return NULL;
+
+   mark = _mark_new(obj, mark_type, mark_time, repeat);
+   wd->marks = eina_list_append(wd->marks, mark);
+   mark->node = eina_list_last(wd->marks);
+   return mark;
+}
+
+/**
+ * Delete mark from the calendar.
+ *
+ * @param mark The mark to delete
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_mark_del(Elm_Calendar_Mark *mark)
+{
+   Evas_Object *obj;
+   Widget_Data *wd;
+
+   if (!mark) return;
+
+   obj = mark->obj;
+   wd = elm_widget_data_get(obj);
+   if (!wd) return;
+
+   wd->marks = eina_list_remove_list(wd->marks, mark->node);
+   _mark_free(mark);
+}
+
+/**
+ * Remove all the marks from the calendar
+ *
+ * @param obj The calendar object
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_marks_clear(Evas_Object *obj)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   Elm_Calendar_Mark *mark;
+
+   if (!wd) return;
+   EINA_LIST_FREE(wd->marks, mark)
+      _mark_free(mark);
+}
+
+/**
+ * Returns a list of all the calendar marks.
+ *
+ * @param obj The calendar object
+ * @return An Eina_List* of the calendar marks, or NULL on failure
+ *
+ * @ingroup Calendar
+ */
+EAPI const Eina_List *
+elm_calendar_marks_get(const Evas_Object *obj)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype) NULL;
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return NULL;
+   return wd->marks;
+}
+
+/**
+ * Draw calendar marks.
+ *
+ * Should be used after adding, removing or clearing marks.
+ * It will go through the entire marks list updating the calendar
+ * (not a cheap function). So if lots of marks will be added,
+ * add all the marks and then call this function.
+ *
+ * When the month is changed marks will be drawed.
+ *
+ * @param obj The calendar object
+ *
+ * @ingroup Calendar
+ */
+EAPI void
+elm_calendar_marks_draw(Evas_Object *obj)
+{
+   ELM_CHECK_WIDTYPE(obj, widtype);
+   Widget_Data *wd = elm_widget_data_get(obj);
+   if (!wd) return;
+   _populate(obj);
+}