Debian packaging: 0.0.4-1
[beifahrer] / src / lift-list-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 LiftListWindow : StackableWindow {
23         public const string GCONF_KEY_PRELOAD = "/apps/beifahrer/preload";
24
25         enum Columns {
26                 ROUTE,
27                 SECONDARY,
28                 DATETIME,
29                 PLACES,
30                 PRICE,
31                 SMOKER,
32                 LIFT
33         }
34
35         AdacMitfahrclub adac;
36         ListStore store;
37         TreeSortable sortable;
38         Alignment alignment;
39         TreeView tree;
40         TreeViewColumn route_column;
41         Label no_lifts;
42         GConf.Client gconf;
43         bool preload = false;
44         string url;
45
46         Gtk.Button goto_website;
47
48         public LiftListWindow (AdacMitfahrclub _adac) {
49                 adac = _adac;
50         }
51
52         construct {
53                 set_title ("Beifahrer");
54
55                 var menu = new AppMenu ();
56                 var sort_by_time = new RadioButton.with_label (null, _("Time"));
57                 sort_by_time.set_mode (false);
58                 sort_by_time.show ();
59                 var sort_by_price = new RadioButton.with_label_from_widget (sort_by_time, _("Price"));
60                 sort_by_price.set_mode (false);
61                 sort_by_price.show ();
62                 menu.add_filter (sort_by_time);
63                 menu.add_filter (sort_by_price);
64                 goto_website = new Gtk.Button.with_label (_("Show website"));
65                 menu.append (goto_website);
66                 set_main_menu (menu);
67
68                 gconf = GConf.Client.get_default ();
69                 store = new ListStore (7, typeof (string), typeof (string), typeof (string), typeof (string), typeof (string), typeof (string), typeof (Lift));
70                 sortable = new TreeModelSort.with_model (store);
71                 sortable.set_sort_func (Columns.DATETIME, datetime_sort_func);
72                 sortable.set_sort_func (Columns.PRICE, price_sort_func);
73
74                 alignment = new Alignment (0.0f, 0.0f, 1.0f, 1.0f);
75                 alignment.top_padding = MARGIN_HALF;
76                 alignment.left_padding = MARGIN_DOUBLE;
77                 alignment.right_padding = MARGIN_DOUBLE;
78
79                 tree = (TreeView) Hildon.gtk_tree_view_new_with_model (UIMode.NORMAL, sortable);
80                 Hildon.gtk_widget_set_theme_size (tree, SizeType.FINGER_HEIGHT);
81
82                 tree.set_headers_visible (false);
83                 tree.set_rules_hint (true);
84
85                 // Tree selection object
86                 var selection = tree.get_selection ();
87                 selection.set_mode (SelectionMode.SINGLE);
88
89                 // Source - destination / driver column
90                 var route_column = new TreeViewColumn ();
91                 route_column.set_title (_("Route"));
92                 route_column.set_reorderable (false);
93                 route_column.set_sizing (TreeViewColumnSizing.AUTOSIZE);
94                 route_column.set_expand (true);
95                 tree.append_column (route_column);
96
97                 var vbox_renderer = new CellRendererVBox ();
98
99                 var renderer = new CellRendererText ();
100                 renderer.yalign = 1.0f;
101                 renderer.set_fixed_size (-1, 32);
102                 vbox_renderer.append (renderer, true);
103                 vbox_renderer.set_data ("route-renderer", renderer);
104
105                 // Add secondary text to column (driver name, route details)
106                 renderer = new CellRendererText ();
107                 renderer.yalign = 0;
108                 renderer.set_fixed_size (-1, 32);
109                 renderer.ellipsize = Pango.EllipsizeMode.END;
110                 Pango.AttrList attr_list = new Pango.AttrList ();
111                 var style = Gtk.rc_get_style_by_paths (Gtk.Settings.get_default (), "SmallSystemFont", null, typeof (void));
112                 if (style != null) {
113                         var attr_font_desc = new Pango.AttrFontDesc (style.font_desc.copy ());
114                         attr_list.insert ((owned) attr_font_desc);
115                 }
116                 Gdk.Color color;
117                 this.ensure_style ();
118                 if (this.style.lookup_color ("SecondaryTextColor", out color)) {
119                         Pango.Attribute attr_color = Pango.attr_foreground_new (color.red, color.green, color.blue);
120                         attr_list.insert ((owned) attr_color);
121                 }
122                 renderer.attributes = attr_list;
123                 vbox_renderer.append (renderer, true);
124                 vbox_renderer.set_data ("secondary-renderer", renderer);
125
126                 route_column.pack_start (vbox_renderer, true);
127                 route_column.set_cell_data_func (vbox_renderer, vbox_data_func);
128
129                 // Date and time column
130                 var datetime_column = new TreeViewColumn.with_attributes ("DateTime", new CellRendererText (), "text", Columns.DATETIME);
131                 datetime_column.set_reorderable (false);
132                 datetime_column.set_sizing (TreeViewColumnSizing.AUTOSIZE);
133                 datetime_column.set_expand (true);
134                 tree.append_column (datetime_column);
135
136                 // Free places column
137                 var places_column = new TreeViewColumn.with_attributes ("Places", new CellRendererText (), "text", Columns.PLACES);
138                 places_column.set_reorderable (false);
139                 places_column.set_sizing (TreeViewColumnSizing.AUTOSIZE);
140                 places_column.set_expand (true);
141                 tree.append_column (places_column);
142
143                 // Price column
144                 var price_column = new TreeViewColumn.with_attributes ("Price", new CellRendererText (), "text", Columns.PRICE);
145                 price_column.set_reorderable (false);
146                 price_column.set_sizing (TreeViewColumnSizing.AUTOSIZE);
147                 price_column.set_expand (true);
148                 tree.append_column (price_column);
149
150                 // Smoker/Non-smoker column
151                 var smoke_column = new TreeViewColumn.with_attributes ("Smoker", new CellRendererPixbuf (), "icon-name", Columns.SMOKER);
152                 smoke_column.set_reorderable (false);
153                 smoke_column.set_sizing (TreeViewColumnSizing.AUTOSIZE);
154                 smoke_column.set_expand (false);
155                 tree.append_column (smoke_column);
156
157                 var pannable = new PannableArea ();
158                 pannable.add (tree);
159
160                 no_lifts = new Label (_("No lifts"));
161                 Hildon.helper_set_logical_font (no_lifts, "LargeSystemFont");
162                 Hildon.helper_set_logical_color (no_lifts, RcFlags.FG, StateType.NORMAL, "SecondaryTextColor");
163                 no_lifts.set_size_request (-1, 6 * 70);
164                 no_lifts.set_alignment ((float) 0.5, (float) 0.42);
165
166                 var vbox = new VBox (false, 0);
167                 vbox.pack_start (pannable, true, true, 0);
168                 vbox.pack_start (no_lifts, false, false, 0);
169
170                 alignment.add (vbox);
171                 add (alignment);
172
173                 alignment.show_all ();
174                 no_lifts.hide ();
175                 route_column.set_visible (!BeifahrerProgram.orientation.portrait);
176
177                 tree.row_activated.connect (this.on_row_activated);
178                 Gdk.Screen.get_default ().size_changed.connect (this.on_orientation_changed);
179                 goto_website.clicked.connect (on_goto_website_clicked);
180                 sort_by_time.toggled.connect (button => {
181                         if (button.get_active ())
182                                 sortable.set_sort_column_id (Columns.DATETIME, Gtk.SortType.ASCENDING);
183                 });
184                 sort_by_price.toggled.connect (button => {
185                         if (button.get_active ())
186                                 sortable.set_sort_column_id (Columns.PRICE, Gtk.SortType.ASCENDING);
187                 });
188         }
189
190         private int datetime_sort_func (TreeModel model, TreeIter a, TreeIter b) {
191                 Lift la;
192                 Lift lb;
193                 model.get (a, Columns.LIFT, out la);
194                 model.get (b, Columns.LIFT, out lb);
195                 int year = la.time.year - lb.time.year;
196                 if (year != 0)
197                         return year;
198                 int month = la.time.month - lb.time.month;
199                 if (month != 0)
200                         return month;
201                 int day = la.time.day - lb.time.day;
202                 if (day != 0)
203                         return day;
204                 int hour = la.time.hour - lb.time.hour;
205                 // Negative hour means no exact time available
206                 // - sort those entries last
207                 if (la.time.hour < 0) {
208                         hour += 24;
209                         return hour;
210                 }
211                 if (lb.time.hour < 0) {
212                         hour -= 24;
213                         return hour;
214                 }
215                 if (hour != 0)
216                         return hour;
217                 int minute = la.time.minute - lb.time.minute;
218                 return minute;
219         }
220
221         private int price_sort_func (TreeModel model, TreeIter a, TreeIter b) {
222                 string pa;
223                 string pb;
224                 model.get (a, Columns.PRICE, out pa);
225                 model.get (b, Columns.PRICE, out pb);
226                 if (pa == null && pb != null)
227                         return 1;
228                 if (pa != null && pb == null)
229                         return -1;
230                 return strcmp (pa, pb);
231         }
232
233         private void vbox_data_func (CellLayout cell_layout, CellRenderer cell, TreeModel model, TreeIter iter) {
234                 unowned string text;
235                 CellRendererText renderer;
236
237                 model.get (iter, Columns.ROUTE, out text);
238                 renderer = cell.get_data ("route-renderer");
239                 renderer.text = text;
240
241                 model.get (iter, Columns.SECONDARY, out text);
242                 renderer = cell.get_data ("secondary-renderer");
243                 renderer.text = text;
244         }
245
246         public async void find_lifts (string city_from, int radius_from, string city_to, int radius_to, Date date, int tolerance = 0) {
247                 TreeIter iter;
248                 set_title ("%s - %s".printf (city_from, city_to));
249                 Hildon.gtk_window_set_progress_indicator (this, 1);
250
251                 url = adac.get_lift_list_url (city_from, radius_from, city_to, radius_to, date, tolerance);
252                 if (url == null)
253                         return;
254                 goto_website.show ();
255                 var lift_list = yield adac.get_lift_list (city_from, radius_from, city_to, radius_to, date, tolerance);
256                 foreach (Lift lift in lift_list) {
257                         string icon_name = null;
258                         if (LiftFlags.SMOKER in lift.flags)
259                                 icon_name = "beifahrer_smoker";
260                         else if (LiftFlags.NON_SMOKER in lift.flags)
261                                 icon_name = "beifahrer_non_smoker";
262                         string datetime = "%02d.%02d.%04d".printf (lift.time.day, lift.time.month, lift.time.year);
263                         if (lift.time.hour >= 0)
264                                 datetime += ", %d:%02d".printf (lift.time.hour, lift.time.minute);
265                         store.insert_with_values (out iter, -1, Columns.ROUTE, lift.city_from + " - " + lift.city_to,
266                                                                 Columns.SECONDARY, "",
267                                                                 Columns.DATETIME, datetime,
268                                                                 Columns.PLACES, _("%d pl.").printf (lift.places),
269                                                                 Columns.PRICE, lift.price,
270                                                                 Columns.SMOKER, icon_name,
271                                                                 Columns.LIFT, lift);
272                         // (LiftFlags.WOMEN_ONLY,ADAC_MEMBER in lift.flags)
273                 }
274                 if (lift_list.length () > 6)
275                         alignment.right_padding = MARGIN_DEFAULT;
276                 if (lift_list.length () == 0)
277                         no_lifts.show ();
278
279                 bool preload = false;
280                 try {
281                         preload = gconf.get_bool (GCONF_KEY_PRELOAD);
282                 } catch (Error e) {
283                 }
284                 if (preload) {
285                         if (store.get_iter_first (out iter)) do {
286                                 Lift lift2;
287                                 store.@get (iter, Columns.LIFT, out lift2);
288                                 if (lift2 == null)
289                                         continue;
290                                 if (lift2.description == null) {
291                                         yield adac.update_lift_details (lift2);
292                                         store.@set (iter, Columns.SECONDARY, lift2.name);
293                                 }
294                         } while (store.iter_next (ref iter));
295                 }
296
297                 Hildon.gtk_window_set_progress_indicator (this, 0);
298         }
299
300         void on_goto_website_clicked () {
301                 BeifahrerProgram.open_browser (this, url);
302         }
303
304         private void on_row_activated (TreeView tree, TreePath /*_*/path, TreeViewColumn column) {
305                 TreeModel model = tree.model;
306                 TreeIter iter;
307                 Lift lift;
308
309                 if (model.get_iter (out iter, path)) {
310                         model.get (iter, Columns.LIFT, out lift);
311
312                         var window = new LiftDetailWindow (adac, lift);
313                         window.show ();
314                 }
315         }
316
317         private void on_orientation_changed (Gdk.Screen screen) {
318                 route_column.set_visible (!BeifahrerProgram.orientation.portrait);
319         }
320 }