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

window.cc

///
// Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING
///
#include "window.h"

#include <iostream>
#include <sstream>
#include <fstream>
#include <vector>
#include <set>
#include <cassert>

#include <gtkmm/adjustment.h>
#include <gtkmm/label.h>
#include <gtkmm/stock.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/table.h>
#include <gtkmm/image.h>
#include <gtkmm/statusbar.h>
#include <gtkmm/optionmenu.h>
#include <gtkmm/menubar.h>
#include <gtkmm/toolbar.h>
#include <gtkmm/main.h>

#include "util/tempfile.h"
#include "util/warning.h"
#include "util/stringutil.h"
#include "util/filesys.h"
#include "util/os.h"

#include "widget/wmisc.h"
#include "widget/usererror.h"
#include "widget/filesel.h"
#include "widget/imagefilesel.h"
#include "widget/errordialog.h"

#include "icons/icons.h"

#include "document/fileerrors.h"
#include "document/page.h"

#include "config.h"
#include "inspiration.h"
#include "printdialog.h"
#include "propertiesdialog.h"
#include "docpropsdialog.h"
#include "streamdialog.h"
#include "pagesel.h"

namespace{
  typedef std::set<FrameWindow*> Windows;
  Windows windows; // a list of all windows

  typedef std::set<unsigned int> ZoomFactors;
  ZoomFactors zoom_factors;
}

// A bit more clever than a usual conversion function
// - it understands per cent signs.
float str2float(const std::string& value) {
  std::istringstream s(value.c_str());
  float f, factor = 1;
  if(s >> f) {
    char ch;
    if(s >> ch)
      if(ch == '%') factor = 0.01;
      else throw std::runtime_error("Strange unit in \"" + value + '"');
    return f * factor;
  } else
    throw std::runtime_error("Not a float value: \"" + value + '"');
}

FrameWindow *FrameWindow::active_window(0);

00075 FrameWindow::FrameWindow(const FrameWindow &original)
  : document_view(original.document_view.get_document_meta(),
                  config.ZoomLevel.values.front() / 100.0), 
    open_dialog(new Filesel(*this, "Open")),
    save_dialog(new Filesel(*this, "Save As", true)), // accept non-existing
    print_dialog(new PrintDialog(*this, document_view)),
    text_frame_dialog(new TextFrameDialog(*this, document_view)),
    icons(new Icons())
{
  constructor_common();
}

// you are not supposed to call with non-empty string and non-null
// document
FrameWindow::FrameWindow(const Glib::ustring &filename, DocRef document)
  : document_view(DocMeta(filename.empty() 
                    ? document
                    : Document::create(filename, false),
                    filename),
                  config.ZoomLevel.values.front() / 100.0),
    open_dialog(new Filesel(*this, "Open")),
    save_dialog(new Filesel(*this, "Save As", true)), // accept non-existing
    print_dialog(new PrintDialog(*this, document_view)),
    text_frame_dialog(new TextFrameDialog(*this, document_view)),
    icons(new Icons())
{
  constructor_common();
}

void FrameWindow::constructor_common() {
  { // get default unit of import image dialog from config file
    Glib::ustring unit = config.LengthUnit.values.front();
    float factor = 1;
    try { factor = length_units.get_factor(unit); }
    catch(unknown_unit_error&) { unit = Glib::ustring(); }
    import_dialog.reset
      (new ImageFilesel(*this, "Import Image", 
                  config.DefaultResolution.values.front(),
                  unit, factor));
  }

  // temp file for gv to read from when doing a print preview:
  preview_tmp_file = Tempfile::find_new_name();

  pagesel = manage(new Pagesel(document_view));

  toolbar = manage(new Gtk::Toolbar()); // before create_menus()
  menubar = manage(new Gtk::MenuBar()); // before create_menus()
  create_menus(); // defined in windowmenus.cc
  
  zoom_lock = false;
  set_default_size(500, 700);

  
  if(zoom_factors.empty()) {
    unsigned int factors[] = {50, 75, 100, 150, 200, 0};
    for(unsigned int *i = factors; *i; i++)
      zoom_factors.insert(*i);
    const Config::Floats &u_factors = config.ZoomLevel.values;
    Config::Floats::const_iterator i;
    for(i = u_factors.begin(); i != u_factors.end(); i++)
      zoom_factors.insert(int(*i));
    //    std::sort(zoom_factors.begin(), zoom_factors.end());
  }

  Gtk::VBox *mainbox = manage(new Gtk::VBox());
  add(*mainbox);

  toolbar->set_orientation(Gtk::ORIENTATION_VERTICAL);
  toolbar->set_toolbar_style(Gtk::TOOLBAR_ICONS);

  Gtk::HBox *hbox = manage(new Gtk::HBox(false, 0));
  hbox->pack_start(*toolbar, Gtk::PACK_SHRINK, 2);

  Gtk::Table *table = manage(new Gtk::Table(2, 2, false));
  hbox->pack_end(*table, Gtk::PACK_EXPAND_WIDGET, 0);

  mainbox->pack_start(*menubar, Gtk::PACK_SHRINK);
  mainbox->pack_start(*hbox);

  scroller = manage(new Gtk::ScrolledWindow());
  table->attach(*scroller, 1, 2, 1, 2, 
            Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL);

  scroller->add(document_view);
  scroller->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);

  {
    using namespace Gtk::Toolbar_Helpers;
    ToolList &tools = toolbar->tools();

    tools.push_back(StockElem(Gtk::Stock::SAVE,
                        slot(*this, &FrameWindow::save),
                        "Save file"));
    save_button = static_cast<Gtk::Button*>(tools.back().get_widget());
    tools.push_back(Space());
    tools.push_back(StockElem(Gtk::Stock::PRINT_PREVIEW,
                        slot(*this, &FrameWindow::print_to_viewer),
                        "Print to external viewer"));
    preview_button = static_cast<Gtk::Button*>(tools.back().get_widget());
    tools.push_back(Space());
    tools.push_back(ButtonElem("Text frame", 
                         icons->new_frame, 
                         slot(*text_frame_dialog, 
                            &TextFrameDialog::show_raise),
                         "Create new text frame"));
    text_frame_button = static_cast<Gtk::Button*>(tools.back().get_widget());
    tools.push_back(ButtonElem("Image",
                         icons->moose, 
                         slot(*import_dialog, &ImageFilesel::show),
                         "Import image file"));
    image_frame_button = static_cast<Gtk::Button*>(tools.back().get_widget());
    tools.push_back(Space());
    tools.push_back(StockElem(Gtk::Stock::PROPERTIES,
                        slot(PropertiesDialog::instance(),
                           &PropertiesDialog::show_raise),
                         "Show object properties"));
    properties_button = static_cast<Gtk::Button*>(tools.back().get_widget());
    tools.push_back(ButtonElem("Streams", 
                         icons->streams, 
                         slot(StreamDialog::instance(),
                            &StreamDialog::show_raise),
                         "Show list of text streams"));
    tools.push_back(Space());
    tools.push_back(ToggleElem("Snap to grid", 
                         icons->grid, 
                         slot(*this, 
                            &FrameWindow::on_grid_button_clicked),
                         "Align objects with grid"));
    grid_button = 
      static_cast<Gtk::ToggleButton*>(tools.back().get_widget());
    tools.push_back(Space());
    tools.push_back(StockElem(Gtk::Stock::GOTO_TOP,
                        bind(slot(document_view, 
                              &DocumentView::rearrange_selected), 
                           TOP),
                        "Move object(s) to top"));

    tools.push_back(StockElem(Gtk::Stock::GO_UP,
                        bind(slot(document_view, 
                              &DocumentView::rearrange_selected), 
                           UP),
                "Move object(s) up"));

    tools.push_back(StockElem(Gtk::Stock::GO_DOWN,
                        bind(slot(document_view, 
                              &DocumentView::rearrange_selected), 
                           DOWN),
                "Move object(s) down"));

    tools.push_back(StockElem(Gtk::Stock::GOTO_BOTTOM,
                        bind(slot(document_view, 
                              &DocumentView::rearrange_selected), 
                           BOTTOM),
                "Move object(s) to bottom"));

    streams_button = static_cast<Gtk::Button*>(tools.back().get_widget());
  }
  
  cafe_opera = manage(new Gtk::Statusbar());
  cafe_opera->set_has_resize_grip(false);
  cafe_opera->property_spacing() = double_space;
  
  cafe_opera->pack_end(*pagesel, Gtk::PACK_SHRINK, 0);
  Gtk::Label *page_label = manage(new Gtk::Label("P_age:", true));
  page_label->set_mnemonic_widget(pagesel->get_optionmenu());
  cafe_opera->pack_end(*page_label,
                   Gtk::PACK_SHRINK, 0);
  zoom_factor = manage(new Gtk::OptionMenu());
  cafe_opera->pack_end(*zoom_factor, Gtk::PACK_SHRINK, 0);
  Gtk::Label *zoom_label = manage(new Gtk::Label("_Zoom:", true));
  zoom_label->set_mnemonic_widget(*zoom_factor);
  cafe_opera->pack_end(*zoom_label, 
                   Gtk::PACK_SHRINK, 0);
  
  mainbox->pack_end(*cafe_opera, Gtk::PACK_SHRINK);
  
  document_view.zoom_change_signal.connect
    (slot(*this, &FrameWindow::zoom_change_action));
  zoom_factor->set_menu(*create_zoom_menu());
  zoom_factor->get_menu()->signal_selection_done().connect
    (slot(*this, &FrameWindow::zoom_factor_changed_action));
  
  open_dialog->signal_hide().connect
    (slot(*this, &FrameWindow::open_dialog_done));
  save_dialog->signal_hide().connect
    (slot(*this, &FrameWindow::save_dialog_done));
  import_dialog->signal_hide().connect
    (slot(*this, &FrameWindow::import_dialog_done));

  document_view.document_set_signal.connect
    (slot(*this, &FrameWindow::on_document_changed));

  Document::changed_signal.connect
    (slot(*this, &FrameWindow::on_document_updated));

  // put window in global list
  windows.insert(this);

  show_all();

  active_window = this;

  // make sure not everything is visible and that document dependent
  // handlers are connected
  on_document_changed();
}

FrameWindow::~FrameWindow() {
  // remove window from list
  windows.erase(this);

  try { unlink(preview_tmp_file); }
  catch(const std::exception& err) {
    warning << "Failed to clean up: " << err.what() << std::endl;
  }
  
  // set active window
  active_window = 0; // don't know which one it'll be

  if(windows.empty())
    Gtk::Main::quit();
  else
    debug << "window count = " << windows.size() << std::endl;
}

void FrameWindow::on_grid_button_clicked() {
  document_view.set_snap(grid_button->get_active() ? snap::GRID : snap::NONE);
}

void FrameWindow::do_nothing() const {
  ErrorDialog::instance().show_error("Not implemented yet", "Sorry.");
}

void FrameWindow::set_filename(const Glib::ustring &filename) {
  if(!filename.empty()) {
    set_title(basename(filename) + " - Passepartout");
  } else
    set_title("Passepartout");
}

void FrameWindow::zoom_change_action(float factor) {
  if(!zoom_lock) {
    using namespace Gtk::Menu_Helpers;
    MenuList::iterator i;
    const MenuList &items = zoom_factor->get_menu()->items();
    int j;
    for(i = items.begin(), j = 0; i != items.end(); i++, j++) {
      if(str2float(dynamic_cast<Gtk::Label&>
                   (*(i->get_child())).get_text()) == factor) {
        zoom_factor->get_menu()->set_active(j);
        return;
      } else if(str2float(dynamic_cast<Gtk::Label&>
                          (*(i->get_child())).get_text()) < factor) {
        warning << "Zoom factor " << factor 
                << " is not defined" << std::endl;
      }
    }
  }
}

void FrameWindow::zoom_factor_changed_action() {
  zoom_lock = true;
  // GtkOtptionMenu (GTK+) behaves in a rather perverse manner:
  // The child of the active MenuItem is reparented to
  // reside within the actual button instead of inside the
  // MenuItem. I went a little crazy before I read the GTK+ FAQ.
  // /Fredrik
  if(Gtk::Label* label = dynamic_cast<Gtk::Label*>(zoom_factor->get_child()))
    document_view.set_zoom_factor(str2float(label->get_text()));
  else ;//run around in circles and scream!

  zoom_lock = false;
  scroller->get_vadjustment()->set_value(0);
  scroller->get_hadjustment()->set_value(0);
}

Gtk::Menu* FrameWindow::create_zoom_menu() {
  using namespace Gtk;
  using namespace Menu_Helpers;

  Menu *menu = manage(new Menu());
  MenuList& menu_list = menu->items();
  for(ZoomFactors::const_iterator i = zoom_factors.begin();
      i != zoom_factors.end();  ++i) {
    menu_list.push_back(MenuElem(tostr(*i) + " %"));
    // the first value in ZoomLevel is the default
    if(int(*i) == int(config.ZoomLevel.values.front()))
      menu->set_active(menu->items().size() - 1);
  }

  return menu;
}

void FrameWindow::on_document_updated(DocRef document_) {
  DocRef document = document_view.get_document();
  if(!delete_page_item || document != document_)
    return;
  on_document_changed();
}

void FrameWindow::on_document_filename_changed() {
  DocMeta document = document_view.get_document_meta();
  set_filename(document ? document.get_filename() : "");
}

void FrameWindow::on_document_changed() {
  DocRef document = document_view.get_document();
  // enable/disable stuff
  bool on = document;
  bool have_pages = on && document->get_num_of_pages() > 0;
  if(!(new_view_item && save_item && save_as_item 
       && print_item && preview_item 
       && props_item && prefs_item
       && save_button && text_frame_button && image_frame_button 
       && preview_button
       && page_menu && edit_menu)) {
    warning << "Some menu stuff is NULL" << std::endl;
    return;
  }
  new_view_item->set_sensitive(on);
  save_item->set_sensitive(on);
  save_button->set_sensitive(on);
  save_as_item->set_sensitive(on);
  print_item->set_sensitive(have_pages);
  preview_item->set_sensitive(have_pages);
  preview_button->set_sensitive(have_pages);
  props_item->set_sensitive(on);

  text_frame_button->set_sensitive(have_pages);
  image_frame_button->set_sensitive(have_pages);
  toolbar->set_sensitive(on);
  pagesel->set_sensitive(have_pages);
  zoom_factor->set_sensitive(on);

  Gtk::Menu_Helpers::MenuList::iterator i;
  for(i = edit_menu->items().begin(); i != edit_menu->items().end(); i++)
    i->set_sensitive(on);
  // but show preferences anyway
  prefs_item->set_sensitive(true);

  for(i = page_menu->items().begin(); i != page_menu->items().end(); i++)
    i->set_sensitive(on);
  delete_page_item->set_sensitive(have_pages);

  // We may have a new DocMeta in view, so connect to the new signal
  DocMeta docmeta = document_view.get_document_meta();
  docmeta.changed_signal().connect
    (slot(*this, &FrameWindow::on_document_filename_changed));
  set_filename(basename(docmeta.get_filename()));

}

void FrameWindow::new_document() {
  DocPropsDialog::instance().create(&document_view);
}

void FrameWindow::duplicate_view() {
  new FrameWindow(*this);
}

void FrameWindow::show_doc_props_dialog() {
  DocPropsDialog::instance().modify(&document_view);
}

void FrameWindow::open_dialog_done() {
  if(!open_dialog->was_cancelled()) {
      if(document_view.get_document())
      new FrameWindow(open_dialog->get_filename());
      else {
      document_view.set_document
        (DocMeta(Document::create(open_dialog->get_filename(), false),
               open_dialog->get_filename()));
      }
  }
}

void FrameWindow::import_dialog_done() {
  if(!import_dialog->was_cancelled())
    document_view.new_image_frame
      (import_dialog->get_filename(),
       dynamic_cast<ImageFilesel*>(import_dialog.get())->get_res());
}

void FrameWindow::save_dialog_done() {
  DocMeta document = document_view.get_document_meta();

  if(!save_dialog->was_cancelled() && document) {
    const std::string filename = save_dialog->get_filename();
    document.set_filename(filename);
    document->save(filename);
  }
}

void FrameWindow::close() {
  DocRef document = document_view.get_document();
  if(windows.size() > 1 || !document) {
    windows.erase(this);
    delete this;
  } else {
    DocRef null = Document::null();
    document_view.set_document(DocMeta(null));
    PropertiesDialog::instance().set_document(null);
    StreamDialog::instance().set_document(null);
    set_filename("");
  }
}

void FrameWindow::save() {
  DocRef document = document_view.get_document();
  if(!document)
    return;

  const std::string filename =
    document_view.get_document_meta().get_filename();
  if(!filename.empty()) {
    document->save(filename);
  } else
    save_dialog->show();
}

void FrameWindow::toggle_toolbar() {
  if(toolbar->is_visible())
    toolbar->hide();
  else
    toolbar->show();
}

void FrameWindow::toggle_cafe_opera() {
  if(cafe_opera->is_visible())
    cafe_opera->hide();
  else
    cafe_opera->show();
}

bool FrameWindow::on_delete_event(GdkEventAny *event) {
  close();
  return true;
}

bool FrameWindow::on_focus_in_event(GdkEventFocus *event) {
  // the nonmodal windows are updated to reflect the content in the window
  // that is currently in focus
  
  active_window = this;
  DocRef document = document_view.get_document();

  PropertiesDialog::instance().set_transient_for(*this);
  PropertiesDialog::instance().set_document(document);

  StreamDialog::instance().set_transient_for(*this);
  StreamDialog::instance().set_document(document);

  return false;
}

bool FrameWindow::on_key_press_event(GdkEventKey *k) {
  Window::on_key_press_event(k);
  document_view.on_key_press_event(k);
  return false;
}

00537 void FrameWindow::print_to_viewer() const {
  const DocRef document = document_view.get_document();

  // try to run gv (or whatever you want to use instead)
  if(document) {
    const std::string &psviewer = config.PSViewer.values.front();
    if(!psviewer.empty()) {
      try {
      // Print to a file and then give that file to gv.
      // This is actually the best way with gv.  If we pipe, gv can't reread
      // the file when jumping pages.
      /// \todo The tempfile belongs to this call, not to the window.  The
      // file should be deleted when gv exits.
      std::ofstream out(preview_tmp_file.c_str());
      if(!out) {
        throw std::runtime_error("Could not open file for printing:\n"
                           + preview_tmp_file);
      }
      // print all pages:
      document->print(out, 
                  document->get_first_page_num(), 
                  document->get_first_page_num() 
                  + document->get_num_of_pages() - 1,
                        false /* not eps */,
                        true /* include fonts */);
      verbose << psviewer + " " + preview_tmp_file << std::endl;
      try {
        Glib::spawn_command_line_async(psviewer + " " + preview_tmp_file);
      } catch(Glib::SpawnError e) {
        throw std::runtime_error("Could not run: " + psviewer + "\n"
                           + e.what());
      }
    }
    catch(const std::exception &e) {
      throw Error::Print(e.what());
    }
    } else
      throw UserError("No PostScript viewer has been defined",
                  "You need to define a PostScript viewer in the"
                  " configuration file");
  }
}

void FrameWindow::quit() {
  // make sure all destructors are executed
  for(Windows::iterator i = windows.begin(); i != windows.end(); i++)
    delete *i;

  Gtk::Main::quit();
}

Generated by  Doxygen 1.6.0   Back to index