/// // Copyright (C) 2002 - 2004, Fredrik Arnerup & Rasmus Kaj, See COPYING /// #include "printdialog.h" #include "widget/subpanel.h" #include "widget/wmisc.h" #include "widget/usererror.h" #include #include #include #include #include #include "util/warning.h" #include "util/filesys.h" #include "util/os.h" #include "util/tempfile.h" #include "config.h" #include "docview.h" #include "document/document.h" #include "widget/errordialog.h" namespace { enum {F_PS = 0, F_EPS, F_PDF}; } PrintDialog::PrintDialog(Gtk::Window &parent, DocumentView &_document_view): DialogWrap("Print", parent), using_button("Print _using:", true), file_button("Print _to file:", true), all_button("_All", true), current_button("Cu_rrent", true), from_button("Fro_m:", true), fonts_button("_Include fonts", true), gray_button("_Grayscale", true), from_spinner(0, false), to_spinner(0, false), document_view(_document_view), file_entry("Print To File") { set_resizable(false); { Gtk::RadioButton::Group group = using_button.get_group(); file_button.set_group(group); } { Gtk::RadioButton::Group group = all_button.get_group(); current_button.set_group(group); from_button.set_group(group); } Glib::RefPtr sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_HORIZONTAL); sizegroup->add_widget(using_button); sizegroup->add_widget(file_button); Gtk::HBox *format_menu_box = manage(new Gtk::HBox(false, double_space)); Gtk::Label *format_menu_label = manage(new Gtk::Label("_Format: ", 0.0, 0.5, true)); format_menu_label->set_mnemonic_widget(format_menu); format_menu_box->pack_start(*format_menu_label, Gtk::PACK_SHRINK); format_menu.append_text("PostScript"); format_menu.append_text("Encapsulated PostScript (EPS)"); format_menu.append_text("Portable Document Format (PDF)"); format_menu.set_active(F_PS); format_menu.signal_changed().connect (sigc::mem_fun(*this, &PrintDialog::update)); format_menu_box->pack_start(format_menu, Gtk::PACK_EXPAND_WIDGET); Gtk::HBox *using_box = manage(new Gtk::HBox(false, double_space)); using_box->pack_start(using_button, Gtk::PACK_SHRINK); using_box->pack_start(using_entry, Gtk::PACK_EXPAND_WIDGET); Gtk::HBox *file_box = manage(new Gtk::HBox(false, double_space)); file_box->pack_start(file_button, Gtk::PACK_SHRINK); file_box->pack_start(file_entry, Gtk::PACK_EXPAND_WIDGET); Gtk::HBox *from_to_box = manage(new Gtk::HBox(false, double_space)); from_to_box->pack_start(from_button, Gtk::PACK_SHRINK); from_to_box->pack_start(from_spinner, Gtk::PACK_SHRINK); from_to_box->pack_start(*manage(new Gtk::Label("to:")), Gtk::PACK_SHRINK); from_to_box->pack_start(to_spinner, Gtk::PACK_SHRINK); SubPanel *pages_box = manage(new SubPanel("Pages")); this->pages_box = pages_box; pages_box->pack_start(all_button, Gtk::PACK_SHRINK); pages_box->pack_start(current_button, Gtk::PACK_SHRINK); pages_box->pack_start(*from_to_box, Gtk::PACK_SHRINK); Gtk::VBox *format_box = manage(new Gtk::VBox(false, single_space)); format_box->pack_start(fonts_button, Gtk::PACK_SHRINK); fonts_button.set_active(); // include fonts by default format_box->pack_start(gray_button, Gtk::PACK_SHRINK); Gtk::HBox *foo_box = manage(new Gtk::HBox(false, double_space)); foo_box->pack_start(*pages_box, Gtk::PACK_EXPAND_WIDGET); foo_box->pack_start(*manage(new Gtk::VSeparator())); foo_box->pack_start(*format_box, Gtk::PACK_SHRINK); Gtk::VBox *vbox = manage(new Gtk::VBox(false, double_space)); vbox->set_border_width(border_width); //set_border_width(border_width); vbox->pack_start(*format_menu_box, Gtk::PACK_SHRINK); vbox->pack_start(*using_box, Gtk::PACK_SHRINK); vbox->pack_start(*file_box, Gtk::PACK_SHRINK); vbox->pack_start(*foo_box, Gtk::PACK_SHRINK, single_space); get_vbox()->pack_start(*vbox); get_vbox()->show_all(); get_action_area()->show_all(); using_entry.set_text(config.PrintCommand.values.front()); using_button.set_active(true); using_button.signal_clicked().connect (sigc::mem_fun(*this, &PrintDialog::update)); // If there are only two buttons in a group, you only need to connect one. // If there are more, you need to connect them all. That is annoying. all_button.signal_clicked().connect (sigc::mem_fun(*this, &PrintDialog::update)); current_button.signal_clicked().connect (sigc::mem_fun(*this, &PrintDialog::update)); from_button.signal_clicked().connect (sigc::mem_fun(*this, &PrintDialog::update)); add_button(Gtk::Stock::CANCEL, 0); add_button(Gtk::Stock::PRINT, 1)->grab_default(); } void PrintDialog::show_it() { DocRef document = document_view.get_document(); if(!document) return; int first = document->get_first_page_num(); int last = first + int(document->get_num_of_pages()) - 1; save_state(); from_spinner.limits(first, last); to_spinner.limits(first, last); from_spinner.set(first); to_spinner.set(last); show(); // Gtk::Entry::set_position doesn't seem to work // unless the entry is shown first const std::string &filename = document_view.get_document_meta().get_filename(); if(filename.empty()) // the document has not been saved file_entry.entry.set_text("pptout.foo"); else file_entry.entry.set_text(filename); update(); } void PrintDialog::on_response(int response_id) { Glib::RefPtr window = get_window(); if(response_id == 0) { restore_state(); hide(); } else if(response_id == 1) { try { DocRef document = document_view.get_document(); if(document) { int first = document->get_first_page_num(); int last = first + document->get_num_of_pages() - 1; if(current_button.get_active()) { first = last = document_view.get_current_page_num(); } else if(from_button.get_active()) { first = int(from_spinner.get()); last = int(to_spinner.get()); if(last < first) throw UserError("Bad page interval", "\"From\" page number must be lower\n" "than \"To\" page number"); // We could make this error impossible to cause, // but I don't like putting leash and collar on the user. } // else all_button is active std::ofstream out; std::auto_ptr tempfile; bool print_to_file = file_button.get_active(); if(print_to_file) { out.open(file_entry.entry.get_text().c_str()); } else { tempfile.reset(new Tempfile); out.open(tempfile->get_filename().c_str()); } if(!out) throw UserError("Failed to open file for printing:\n" + file_entry.entry.get_text(), "Check if you have permission " "to write to this file"); window->set_cursor(Gdk::Cursor(Gdk::WATCH)); // make sure the cursor is updated while(Gtk::Main::events_pending()) Gtk::Main::iteration(); int format = format_menu.get_active_row_number(); if(format == F_PDF) { PDF::Document::Ptr result = PDF::Document::create(); document->print_pdf(result, first, last); result->write(out); int failed_chars = result->getNumOfFailedChars(); /// \todo show warning dialog instead if(failed_chars) ErrorDialog::instance().show_warning ("Failed to print " + tostr(failed_chars) + " characters", "This was probably caused by using characters not " "present in the MacRoman encoding together with Type1 fonts.\n" "Passepartout currently lacks full Unicode support for Type1 " "fonts when printing to PDF."); } else if(format == F_EPS) document->print(out, // only the current page document_view.get_current_page_num(), document_view.get_current_page_num(), true, fonts_button.get_active(), gray_button.get_active()); else // ps document->print(out, first, last, false, fonts_button.get_active(), gray_button.get_active()); window->set_cursor(); if(!print_to_file) { // pipe to a program std::string stdout_data, stderr_data; int status; std::string command = using_entry.get_text() + " < " + tempfile->get_filename(); command = "sh -c \"" + command + '\"'; try { debug << command << std::endl; Glib::spawn_command_line_sync(command, &stdout_data, &stderr_data, &status); debug << status << std::endl; debug << stderr_data << std::endl; } catch(const Glib::SpawnError &e) { // Glib::SpawnError is not a std::exception throw UserError("Failed to run: " + using_entry.get_text(), e.what()); } if(status != 0) throw UserError("Failed to print using: " + using_entry.get_text(), stderr_data); } hide(); } } catch(...) { window->set_cursor(); // restore normal cursor throw; } } } void PrintDialog::save_state() { saved_using_text = using_entry.get_text(); saved_file_text = file_entry.entry.get_text(); saved_file_not_using = file_button.get_active(); saved_gray = gray_button.get_active(); saved_fonts = fonts_button.get_active(); saved_format = format_menu.get_active_row_number(); } void PrintDialog::restore_state() { using_entry.set_text(saved_using_text); file_entry.entry.set_text(saved_file_text); file_button.set_active(saved_file_not_using); gray_button.set_active(saved_gray); fonts_button.set_active(saved_fonts); format_menu.set_active(saved_format); } void PrintDialog::update() { using_entry.set_sensitive(using_button.get_active()); file_entry.set_sensitive(file_button.get_active()); from_spinner.set_sensitive(from_button.get_active()); to_spinner.set_sensitive(from_button.get_active()); int format = format_menu.get_active_row_number(); // always include fonts in PDFs fonts_button.set_sensitive(format != F_PDF); // grayscale should apply to pdf too, eventually gray_button.set_sensitive(format != F_PDF); // eps format always prints the current page only pages_box->set_sensitive(format != F_EPS); std::string suffix = ".ps"; if(format == F_PDF) suffix = ".pdf"; else if(format == F_EPS) suffix = ".eps"; file_entry.entry.set_text(no_suffix(file_entry.entry.get_text()) + suffix); }