Parse back the log entries
[gregoa/zavai.git] / src / calendar.vala
1 /*
2  * widgets/calendar - zavai calendar widget
3  *
4  * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 namespace zavai {
22 namespace widgets {
23
24 private string month_path(int year, int month)
25 {
26         return "%s/cal/%04d/%02d".printf(zavai.config.homedir, year, month);
27 }
28
29 private string day_path(int year, int month, int day)
30 {
31         return "%s/%02d.txt".printf(month_path(year, month), day);
32 }
33
34
35 public class FileNotes : Gtk.ScrolledWindow
36 {
37         protected Gtk.TextBuffer text_buffer;
38         protected Gtk.TextView text;
39         protected bool text_dirty;
40         protected string fname;
41
42         public FileNotes()
43         {
44                 fname = null;
45                 text_dirty = false;
46                 text_buffer = new Gtk.TextBuffer(null);
47                 text_buffer.changed += on_textbuffer_changed;
48                 text = new Gtk.TextView.with_buffer(text_buffer);
49                 text.wrap_mode = Gtk.WrapMode.WORD;
50                 text.cursor_visible = true;
51                 text.editable = true;
52
53                 set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
54                 add(text);
55         }
56         
57         private void on_textbuffer_changed()
58         {
59                 text_dirty = true;
60         }
61
62         public void load(string new_fname)
63         {
64                 save();
65                 fname = new_fname;
66
67                 if (!FileUtils.test (fname, FileTest.IS_REGULAR))
68                         text_buffer.text = "";
69                 else
70                 {
71                         try {
72                                 string text;
73                                 FileUtils.get_contents(fname, out text);
74                                 text_buffer.text = text;
75                         } catch (FileError e) {
76                                 zavai.log.error(e.message);
77                                 text_buffer.text = "";
78                         }
79                 }
80
81                 // Scroll to beginning
82                 Gtk.TextIter iter;
83                 text_buffer.get_iter_at_offset(out iter, 0);
84                 Gtk.TextMark mark = text_buffer.create_mark(null, iter, true);
85                 text.scroll_mark_onscreen(mark);
86
87                 text_dirty = false;
88         }
89
90         public void save()
91         {
92                 if (fname == null) return;
93                 if (text_dirty == false) return;
94
95                 string mpath = Path.get_dirname(fname);
96                 if (DirUtils.create_with_parents(mpath, 0777) < 0)
97                 {
98                         zavai.log.error("Cannot create directory " + mpath);
99                         return;
100                 }
101                 try {
102                         if (text_buffer.text == "")
103                         {
104                                 if (FileUtils.test(fname, FileTest.EXISTS))
105                                         FileUtils.unlink(fname);
106                         } else {
107                                 FileUtils.set_contents(fname, text_buffer.text);
108                         }
109                 } catch (FileError e) {
110                         zavai.log.error(e.message);
111                 }
112                 text_dirty = false;
113         }
114 }
115
116 public class Next30 : Gtk.ScrolledWindow
117 {
118         protected Gtk.ListStore model;
119         protected Gtk.TreeView list;
120         public signal void selected(int year, int month, int day);
121
122         public Next30()
123         {
124                 set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
125                 model = new Gtk.ListStore(5, typeof(string), typeof(string), typeof(int), typeof(int), typeof(int));
126                 list = new Gtk.TreeView.with_model(model);
127                 list.insert_column_with_attributes (-1, "Day", new Gtk.CellRendererText(), "text", 0);
128                 list.insert_column_with_attributes (-1, "Notes", new Gtk.CellRendererText(), "text", 1);
129                 add(list);
130
131                 list.cursor_changed += on_row_selected;
132         }
133
134         //private void on_row_selected(Gtk.TreeView tv, Gtk.TreePath path, Gtk.TreeViewColumn column)
135         private void on_row_selected(Gtk.TreeView tv)
136         {
137                 Gtk.TreePath path;
138                 list.get_cursor(out path, null);
139
140                 Gtk.TreeIter iter;
141
142                 if (!model.get_iter(out iter, path)) return;
143                 Value year, month, day;
144                 model.get_value(iter, 2, out year);
145                 model.get_value(iter, 3, out month);
146                 model.get_value(iter, 4, out day);
147                 selected((int)year, (int)month, (int)day);
148         }
149
150         protected void add_day(Date date, string notes)
151         {
152                 var buffer = new char[64];
153                 date.strftime(buffer, "%d %a");
154                 Gtk.TreeIter iter;
155                 model.append (out iter);
156                 model.set(iter, 0, (string)buffer);
157                 model.set(iter, 1, notes);
158                 model.set(iter, 2, date.get_year());
159                 model.set(iter, 3, date.get_month());
160                 model.set(iter, 4, date.get_day());
161         }
162
163         public void update(Date start)
164         {
165                 Date date = start;
166                 model.clear();
167                 for (uint i = 0; i < 30; ++i)
168                 {
169                         string path = day_path(date.get_year(), date.get_month(), date.get_day());
170                         string text;
171                         if (!FileUtils.test (path, FileTest.IS_REGULAR))
172                                 text = "";
173                         else
174                         {
175                                 try {
176                                         FileUtils.get_contents(path, out text);
177                                 } catch (FileError e) {
178                                         text = "";
179                                 }
180                         }
181                         if (i == 0 || text != "")
182                         {
183                                 add_day(date, text);
184                         }
185                         date.add_days(1);
186                 }
187         }
188 }
189
190 public class Calendar : Gtk.VBox
191 {
192         protected Gtk.Calendar calendar;
193         protected Gtk.Notebook notebook;
194         protected Gtk.Notebook day_notebook;
195         protected FileNotes month_notes;
196         protected FileNotes day_notes;
197         protected FileNotes general_notes;
198         protected Next30 next_30;
199         protected int cur_year;
200         protected int cur_month;
201         protected int cur_day;
202
203         public Calendar()
204         {
205                 calendar = new Gtk.Calendar();
206                 calendar.day_selected += on_day_selected;
207                 calendar.month_changed += on_month_changed;
208                 pack_start(calendar, false, false, 0);
209
210                 notebook = new Gtk.Notebook();
211
212                 day_notebook = new Gtk.Notebook();
213                 next_30 = new Next30();
214                 next_30.selected += on_next30_selected;
215                 day_notebook.append_page(next_30, new Gtk.Label("Next 30"));
216                 day_notes = new FileNotes();
217                 day_notebook.append_page(day_notes, new Gtk.Label("Day"));
218                 notebook.append_page(day_notebook, new Gtk.Label("Day"));
219                 day_notebook.show_tabs = false; 
220
221                 month_notes = new FileNotes();
222                 notebook.append_page(month_notes, new Gtk.Label("Month"));
223                 general_notes = new FileNotes();
224                 general_notes.load(zavai.config.homedir + "/cal/notes.txt");
225                 notebook.append_page(general_notes, new Gtk.Label("General"));
226
227                 pack_start(notebook, true, true, 0);
228
229                 on_month_changed();
230                 on_day_selected();
231         }
232
233         private void on_next30_selected(int year, int month, int day)
234         {
235                 // Select this day in the calendar
236                 calendar.year = year;
237                 calendar.month = month-1;
238                 calendar.day = day;
239
240                 // View the day notes
241                 day_notebook.set_current_page(1);
242
243                 // TODO: when clicking on calendar, show next30 again
244         }
245
246         private void on_month_changed()
247         {
248                 flush();
249                 calendar.clear_marks();
250                 string mpath = month_path(calendar.year, calendar.month + 1);
251                 month_notes.load(mpath + "/00.txt");
252                 Dir dir;
253                 try {
254                         dir = Dir.open(mpath);
255                 } catch (FileError e) {
256                         zavai.log.error(e.message);
257                         return;
258                 }
259                 while (true)
260                 {
261                         var d = dir.read_name();
262                         if (d == null) break;
263                         if (d[0].isdigit() && d.has_suffix(".txt") && d.size() == 6)
264                         {
265                                 calendar.mark_day((int)d.to_ulong(null, 10));
266                         }
267                 }
268         }
269
270         private void on_day_selected()
271         {
272                 cur_year = calendar.year;
273                 cur_month = calendar.month + 1;
274                 cur_day = calendar.day;
275
276                 string path = day_path(cur_year, cur_month, cur_day);
277                 day_notes.load(path);
278
279                 string mpath = month_path(cur_year, cur_month);
280                 month_notes.load(mpath + "/00.txt");
281
282                 Date date = Date();
283                 date.set_dmy((DateDay)cur_day, cur_month, (DateYear)cur_year);
284                 next_30.update(date);
285                 day_notebook.set_current_page(0);
286         }
287
288         public void flush()
289         {
290                 day_notes.save();
291                 month_notes.save();
292                 general_notes.save();
293         }
294
295         public void show_today()
296         {
297                 // Go to current date
298                 var now = Time.local(time_t());
299                 calendar.year = now.year + 1900;
300                 calendar.month = now.month;
301                 calendar.day = now.day;
302                 on_month_changed();
303         }
304 }
305
306 }
307 }