Code

6ccd10f87f7299c8ffc5ca5e94c8f293656aba0d
[inkscape.git] / src / ui / widget / page-sizer.cpp
1 /** \file
2  *
3  * Paper-size widget and helper functions
4  *
5  * Authors:
6  *   bulia byak <buliabyak@users.sf.net>
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Jon Phillips <jon@rejon.org>
9  *   Ralf Stephan <ralf@ark.in-berlin.de> (Gtkmm)
10  *
11  * Copyright (C) 2000 - 2005 Authors
12  *
13  * Released under GNU GPL.  Read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
20 #include <cmath>
21 #include <gtkmm.h>
22 #include <gtkmm/optionmenu.h>
23 #include <gtkmm/frame.h>
24 #include <gtkmm/table.h>
25 #include "ui/widget/button.h"
27 #include "ui/widget/scalar-unit.h"
29 #include "helper/units.h"
30 #include "inkscape.h"
31 #include "verbs.h"
32 #include "desktop-handles.h"
33 #include "document.h"
34 #include "desktop.h"
35 #include "page-sizer.h"
36 #include "helper/action.h"
38 using std::pair;
40 namespace Inkscape {
41 namespace UI {
42 namespace Widget {
44 struct PaperSize {
45     char const * const name;
46     double const smaller;
47     double const larger;
48     SPUnitId const unit;
49 };
51     /** \note
52      * The ISO page sizes in the table below differ from ghostscript's idea of page sizes (by
53      * less than 1pt).  Being off by <1pt should be OK for most purposes, but may cause fuzziness
54      * (antialiasing) problems when printing to 72dpi or 144dpi printers or bitmap files due to
55      * postscript's different coordinate system (y=0 meaning bottom of page in postscript and top
56      * of page in SVG).  I haven't looked into whether this does in fact cause fuzziness, I merely
57      * note the possibility.  Rounding done by extension/internal/ps.cpp (e.g. floor/ceil calls)
58      * will also affect whether fuzziness occurs.
59      *
60      * The remainder of this comment discusses the origin of the numbers used for ISO page sizes in
61      * this table and in ghostscript.
62      *
63      * The versions here, in mm, are the official sizes according to
64      * <a href="http://en.wikipedia.org/wiki/Paper_sizes">http://en.wikipedia.org/wiki/Paper_sizes</a> 
65      * at 2005-01-25.  (The ISO entries in the below table
66      * were produced mechanically from the table on that page.)
67      *
68      * (The rule seems to be that A0, B0, ..., D0. sizes are rounded to the nearest number of mm
69      * from the "theoretical size" (i.e. 1000 * sqrt(2) or pow(2.0, .25) or the like), whereas
70      * going from e.g. A0 to A1 always take the floor of halving -- which by chance coincides
71      * exactly with flooring the "theoretical size" for n != 0 instead of the rounding to nearest
72      * done for n==0.)
73      *
74      * Ghostscript paper sizes are given in gs_statd.ps according to gs(1).  gs_statd.ps always
75      * uses an integer number of pt: sometimes gs_statd.ps rounds to nearest (e.g. a1), sometimes
76      * floors (e.g. a10), sometimes ceils (e.g. a8).
77      *
78      * I'm not sure how ghostscript's gs_statd.ps was calculated: it isn't just rounding the
79      * "theoretical size" of each page to pt (see a0), nor is it rounding the a0 size times an
80      * appropriate power of two (see a1).  Possibly it was prepared manually, with a human applying
81      * inconsistent rounding rules when converting from mm to pt.
82      */
83     /** \todo
84      * Should we include the JIS B series (used in Japan)  
85      * (JIS B0 is sometimes called JB0, and similarly for JB1 etc)?
86      * Should we exclude B7--B10 and A7--10 to make the list smaller ?
87      * Should we include any of the ISO C, D and E series (see below) ?
88      */
90 static PaperSize const inkscape_papers[] = {
91     { "A4", 210, 297, SP_UNIT_MM },
92     { "US Letter", 8.5, 11, SP_UNIT_IN },
93     { "US Legal", 8.5, 14, SP_UNIT_IN },
94     { "US Executive", 7.25, 10.5, SP_UNIT_IN },
95     { "A0", 841, 1189, SP_UNIT_MM },
96     { "A1", 594, 841, SP_UNIT_MM },
97     { "A2", 420, 594, SP_UNIT_MM },
98     { "A3", 297, 420, SP_UNIT_MM },
99     { "A5", 148, 210, SP_UNIT_MM },
100     { "A6", 105, 148, SP_UNIT_MM },
101     { "A7", 74, 105, SP_UNIT_MM },
102     { "A8", 52, 74, SP_UNIT_MM },
103     { "A9", 37, 52, SP_UNIT_MM },
104     { "A10", 26, 37, SP_UNIT_MM },
105     { "B0", 1000, 1414, SP_UNIT_MM },
106     { "B1", 707, 1000, SP_UNIT_MM },
107     { "B2", 500, 707, SP_UNIT_MM },
108     { "B3", 353, 500, SP_UNIT_MM },
109     { "B4", 250, 353, SP_UNIT_MM },
110     { "B5", 176, 250, SP_UNIT_MM },
111     { "B6", 125, 176, SP_UNIT_MM },
112     { "B7", 88, 125, SP_UNIT_MM },
113     { "B8", 62, 88, SP_UNIT_MM },
114     { "B9", 44, 62, SP_UNIT_MM },
115     { "B10", 31, 44, SP_UNIT_MM },
117 #if 0 /* Whether to include or exclude these depends on how big we mind our page size menu
118          becoming.  C series is used for envelopes; don't know what D and E series are used for. */
119     { "C0", 917, 1297, SP_UNIT_MM },
120     { "C1", 648, 917, SP_UNIT_MM },
121     { "C2", 458, 648, SP_UNIT_MM },
122     { "C3", 324, 458, SP_UNIT_MM },
123     { "C4", 229, 324, SP_UNIT_MM },
124     { "C5", 162, 229, SP_UNIT_MM },
125     { "C6", 114, 162, SP_UNIT_MM },
126     { "C7", 81, 114, SP_UNIT_MM },
127     { "C8", 57, 81, SP_UNIT_MM },
128     { "C9", 40, 57, SP_UNIT_MM },
129     { "C10", 28, 40, SP_UNIT_MM },
130     { "D1", 545, 771, SP_UNIT_MM },
131     { "D2", 385, 545, SP_UNIT_MM },
132     { "D3", 272, 385, SP_UNIT_MM },
133     { "D4", 192, 272, SP_UNIT_MM },
134     { "D5", 136, 192, SP_UNIT_MM },
135     { "D6", 96, 136, SP_UNIT_MM },
136     { "D7", 68, 96, SP_UNIT_MM },
137     { "E3", 400, 560, SP_UNIT_MM },
138     { "E4", 280, 400, SP_UNIT_MM },
139     { "E5", 200, 280, SP_UNIT_MM },
140     { "E6", 140, 200, SP_UNIT_MM },
141 #endif
143     { "CSE", 462, 649, SP_UNIT_PT },
144     { "US #10 Envelope", 4.125, 9.5, SP_UNIT_IN }, // TODO: Select landscape by default.
145     /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
146        sizes. */
147     { "DL Envelope", 110, 220, SP_UNIT_MM }, // TODO: Select landscape by default.
148     { "Ledger/Tabloid", 11, 17, SP_UNIT_IN },
149     /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
150        means different sizes to different people: different people may expect the width to be
151        either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
152        restricting our interpretation to foolscap folio.  If you wish to introduce a folio-like
153        page size to the list, then please consider using a name more specific than just `Folio' or
154        `Foolscap Folio'. */
155     { "Banner 468x60", 60, 468, SP_UNIT_PX },  // TODO: Select landscape by default.
156     { "Icon 16x16", 16, 16, SP_UNIT_PX },
157     { "Icon 32x32", 32, 32, SP_UNIT_PX },
158     { NULL, 0, 0, SP_UNIT_PX },
159 };
161 //===================================================
162 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
164 class SizeMenuItem : public Gtk::MenuItem {
165 public:
166     SizeMenuItem (PaperSize const * paper, PageSizer * widget)
167     : Gtk::MenuItem (paper ? paper->name : _("Custom")), 
168       _paper(paper), _parent(widget) {}
169 protected:
170     PaperSize const * _paper;
171     PageSizer *_parent;
172     void on_activate();
173 };
175 void
176 SizeMenuItem::on_activate()
178     if (_parent == 0) // handle Custom entry
179         return;
180         
181     double w = _paper->smaller, h = _paper->larger;
182     SPUnit const &src_unit = sp_unit_get_by_id (_paper->unit);
183     sp_convert_distance (&w, &src_unit, &_px_unit);
184     sp_convert_distance (&h, &src_unit, &_px_unit);
185     if (_parent->_landscape)
186         _parent->setDim (h, w, true);
187     else
188         _parent->setDim (w, h, true);
191 //---------------------------------------------------
193 PageSizer::PageSizer()
194 : Gtk::VBox(false,4)
196     Gtk::HBox *hbox_size = manage (new Gtk::HBox (false, 4));
197     pack_start (*hbox_size, false, false, 0);
198     Gtk::Label *label_size = manage (new Gtk::Label (_("P_age size:"), 1.0, 0.5)); 
199     label_size->set_use_underline();
200     hbox_size->pack_start (*label_size, false, false, 0);
201     _omenu_size = manage (new Gtk::OptionMenu);
202     label_size->set_mnemonic_widget (*_omenu_size);
203     hbox_size->pack_start (*_omenu_size, true, true, 0);
204     Gtk::Menu *menu_size = manage (new Gtk::Menu);
206     for (PaperSize const *paper = inkscape_papers; paper->name; paper++) {
207         SizeMenuItem *item = manage (new SizeMenuItem (paper, this));
208         menu_size->append (*item);
209     }
210     SizeMenuItem *item = manage (new SizeMenuItem (0, 0));
211     menu_size->prepend (*item);
212     _omenu_size->set_menu (*menu_size);
215 PageSizer::~PageSizer()
217     _portrait_connection.disconnect();
218     _landscape_connection.disconnect();
219     _changedw_connection.disconnect();
220     _changedh_connection.disconnect();
223 void
224 PageSizer::init (Registry& reg)
226     Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
227     pack_start (*hbox_ori, false, false, 0);
228     Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5)); 
229     hbox_ori->pack_start (*label_ori, false, false, 0);
230     _rb_land = manage (new Gtk::RadioButton (_("_Landscape"), true));
231     Gtk::RadioButton::Group group = _rb_land->get_group();
232     hbox_ori->pack_end (*_rb_land, false, false, 5);
233     _rb_port = manage (new Gtk::RadioButton (_("_Portrait"), true));
234     hbox_ori->pack_end (*_rb_port, false, false, 5);
235     _rb_port->set_group (group);
236     _rb_port->set_active (true);
237     
238     /* Custom paper frame */
239     Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
240     pack_start (*frame, false, false, 0);
241     Gtk::Table *table = manage (new Gtk::Table (5, 2, false));
242     table->set_border_width (4);
243     table->set_row_spacings (4);
244     table->set_col_spacings (4);
245     
246     Inkscape::UI::Widget::Button* fit_canv = manage(new Inkscape::UI::Widget::Button(_("_Fit page to selection"),
247                     _("Resize the page to fit the current selection, or the entire drawing if there is no selection")));
248     // prevent fit_canv from expanding
249     Gtk::Alignment *fit_canv_cont = manage(new Gtk::Alignment(1.0,0.5,0.0,0.0));
250     fit_canv_cont->add(*fit_canv);
252     frame->add (*table);
253     
254     _wr = &reg;
256     _rum.init (_("U_nits:"), "units", *_wr);
257     _rusw.init (_("_Width:"), _("Width of paper"), "width", _rum, *_wr);
258     _rush.init (_("_Height:"), _("Height of paper"), "height", _rum, *_wr);
260     table->attach (*_rum._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
261     table->attach (*_rum._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
262     table->attach (*_rusw.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
263     table->attach (*_rush.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
264     table->attach (*fit_canv_cont, 0,2,3,4, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
266     _landscape_connection = _rb_land->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
267     _portrait_connection = _rb_port->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
268     _changedw_connection = _rusw.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
269     _changedh_connection = _rush.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
270     fit_canv->signal_clicked().connect(sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
271     
272     show_all_children();
275 /**
276  * \param w, h given in px
277  */
278 void 
279 PageSizer::setDim (double w, double h, bool update)
281     _landscape = w>h;
282     _rb_land->set_active (_landscape ? true : false);
283     _rb_port->set_active (_landscape ? false : true);
284     _omenu_size->set_history (1 + find_paper_size (w, h));
285     
286     Unit const& unit = _rum._sel->getUnit();
287     if (!update) _changedw_connection.block();
288     _rusw.setValue (w / unit.factor);
289     if (!update) _changedw_connection.unblock();
290     if (!update) _changedh_connection.block();
291     _rush.setValue (h / unit.factor);
292     if (!update) _changedh_connection.unblock();
295 void
296 PageSizer::setDoc (double w, double h)
298     if (SP_ACTIVE_DESKTOP && !_wr->isUpdating()) {
299         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
300         sp_document_set_width (doc, w, &_px_unit);
301         sp_document_set_height (doc, h, &_px_unit);
302         sp_document_done (doc);
303     } else {
304         setDim (w, h);
305     }
308 /** 
309  * Returns an index into inkscape_papers of a paper of the specified 
310  * size (specified in px), or -1 if there's no such paper.
311  */
312 int
313 PageSizer::find_paper_size (double w, double h) const
315     double given[2];
316     if ( w < h ) {
317         given[0] = w; given[1] = h;
318     } else {
319         given[0] = h; given[1] = w;
320     }
321     g_return_val_if_fail(given[0] <= given[1], -1);
322     for (unsigned i = 0; i < G_N_ELEMENTS(inkscape_papers) - 1; ++i) {
323         SPUnit const &i_unit = sp_unit_get_by_id(inkscape_papers[i].unit);
324         double const i_sizes[2] = { sp_units_get_pixels(inkscape_papers[i].smaller, i_unit),
325                                     sp_units_get_pixels(inkscape_papers[i].larger, i_unit) };
326         g_return_val_if_fail(i_sizes[0] <= i_sizes[1], -1);
327         if ((std::abs(given[0] - i_sizes[0]) <= .1) &&
328             (std::abs(given[1] - i_sizes[1]) <= .1)   )
329         {
330             return (int) i;
331         }
332     }
333     return -1;
336 void
337 PageSizer::fire_fit_canvas_to_selection_or_drawing() {
338     SPDesktop *dt = SP_ACTIVE_DESKTOP;
339     if (!dt) return;
340     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
341     if (verb) {
342         SPAction *action = verb->get_action(dt);
343         if (action) {
344             sp_action_perform(action, NULL);        
345         }
346     }
349 void
350 PageSizer::on_portrait()
352     if (!_rb_port->get_active())
353         return;
354     double w = _rusw.getSU()->getValue ("px");
355     double h = _rush.getSU()->getValue ("px");
356     if (h<w) setDoc (h, w);
359 void
360 PageSizer::on_landscape()
362     if (!_rb_land->get_active())
363         return;
364     double w = _rusw.getSU()->getValue ("px");
365     double h = _rush.getSU()->getValue ("px");
366     if (w<h) setDoc (h, w);
369 void
370 PageSizer::on_value_changed()
372     if (_wr->isUpdating()) return;
374     setDoc (_rusw.getSU()->getValue("px"), _rush.getSU()->getValue("px"));
377 } // namespace Widget
378 } // namespace UI
379 } // namespace Inkscape
381 /*
382   Local Variables:
383   mode:c++
384   c-file-style:"stroustrup"
385   c-file-offsets:((innamespace . 0)(inline-open . 0))
386   indent-tabs-mode:nil
387   fill-column:99
388   End:
389 */
390 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :