Code

ac78c5279d4248bda2df14b17d764aa7f32e3953
[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(_paperSizeColumns);
194     _paperSizeList.set_model(_paperSizeListStore);
195     _paperSizeList.append_column(_("Name"),
196                  _paperSizeColumns.nameColumn);
197     _paperSizeList.append_column(_("Description"),
198                  _paperSizeColumns.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                 
208     for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
209             {
210         Glib::ustring name = p->name;
211         char formatBuf[80];
212         snprintf(formatBuf, 79, "%0.2f x %0.2f", p->smaller, p->larger);
213         Glib::ustring desc = formatBuf;
214         if (p->unit == SP_UNIT_IN)
215             desc.append(" in");
216         else if (p->unit == SP_UNIT_MM)
217              desc.append(" mm");
218         else if (p->unit == SP_UNIT_PX)
219             desc.append(" px");
220         PaperSize paper(name, p->smaller, p->larger, p->unit);
221         _paperSizeTable[name] = paper;
222         Gtk::TreeModel::Row row = *(_paperSizeListStore->append());
223         row[_paperSizeColumns.nameColumn] = name;
224         row[_paperSizeColumns.descColumn] = desc;
225         }
226     //Gtk::TreeModel::Row row = _paperSizeListStore->children()[0];
227     //if (row)
228     //    _paperSizeListSelection->select(row);
231     Gtk::HBox *hbox_size = manage (new Gtk::HBox (false, 4));
232     pack_start (*hbox_size, false, false, 0);
233     Gtk::Label *label_size = manage (new Gtk::Label (_("P_age size:"), 1.0, 0.5)); 
234     label_size->set_use_underline();
235     hbox_size->pack_start (*label_size, false, false, 0);
236     label_size->set_mnemonic_widget (_paperSizeList);
237     hbox_size->pack_start (_paperSizeListScroller, true, true, 0);
242 /**
243  * Destructor
244  */ 
245 PageSizer::~PageSizer()
251 /**
252  * Initialize or reset this widget
253  */ 
254 void
255 PageSizer::init (Registry& reg)
258     Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
259     pack_start (*hbox_ori, false, false, 0);
260     Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5)); 
261     hbox_ori->pack_start (*label_ori, false, false, 0);
262     
263     _landscapeButton.set_label(_("_Landscape"));
264         _landscapeButton.set_active(true);
265     Gtk::RadioButton::Group group = _landscapeButton.get_group();
266     hbox_ori->pack_end (_landscapeButton, false, false, 5);
267     _portraitButton.set_label(_("_Portrait"));
268         _portraitButton.set_active(true);
269     hbox_ori->pack_end (_portraitButton, false, false, 5);
270     _portraitButton.set_group (group);
271     _portraitButton.set_active (true);
272     
273     /* Custom paper frame */
274     Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
275     pack_start (*frame, false, false, 0);
276     Gtk::Table *table = manage (new Gtk::Table (5, 2, false));
277     table->set_border_width (4);
278     table->set_row_spacings (4);
279     table->set_col_spacings (4);
280     
281     Inkscape::UI::Widget::Button* fit_canv =
282              manage(new Inkscape::UI::Widget::Button(_("_Fit page to selection"),
283     _("Resize the page to fit the current selection, or the entire drawing if there is no selection")));
285     // prevent fit_canv from expanding
286     Gtk::Alignment *fit_canv_cont = manage(new Gtk::Alignment(1.0,0.5,0.0,0.0));
287     fit_canv_cont->add(*fit_canv);
289     frame->add (*table);
290     
291     _widgetRegistry = &reg;
293     _dimensionUnits.init (_("U_nits:"), "units",
294                          *_widgetRegistry);
295     _dimensionWidth.init (_("_Width:"), _("Width of paper"), "width",
296                          _dimensionUnits, *_widgetRegistry);
297     _dimensionHeight.init (_("_Height:"), _("Height of paper"), "height",
298                          _dimensionUnits, *_widgetRegistry);
300     table->attach (*_dimensionUnits._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND,
301                     (Gtk::AttachOptions)0,0,0);
302     table->attach (*_dimensionUnits._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND,
303                     (Gtk::AttachOptions)0,0,0);
304     table->attach (*_dimensionWidth.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND,
305                     (Gtk::AttachOptions)0,0,0);
306     table->attach (*_dimensionHeight.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND,
307                    (Gtk::AttachOptions)0,0,0);
308     table->attach (*fit_canv_cont, 0,2,3,4, Gtk::FILL|Gtk::EXPAND,
309                    (Gtk::AttachOptions)0,0,0);
311     _landscape_connection = _landscapeButton.signal_toggled().connect (
312                 sigc::mem_fun (*this, &PageSizer::on_landscape));
313     _portrait_connection = _portraitButton.signal_toggled().connect (
314                 sigc::mem_fun (*this, &PageSizer::on_portrait));
315     _changedw_connection = _dimensionWidth.getSU()->signal_value_changed().connect (
316                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
317     _changedh_connection = _dimensionHeight.getSU()->signal_value_changed().connect (
318                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
319     fit_canv->signal_clicked().connect(
320              sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
321     
322     show_all_children();
326 /**
327  * Set document dimensions (if not called by Doc prop's update()) and
328  * set the PageSizer's widgets and text entries accordingly. If
329  * 'chageList' is true, then adjust the paperSizeList to show the closest
330  * standard page size.  
331  *
332  * \param w, h given in px
333  * \param changeList whether to modify the paper size list 
334  */
335 void
336 PageSizer::setDim (double w, double h, bool changeList)
338     static bool _called = false;
339     if (_called)
340             return;
342     _called = true;
343     
344     _paper_size_list_connection.block();
345         _landscape_connection.block();
346     _portrait_connection.block(); 
347     _changedw_connection.block();
348     _changedh_connection.block();
350     if (SP_ACTIVE_DESKTOP && !_widgetRegistry->isUpdating()) {
351         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
352         sp_document_set_width (doc, w, &_px_unit);
353         sp_document_set_height (doc, h, &_px_unit);
354         sp_document_done (doc, SP_VERB_NONE, 
355         /* TODO: annotate */ "page-sizer.cpp:301");
356     } 
357     
358     _landscape = ( w > h );
359     _landscapeButton.set_active(_landscape ? true : false);
360     _portraitButton.set_active (_landscape ? false : true);
361     
362     if (changeList)
363         {
364         int index = find_paper_size(w, h);
365         Gtk::TreeModel::Row row = _paperSizeListStore->children()[index];
366         if (row)
367             _paperSizeListSelection->select(row);
368         }
369     
370     Unit const& unit = _dimensionUnits._sel->getUnit();
371     _dimensionWidth.setValue (w / unit.factor);
372     _dimensionHeight.setValue (h / unit.factor);
374     _paper_size_list_connection.unblock();
375         _landscape_connection.unblock();
376     _portrait_connection.unblock();
377     _changedw_connection.unblock();
378     _changedh_connection.unblock();
379     
380     _called = false;
384 /** 
385  * Returns an index into paperSizeTable of a paper of the specified 
386  * size (specified in px), or -1 if there's no such paper.
387  */
388 int
389 PageSizer::find_paper_size (double w, double h) const
391     double smaller = w;
392     double larger  = h;
393     if ( h < w ) {
394         smaller = h; larger = w;
395     }
397     g_return_val_if_fail(smaller <= larger, -1);
398     
399     int index = 0;
400     std::map<Glib::ustring, PaperSize>::const_iterator iter;
401     for (iter = _paperSizeTable.begin() ;
402              iter != _paperSizeTable.end() ; iter++) {
403         PaperSize paper = iter->second;
404         SPUnit const &i_unit = sp_unit_get_by_id(paper.unit);
405         double smallX = sp_units_get_pixels(paper.smaller, i_unit);
406         double largeX = sp_units_get_pixels(paper.larger,  i_unit);
407         
408         g_return_val_if_fail(smallX <= largeX, -1);
409         
410         if ((std::abs(smaller - smallX) <= 0.1) &&
411             (std::abs(larger  - largeX) <= 0.1)   )
412             return index;
413             
414         index++;
415     }
416     return -1;
421 /**
422  * Tell the desktop to change the page size
423  */ 
424 void
425 PageSizer::fire_fit_canvas_to_selection_or_drawing()
427     SPDesktop *dt = SP_ACTIVE_DESKTOP;
428     if (!dt)
429             return;
430     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
431     if (verb) {
432         SPAction *action = verb->get_action(dt);
433         if (action)
434             sp_action_perform(action, NULL);        
435     }
440 /**
441  * Paper Size list callback for when a user changes the selection
442  */ 
443 void
444 PageSizer::on_paper_size_list_changed()
446     //Glib::ustring name = _paperSizeList.get_active_text();
447     Gtk::TreeModel::iterator miter = _paperSizeListSelection->get_selected();
448     if(!miter)
449         {
450         //error?
451         return;
452         }
453     Gtk::TreeModel::Row row = *miter;
454     Glib::ustring name = row[_paperSizeColumns.nameColumn];
455     std::map<Glib::ustring, PaperSize>::const_iterator piter =
456                     _paperSizeTable.find(name);
457     if (piter == _paperSizeTable.end()) {
458         g_warning("paper size '%s' not found in table", name.c_str());
459         return;
460     }
461     PaperSize paper = piter->second;
462     double w = paper.smaller;
463     double h = paper.larger;
464     SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
465     sp_convert_distance (&w, &src_unit, &_px_unit);
466     sp_convert_distance (&h, &src_unit, &_px_unit);
468     if (_landscape)
469         setDim (h, w, false);
470     else
471         setDim (w, h, false);
476 /**
477  * Portrait button callback
478  */
479 void
480 PageSizer::on_portrait()
482     if (!_portraitButton.get_active())
483         return;
484     double w = _dimensionWidth.getSU()->getValue ("px");
485     double h = _dimensionHeight.getSU()->getValue ("px");
486     if (h < w)
487             setDim (h, w);
491 /**
492  * Landscape button callback
493  */ 
494 void
495 PageSizer::on_landscape()
497     if (!_landscapeButton.get_active())
498         return;
499     double w = _dimensionWidth.getSU()->getValue ("px");
500     double h = _dimensionHeight.getSU()->getValue ("px");
501     if (w < h)
502             setDim (h, w);
505 /**
506  * Callback for the dimension widgets
507  */ 
508 void
509 PageSizer::on_value_changed()
511     if (_widgetRegistry->isUpdating()) return;
513     setDim (_dimensionWidth.getSU()->getValue("px"),
514                 _dimensionHeight.getSU()->getValue("px"));
518 } // namespace Widget
519 } // namespace UI
520 } // namespace Inkscape
522 /*
523   Local Variables:
524   mode:c++
525   c-file-style:"stroustrup"
526   c-file-offsets:((innamespace . 0)(inline-open . 0))
527   indent-tabs-mode:nil
528   fill-column:99
529   End:
530 */
531 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :