163da1550ffb82a54b6c0d3a047eaa8eec37f603
[beifahrer] / src / lift-detail-window.vala
1 /* This file is part of Beifahrer.
2  *
3  * Copyright (C) 2010 Philipp Zabel
4  *
5  * Beifahrer is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * Beifahrer is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Beifahrer. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 using Gtk;
20 using Hildon;
21
22 public class LiftDetailWindow : StackableWindow {
23         private const string BROWSER_SERVICE = "com.nokia.osso_browser";
24         private const string BROWSER_PATH = "/com/nokia/osso_browser";
25         private const string BROWSER_IF = "com.nokia.osso_browser";
26
27         private const string CALENDAR_SERVICE = "com.nokia.calendar";
28         private const string CALENDAR_PATH = "/com/nokia/calendar";
29         private const string CALENDAR_IF = "com.nokia.calendar";
30
31         AdacMitfahrclub adac;
32         Lift lift;
33         Alignment alignment;
34         PannableArea pannable;
35         VBox vbox;
36         OssoABook.Button button_route;
37         OssoABook.Button button_calendar;
38         Label label_driver;
39         OssoABook.Button button_phone;
40         OssoABook.Button button_sms;
41         Image image_smoke;
42         Label label;
43         Label label_changed;
44         Table table;
45         string ics_filename = null;
46
47         public LiftDetailWindow (AdacMitfahrclub _adac, Lift _lift) {
48                 adac = _adac;
49                 lift = _lift;
50
51                 update_lift_details ();
52
53                 if (lift.description == null)
54                         get_lift_details.begin ();
55         }
56
57         construct {
58                 set_title ("Beifahrer");
59
60                 var menu = new AppMenu ();
61                 var add_contact = new Gtk.Button.with_label (_("New contact"));
62                 add_contact.show ();
63                 menu.append (add_contact);
64                 var goto_website = new Gtk.Button.with_label (_("Show website"));
65                 goto_website.show ();
66                 menu.append (goto_website);
67                 set_main_menu (menu);
68
69                 alignment = new Alignment (0.0f, 0.0f, 1.0f, 1.0f);
70                 alignment.top_padding = MARGIN_HALF;
71                 alignment.left_padding = MARGIN_DOUBLE;
72                 alignment.right_padding = MARGIN_DOUBLE;
73
74                 pannable = new PannableArea ();
75                 pannable.hscrollbar_policy = PolicyType.NEVER;
76
77                 vbox = new VBox (false, 0);
78
79                 var hbox = new HBox (false, 0);
80
81                 button_route = new OssoABook.Button (SizeType.FINGER_HEIGHT);
82                 button_route.style = OssoABook.ButtonStyle.LABEL;
83
84                 var image_eventbox = new EventBox ();
85                 image_smoke = new Image ();
86                 image_eventbox.add (image_smoke);
87
88                 hbox.pack_start (button_route, true, true, 0);
89                 hbox.pack_start (image_eventbox, false, false, MARGIN_DEFAULT);
90
91                 table = new Table (6, 2, false);
92
93                 button_calendar = new OssoABook.Button (SizeType.FINGER_HEIGHT);
94                 button_calendar.set_icon_name ("general_calendar");
95                 button_calendar.set_icon_visible (true);
96                 button_calendar.title = _("Departure time");
97
98                 label_driver = new Label (_("Driver:"));
99                 label_driver.set_alignment (0.0f, 0.5f);
100                 Hildon.gtk_widget_set_theme_size (label_driver, SizeType.FINGER_HEIGHT);
101
102                 button_phone = new OssoABook.Button (SizeType.FINGER_HEIGHT);
103                 button_phone.set_icon_name ("general_call");
104                 button_phone.set_icon_visible (true);
105                 button_phone.title = _("Phone");
106
107                 button_sms = new OssoABook.Button (SizeType.FINGER_HEIGHT);
108                 button_sms.set_icon_name ("general_sms");
109                 button_sms.set_icon_visible (true);
110                 button_sms.title = _("SMS");
111
112                 label = new Label ("");
113                 label.set_alignment (0.0f, 0.5f);
114                 label.set_line_wrap (true);
115
116                 label_changed = new Label ("");
117                 label_changed.set_alignment (1.0f, 0.0f);
118                 Hildon.helper_set_logical_color (label_changed, RcFlags.FG, StateType.NORMAL, "SecondaryTextColor");
119                 Hildon.helper_set_logical_font (label_changed, "SmallSystemFont");
120
121                 if (BeifahrerProgram.orientation.portrait) {
122                         table.attach (button_calendar, 0, 2, 0, 1, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
123                         table.attach (label_driver, 0, 2, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, MARGIN_DEFAULT, 0);
124                         table.attach (button_phone, 0, 2, 2, 3, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
125                         table.attach (button_sms, 0, 2, 3, 4, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
126                 } else {
127                         table.attach (button_calendar, 1, 2, 0, 1, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
128                         table.attach (label_driver, 0, 1, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, MARGIN_DEFAULT, 0);
129                         table.attach (button_phone, 1, 2, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
130                         table.attach (button_sms, 1, 2, 2, 3, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
131                 }
132                 table.attach (label, 0, 2, 4, 5, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, MARGIN_DEFAULT, 0);
133                 table.attach (label_changed, 0, 2, 5, 6, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, MARGIN_DEFAULT, 0);
134
135                 vbox.pack_start (hbox, true, true, 0);
136                 vbox.pack_start (table, true, true, 0);
137
138                 pannable.add_with_viewport (vbox);
139                 alignment.add (pannable);
140
141                 alignment.show_all ();
142                 add (alignment);
143
144                 BeifahrerProgram.orientation.changed.connect (on_orientation_changed);
145                 add_contact.clicked.connect (on_add_contact_clicked);
146                 goto_website.clicked.connect (on_goto_website_clicked);
147                 image_eventbox.button_press_event.connect (on_image_eventbox_button_pressed);
148                 button_calendar.clicked.connect (on_button_calendar_clicked);
149                 button_phone.clicked.connect (on_button_phone_clicked);
150                 button_sms.clicked.connect (on_button_sms_clicked);
151                 destroy.connect (on_destroy);
152                 map_event.connect (on_map_event);
153         }
154
155         private async void get_lift_details () {
156                 Hildon.gtk_window_set_progress_indicator (this, 1);
157                 yield adac.update_lift_details (lift);
158                 Hildon.gtk_window_set_progress_indicator (this, 0);
159
160                 update_lift_details ();
161         }
162
163         private void update_lift_details () {
164                 button_route.title = _("From %s to %s").printf (lift.city_from, lift.city_to);
165                 if (lift.city_via.length () > 0) {
166                         string route = _("via ");
167                         foreach (string via in lift.city_via)
168                                 route += via + " ";
169                         button_route.value = route;
170                 } else {
171                         button_route.value = "";
172                 }
173
174                 button_calendar.value = "%02d.%02d.%04d, %d:%02d".printf (lift.time.day, lift.time.month, lift.time.year, lift.time.hour, lift.time.minute);
175
176                 label_driver.set_text (_("Driver: ") + lift.name);
177                 if (lift.cell != null) {
178                         button_phone.title = _("Cell");
179                         string cell_number = E.normalize_phone_number (lift.cell);
180                         button_phone.value = cell_number;
181                         button_sms.value = cell_number;
182                 } else if (lift.phone != null) {
183                         button_phone.title = _("Phone");
184                         string phone_number = E.normalize_phone_number (lift.phone);
185                         button_phone.value = phone_number;
186                         button_sms.value = phone_number;
187                 }
188
189                 string lift_text = _("Free places: %d\n").printf (lift.places);
190                 lift_text += (lift.price != null) ? _("Price: %s\n").printf (lift.price) : _("(no price given)\n");
191                 if (LiftFlags.SMOKER in lift.flags) try {
192                         var pixbuf = IconTheme.get_default ().load_icon ("beifahrer_smoker", 32, IconLookupFlags.NO_SVG);
193                         image_smoke.pixbuf = pixbuf;
194                 } catch (Error e) {
195                 } else if (LiftFlags.NON_SMOKER in lift.flags) try {
196                         var pixbuf = IconTheme.get_default ().load_icon ("beifahrer_non_smoker", 32, IconLookupFlags.NO_SVG);
197                         image_smoke.pixbuf = pixbuf;
198                 } catch (Error e) {
199                 }
200                 if (LiftFlags.ADAC_MEMBER in lift.flags)
201                         lift_text += _("ADAC member ");
202                 if (LiftFlags.WOMEN_ONLY in lift.flags)
203                         lift_text += _("only women ");
204                 lift_text += "\n\n" + lift.description;
205
206                 label.set_text (lift_text);
207
208                 if (lift.modified != null)
209                         label_changed.set_text (_("\nLast changed: ") + lift.modified);
210
211                 // Reduce right margin for scrollbar, if update_lift_details is called after mapping the window
212                 if (vbox.allocation.height > pannable.allocation.height)
213                         alignment.right_padding = MARGIN_DEFAULT;
214         }
215
216         void on_button_calendar_clicked () {
217                 var ical = new StringBuilder ("BEGIN:VCALENDAR\nVERSION:2.0\nMETHOD:PUBLISH\nBEGIN:VEVENT\nUID:");
218                 ical.append ("beifahrer" + lift.href); // UID
219                 ical.append ("\nCLASS:PUBLIC\nSUMMARY:");
220                 ical.append_printf (_("From %s to %s with %s"), lift.city_from, lift.city_to, lift.name);
221                 ical.append ("\nDTSTART;TZID=Europe/Berlin:");
222                 ical.append_printf ("%04d%02d%02dT%02d%02d00", lift.time.year + 2000, lift.time.month, lift.time.day, lift.time.hour, lift.time.minute);
223                 ical.append ("\nDTEND;TZID=Europe/Berlin:");
224                 ical.append_printf ("%04d%02d%02dT%02d%02d00", lift.time.year + 2000, lift.time.month, lift.time.day, lift.time.hour, lift.time.minute);
225                 ical.append ("\nDESCRIPTION:");
226                 ical.append (lift.description.replace ("\n", "\\n"));
227                 ical.append ("\nEND:VEVENT\nEND:VCALENDAR\n");
228
229                 ics_filename = Path.build_path ("/", Environment.get_tmp_dir (),
230                                                 "beifahrer.ics");
231                 try {
232                         FileUtils.set_contents (ics_filename, ical.str);
233                 } catch (FileError e) {
234                         stderr.printf ("Failed to write %s: %s\n", ics_filename, e.message);
235                         return;
236                 }
237
238                 var error = DBus.RawError ();
239                 var bus = DBus.RawBus.get (DBus.BusType.SESSION, ref error);
240                 var res = HildonMime.open_file (bus, "file://" + ics_filename);
241         }
242
243         void on_destroy () {
244                 if (ics_filename != null)
245                         FileUtils.remove (ics_filename);
246         }
247
248         bool on_map_event (Gdk.Event event) {
249                 // Reduce right margin for scrollbar, for preloaded lift
250                 if (vbox.allocation.height > pannable.allocation.height)
251                         alignment.right_padding = MARGIN_DEFAULT;
252                 return true;
253         }
254
255         void on_button_phone_clicked () {
256                 var uri = "tel://";
257                 if (lift.cell != null)
258                         uri += E.normalize_phone_number (lift.cell);
259                 else if (lift.phone != null)
260                         uri += E.normalize_phone_number (lift.phone);
261                 try {
262                         var action = URIAction.get_default_action_by_uri (uri);
263                         if (action != null) {
264                                 action.open (uri);
265                         } else {
266                                 
267                                 Banner.show_information (this, null, _("Couldn't call %s URI").printf (uri));
268                         }
269                 } catch (Error e) {
270                         if (e is URIError) {
271                                 Banner.show_information (this, null, _("Error: %s").printf (e.message));
272                         }
273                 }
274         }
275
276         void on_button_sms_clicked () {
277                 var uri = "sms://";
278                 if (lift.cell != null)
279                         uri += E.normalize_phone_number (lift.cell);
280                 else if (lift.phone != null)
281                         uri += E.normalize_phone_number (lift.phone);
282                 try {
283                         var action = URIAction.get_default_action_by_uri (uri);
284                         if (action != null) {
285                                 action.open (uri);
286                         } else {
287                                 
288                                 Banner.show_information (this, null, _("Couldn't open %s URI").printf (uri));
289                         }
290                 } catch (Error e) {
291                         if (e is URIError) {
292                                 Banner.show_information (this, null, _("Error: %s").printf (e.message));
293                         }
294                 }
295         }
296
297         void on_add_contact_clicked () {
298                 var contact = new OssoABook.Contact ();
299                 var attr_fn = new E.VCardAttribute (null, E.EVC_FN);
300                 contact.add_attribute_with_value ((owned) attr_fn, lift.name);
301                 if (lift.cell != null) {
302                         var attr_cell = new E.VCardAttribute (null, E.EVC_TEL);
303                         var param = new E.VCardAttributeParam (E.EVC_TYPE);
304                         attr_cell.add_param_with_value ((owned) param, "CELL");
305                         contact.add_attribute_with_value ((owned) attr_cell, E.normalize_phone_number (lift.cell));
306                 }
307                 if (lift.phone != null) {
308                         var attr_phone = new E.VCardAttribute (null, E.EVC_TEL);
309                         var param = new E.VCardAttributeParam (E.EVC_TYPE);
310                         attr_phone.add_param_with_value ((owned) param, "VOICE");
311                         contact.add_attribute_with_value ((owned) attr_phone, E.normalize_phone_number (lift.phone));
312                 }
313                 var dialog = new OssoABook.TemporaryContactDialog.with_contact (this, contact);
314                 dialog.show ();
315         }
316
317         void on_goto_website_clicked () {
318                 var url = "http://mitfahrclub.adac.de" + lift.href;
319
320                 try {
321                         var conn = DBus.Bus.get (DBus.BusType.SESSION);
322
323                         dynamic DBus.Object browser = conn.get_object (BROWSER_SERVICE, BROWSER_PATH, BROWSER_IF);
324                         browser.open_new_window (url, false);
325                 } catch (Error e) {
326                         stderr.printf ("Error: %s\n", e.message);
327                         Banner.show_information (this, null, _("Failed to open browser."));
328                 }
329         }
330
331         bool on_image_eventbox_button_pressed (Gdk.EventButton event) {
332                 if (event.button != 1)
333                         return true;
334                 if (LiftFlags.SMOKER in lift.flags)
335                         Banner.show_information (this, null, _("Smoker"));
336                 if (LiftFlags.NON_SMOKER in lift.flags)
337                         Banner.show_information (this, null, _("Non-smoker"));
338                 return false;
339         }
340
341         void on_orientation_changed () {
342                 // Maybe this will be fixed directly in Maemo-GTK, see
343                 // http://lists.maemo.org/pipermail/maemo-developers/2010-May/026332.html
344                 vbox.queue_resize ();
345
346                 table.remove (button_calendar);
347                 table.remove (label_driver);
348                 table.remove (button_phone);
349                 table.remove (button_sms);
350                 if (BeifahrerProgram.orientation.portrait) {
351                         table.attach (button_calendar, 0, 2, 0, 1, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
352                         table.attach (label_driver, 0, 2, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, MARGIN_DEFAULT, 0);
353                         table.attach (button_phone, 0, 2, 2, 3, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
354                         table.attach (button_sms, 0, 2, 3, 4, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
355                 } else {
356                         table.attach (button_calendar, 1, 2, 0, 1, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
357                         table.attach (label_driver, 0, 1, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, MARGIN_DEFAULT, 0);
358                         table.attach (button_phone, 1, 2, 1, 2, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
359                         table.attach (button_sms, 1, 2, 2, 3, AttachOptions.FILL | AttachOptions.EXPAND, AttachOptions.FILL, 0, 0);
360                 }
361         }
362 }