/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include #include #include #include #include #include #include #include "widget/usererror.h" #include "widget/wmisc.h" #include "widget/subpanel.h" #include "util/warning.h" #include "document/textstream.h" #include "document/document.h" #include "document/page.h" #include "document/textframe.h" #include "streamdialog.h" #include "config.h" #include "docview.h" /** * File-local utility classes in an unnamed namespace */ namespace { class MainModelColumns: public Gtk::TreeModel::ColumnRecord { public: MainModelColumns() { add(name); } Gtk::TreeModelColumn name; }; MainModelColumns main_columns; class ParamModelColumns: public Gtk::TreeModel::ColumnRecord { public: ParamModelColumns() { add(name); add(value); } Gtk::TreeModelColumn name; Gtk::TreeModelColumn value; }; ParamModelColumns param_columns; namespace Response { enum ReponseType { ADD, DELETE, SETNAME, SETFILE, SETXFRM, }; } } // *** StreamDialog Methods: StreamDialog *StreamDialog::_instance = 0; StreamDialog &StreamDialog::instance() { if(!_instance) _instance = new StreamDialog(); return *_instance; } namespace { sigc::slot adapt_focus_out(sigc::slot slot, int response_id) { return sigc::hide(sigc::bind_return(sigc::bind(slot, response_id), true)); } } StreamDialog::StreamDialog() : UtilityWindow("Text Streams"), file_entry("Stream File"), xfrm_entry("Stylesheet File", config.StylesheetPath.values.front()), document(0), current_selection("") { Gtk::HBox *mainbox = manage(new Gtk::HBox(false, double_space)); mainbox->set_border_width(border_width); { Gtk::VBox *vbox = manage(new Gtk::VBox(false, single_space)); // listan should go here main_model = Gtk::ListStore::create(main_columns); stream_list.set_model(main_model); stream_list.set_headers_visible(false); stream_list.append_column("Stream", main_columns.name); /// \todo Get a more motivated size, not a hardcoded number of pixels. stream_list.property_width_request() = 100; stream_list.property_height_request() = 100; Glib::RefPtr selection = stream_list.get_selection(); selection->signal_changed().connect (sigc::mem_fun(*this, &StreamDialog::on_selection_changed)); Gtk::ScrolledWindow *scroll = manage(new Gtk::ScrolledWindow()); scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scroll->set_shadow_type(Gtk::SHADOW_IN); scroll->add(stream_list); Gtk::Label *list_label = manage(new Gtk::Label("S_treams", 0.0, 0.5, true)); list_label->set_mnemonic_widget(stream_list); vbox->pack_start(*list_label, Gtk::PACK_SHRINK); vbox->pack_start(*scroll); vbox->pack_start(*fix_button(new Gtk::Button(Gtk::Stock::ADD), Response::ADD), Gtk::PACK_SHRINK); vbox->pack_start(*fix_button(new Gtk::Button(Gtk::Stock::DELETE), Response::DELETE), Gtk::PACK_SHRINK); mainbox->pack_start(*vbox); } { Gtk::VBox *propsbox = manage(new Gtk::VBox(false, double_space)); mainbox->pack_start(*propsbox); this->propsbox = propsbox; { SubPanel *box = manage(new SubPanel("Stream Source")); Gtk::Box *line; Glib::RefPtr sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); line = manage(new Gtk::HBox(false, double_space)); Gtk::Label *label = manage(new Gtk::Label("_Name:", 0.0, 0.5, true)); sizegroup->add_widget(*label); label->set_mnemonic_widget(name_entry); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(name_entry, Gtk::PACK_EXPAND_WIDGET); box->pack_start(*line, Gtk::PACK_SHRINK); line = manage(new Gtk::HBox(false, double_space)); label = manage(new Gtk::Label("_File:", 0.0, 0.5, true)); sizegroup->add_widget(*label); label->set_mnemonic_widget(file_entry); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(file_entry, Gtk::PACK_EXPAND_WIDGET); box->pack_start(*line, Gtk::PACK_SHRINK); line = manage(new Gtk::HBox(false, double_space)); label = manage(new Gtk::Label("_Stylesheet:", 1.0, 0.5, true)); sizegroup->add_widget(*label); label->set_mnemonic_widget(xfrm_entry.entry.get_entry()); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(xfrm_entry, Gtk::PACK_EXPAND_WIDGET); box->pack_start(*line, Gtk::PACK_SHRINK); propsbox->pack_start(*box, Gtk::PACK_SHRINK); } { SubPanel* box = manage(new SubPanel("Parameters")); param_box = box; // we need this for set_sensitive later param_model = Gtk::ListStore::create(param_columns); param_list.set_model(param_model); param_list.append_column("Name", param_columns.name); param_list.append_column_editable("Value", param_columns.value); param_list.property_height_request() = 90; param_model->signal_row_changed().connect (sigc::mem_fun(*this, &StreamDialog::on_param_row_changed)); Gtk::ScrolledWindow *scroll = manage(new Gtk::ScrolledWindow()); scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); scroll->set_shadow_type(Gtk::SHADOW_IN); scroll->add(param_list); box->pack_start(*scroll); propsbox->pack_start(*box); } propsbox->set_sensitive(false); // nothing is selected to start with } add(*mainbox); show_all_children(); Document::streams_changed_signal.connect (sigc::mem_fun(*this, &StreamDialog::_update)); name_entry.signal_activate().connect (bind(sigc::mem_fun(*this, &StreamDialog::on_response), Response::SETNAME)); name_entry.signal_focus_out_event().connect (adapt_focus_out(sigc::mem_fun(*this, &StreamDialog::on_response), Response::SETNAME)); file_entry.entry.get_entry().signal_activate().connect (bind(sigc::mem_fun(*this, &StreamDialog::on_response), Response::SETFILE)); file_entry.entry.get_entry().signal_focus_out_event().connect (adapt_focus_out(sigc::mem_fun(*this, &StreamDialog::on_response), Response::SETFILE)); xfrm_entry.entry.get_entry().signal_activate().connect (bind(sigc::mem_fun(*this, &StreamDialog::on_response), Response::SETXFRM)); xfrm_entry.entry.get_entry().signal_focus_out_event().connect (adapt_focus_out(sigc::mem_fun(*this, &StreamDialog::on_response), Response::SETXFRM)); } StreamDialog::~StreamDialog() {} void StreamDialog::_update(DocRef document_) { if(document == document_) update(); } void StreamDialog::update() { main_model->clear(); if(document) { Document::StreamVec tmp = document->get_text_streams(); for(Document::StreamVec::iterator i = tmp.begin(); i != tmp.end(); ++i) { Gtk::TreeModel::Row row = *(main_model->append()); row[main_columns.name] = (*i)->get_name(); } on_selection_changed(); // Restore entry selection } set_sensitive(document); } Gtk::Button* StreamDialog::fix_button(Gtk::Button* button, int action_id) { Gtk::Button *b = manage(button); // Note: It seems that activate means "in any way except a simple click". // So we have to add callbacks for both activate and clicked. b->signal_clicked().connect (bind(sigc::mem_fun(*this, &StreamDialog::on_response), action_id)); b->signal_activate().connect (bind(sigc::mem_fun(*this, &StreamDialog::on_response), action_id)); return b; } void StreamDialog::on_response(int response_id) { const std::string no_stream = "No current stream in StreamDialog!"; switch(response_id) { case Response::ADD: debug << "StreamDialog: add\n"; current_selection = document->make_up_new_name(); { std::auto_ptr stream(new TextStream(current_selection, "", "")); document->add_text_stream(stream.release()); } break; case Response::DELETE: debug << "StreamDialog: delete\n"; if(document && current_stream) { document->remove_text_stream(current_stream->get_name()); current_selection = ""; on_selection_changed(); // Select last item in the list } break; case Response::SETNAME: if(current_stream) { Glib::ustring newname = name_entry.get_text(); Glib::ustring oldname = current_stream->get_name(); try { document->rename_text_stream(oldname, newname); current_selection = newname; if(Gtk::TreeModel::iterator i = stream_list.get_selection()->get_selected()) { (*i)[main_columns.name] = newname; } } catch(const Error::TextStreamName &e) { throw UserError("Failed to rename text stream \"" + oldname + "\" to \"" + newname + "\"", e.what()); } } else debug << no_stream << std::endl; break; case Response::SETFILE: if(current_stream) current_stream->set_association(file_entry.entry.get_text(true)); else debug << no_stream << std::endl; break; case Response::SETXFRM: if(current_stream) { current_stream->set_transform(xfrm_entry.entry.get_text(true)); update_params(current_stream); } else debug << no_stream << std::endl; break; default: // well ... break; } } void StreamDialog::update_params(TextStream *stream) { param_model->clear(); // disable param list if the stream lacks parameters param_box->set_sensitive(stream->param_begin() != stream->param_end()); for(TextStream::ParamIter i = stream->param_begin(); i != stream->param_end(); ++i) { Gtk::TreeModel::Row row = *(param_model->append()); row[param_columns.name] = i->first; row[param_columns.value] = i->second; } } void StreamDialog::on_selection_changed() { if(!document) return; typedef Gtk::TreeModel Model; // Previous stream is no longer current. current_stream = 0; Glib::RefPtr tree_selection = stream_list.get_selection(); if(Model::iterator i = tree_selection->get_selected()) { Model::Row row = *i; Glib::ustring name = row[main_columns.name]; name_entry.set_text(name); current_selection = name; TextStream *stream = document->get_text_stream(name); file_entry.entry.set_text(stream->get_association(), true /* save history */); if(stream->get_transform().empty()) { //Set default stylesheet xfrm_entry.set_default_value(config.DefaultStylesheet.values.front()); } else { xfrm_entry.entry.set_text(stream->get_transform(), true /* save history */); } update_params(stream); // Only set the stream after the updates, so we can ignore signals that // actually come from our own updates current_stream = stream; } else { //If nothing is selected (after update) Model::Children children = stream_list.get_model()->children(); Gtk::TreeModel::Row row; Model::Children::iterator iter; for(iter = children.begin(); iter != children.end(); ++iter) { //Find last known selection row = *iter; if(current_selection == row[main_columns.name]) { tree_selection->select(row); return; // Don't disable propsbox } } // No current selection => select last element (if list not empty) if(current_selection.empty() && iter != children.begin()) { tree_selection->select(row); return; // Don't disable propsbox } } propsbox->set_sensitive(current_stream); } void StreamDialog::on_param_row_changed(const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter) { if(current_stream && iter) current_stream->set_parameter(iter->get_value(param_columns.name), iter->get_value(param_columns.value)); } void StreamDialog::show_raise() { show_all(); UtilityWindow::show_raise(); } void StreamDialog::set_document(DocRef document_) { document = document_; update(); } //*** StreamMenu methods *** namespace{ static const std::string no_stream("( No Stream )"); } StreamMenu::StreamMenu() { update(); } std::string StreamMenu::get_stream() const { Glib::ustring text = get_active_text(); return text == no_stream ? "" : text; } void StreamMenu::update(DocRef document, const std::string &select_name) { Glib::RefPtr::cast_dynamic(get_model())->clear(); if(!document) { append_text("-"); set_active(0); return; } int index = 0; Document::StreamVec streams = document->get_text_streams(); for(Document::StreamVec::iterator i = streams.begin(); i != streams.end(); i++) { const std::string &name = (*i)->get_name(); append_text(name); if(name == select_name) set_active(index); index++; } append_text(no_stream); if(select_name.empty()) set_active(index); } // *** TextFrameDialog methods *** namespace { enum { RESPONSE_TOGGLE_MODE = 4711 }; } TextFrameDialog::TextFrameDialog(Gtk::Window &parent, DocumentView &_view) : DialogWrap("Create new text frame", parent), view(_view), file_entry("Stream File"), xfrm_entry("Stylesheet File", config.StylesheetPath.values.front()) { set_modal(true); Gtk::Box *main_box = manage(new Gtk::VBox(false, double_space)); main_box->set_border_width(border_width); Gtk::Box *old_box = manage(new Gtk::HBox(false, single_space)); old_button = manage(new Gtk::RadioButton("From _existing stream: ", true)); old_box->pack_start(*old_button, Gtk::PACK_SHRINK); old_box->pack_start(streams, Gtk::PACK_SHRINK); main_box->pack_start(*old_box, Gtk::PACK_SHRINK); new_button = manage(new Gtk::RadioButton("C_reate new stream:", true)); main_box->pack_start(*new_button, Gtk::PACK_SHRINK); Gtk::Box *line; Glib::RefPtr sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); new_box = manage(new Gtk::VBox(false, single_space)); line = manage(new Gtk::HBox(false, double_space)); Gtk::Label *label = manage(new Gtk::Label("N_ame:", 0.0, 0.5, true)); sizegroup->add_widget(*label); label->set_mnemonic_widget(name_entry); name_entry.set_activates_default(); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(name_entry, Gtk::PACK_EXPAND_WIDGET); new_box->pack_start(*line, Gtk::PACK_SHRINK); line = manage(new Gtk::HBox(false, double_space)); label = manage(new Gtk::Label("_File:", 0.0, 0.5, true)); sizegroup->add_widget(*label); label->set_mnemonic_widget(file_entry); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(file_entry, Gtk::PACK_EXPAND_WIDGET); new_box->pack_start(*line, Gtk::PACK_SHRINK); line = manage(new Gtk::HBox(false, double_space)); label = manage(new Gtk::Label("_Stylesheet:", 1.0, 0.5, true)); sizegroup->add_widget(*label); label->set_mnemonic_widget(xfrm_entry.entry.get_entry()); line->pack_start(*label, Gtk::PACK_SHRINK); line->pack_start(xfrm_entry, Gtk::PACK_EXPAND_WIDGET); new_box->pack_start(*line, Gtk::PACK_SHRINK); Gtk::Box *format_box = manage(new Gtk::HBox()); format_box->pack_start(*manage(new Gtk::Label(" ")), Gtk::PACK_SHRINK); format_box->pack_start(*new_box); main_box->pack_start(*format_box, Gtk::PACK_SHRINK); add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); add_button(Gtk::Stock::NEW, Gtk::RESPONSE_OK)->grab_default(); get_vbox()->pack_start(*main_box); Gtk::RadioButton::Group group = old_button->get_group(); new_button->set_group(group); old_button->set_active(); fix_button(new_button, RESPONSE_TOGGLE_MODE); new_box->set_sensitive(false); show_all_children(); } void TextFrameDialog::show_raise() { DocRef document = view.get_document(); if(!document) return; name_entry.set_text(view.get_document()->make_up_new_name()); xfrm_entry.set_default_value(config.DefaultStylesheet.values.front()); streams.update(view.get_document()); show(); DialogWrap::show_raise(); } namespace { void new_text_frame(TextStream *stream, Page *page) { int w = 200, h = 300; page->addObject(new TextFrame(page, stream, w, h)); } } void TextFrameDialog::on_response(int response_id) { switch(response_id) { case Gtk::RESPONSE_OK: TextStream *stream; if(old_button->get_active()) stream = view.get_document()->get_text_stream(streams.get_stream()); else { stream = new TextStream(name_entry.get_text(), file_entry.entry.get_text(), xfrm_entry.entry.get_text()); try { view.get_document()->add_text_stream(stream); } catch(const Error::TextStreamName &e) { delete stream; throw UserError("Could not create text stream", e.what()); } } new_text_frame(stream, view.get_page()); hide(); break; case Gtk::RESPONSE_CANCEL: hide(); break; case RESPONSE_TOGGLE_MODE: { bool foo = old_button->get_active(); streams.set_sensitive(foo); new_box->set_sensitive(!foo); } break; default: break; } }