Logo Search packages:      
Sourcecode: passepartout version File versions  Download package

streamdialog.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/stock.h>
#include <gtkmm/table.h>
#include <gtkmm/sizegroup.h>
#include <gtkmm/cellrenderertext.h>

#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 "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<Glib::ustring> name;
  };
  
  MainModelColumns main_columns;
  
  class ParamModelColumns: public Gtk::TreeModel::ColumnRecord {
  public:
    ParamModelColumns() { add(name);  add(value); }
    
    Gtk::TreeModelColumn<Glib::ustring> name;
    Gtk::TreeModelColumn<Glib::ustring> 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::Slot1<bool, GdkEventFocus*> 
  adapt_focus_out(SigC::Slot1<void, int> slot, int response_id) {
    return SigC::hide<GdkEventFocus*>(SigC::bind_return
                                      (SigC::bind(slot, response_id), true));
  }
}

00071 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<Gtk::TreeSelection> selection = stream_list.get_selection();
    selection->signal_changed().connect
      (slot(*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<Gtk::SizeGroup>  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
      (slot(*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
    (slot(*this, &StreamDialog::_update));
   
  name_entry.signal_activate().connect
    (bind(slot(*this, &StreamDialog::on_response), Response::SETNAME));
  name_entry.signal_focus_out_event().connect
    (adapt_focus_out(slot(*this, &StreamDialog::on_response),
                     Response::SETNAME));
  
  file_entry.entry.get_entry()->signal_activate().connect
    (bind(slot(*this, &StreamDialog::on_response), Response::SETFILE));
  file_entry.entry.get_entry()->signal_focus_out_event().connect
    (adapt_focus_out(slot(*this, &StreamDialog::on_response),
                     Response::SETFILE));
  
  xfrm_entry.entry.get_entry()->signal_activate().connect
    (bind(slot(*this, &StreamDialog::on_response), Response::SETXFRM));
  xfrm_entry.entry.get_entry()->signal_focus_out_event().connect
    (adapt_focus_out(slot(*this, &StreamDialog::on_response),
                     Response::SETXFRM));
}

StreamDialog::~StreamDialog() {}

00200 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(slot(*this, &StreamDialog::on_response),
                                   action_id));
  b->signal_activate().connect(bind(slot(*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<TextStream> 
      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(false));
    else
      debug << no_stream << std::endl;
    break;
    
  case Response::SETXFRM:
    if(current_stream) {
      current_stream->set_transform(xfrm_entry.entry.get_text(false));
      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<Gtk::TreeSelection> 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());
    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());
    }

    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));
}

00370 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 {
  const Gtk::Label *tmp = dynamic_cast<const Gtk::Label*> (get_child());
  if(!tmp || tmp->get_text() == no_stream)
    return ""; 
  return tmp->get_text();
}
 
void StreamMenu::update(DocRef document, const std::string &select_name) {
  Gtk::Menu dummy;
  set_menu(dummy); //so it will notice the change
  using namespace Gtk;
  using namespace Menu_Helpers;
  MenuList& menu_list = menu.items();
  menu_list.clear();
  if(!document) {
      menu.items().push_back(MenuElem("")); // cosmetic fix
      menu.set_active(0);
      set_menu(menu);
      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();
      menu.items().push_back(MenuElem(name));
      if(name == select_name)
      menu.set_active(index);
      index++;
    }
  menu.items().push_back(SeparatorElem());
  menu.items().push_back(MenuElem(no_stream));
  if(select_name.empty())
    menu.set_active(index + 1);
  set_menu(menu);
}


// *** 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<Gtk::SizeGroup>  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_Helpers::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();
}

00503 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();
}

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());
      }
    }
    view.new_text_frame(stream);
    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;
  }
}

Generated by  Doxygen 1.6.0   Back to index