Code

31390b676b38342523a8e5b6de4c2bdfd5aafd7f
[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 (_("P_age size:"), 1.0, 0.5)); 
194     label_size->set_use_underline();
195     hbox_size->pack_start (*label_size, false, false, 0);
196     _omenu_size = manage (new Gtk::OptionMenu);
197     label_size->set_mnemonic_widget (*_omenu_size);
198     hbox_size->pack_start (*_omenu_size, true, true, 0);
199     Gtk::Menu *menu_size = manage (new Gtk::Menu);
201     for (PaperSize const *paper = inkscape_papers; paper->name; paper++) {
202         SizeMenuItem *item = manage (new SizeMenuItem (paper, this));
203         menu_size->append (*item);
204     }
205     SizeMenuItem *item = manage (new SizeMenuItem (0, 0));
206     menu_size->prepend (*item);
207     _omenu_size->set_menu (*menu_size);
210 PageSizer::~PageSizer()
212     _portrait_connection.disconnect();
213     _landscape_connection.disconnect();
214     _changedw_connection.disconnect();
215     _changedh_connection.disconnect();
218 void
219 PageSizer::init (Registry& reg)
221     Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
222     pack_start (*hbox_ori, false, false, 0);
223     Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5)); 
224     hbox_ori->pack_start (*label_ori, false, false, 0);
225     _rb_land = manage (new Gtk::RadioButton (_("_Landscape"), true));
226     Gtk::RadioButton::Group group = _rb_land->get_group();
227     hbox_ori->pack_end (*_rb_land, false, false, 5);
228     _rb_port = manage (new Gtk::RadioButton (_("_Portrait"), true));
229     hbox_ori->pack_end (*_rb_port, false, false, 5);
230     _rb_port->set_group (group);
231     _rb_port->set_active (true);
232     
233     /* Custom paper frame */
234     Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
235     pack_start (*frame, false, false, 0);
236     Gtk::Table *table = manage (new Gtk::Table (4, 2, false));
237     table->set_border_width (4);
238     table->set_row_spacings (4);
239     table->set_col_spacings (4);
240     frame->add (*table);
241     
242     _wr = &reg;
244     _rum.init (_("U_nits:"), "units", *_wr);
245     _rusw.init (_("_Width:"), _("Width of paper"), "width", _rum, *_wr);
246     _rush.init (_("_Height:"), _("Height of paper"), "height", _rum, *_wr);
248     table->attach (*_rum._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
249     table->attach (*_rum._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
250     table->attach (*_rusw.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
251     table->attach (*_rush.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
253     _landscape_connection = _rb_land->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
254     _portrait_connection = _rb_port->signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
255     _changedw_connection = _rusw.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
256     _changedh_connection = _rush.getSU()->signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
257     
258     show_all_children();
261 /**
262  * \param w, h given in px
263  */
264 void 
265 PageSizer::setDim (double w, double h, bool update)
267     _landscape = w>h;
268     _rb_land->set_active (_landscape ? true : false);
269     _rb_port->set_active (_landscape ? false : true);
270     _omenu_size->set_history (1 + find_paper_size (w, h));
271     
272     Unit const& unit = _rum._sel->getUnit();
273     if (!update) _changedw_connection.block();
274     _rusw.setValue (w / unit.factor);
275     if (!update) _changedw_connection.unblock();
276     if (!update) _changedh_connection.block();
277     _rush.setValue (h / unit.factor);
278     if (!update) _changedh_connection.unblock();
281 void
282 PageSizer::setDoc (double w, double h)
284     setDim (w, h);
285     if (!SP_ACTIVE_DESKTOP || _wr->isUpdating())
286         return;
288     SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
289     sp_document_set_width (doc, _rusw.getSU()->getValue("px"), &_px_unit);
290     sp_document_set_height (doc, _rush.getSU()->getValue("px"), &_px_unit);
291     sp_document_done (doc);
294 /** 
295  * Returns an index into inkscape_papers of a paper of the specified 
296  * size (specified in px), or -1 if there's no such paper.
297  */
298 int
299 PageSizer::find_paper_size (double w, double h) const
301     double given[2];
302     if ( w < h ) {
303         given[0] = w; given[1] = h;
304     } else {
305         given[0] = h; given[1] = w;
306     }
307     g_return_val_if_fail(given[0] <= given[1], -1);
308     for (unsigned i = 0; i < G_N_ELEMENTS(inkscape_papers) - 1; ++i) {
309         SPUnit const &i_unit = sp_unit_get_by_id(inkscape_papers[i].unit);
310         double const i_sizes[2] = { sp_units_get_pixels(inkscape_papers[i].smaller, i_unit),
311                                     sp_units_get_pixels(inkscape_papers[i].larger, i_unit) };
312         g_return_val_if_fail(i_sizes[0] <= i_sizes[1], -1);
313         if ((std::abs(given[0] - i_sizes[0]) <= .1) &&
314             (std::abs(given[1] - i_sizes[1]) <= .1)   )
315         {
316             return (int) i;
317         }
318     }
319     return -1;
322 void
323 PageSizer::on_portrait()
325     double w = _rusw.getSU()->getValue ("px");
326     double h = _rush.getSU()->getValue ("px");
327     if (h<w) setDoc (h, w);
330 void
331 PageSizer::on_landscape()
333     double w = _rusw.getSU()->getValue ("px");
334     double h = _rush.getSU()->getValue ("px");
335     if (w<h) setDoc (h, w);
338 void
339 PageSizer::on_value_changed()
341     if (_wr->isUpdating()) return;
343     setDoc (_rusw.getSU()->getValue("px"), _rush.getSU()->getValue("px"));
346 } // namespace Widget
347 } // namespace UI
348 } // namespace Inkscape
350 /*
351   Local Variables:
352   mode:c++
353   c-file-style:"stroustrup"
354   c-file-offsets:((innamespace . 0)(inline-open . 0))
355   indent-tabs-mode:nil
356   fill-column:99
357   End:
358 */
359 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :