Code

d657b909640f8383b099dbb91f94c1bf1833b8d5
[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 <cmath>
22 #include <gtkmm.h>
23 #include "ui/widget/button.h"
25 #include "ui/widget/scalar-unit.h"
27 #include "helper/units.h"
28 #include "inkscape.h"
29 #include "verbs.h"
30 #include "desktop-handles.h"
31 #include "document.h"
32 #include "desktop.h"
33 #include "page-sizer.h"
34 #include "helper/action.h"
36 using std::pair;
38 namespace Inkscape {
39 namespace UI {
40 namespace Widget {
42     /** \note
43      * The ISO page sizes in the table below differ from ghostscript's idea of page sizes (by
44      * less than 1pt).  Being off by <1pt should be OK for most purposes, but may cause fuzziness
45      * (antialiasing) problems when printing to 72dpi or 144dpi printers or bitmap files due to
46      * postscript's different coordinate system (y=0 meaning bottom of page in postscript and top
47      * of page in SVG).  I haven't looked into whether this does in fact cause fuzziness, I merely
48      * note the possibility.  Rounding done by extension/internal/ps.cpp (e.g. floor/ceil calls)
49      * will also affect whether fuzziness occurs.
50      *
51      * The remainder of this comment discusses the origin of the numbers used for ISO page sizes in
52      * this table and in ghostscript.
53      *
54      * The versions here, in mm, are the official sizes according to
55      * <a href="http://en.wikipedia.org/wiki/Paper_sizes">http://en.wikipedia.org/wiki/Paper_sizes</a> 
56      * at 2005-01-25.  (The ISO entries in the below table
57      * were produced mechanically from the table on that page.)
58      *
59      * (The rule seems to be that A0, B0, ..., D0. sizes are rounded to the nearest number of mm
60      * from the "theoretical size" (i.e. 1000 * sqrt(2) or pow(2.0, .25) or the like), whereas
61      * going from e.g. A0 to A1 always take the floor of halving -- which by chance coincides
62      * exactly with flooring the "theoretical size" for n != 0 instead of the rounding to nearest
63      * done for n==0.)
64      *
65      * Ghostscript paper sizes are given in gs_statd.ps according to gs(1).  gs_statd.ps always
66      * uses an integer number of pt: sometimes gs_statd.ps rounds to nearest (e.g. a1), sometimes
67      * floors (e.g. a10), sometimes ceils (e.g. a8).
68      *
69      * I'm not sure how ghostscript's gs_statd.ps was calculated: it isn't just rounding the
70      * "theoretical size" of each page to pt (see a0), nor is it rounding the a0 size times an
71      * appropriate power of two (see a1).  Possibly it was prepared manually, with a human applying
72      * inconsistent rounding rules when converting from mm to pt.
73      */
74     /** \todo
75      * Should we include the JIS B series (used in Japan)  
76      * (JIS B0 is sometimes called JB0, and similarly for JB1 etc)?
77      * Should we exclude B7--B10 and A7--10 to make the list smaller ?
78      * Should we include any of the ISO C, D and E series (see below) ?
79      */
81 struct PaperSizeRec {
82     char const * const name;  //name
83     double const smaller;     //lesser dimension
84     double const larger;      //greater dimension
85     SPUnitId const unit;      //units
86 };
88 static PaperSizeRec const inkscape_papers[] = {
89     { "A4",                210,  297, SP_UNIT_MM },
90     { "US Letter",         8.5,   11, SP_UNIT_IN },
91     { "US Legal",          8.5,   14, SP_UNIT_IN },
92     { "US Executive",     7.25, 10.5, SP_UNIT_IN },
93     { "A0",                841, 1189, SP_UNIT_MM },
94     { "A1",                594,  841, SP_UNIT_MM },
95     { "A2",                420,  594, SP_UNIT_MM },
96     { "A3",                297,  420, SP_UNIT_MM },
97     { "A5",                148,  210, SP_UNIT_MM },
98     { "A6",                105,  148, SP_UNIT_MM },
99     { "A7",                 74,  105, SP_UNIT_MM },
100     { "A8",                 52,   74, SP_UNIT_MM },
101     { "A9",                 37,   52, SP_UNIT_MM },
102     { "A10",                26,   37, SP_UNIT_MM },
103     { "B0",               1000, 1414, SP_UNIT_MM },
104     { "B1",                707, 1000, SP_UNIT_MM },
105     { "B2",                500,  707, SP_UNIT_MM },
106     { "B3",                353,  500, SP_UNIT_MM },
107     { "B4",                250,  353, SP_UNIT_MM },
108     { "B5",                176,  250, SP_UNIT_MM },
109     { "B6",                125,  176, SP_UNIT_MM },
110     { "B7",                 88,  125, SP_UNIT_MM },
111     { "B8",                 62,   88, SP_UNIT_MM },
112     { "B9",                 44,   62, SP_UNIT_MM },
113     { "B10",                31,   44, SP_UNIT_MM },
115 #if 0 /* 
116          Whether to include or exclude these depends on how
117          big we mind our page size menu
118          becoming.  C series is used for envelopes;
119                  don't know what D and E series are used for. */
120     { "C0",                917, 1297, SP_UNIT_MM },
121     { "C1",                648,  917, SP_UNIT_MM },
122     { "C2",                458,  648, SP_UNIT_MM },
123     { "C3",                324,  458, SP_UNIT_MM },
124     { "C4",                229,  324, SP_UNIT_MM },
125     { "C5",                162,  229, SP_UNIT_MM },
126     { "C6",                114,  162, SP_UNIT_MM },
127     { "C7",                 81,  114, SP_UNIT_MM },
128     { "C8",                 57,   81, SP_UNIT_MM },
129     { "C9",                 40,   57, SP_UNIT_MM },
130     { "C10",                28,   40, SP_UNIT_MM },
131     { "D1",                545,  771, SP_UNIT_MM },
132     { "D2",                385,  545, SP_UNIT_MM },
133     { "D3",                272,  385, SP_UNIT_MM },
134     { "D4",                192,  272, SP_UNIT_MM },
135     { "D5",                136,  192, SP_UNIT_MM },
136     { "D6",                 96,  136, SP_UNIT_MM },
137     { "D7",                 68,   96, SP_UNIT_MM },
138     { "E3",                400,  560, SP_UNIT_MM },
139     { "E4",                280,  400, SP_UNIT_MM },
140     { "E5",                200,  280, SP_UNIT_MM },
141     { "E6",                140,  200, SP_UNIT_MM },
142 #endif
144     { "CSE",               462,  649, SP_UNIT_PT },
145     { "US #10 Envelope", 4.125,  9.5, SP_UNIT_IN },
146          // TODO: Select landscape by default.
147     /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
148        sizes. */
149     { "DL Envelope",       110,  220, SP_UNIT_MM },
150          // TODO: Select landscape by default.
151     { "Ledger/Tabloid",     11,   17, SP_UNIT_IN },
152     /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
153        means different sizes to different people: different people may expect the width to be
154        either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
155        restricting our interpretation to foolscap folio.  If you wish to introduce a folio-like
156        page size to the list, then please consider using a name more specific than just `Folio' or
157        `Foolscap Folio'. */
158     { "Banner 468x60",      60,  468, SP_UNIT_PX },
159           // TODO: Select landscape by default.
160     { "Icon 16x16",         16,   16, SP_UNIT_PX },
161     { "Icon 32x32",         32,   32, SP_UNIT_PX },
162     { "Icon 48x48",         48,   48, SP_UNIT_PX },
163     { NULL,                  0,    0, SP_UNIT_PX },
164 };
168 //########################################################################
169 //# P A G E    S I Z E R
170 //########################################################################
172 //The default unit for this widget and its calculations
173 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
176 /**
177  * Constructor
178  */ 
179 PageSizer::PageSizer() : Gtk::VBox(false,4)
181     Gtk::HBox *hbox_size = manage (new Gtk::HBox (false, 4));
182     pack_start (*hbox_size, false, false, 0);
183     Gtk::Label *label_size = manage (new Gtk::Label (_("P_age size:"), 1.0, 0.5)); 
184     label_size->set_use_underline();
185     hbox_size->pack_start (*label_size, false, false, 0);
186     label_size->set_mnemonic_widget (_paperSizeList);
187     hbox_size->pack_start (_paperSizeList, true, true, 0);
189     //# Set up the Paper Size combo box
190     
191     for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
192             {
193         Glib::ustring name = p->name;
194         PaperSize paper(name, p->smaller, p->larger, p->unit);
195         paperSizeTable[name] = paper;
196         _paperSizeList.append_text(name);
197         }
202 /**
203  * Destructor
204  */ 
205 PageSizer::~PageSizer()
211 /**
212  * Initialize or reset this widget
213  */ 
214 void
215 PageSizer::init (Registry& reg)
217     Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
218     pack_start (*hbox_ori, false, false, 0);
219     Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5)); 
220     hbox_ori->pack_start (*label_ori, false, false, 0);
221     
222     _landscapeButton.set_label(_("_Landscape"));
223         _landscapeButton.set_active(true);
224     Gtk::RadioButton::Group group = _landscapeButton.get_group();
225     hbox_ori->pack_end (_landscapeButton, false, false, 5);
226     _portraitButton.set_label(_("_Portrait"));
227         _portraitButton.set_active(true);
228     hbox_ori->pack_end (_portraitButton, false, false, 5);
229     _portraitButton.set_group (group);
230     _portraitButton.set_active (true);
231     
232     /* Custom paper frame */
233     Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
234     pack_start (*frame, false, false, 0);
235     Gtk::Table *table = manage (new Gtk::Table (5, 2, false));
236     table->set_border_width (4);
237     table->set_row_spacings (4);
238     table->set_col_spacings (4);
239     
240     Inkscape::UI::Widget::Button* fit_canv =
241              manage(new Inkscape::UI::Widget::Button(_("_Fit page to selection"),
242     _("Resize the page to fit the current selection, or the entire drawing if there is no selection")));
244     // prevent fit_canv from expanding
245     Gtk::Alignment *fit_canv_cont = manage(new Gtk::Alignment(1.0,0.5,0.0,0.0));
246     fit_canv_cont->add(*fit_canv);
248     frame->add (*table);
249     
250     _widgetRegistry = &reg;
252     _dimensionUnits.init (_("U_nits:"), "units",
253                          *_widgetRegistry);
254     _dimensionWidth.init (_("_Width:"), _("Width of paper"), "width",
255                          _dimensionUnits, *_widgetRegistry);
256     _dimensionHeight.init (_("_Height:"), _("Height of paper"), "height",
257                          _dimensionUnits, *_widgetRegistry);
259     table->attach (*_dimensionUnits._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND,
260                     (Gtk::AttachOptions)0,0,0);
261     table->attach (*_dimensionUnits._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND,
262                     (Gtk::AttachOptions)0,0,0);
263     table->attach (*_dimensionWidth.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND,
264                     (Gtk::AttachOptions)0,0,0);
265     table->attach (*_dimensionHeight.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND,
266                    (Gtk::AttachOptions)0,0,0);
267     table->attach (*fit_canv_cont, 0,2,3,4, Gtk::FILL|Gtk::EXPAND,
268                    (Gtk::AttachOptions)0,0,0);
270     _paper_size_list_connection = _paperSizeList.signal_changed().connect (
271                 sigc::mem_fun (*this, &PageSizer::on_paper_size_list_changed));
272                 
273     _landscape_connection = _landscapeButton.signal_toggled().connect (
274                 sigc::mem_fun (*this, &PageSizer::on_landscape));
275     _portrait_connection = _portraitButton.signal_toggled().connect (
276                 sigc::mem_fun (*this, &PageSizer::on_portrait));
277     _changedw_connection = _dimensionWidth.getSU()->signal_value_changed().connect (
278                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
279     _changedh_connection = _dimensionHeight.getSU()->signal_value_changed().connect (
280                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
281     fit_canv->signal_clicked().connect(
282              sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
283     
284     show_all_children();
288 /**
289  * Set document dimensions (if not called by Doc prop's update()) and
290  * set the PageSizer's widgets and text entries accordingly. If
291  * 'chageList' is true, then adjust the paperSizeList to show the closest
292  * standard page size.  
293  *
294  * \param w, h given in px
295  * \param changeList whether to modify the paper size list 
296  */
297 void
298 PageSizer::setDim (double w, double h, bool changeList)
300     static bool _called = false;
301     if (_called)
302             return;
304     _called = true;
305     
306     _paper_size_list_connection.block();
307         _landscape_connection.block();
308     _portrait_connection.block(); 
309     _changedw_connection.block();
310     _changedh_connection.block();
312     if (SP_ACTIVE_DESKTOP && !_widgetRegistry->isUpdating()) {
313         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
314         sp_document_set_width (doc, w, &_px_unit);
315         sp_document_set_height (doc, h, &_px_unit);
316         sp_document_done (doc, SP_VERB_NONE, 
317         /* TODO: annotate */ "page-sizer.cpp:301");
318     } 
319     
320     _landscape = ( w > h );
321     _landscapeButton.set_active(_landscape ? true : false);
322     _portraitButton.set_active (_landscape ? false : true);
323     
324     if (changeList)
325             _paperSizeList.set_active (find_paper_size (w, h));
326     
327     Unit const& unit = _dimensionUnits._sel->getUnit();
328     _dimensionWidth.setValue (w / unit.factor);
329     _dimensionHeight.setValue (h / unit.factor);
331     _paper_size_list_connection.unblock();
332         _landscape_connection.unblock();
333     _portrait_connection.unblock();
334     _changedw_connection.unblock();
335     _changedh_connection.unblock();
336     
337     _called = false;
341 /** 
342  * Returns an index into paperSizeTable of a paper of the specified 
343  * size (specified in px), or -1 if there's no such paper.
344  */
345 int
346 PageSizer::find_paper_size (double w, double h) const
348     double smaller = w;
349     double larger  = h;
350     if ( h < w ) {
351         smaller = h; larger = w;
352     }
354     g_return_val_if_fail(smaller <= larger, -1);
355     
356     int index = 0;
357     std::map<Glib::ustring, PaperSize>::const_iterator iter;
358     for (iter = paperSizeTable.begin() ; iter != paperSizeTable.end() ; iter++) {
359         PaperSize paper = iter->second;
360         SPUnit const &i_unit = sp_unit_get_by_id(paper.unit);
361         double smallX = sp_units_get_pixels(paper.smaller, i_unit);
362         double largeX = sp_units_get_pixels(paper.larger,  i_unit);
363         
364         g_return_val_if_fail(smallX <= largeX, -1);
365         
366         if ((std::abs(smaller - smallX) <= 0.1) &&
367             (std::abs(larger  - largeX) <= 0.1)   )
368             return index;
369             
370         index++;
371     }
372     return -1;
377 /**
378  * Tell the desktop to change the page size
379  */ 
380 void
381 PageSizer::fire_fit_canvas_to_selection_or_drawing()
383     SPDesktop *dt = SP_ACTIVE_DESKTOP;
384     if (!dt)
385             return;
386     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
387     if (verb) {
388         SPAction *action = verb->get_action(dt);
389         if (action)
390             sp_action_perform(action, NULL);        
391     }
396 /**
397  * Paper Size list callback for when a user changes the selection
398  */ 
399 void
400 PageSizer::on_paper_size_list_changed()
402     Glib::ustring name = _paperSizeList.get_active_text();
403     std::map<Glib::ustring, PaperSize>::const_iterator iter =
404         paperSizeTable.find(name);
405     if (iter == paperSizeTable.end()) {
406         g_warning("paper size '%s' not found in table", name.c_str());
407         return;
408     }
409     PaperSize paper = iter->second;
410     double w = paper.smaller;
411     double h = paper.larger;
412     SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
413     sp_convert_distance (&w, &src_unit, &_px_unit);
414     sp_convert_distance (&h, &src_unit, &_px_unit);
416     if (_landscape)
417         setDim (h, w, false);
418     else
419         setDim (w, h, false);
424 /**
425  * Portrait button callback
426  */
427 void
428 PageSizer::on_portrait()
430     if (!_portraitButton.get_active())
431         return;
432     double w = _dimensionWidth.getSU()->getValue ("px");
433     double h = _dimensionHeight.getSU()->getValue ("px");
434     if (h < w)
435             setDim (h, w);
439 /**
440  * Landscape button callback
441  */ 
442 void
443 PageSizer::on_landscape()
445     if (!_landscapeButton.get_active())
446         return;
447     double w = _dimensionWidth.getSU()->getValue ("px");
448     double h = _dimensionHeight.getSU()->getValue ("px");
449     if (w < h)
450             setDim (h, w);
453 /**
454  * Callback for the dimension widgets
455  */ 
456 void
457 PageSizer::on_value_changed()
459     if (_widgetRegistry->isUpdating()) return;
461     setDim (_dimensionWidth.getSU()->getValue("px"),
462                 _dimensionHeight.getSU()->getValue("px"));
466 } // namespace Widget
467 } // namespace UI
468 } // namespace Inkscape
470 /*
471   Local Variables:
472   mode:c++
473   c-file-style:"stroustrup"
474   c-file-offsets:((innamespace . 0)(inline-open . 0))
475   indent-tabs-mode:nil
476   fill-column:99
477   End:
478 */
479 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :