Code

Change deprecated OptionMenu to ComboBox. Modernize lookup a bit
[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.h>
22 //#include <gtkmm/optionmenu.h>
23 //#include <gtkmm/frame.h>
24 //#include <gtkmm/table.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;
85     double const smaller;
86     double const larger;
87     SPUnitId const unit;
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 },
117 #if 0 /* Whether to include or exclude these depends on how big we mind our page size menu
118          becoming.  C series is used for envelopes; don't know what D and E series are used for. */
119     { "C0", 917, 1297, SP_UNIT_MM },
120     { "C1", 648, 917, SP_UNIT_MM },
121     { "C2", 458, 648, SP_UNIT_MM },
122     { "C3", 324, 458, SP_UNIT_MM },
123     { "C4", 229, 324, SP_UNIT_MM },
124     { "C5", 162, 229, SP_UNIT_MM },
125     { "C6", 114, 162, SP_UNIT_MM },
126     { "C7", 81, 114, SP_UNIT_MM },
127     { "C8", 57, 81, SP_UNIT_MM },
128     { "C9", 40, 57, SP_UNIT_MM },
129     { "C10", 28, 40, SP_UNIT_MM },
130     { "D1", 545, 771, SP_UNIT_MM },
131     { "D2", 385, 545, SP_UNIT_MM },
132     { "D3", 272, 385, SP_UNIT_MM },
133     { "D4", 192, 272, SP_UNIT_MM },
134     { "D5", 136, 192, SP_UNIT_MM },
135     { "D6", 96, 136, SP_UNIT_MM },
136     { "D7", 68, 96, SP_UNIT_MM },
137     { "E3", 400, 560, SP_UNIT_MM },
138     { "E4", 280, 400, SP_UNIT_MM },
139     { "E5", 200, 280, SP_UNIT_MM },
140     { "E6", 140, 200, SP_UNIT_MM },
141 #endif
143     { "CSE", 462, 649, SP_UNIT_PT },
144     { "US #10 Envelope", 4.125, 9.5, SP_UNIT_IN }, // TODO: Select landscape by default.
145     /* See http://www.hbp.com/content/PCR_envelopes.cfm for a much larger list of US envelope
146        sizes. */
147     { "DL Envelope", 110, 220, SP_UNIT_MM }, // TODO: Select landscape by default.
148     { "Ledger/Tabloid", 11, 17, SP_UNIT_IN },
149     /* Note that `Folio' (used in QPrinter/KPrinter) is deliberately absent from this list, as it
150        means different sizes to different people: different people may expect the width to be
151        either 8, 8.25 or 8.5 inches, and the height to be either 13 or 13.5 inches, even
152        restricting our interpretation to foolscap folio.  If you wish to introduce a folio-like
153        page size to the list, then please consider using a name more specific than just `Folio' or
154        `Foolscap Folio'. */
155     { "Banner 468x60", 60, 468, SP_UNIT_PX },  // TODO: Select landscape by default.
156     { "Icon 16x16", 16, 16, SP_UNIT_PX },
157     { "Icon 32x32", 32, 32, SP_UNIT_PX },
158     { NULL, 0, 0, SP_UNIT_PX },
159 };
161 //===================================================
164 static const SPUnit _px_unit = sp_unit_get_by_id (SP_UNIT_PX);
167 /*
168 class SizeMenuItem : public Gtk::MenuItem {
169 public:
170     SizeMenuItem (PaperSizeRec const * paper, PageSizer * widget)
171                 : Gtk::MenuItem (paper ? paper->name : _("Custom")), 
172                   _paper(paper),
173                       _parent(widget)
174           {}
175 protected:
176     PaperSizeRec const * _paper;
177     PageSizer       *_parent;
178     void            on_activate();
179 };
181 void
182 SizeMenuItem::on_activate()
184     if (_parent == 0) // handle Custom entry
185         return;
186         
187     double w = _paper->smaller, h = _paper->larger;
188     SPUnit const &src_unit = sp_unit_get_by_id (_paper->unit);
189     sp_convert_distance (&w, &src_unit, &_px_unit);
190     sp_convert_distance (&h, &src_unit, &_px_unit);
191     if (_parent->_landscape)
192         _parent->setDim (h, w);
193     else
194         _parent->setDim (w, h);
196 */
198 //---------------------------------------------------
204 PageSizer::PageSizer()
205 : Gtk::VBox(false,4)
207     Gtk::HBox *hbox_size = manage (new Gtk::HBox (false, 4));
208     pack_start (*hbox_size, false, false, 0);
209     Gtk::Label *label_size = manage (new Gtk::Label (_("P_age size:"), 1.0, 0.5)); 
210     label_size->set_use_underline();
211     hbox_size->pack_start (*label_size, false, false, 0);
212     label_size->set_mnemonic_widget (_paperSizeList);
213     hbox_size->pack_start (_paperSizeList, true, true, 0);
215     //# Set up the Paper Size combo box
216     
217     for (PaperSizeRec const *p = inkscape_papers; p->name; p++)
218             {
219         Glib::ustring name = p->name;
220         PaperSize paper(name, p->smaller, p->larger, p->unit);
221         paperSizeTable[name] = paper;
222         _paperSizeList.append_text(name);
223         }
227 PageSizer::~PageSizer()
229     _paper_size_list_connection.disconnect();
230     _portrait_connection.disconnect();
231     _landscape_connection.disconnect();
232     _changedw_connection.disconnect();
233     _changedh_connection.disconnect();
239 void
240 PageSizer::init (Registry& reg)
242     Gtk::HBox *hbox_ori = manage (new Gtk::HBox);
243     pack_start (*hbox_ori, false, false, 0);
244     Gtk::Label *label_ori = manage (new Gtk::Label (_("Page orientation:"), 0.0, 0.5)); 
245     hbox_ori->pack_start (*label_ori, false, false, 0);
246     
247     _landscapeButton.set_label(_("_Landscape"));
248         _landscapeButton.set_active(true);
249     Gtk::RadioButton::Group group = _landscapeButton.get_group();
250     hbox_ori->pack_end (_landscapeButton, false, false, 5);
251     _portraitButton.set_label(_("_Portrait"));
252         _portraitButton.set_active(true);
253     hbox_ori->pack_end (_portraitButton, false, false, 5);
254     _portraitButton.set_group (group);
255     _portraitButton.set_active (true);
256     
257     /* Custom paper frame */
258     Gtk::Frame *frame = manage (new Gtk::Frame(_("Custom size")));
259     pack_start (*frame, false, false, 0);
260     Gtk::Table *table = manage (new Gtk::Table (5, 2, false));
261     table->set_border_width (4);
262     table->set_row_spacings (4);
263     table->set_col_spacings (4);
264     
265     Inkscape::UI::Widget::Button* fit_canv =
266              manage(new Inkscape::UI::Widget::Button(_("_Fit page to selection"),
267     _("Resize the page to fit the current selection, or the entire drawing if there is no selection")));
269     // prevent fit_canv from expanding
270     Gtk::Alignment *fit_canv_cont = manage(new Gtk::Alignment(1.0,0.5,0.0,0.0));
271     fit_canv_cont->add(*fit_canv);
273     frame->add (*table);
274     
275     _wr = &reg;
277     _rum.init (_("U_nits:"), "units", *_wr);
278     _rusw.init (_("_Width:"), _("Width of paper"), "width", _rum, *_wr);
279     _rush.init (_("_Height:"), _("Height of paper"), "height", _rum, *_wr);
281     table->attach (*_rum._label, 0,1,0,1, Gtk::FILL|Gtk::EXPAND,
282                     (Gtk::AttachOptions)0,0,0);
283     table->attach (*_rum._sel, 1,2,0,1, Gtk::FILL|Gtk::EXPAND,
284                     (Gtk::AttachOptions)0,0,0);
285     table->attach (*_rusw.getSU(), 0,2,1,2, Gtk::FILL|Gtk::EXPAND,
286                     (Gtk::AttachOptions)0,0,0);
287     table->attach (*_rush.getSU(), 0,2,2,3, Gtk::FILL|Gtk::EXPAND,
288                    (Gtk::AttachOptions)0,0,0);
289     table->attach (*fit_canv_cont, 0,2,3,4, Gtk::FILL|Gtk::EXPAND,
290                    (Gtk::AttachOptions)0,0,0);
292     _paper_size_list_connection = _paperSizeList.signal_changed().connect (
293                 sigc::mem_fun (*this, &PageSizer::on_paper_size_list_changed));
294                 
295     _landscape_connection = _landscapeButton.signal_toggled().connect (
296                 sigc::mem_fun (*this, &PageSizer::on_landscape));
297     _portrait_connection = _portraitButton.signal_toggled().connect (
298                 sigc::mem_fun (*this, &PageSizer::on_portrait));
299     _changedw_connection = _rusw.getSU()->signal_value_changed().connect (
300                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
301     _changedh_connection = _rush.getSU()->signal_value_changed().connect (
302                 sigc::mem_fun (*this, &PageSizer::on_value_changed));
303     fit_canv->signal_clicked().connect(
304              sigc::mem_fun(*this, &PageSizer::fire_fit_canvas_to_selection_or_drawing));
305     
306     show_all_children();
310 /**
311  * Set document dimensions (if not called by Doc prop's update()) and
312  * set the PageSizer's widgets and text entries accordingly. This is
313  * somewhat slow, is there something done too often invisibly?
314  *
315  * \param w, h given in px
316  */
317 void
318 PageSizer::setDim (double w, double h, bool changeList)
320     static bool _called = false;
321     if (_called)
322             return;
324     _called = true;
325     
326     _paper_size_list_connection.block();
327         _landscape_connection.block();
328     _portrait_connection.block(); 
329     _changedw_connection.block();
330     _changedh_connection.block();
332     if (SP_ACTIVE_DESKTOP && !_wr->isUpdating()) {
333         SPDocument *doc = sp_desktop_document(SP_ACTIVE_DESKTOP);
334         sp_document_set_width (doc, w, &_px_unit);
335         sp_document_set_height (doc, h, &_px_unit);
336         sp_document_done (doc, SP_VERB_NONE, 
337                           /* TODO: annotate */ "page-sizer.cpp:301");
338     } 
339     
340     _landscape = w>h;
341     _landscapeButton.set_active(_landscape ? true : false);
342     _portraitButton.set_active (_landscape ? false : true);
343     
344     if (changeList)
345             _paperSizeList.set_active (find_paper_size (w, h));
346     
347     Unit const& unit = _rum._sel->getUnit();
348     _rusw.setValue (w / unit.factor);
349     _rush.setValue (h / unit.factor);
351     _paper_size_list_connection.unblock();
352         _landscape_connection.unblock();
353     _portrait_connection.unblock();
354     _changedw_connection.unblock();
355     _changedh_connection.unblock();
356     
357     _called = false;
360 /** 
361  * Returns an index into inkscape_papers of a paper of the specified 
362  * size (specified in px), or -1 if there's no such paper.
363  */
364 int
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, -1);
374     
375     int index = 0;
376     std::map<Glib::ustring, PaperSize>::const_iterator iter;
377     for (iter = paperSizeTable.begin() ; 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);
382         
383         g_return_val_if_fail(smallX <= largeX, -1);
384         
385         if ((std::abs(smaller - smallX) <= 0.1) &&
386             (std::abs(larger  - largeX) <= 0.1)   )
387             return index;
388             
389         index++;
390     }
391     return -1;
394 void
395 PageSizer::fire_fit_canvas_to_selection_or_drawing() {
396     SPDesktop *dt = SP_ACTIVE_DESKTOP;
397     if (!dt) return;
398     Verb *verb = Verb::get( SP_VERB_FIT_CANVAS_TO_SELECTION_OR_DRAWING );
399     if (verb) {
400         SPAction *action = verb->get_action(dt);
401         if (action) {
402             sp_action_perform(action, NULL);        
403         }
404     }
407 void
408 PageSizer::on_paper_size_list_changed()
410     Glib::ustring name = _paperSizeList.get_active_text();
411     std::map<Glib::ustring, PaperSize>::const_iterator iter =
412         paperSizeTable.find(name);
413     if (iter == paperSizeTable.end()) {
414         g_warning("paper size '%s' not found in table", name.c_str());
415         return;
416     }
417     PaperSize paper = iter->second;
418     double w = paper.smaller;
419     double h = paper.larger;
420     SPUnit const &src_unit = sp_unit_get_by_id (paper.unit);
421     sp_convert_distance (&w, &src_unit, &_px_unit);
422     sp_convert_distance (&h, &src_unit, &_px_unit);
423     if (_landscape)
424         setDim (h, w, false);
425     else
426         setDim (w, h, false);
430 void
431 PageSizer::on_portrait()
433     if (!_portraitButton.get_active())
434         return;
435     double w = _rusw.getSU()->getValue ("px");
436     double h = _rush.getSU()->getValue ("px");
437     if (h<w)
438             setDim (h, w);
441 void
442 PageSizer::on_landscape()
444     if (!_landscapeButton.get_active())
445         return;
446     double w = _rusw.getSU()->getValue ("px");
447     double h = _rush.getSU()->getValue ("px");
448     if (w<h)
449             setDim (h, w);
452 void
453 PageSizer::on_value_changed()
455     if (_wr->isUpdating()) return;
457     setDim (_rusw.getSU()->getValue("px"),
458                 _rush.getSU()->getValue("px"));
461 } // namespace Widget
462 } // namespace UI
463 } // namespace Inkscape
465 /*
466   Local Variables:
467   mode:c++
468   c-file-style:"stroustrup"
469   c-file-offsets:((innamespace . 0)(inline-open . 0))
470   indent-tabs-mode:nil
471   fill-column:99
472   End:
473 */
474 // vim: filetype=c++:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :