/* * 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 "DialogFind.h" #include "ISubtitleEditor.h" #include "utility.h" #include "Config.h" #include "RegEx.h" /* * */ DialogFindAndReplace::DialogFindAndReplace(BaseObjectType* cobject, const Glib::RefPtr& refGlade) :Gtk::Dialog(cobject) { se_debug(SE_DEBUG_SEARCH); refGlade->get_widget("textview", m_textview); refGlade->get_widget("entry-pattern", m_entryPattern); refGlade->get_widget("entry-replace-with", m_entryReplaceWith); refGlade->get_widget("check-ignore-case", m_checkIgnoreCase); refGlade->get_widget("check-used-regular-expression", m_checkUsedRegularExpression); refGlade->get_widget("label-information", m_labelInformation); refGlade->get_widget("button-find", m_buttonFind); refGlade->get_widget("button-replace", m_buttonReplace); refGlade->get_widget("button-replace-all", m_buttonReplaceAll); // signaux m_buttonFind->signal_clicked().connect( sigc::mem_fun(*this, &DialogFindAndReplace::on_button_find)); m_buttonReplace->signal_clicked().connect( sigc::mem_fun(*this, &DialogFindAndReplace::on_button_replace)); m_buttonReplaceAll->signal_clicked().connect( sigc::mem_fun(*this, &DialogFindAndReplace::on_button_replace_all)); m_textview->set_editable(false); // default config Config &cfg = Config::getInstance(); Glib::ustring text; bool state; if(cfg.get_value_string("dialog-find-and-replace", "find", text)) m_entryPattern->set_text(text); if(cfg.get_value_string("dialog-find-and-replace", "replace-with", text)) m_entryReplaceWith->set_text(text); if(cfg.get_value_bool("dialog-find-and-replace", "ignore-case", state)) m_checkIgnoreCase->set_active(state); if(cfg.get_value_bool("dialog-find-and-replace", "used-regular-expression", state)) m_checkUsedRegularExpression->set_active(state); m_entryPattern->grab_focus(); set_default_response(Gtk::RESPONSE_CLOSE); } /* * */ DialogFindAndReplace::~DialogFindAndReplace() { se_debug(SE_DEBUG_SEARCH); Config &cfg = Config::getInstance(); cfg.set_value_string("dialog-find-and-replace", "find", m_entryPattern->get_text()); cfg.set_value_string("dialog-find-and-replace", "replace-with", m_entryReplaceWith->get_text()); cfg.set_value_bool("dialog-find-and-replace", "ignore-case", m_checkIgnoreCase->get_active()); cfg.set_value_bool("dialog-find-and-replace", "used-regular-expression", m_checkUsedRegularExpression->get_active()); } /* * */ void DialogFindAndReplace::execute() { se_debug(SE_DEBUG_SEARCH); Document *doc = SE::getInstance()->getDocument(); g_return_if_fail(doc); show(); m_eod = false; m_current_text = ""; m_current_start = 0; m_current_len = 0; m_current_iter = SE::getInstance()->getDocument()->get_subtitle_model()->getFirst(); while(run() != Gtk::RESPONSE_CLOSE) { } hide(); } /* * text : la ligne * pattern : la recherche */ bool DialogFindAndReplace::find(const Glib::ustring &text, const Glib::ustring &pattern, Glib::ustring::size_type &start, Glib::ustring::size_type &len) { se_debug_message(SE_DEBUG_SEARCH, "text=<%s> pattern=<%s>", text.c_str(), pattern.c_str()); if(pattern.size() == 0) return false; if(m_checkUsedRegularExpression->get_active()) { se_debug_message(SE_DEBUG_SEARCH, "Used regular expression"); // options... int flag = 0; if(m_checkIgnoreCase->get_active()) flag |= RegEx::CASELESS; // création de l'expression RegEx ex(pattern, flag); return ex.exec(text, start, len); } else { if(m_checkIgnoreCase->get_active()) { se_debug_message(SE_DEBUG_SEARCH, "Ignore case"); try { Glib::ustring exp_lc = pattern.lowercase(); Glib::ustring text_lc = text.lowercase(); Glib::ustring::size_type res = text_lc.find(exp_lc); if(res != Glib::ustring::npos) { start = res; len = pattern.size(); se_debug_message(SE_DEBUG_SEARCH, "Found : start=%i len=%i", start, len); return true; } } catch(const std::exception &ex) { std::cerr << ex.what() << std::endl; } } else { se_debug_message(SE_DEBUG_SEARCH, "Don't ignore case"); try { Glib::ustring::size_type res = text.find(pattern); if(res != Glib::ustring::npos) { start = res; len = pattern.size(); return true; } } catch(const std::exception &ex) { std::cerr << ex.what() << std::endl; } } } return false; } /* * */ bool DialogFindAndReplace::find_and_replace(Glib::ustring &text, const Glib::ustring &pattern, const Glib::ustring &replace) { se_debug_message(SE_DEBUG_SEARCH, "text=<%s> pattern=<%s> replace=<%s>", text.c_str(), pattern.c_str(), replace.c_str()); if(pattern.size() == 0) return false; bool found = false; Glib::ustring::size_type pos=0; Glib::ustring::size_type start, len; try { // TODO: tant que dans la boucle on recherche (si plusieurs fois dans la même ligne) while(find(text.substr(pos, text.size() - pos), pattern, start, len)) { if(start != Glib::ustring::npos && len != Glib::ustring::npos) { text.replace(start+pos, len, replace); found = true; pos = start + len; } if(pos >= text.size()) break; } } catch(const std::exception &ex) { std::cerr << ex.what() << std::endl; return false; } return found; } /* * */ void DialogFindAndReplace::findNext() { se_debug(SE_DEBUG_SEARCH); Document *doc = SE::getInstance()->getDocument(); g_return_if_fail(doc); Glib::ustring pattern = m_entryPattern->get_text(); if(pattern.empty()) return; SubtitleColumnRecorder m_column; Gtk::TreeIter iter = doc->get_subtitle_model()->getFirst(); if(!iter) iter = doc->get_subtitle_model()->getFirst(); else { bool found = false; // pour ne pas rester bloquer sur la selection Gtk::TreeIter selected = SE::getInstance()->getSubtitleView()->getSelected(); if(selected) if(iter == selected) ++iter; Glib::ustring::size_type start, len; // recherche dans le model for(; iter; ++iter) { Glib::ustring text = (*iter)[m_column.text]; if(found = find(text, pattern, start, len)) break; } if(found && iter) { SE::getInstance()->setStatusbar(""); SE::getInstance()->getSubtitleView()->select_and_set_cursor(iter); } else { if(m_checkUsedRegularExpression->get_active()) SE::getInstance()->setStatusbar(_("The regular expression \"%s\" was not found."), pattern.c_str()); else SE::getInstance()->setStatusbar(_("The text \"%s\" was not found."), pattern.c_str()); iter = doc->get_subtitle_model()->getFirst(); } } } /* * */ void DialogFindAndReplace::on_button_find() { if(m_eod) { m_eod = false; m_labelInformation->hide(); if(find_pattern_in_next_iter()); { if(init_text_and_select()) SE::getInstance()->getSubtitleView()->select_and_set_cursor(m_current_iter); } } else if(find_pattern_in_current_iter()) { if(init_text_and_select()) SE::getInstance()->getSubtitleView()->select_and_set_cursor(m_current_iter); } } /* * */ void DialogFindAndReplace::on_button_replace() { if(replace()) { m_textview->get_buffer()->set_text(m_current_text); if(find_pattern_in_current_iter()) { if(init_text_and_select()) SE::getInstance()->getSubtitleView()->select_and_set_cursor(m_current_iter); } } } /* * */ void DialogFindAndReplace::on_button_replace_all() { m_textview->get_buffer()->set_text(""); m_current_text = ""; m_current_start = 0; m_current_len = 0; m_current_iter = SE::getInstance()->getDocument()->get_subtitle_model()->getFirst(); if(m_current_iter) { while(find_pattern_in_current_iter()) { replace(); } } } /* * */ bool DialogFindAndReplace::find_pattern_in_next_iter() { m_current_text = ""; m_current_start = 0; m_current_len = 0; if(!m_current_iter) m_current_iter = SE::getInstance()->getDocument()->get_subtitle_model()->getFirst(); else ++m_current_iter; // fin du document ? if(!m_current_iter) { m_eod = true; m_labelInformation->show(); return false; } return find_pattern_in_current_iter(); } /* * */ bool DialogFindAndReplace::find_pattern_in_current_iter() { if(!m_current_iter) return false; Glib::ustring pattern = m_entryPattern->get_text(); Glib::ustring::size_type start, len; m_current_text = SubtitleModifier(m_current_iter).get_text(); unsigned int pos = m_current_start + m_current_len; bool res = find(m_current_text.substr(pos, m_current_text.size()), pattern, start, len); if(res) { m_current_start = pos + start; m_current_len = len; return true; } return find_pattern_in_next_iter(); } /* * init le textview et selectionne la recherche */ bool DialogFindAndReplace::init_text_and_select() { if(!m_current_iter) return false; if(m_current_start == Glib::ustring::npos || m_current_len == Glib::ustring::npos) return false; Glib::RefPtr buffer = m_textview->get_buffer(); buffer->set_text(m_current_text); Gtk::TextIter ins = buffer->get_iter_at_offset(m_current_start); Gtk::TextIter bound = buffer->get_iter_at_offset(m_current_start + m_current_len); buffer->select_range(ins, bound); return true; } /* * */ bool DialogFindAndReplace::replace() { if(!m_current_iter) return false; if(m_current_start == Glib::ustring::npos || m_current_len == Glib::ustring::npos) return false; Glib::ustring replaceWith = m_entryReplaceWith->get_text(); m_current_text.replace(m_current_start, m_current_len, replaceWith); // mise a jours de la longueur du texte par rapport // au remplacement du nouveau mots m_current_len = replaceWith.size(); // mise a jours du model SubtitleModifier(m_current_iter).set_text(m_current_text); return true; }