Code

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