Initial commit
[fillmore] / src / marina / DialogUtils.vala
1 /* Copyright 2009-2010 Yorba Foundation
2  *
3  * This software is licensed under the GNU Lesser General Public License
4  * (version 2.1 or later).  See the COPYING file in this distribution. 
5  */
6
7 namespace DialogUtils {
8     public struct filter_description_struct {
9         public string name;
10         public string extension;
11     }
12
13     Gtk.FileFilter add_filter(Gtk.FileChooserDialog d, string name, string? extension) {
14         Gtk.FileFilter filter = new Gtk.FileFilter();
15         filter.set_name(name);
16         if (extension != null) {
17             filter.add_pattern("*." + extension);
18         } else {
19             filter.add_pattern("*");
20         }
21         d.add_filter(filter);
22         return filter;
23     }
24
25     void add_filters(filter_description_struct[] filter_descriptions, Gtk.FileChooserDialog d,
26         Gee.ArrayList<Gtk.FileFilter> filters, bool allow_all) {
27
28         int length = filter_descriptions.length;
29         for (int i=0;i<length;++i) {
30             Gtk.FileFilter filter = add_filter(d, filter_descriptions[i].name,
31                                                 filter_descriptions[i].extension);
32             filters.add(filter);
33         }
34
35         if (allow_all) {
36             add_filter(d, "All files", null);
37             //don't add to filters.  filters should have same number of items as filter_descriptions
38         }
39
40         assert(filter_descriptions.length == filters.size);
41
42         d.set_filter(filters[0]);
43     }
44
45     public bool open(Gtk.Window parent, filter_description_struct[] filter_descriptions,
46         bool allow_multiple, bool allow_all, out GLib.SList<string> filenames) {
47         bool return_value = false;
48         Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(parent, Gtk.FileChooserAction.OPEN);
49         file_chooser.set_title("Open Files");
50         //file_chooser.set_parent(parent);
51         //file_chooser.set_current_folder(GLib.Environment.get_home_dir());
52         //file_chooser.set_safe_folder(GLib.Environment.get_home_dir());
53         file_chooser.set_show_upnp(true);
54         Gee.ArrayList<Gtk.FileFilter> filters = new Gee.ArrayList<Gtk.FileFilter>();    
55         add_filters(filter_descriptions, (Gtk.FileChooserDialog)file_chooser, filters, allow_all);
56         file_chooser.set_select_multiple(allow_multiple);
57         if (file_chooser.run() == Gtk.ResponseType.OK) {
58             return_value = true;
59             filenames = file_chooser.get_filenames();
60         }
61         file_chooser.destroy();
62         return return_value;
63     }
64
65     public bool save(Gtk.Window parent, string title, bool create_directory,
66             filter_description_struct[] filter_descriptions, ref string filename) {
67         bool return_value = false;
68         
69         
70         Hildon.FileChooserDialog file_chooser = new Hildon.FileChooserDialog(parent, Gtk.FileChooserAction.SAVE);
71         file_chooser.set_title(title);
72         file_chooser.set_show_upnp(false);
73         if (filename != null) {
74             file_chooser.set_current_folder(Path.get_dirname(filename));
75         } else {
76             //file_chooser.set_current_folder(GLib.Environment.get_home_dir());
77         }
78         int length = filter_descriptions.length;
79         Gee.ArrayList<Gtk.FileFilter> filters = new Gee.ArrayList<Gtk.FileFilter>();    
80
81        // add_filters(filter_descriptions, (Gtk.FileChooserDialog)file_chooser, filters, false);
82         
83         //all this extra code is because the dialog doesn't append the extension for us
84         //in the filename, so we can't use d.set_do_overwrite_confirmation
85         
86         while (file_chooser.run() == Gtk.ResponseType.OK) {
87             string local_filename = file_chooser.get_filename();
88             if (create_directory) {
89                 if (GLib.DirUtils.create(local_filename, 0777) != 0) {
90                     error("Could not create directory", GLib.strerror(GLib.errno));;
91                     file_chooser.present();
92                     continue;
93                 }
94                 local_filename = Path.build_filename(local_filename,
95                     Path.get_basename(local_filename));
96             }
97
98             unowned Gtk.FileFilter selected_filter = file_chooser.get_filter();
99
100             int i = 0;
101             foreach (Gtk.FileFilter file_filter in filters) {
102                 if (file_filter == selected_filter) {
103                     break;
104                 }
105                 ++i;
106             }
107
108             assert(i < length);
109
110             local_filename = append_extension(local_filename, filter_descriptions[i].extension);
111             if (!FileUtils.test(local_filename, FileTest.EXISTS) || 
112                 confirm_replace(parent, local_filename)) {
113                 return_value = true;
114                 filename = local_filename;
115                 break;
116             }
117             else {
118                 file_chooser.present();
119             }
120         }
121         file_chooser.destroy();
122         return return_value;
123     }
124
125     string bold_message(string message) {
126         return message; //"<span weight=\"bold\" size=\"larger\">" + message + "</span>";
127     }
128
129     public void warning(string major_message, string? minor_message) {
130         string message = bold_message(major_message);
131         if (minor_message != null) {
132             message = message + "\n\n" + minor_message;
133         }
134       Gtk.Widget banner = Hildon.Banner.show_information_with_markup(null, null, message);
135       ((Hildon.Banner)banner).set_timeout(1000 * 2);
136     }
137
138     public void error(string major_message, string? minor_message) {
139         string message = bold_message(major_message);
140         if (minor_message != null) {
141             message = message + "\n\n" + minor_message;
142         }
143       Gtk.Widget banner = Hildon.Banner.show_information_with_markup(null, null, message);
144       ((Hildon.Banner)banner).set_timeout(1000 * 2);
145     }
146
147     Gtk.ResponseType run_dialog(Gtk.Window? parent, Gtk.MessageType type, 
148         string? title, string message, ButtonStruct[] buttons) {
149         //string the_message = bold_message(message);
150         if (null == title) title = "Message";
151         Hildon.Dialog dialog = new Hildon.Dialog();
152         dialog.set_parent(parent);
153         dialog.set_title(title);
154         Gtk.VBox content = (Gtk.VBox)dialog.get_content_area();
155         Gtk.Label label = new Gtk.Label(message);
156         //label.use_markup = true;
157         label.single_line_mode = false;
158         label.set_line_wrap(true);
159         
160         content.add(label);
161
162         int length = buttons.length;
163         for (int i = 0; i < length; ++i) {
164             dialog.add_button(buttons[i].title, buttons[i].type);
165         }
166
167         dialog.show_all();
168         Gtk.ResponseType response = (Gtk.ResponseType) dialog.run();
169         dialog.destroy();
170
171         return response;
172     }
173
174     Gtk.ResponseType two_button_dialog(string message, string first_button, string second_button) {
175         Hildon.Dialog d = new Hildon.Dialog();
176         
177         Gtk.VBox contents = (Gtk.VBox)d.get_content_area();
178         Gtk.VBox vbox = new Gtk.VBox(true, 4);
179         Gtk.Label label = new Gtk.Label(message);
180         label.use_markup = true;
181         label.single_line_mode = false;  
182         label.set_line_wrap(true);
183         vbox.pack_start(label);
184         contents.add(vbox);
185         d.add_buttons(first_button, Gtk.ResponseType.NO,
186                       second_button, Gtk.ResponseType.YES);
187
188         d.show_all();
189         Gtk.ResponseType r = (Gtk.ResponseType) d.run();
190         d.destroy();
191
192         return r;
193     }
194
195     public Gtk.ResponseType delete_keep(string message) {
196         return two_button_dialog(message, "Keep", Gtk.STOCK_DELETE);
197     }
198
199     public Gtk.ResponseType add_cancel(string message) {
200         return two_button_dialog(message, Gtk.STOCK_CANCEL, Gtk.STOCK_ADD);
201     }
202
203     public Gtk.ResponseType delete_cancel(string message) {
204         return two_button_dialog(message, Gtk.STOCK_CANCEL, Gtk.STOCK_DELETE);
205     }
206
207     public bool confirm_replace(Gtk.Window? parent, string filename) {
208         return Gtk.ResponseType.YES == two_button_dialog("<big><b>A file named \"%s\" already exists.  Do you want to replace it?</b></big>", "Cancel", "Replace");
209     }
210     
211     struct ButtonStruct {
212         public ButtonStruct(string title, Gtk.ResponseType type) {
213             this.title = title;
214             this.type = type;
215         }
216
217         public string title;
218         public Gtk.ResponseType type;
219     }
220
221     public Gtk.ResponseType save_close_cancel(Gtk.Window? parent, string? title, string message) {
222         ButtonStruct[] buttons = {
223             ButtonStruct(Gtk.STOCK_CLOSE, Gtk.ResponseType.NO),
224             ButtonStruct(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL),
225             ButtonStruct(Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT)
226         };
227
228         return run_dialog(parent, Gtk.MessageType.WARNING, title, message, buttons);
229     }
230
231     Gtk.Alignment get_aligned_label(float x, float y, float exp_x, float exp_y, string text, 
232             bool selectable) {
233         Gtk.Label l = new Gtk.Label(text);
234         l.set_line_wrap(true);
235         l.use_markup = true;
236         l.selectable = selectable;
237         
238         Gtk.Alignment a = new Gtk.Alignment(x, y, exp_x, exp_y);
239         a.add(l);
240         
241         return a;
242     }
243
244     void add_label_to_table(Gtk.Table t, string str, int x, int y, int xpad, int ypad, 
245             bool selectable) {
246         Gtk.Alignment a = get_aligned_label(0.0f, 0.0f, 0.0f, 0.0f, str, selectable);
247         t.attach(a, x, x + 1, y, y + 1, Gtk.AttachOptions.FILL, Gtk.AttachOptions.FILL, xpad, ypad);
248     }
249
250     public void show_clip_properties(Gtk.Window parent, ClipView? selected_clip, 
251             Model.ClipFile ? clip_file, Fraction? frames_per_second) {
252         Hildon.Dialog d = new Hildon.Dialog.with_buttons("Clip Properties", parent, 
253                                     Gtk.DialogFlags.MODAL, Gtk.STOCK_OK, Gtk.ResponseType.ACCEPT);
254         if (selected_clip != null) {
255             clip_file = selected_clip.clip.clipfile;
256         }
257
258         d.set("has-separator", false);
259
260         Gtk.Table t = new Gtk.Table(10, 2, false);
261         int row = 0;
262         int tab_padding = 25;
263
264         for (int i = 0; i < 10; i++)
265             t.set_row_spacing(i, 10);
266
267         row = 1;
268         add_label_to_table(t, "<b>Clip</b>", 0, row++, 5, 0, false);
269
270         if (selected_clip != null) {
271             add_label_to_table(t, "<i>Name:</i>", 0, row, tab_padding, 0, false);
272             add_label_to_table(t, "%s".printf(selected_clip.clip.name), 1, row++, 5, 0, true);
273         }
274
275         add_label_to_table(t, "<i>Location:</i>", 0, row, tab_padding, 0, false);
276         add_label_to_table(t, "%s".printf(clip_file.filename), 1, row++, 5, 0, true); 
277
278         if (selected_clip != null) {
279             add_label_to_table(t, "<i>Timeline length:</i>", 0, row, tab_padding, 0, false);
280
281             string length_string = "";
282             string actual_length = "";
283
284             if (frames_per_second != null && frames_per_second.numerator > 0) {
285                 TimeCode time = frame_to_time (time_to_frame_with_rate(selected_clip.clip.duration, 
286                     frames_per_second), frames_per_second);
287                 length_string = time.to_string();
288                 time = frame_to_time(time_to_frame_with_rate(
289                     selected_clip.clip.clipfile.length, frames_per_second), frames_per_second);
290                 actual_length = time.to_string();
291             } else {
292                 length_string = time_to_string(selected_clip.clip.duration);
293                 actual_length = time_to_string(selected_clip.clip.clipfile.length);
294             }
295
296             add_label_to_table(t, "%s".printf(length_string), 1, row++, 5, 0, true);
297
298             if (selected_clip.clip.is_trimmed()) {
299                 add_label_to_table(t, "<i>Actual length:</i>", 0, row, tab_padding, 0, false);
300                 add_label_to_table(t, "%s".printf(actual_length), 1, row++, 5, 0, true);
301             }
302         }
303
304         if (clip_file.has_caps_structure(Model.MediaType.VIDEO)) {
305             add_label_to_table(t, "<b>Video</b>", 0, row++, 5, 0, false);
306
307             int w, h;
308             if (clip_file.get_dimensions(out w, out h)) {
309                 add_label_to_table(t, "<i>Dimensions:</i>", 0, row, tab_padding, 0, false);
310                 add_label_to_table(t, "%d x %d".printf(w, h), 1, row++, 5, 0, true);
311             }
312
313             Fraction r;
314             if (clip_file.get_frame_rate(out r)) {
315                 add_label_to_table(t, "<i>Frame rate:</i>", 0, row, tab_padding, 0, false);
316
317                 if (r.numerator % r.denominator != 0)
318                     add_label_to_table(t, 
319                                "%.2f frames per second".printf(r.numerator / (float)r.denominator), 
320                                1, row++, 5, 0, true);
321                 else
322                     add_label_to_table(t, 
323                                 "%d frames per second".printf(r.numerator / r.denominator), 
324                                 1, row++, 5, 0, true);
325             }
326         }
327
328         if (clip_file.has_caps_structure(Model.MediaType.AUDIO)) {
329             add_label_to_table(t, "<b>Audio</b>", 0, row++, 5, 0, false);
330
331             int rate;
332             if (clip_file.get_sample_rate(out rate)) {
333                 add_label_to_table(t, "<i>Sample rate:</i>", 0, row, tab_padding, 0, false);
334                 add_label_to_table(t, "%d Hz".printf(rate), 1, row++, 5, 0, true);
335             }
336
337             string s;
338             if (clip_file.get_num_channels_string(out s)) {
339                 add_label_to_table(t, "<i>Number of channels:</i>", 0, row, tab_padding, 0, false);
340                 add_label_to_table(t, "%s".printf(s), 1, row++, 5, 0, true);
341             }
342         }
343
344         d.vbox.pack_start(t, false, false, 0);
345
346         d.show_all();
347         d.run();
348         d.destroy();
349     }
350 }