Code

copyedit
[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  *   Bob Jamison <ishmal@users.sf.net>
11  *
12  * Copyright (C) 2000 - 2006 Authors
13  *
14  * Released under GNU GPL.  Read the file 'COPYING' for more information
15  */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <string.h>
23 #include <cmath>
24 #include <gtkmm.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     /** \note
45      * The ISO page sizes in the table below differ from ghostscript's idea of page sizes (by
46      * less than 1pt).  Being off by <1pt should be OK for most purposes, but may cause fuzziness
47      * (antialiasing) problems when printing to 72dpi or 144dpi printers or bitmap files due to
48      * postscript's different coordinate system (y=0 meaning bottom of page in postscript and top
49      * of page in SVG).  I haven't looked into whether this does in fact cause fuzziness, I merely
50      * note the possibility.  Rounding done by extension/internal/ps.cpp (e.g. floor/ceil calls)
51      * will also affect whether fuzziness occurs.
52      *
53      * The remainder of this comment discusses the origin of the numbers used for ISO page sizes in
54      * this table and in ghostscript.
55      *
56      * The versions here, in mm, are the official sizes according to
57      * <a href="http://en.wikipedia.org/wiki/Paper_sizes">http://en.wikipedia.org/wiki/Paper_sizes</a>
58      * at 2005-01-25.  (The ISO entries in the below table
59      * were produced mechanically from the table on that page.)
60      *
61      * (The rule seems to be that A0, B0, ..., D0. sizes are rounded to the nearest number of mm
62      * from the "theoretical size" (i.e. 1000 * sqrt(2) or pow(2.0, .25) or the like), whereas
63      * going from e.g. A0 to A1 always take the floor of halving -- which by chance coincides
64      * exactly with flooring the "theoretical size" for n != 0 instead of the rounding to nearest
65      * done for n==0.)
66      *
67      * Ghostscript paper sizes are given in gs_statd.ps according to gs(1).  gs_statd.ps always
68      * uses an integer number of pt: sometimes gs_statd.ps rounds to nearest (e.g. a1), sometimes
69      * floors (e.g. a10), sometimes ceils (e.g. a8).
70      *
71      * I'm not sure how ghostscript's gs_statd.ps was calculated: it isn't just rounding the
72      * "theoretical size" of each page to pt (see a0), nor is it rounding the a0 size times an
73      * appropriate power of two (see a1).  Possibly it was prepared manually, with a human applying
74      * inconsistent rounding rules when converting from mm to pt.
75      */
76     /** \todo
77      * Should we include the JIS B series (used in Japan)
78      * (JIS B0 is sometimes called JB0, and similarly for JB1 etc)?
79      * Should we exclude B7--B10 and A7--10 to make the list smaller ?
80      * Should we include any of the ISO C, D and E series (see below) ?
81      */
83 struct PaperSizeRec {
84     char const * const name;  //name
85     double const smaller;     //lesser dimension
86     double const larger;      //greater dimension
87     SPUnitId const unit;      //units
88 };
90 static PaperSizeRec 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 },
119 //#if 0
120          /*
121          Whether to include or exclude these depends on how
122          big we mind our page size menu
123          becoming.  C series is used for envelopes;
124          don't know what D and E series are used for.
125          */
127     { "C0",                917, 1297, SP_UNIT_MM },
128     { "C1",                648,  917, SP_UNIT_MM },
129     { "C2",                458,  648, SP_UNIT_MM },
130     { "C3",                324,  458, SP_UNIT_MM },
131     { "C4",                229,  324, SP_UNIT_MM },
132     { "C5",                162,  229, SP_UNIT_MM },
133     { "C6",                114,  162, SP_UNIT_MM },
134     { "C7",                 81,  114, SP_UNIT_MM },
135     { "C8",                 57,   81, SP_UNIT_MM },
136     { "C9",                 40,   57, SP_UNIT_MM },
137     { "C10",                28,   40, SP_UNIT_MM },
138     { "D1",                545,  771, SP_UNIT_MM },
139     { "D2",                385,  545, SP_UNIT_MM },
140     { "D3",                272,  385, SP_UNIT_MM },
141     { "D4",                192,  272, SP_UNIT_MM },
142     { "D5",                136,  192, SP_UNIT_MM },
143     { "D6",                 96,  136, SP_UNIT_MM },
144     { "D7",                 68,   96, SP_UNIT_MM },
145     { "E3",                400,  560, SP_UNIT_MM },
146     { "E4",                280,  400, SP_UNIT_MM },
147     { "E5",                200,  280, SP_UNIT_MM },
148     { "E6",                140,  200, SP_UNIT_MM },
149 //#endif
153     { "CSE",               462,  649, SP_UNIT_PT },
154     { "US #10 Envelope", 4.125,  9.5, SP_UNIT_IN },
155     // TODO: Select landscape by default.
156     /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
157        sizes. */
158     { "DL Envelope",       110,  220, SP_UNIT_MM },
159     // TODO: Select landscape by default.
160     { "Ledger/Tabloid",     11,   17, SP_UNIT_IN },
161     /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
162        means different sizes to different people: different people may expect the width to be
163        either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
164        restricting our interpretation to foolscap folio.  If you wish to introduce a folio-like
165        page size to the list, then please consider using a name more specific than just `Folio' or
166        `Foolscap Folio'. */
167     { "Banner 468x60",      60,  468, SP_UNIT_PX },
168     // TODO: Select landscape by default.
169     { "Icon 16x16",         16,   16, SP_UNIT_PX },
170     { "Icon 32x32",         32,   32, SP_UNIT_PX },
171     { "Icon 48x48",         48,   48, SP_UNIT_PX },
172     { NULL,                  0,    0, SP_UNIT_PX },
173 };
177 //########################################################################
178 //# P A G E    S I Z E R
179 //########################################################################
181 //The default unit for this widget and its calculations
182 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
185 /**
186  * Constructor
187  */
188 PageSizer::PageSizer(Registry & _wr)
189     : Gtk::VBox(false,4),
190       _dimensionUnits( _("U_nits:"), "units", _wr ),
191       _dimensionWidth( _("_Width:"), _("Width of paper"), "width", _dimensionUnits, _wr ),
192       _dimensionHeight( _("_Height:"), _("Height of paper"), "height", _dimensionUnits, _wr ),
193       _widgetRegistry(&_wr)
195     //# Set up the Paper Size combo box
196     _paperSizeListStore = Gtk::ListStore::create(_paperSizeListColumns);
197     _paperSizeList.set_model(_paperSizeListStore);
198     _paperSizeList.append_column(_("Name"),
199                                  _paperSizeListColumns.nameColumn);
200     _paperSizeList.append_column(_("Description"),
201                                  _paperSizeListColumns.descColumn);
202     _paperSizeList.set_headers_visible(false);
203     _paperSizeListSelection = _paperSizeList.get_selection();
204     _paper_size_list_connection =
205         _paperSizeListSelection->signal_changed().connect (
206             sigc::mem_fun (*this, &PageSizer::on_paper_size_list_changed));
207     _paperSizeListScroller.add(_paperSizeList);
208     _paperSizeListScroller.set_shadow_type(Gtk::SHADOW_IN);
209     _paperSizeListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
210     _paperSizeListScroller.set_size_request(-1, 90);
212     for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
213     {
214         Glib::ustring name = p->name;
215         char formatBuf[80];
216         snprintf(formatBuf, 79, "%0.1f x %0.1f", p->smaller, p->larger);
217         Glib::ustring desc = formatBuf;
218         if (p->unit == SP_UNIT_IN)
219             desc.append(" in");
220         else if (p->unit == SP_UNIT_MM)
221              desc.append(" mm");
222         else if (p->unit == SP_UNIT_PX)
223             desc.append(" px");
224         PaperSize paper(name, p->smaller, p->larger, p->unit);
225         _paperSizeTable[name] = paper;
226         Gtk::TreeModel::Row row = *(_paperSizeListStore->append());
227         row[_paperSizeListColumns.nameColumn] = name;
228         row[_paperSizeListColumns.descColumn] = desc;
229         }
230     //Gtk::TreeModel::iterator iter = _paperSizeListStore->children().begin();
231     //if (iter)
232     //    _paperSizeListSelection->select(iter);
235     pack_start (_paperSizeListBox, false, false, 0);
236     _paperSizeListLabel.set_label(_("P_age size:"));
237     _paperSizeListLabel.set_use_underline();
238     _paperSizeListBox.pack_start (_paperSizeListLabel, false, false, 0);
239     _paperSizeListLabel.set_mnemonic_widget (_paperSizeList);
240     _paperSizeListBox.pack_start (_paperSizeListScroller, true, true, 0);
242     //## Set up orientation radio buttons
243     pack_start (_orientationBox, false, false, 0);
244     _orientationLabel.set_label(_("Page orientation:"));
245     _orientationBox.pack_start(_orientationLabel, false, false, 0);
246     _landscapeButton.set_use_underline();
247     _landscapeButton.set_label(_("_Landscape"));
248     _landscapeButton.set_active(true);
249     Gtk::RadioButton::Group group = _landscapeButton.get_group();
250     _orientationBox.pack_end (_landscapeButton, false, false, 5);
251     _portraitButton.set_use_underline();
252     _portraitButton.set_label(_("_Portrait"));
253     _portraitButton.set_active(true);
254     _orientationBox.pack_end (_portraitButton, false, false, 5);
255     _portraitButton.set_group (group);
256     _portraitButton.set_active (true);
258     //## Set up custom size frame
259     _customFrame.set_label(_("Custom size"));
260     pack_start (_customFrame, false, false, 0);
261     _customTable.resize(2, 2);
262     _customTable.set_border_width (4);
263     _customTable.set_row_spacings (4);
264     _customTable.set_col_spacings (4);
265     _customTable.attach(_dimensionWidth, 0,1,0,1);
266     _customTable.attach(_dimensionUnits, 1,2,0,1);
267     _customTable.attach(_dimensionHeight, 0,1,1,2);
268     _customTable.attach(_fitPageButton,              1,2,1,2);
269     _customFrame.add(_customTable);
271     _fitPageButton.set_use_underline();
272     _fitPageButton.set_label(_("_Fit page to selection"));
273     _tips.set_tip(_fitPageButton, _("Resize the page to fit the current selection, or the entire drawing if there is no selection"));
277 /**
278  * Destructor
279  */
280 PageSizer::~PageSizer()
286 /**
287  * Initialize or reset this widget
288  */
289 void
290 PageSizer::init ()
292     _landscape_connection = _landscapeButton.signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_landscape));
293     _portrait_connection = _portraitButton.signal_toggled().connect (sigc::mem_fun (*this, &PageSizer::on_portrait));
294     _changedw_connection = _dimensionWidth.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
295     _changedh_connection = _dimensionHeight.signal_value_changed().connect (sigc::mem_fun (*this, &PageSizer::on_value_changed));
296     _fitPageButton.signal_clicked().connect(sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
298     show_all_children();
302 /**
303  * Set document dimensions (if not called by Doc prop's update()) and
304  * set the PageSizer's widgets and text entries accordingly. If
305  * 'chageList' is true, then adjust the paperSizeList to show the closest
306  * standard page size.
307  *
308  * \param w, h given in px
309  * \param changeList whether to modify the paper size list
310  */
311 void
312 PageSizer::setDim (double w, double h, bool changeList)
314     static bool _called = false;
315     if (_called) {
316         return;
317     }
319     _called = true;
321     _paper_size_list_connection.block();
322     _landscape_connection.block();
323     _portrait_connection.block();
324     _changedw_connection.block();
325     _changedh_connection.block();
327     if (SP_ACTIVE_DESKTOP && !_widgetRegistry->isUpdating()) {
328         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
329         sp_document_set_width (doc, w, &_px_unit);
330         sp_document_set_height (doc, h, &_px_unit);
331         sp_document_done (doc, SP_VERB_NONE, _("Set page size"));
332     }
334     _landscape = ( w > h );
335     _landscapeButton.set_active(_landscape ? true : false);
336     _portraitButton.set_active (_landscape ? false : true);
338     if (changeList)
339         {
340         Gtk::TreeModel::Row row = (*find_paper_size(w, h));
341         if (row)
342             _paperSizeListSelection->select(row);
343         }
345     Unit const& unit = _dimensionUnits.getUnit();
346     _dimensionWidth.setValue (w / unit.factor);
347     _dimensionHeight.setValue (h / unit.factor);
349     _paper_size_list_connection.unblock();
350     _landscape_connection.unblock();
351     _portrait_connection.unblock();
352     _changedw_connection.unblock();
353     _changedh_connection.unblock();
355     _called = false;
359 /**
360  * Returns an iterator pointing to a row in paperSizeListStore which
361  * contains a paper of the specified size (specified in px), or
362  * paperSizeListStore->children().end() if no such paper exists.
363  */
364 Gtk::ListStore::iterator
365 PageSizer::find_paper_size (double w, double h) const
367     double smaller = w;
368     double larger  = h;
369     if ( h < w ) {
370         smaller = h; larger = w;
371     }
373     g_return_val_if_fail(smaller <= larger, _paperSizeListStore->children().end());
375     std::map<Glib::ustring, PaperSize>::const_iterator iter;
376     for (iter = _paperSizeTable.begin() ;
377          iter != _paperSizeTable.end() ; iter++) {
378         PaperSize paper = iter->second;
379         SPUnit const &i_unit = sp_unit_get_by_id(paper.unit);
380         double smallX = sp_units_get_pixels(paper.smaller, i_unit);
381         double largeX = sp_units_get_pixels(paper.larger,  i_unit);
383         g_return_val_if_fail(smallX <= largeX, _paperSizeListStore->children().end());
385         if ((std::abs(smaller - smallX) <= 0.1) &&
386             (std::abs(larger  - largeX) <= 0.1)   ) {
387             Gtk::ListStore::iterator p;
388             // We need to search paperSizeListStore explicitly for the
389             // specified paper size because it is sorted in a different
390             // way than paperSizeTable (which is sorted alphabetically)
391             for (p = _paperSizeListStore->children().begin(); p != _paperSizeListStore->children().end(); p++) {
392                 if ((*p)[_paperSizeListColumns.nameColumn] == paper.name) {
393                     return p;
394                 }
395             }
396         }
397     }
398     return _paperSizeListStore->children().end();
403 /**
404  * Tell the desktop to change the page size
405  */
406 void
407 PageSizer::fire_fit_canvas_to_selection_or_drawing()
409     SPDesktop *dt = SP_ACTIVE_DESKTOP;
410     if (!dt) {
411         return;
412     }
413     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
414     if (verb) {
415         SPAction *action = verb->get_action(dt);
416         if (action)
417             sp_action_perform(action, NULL);
418     }
423 /**
424  * Paper Size list callback for when a user changes the selection
425  */
426 void
427 PageSizer::on_paper_size_list_changed()
429     //Glib::ustring name = _paperSizeList.get_active_text();
430     Gtk::TreeModel::iterator miter = _paperSizeListSelection->get_selected();
431     if(!miter)
432         {
433         //error?
434         return;
435         }
436     Gtk::TreeModel::Row row = *miter;
437     Glib::ustring name = row[_paperSizeListColumns.nameColumn];
438     std::map<Glib::ustring, PaperSize>::const_iterator piter =
439                     _paperSizeTable.find(name);
440     if (piter == _paperSizeTable.end()) {
441         g_warning("paper size '%s' not found in table", name.c_str());
442         return;
443     }
444     PaperSize paper = piter->second;
445     double w = paper.smaller;
446     double h = paper.larger;
447     SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
448     sp_convert_distance (&w, &src_unit, &_px_unit);
449     sp_convert_distance (&h, &src_unit, &_px_unit);
451     if (_landscape)
452         setDim (h, w, false);
453     else
454         setDim (w, h, false);
459 /**
460  * Portrait button callback
461  */
462 void
463 PageSizer::on_portrait()
465     if (!_portraitButton.get_active())
466         return;
467     double w = _dimensionWidth.getValue ("px");
468     double h = _dimensionHeight.getValue ("px");
469     if (h < w) {
470         setDim (h, w);
471     }
475 /**
476  * Landscape button callback
477  */
478 void
479 PageSizer::on_landscape()
481     if (!_landscapeButton.get_active())
482         return;
483     double w = _dimensionWidth.getValue ("px");
484     double h = _dimensionHeight.getValue ("px");
485     if (w < h) {
486         setDim (h, w);
487     }
490 /**
491  * Callback for the dimension widgets
492  */
493 void
494 PageSizer::on_value_changed()
496     if (_widgetRegistry->isUpdating()) return;
498     setDim (_dimensionWidth.getValue("px"),
499             _dimensionHeight.getValue("px"));
503 } // namespace Widget
504 } // namespace UI
505 } // namespace Inkscape
507 /*
508   Local Variables:
509   mode:c++
510   c-file-style:"stroustrup"
511   c-file-offsets:((innamespace . 0)(inline-open . 0))
512   indent-tabs-mode:nil
513   fill-column:99
514   End:
515 */
516 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :