Code

ac622ba41a9800117ab965bdab036ec0b86bf165
[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() : Gtk::VBox(false,4)
192     //# Set up the Paper Size combo box    
193     _paperSizeListStore = Gtk::ListStore::create(_paperSizeListColumns);
194     _paperSizeList.set_model(_paperSizeListStore);
195     _paperSizeList.append_column(_("Name"),
196                  _paperSizeListColumns.nameColumn);
197     _paperSizeList.append_column(_("Description"),
198                  _paperSizeListColumns.descColumn);
199         _paperSizeList.set_headers_visible(false);
200     _paperSizeListSelection = _paperSizeList.get_selection();
201     _paper_size_list_connection = 
202                 _paperSizeListSelection->signal_changed().connect (
203                 sigc::mem_fun (*this, &PageSizer::on_paper_size_list_changed));
204         _paperSizeListScroller.add(_paperSizeList);
205         _paperSizeListScroller.set_shadow_type(Gtk::SHADOW_IN);
206         _paperSizeListScroller.set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_ALWAYS);
207         _paperSizeListScroller.set_size_request(-1, 90);
208                 
209     for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
210             {
211         Glib::ustring name = p->name;
212         char formatBuf[80];
213         snprintf(formatBuf, 79, "%0.1f x %0.1f", p->smaller, p->larger);
214         Glib::ustring desc = formatBuf;
215         if (p->unit == SP_UNIT_IN)
216             desc.append(" in");
217         else if (p->unit == SP_UNIT_MM)
218              desc.append(" mm");
219         else if (p->unit == SP_UNIT_PX)
220             desc.append(" px");
221         PaperSize paper(name, p->smaller, p->larger, p->unit);
222         _paperSizeTable[name] = paper;
223         Gtk::TreeModel::Row row = *(_paperSizeListStore->append());
224         row[_paperSizeListColumns.nameColumn] = name;
225         row[_paperSizeListColumns.descColumn] = desc;
226         }
227     //Gtk::TreeModel::iterator iter = _paperSizeListStore->children().begin();
228     //if (iter)
229     //    _paperSizeListSelection->select(iter);
232     pack_start (_paperSizeListBox, false, false, 0);
233     _paperSizeListLabel.set_label(_("P_age size:"));
234     _paperSizeListLabel.set_use_underline();
235     _paperSizeListBox.pack_start (_paperSizeListLabel, false, false, 0);
236     _paperSizeListLabel.set_mnemonic_widget (_paperSizeList);
237     _paperSizeListBox.pack_start (_paperSizeListScroller, true, true, 0);
239     //## Set up orientation radio buttons
240     pack_start (_orientationBox, false, false, 0);
241     _orientationLabel.set_label(_("Page orientation:")); 
242     _orientationBox.pack_start(_orientationLabel, false, false, 0);
243     _landscapeButton.set_label(_("_Landscape"));
244         _landscapeButton.set_active(true);
245     Gtk::RadioButton::Group group = _landscapeButton.get_group();
246     _orientationBox.pack_end (_landscapeButton, false, false, 5);
247     _portraitButton.set_label(_("_Portrait"));
248         _portraitButton.set_active(true);
249     _orientationBox.pack_end (_portraitButton, false, false, 5);
250     _portraitButton.set_group (group);
251     _portraitButton.set_active (true);
252     
253     //## Set up custom size frame
254     _customFrame.set_label(_("Custom size"));
255     pack_start (_customFrame, false, false, 0);
256     _customTable.resize(2, 2);
257     _customTable.set_border_width (4);
258     _customTable.set_row_spacings (4);
259     _customTable.set_col_spacings (4);
260     _customFrame.add(_customTable);
261     
262     _fitPageButton.set_label(_("_Fit page to selection"));
263     _tips.set_tip(_fitPageButton, 
264         _("Resize the page to fit the current selection, or the entire drawing if there is no selection"));
271 /**
272  * Destructor
273  */ 
274 PageSizer::~PageSizer()
280 /**
281  * Initialize or reset this widget
282  */ 
283 void
284 PageSizer::init (Registry& reg)
287     /*
288     Note that the registered widgets can only be placed onto a
289     container after they have been init()-ed.  That is why some
290     of the widget creation is in the constructor, and the rest is
291     here.
292     */
293     
294     _widgetRegistry = &reg;
296     _dimensionUnits.init (_("U_nits:"), "units",
297                          *_widgetRegistry);
298     _dimensionWidth.init (_("_Width:"), _("Width of paper"), "width",
299                          _dimensionUnits, *_widgetRegistry);
300     _dimensionHeight.init (_("_Height:"), _("Height of paper"), "height",
301                          _dimensionUnits, *_widgetRegistry);
303     _customTable.attach(*(_dimensionWidth.getSU()),  0,1,0,1);
304     _customTable.attach(*(_dimensionUnits._sel),     1,2,0,1);
305     _customTable.attach(*(_dimensionHeight.getSU()), 0,1,1,2);
306     _customTable.attach(_fitPageButton,              1,2,1,2);
308     _landscape_connection = _landscapeButton.signal_toggled().connect (
309                 sigc::mem_fun (*this, &PageSizer::on_landscape));
310     _portrait_connection = _portraitButton.signal_toggled().connect (
311                 sigc::mem_fun (*this, &PageSizer::on_portrait));
312     _changedw_connection = _dimensionWidth.getSU()->signal_value_changed().connect (
313                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
314     _changedh_connection = _dimensionHeight.getSU()->signal_value_changed().connect (
315                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
316     _fitPageButton.signal_clicked().connect(
317              sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
318     
319     show_all_children();
324 /**
325  * Set document dimensions (if not called by Doc prop's update()) and
326  * set the PageSizer's widgets and text entries accordingly. If
327  * 'chageList' is true, then adjust the paperSizeList to show the closest
328  * standard page size.  
329  *
330  * \param w, h given in px
331  * \param changeList whether to modify the paper size list 
332  */
333 void
334 PageSizer::setDim (double w, double h, bool changeList)
336     static bool _called = false;
337     if (_called)
338             return;
340     _called = true;
341     
342     _paper_size_list_connection.block();
343         _landscape_connection.block();
344     _portrait_connection.block(); 
345     _changedw_connection.block();
346     _changedh_connection.block();
348     if (SP_ACTIVE_DESKTOP && !_widgetRegistry->isUpdating()) {
349         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
350         sp_document_set_width (doc, w, &_px_unit);
351         sp_document_set_height (doc, h, &_px_unit);
352         sp_document_done (doc, SP_VERB_NONE, _("Set page size"));
353     } 
354     
355     _landscape = ( w > h );
356     _landscapeButton.set_active(_landscape ? true : false);
357     _portraitButton.set_active (_landscape ? false : true);
358     
359     if (changeList)
360         {
361         int index = find_paper_size(w, h);
362         Gtk::TreeModel::Row row = _paperSizeListStore->children()[index];
363         if (row)
364             _paperSizeListSelection->select(row);
365         }
366     
367     Unit const& unit = _dimensionUnits._sel->getUnit();
368     _dimensionWidth.setValue (w / unit.factor);
369     _dimensionHeight.setValue (h / unit.factor);
371     _paper_size_list_connection.unblock();
372         _landscape_connection.unblock();
373     _portrait_connection.unblock();
374     _changedw_connection.unblock();
375     _changedh_connection.unblock();
376     
377     _called = false;
381 /** 
382  * Returns an index into paperSizeTable of a paper of the specified 
383  * size (specified in px), or -1 if there's no such paper.
384  */
385 int
386 PageSizer::find_paper_size (double w, double h) const
388     double smaller = w;
389     double larger  = h;
390     if ( h < w ) {
391         smaller = h; larger = w;
392     }
394     g_return_val_if_fail(smaller <= larger, -1);
395     
396     int index = 0;
397     std::map<Glib::ustring, PaperSize>::const_iterator iter;
398     for (iter = _paperSizeTable.begin() ;
399              iter != _paperSizeTable.end() ; iter++) {
400         PaperSize paper = iter->second;
401         SPUnit const &i_unit = sp_unit_get_by_id(paper.unit);
402         double smallX = sp_units_get_pixels(paper.smaller, i_unit);
403         double largeX = sp_units_get_pixels(paper.larger,  i_unit);
404         
405         g_return_val_if_fail(smallX <= largeX, -1);
406         
407         if ((std::abs(smaller - smallX) <= 0.1) &&
408             (std::abs(larger  - largeX) <= 0.1)   )
409             return index;
410             
411         index++;
412     }
413     return -1;
418 /**
419  * Tell the desktop to change the page size
420  */ 
421 void
422 PageSizer::fire_fit_canvas_to_selection_or_drawing()
424     SPDesktop *dt = SP_ACTIVE_DESKTOP;
425     if (!dt)
426             return;
427     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
428     if (verb) {
429         SPAction *action = verb->get_action(dt);
430         if (action)
431             sp_action_perform(action, NULL);        
432     }
437 /**
438  * Paper Size list callback for when a user changes the selection
439  */ 
440 void
441 PageSizer::on_paper_size_list_changed()
443     //Glib::ustring name = _paperSizeList.get_active_text();
444     Gtk::TreeModel::iterator miter = _paperSizeListSelection->get_selected();
445     if(!miter)
446         {
447         //error?
448         return;
449         }
450     Gtk::TreeModel::Row row = *miter;
451     Glib::ustring name = row[_paperSizeListColumns.nameColumn];
452     std::map<Glib::ustring, PaperSize>::const_iterator piter =
453                     _paperSizeTable.find(name);
454     if (piter == _paperSizeTable.end()) {
455         g_warning("paper size '%s' not found in table", name.c_str());
456         return;
457     }
458     PaperSize paper = piter->second;
459     double w = paper.smaller;
460     double h = paper.larger;
461     SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
462     sp_convert_distance (&w, &src_unit, &_px_unit);
463     sp_convert_distance (&h, &src_unit, &_px_unit);
465     if (_landscape)
466         setDim (h, w, false);
467     else
468         setDim (w, h, false);
473 /**
474  * Portrait button callback
475  */
476 void
477 PageSizer::on_portrait()
479     if (!_portraitButton.get_active())
480         return;
481     double w = _dimensionWidth.getSU()->getValue ("px");
482     double h = _dimensionHeight.getSU()->getValue ("px");
483     if (h < w)
484             setDim (h, w);
488 /**
489  * Landscape button callback
490  */ 
491 void
492 PageSizer::on_landscape()
494     if (!_landscapeButton.get_active())
495         return;
496     double w = _dimensionWidth.getSU()->getValue ("px");
497     double h = _dimensionHeight.getSU()->getValue ("px");
498     if (w < h)
499             setDim (h, w);
502 /**
503  * Callback for the dimension widgets
504  */ 
505 void
506 PageSizer::on_value_changed()
508     if (_widgetRegistry->isUpdating()) return;
510     setDim (_dimensionWidth.getSU()->getValue("px"),
511                 _dimensionHeight.getSU()->getValue("px"));
515 } // namespace Widget
516 } // namespace UI
517 } // namespace Inkscape
519 /*
520   Local Variables:
521   mode:c++
522   c-file-style:"stroustrup"
523   c-file-offsets:((innamespace . 0)(inline-open . 0))
524   indent-tabs-mode:nil
525   fill-column:99
526   End:
527 */
528 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :