diff options
| author | Carl Hetherington <cth@carlh.net> | 2012-07-15 00:14:28 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2012-07-15 00:14:28 +0100 |
| commit | bb767c7e338414beee132af3e96829c1448e214b (patch) | |
| tree | bec2858dcc7225a9bcc2acd8170c25508f6df6cb /src/gtk | |
| parent | 66c9be6bdb1361e5681e094a0c8170d268aa9518 (diff) | |
Move things round a bit.
Diffstat (limited to 'src/gtk')
| -rw-r--r-- | src/gtk/alignment.cc | 167 | ||||
| -rw-r--r-- | src/gtk/alignment.h | 35 | ||||
| -rw-r--r-- | src/gtk/config_dialog.cc | 369 | ||||
| -rw-r--r-- | src/gtk/config_dialog.h | 113 | ||||
| -rw-r--r-- | src/gtk/dcp_range_dialog.cc | 117 | ||||
| -rw-r--r-- | src/gtk/dcp_range_dialog.h | 46 | ||||
| -rw-r--r-- | src/gtk/film_editor.cc | 572 | ||||
| -rw-r--r-- | src/gtk/film_editor.h | 125 | ||||
| -rw-r--r-- | src/gtk/film_list.cc | 65 | ||||
| -rw-r--r-- | src/gtk/film_list.h | 41 | ||||
| -rw-r--r-- | src/gtk/film_player.cc | 310 | ||||
| -rw-r--r-- | src/gtk/film_player.h | 63 | ||||
| -rw-r--r-- | src/gtk/film_viewer.cc | 224 | ||||
| -rw-r--r-- | src/gtk/film_viewer.h | 63 | ||||
| -rw-r--r-- | src/gtk/filter_dialog.cc | 47 | ||||
| -rw-r--r-- | src/gtk/filter_dialog.h | 43 | ||||
| -rw-r--r-- | src/gtk/filter_view.cc | 72 | ||||
| -rw-r--r-- | src/gtk/filter_view.h | 47 | ||||
| -rw-r--r-- | src/gtk/gpl.cc | 370 | ||||
| -rw-r--r-- | src/gtk/gpl.h | 24 | ||||
| -rw-r--r-- | src/gtk/gtk_util.cc | 45 | ||||
| -rw-r--r-- | src/gtk/gtk_util.h | 27 | ||||
| -rw-r--r-- | src/gtk/job_manager_view.cc | 134 | ||||
| -rw-r--r-- | src/gtk/job_manager_view.h | 83 | ||||
| -rw-r--r-- | src/gtk/job_wrapper.cc | 50 | ||||
| -rw-r--r-- | src/gtk/job_wrapper.h | 27 | ||||
| -rw-r--r-- | src/gtk/wscript | 22 |
27 files changed, 3301 insertions, 0 deletions
diff --git a/src/gtk/alignment.cc b/src/gtk/alignment.cc new file mode 100644 index 000000000..ee4ca51c1 --- /dev/null +++ b/src/gtk/alignment.cc @@ -0,0 +1,167 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <iostream> +#include <gtkmm.h> +#include <cairomm/cairomm.h> +#include "alignment.h" + +using namespace std; + +class AlignmentWidget : public Gtk::DrawingArea +{ +public: + void set_text_line (int n, string t) + { + if (int(_text.size()) < (n + 1)) { + _text.resize (n + 1); + } + + _text[n] = t; + queue_draw (); + } + +private: + bool on_expose_event (GdkEventExpose* ev) + { + if (!get_window ()) { + return false; + } + + Cairo::RefPtr<Cairo::Context> c = get_window()->create_cairo_context (); + + Gtk::Allocation a = get_allocation (); + int const w = a.get_width (); + int const h = a.get_height (); + + c->rectangle (0, 0, w, h); + c->set_source_rgb (0, 0, 0); + c->fill (); + + c->set_source_rgb (1, 1, 1); + c->set_line_width (1); + + int const arrow_size = h / 8; + int const head_size = h / 32; + + /* arrow to left edge */ + c->move_to (arrow_size, h / 2); + c->line_to (0, h / 2); + c->rel_line_to (head_size, head_size); + c->move_to (0, h / 2); + c->rel_line_to (head_size, -head_size); + c->stroke (); + + /* arrow to right edge */ + c->move_to (w - arrow_size, h / 2); + c->line_to (w, h / 2); + c->rel_line_to (-head_size, head_size); + c->move_to (w, h / 2); + c->rel_line_to (-head_size, -head_size); + c->stroke (); + + /* arrow to top edge */ + c->move_to (w / 2, arrow_size); + c->line_to (w / 2, 0); + c->rel_line_to (head_size, head_size); + c->move_to (w / 2, 0); + c->rel_line_to (-head_size, head_size); + c->stroke (); + + /* arrow to bottom edge */ + c->move_to (w / 2, h - h / 8); + c->line_to (w / 2, h); + c->rel_line_to (head_size, -head_size); + c->move_to (w / 2, h); + c->rel_line_to (-head_size, -head_size); + c->stroke (); + + /* arrow to top-left corner */ + c->move_to (arrow_size, arrow_size); + c->line_to (0, 0); + c->rel_line_to (head_size, 0); + c->move_to (0, 0); + c->rel_line_to (0, head_size); + c->stroke (); + + /* arrow to top-right corner */ + c->move_to (w - arrow_size, arrow_size); + c->line_to (w, 0); + c->rel_line_to (0, head_size); + c->move_to (w, 0); + c->rel_line_to (-head_size, 0); + c->stroke (); + + /* arrow to bottom-left corner */ + c->move_to (arrow_size, h - arrow_size); + c->line_to (0, h); + c->rel_line_to (head_size, 0); + c->move_to (0, h); + c->rel_line_to (0, -head_size); + c->stroke (); + + /* arrow to bottom-right corner */ + c->move_to (w - arrow_size, h - arrow_size); + c->line_to (w, h); + c->rel_line_to (-head_size, 0); + c->line_to (w, h); + c->rel_line_to (0, -head_size); + c->stroke (); + + /* text */ + int max_height = 0; + for (vector<string>::iterator i = _text.begin(); i != _text.end(); ++i) { + Cairo::TextExtents e; + c->get_text_extents (*i, e); + max_height = max (max_height, int (e.height)); + } + + int total_height = max_height * _text.size() * 2; + + for (vector<string>::size_type i = 0; i < _text.size(); ++i) { + Cairo::TextExtents e; + c->get_text_extents (_text[i], e); + c->move_to ((w - e.width) / 2, ((h - total_height) / 2) + ((i * 2) + 1) * max_height); + c->text_path (_text[i]); + c->stroke (); + } + + return true; + } + + std::vector<std::string> _text; +}; + +Alignment::Alignment (Position p, Size s) +{ + _widget = Gtk::manage (new AlignmentWidget); + add (*_widget); + show_all (); + + set_decorated (false); + set_resizable (false); + set_size_request (s.width, s.height); + move (p.x, p.y); +} + +void +Alignment::set_text_line (int n, string t) +{ + _widget->set_text_line (n, t); +} diff --git a/src/gtk/alignment.h b/src/gtk/alignment.h new file mode 100644 index 000000000..fb740b7c0 --- /dev/null +++ b/src/gtk/alignment.h @@ -0,0 +1,35 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <gtkmm.h> +#include <string> +#include "lib/util.h" + +class AlignmentWidget; + +class Alignment : public Gtk::Window +{ +public: + Alignment (Position, Size); + + void set_text_line (int, std::string); + +private: + AlignmentWidget* _widget; +}; diff --git a/src/gtk/config_dialog.cc b/src/gtk/config_dialog.cc new file mode 100644 index 000000000..03f5b99a0 --- /dev/null +++ b/src/gtk/config_dialog.cc @@ -0,0 +1,369 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/config_dialog.cc + * @brief A dialogue to edit DVD-o-matic configuration. + */ + +#include <iostream> +#include <boost/lexical_cast.hpp> +#include "lib/config.h" +#include "lib/server.h" +#include "lib/screen.h" +#include "lib/format.h" +#include "lib/scaler.h" +#include "lib/filter.h" +#include "config_dialog.h" +#include "gtk_util.h" +#include "filter_dialog.h" + +using namespace std; +using namespace boost; + +ConfigDialog::ConfigDialog () + : Gtk::Dialog ("DVD-o-matic Configuration") + , _reference_filters_button ("Edit...") + , _add_server ("Add Server") + , _remove_server ("Remove Server") + , _add_screen ("Add Screen") + , _remove_screen ("Remove Screen") +{ + Gtk::Table* t = manage (new Gtk::Table); + t->set_row_spacings (6); + t->set_col_spacings (6); + t->set_border_width (6); + + Config* config = Config::instance (); + + _tms_ip.set_text (config->tms_ip ()); + _tms_ip.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::tms_ip_changed)); + _tms_path.set_text (config->tms_path ()); + _tms_path.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::tms_path_changed)); + _tms_user.set_text (config->tms_user ()); + _tms_user.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::tms_user_changed)); + _tms_password.set_text (config->tms_password ()); + _tms_password.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::tms_password_changed)); + + _num_local_encoding_threads.set_range (1, 128); + _num_local_encoding_threads.set_increments (1, 4); + _num_local_encoding_threads.set_value (config->num_local_encoding_threads ()); + _num_local_encoding_threads.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::num_local_encoding_threads_changed)); + + _colour_lut.append_text ("sRGB"); + _colour_lut.append_text ("Rec 709"); + _colour_lut.set_active (config->colour_lut_index ()); + _colour_lut.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::colour_lut_changed)); + + _j2k_bandwidth.set_range (50, 250); + _j2k_bandwidth.set_increments (10, 50); + _j2k_bandwidth.set_value (config->j2k_bandwidth() / 1e6); + _j2k_bandwidth.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::j2k_bandwidth_changed)); + + vector<Scaler const *> const sc = Scaler::all (); + for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) { + _reference_scaler.append_text ((*i)->name ()); + } + _reference_scaler.set_active (Scaler::as_index (config->reference_scaler ())); + _reference_scaler.signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::reference_scaler_changed)); + + _reference_filters.set_alignment (0, 0.5); + pair<string, string> p = Filter::ffmpeg_strings (config->reference_filters ()); + _reference_filters.set_text (p.first + " " + p.second); + _reference_filters_button.signal_clicked().connect (sigc::mem_fun (*this, &ConfigDialog::edit_reference_filters_clicked)); + + _servers_store = Gtk::ListStore::create (_servers_columns); + vector<Server*> servers = config->servers (); + for (vector<Server*>::iterator i = servers.begin(); i != servers.end(); ++i) { + add_server_to_store (*i); + } + + _servers_view.set_model (_servers_store); + _servers_view.append_column_editable ("Host Name", _servers_columns._host_name); + _servers_view.append_column_editable ("Threads", _servers_columns._threads); + + _add_server.signal_clicked().connect (sigc::mem_fun (*this, &ConfigDialog::add_server_clicked)); + _remove_server.signal_clicked().connect (sigc::mem_fun (*this, &ConfigDialog::remove_server_clicked)); + + _servers_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::server_selection_changed)); + server_selection_changed (); + + _screens_store = Gtk::TreeStore::create (_screens_columns); + vector<shared_ptr<Screen> > screens = config->screens (); + for (vector<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) { + add_screen_to_store (*i); + } + + _screens_view.set_model (_screens_store); + _screens_view.append_column_editable ("Screen", _screens_columns._name); + _screens_view.append_column ("Format", _screens_columns._format_name); + _screens_view.append_column_editable ("x", _screens_columns._x); + _screens_view.append_column_editable ("y", _screens_columns._y); + _screens_view.append_column_editable ("Width", _screens_columns._width); + _screens_view.append_column_editable ("Height", _screens_columns._height); + + _add_screen.signal_clicked().connect (sigc::mem_fun (*this, &ConfigDialog::add_screen_clicked)); + _remove_screen.signal_clicked().connect (sigc::mem_fun (*this, &ConfigDialog::remove_screen_clicked)); + + _screens_view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &ConfigDialog::screen_selection_changed)); + screen_selection_changed (); + + int n = 0; + t->attach (left_aligned_label ("TMS IP address"), 0, 1, n, n + 1); + t->attach (_tms_ip, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("TMS target path"), 0, 1, n, n + 1); + t->attach (_tms_path, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("TMS user name"), 0, 1, n, n + 1); + t->attach (_tms_user, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("TMS password"), 0, 1, n, n + 1); + t->attach (_tms_password, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Threads to use for encoding on this host"), 0, 1, n, n + 1); + t->attach (_num_local_encoding_threads, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Colour look-up table"), 0, 1, n, n + 1); + t->attach (_colour_lut, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("JPEG2000 bandwidth"), 0, 1, n, n + 1); + t->attach (_j2k_bandwidth, 1, 2, n, n + 1); + t->attach (left_aligned_label ("MBps"), 2, 3, n, n + 1); + ++n; + t->attach (left_aligned_label ("Reference scaler for A/B"), 0, 1, n, n + 1); + t->attach (_reference_scaler, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Reference filters for A/B"), 0, 1, n, n + 1); + Gtk::HBox* fb = Gtk::manage (new Gtk::HBox); + fb->set_spacing (4); + fb->pack_start (_reference_filters, true, true); + fb->pack_start (_reference_filters_button, false, false); + t->attach (*fb, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Encoding Servers"), 0, 1, n, n + 1); + t->attach (_servers_view, 1, 2, n, n + 1); + Gtk::VBox* b = manage (new Gtk::VBox); + b->pack_start (_add_server, false, false); + b->pack_start (_remove_server, false, false); + t->attach (*b, 2, 3, n, n + 1); + ++n; + t->attach (left_aligned_label ("Screens"), 0, 1, n, n + 1); + t->attach (_screens_view, 1, 2, n, n + 1); + b = manage (new Gtk::VBox); + b->pack_start (_add_screen, false, false); + b->pack_start (_remove_screen, false, false); + t->attach (*b, 2, 3, n, n + 1); + ++n; + + t->show_all (); + get_vbox()->pack_start (*t); + + get_vbox()->set_border_width (24); + + add_button ("Close", Gtk::RESPONSE_CLOSE); +} + +void +ConfigDialog::tms_ip_changed () +{ + Config::instance()->set_tms_ip (_tms_ip.get_text ()); +} + +void +ConfigDialog::tms_path_changed () +{ + Config::instance()->set_tms_path (_tms_path.get_text ()); +} + +void +ConfigDialog::tms_user_changed () +{ + Config::instance()->set_tms_user (_tms_user.get_text ()); +} + +void +ConfigDialog::tms_password_changed () +{ + Config::instance()->set_tms_password (_tms_password.get_text ()); +} + + +void +ConfigDialog::num_local_encoding_threads_changed () +{ + Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads.get_value ()); +} + +void +ConfigDialog::colour_lut_changed () +{ + Config::instance()->set_colour_lut_index (_colour_lut.get_active_row_number ()); +} + +void +ConfigDialog::j2k_bandwidth_changed () +{ + Config::instance()->set_j2k_bandwidth (_j2k_bandwidth.get_value() * 1e6); +} + +void +ConfigDialog::on_response (int r) +{ + vector<Server*> servers; + + Gtk::TreeModel::Children c = _servers_store->children (); + for (Gtk::TreeModel::Children::iterator i = c.begin(); i != c.end(); ++i) { + Gtk::TreeModel::Row r = *i; + Server* s = new Server (r[_servers_columns._host_name], r[_servers_columns._threads]); + servers.push_back (s); + } + + Config::instance()->set_servers (servers); + + vector<shared_ptr<Screen> > screens; + + c = _screens_store->children (); + for (Gtk::TreeModel::Children::iterator i = c.begin(); i != c.end(); ++i) { + + Gtk::TreeModel::Row r = *i; + shared_ptr<Screen> s (new Screen (r[_screens_columns._name])); + + Gtk::TreeModel::Children cc = r.children (); + for (Gtk::TreeModel::Children::iterator j = cc.begin(); j != cc.end(); ++j) { + Gtk::TreeModel::Row r = *j; + string const x_ = r[_screens_columns._x]; + string const y_ = r[_screens_columns._y]; + string const width_ = r[_screens_columns._width]; + string const height_ = r[_screens_columns._height]; + s->set_geometry ( + Format::from_nickname (r[_screens_columns._format_nickname]), + Position (lexical_cast<int> (x_), lexical_cast<int> (y_)), + Size (lexical_cast<int> (width_), lexical_cast<int> (height_)) + ); + } + + screens.push_back (s); + } + + Config::instance()->set_screens (screens); + + Gtk::Dialog::on_response (r); +} + +void +ConfigDialog::add_server_to_store (Server* s) +{ + Gtk::TreeModel::iterator i = _servers_store->append (); + Gtk::TreeModel::Row r = *i; + r[_servers_columns._host_name] = s->host_name (); + r[_servers_columns._threads] = s->threads (); +} + +void +ConfigDialog::add_server_clicked () +{ + Server s ("localhost", 1); + add_server_to_store (&s); +} + +void +ConfigDialog::remove_server_clicked () +{ + Gtk::TreeModel::iterator i = _servers_view.get_selection()->get_selected (); + if (i) { + _servers_store->erase (i); + } +} + +void +ConfigDialog::server_selection_changed () +{ + Gtk::TreeModel::iterator i = _servers_view.get_selection()->get_selected (); + _remove_server.set_sensitive (i); +} + + +void +ConfigDialog::add_screen_to_store (shared_ptr<Screen> s) +{ + Gtk::TreeModel::iterator i = _screens_store->append (); + Gtk::TreeModel::Row r = *i; + r[_screens_columns._name] = s->name (); + + Screen::GeometryMap geoms = s->geometries (); + for (Screen::GeometryMap::const_iterator j = geoms.begin(); j != geoms.end(); ++j) { + i = _screens_store->append (r.children ()); + Gtk::TreeModel::Row c = *i; + c[_screens_columns._format_name] = j->first->name (); + c[_screens_columns._format_nickname] = j->first->nickname (); + c[_screens_columns._x] = lexical_cast<string> (j->second.position.x); + c[_screens_columns._y] = lexical_cast<string> (j->second.position.y); + c[_screens_columns._width] = lexical_cast<string> (j->second.size.width); + c[_screens_columns._height] = lexical_cast<string> (j->second.size.height); + } +} + +void +ConfigDialog::add_screen_clicked () +{ + shared_ptr<Screen> s (new Screen ("New Screen")); + add_screen_to_store (s); +} + +void +ConfigDialog::remove_screen_clicked () +{ + Gtk::TreeModel::iterator i = _screens_view.get_selection()->get_selected (); + if (i) { + _screens_store->erase (i); + } +} + +void +ConfigDialog::screen_selection_changed () +{ + Gtk::TreeModel::iterator i = _screens_view.get_selection()->get_selected (); + _remove_screen.set_sensitive (i); +} + + +void +ConfigDialog::reference_scaler_changed () +{ + int const n = _reference_scaler.get_active_row_number (); + if (n >= 0) { + Config::instance()->set_reference_scaler (Scaler::from_index (n)); + } +} + +void +ConfigDialog::edit_reference_filters_clicked () +{ + FilterDialog d (Config::instance()->reference_filters ()); + d.ActiveChanged.connect (sigc::mem_fun (*this, &ConfigDialog::reference_filters_changed)); + d.run (); +} + +void +ConfigDialog::reference_filters_changed (vector<Filter const *> f) +{ + Config::instance()->set_reference_filters (f); + pair<string, string> p = Filter::ffmpeg_strings (Config::instance()->reference_filters ()); + _reference_filters.set_text (p.first + " " + p.second); +} diff --git a/src/gtk/config_dialog.h b/src/gtk/config_dialog.h new file mode 100644 index 000000000..ec345750a --- /dev/null +++ b/src/gtk/config_dialog.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/config_dialog.h + * @brief A dialogue to edit DVD-o-matic configuration. + */ + +#include <gtkmm.h> + +class Screen; +class Server; + +/** @class ConfigDialog + * @brief A dialogue to edit DVD-o-matic configuration. + */ +class ConfigDialog : public Gtk::Dialog +{ +public: + ConfigDialog (); + +private: + void on_response (int); + + void tms_ip_changed (); + void tms_path_changed (); + void tms_user_changed (); + void tms_password_changed (); + void num_local_encoding_threads_changed (); + void colour_lut_changed (); + void j2k_bandwidth_changed (); + void add_server_clicked (); + void remove_server_clicked (); + void server_selection_changed (); + void add_screen_clicked (); + void remove_screen_clicked (); + void screen_selection_changed (); + void reference_scaler_changed (); + void edit_reference_filters_clicked (); + void reference_filters_changed (std::vector<Filter const *>); + + void add_screen_to_store (boost::shared_ptr<Screen>); + void add_server_to_store (Server *); + + struct ServersModelColumns : public Gtk::TreeModelColumnRecord + { + ServersModelColumns () { + add (_host_name); + add (_threads); + } + + Gtk::TreeModelColumn<std::string> _host_name; + Gtk::TreeModelColumn<int> _threads; + }; + + struct ScreensModelColumns : public Gtk::TreeModelColumnRecord + { + ScreensModelColumns () { + add (_name); + add (_format_name); + add (_format_nickname); + add (_x); + add (_y); + add (_width); + add (_height); + } + + Gtk::TreeModelColumn<std::string> _name; + Gtk::TreeModelColumn<std::string> _format_name; + Gtk::TreeModelColumn<std::string> _format_nickname; + Gtk::TreeModelColumn<std::string> _x; + Gtk::TreeModelColumn<std::string> _y; + Gtk::TreeModelColumn<std::string> _width; + Gtk::TreeModelColumn<std::string> _height; + }; + + Gtk::Entry _tms_ip; + Gtk::Entry _tms_path; + Gtk::Entry _tms_user; + Gtk::Entry _tms_password; + Gtk::SpinButton _num_local_encoding_threads; + Gtk::ComboBoxText _colour_lut; + Gtk::SpinButton _j2k_bandwidth; + Gtk::ComboBoxText _reference_scaler; + Gtk::Label _reference_filters; + Gtk::Button _reference_filters_button; + ServersModelColumns _servers_columns; + Glib::RefPtr<Gtk::ListStore> _servers_store; + Gtk::TreeView _servers_view; + Gtk::Button _add_server; + Gtk::Button _remove_server; + ScreensModelColumns _screens_columns; + Glib::RefPtr<Gtk::TreeStore> _screens_store; + Gtk::TreeView _screens_view; + Gtk::Button _add_screen; + Gtk::Button _remove_screen; +}; + diff --git a/src/gtk/dcp_range_dialog.cc b/src/gtk/dcp_range_dialog.cc new file mode 100644 index 000000000..d1fef0e8b --- /dev/null +++ b/src/gtk/dcp_range_dialog.cc @@ -0,0 +1,117 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "dcp_range_dialog.h" +#include "lib/film.h" + +DCPRangeDialog::DCPRangeDialog (Film* f) + : _film (f) + , _whole ("Whole film") + , _first ("First") + , _cut ("Cut remainder") + , _black_out ("Black-out remainder") +{ + set_title ("DCP range"); + + Gtk::Table* table = Gtk::manage (new Gtk::Table ()); + table->set_border_width (6); + table->set_row_spacings (6); + table->set_col_spacings (6); + table->attach (_whole, 0, 4, 0, 1); + table->attach (_first, 0, 1, 1, 2); + table->attach (_n_frames, 1, 2, 1, 2); + table->attach (*manage (new Gtk::Label ("frames")), 2, 3, 1, 2); + table->attach (_cut, 1, 2, 2, 3); + table->attach (_black_out, 1, 2, 3, 4); + + Gtk::RadioButtonGroup g = _whole.get_group (); + _first.set_group (g); + + g = _black_out.get_group (); + _cut.set_group (g); + + _n_frames.set_range (1, INT_MAX - 1); + _n_frames.set_increments (24, 24 * 60); + if (_film->dcp_frames() > 0) { + _whole.set_active (false); + _first.set_active (true); + _n_frames.set_value (_film->dcp_frames ()); + } else { + _whole.set_active (true); + _first.set_active (false); + _n_frames.set_value (24); + } + + _black_out.set_active (_film->dcp_trim_action() == BLACK_OUT); + _cut.set_active (_film->dcp_trim_action() == CUT); + + _whole.signal_toggled().connect (sigc::mem_fun (*this, &DCPRangeDialog::whole_toggled)); + _cut.signal_toggled().connect (sigc::mem_fun (*this, &DCPRangeDialog::cut_toggled)); + _n_frames.signal_value_changed().connect (sigc::mem_fun (*this, &DCPRangeDialog::n_frames_changed)); + + get_vbox()->pack_start (*table); + + add_button ("Close", Gtk::RESPONSE_CLOSE); + show_all_children (); + + set_sensitivity (); +} + +void +DCPRangeDialog::whole_toggled () +{ + set_sensitivity (); + emit_changed (); +} + +void +DCPRangeDialog::set_sensitivity () +{ + _n_frames.set_sensitive (_first.get_active ()); + _black_out.set_sensitive (_first.get_active ()); + _cut.set_sensitive (_first.get_active ()); +} + +void +DCPRangeDialog::cut_toggled () +{ + emit_changed (); +} + +void +DCPRangeDialog::n_frames_changed () +{ + emit_changed (); +} + +void +DCPRangeDialog::emit_changed () +{ + int frames = 0; + if (!_whole.get_active ()) { + frames = _n_frames.get_value_as_int (); + } + + TrimAction action = CUT; + if (_black_out.get_active ()) { + action = BLACK_OUT; + } + + Changed (frames, action); +} diff --git a/src/gtk/dcp_range_dialog.h b/src/gtk/dcp_range_dialog.h new file mode 100644 index 000000000..7469a2576 --- /dev/null +++ b/src/gtk/dcp_range_dialog.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <gtkmm.h> +#include "lib/trim_action.h" + +class Film; + +class DCPRangeDialog : public Gtk::Dialog +{ +public: + DCPRangeDialog (Film *); + + sigc::signal2<void, int, TrimAction> Changed; + +private: + void whole_toggled (); + void cut_toggled (); + void n_frames_changed (); + + void set_sensitivity (); + void emit_changed (); + + Film* _film; + Gtk::RadioButton _whole; + Gtk::RadioButton _first; + Gtk::SpinButton _n_frames; + Gtk::RadioButton _cut; + Gtk::RadioButton _black_out; +}; diff --git a/src/gtk/film_editor.cc b/src/gtk/film_editor.cc new file mode 100644 index 000000000..0e410aa6b --- /dev/null +++ b/src/gtk/film_editor.cc @@ -0,0 +1,572 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/film_editor.cc + * @brief A GTK widget to edit a film's metadata, and perform various functions. + */ + +#include <iostream> +#include <gtkmm.h> +#include <boost/thread.hpp> +#include <boost/filesystem.hpp> +#include "lib/format.h" +#include "lib/film.h" +#include "lib/transcode_job.h" +#include "lib/exceptions.h" +#include "lib/ab_transcode_job.h" +#include "lib/thumbs_job.h" +#include "lib/make_mxf_job.h" +#include "lib/job_manager.h" +#include "lib/filter.h" +#include "lib/screen.h" +#include "lib/config.h" +#include "lib/scp_dcp_job.h" +#include "filter_dialog.h" +#include "gtk_util.h" +#include "film_editor.h" +#include "dcp_range_dialog.h" + +using namespace std; +using namespace boost; +using namespace Gtk; + +/** @param f Film to edit */ +FilmEditor::FilmEditor (Film* f) + : _film (f) + , _filters_button ("Edit...") + , _change_dcp_range_button ("Edit...") + , _dcp_ab ("A/B") +{ + _vbox.set_border_width (12); + _vbox.set_spacing (12); + + /* Set up our editing widgets */ + _left_crop.set_range (0, 1024); + _left_crop.set_increments (1, 16); + _top_crop.set_range (0, 1024); + _top_crop.set_increments (1, 16); + _right_crop.set_range (0, 1024); + _right_crop.set_increments (1, 16); + _bottom_crop.set_range (0, 1024); + _bottom_crop.set_increments (1, 16); + _filters.set_alignment (0, 0.5); + _audio_gain.set_range (-60, 60); + _audio_gain.set_increments (1, 3); + _audio_delay.set_range (-1000, 1000); + _audio_delay.set_increments (1, 20); + _still_duration.set_range (0, 60 * 60); + _still_duration.set_increments (1, 5); + _dcp_range.set_alignment (0, 0.5); + + vector<Format const *> fmt = Format::all (); + for (vector<Format const *>::iterator i = fmt.begin(); i != fmt.end(); ++i) { + _format.append_text ((*i)->name ()); + } + + _frames_per_second.set_increments (1, 5); + _frames_per_second.set_digits (2); + _frames_per_second.set_range (0, 60); + + vector<DCPContentType const *> const ct = DCPContentType::all (); + for (vector<DCPContentType const *>::const_iterator i = ct.begin(); i != ct.end(); ++i) { + _dcp_content_type.append_text ((*i)->pretty_name ()); + } + + vector<Scaler const *> const sc = Scaler::all (); + for (vector<Scaler const *>::const_iterator i = sc.begin(); i != sc.end(); ++i) { + _scaler.append_text ((*i)->name ()); + } + + _original_size.set_alignment (0, 0.5); + _length.set_alignment (0, 0.5); + _audio.set_alignment (0, 0.5); + + /* And set their values from the Film */ + set_film (f); + + /* Now connect to them, since initial values are safely set */ + _name.signal_changed().connect (sigc::mem_fun (*this, &FilmEditor::name_changed)); + _frames_per_second.signal_changed().connect (sigc::mem_fun (*this, &FilmEditor::frames_per_second_changed)); + _format.signal_changed().connect (sigc::mem_fun (*this, &FilmEditor::format_changed)); + _content.signal_file_set().connect (sigc::mem_fun (*this, &FilmEditor::content_changed)); + _left_crop.signal_value_changed().connect (sigc::mem_fun (*this, &FilmEditor::left_crop_changed)); + _right_crop.signal_value_changed().connect (sigc::mem_fun (*this, &FilmEditor::right_crop_changed)); + _top_crop.signal_value_changed().connect (sigc::mem_fun (*this, &FilmEditor::top_crop_changed)); + _bottom_crop.signal_value_changed().connect (sigc::mem_fun (*this, &FilmEditor::bottom_crop_changed)); + _filters_button.signal_clicked().connect (sigc::mem_fun (*this, &FilmEditor::edit_filters_clicked)); + _scaler.signal_changed().connect (sigc::mem_fun (*this, &FilmEditor::scaler_changed)); + _dcp_content_type.signal_changed().connect (sigc::mem_fun (*this, &FilmEditor::dcp_content_type_changed)); + _dcp_ab.signal_toggled().connect (sigc::mem_fun (*this, &FilmEditor::dcp_ab_toggled)); + _audio_gain.signal_value_changed().connect (sigc::mem_fun (*this, &FilmEditor::audio_gain_changed)); + _audio_delay.signal_value_changed().connect (sigc::mem_fun (*this, &FilmEditor::audio_delay_changed)); + _still_duration.signal_value_changed().connect (sigc::mem_fun (*this, &FilmEditor::still_duration_changed)); + _change_dcp_range_button.signal_clicked().connect (sigc::mem_fun (*this, &FilmEditor::change_dcp_range_clicked)); + + /* Set up the table */ + + Table* t = manage (new Table); + + t->set_row_spacings (4); + t->set_col_spacings (12); + + int n = 0; + t->attach (left_aligned_label ("Name"), 0, 1, n, n + 1); + t->attach (_name, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Content"), 0, 1, n, n + 1); + t->attach (_content, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Content Type"), 0, 1, n, n + 1); + t->attach (_dcp_content_type, 1, 2, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Frames Per Second")), 0, 1, n, n + 1); + t->attach (video_widget (_frames_per_second), 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Format"), 0, 1, n, n + 1); + t->attach (_format, 1, 2, n, n + 1); + ++n; + t->attach (left_aligned_label ("Crop"), 0, 1, n, n + 1); + HBox* c = manage (new HBox); + c->set_spacing (4); + c->pack_start (left_aligned_label ("L"), false, false); + c->pack_start (_left_crop, true, true); + c->pack_start (left_aligned_label ("R"), false, false); + c->pack_start (_right_crop, true, true); + c->pack_start (left_aligned_label ("T"), false, false); + c->pack_start (_top_crop, true, true); + c->pack_start (left_aligned_label ("B"), false, false); + c->pack_start (_bottom_crop, true, true); + t->attach (*c, 1, 2, n, n + 1); + ++n; + + /* VIDEO-only stuff */ + int const special = n; + t->attach (video_widget (left_aligned_label ("Filters")), 0, 1, n, n + 1); + HBox* fb = manage (new HBox); + fb->set_spacing (4); + fb->pack_start (video_widget (_filters), true, true); + fb->pack_start (video_widget (_filters_button), false, false); + t->attach (*fb, 1, 2, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Scaler")), 0, 1, n, n + 1); + t->attach (video_widget (_scaler), 1, 2, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Audio Gain")), 0, 1, n, n + 1); + t->attach (video_widget (_audio_gain), 1, 2, n, n + 1); + t->attach (video_widget (left_aligned_label ("dB")), 2, 3, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Audio Delay")), 0, 1, n, n + 1); + t->attach (video_widget (_audio_delay), 1, 2, n, n + 1); + t->attach (video_widget (left_aligned_label ("ms")), 2, 3, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Original Size")), 0, 1, n, n + 1); + t->attach (video_widget (_original_size), 1, 2, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Length")), 0, 1, n, n + 1); + t->attach (video_widget (_length), 1, 2, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Audio")), 0, 1, n, n + 1); + t->attach (video_widget (_audio), 1, 2, n, n + 1); + ++n; + t->attach (video_widget (left_aligned_label ("Range")), 0, 1, n, n + 1); + Gtk::HBox* db = manage (new Gtk::HBox); + db->pack_start (_dcp_range, true, true); + db->pack_start (_change_dcp_range_button, false, false); + t->attach (*db, 1, 2, n, n + 1); + ++n; + t->attach (_dcp_ab, 0, 3, n, n + 1); + + /* STILL-only stuff */ + n = special; + t->attach (still_widget (left_aligned_label ("Duration")), 0, 1, n, n + 1); + t->attach (still_widget (_still_duration), 1, 2, n, n + 1); + t->attach (still_widget (left_aligned_label ("s")), 2, 3, n, n + 1); + ++n; + + t->show_all (); + _vbox.pack_start (*t, false, false); +} + +/** @return Our main widget, which contains everything else */ +Widget& +FilmEditor::widget () +{ + return _vbox; +} + +/** Called when the left crop widget has been changed */ +void +FilmEditor::left_crop_changed () +{ + if (_film) { + _film->set_left_crop (_left_crop.get_value ()); + } +} + +/** Called when the right crop widget has been changed */ +void +FilmEditor::right_crop_changed () +{ + if (_film) { + _film->set_right_crop (_right_crop.get_value ()); + } +} + +/** Called when the top crop widget has been changed */ +void +FilmEditor::top_crop_changed () +{ + if (_film) { + _film->set_top_crop (_top_crop.get_value ()); + } +} + +/** Called when the bottom crop value has been changed */ +void +FilmEditor::bottom_crop_changed () +{ + if (_film) { + _film->set_bottom_crop (_bottom_crop.get_value ()); + } +} + +/** Called when the content filename has been changed */ +void +FilmEditor::content_changed () +{ + if (!_film) { + return; + } + + try { + _film->set_content (_content.get_filename ()); + } catch (std::exception& e) { + _content.set_filename (_film->directory ()); + stringstream m; + m << "Could not set content: " << e.what() << "."; + Gtk::MessageDialog d (m.str(), false, MESSAGE_ERROR); + d.set_title ("DVD-o-matic"); + d.run (); + } +} + +/** Called when the DCP A/B switch has been toggled */ +void +FilmEditor::dcp_ab_toggled () +{ + if (_film) { + _film->set_dcp_ab (_dcp_ab.get_active ()); + } +} + +/** Called when the name widget has been changed */ +void +FilmEditor::name_changed () +{ + if (_film) { + _film->set_name (_name.get_text ()); + } +} + +/** Called when the metadata stored in the Film object has changed; + * so that we can update the GUI. + * @param p Property of the Film that has changed. + */ +void +FilmEditor::film_changed (Film::Property p) +{ + if (!_film) { + return; + } + + stringstream s; + + switch (p) { + case Film::CONTENT: + _content.set_filename (_film->content ()); + setup_visibility (); + break; + case Film::FORMAT: + _format.set_active (Format::as_index (_film->format ())); + break; + case Film::LEFT_CROP: + _left_crop.set_value (_film->left_crop ()); + break; + case Film::RIGHT_CROP: + _right_crop.set_value (_film->right_crop ()); + break; + case Film::TOP_CROP: + _top_crop.set_value (_film->top_crop ()); + break; + case Film::BOTTOM_CROP: + _bottom_crop.set_value (_film->bottom_crop ()); + break; + case Film::FILTERS: + { + pair<string, string> p = Filter::ffmpeg_strings (_film->filters ()); + _filters.set_text (p.first + " " + p.second); + break; + } + case Film::NAME: + _name.set_text (_film->name ()); + break; + case Film::FRAMES_PER_SECOND: + _frames_per_second.set_value (_film->frames_per_second ()); + break; + case Film::AUDIO_CHANNELS: + case Film::AUDIO_SAMPLE_RATE: + s << _film->audio_channels () << " channels, " << _film->audio_sample_rate() << "Hz"; + _audio.set_text (s.str ()); + break; + case Film::SIZE: + s << _film->size().width << " x " << _film->size().height; + _original_size.set_text (s.str ()); + break; + case Film::LENGTH: + if (_film->frames_per_second() > 0) { + s << _film->length() << " frames; " << seconds_to_hms (_film->length() / _film->frames_per_second()); + } else { + s << _film->length() << " frames"; + } + _length.set_text (s.str ()); + break; + case Film::DCP_CONTENT_TYPE: + _dcp_content_type.set_active (DCPContentType::as_index (_film->dcp_content_type ())); + break; + case Film::THUMBS: + break; + case Film::DCP_FRAMES: + if (_film->dcp_frames() == 0) { + _dcp_range.set_text ("Whole film"); + } else { + stringstream s; + s << "First " << _film->dcp_frames() << " frames"; + _dcp_range.set_text (s.str ()); + } + break; + case Film::DCP_TRIM_ACTION: + break; + case Film::DCP_AB: + _dcp_ab.set_active (_film->dcp_ab ()); + break; + case Film::SCALER: + _scaler.set_active (Scaler::as_index (_film->scaler ())); + break; + case Film::AUDIO_GAIN: + _audio_gain.set_value (_film->audio_gain ()); + break; + case Film::AUDIO_DELAY: + _audio_delay.set_value (_film->audio_delay ()); + break; + case Film::STILL_DURATION: + _still_duration.set_value (_film->still_duration ()); + break; + } +} + +/** Called when the format widget has been changed */ +void +FilmEditor::format_changed () +{ + if (_film) { + int const n = _format.get_active_row_number (); + if (n >= 0) { + _film->set_format (Format::from_index (n)); + } + } +} + +/** Called when the DCP content type widget has been changed */ +void +FilmEditor::dcp_content_type_changed () +{ + if (_film) { + int const n = _dcp_content_type.get_active_row_number (); + if (n >= 0) { + _film->set_dcp_content_type (DCPContentType::from_index (n)); + } + } +} + +/** Sets the Film that we are editing */ +void +FilmEditor::set_film (Film* f) +{ + _film = f; + + set_things_sensitive (_film != 0); + + if (_film) { + _film->Changed.connect (sigc::mem_fun (*this, &FilmEditor::film_changed)); + } + + if (_film) { + FileChanged (_film->directory ()); + } else { + FileChanged (""); + } + + film_changed (Film::NAME); + film_changed (Film::CONTENT); + film_changed (Film::DCP_CONTENT_TYPE); + film_changed (Film::FORMAT); + film_changed (Film::LEFT_CROP); + film_changed (Film::RIGHT_CROP); + film_changed (Film::TOP_CROP); + film_changed (Film::BOTTOM_CROP); + film_changed (Film::FILTERS); + film_changed (Film::DCP_FRAMES); + film_changed (Film::DCP_TRIM_ACTION); + film_changed (Film::DCP_AB); + film_changed (Film::SIZE); + film_changed (Film::LENGTH); + film_changed (Film::FRAMES_PER_SECOND); + film_changed (Film::AUDIO_CHANNELS); + film_changed (Film::AUDIO_SAMPLE_RATE); + film_changed (Film::SCALER); + film_changed (Film::AUDIO_GAIN); + film_changed (Film::AUDIO_DELAY); + film_changed (Film::STILL_DURATION); +} + +/** Updates the sensitivity of lots of widgets to a given value. + * @param s true to make sensitive, false to make insensitive. + */ +void +FilmEditor::set_things_sensitive (bool s) +{ + _name.set_sensitive (s); + _frames_per_second.set_sensitive (s); + _format.set_sensitive (s); + _content.set_sensitive (s); + _left_crop.set_sensitive (s); + _right_crop.set_sensitive (s); + _top_crop.set_sensitive (s); + _bottom_crop.set_sensitive (s); + _filters_button.set_sensitive (s); + _scaler.set_sensitive (s); + _dcp_content_type.set_sensitive (s); + _dcp_range.set_sensitive (s); + _change_dcp_range_button.set_sensitive (s); + _dcp_ab.set_sensitive (s); + _audio_gain.set_sensitive (s); + _audio_delay.set_sensitive (s); + _still_duration.set_sensitive (s); +} + +/** Called when the `Edit filters' button has been clicked */ +void +FilmEditor::edit_filters_clicked () +{ + FilterDialog d (_film->filters ()); + d.ActiveChanged.connect (sigc::mem_fun (*_film, &Film::set_filters)); + d.run (); +} + +/** Called when the scaler widget has been changed */ +void +FilmEditor::scaler_changed () +{ + if (_film) { + int const n = _scaler.get_active_row_number (); + if (n >= 0) { + _film->set_scaler (Scaler::from_index (n)); + } + } +} + +/** Called when the frames per second widget has been changed */ +void +FilmEditor::frames_per_second_changed () +{ + if (_film) { + _film->set_frames_per_second (_frames_per_second.get_value ()); + } +} + +void +FilmEditor::audio_gain_changed () +{ + if (_film) { + _film->set_audio_gain (_audio_gain.get_value ()); + } +} + +void +FilmEditor::audio_delay_changed () +{ + if (_film) { + _film->set_audio_delay (_audio_delay.get_value ()); + } +} + +Widget& +FilmEditor::video_widget (Widget& w) +{ + _video_widgets.push_back (&w); + return w; +} + +Widget& +FilmEditor::still_widget (Widget& w) +{ + _still_widgets.push_back (&w); + return w; +} + +void +FilmEditor::setup_visibility () +{ + if (!_film) { + return; + } + + ContentType const c = _film->content_type (); + + for (list<Widget *>::iterator i = _video_widgets.begin(); i != _video_widgets.end(); ++i) { + (*i)->property_visible() = (c == VIDEO); + } + + for (list<Widget *>::iterator i = _still_widgets.begin(); i != _still_widgets.end(); ++i) { + (*i)->property_visible() = (c == STILL); + } +} + +void +FilmEditor::still_duration_changed () +{ + if (_film) { + _film->set_still_duration (_still_duration.get_value ()); + } +} + +void +FilmEditor::change_dcp_range_clicked () +{ + DCPRangeDialog d (_film); + d.Changed.connect (sigc::mem_fun (*this, &FilmEditor::dcp_range_changed)); + d.run (); +} + +void +FilmEditor::dcp_range_changed (int frames, TrimAction action) +{ + _film->set_dcp_frames (frames); + _film->set_dcp_trim_action (action); +} diff --git a/src/gtk/film_editor.h b/src/gtk/film_editor.h new file mode 100644 index 000000000..9d15b436d --- /dev/null +++ b/src/gtk/film_editor.h @@ -0,0 +1,125 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/film_editor.h + * @brief A GTK widget to edit a film's metadata, and perform various functions. + */ + +#include <gtkmm.h> + +class Film; + +/** @class FilmEditor + * @brief A GTK widget to edit a film's metadata, and perform various functions. + */ +class FilmEditor +{ +public: + FilmEditor (Film *); + + Gtk::Widget& widget (); + + void set_film (Film *); + void setup_visibility (); + + sigc::signal1<void, std::string> FileChanged; + +private: + /* Handle changes to the view */ + void name_changed (); + void left_crop_changed (); + void right_crop_changed (); + void top_crop_changed (); + void bottom_crop_changed (); + void content_changed (); + void frames_per_second_changed (); + void format_changed (); + void dcp_range_changed (int, TrimAction); + void dcp_content_type_changed (); + void dcp_ab_toggled (); + void scaler_changed (); + void audio_gain_changed (); + void audio_delay_changed (); + void still_duration_changed (); + + /* Handle changes to the model */ + void film_changed (Film::Property); + + /* Button clicks */ + void edit_filters_clicked (); + void change_dcp_range_clicked (); + + void set_things_sensitive (bool); + + Gtk::Widget & video_widget (Gtk::Widget &); + Gtk::Widget & still_widget (Gtk::Widget &); + + /** The film we are editing */ + Film* _film; + /** The overall VBox containing our widget */ + Gtk::VBox _vbox; + /** The Film's name */ + Gtk::Entry _name; + /** The Film's frames per second */ + Gtk::SpinButton _frames_per_second; + /** The Film's format */ + Gtk::ComboBoxText _format; + /** The Film's content file */ + Gtk::FileChooserButton _content; + /** The Film's left crop */ + Gtk::SpinButton _left_crop; + /** The Film's right crop */ + Gtk::SpinButton _right_crop; + /** The Film's top crop */ + Gtk::SpinButton _top_crop; + /** The Film's bottom crop */ + Gtk::SpinButton _bottom_crop; + /** Currently-applied filters */ + Gtk::Label _filters; + /** Button to open the filters dialogue */ + Gtk::Button _filters_button; + /** The Film's scaler */ + Gtk::ComboBoxText _scaler; + /** The Film's audio gain */ + Gtk::SpinButton _audio_gain; + /** The Film's audio delay */ + Gtk::SpinButton _audio_delay; + /** The Film's DCP content type */ + Gtk::ComboBoxText _dcp_content_type; + /** The Film's original size */ + Gtk::Label _original_size; + /** The Film's length */ + Gtk::Label _length; + /** The Film's audio details */ + Gtk::Label _audio; + /** The Film's duration for still sources */ + Gtk::SpinButton _still_duration; + + /** Button to start making a DCP from existing J2K and WAV files */ + Gtk::Button _make_dcp_from_existing_button; + /** Display of the range of frames that will be used */ + Gtk::Label _dcp_range; + /** Button to change the range */ + Gtk::Button _change_dcp_range_button; + /** Selector to generate an A/B comparison DCP */ + Gtk::CheckButton _dcp_ab; + + std::list<Gtk::Widget*> _video_widgets; + std::list<Gtk::Widget*> _still_widgets; +}; diff --git a/src/gtk/film_list.cc b/src/gtk/film_list.cc new file mode 100644 index 000000000..1a9854450 --- /dev/null +++ b/src/gtk/film_list.cc @@ -0,0 +1,65 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <boost/filesystem.hpp> +#include "lib/film.h" +#include "film_list.h" + +using namespace std; +using namespace boost; + +FilmList::FilmList (string d) + : _directory (d) + , _list (1) +{ + for (filesystem::directory_iterator i = filesystem::directory_iterator (_directory); i != filesystem::directory_iterator(); ++i) { + if (is_directory (*i)) { + filesystem::path m = filesystem::path (*i) / filesystem::path ("metadata"); + if (is_regular_file (m)) { + Film* f = new Film (i->path().string()); + _films.push_back (f); + } + } + } + + for (vector<Film const *>::iterator i = _films.begin(); i != _films.end(); ++i) { + _list.append_text ((*i)->name ()); + } + + _list.set_headers_visible (false); + _list.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &FilmList::selection_changed)); +} + +Gtk::Widget& +FilmList::widget () +{ + return _list; +} + +void +FilmList::selection_changed () +{ + Gtk::ListViewText::SelectionList s = _list.get_selected (); + if (s.empty ()) { + return; + } + + assert (s[0] < int (_films.size ())); + SelectionChanged (_films[s[0]]); +} diff --git a/src/gtk/film_list.h b/src/gtk/film_list.h new file mode 100644 index 000000000..5a4ac3cc1 --- /dev/null +++ b/src/gtk/film_list.h @@ -0,0 +1,41 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <string> +#include <vector> +#include <gtkmm.h> + +class Film; + +class FilmList +{ +public: + FilmList (std::string); + + Gtk::Widget& widget (); + + sigc::signal<void, Film const *> SelectionChanged; + +private: + void selection_changed (); + + std::string _directory; + std::vector<Film const *> _films; + Gtk::ListViewText _list; +}; diff --git a/src/gtk/film_player.cc b/src/gtk/film_player.cc new file mode 100644 index 000000000..63e6e49ee --- /dev/null +++ b/src/gtk/film_player.cc @@ -0,0 +1,310 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "lib/screen.h" +#include "lib/config.h" +#include "lib/player_manager.h" +#include "lib/film.h" +#include "film_player.h" +#include "gtk_util.h" + +using namespace std; +using namespace boost; + +FilmPlayer::FilmPlayer (Film const * f) + : _play ("Play") + , _pause ("Pause") + , _stop ("Stop") + , _ab ("A/B") + , _ignore_position_changed (false) +{ + set_film (f); + + vector<shared_ptr<Screen> > const scr = Config::instance()->screens (); + for (vector<shared_ptr<Screen> >::const_iterator i = scr.begin(); i != scr.end(); ++i) { + _screen.append_text ((*i)->name ()); + } + + if (!scr.empty ()) { + _screen.set_active (0); + } + + _status.set_use_markup (true); + + _position.set_digits (0); + + _main_vbox.set_spacing (12); + + Gtk::HBox* l = manage (new Gtk::HBox); + l->pack_start (_play); + l->pack_start (_pause); + l->pack_start (_stop); + + Gtk::VBox* r = manage (new Gtk::VBox); + r->pack_start (_screen, false, false); + r->pack_start (_ab, false, false); + r->pack_start (*manage (new Gtk::Label ("")), true, true); + + Gtk::HBox* t = manage (new Gtk::HBox); + t->pack_start (*l, true, true); + t->pack_start (*r, false, false); + + _main_vbox.pack_start (*t, true, true); + _main_vbox.pack_start (_position, false, false); + _main_vbox.pack_start (_status, false, false); + + _play.signal_clicked().connect (sigc::mem_fun (*this, &FilmPlayer::play_clicked)); + _pause.signal_clicked().connect (sigc::mem_fun (*this, &FilmPlayer::pause_clicked)); + _stop.signal_clicked().connect (sigc::mem_fun (*this, &FilmPlayer::stop_clicked)); + _position.signal_value_changed().connect (sigc::mem_fun (*this, &FilmPlayer::position_changed)); + _position.signal_format_value().connect (sigc::mem_fun (*this, &FilmPlayer::format_position)); + + set_button_states (); + Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &FilmPlayer::update), true), 1000); + + Config::instance()->Changed.connect (sigc::mem_fun (*this, &FilmPlayer::update_screens)); +} + +void +FilmPlayer::set_film (Film const * f) +{ + _film = f; + + if (_film && _film->length() != 0 && _film->frames_per_second() != 0) { + _position.set_range (0, _film->length() / _film->frames_per_second()); + } + + if (_film) { + _film->Changed.connect (sigc::mem_fun (*this, &FilmPlayer::film_changed)); + } +} + +Gtk::Widget & +FilmPlayer::widget () +{ + return _main_vbox; +} + +void +FilmPlayer::set_button_states () +{ + if (_film == 0) { + _play.set_sensitive (false); + _pause.set_sensitive (false); + _stop.set_sensitive (false); + _screen.set_sensitive (false); + _position.set_sensitive (false); + _ab.set_sensitive (false); + return; + } + + PlayerManager::State s = PlayerManager::instance()->state (); + + switch (s) { + case PlayerManager::QUIESCENT: + _play.set_sensitive (true); + _pause.set_sensitive (false); + _stop.set_sensitive (false); + _screen.set_sensitive (true); + _position.set_sensitive (false); + _ab.set_sensitive (true); + break; + case PlayerManager::PLAYING: + _play.set_sensitive (false); + _pause.set_sensitive (true); + _stop.set_sensitive (true); + _screen.set_sensitive (false); + _position.set_sensitive (true); + _ab.set_sensitive (false); + break; + case PlayerManager::PAUSED: + _play.set_sensitive (true); + _pause.set_sensitive (false); + _stop.set_sensitive (true); + _screen.set_sensitive (false); + _position.set_sensitive (false); + _ab.set_sensitive (false); + break; + } +} + +void +FilmPlayer::play_clicked () +{ + PlayerManager* p = PlayerManager::instance (); + + switch (p->state ()) { + case PlayerManager::QUIESCENT: + _last_play_fs = _film->state_copy (); + if (_ab.get_active ()) { + shared_ptr<FilmState> fs_a = _film->state_copy (); + fs_a->filters.clear (); + /* This is somewhat arbitrary, but hey ho */ + fs_a->scaler = Scaler::from_id ("bicubic"); + p->setup (fs_a, _last_play_fs, screen ()); + } else { + p->setup (_last_play_fs, screen ()); + } + p->pause_or_unpause (); + break; + case PlayerManager::PLAYING: + break; + case PlayerManager::PAUSED: + p->pause_or_unpause (); + break; + } +} + +void +FilmPlayer::pause_clicked () +{ + PlayerManager* p = PlayerManager::instance (); + + switch (p->state ()) { + case PlayerManager::QUIESCENT: + break; + case PlayerManager::PLAYING: + p->pause_or_unpause (); + break; + case PlayerManager::PAUSED: + break; + } +} + +void +FilmPlayer::stop_clicked () +{ + PlayerManager::instance()->stop (); +} + +shared_ptr<Screen> +FilmPlayer::screen () const +{ + vector<shared_ptr<Screen> > const s = Config::instance()->screens (); + if (s.empty ()) { + return shared_ptr<Screen> (); + } + + int const r = _screen.get_active_row_number (); + if (r >= int (s.size ())) { + return s[0]; + } + + return s[r]; +} + +void +FilmPlayer::update () +{ + set_button_states (); + set_status (); +} + +void +FilmPlayer::set_status () +{ + PlayerManager::State s = PlayerManager::instance()->state (); + + stringstream m; + switch (s) { + case PlayerManager::QUIESCENT: + m << "Idle"; + break; + case PlayerManager::PLAYING: + m << "<span foreground=\"red\" weight=\"bold\">PLAYING</span>"; + break; + case PlayerManager::PAUSED: + m << "<b>Paused</b>"; + break; + } + + _ignore_position_changed = true; + + if (s != PlayerManager::QUIESCENT) { + float const p = PlayerManager::instance()->position (); + if (_last_play_fs->frames_per_second != 0 && _last_play_fs->length != 0) { + m << " <i>(" << seconds_to_hms (_last_play_fs->length / _last_play_fs->frames_per_second - p) << " remaining)</i>"; + } + + _position.set_value (p); + } else { + _position.set_value (0); + } + + _ignore_position_changed = false; + + _status.set_markup (m.str ()); +} + +void +FilmPlayer::position_changed () +{ + if (_ignore_position_changed) { + return; + } + + PlayerManager::instance()->set_position (_position.get_value ()); +} + +string +FilmPlayer::format_position (double v) +{ + return seconds_to_hms (v); +} + +void +FilmPlayer::update_screens () +{ + string const c = _screen.get_active_text (); + + _screen.clear (); + + vector<shared_ptr<Screen> > const scr = Config::instance()->screens (); + bool have_last_active_text = false; + for (vector<shared_ptr<Screen> >::const_iterator i = scr.begin(); i != scr.end(); ++i) { + _screen.append_text ((*i)->name ()); + if ((*i)->name() == c) { + have_last_active_text = true; + } + } + + if (have_last_active_text) { + _screen.set_active_text (c); + } else if (!scr.empty ()) { + _screen.set_active (0); + } +} + +void +FilmPlayer::film_changed (Film::Property p) +{ + if (p == Film::CONTENT) { + setup_visibility (); + } +} + +void +FilmPlayer::setup_visibility () +{ + if (!_film) { + return; + } + + widget().property_visible() = (_film->content_type() == VIDEO); +} diff --git a/src/gtk/film_player.h b/src/gtk/film_player.h new file mode 100644 index 000000000..bb60eeb3b --- /dev/null +++ b/src/gtk/film_player.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <gtkmm.h> +#include "lib/film.h" + +class Film; +class Screen; +class FilmState; + +class FilmPlayer +{ +public: + FilmPlayer (Film const * f = 0); + + Gtk::Widget& widget (); + + void set_film (Film const *); + void setup_visibility (); + +private: + void play_clicked (); + void pause_clicked (); + void stop_clicked (); + void position_changed (); + std::string format_position (double); + void film_changed (Film::Property); + + void set_button_states (); + boost::shared_ptr<Screen> screen () const; + void set_status (); + void update (); + void update_screens (); + + Film const * _film; + boost::shared_ptr<const FilmState> _last_play_fs; + + Gtk::VBox _main_vbox; + Gtk::Button _play; + Gtk::Button _pause; + Gtk::Button _stop; + Gtk::Label _status; + Gtk::CheckButton _ab; + Gtk::ComboBoxText _screen; + Gtk::HScale _position; + bool _ignore_position_changed; +}; diff --git a/src/gtk/film_viewer.cc b/src/gtk/film_viewer.cc new file mode 100644 index 000000000..0408d50b8 --- /dev/null +++ b/src/gtk/film_viewer.cc @@ -0,0 +1,224 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/film_viewer.cc + * @brief A GTK widget to view `thumbnails' of a Film. + */ + +#include <iostream> +#include <iomanip> +#include "lib/film.h" +#include "lib/format.h" +#include "lib/util.h" +#include "lib/thumbs_job.h" +#include "lib/job_manager.h" +#include "lib/film_state.h" +#include "lib/options.h" +#include "film_viewer.h" + +using namespace std; +using namespace boost; + +FilmViewer::FilmViewer (Film* f) + : _film (f) + , _update_button ("Update") +{ + _scroller.add (_image); + + Gtk::HBox* controls = manage (new Gtk::HBox); + controls->set_spacing (6); + controls->pack_start (_update_button, false, false); + controls->pack_start (_position_slider); + + _vbox.pack_start (_scroller, true, true); + _vbox.pack_start (*controls, false, false); + _vbox.set_border_width (12); + + _update_button.signal_clicked().connect (sigc::mem_fun (*this, &FilmViewer::update_thumbs)); + + _position_slider.set_digits (0); + _position_slider.signal_format_value().connect (sigc::mem_fun (*this, &FilmViewer::format_position_slider_value)); + _position_slider.signal_value_changed().connect (sigc::mem_fun (*this, &FilmViewer::position_slider_changed)); + + _scroller.signal_size_allocate().connect (sigc::mem_fun (*this, &FilmViewer::scroller_size_allocate)); + + set_film (_film); +} + +void +FilmViewer::load_thumbnail (int n) +{ + if (_film == 0 || _film->num_thumbs() <= n) { + return; + } + + int const left = _film->left_crop (); + int const right = _film->right_crop (); + int const top = _film->top_crop (); + int const bottom = _film->bottom_crop (); + + _pixbuf = Gdk::Pixbuf::create_from_file (_film->thumb_file (n)); + + int const cw = _film->size().width - left - right; + int const ch = _film->size().height - top - bottom; + _cropped_pixbuf = Gdk::Pixbuf::create_subpixbuf (_pixbuf, left, top, cw, ch); + update_scaled_pixbuf (); + _image.set (_scaled_pixbuf); +} + +void +FilmViewer::reload_current_thumbnail () +{ + load_thumbnail (_position_slider.get_value ()); +} + +void +FilmViewer::position_slider_changed () +{ + reload_current_thumbnail (); +} + +string +FilmViewer::format_position_slider_value (double v) const +{ + stringstream s; + + if (_film && int (v) < _film->num_thumbs ()) { + int const f = _film->thumb_frame (int (v)); + s << f << " " << seconds_to_hms (f / _film->frames_per_second ()); + } else { + s << "-"; + } + + return s.str (); +} + +void +FilmViewer::film_changed (Film::Property p) +{ + if (p == Film::LEFT_CROP || p == Film::RIGHT_CROP || p == Film::TOP_CROP || p == Film::BOTTOM_CROP) { + reload_current_thumbnail (); + } else if (p == Film::THUMBS) { + if (_film && _film->num_thumbs() > 1) { + _position_slider.set_range (0, _film->num_thumbs () - 1); + } else { + _image.clear (); + _position_slider.set_range (0, 1); + } + + _position_slider.set_value (0); + reload_current_thumbnail (); + } else if (p == Film::FORMAT) { + reload_current_thumbnail (); + } else if (p == Film::CONTENT) { + setup_visibility (); + update_thumbs (); + } +} + +void +FilmViewer::set_film (Film* f) +{ + _film = f; + + _update_button.set_sensitive (_film != 0); + + if (!_film) { + _image.clear (); + return; + } + + _film->Changed.connect (sigc::mem_fun (*this, &FilmViewer::film_changed)); + + film_changed (Film::THUMBS); +} + +pair<int, int> +FilmViewer::scaled_pixbuf_size () const +{ + if (_film == 0) { + return make_pair (0, 0); + } + + int const cw = _film->size().width - _film->left_crop() - _film->right_crop(); + int const ch = _film->size().height - _film->top_crop() - _film->bottom_crop(); + + float ratio = 1; + if (_film->format()) { + ratio = _film->format()->ratio_as_float() * ch / cw; + } + + Gtk::Allocation const a = _scroller.get_allocation (); + float const zoom = min (float (a.get_width()) / (cw * ratio), float (a.get_height()) / cw); + return make_pair (cw * zoom * ratio, ch * zoom); +} + +void +FilmViewer::update_scaled_pixbuf () +{ + pair<int, int> const s = scaled_pixbuf_size (); + + if (s.first > 0 && s.second > 0 && _cropped_pixbuf) { + _scaled_pixbuf = _cropped_pixbuf->scale_simple (s.first, s.second, Gdk::INTERP_HYPER); + _image.set (_scaled_pixbuf); + } +} + +void +FilmViewer::update_thumbs () +{ + if (!_film) { + return; + } + + _film->update_thumbs_pre_gui (); + + shared_ptr<const FilmState> s = _film->state_copy (); + shared_ptr<Options> o (new Options (s->dir ("thumbs"), ".tiff", "")); + o->out_size = _film->size (); + o->apply_crop = false; + o->decode_audio = false; + o->decode_video_frequency = 128; + + shared_ptr<Job> j (new ThumbsJob (s, o, _film->log ())); + j->Finished.connect (sigc::mem_fun (_film, &Film::update_thumbs_post_gui)); + JobManager::instance()->add (j); +} + +void +FilmViewer::scroller_size_allocate (Gtk::Allocation a) +{ + if (a.get_width() != _last_scroller_allocation.get_width() || a.get_height() != _last_scroller_allocation.get_height()) { + update_scaled_pixbuf (); + } + + _last_scroller_allocation = a; +} + +void +FilmViewer::setup_visibility () +{ + if (!_film) { + return; + } + + ContentType const c = _film->content_type (); + _update_button.property_visible() = (c == VIDEO); + _position_slider.property_visible() = (c == VIDEO); +} diff --git a/src/gtk/film_viewer.h b/src/gtk/film_viewer.h new file mode 100644 index 000000000..e01d6c096 --- /dev/null +++ b/src/gtk/film_viewer.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/film_viewer.h + * @brief A GTK widget to view `thumbnails' of a Film. + */ + +#include <gtkmm.h> +#include "lib/film.h" + +/** @class FilmViewer + * @brief A GTK widget to view `thumbnails' of a Film. + */ +class FilmViewer +{ +public: + FilmViewer (Film *); + + Gtk::Widget& widget () { + return _vbox; + } + + void set_film (Film *); + void setup_visibility (); + +private: + void position_slider_changed (); + void update_thumbs (); + std::string format_position_slider_value (double) const; + void load_thumbnail (int); + void film_changed (Film::Property); + void reload_current_thumbnail (); + void update_scaled_pixbuf (); + std::pair<int, int> scaled_pixbuf_size () const; + void scroller_size_allocate (Gtk::Allocation); + + Film* _film; + Gtk::VBox _vbox; + Gtk::ScrolledWindow _scroller; + Gtk::Image _image; + Glib::RefPtr<Gdk::Pixbuf> _pixbuf; + Glib::RefPtr<Gdk::Pixbuf> _cropped_pixbuf; + Glib::RefPtr<Gdk::Pixbuf> _scaled_pixbuf; + Gtk::HScale _position_slider; + Gtk::Button _update_button; + Gtk::Allocation _last_scroller_allocation; +}; diff --git a/src/gtk/filter_dialog.cc b/src/gtk/filter_dialog.cc new file mode 100644 index 000000000..e52efb68b --- /dev/null +++ b/src/gtk/filter_dialog.cc @@ -0,0 +1,47 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/filter_dialog.cc + * @brief A dialog to select FFmpeg filters. + */ + +#include "lib/film.h" +#include "filter_dialog.h" + +using namespace std; + +FilterDialog::FilterDialog (vector<Filter const *> const & f) + : Gtk::Dialog ("Filters") + , _filters (f) +{ + get_vbox()->pack_start (_filters.widget ()); + + _filters.ActiveChanged.connect (sigc::mem_fun (*this, &FilterDialog::active_changed)); + + add_button ("Close", Gtk::RESPONSE_CLOSE); + + show_all (); +} + + +void +FilterDialog::active_changed () +{ + ActiveChanged (_filters.active ()); +} diff --git a/src/gtk/filter_dialog.h b/src/gtk/filter_dialog.h new file mode 100644 index 000000000..84c6e2966 --- /dev/null +++ b/src/gtk/filter_dialog.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/filter_dialog.h + * @brief A dialog to select FFmpeg filters. + */ + +#include <gtkmm.h> +#include "filter_view.h" + +class Film; + +/** @class FilterDialog + * @brief A dialog to select FFmpeg filters. + */ +class FilterDialog : public Gtk::Dialog +{ +public: + FilterDialog (std::vector<Filter const *> const &); + + sigc::signal1<void, std::vector<Filter const *> > ActiveChanged; + +private: + void active_changed (); + + FilterView _filters; +}; diff --git a/src/gtk/filter_view.cc b/src/gtk/filter_view.cc new file mode 100644 index 000000000..f686c204d --- /dev/null +++ b/src/gtk/filter_view.cc @@ -0,0 +1,72 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/filter_view.cc + * @brief A widget to select FFmpeg filters. + */ + +#include <iostream> +#include "lib/filter.h" +#include "filter_view.h" + +using namespace std; + +FilterView::FilterView (vector<Filter const *> const & active) +{ + _box.set_spacing (4); + + vector<Filter const *> filters = Filter::all (); + + for (vector<Filter const *>::iterator i = filters.begin(); i != filters.end(); ++i) { + Gtk::CheckButton* b = Gtk::manage (new Gtk::CheckButton ((*i)->name())); + bool const a = find (active.begin(), active.end(), *i) != active.end (); + b->set_active (a); + _filters[*i] = a; + b->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &FilterView::filter_toggled), *i)); + _box.pack_start (*b, false, false); + } + + _box.show_all (); +} + +Gtk::Widget & +FilterView::widget () +{ + return _box; +} + +void +FilterView::filter_toggled (Filter const * f) +{ + _filters[f] = !_filters[f]; + ActiveChanged (); +} + +vector<Filter const*> +FilterView::active () const +{ + vector<Filter const *> active; + for (map<Filter const *, bool>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) { + if (i->second) { + active.push_back (i->first); + } + } + + return active; +} diff --git a/src/gtk/filter_view.h b/src/gtk/filter_view.h new file mode 100644 index 000000000..0c96b0e14 --- /dev/null +++ b/src/gtk/filter_view.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/filter_view.h + * @brief A widget to select FFmpeg filters. + */ + +#include <gtkmm.h> +#include <vector> + +class Filter; + +/** @class FilterView + * @brief A widget to select FFmpeg filters. + */ +class FilterView +{ +public: + FilterView (std::vector<Filter const *> const &); + + Gtk::Widget & widget (); + std::vector<Filter const *> active () const; + + sigc::signal0<void> ActiveChanged; + +private: + void filter_toggled (Filter const *); + + Gtk::VBox _box; + std::map<Filter const *, bool> _filters; +}; diff --git a/src/gtk/gpl.cc b/src/gtk/gpl.cc new file mode 100644 index 000000000..b7bcae9e0 --- /dev/null +++ b/src/gtk/gpl.cc @@ -0,0 +1,370 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/gpl.cc + * @brief The GPL. + */ + +const char* gpl = "\n\ +DVD-o-matic comes with NO WARRANTY. It is free software, and you are \n\ +welcome to redistribute it under the terms of the GNU Public License,\n\ +shown below.\n \ +\n\ + GNU GENERAL PUBLIC LICENSE\n\ + Version 2, June 1991\n\ +\n\ + Copyright (C) 1989, 1991 Free Software Foundation, Inc.\n\ + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n\ + Everyone is permitted to copy and distribute verbatim copies\n\ + of this license document, but changing it is not allowed.\n\ +\n\ + Preamble\n\ +\n\ + The licenses for most software are designed to take away your\n\ +freedom to share and change it. By contrast, the GNU General Public\n\ +License is intended to guarantee your freedom to share and change free\n\ +software--to make sure the software is free for all its users. This\n\ +General Public License applies to most of the Free Software\n\ +Foundation's software and to any other program whose authors commit to\n\ +using it. (Some other Free Software Foundation software is covered by\n\ +the GNU Library General Public License instead.) You can apply it to\n\ +your programs, too.\n\ +\n\ + When we speak of free software, we are referring to freedom, not\n\ +price. Our General Public Licenses are designed to make sure that you\n\ +have the freedom to distribute copies of free software (and charge for\n\ +this service if you wish), that you receive source code or can get it\n\ +if you want it, that you can change the software or use pieces of it\n\ +in new free programs; and that you know you can do these things.\n\ +\n\ + To protect your rights, we need to make restrictions that forbid\n\ +anyone to deny you these rights or to ask you to surrender the rights.\n\ +These restrictions translate to certain responsibilities for you if you\n\ +distribute copies of the software, or if you modify it.\n\ +\n\ + For example, if you distribute copies of such a program, whether\n\ +gratis or for a fee, you must give the recipients all the rights that\n\ +you have. You must make sure that they, too, receive or can get the\n\ +source code. And you must show them these terms so they know their\n\ +rights.\n\ +\n\ + We protect your rights with two steps: (1) copyright the software, and\n\ +(2) offer you this license which gives you legal permission to copy,\n\ +distribute and/or modify the software.\n\ +\n\ + Also, for each author's protection and ours, we want to make certain\n\ +that everyone understands that there is no warranty for this free\n\ +software. If the software is modified by someone else and passed on, we\n\ +want its recipients to know that what they have is not the original, so\n\ +that any problems introduced by others will not reflect on the original\n\ +authors' reputations.\n\ +\n\ + Finally, any free program is threatened constantly by software\n\ +patents. We wish to avoid the danger that redistributors of a free\n\ +program will individually obtain patent licenses, in effect making the\n\ +program proprietary. To prevent this, we have made it clear that any\n\ +patent must be licensed for everyone's free use or not licensed at all.\n\ +\n\ + The precise terms and conditions for copying, distribution and\n\ +modification follow.\n\ +\n\ + GNU GENERAL PUBLIC LICENSE\n\ + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\ +\n\ + 0. This License applies to any program or other work which contains\n\ +a notice placed by the copyright holder saying it may be distributed\n\ +under the terms of this General Public License. The \"Program\", below,\n\ +refers to any such program or work, and a \"work based on the Program\"\n\ +means either the Program or any derivative work under copyright law:\n\ +that is to say, a work containing the Program or a portion of it,\n\ +either verbatim or with modifications and/or translated into another\n\ +language. (Hereinafter, translation is included without limitation in\n\ +the term \"modification\".) Each licensee is addressed as \"you\".\n\ +\n\ +Activities other than copying, distribution and modification are not\n\ +covered by this License; they are outside its scope. The act of\n\ +running the Program is not restricted, and the output from the Program\n\ +is covered only if its contents constitute a work based on the\n\ +Program (independent of having been made by running the Program).\n\ +Whether that is true depends on what the Program does.\n\ +\n\ + 1. You may copy and distribute verbatim copies of the Program's\n\ +source code as you receive it, in any medium, provided that you\n\ +conspicuously and appropriately publish on each copy an appropriate\n\ +copyright notice and disclaimer of warranty; keep intact all the\n\ +notices that refer to this License and to the absence of any warranty;\n\ +and give any other recipients of the Program a copy of this License\n\ +along with the Program.\n\ +\n\ +You may charge a fee for the physical act of transferring a copy, and\n\ +you may at your option offer warranty protection in exchange for a fee.\n\ +\n\ + 2. You may modify your copy or copies of the Program or any portion\n\ +of it, thus forming a work based on the Program, and copy and\n\ +distribute such modifications or work under the terms of Section 1\n\ +above, provided that you also meet all of these conditions:\n\ +\n\ + a) You must cause the modified files to carry prominent notices\n\ + stating that you changed the files and the date of any change.\n\ +\n\ + b) You must cause any work that you distribute or publish, that in\n\ + whole or in part contains or is derived from the Program or any\n\ + part thereof, to be licensed as a whole at no charge to all third\n\ + parties under the terms of this License.\n\ +\n\ + c) If the modified program normally reads commands interactively\n\ + when run, you must cause it, when started running for such\n\ + interactive use in the most ordinary way, to print or display an\n\ + announcement including an appropriate copyright notice and a\n\ + notice that there is no warranty (or else, saying that you provide\n\ + a warranty) and that users may redistribute the program under\n\ + these conditions, and telling the user how to view a copy of this\n\ + License. (Exception: if the Program itself is interactive but\n\ + does not normally print such an announcement, your work based on\n\ + the Program is not required to print an announcement.)\n\ +\n\ +These requirements apply to the modified work as a whole. If\n\ +identifiable sections of that work are not derived from the Program,\n\ +and can be reasonably considered independent and separate works in\n\ +themselves, then this License, and its terms, do not apply to those\n\ +sections when you distribute them as separate works. But when you\n\ +distribute the same sections as part of a whole which is a work based\n\ +on the Program, the distribution of the whole must be on the terms of\n\ +this License, whose permissions for other licensees extend to the\n\ +entire whole, and thus to each and every part regardless of who wrote it.\n\ +\n\ +Thus, it is not the intent of this section to claim rights or contest\n\ +your rights to work written entirely by you; rather, the intent is to\n\ +exercise the right to control the distribution of derivative or\n\ +collective works based on the Program.\n\ +\n\ +In addition, mere aggregation of another work not based on the Program\n\ +with the Program (or with a work based on the Program) on a volume of\n\ +a storage or distribution medium does not bring the other work under\n\ +the scope of this License.\n\ +\n\ + 3. You may copy and distribute the Program (or a work based on it,\n\ +under Section 2) in object code or executable form under the terms of\n\ +Sections 1 and 2 above provided that you also do one of the following:\n\ +\n\ + a) Accompany it with the complete corresponding machine-readable\n\ + source code, which must be distributed under the terms of Sections\n\ + 1 and 2 above on a medium customarily used for software interchange; or,\n\ +\n\ + b) Accompany it with a written offer, valid for at least three\n\ + years, to give any third party, for a charge no more than your\n\ + cost of physically performing source distribution, a complete\n\ + machine-readable copy of the corresponding source code, to be\n\ + distributed under the terms of Sections 1 and 2 above on a medium\n\ + customarily used for software interchange; or,\n\ +\n\ + c) Accompany it with the information you received as to the offer\n\ + to distribute corresponding source code. (This alternative is\n\ + allowed only for noncommercial distribution and only if you\n\ + received the program in object code or executable form with such\n\ + an offer, in accord with Subsection b above.)\n\ +\n\ +The source code for a work means the preferred form of the work for\n\ +making modifications to it. For an executable work, complete source\n\ +code means all the source code for all modules it contains, plus any\n\ +associated interface definition files, plus the scripts used to\n\ +control compilation and installation of the executable. However, as a\n\ +special exception, the source code distributed need not include\n\ +anything that is normally distributed (in either source or binary\n\ +form) with the major components (compiler, kernel, and so on) of the\n\ +operating system on which the executable runs, unless that component\n\ +itself accompanies the executable.\n\ +\n\ +If distribution of executable or object code is made by offering\n\ +access to copy from a designated place, then offering equivalent\n\ +access to copy the source code from the same place counts as\n\ +distribution of the source code, even though third parties are not\n\ +compelled to copy the source along with the object code.\n\ +\n\ + 4. You may not copy, modify, sublicense, or distribute the Program\n\ +except as expressly provided under this License. Any attempt\n\ +otherwise to copy, modify, sublicense or distribute the Program is\n\ +void, and will automatically terminate your rights under this License.\n\ +However, parties who have received copies, or rights, from you under\n\ +this License will not have their licenses terminated so long as such\n\ +parties remain in full compliance.\n\ +\n\ + 5. You are not required to accept this License, since you have not\n\ +signed it. However, nothing else grants you permission to modify or\n\ +distribute the Program or its derivative works. These actions are\n\ +prohibited by law if you do not accept this License. Therefore, by\n\ +modifying or distributing the Program (or any work based on the\n\ +Program), you indicate your acceptance of this License to do so, and\n\ +all its terms and conditions for copying, distributing or modifying\n\ +the Program or works based on it.\n\ +\n\ + 6. Each time you redistribute the Program (or any work based on the\n\ +Program), the recipient automatically receives a license from the\n\ +original licensor to copy, distribute or modify the Program subject to\n\ +these terms and conditions. You may not impose any further\n\ +restrictions on the recipients' exercise of the rights granted herein.\n\ +You are not responsible for enforcing compliance by third parties to\n\ +this License.\n\ +\n\ + 7. If, as a consequence of a court judgment or allegation of patent\n\ +infringement or for any other reason (not limited to patent issues),\n\ +conditions are imposed on you (whether by court order, agreement or\n\ +otherwise) that contradict the conditions of this License, they do not\n\ +excuse you from the conditions of this License. If you cannot\n\ +distribute so as to satisfy simultaneously your obligations under this\n\ +License and any other pertinent obligations, then as a consequence you\n\ +may not distribute the Program at all. For example, if a patent\n\ +license would not permit royalty-free redistribution of the Program by\n\ +all those who receive copies directly or indirectly through you, then\n\ +the only way you could satisfy both it and this License would be to\n\ +refrain entirely from distribution of the Program.\n\ +\n\ +If any portion of this section is held invalid or unenforceable under\n\ +any particular circumstance, the balance of the section is intended to\n\ +apply and the section as a whole is intended to apply in other\n\ +circumstances.\n\ +\n\ +It is not the purpose of this section to induce you to infringe any\n\ +patents or other property right claims or to contest validity of any\n\ +such claims; this section has the sole purpose of protecting the\n\ +integrity of the free software distribution system, which is\n\ +implemented by public license practices. Many people have made\n\ +generous contributions to the wide range of software distributed\n\ +through that system in reliance on consistent application of that\n\ +system; it is up to the author/donor to decide if he or she is willing\n\ +to distribute software through any other system and a licensee cannot\n\ +impose that choice.\n\ +\n\ +This section is intended to make thoroughly clear what is believed to\n\ +be a consequence of the rest of this License.\n\ +\n\ + 8. If the distribution and/or use of the Program is restricted in\n\ +certain countries either by patents or by copyrighted interfaces, the\n\ +original copyright holder who places the Program under this License\n\ +may add an explicit geographical distribution limitation excluding\n\ +those countries, so that distribution is permitted only in or among\n\ +countries not thus excluded. In such case, this License incorporates\n\ +the limitation as if written in the body of this License.\n\ +\n\ + 9. The Free Software Foundation may publish revised and/or new versions\n\ +of the General Public License from time to time. Such new versions will\n\ +be similar in spirit to the present version, but may differ in detail to\n\ +address new problems or concerns.\n\ +\n\ +Each version is given a distinguishing version number. If the Program\n\ +specifies a version number of this License which applies to it and \"any\n\ +later version\", you have the option of following the terms and conditions\n\ +either of that version or of any later version published by the Free\n\ +Software Foundation. If the Program does not specify a version number of\n\ +this License, you may choose any version ever published by the Free Software\n\ +Foundation.\n\ +\n\ + 10. If you wish to incorporate parts of the Program into other free\n\ +programs whose distribution conditions are different, write to the author\n\ +to ask for permission. For software which is copyrighted by the Free\n\ +Software Foundation, write to the Free Software Foundation; we sometimes\n\ +make exceptions for this. Our decision will be guided by the two goals\n\ +of preserving the free status of all derivatives of our free software and\n\ +of promoting the sharing and reuse of software generally.\n\ +\n\ + NO WARRANTY\n\ +\n\ + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\n\ +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN\n\ +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\n\ +PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\n\ +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\n\ +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS\n\ +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE\n\ +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\n\ +REPAIR OR CORRECTION.\n\ +\n\ + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\n\ +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\n\ +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\n\ +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\n\ +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\n\ +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\n\ +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\n\ +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\n\ +POSSIBILITY OF SUCH DAMAGES.\n\ +\n\ + END OF TERMS AND CONDITIONS\n\ +\n\ + How to Apply These Terms to Your New Programs\n\ +\n\ + If you develop a new program, and you want it to be of the greatest\n\ +possible use to the public, the best way to achieve this is to make it\n\ +free software which everyone can redistribute and change under these terms.\n\ +\n\ + To do so, attach the following notices to the program. It is safest\n\ +to attach them to the start of each source file to most effectively\n\ +convey the exclusion of warranty; and each file should have at least\n\ +the \"copyright\" line and a pointer to where the full notice is found.\n\ +\n\ + <one line to give the program's name and a brief idea of what it does.>\n\ + Copyright (C) <year> <name of author>\n\ +\n\ + This program is free software; you can redistribute it and/or modify\n\ + it under the terms of the GNU General Public License as published by\n\ + the Free Software Foundation; either version 2 of the License, or\n\ + (at your option) any later version.\n\ +\n\ + This program is distributed in the hope that it will be useful,\n\ + but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ + GNU General Public License for more details.\n\ +\n\ + You should have received a copy of the GNU General Public License\n\ + along with this program; if not, write to the Free Software\n\ + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n\ +\n\ +\n\ +Also add information on how to contact you by electronic and paper mail.\n\ +\n\ +If the program is interactive, make it output a short notice like this\n\ +when it starts in an interactive mode:\n\ +\n\ + Gnomovision version 69, Copyright (C) year name of author\n\ + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n\ + This is free software, and you are welcome to redistribute it\n\ + under certain conditions; type `show c' for details.\n\ +\n\ +The hypothetical commands `show w' and `show c' should show the appropriate\n\ +parts of the General Public License. Of course, the commands you use may\n\ +be called something other than `show w' and `show c'; they could even be\n\ +mouse-clicks or menu items--whatever suits your program.\n\ +\n\ +You should also get your employer (if you work as a programmer) or your\n\ +school, if any, to sign a \"copyright disclaimer\" for the program, if\n\ +necessary. Here is a sample; alter the names:\n\ +\n\ + Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n\ + `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\ +\n\ + <signature of Ty Coon>, 1 April 1989\n\ + Ty Coon, President of Vice\n\ +\n\ +This General Public License does not permit incorporating your program into\n\ +proprietary programs. If your program is a subroutine library, you may\n\ +consider it more useful to permit linking proprietary applications with the\n\ +library. If this is what you want to do, use the GNU Library General\n\ +Public License instead of this License.\n\ +"; + diff --git a/src/gtk/gpl.h b/src/gtk/gpl.h new file mode 100644 index 000000000..c9c4ffe46 --- /dev/null +++ b/src/gtk/gpl.h @@ -0,0 +1,24 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/gpl.h + * @brief The GPL. + */ + +extern const char * gpl; diff --git a/src/gtk/gtk_util.cc b/src/gtk/gtk_util.cc new file mode 100644 index 000000000..41f8cb5b5 --- /dev/null +++ b/src/gtk/gtk_util.cc @@ -0,0 +1,45 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/gtk/util.cc + * @brief Some utility functions. + */ + +#include <gtkmm.h> + +using namespace std; + +/** @param t Label text. + * @return GTK label containing t, left-aligned (passed through Gtk::manage) + */ +Gtk::Label & +left_aligned_label (string t) +{ + Gtk::Label* l = Gtk::manage (new Gtk::Label (t)); + l->set_alignment (0, 0.5); + return *l; +} + +void +error_dialog (string m) +{ + Gtk::MessageDialog d (m, false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + d.set_title ("DVD-o-matic"); + d.run (); +} diff --git a/src/gtk/gtk_util.h b/src/gtk/gtk_util.h new file mode 100644 index 000000000..518842872 --- /dev/null +++ b/src/gtk/gtk_util.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <gtkmm.h> + +/** @file src/gtk/util.h + * @brief Some utility functions. + */ + +extern void error_dialog (std::string); +extern Gtk::Label & left_aligned_label (std::string); diff --git a/src/gtk/job_manager_view.cc b/src/gtk/job_manager_view.cc new file mode 100644 index 000000000..60c13990d --- /dev/null +++ b/src/gtk/job_manager_view.cc @@ -0,0 +1,134 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/job_manager_view.cc + * @brief Class generating a GTK widget to show the progress of jobs. + */ + +#include "lib/job_manager.h" +#include "lib/job.h" +#include "lib/util.h" +#include "lib/exceptions.h" +#include "job_manager_view.h" +#include "gtk_util.h" + +using namespace std; +using namespace boost; + +/** Must be called in the GUI thread */ +JobManagerView::JobManagerView () +{ + _scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS); + + _store = Gtk::TreeStore::create (_columns); + _view.set_model (_store); + _view.append_column ("Name", _columns.name); + + Gtk::CellRendererProgress* r = Gtk::manage (new Gtk::CellRendererProgress ()); + int const n = _view.append_column ("Progress", *r); + Gtk::TreeViewColumn* c = _view.get_column (n - 1); + c->add_attribute (r->property_value(), _columns.progress); + c->add_attribute (r->property_pulse(), _columns.progress_unknown); + c->add_attribute (r->property_text(), _columns.text); + + _scroller.add (_view); + _scroller.set_size_request (-1, 150); + + update (); +} + +/** Update the view by examining the state of each jobs. + * Must be called in the GUI thread. + */ +void +JobManagerView::update () +{ + list<shared_ptr<Job> > jobs = JobManager::instance()->get (); + + for (list<shared_ptr<Job> >::iterator i = jobs.begin(); i != jobs.end(); ++i) { + Gtk::ListStore::iterator j = _store->children().begin(); + while (j != _store->children().end()) { + Gtk::TreeRow r = *j; + shared_ptr<Job> job = r[_columns.job]; + if (job == *i) { + break; + } + ++j; + } + + Gtk::TreeRow r; + if (j == _store->children().end ()) { + j = _store->append (); + r = *j; + r[_columns.name] = (*i)->name (); + r[_columns.job] = *i; + r[_columns.progress_unknown] = -1; + r[_columns.informed_of_finish] = false; + } else { + r = *j; + } + + bool inform_of_finish = false; + string const st = (*i)->status (); + + if (!(*i)->finished ()) { + float const p = (*i)->overall_progress (); + if (p >= 0) { + r[_columns.text] = st; + r[_columns.progress] = p * 100; + } else { + r[_columns.progress_unknown] = r[_columns.progress_unknown] + 1; + } + } + + /* Hack to work around our lack of cross-thread + signalling; we tell the job to emit_finished() + from here (the GUI thread). + */ + + if ((*i)->finished_ok ()) { + bool i = r[_columns.informed_of_finish]; + if (!i) { + r[_columns.progress_unknown] = -1; + r[_columns.progress] = 100; + r[_columns.text] = st; + inform_of_finish = true; + } + } else if ((*i)->finished_in_error ()) { + bool i = r[_columns.informed_of_finish]; + if (!i) { + r[_columns.progress_unknown] = -1; + r[_columns.progress] = 100; + r[_columns.text] = st; + inform_of_finish = true; + } + } + + if (inform_of_finish) { + try { + (*i)->emit_finished (); + } catch (OpenFileError& e) { + stringstream s; + s << "Error: " << e.what(); + error_dialog (s.str ()); + } + r[_columns.informed_of_finish] = true; + } + } +} diff --git a/src/gtk/job_manager_view.h b/src/gtk/job_manager_view.h new file mode 100644 index 000000000..c88a1ce9a --- /dev/null +++ b/src/gtk/job_manager_view.h @@ -0,0 +1,83 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +/** @file src/job_manager_view.h + * @brief Class generating a GTK widget to show the progress of jobs. + */ + +#include <string> +#include <boost/shared_ptr.hpp> +#include <gtkmm.h> + +class Job; + +/** @class JobManagerView + * @brief Class generating a GTK widget to show the progress of jobs. + */ +class JobManagerView +{ +public: + JobManagerView (); + + /** @return Our main widget, which contains everything else */ + Gtk::Widget& widget () { + return _scroller; + } + + void update (); + +private: + /** Scroller for all our contents */ + Gtk::ScrolledWindow _scroller; + /** View for the jobs */ + Gtk::TreeView _view; + /** Store for the jobs */ + Glib::RefPtr<Gtk::TreeStore> _store; + + /** The TreeModelColumnRecord for the store */ + class StoreColumns : public Gtk::TreeModelColumnRecord + { + public: + StoreColumns () + { + add (name); + add (job); + add (progress); + add (progress_unknown); + add (text); + add (informed_of_finish); + } + + /** Job name */ + Gtk::TreeModelColumn<std::string> name; + /** Job */ + Gtk::TreeModelColumn<boost::shared_ptr<Job> > job; + /** Progress */ + Gtk::TreeModelColumn<float> progress; + /** An increasing integer number if the progress is unknown */ + Gtk::TreeModelColumn<int> progress_unknown; + /** Text to write into the progress bar */ + Gtk::TreeModelColumn<std::string> text; + /** true if the job has been informed of its finish */ + Gtk::TreeModelColumn<bool> informed_of_finish; + }; + + /** The columns for the store */ + StoreColumns _columns; +}; diff --git a/src/gtk/job_wrapper.cc b/src/gtk/job_wrapper.cc new file mode 100644 index 000000000..be214b0ac --- /dev/null +++ b/src/gtk/job_wrapper.cc @@ -0,0 +1,50 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include <sstream> +#include "lib/film.h" +#include "lib/exceptions.h" +#include "job_wrapper.h" +#include "gtk_util.h" + +using namespace std; + +void +JobWrapper::make_dcp (Film* film, bool transcode) +{ + if (!film) { + return; + } + + try { + film->make_dcp (transcode); + } catch (BadSettingError& e) { + stringstream s; + if (e.setting() == "dcp_long_name") { + s << "Could not make DCP: long name is invalid (" << e.what() << ")"; + } else { + s << "Bad setting for " << e.setting() << "(" << e.what() << ")"; + } + error_dialog (s.str ()); + } catch (std::exception& e) { + stringstream s; + s << "Could not make DCP: " << e.what () << "."; + error_dialog (s.str ()); + } +} diff --git a/src/gtk/job_wrapper.h b/src/gtk/job_wrapper.h new file mode 100644 index 000000000..b8760f6e5 --- /dev/null +++ b/src/gtk/job_wrapper.h @@ -0,0 +1,27 @@ +/* + Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +class Film; + +namespace JobWrapper +{ + +void make_dcp (Film *, bool); + +} diff --git a/src/gtk/wscript b/src/gtk/wscript new file mode 100644 index 000000000..c68efea90 --- /dev/null +++ b/src/gtk/wscript @@ -0,0 +1,22 @@ +def build(bld): + obj = bld(features = 'cxx cxxshlib') + obj.name = 'libdvdomatic-gtk' + obj.includes = [ '..' ] + obj.export_includes = ['.'] + obj.uselib = 'GLIB GTKMM CAIROMM' + obj.source = """ + alignment.cc + config_dialog.cc + dcp_range_dialog.cc + film_editor.cc + film_list.cc + film_player.cc + film_viewer.cc + filter_dialog.cc + filter_view.cc + gpl.cc + job_manager_view.cc + gtk_util.cc + job_wrapper.cc + """ + obj.target = 'dvdomatic-gtk' |
