/* * subtitle editor * * http://kitone.free.fr/subtitleeditor/ * * Copyright @ 2005-2006, kitone * * Contact: kitone at free dot fr * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * See gpl.txt for more information regarding the GNU General Public License. * * * \file * \brief * \author kitone (kitone at free dot fr) */ #include #include "SubtitleView.h" #include "utility.h" #include #include #include #include #include #include #include #include "Config.h" #include "ISubtitleEditor.h" #include "debug.h" /* * une CellEditable deriver de TextView qui permet d'avoir plusieur ligne * contrairement a GtkEntry (par defaut) */ class TextViewCell : public Gtk::TextView, public Gtk::CellEditable { public: /* * */ TextViewCell(const Glib::ustring &path) : Glib::ObjectBase(typeid(TextViewCell)), //Gtk::TextView(), Gtk::CellEditable(), m_path(path) { SE::getInstance()->setStatusbar(_("Use Ctrl+Return for line-break")); } /* * */ void do_editing_done() { remove_widget(); SE::getInstance()->setStatusbar(""); } /* * */ Glib::ustring get_text() { Glib::RefPtr buffer = get_buffer(); Gtk::TextBuffer::iterator start, end; buffer->get_bounds(start,end); return buffer->get_text(start,end); } /* * */ void set_text(const Glib::ustring &text) { get_buffer()->set_text(text); } /* * */ bool on_key_press_event(GdkEventKey* event) { if(event->keyval == GDK_Escape) { editing_done(); remove_widget(); return true; } else if( ( event->keyval == GDK_Return || event->keyval == GDK_KP_Enter || event->keyval == GDK_ISO_Enter || event->keyval == GDK_3270_Enter ) && !(event->state & GDK_CONTROL_MASK)) { m_signal_edited(m_path, get_text()); editing_done(); remove_widget(); return true; } return Gtk::TextView::on_key_press_event(event); } /* * */ typedef sigc::signal SignalEdited; /* * */ SignalEdited& signal_edited() { return m_signal_edited; } protected: Glib::ustring m_path; SignalEdited m_signal_edited; }; /* * derive simplement de CellRendererText pour pouvoir utiliser * TextViewCell a l'edition. */ class CellRendererTextMultiline : public Gtk::CellRendererText { public: /* * */ CellRendererTextMultiline() : Glib::ObjectBase(typeid(CellRendererTextMultiline)), Gtk::CellRendererText(), m_textViewCell(NULL) { } /* * */ virtual Gtk::CellEditable* start_editing_vfunc( GdkEvent* event, Gtk::Widget &widget, const Glib::ustring &path, const Gdk::Rectangle& background_area, const Gdk::Rectangle& cell_area, Gtk::CellRendererState flags) { if(!property_editable()) return 0; m_textViewCell = manage(new TextViewCell(path)); m_textViewCell->set_wrap_mode(Gtk::WRAP_NONE); m_textViewCell->set_text(property_text()); m_textViewCell->set_size_request(cell_area.get_width(), cell_area.get_height()); m_textViewCell->signal_edited().connect( sigc::mem_fun(*this, &CellRendererTextMultiline::edited)); m_textViewCell->grab_focus(); m_textViewCell->show(); return m_textViewCell; } protected: TextViewCell* m_textViewCell; }; /* * */ SubtitleView::SubtitleView(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :Gtk::TreeView(cobject) { se_debug(SE_DEBUG_VIEW); m_refDocument = SE::getInstance()->getDocument(); // cela est connecter au dernier moment dans SubtitleEditor::show() // c'est plus rapide ainsi ... // //set_model(m_refDocument->m_subtitleModel); m_subtitleModel = m_refDocument->m_subtitleModel; m_styleModel = m_refDocument->m_styleModel; createColumnNum(); createColumnLayer(); createColumnStart(); createColumnEnd(); createColumnDuration(); createColumnStyle(); createColumnName(); createColumnMarginL(); createColumnMarginR(); createColumnMarginV(); createColumnEffect(); createColumnText(); createColumnCharactersPerLine(); createColumnTranslation(); set_rules_hint(true); set_enable_search(false); set_view_type("ssa"); // config loadCfg(); // je le place ici parceque dans le destructeur il y a un segfault! saveCfg(); get_selection()->signal_changed().connect( sigc::mem_fun(*this, &SubtitleView::on_selection_changed)); get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); } SubtitleView::~SubtitleView() { //saveCfg(); } /* * */ void SubtitleView::loadCfg() { se_debug(SE_DEBUG_VIEW); bool state = false; Config &cfg = Config::getInstance(); #define SET(name) if(cfg.get_value_bool("setup-view", "show-column-"name, state)) set_column_visible(name, state); SET("number"); SET("layer"); SET("start"); SET("end"); SET("duration"); SET("style"); SET("name"); SET("margin-l"); SET("margin-r"); SET("margin-v"); SET("effect"); SET("text"); SET("translation"); SET("characters-per-line"); #undef SET } /* * */ void SubtitleView::saveCfg() { se_debug(SE_DEBUG_VIEW); Config &cfg = Config::getInstance(); #define SET(name) cfg.set_value_bool("setup-view", "show-column-"name, get_column_visible(name)); SET("number"); SET("layer"); SET("start"); SET("end"); SET("duration"); SET("style"); SET("name"); SET("margin-l"); SET("margin-r"); SET("margin-v"); SET("effect"); SET("text"); SET("translation"); SET("characters-per-line"); #undef SET } /* * */ void SubtitleView::set_tooltips(Gtk::TreeViewColumn *column, const Glib::ustring &text) { se_debug_message(SE_DEBUG_VIEW, "[%s]=%s", column->get_title().c_str(), text.c_str()); /* Gtk::EventBox *event = manage(new Gtk::EventBox); Gtk::Label* label = manage(new Gtk::Label(column->get_title())); event->add(*label); Gtk::Tooltips *tooltips = manage(new Gtk::Tooltips); tooltips->set_tip(*event, text); tooltips->enable(); column->set_widget(*event); event->show_all(); */ } /* * create columns */ void SubtitleView::createColumnNum() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("num"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.num); append_column(*column); //append_column_numeric_editable("num", m_column.num, "%d"); renderer->property_editable() = false; renderer->property_yalign() = 0; m_columns["number"] = column; set_tooltips(column, _("This number column")); } /* * */ void SubtitleView::createColumnLayer() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("layer"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.layer); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_layer)); append_column(*column); m_columns["layer"] = column; set_tooltips(column, _("Layer number.")); } /* * */ void SubtitleView::createColumnStart() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("start"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.start); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_start)); append_column(*column); m_columns["start"] = column; set_tooltips(column, _("This time is the time when a subtitle appears on the screen.")); } /* * */ void SubtitleView::createColumnEnd() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("end"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.end); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_end)); append_column(*column); m_columns["end"] = column; set_tooltips(column, _("This time is the time when a subtitle disappears from the screen.")); } /* * */ void SubtitleView::createColumnDuration() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("duration"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.duration); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_duration)); append_column(*column); m_columns["duration"] = column; #warning "FIXME tooltip duration" set_tooltips(column, _("This time is the duration...")); } /* * */ void SubtitleView::createColumnStyle() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererCombo* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("style"))); renderer = manage(new Gtk::CellRendererCombo); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.style); renderer->property_model() = m_styleModel; renderer->property_text_column() = 0; renderer->property_editable() = true; renderer->property_has_entry() = false; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_style)); append_column(*column); m_columns["style"] = column; } /* * */ void SubtitleView::createColumnName() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("name"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.name); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_name)); append_column(*column); m_columns["name"] = column; } /* * */ void SubtitleView::createColumnText() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; //Gtk::CellRendererText* renderer = NULL; CellRendererTextMultiline* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("text"))); //renderer = manage(new Gtk::CellRendererText); renderer = manage(new CellRendererTextMultiline);//manage(new Gtk::CellRendererText); column->pack_start(*renderer, true);//false); column->add_attribute(renderer->property_text(), m_column.text); append_column(*column); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_text)); //renderer->signal_editing_started().connect( // sigc::mem_fun(*this, &SubtitleView::editing_start)); column->set_resizable(true); // column->set_expand(true); m_columns["text"] = column; } /* * */ void SubtitleView::createColumnTranslation() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("translation"))); renderer = manage(new CellRendererTextMultiline); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.translation); append_column(*column); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_translation)); column->set_resizable(true); // column->set_expand(true); m_columns["translation"] = column; } /* * */ void SubtitleView::createColumnEffect() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("effect"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.effect); append_column(*column); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_effect)); column->set_resizable(true); // column->set_expand(true); m_columns["effect"] = column; } /* * */ void SubtitleView::createColumnMarginR() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("R"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.marginR); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_margin_r)); append_column(*column); m_columns["margin-r"] = column; } /* * */ void SubtitleView::createColumnMarginL() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("L"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.marginL); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_margin_l)); append_column(*column); m_columns["margin-l"] = column; } /* * */ void SubtitleView::createColumnMarginV() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("V"))); renderer = manage(new Gtk::CellRendererText); column->pack_start(*renderer, false); column->add_attribute(renderer->property_text(), m_column.marginV); renderer->property_editable() = true; renderer->property_yalign() = 0; renderer->signal_edited().connect( sigc::mem_fun(*this, &SubtitleView::on_edited_margin_v)); append_column(*column); m_columns["margin-v"] = column; } /* * */ void SubtitleView::createColumnCharactersPerLine() { se_debug(SE_DEBUG_VIEW); Gtk::TreeViewColumn* column = NULL; Gtk::CellRendererText* renderer = NULL; column = manage(new Gtk::TreeViewColumn(_("cpl"))); renderer = manage(new Gtk::CellRendererText); column->pack_end(*renderer, false); column->add_attribute(renderer->property_text(), m_column.characters_per_line); //renderer->property_editable() = true; renderer->property_yalign() = 0; //renderer->signal_edited().connect( // sigc::mem_fun(*this, &SubtitleView::on_edited_)); append_column(*column); m_columns["characters-per-line"] = column; set_tooltips(column, _("This column represents the number of characters per line.")); } /* * retourne l'item select ou NULL */ Gtk::TreeIter SubtitleView::getSelected() { se_debug(SE_DEBUG_VIEW); Glib::RefPtr selection = get_selection(); std::vector rows = selection->get_selected_rows(); if(rows.size() > 0) { return selection->get_model()->get_iter(rows[0]); } Gtk::TreeIter null; return null; } /* * */ void SubtitleView::on_selection_changed() { se_debug(SE_DEBUG_VIEW); Signal::getInstance().subtitle_view_selection_changed(getSelected()); } /* * */ void SubtitleView::on_edited_layer( const Glib::ustring &path, const Glib::ustring &value) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { unsigned int val = 0; if(from_string(value, val)) subtitle.set_layer(value); } } /* * callback utiliser pour modifier le temps directement depuis la list (treeview) */ void SubtitleView::on_edited_start( const Glib::ustring &path, const Glib::ustring &newtext) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { if(SubtitleTime::validate(newtext)) { subtitle.set_start(newtext); Signal::getInstance().subtitle_time_changed(subtitle.get_iter()); } else { std::cerr << "on_edited_start::invalide> " << newtext << std::endl; } } } /* * callback utiliser pour modifier le temps directement depuis la list (treeview) */ void SubtitleView::on_edited_end( const Glib::ustring &path, const Glib::ustring &newtext) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { if(SubtitleTime::validate(newtext)) { subtitle.set_end(newtext); Signal::getInstance().subtitle_time_changed(subtitle.get_iter()); } else { std::cerr << "on_edited_end::invalide> " << newtext << std::endl; } } } /* * callback utiliser pour modifier le temps directement depuis la list (treeview) */ void SubtitleView::on_edited_duration( const Glib::ustring &path, const Glib::ustring &newtext) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { if(SubtitleTime::validate(newtext)) { subtitle.set_duration(newtext); Signal::getInstance().subtitle_time_changed(subtitle.get_iter()); } else { std::cerr << "on_edited_duration::invalide> " << newtext << std::endl; } } } /* * callback utiliser pour modifier le texte */ void SubtitleView::on_edited_text( const Glib::ustring &path, const Glib::ustring &newtext) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { subtitle.set_text(newtext); } } /* * callback utiliser pour modifier le texte */ void SubtitleView::on_edited_translation( const Glib::ustring &path, const Glib::ustring &newtext) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { subtitle.set_translation(newtext); } } /* */ void SubtitleView::on_edited_effect( const Glib::ustring &path, const Glib::ustring &newtext) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newtext.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { subtitle.set_effect(newtext); } } /* * callback utiliser pour modifier le style a partir d'un menu */ void SubtitleView::on_edited_style( const Glib::ustring &path, const Glib::ustring &newstyle) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newstyle.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { subtitle.set_style(newstyle); } } /* * callback utiliser pour modifier le nom */ void SubtitleView::on_edited_name( const Glib::ustring &path, const Glib::ustring &newname) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), newname.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { subtitle.set_name(newname); } } /* * */ void SubtitleView::on_edited_margin_l( const Glib::ustring &path, const Glib::ustring &value) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { unsigned int val = 0; if(from_string(value, val)) subtitle.set_margin_l(value); } } /* * */ void SubtitleView::on_edited_margin_r( const Glib::ustring &path, const Glib::ustring &value) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { unsigned int val = 0; if(from_string(value, val)) subtitle.set_margin_r(value); } } /* * */ void SubtitleView::on_edited_margin_v( const Glib::ustring &path, const Glib::ustring &value) { se_debug_message(SE_DEBUG_VIEW, "%s %s", path.c_str(), value.c_str()); SubtitleModifier subtitle(m_subtitleModel->get_iter(path)); if(subtitle) { unsigned int val = 0; if(from_string(value, val)) subtitle.set_margin_v(value); } } /* * */ void SubtitleView::select_and_set_cursor(const Gtk::TreeIter &iter) { se_debug(SE_DEBUG_VIEW); get_selection()->select(iter); set_cursor(m_subtitleModel->get_path(iter), *m_columns["text"], false); } /* * passe d'une vue simple pour le format SRT * a la vue total (SSA) * afficher certaine colonne ou non * "srt" ou "ssa" */ void SubtitleView::set_view_type(const Glib::ustring &type) { se_debug_message(SE_DEBUG_VIEW, "%s", type.c_str()); if(type == "srt") { m_columns["number"]->set_visible(true); m_columns["layer"]->set_visible(false); m_columns["start"]->set_visible(true); m_columns["end"]->set_visible(true); m_columns["duration"]->set_visible(true); m_columns["style"]->set_visible(false); m_columns["name"]->set_visible(false); m_columns["margin-l"]->set_visible(false); m_columns["margin-r"]->set_visible(false); m_columns["margin-v"]->set_visible(false); m_columns["text"]->set_visible(true); // m_columns["effect"]->set_visible(false); m_columns["translation"]->set_visible(false); } else if(type == "ssa") { m_columns["number"]->set_visible(true); m_columns["layer"]->set_visible(true); m_columns["start"]->set_visible(true); m_columns["end"]->set_visible(true); m_columns["duration"]->set_visible(true); m_columns["style"]->set_visible(true); m_columns["name"]->set_visible(true); m_columns["margin-l"]->set_visible(false); m_columns["margin-r"]->set_visible(false); m_columns["margin-v"]->set_visible(false); m_columns["text"]->set_visible(true); // m_columns["effect"]->set_visible(false); m_columns["translation"]->set_visible(false); } else std::cerr << "SubtitleView::set_view_type type no found :" << type << std::endl; } /* * */ void SubtitleView::set_column_visible(const Glib::ustring& name, bool state) { se_debug_message(SE_DEBUG_VIEW, "%s=%s", name.c_str(), state ? "true" : "false"); std::map::iterator it = m_columns.find(name); if(it == m_columns.end()) { std::cerr << "SubtitleView::set_column_visible : " << name << " not found" << std::endl; } else { it->second->set_visible(state); Config &cfg = Config::getInstance(); cfg.set_value_bool("setup-view", "show-column-"+name, state); } } /* * */ bool SubtitleView::get_column_visible(const Glib::ustring &name) { std::map::iterator it = m_columns.find(name); if(it == m_columns.end()) { se_debug_message(SE_DEBUG_VIEW, "<%s> not found.", name.c_str()); return false; } se_debug_message(SE_DEBUG_VIEW, "<%s> = %s", name.c_str(), it->second->get_visible() ? "true" : "false"); return it->second->get_visible(); }