Desensitize inappropriate menu entries.
[dcpomatic.git] / src / tools / dvdomatic.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <iostream>
21 #include <boost/filesystem.hpp>
22 #include "gtk/film_viewer.h"
23 #include "gtk/film_editor.h"
24 #include "gtk/film_player.h"
25 #include "gtk/job_manager_view.h"
26 #include "gtk/config_dialog.h"
27 #include "gtk/gpl.h"
28 #include "gtk/job_wrapper.h"
29 #include "gtk/dvd_title_dialog.h"
30 #include "gtk/gtk_util.h"
31 #include "lib/film.h"
32 #include "lib/format.h"
33 #include "lib/config.h"
34 #include "lib/filter.h"
35 #include "lib/util.h"
36 #include "lib/scaler.h"
37 #include "lib/exceptions.h"
38
39 using namespace std;
40 using namespace boost;
41
42 static Gtk::Window* window = 0;
43 static FilmViewer* film_viewer = 0;
44 static FilmEditor* film_editor = 0;
45 static FilmPlayer* film_player = 0;
46 static Film* film = 0;
47
48 static void set_menu_sensitivity ();
49
50 class FilmChangedDialog : public Gtk::MessageDialog
51 {
52 public:
53         FilmChangedDialog ()
54                 : Gtk::MessageDialog ("", false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE)
55         {
56                 stringstream s;
57                 s << "Save changes to film \"" << film->name() << "\" before closing?";
58                 set_message (s.str ());
59                 add_button ("Close _without saving", Gtk::RESPONSE_NO);
60                 add_button ("_Cancel", Gtk::RESPONSE_CANCEL);
61                 add_button ("_Save", Gtk::RESPONSE_YES);
62         }
63 };
64
65 bool
66 maybe_save_then_delete_film ()
67 {
68         if (!film) {
69                 return false;
70         }
71                         
72         if (film->dirty ()) {
73                 FilmChangedDialog d;
74                 switch (d.run ()) {
75                 case Gtk::RESPONSE_CANCEL:
76                         return true;
77                 case Gtk::RESPONSE_YES:
78                         film->write_metadata ();
79                         break;
80                 case Gtk::RESPONSE_NO:
81                         return false;
82                 }
83         }
84         
85         delete film;
86         film = 0;
87         return false;
88 }
89
90 void
91 file_new ()
92 {
93         Gtk::FileChooserDialog c (*window, "New Film", Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER);
94         c.add_button ("_Cancel", Gtk::RESPONSE_CANCEL);
95         c.add_button ("C_reate", Gtk::RESPONSE_ACCEPT);
96
97         int const r = c.run ();
98         if (r == Gtk::RESPONSE_ACCEPT) {
99                 if (maybe_save_then_delete_film ()) {
100                         return;
101                 }
102                 film = new Film (c.get_filename ());
103 #if BOOST_FILESYSTEM_VERSION == 3               
104                 film->set_name (filesystem::path (c.get_filename().c_str()).filename().generic_string());
105 #else           
106                 film->set_name (filesystem::path (c.get_filename().c_str()).filename());
107 #endif          
108                 film_viewer->set_film (film);
109                 film_editor->set_film (film);
110                 set_menu_sensitivity ();
111         }
112 }
113
114 void
115 file_open ()
116 {
117         Gtk::FileChooserDialog c (*window, "Open Film", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
118         c.add_button ("_Cancel", Gtk::RESPONSE_CANCEL);
119         c.add_button ("_Open", Gtk::RESPONSE_ACCEPT);
120
121         int const r = c.run ();
122         if (r == Gtk::RESPONSE_ACCEPT) {
123                 if (maybe_save_then_delete_film ()) {
124                         return;
125                 }
126                 film = new Film (c.get_filename ());
127                 film_viewer->set_film (film);
128                 film_editor->set_film (film);
129                 set_menu_sensitivity ();
130         }
131 }
132
133 void
134 file_save ()
135 {
136         film->write_metadata ();
137 }
138
139 void
140 file_quit ()
141 {
142         if (maybe_save_then_delete_film ()) {
143                 return;
144         }
145         
146         Gtk::Main::quit ();
147 }
148
149 void
150 edit_preferences ()
151 {
152         ConfigDialog d;
153         d.run ();
154         Config::instance()->write ();
155 }
156
157 void
158 jobs_make_dcp ()
159 {
160         JobWrapper::make_dcp (film, true);
161 }
162
163 void
164 jobs_make_dcp_from_existing_transcode ()
165 {
166         JobWrapper::make_dcp (film, false);
167 }
168
169 void
170 jobs_copy_from_dvd ()
171 {
172         try {
173                 DVDTitleDialog d;
174                 if (d.run () != Gtk::RESPONSE_OK) {
175                         return;
176                 }
177                 film->copy_from_dvd ();
178         } catch (DVDError& e) {
179                 error_dialog (e.what ());
180         }
181 }
182
183 void
184 jobs_send_dcp_to_tms ()
185 {
186         film->send_dcp_to_tms ();
187 }
188
189 void
190 jobs_examine_content ()
191 {
192         film->examine_content ();
193 }
194
195 void
196 help_about ()
197 {
198         Gtk::AboutDialog d;
199         d.set_name ("DVD-o-matic");
200         d.set_version (DVDOMATIC_VERSION);
201
202         stringstream s;
203         s << "DCP generation from arbitrary formats\n\n"
204           << "Using " << dependency_version_summary() << "\n";
205         d.set_comments (s.str ());
206
207         vector<string> authors;
208         authors.push_back ("Carl Hetherington");
209         authors.push_back ("Terrence Meiczinger");
210         authors.push_back ("Paul Davis");
211         d.set_authors (authors);
212
213         d.set_website ("http://carlh.net/software/dvdomatic");
214         d.set_license (gpl);
215         
216         d.run ();
217 }
218
219 enum Sensitivity {
220         ALWAYS,
221         NEEDS_FILM
222 };
223
224 map<Gtk::MenuItem *, Sensitivity> menu_items;
225         
226 void
227 add_item (Gtk::Menu_Helpers::MenuList& items, string text, sigc::slot0<void> handler, Sensitivity sens)
228 {
229         items.push_back (Gtk::Menu_Helpers::MenuElem (text, handler));
230         menu_items.insert (make_pair (&items.back(), sens));
231 }
232
233 void
234 set_menu_sensitivity ()
235 {
236         for (map<Gtk::MenuItem *, Sensitivity>::iterator i = menu_items.begin(); i != menu_items.end(); ++i) {
237                 if (i->second == NEEDS_FILM) {
238                         i->first->set_sensitive (film != 0);
239                 } else {
240                         i->first->set_sensitive (true);
241                 }
242         }
243 }
244
245 void
246 setup_menu (Gtk::MenuBar& m)
247 {
248         using namespace Gtk::Menu_Helpers;
249
250         Gtk::Menu* file = manage (new Gtk::Menu);
251         MenuList& file_items (file->items ());
252         add_item (file_items, "New...", sigc::ptr_fun (file_new), ALWAYS);
253         add_item (file_items, "_Open...", sigc::ptr_fun (file_open), ALWAYS);
254         file_items.push_back (SeparatorElem ());
255         add_item (file_items, "_Save", sigc::ptr_fun (file_save), NEEDS_FILM);
256         file_items.push_back (SeparatorElem ());
257         add_item (file_items, "_Quit", sigc::ptr_fun (file_quit), NEEDS_FILM);
258
259         Gtk::Menu* edit = manage (new Gtk::Menu);
260         MenuList& edit_items (edit->items ());
261         add_item (edit_items, "_Preferences...", sigc::ptr_fun (edit_preferences), ALWAYS);
262
263         Gtk::Menu* jobs = manage (new Gtk::Menu);
264         MenuList& jobs_items (jobs->items ());
265         add_item (jobs_items, "_Make DCP", sigc::ptr_fun (jobs_make_dcp), NEEDS_FILM);
266         add_item (jobs_items, "_Send DCP to TMS", sigc::ptr_fun (jobs_send_dcp_to_tms), NEEDS_FILM);
267         add_item (jobs_items, "Copy from _DVD...", sigc::ptr_fun (jobs_copy_from_dvd), NEEDS_FILM);
268         add_item (jobs_items, "_Examine content", sigc::ptr_fun (jobs_examine_content), NEEDS_FILM);
269         add_item (jobs_items, "Make DCP from _existing transcode", sigc::ptr_fun (jobs_make_dcp_from_existing_transcode), NEEDS_FILM);
270
271         Gtk::Menu* help = manage (new Gtk::Menu);
272         MenuList& help_items (help->items ());
273         add_item (help_items, "About", sigc::ptr_fun (help_about), ALWAYS);
274         
275         MenuList& items (m.items ());
276         items.push_back (MenuElem ("_File", *file));
277         items.push_back (MenuElem ("_Edit", *edit));
278         items.push_back (MenuElem ("_Jobs", *jobs));
279         items.push_back (MenuElem ("_Help", *help));
280 }
281
282 bool
283 window_closed (GdkEventAny *)
284 {
285         if (maybe_save_then_delete_film ()) {
286                 return true;
287         }
288
289         return false;
290 }
291
292 void
293 file_changed (string f)
294 {
295         stringstream s;
296         s << "DVD-o-matic";
297         if (!f.empty ()) {
298                 s << " — " << f;
299         }
300         
301         window->set_title (s.str ());
302 }
303
304 int
305 main (int argc, char* argv[])
306 {
307         dvdomatic_setup ();
308         
309         Gtk::Main kit (argc, argv);
310
311         if (argc == 2 && boost::filesystem::is_directory (argv[1])) {
312                 film = new Film (argv[1]);
313         }
314
315         window = new Gtk::Window ();
316         window->signal_delete_event().connect (sigc::ptr_fun (window_closed));
317         
318         film_viewer = new FilmViewer (film);
319         film_editor = new FilmEditor (film);
320         film_player = new FilmPlayer (film);
321         JobManagerView jobs_view;
322
323         window->set_title ("DVD-o-matic");
324
325         Gtk::VBox vbox;
326
327         Gtk::MenuBar menu_bar;
328         vbox.pack_start (menu_bar, false, false);
329         setup_menu (menu_bar);
330         set_menu_sensitivity ();
331
332         Gtk::HBox hbox;
333         hbox.set_spacing (12);
334
335         Gtk::VBox left_vbox;
336         left_vbox.set_spacing (12);
337         left_vbox.pack_start (film_editor->widget (), false, false);
338 //      left_vbox.pack_start (film_player->widget (), false, false);
339         hbox.pack_start (left_vbox, false, false);
340
341         Gtk::VBox right_vbox;
342         right_vbox.pack_start (film_viewer->widget (), true, true);
343         right_vbox.pack_start (jobs_view.widget(), false, false);
344         hbox.pack_start (right_vbox, true, true);
345
346         vbox.pack_start (hbox, true, true);
347
348         window->add (vbox);
349         window->show_all ();
350
351         /* XXX: calling these here is a bit of a hack */
352         film_editor->setup_visibility ();
353         film_player->setup_visibility ();
354         film_viewer->setup_visibility ();
355
356         film_editor->FileChanged.connect (ptr_fun (file_changed));
357         if (film) {
358                 file_changed (film->directory ());
359         } else {
360                 file_changed ("");
361         }
362
363         /* XXX this should be in JobManagerView, shouldn't it? */
364         Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (jobs_view, &JobManagerView::update), true), 1000);
365
366         window->maximize ();
367         Gtk::Main::run (*window);
368
369         return 0;
370 }