Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
export-single.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/* Authors:
3 * Lauris Kaplinski <lauris@kaplinski.com>
4 * bulia byak <buliabyak@users.sf.net>
5 * Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
6 * Anshudhar Kumar Singh <anshudhar2001@gmail.com>
7 *
8 * Copyright (C) 1999-2007, 2021 Authors
9 * Copyright (C) 2001-2002 Ximian, Inc.
10 *
11 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
12 */
13
14#include "export-single.h"
15
16#include <cassert>
17#include <png.h>
18
19#include <glibmm/convert.h>
20#include <glibmm/i18n.h>
21#include <glibmm/miscutils.h>
22#include <gtkmm/builder.h>
23#include <gtkmm/button.h>
24#include <gtkmm/checkbutton.h>
25#include <gtkmm/filefilter.h>
26#include <gtkmm/flowbox.h>
27#include <gtkmm/grid.h>
28#include <gtkmm/label.h>
29#include <gtkmm/menubutton.h>
30#include <gtkmm/progressbar.h>
31#include <gtkmm/recentmanager.h>
32#include <gtkmm/scrolledwindow.h>
33#include <gtkmm/spinbutton.h>
34#include <gtkmm/togglebutton.h>
35#include <png.h>
36#include <sigc++/adaptors/bind.h>
37#include <sigc++/functors/mem_fun.h>
38
39#include "desktop.h"
40#include "document-undo.h"
41#include "document.h"
42#include "extension/output.h"
43#include "inkscape-window.h"
44#include "io/sandbox.h"
45#include "object/sp-namedview.h"
46#include "object/sp-page.h"
47#include "object/sp-root.h"
48#include "page-manager.h"
49#include "preferences.h"
50#include "selection.h"
51#include "ui/builder-utils.h"
52#include "ui/dialog/export.h"
55#include "ui/icon-names.h"
56#include "ui/util.h"
60#include "ui/widget/unit-menu.h"
61
64
65namespace Inkscape::UI::Dialog {
66
67SingleExport::SingleExport(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &builder)
68 : Gtk::Box(cobject)
69 , pages_list (get_widget<Gtk::FlowBox> (builder, "si_pages"))
70 , pages_list_box (get_widget<Gtk::ScrolledWindow> (builder, "si_pages_box"))
71 , size_box (get_widget<Gtk::Grid> (builder, "si_sizes"))
72 , units (get_derived_widget<UnitMenu> (builder, "si_units"))
73 , si_units_row (get_widget<Gtk::Box> (builder, "si_units_row"))
74 , si_hide_all (get_widget<Gtk::CheckButton> (builder, "si_hide_all"))
75 , si_show_preview (get_widget<Gtk::CheckButton> (builder, "si_show_preview"))
77 , preview_box (get_widget<Gtk::Box> (builder, "si_preview_box"))
78
79 , si_extension_cb (get_derived_widget<ExtensionList>(builder, "si_extention"))
80 , si_filename_entry (get_widget<Gtk::Entry> (builder, "si_filename"))
81 , si_filename_button(get_widget<Gtk::Button> (builder, "si_filename_button"))
82 , si_export (get_widget<Gtk::Button> (builder, "si_export"))
83
84 , progress_bar (get_widget<Gtk::ProgressBar> (builder, "si_progress"))
85 , cancel_button (get_widget<Gtk::Button> (builder, "si_cancel"))
86 , progress_box (get_widget<Gtk::Box> (builder, "si_inprogress"))
87 , _background_color (get_derived_widget<UI::Widget::ColorPicker>(builder, "si_backgnd", _("Background color"), true))
88{
90
95
96 selection_buttons[SELECTION_DRAWING] = &get_widget<Gtk::ToggleButton>(builder, "si_s_document");
97 selection_buttons[SELECTION_PAGE] = &get_widget<Gtk::ToggleButton>(builder, "si_s_page");
98 selection_buttons[SELECTION_SELECTION] = &get_widget<Gtk::ToggleButton>(builder, "si_s_selection");
99 selection_buttons[SELECTION_CUSTOM] = &get_widget<Gtk::ToggleButton>(builder, "si_s_custom");
100
101 spin_buttons[SPIN_X0] = &get_widget<Gtk::SpinButton>(builder, "si_left_sb");
102 spin_buttons[SPIN_X1] = &get_widget<Gtk::SpinButton>(builder, "si_right_sb");
103 spin_buttons[SPIN_Y0] = &get_widget<Gtk::SpinButton>(builder, "si_top_sb");
104 spin_buttons[SPIN_Y1] = &get_widget<Gtk::SpinButton>(builder, "si_bottom_sb");
105 spin_buttons[SPIN_HEIGHT] = &get_widget<Gtk::SpinButton>(builder, "si_height_sb");
106 spin_buttons[SPIN_WIDTH] = &get_widget<Gtk::SpinButton>(builder, "si_width_sb");
107 spin_buttons[SPIN_BMHEIGHT] = &get_widget<Gtk::SpinButton>(builder, "si_img_height_sb");
108 spin_buttons[SPIN_BMWIDTH] = &get_widget<Gtk::SpinButton>(builder, "si_img_width_sb");
109 spin_buttons[SPIN_DPI] = &get_widget<Gtk::SpinButton>(builder, "si_dpi_sb");
110
111 spin_labels[SPIN_X0] = &get_widget<Gtk::Label>(builder, "si_label_left");
112 spin_labels[SPIN_X1] = &get_widget<Gtk::Label>(builder, "si_label_right");
113 spin_labels[SPIN_Y0] = &get_widget<Gtk::Label>(builder, "si_label_top");
114 spin_labels[SPIN_Y1] = &get_widget<Gtk::Label>(builder, "si_label_bottom");
115 spin_labels[SPIN_HEIGHT] = &get_widget<Gtk::Label>(builder, "si_label_height");
116 spin_labels[SPIN_WIDTH] = &get_widget<Gtk::Label>(builder, "si_label_width");
117
118 auto &pref_button_box = get_widget<Gtk::Box>(builder, "si_prefs");
119 auto &pref_button = *si_extension_cb.getPrefButton();
120 pref_button_box.append(pref_button);
121 pref_button.set_expand(false);
122 pref_button_box.set_expand(false);
123 pref_button.set_valign(Gtk::Align::BASELINE_CENTER);
124 pref_button_box.set_valign(Gtk::Align::BASELINE_CENTER);
125
126 setup();
127}
128
129// Inkscape Selection Modified CallBack
131{
132 if (!_desktop || _desktop->getSelection() != selection) {
133 return;
134 }
135 if (!(flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
136 return;
137 }
138 refreshArea();
139 // Do not load export hits for modifications
140}
141
143{
144 if (!_desktop || _desktop->getSelection() != selection) {
145 return;
146 }
147
148 Glib::ustring pref_key_name = prefs->getString("/dialogs/export/exportarea/value");
149 for (auto [key, name] : selection_names) {
150 if (name == pref_key_name && current_key != key && key != SELECTION_SELECTION) {
151 selection_buttons[key]->set_active(true);
153 break;
154 }
155 }
156 if (selection->isEmpty()) {
157 selection_buttons[SELECTION_SELECTION]->set_sensitive(false);
159 selection_buttons[(selection_mode)0]->set_active(true); // This causes refresh area
160 // even though we are at default key, selection is the one which was original key.
161 prefs->setString("/dialogs/export/exportarea/value", selection_names[SELECTION_SELECTION]);
162 // return otherwise refreshArea will be called again
163 return;
164 }
165 } else {
166 selection_buttons[SELECTION_SELECTION]->set_sensitive(true);
169 return;
170 }
171 }
172
173 refreshArea();
175}
176
177// Setup Single Export.Called by export on realize
179{
180 if (setupDone) {
181 // We need to setup only once
182 return;
183 }
184 setupDone = true;
185
187
188 setupUnits();
190
191 // set them before connecting to signals
193 setPagesMode(false);
194 setExporting(false);
195
196 // Make the filename entry box read-only when the filesystem is sandboxed.
197 // "Sandboxed" means that the user can't access arbitrary paths but only those
198 // that come from the file chooser.
200 si_filename_entry.set_editable(false);
201 si_filename_entry.set_can_focus(false);
202 si_filename_entry.set_has_frame(false);
203 }
204
205 // Refresh the filename when the user selects a different page
206 _pages_list_changed = pages_list.signal_selected_children_changed().connect([this]() {
208 refreshArea();
209 });
210
211 // Connect Signals Here
212 for (auto [key, button] : selection_buttons) {
213 button->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SingleExport::onAreaTypeToggle), key));
214 }
215 units.signal_changed().connect(sigc::mem_fun(*this, &SingleExport::onUnitChanged));
216 extensionConn = si_extension_cb.signal_changed().connect(sigc::mem_fun(*this, &SingleExport::onExtensionChanged));
217 exportConn = si_export.signal_clicked().connect(sigc::mem_fun(*this, &SingleExport::onExport));
218 filenameConn = si_filename_entry.signal_changed().connect(sigc::mem_fun(*this, &SingleExport::onFilenameModified));
219 cancelConn = cancel_button.signal_clicked().connect(sigc::mem_fun(*this, &SingleExport::onCancel));
220 si_filename_entry.signal_activate().connect(sigc::mem_fun(*this, &SingleExport::onExport));
221 browseConn = si_filename_button.signal_clicked().connect(sigc::mem_fun(*this, &SingleExport::onBrowse));
222 si_show_preview.signal_toggled().connect(sigc::mem_fun(*this, &SingleExport::refreshPreview));
223 si_hide_all.signal_toggled().connect(sigc::mem_fun(*this, &SingleExport::refreshPreview));
224 _background_color.connectChanged([=, this](Colors::Color const &color){
225 if (_desktop) {
227 }
229 });
230}
231
232// Setup units combobox
240
241// Create all spin buttons
243{
244 setupSpinButton<sb_type>(spin_buttons[SPIN_X0], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
246 setupSpinButton<sb_type>(spin_buttons[SPIN_X1], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
248 setupSpinButton<sb_type>(spin_buttons[SPIN_Y0], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
250 setupSpinButton<sb_type>(spin_buttons[SPIN_Y1], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
252
253 setupSpinButton<sb_type>(spin_buttons[SPIN_HEIGHT], 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0, EXPORT_COORD_PRECISION,
255 setupSpinButton<sb_type>(spin_buttons[SPIN_WIDTH], 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0, EXPORT_COORD_PRECISION,
257
258 setupSpinButton<sb_type>(spin_buttons[SPIN_BMHEIGHT], 1.0, 1.0, 1000000.0, 1.0, 10.0, 0, true,
260 setupSpinButton<sb_type>(spin_buttons[SPIN_BMWIDTH], 1.0, 1.0, 1000000.0, 1.0, 10.0, 0, true,
262 setupSpinButton<sb_type>(spin_buttons[SPIN_DPI], prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
263 1.0, 100000.0, 0.1, 1.0, 2, true, &SingleExport::onDpiChange, SPIN_DPI);
264}
265
266template <typename T>
267void SingleExport::setupSpinButton(Gtk::SpinButton *sb, double val, double min, double max, double step, double page,
268 int digits, bool sensitive, void (SingleExport::*cb)(T), T param)
269{
270 if (sb) {
271 sb->set_digits(digits);
272 sb->set_increments(step, page);
273 sb->set_range(min, max);
274 sb->set_value(val);
275 sb->set_sensitive(sensitive);
276 sb->set_width_chars(0);
277 sb->set_max_width_chars(0);
278 if (cb) {
279 auto signal = sb->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, cb), param));
280 // add signals to list to block all easily
281 spinButtonConns.push_back(signal);
282 }
283 }
284}
285
287{
288 if (_document) {
289 Geom::OptRect bbox;
290 auto sel = getSelectedPages();
291
292 switch (current_key) {
294 if ((_desktop->getSelection())->isEmpty() == false) {
296 break;
297 }
300 if (bbox) {
301 break;
302 }
303 case SELECTION_PAGE:
304 // If the page is set in the multi-selection use that.
305 if (sel.size() == 1) {
306 bbox = sel[0]->getDesktopRect();
307 } else {
309 }
310 break;
311 case SELECTION_CUSTOM:
312 break;
313 default:
314 break;
315 }
316 if (current_key != SELECTION_CUSTOM && bbox) {
317 setArea(bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y]);
318 }
319 }
321}
322
324{
325 if (!_document)
326 return;
327
328 bool multi = pages_list.get_selection_mode() == Gtk::SelectionMode::MULTIPLE;
329 auto &pm = _document->getPageManager();
330 bool has_pages = current_key == SELECTION_PAGE && pm.getPageCount() > 1;
331 pages_list_box.set_visible(has_pages);
332 preview_box.set_visible(!has_pages);
333 size_box.set_visible(!has_pages || !multi);
334}
335
337{
338 // Set set the internal mode to NONE to preserve selections while changing
339 UI::for_each_child(pages_list, [] (Gtk::Widget &widget) {
340 if (auto item = dynamic_cast<BatchItem *>(&widget)) {
341 item->on_mode_changed(Gtk::SelectionMode::NONE);
342 }
344 });
345 pages_list.set_selection_mode(multi ? Gtk::SelectionMode::MULTIPLE : Gtk::SelectionMode::SINGLE);
346 // A second call is needed in its own loop because of how updates happen in the FlowBox
347 UI::for_each_child(pages_list, [] (Gtk::Widget &widget) {
348 if (auto item = dynamic_cast<BatchItem *>(&widget)) {
349 item->update_selected();
350 }
352 });
353 refreshPage();
354}
355
357{
358 UI::for_each_child(pages_list, [=] (Gtk::Widget &widget) {
359 if (auto item = dynamic_cast<BatchItem *>(&widget)) {
360 if (item->getPage() == page) {
361 item->set_selected(true);
362 }
363 }
365 });
366}
367
368std::vector<SPPage const *> SingleExport::getSelectedPages() const
369{
370 std::vector<SPPage const *> pages;
371 pages_list.selected_foreach([&pages](Gtk::FlowBox *box, Gtk::FlowBoxChild *child) {
372 if (auto item = dynamic_cast<BatchItem *>(child))
373 pages.push_back(item->getPage());
374 });
375 return pages;
376}
377
379{
380 std::map<std::string, SPObject*> itemsList;
381
382 if (_document) {
383 auto &pm = _document->getPageManager();
384 if (pm.getPageCount() > 1) {
385 for (auto page : pm.getPages()) {
386 if (auto id = page->getId()) {
387 itemsList[id] = page;
388 }
389 }
390 }
391 }
392
393 _pages_list_changed.block();
395 refreshPage();
396 if (auto ext = si_extension_cb.getExtension()) {
397 setPagesMode(!ext->is_raster());
398 }
399 _pages_list_changed.unblock();
400}
401
406
408 if (pages_list.get_selection_mode() != Gtk::SelectionMode::MULTIPLE) {
410 }
411 refreshArea();
412}
413
418{
419 if (!_document || !_desktop)
420 return;
421
423 std::string old_raw_filepath = filepath_native;
425 std::string filename;
426 Geom::Point dpi;
427 switch (current_key) {
428 case SELECTION_PAGE:
429 {
430 auto pages = getSelectedPages();
431 if (pages.size() == 1) {
432 dpi = pages[0]->getExportDpi();
433
434 auto page_filename = pages[0]->getExportFilename();
435 if (page_filename.empty()) {
436 page_filename = pages[0]->getLabel();
437 }
438
439 filename =
440 Export::prependDirectory(Glib::filename_from_utf8(page_filename), old_raw_filepath, _document);
441
442 break;
443 }
444 // No or many pages means output is drawing, continue.
445 }
446 case SELECTION_CUSTOM:
448 {
449 dpi = _document->getRoot()->getExportDpi();
450 filename = Export::prependDirectory(Glib::filename_from_utf8(_document->getRoot()->getExportFilename()),
451 old_raw_filepath, _document);
452 break;
453 }
455 {
456 auto selection = _desktop->getSelection();
457 if (selection->isEmpty()) break;
458
459 // Get filename and dpi from selected items
460 for (auto item : selection->items()) {
461 if (!dpi.x()) {
462 dpi = item->getExportDpi();
463 }
464 if (filename.empty()) {
465 filename = Export::prependDirectory(Glib::filename_from_utf8(item->getExportFilename()),
466 old_raw_filepath, _document);
467 }
468 }
469
470 if (filename.empty()) {
471 filename = Export::filePathFromObject(_document, selection->firstItem(), old_raw_filepath);
472 }
473 break;
474 }
475 default:
476 break;
477 }
478 if (filename.empty()) {
479 filename = old_raw_filepath;
481 filename = Export::defaultFilename(_document, filename, ".png");
482 }
483 if (auto ext = si_extension_cb.getExtension()) {
485 ext->add_extension(filename);
486 }
487
489 // If the filename was NOT manually modified by the user
490 // since the last export, then update the filename edit box
491 // and internal filename value to the new suggested filename.
492 setFilename(filename, false);
493 }
494
495 if (dpi.x() != 0.0) { // XXX Should this deal with dpi.y() ?
496 spin_buttons[SPIN_DPI]->set_value(dpi.x());
497 }
498}
499
512void SingleExport::setFilename(std::string filename, bool is_user_input)
513{
514 // Update modification status:
515 if (filepath_native != filename) {
516 // Filename was changed.
517 // If it was changed based on user input, then set the "modified by user" flag.
518 // Else, reset the flag.
519 filename_modified_by_user = is_user_input;
520 }
521
522 // Update filename:
523 if (!is_user_input && Inkscape::IO::Sandbox::filesystem_is_sandboxed()) {
524 // With a sandboxed filesystem, autogenerated filenames don't work.
525 // We can only use files coming from the file chooser dialog.
526 // Therefore, if the filename changed and it is not from user input,
527 // make the filename invalid to force that the user opens the file dialog again.
528 if (filepath_native != filename) {
529 filename = "";
530 }
531 }
532 filepath_native = filename;
533
534 // Determine filename label for filename entry box:
535 Glib::ustring filename_label = Glib::filename_to_utf8(filename);
537 // In a sandboxed filesystem, the file entry box is not editable.
538 // We show a nice label instead of the raw path.
539 filename_label = Inkscape::IO::Sandbox::filesystem_get_display_path(Gio::File::create_for_path(filename));
540 }
541
542 // Set value of filename entry box:
543 if (si_filename_entry.get_text().raw() != filename_label.raw()) {
544 auto old_block_state = filenameConn.blocked();
545 filenameConn.block();
546 si_filename_entry.set_text(filename_label);
547 si_filename_entry.set_position(filename_label.length());
548 filenameConn.block(old_block_state);
549 filename_entry_original_value = filename_label;
550 }
551}
552
554{
555 if (target) {
556 target->setExportFilename(Glib::filename_to_utf8(filepath_native));
558 spin_buttons[SPIN_DPI]->get_value(),
559 spin_buttons[SPIN_DPI]->get_value()
560 ));
561 }
562}
563
564void SingleExport::setArea(double x0, double y0, double x1, double y1)
565{
566 blockSpinConns(true);
567
568 Unit const *unit = units.getUnit();
569 auto px = UnitTable::get().getUnit("px");
570 spin_buttons[SPIN_X0]->get_adjustment()->set_value(px->convert(x0, unit));
571 spin_buttons[SPIN_X1]->get_adjustment()->set_value(px->convert(x1, unit));
572 spin_buttons[SPIN_Y0]->get_adjustment()->set_value(px->convert(y0, unit));
573 spin_buttons[SPIN_Y1]->get_adjustment()->set_value(px->convert(y1, unit));
574
577
578 blockSpinConns(false);
579}
580
581// Signals CallBack
582
587
589{
590 // Prevent executing function twice
591 if (!selection_buttons[key]->get_active()) {
592 return;
593 }
594 // If you have reached here means the current key is active one ( not sure if multiple transitions happen but
595 // last call will change values)
597 prefs->setString("/dialogs/export/exportarea/value", selection_names[current_key]);
598
599 refreshArea();
602 refreshPage();
603}
604
606{
607 bool show = current_key == SELECTION_CUSTOM;
608 spin_buttons[SPIN_X0]->set_visible(show);
609 spin_buttons[SPIN_X1]->set_visible(show);
610 spin_buttons[SPIN_Y0]->set_visible(show);
611 spin_buttons[SPIN_Y1]->set_visible(show);
612 spin_buttons[SPIN_WIDTH]->set_visible(show);
613 spin_buttons[SPIN_HEIGHT]->set_visible(show);
614
615 spin_labels[SPIN_X0]->set_visible(show);
616 spin_labels[SPIN_X1]->set_visible(show);
617 spin_labels[SPIN_Y0]->set_visible(show);
618 spin_labels[SPIN_Y1]->set_visible(show);
619 spin_labels[SPIN_WIDTH]->set_visible(show);
620 spin_labels[SPIN_HEIGHT]->set_visible(show);
621
622 si_units_row.set_visible(show);
623}
624
626{
627 blockSpinConns(true);
628 areaXChange(type);
629 selection_buttons[SELECTION_CUSTOM]->set_active(true);
631 blockSpinConns(false);
632}
634{
635 blockSpinConns(true);
636 areaYChange(type);
637 selection_buttons[SELECTION_CUSTOM]->set_active(true);
639 blockSpinConns(false);
640}
642{
643 blockSpinConns(true);
644 dpiChange(type);
645 blockSpinConns(false);
646}
647
652{
653 extensionConn.block();
654
655 Glib::ustring filename = si_filename_entry.get_text();
656 if (filename_entry_original_value.raw() != filename.raw()) {
657 // Textbox entry was changed
658 setFilename(filename, true);
660 }
661
662 // This will not change the output extension if filename extension is same as previously
663 // selected extension's filename extension. In otherwords, selecting "SVG" in file dialog
664 // won't override "Plain SVG" in export dialog.
666
667 extensionConn.unblock();
668}
669
671{
672 if (auto ext = si_extension_cb.getExtension()) {
673 setPagesMode(!ext->is_raster());
675 }
676}
677
679{
680 interrupted = true;
681 setExporting(false);
682}
683
685{
686 interrupted = false;
687 if (!_desktop || !_document)
688 return;
689
690 if (filepath_native.empty()) {
691 // No file selected yet - call file chooser first
692 onBrowse();
693 }
694
695 auto &page_manager = _document->getPageManager();
696 auto selection = _desktop->getSelection();
697 bool exportSuccessful = false;
698 auto omod = si_extension_cb.getExtension();
699 if (!omod) {
700 std::cerr << "SingleExport::onExport(): Cannot find export extension!" << std::endl;
701 return;
702 }
703
704 bool selected_only = si_hide_all.get_active();
705 Unit const *unit = units.getUnit();
706
708 return;
709 }
710
712 Glib::ustring filename_label =
714
716 Glib::ustring filename_utf8 = Glib::filename_to_utf8(filepath_native);
717
718 setExporting(true, _("Exporting"));
719
720 float x0 = unit->convert(spin_buttons[SPIN_X0]->get_value(), "px");
721 float x1 = unit->convert(spin_buttons[SPIN_X1]->get_value(), "px");
722 float y0 = unit->convert(spin_buttons[SPIN_Y0]->get_value(), "px");
723 float y1 = unit->convert(spin_buttons[SPIN_Y1]->get_value(), "px");
724 auto area = Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1));
725
726 if (omod->is_raster()) {
727 area *= _desktop->dt2doc();
728 unsigned long int width = int(spin_buttons[SPIN_BMWIDTH]->get_value() + 0.5);
729 unsigned long int height = int(spin_buttons[SPIN_BMHEIGHT]->get_value() + 0.5);
730
731 float dpi = spin_buttons[SPIN_DPI]->get_value();
732
733 setExporting(true, Glib::ustring::compose(_("Exporting %1 (%2 x %3)"), filename_label, width, height));
734
735 std::vector<SPItem const *> selected(selection->items().begin(), selection->items().end());
736
737 exportSuccessful = Export::exportRaster(area, width, height, dpi,
738 _background_color.get_current_color().toRGBA(), filename_utf8, false,
739 onProgressCallback, this, omod, selected_only ? &selected : nullptr);
740
741 } else {
742 setExporting(true, Glib::ustring::compose(_("Exporting %1"), filename_label));
743
744 auto copy_doc = _document->copy();
745
746 std::vector<SPItem const *> items;
747 if (selected_only) {
748 auto itemlist = selection->items();
749 items.insert(items.end(), itemlist.begin(), itemlist.end());
750 }
751
752 if (current_key == SELECTION_PAGE && page_manager.hasPages()) {
753 auto pages = getSelectedPages();
754 // A single page won't have a selection UI, so emplace it
755 if (page_manager.getPageCount() == 1) {
756 pages.emplace_back(page_manager.getPage(0));
757 }
758 exportSuccessful = Export::exportVector(omod, copy_doc.get(), filename_utf8, false, items, pages);
759 } else {
760 // To get the right kind of export, we're going to make a page
761 // This allows all the same raster options to work for vectors
762 auto const page = copy_doc->getPageManager().newDocumentPage(area);
763 exportSuccessful = Export::exportVector(omod, copy_doc.get(), filename_utf8, false, items, page);
764 }
765 }
766 // Save the export hints back to the svg document
767 if (exportSuccessful) {
769 auto recentmanager = Gtk::RecentManager::get_default();
770 if (recentmanager && Glib::path_is_absolute(path)) {
771 Glib::ustring uri = Glib::filename_to_uri(path);
772 recentmanager->add_item(uri);
773 }
774
775 SPObject *target;
776 switch (current_key) {
777 case SELECTION_CUSTOM:
779 target = _document->getRoot();
780 break;
781 case SELECTION_PAGE:
782 {
783 auto pages = getSelectedPages();
784 if (pages.size() == 1) {
785 if ((target = page_manager.getSelected()))
786 break;
787 }
788 target = _document->getRoot();
789 break;
790 }
792 target = _desktop->getSelection()->firstItem();
793 break;
794 default:
795 break;
796 }
797 if (target) {
798 saveExportHints(target);
799 DocumentUndo::done(_document, _("Set Export Options"), INKSCAPE_ICON("export"));
800 }
801 }
802 setExporting(false);
804 interrupted = false;
805}
806
808{
809 if (!_app || !_app->get_active_window() || !_document) {
810 return;
811 }
812
813 Gtk::Window *window = _app->get_active_window();
814
815 browseConn.block();
816 auto omod = si_extension_cb.getExtension();
817 assert(omod);
818
819 std::string filename = Glib::filename_from_utf8(si_filename_entry.get_text());
820
821 if (filename.empty()) {
822 filename = Export::defaultFilename(_document, filename, omod->get_extension());
823 }
824
825 // Note, there are currently multiple modules per filename extension (.svg, .dxf, .zip).
826 // We cannot distinguish between them.
827 std::string basename = Glib::path_get_basename(filename);
828 std::string dirname = Glib::path_get_dirname(filename);
829 auto file = choose_file_save( _("Select a filename for exporting"), window,
830 create_export_filters(), // {}, // mimetype
831 basename,
832 dirname);
833
834 if (file) {
835 Glib::ustring filename_utf8 = file->get_parse_name();
836 si_filename_entry.set_text(filename_utf8);
837 si_filename_entry.set_position(filename_utf8.length());
838 onExport();
839 }
840
841 browseConn.unblock();
842}
843
844// Utils Functions
845
846void SingleExport::blockSpinConns(bool status = true)
847{
848 for (auto &signal : spinButtonConns) {
849 signal.block(status);
850 }
851}
852
854{
855 auto x0_adj = spin_buttons[SPIN_X0]->get_adjustment();
856 auto x1_adj = spin_buttons[SPIN_X1]->get_adjustment();
857 auto width_adj = spin_buttons[SPIN_WIDTH]->get_adjustment();
858
859 float x0, x1, dpi, width, bmwidth;
860
861 // Get all values in px
862 Unit const *unit = units.getUnit();
863 x0 = unit->convert(x0_adj->get_value(), "px");
864 x1 = unit->convert(x1_adj->get_value(), "px");
865 width = unit->convert(width_adj->get_value(), "px");
866 bmwidth = spin_buttons[SPIN_BMWIDTH]->get_value();
867 dpi = spin_buttons[SPIN_DPI]->get_value();
868
869 switch (type) {
870 case SPIN_X0:
871 bmwidth = (x1 - x0) * dpi / DPI_BASE;
872 if (bmwidth < SP_EXPORT_MIN_SIZE) {
873 x0 = x1 - (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
874 }
875 break;
876 case SPIN_X1:
877 bmwidth = (x1 - x0) * dpi / DPI_BASE;
878 if (bmwidth < SP_EXPORT_MIN_SIZE) {
879 x1 = x0 + (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
880 }
881 break;
882 case SPIN_WIDTH:
883 bmwidth = width * dpi / DPI_BASE;
884 if (bmwidth < SP_EXPORT_MIN_SIZE) {
885 width = (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
886 }
887 x1 = x0 + width;
888 break;
889 default:
890 break;
891 }
892
893 width = x1 - x0;
894 bmwidth = floor(width * dpi / DPI_BASE + 0.5);
895
896 auto px = UnitTable::get().getUnit("px");
897 x0_adj->set_value(px->convert(x0, unit));
898 x1_adj->set_value(px->convert(x1, unit));
899 width_adj->set_value(px->convert(width, unit));
900 spin_buttons[SPIN_BMWIDTH]->set_value(bmwidth);
901}
902
904{
905 auto y0_adj = spin_buttons[SPIN_Y0]->get_adjustment();
906 auto y1_adj = spin_buttons[SPIN_Y1]->get_adjustment();
907 auto height_adj = spin_buttons[SPIN_HEIGHT]->get_adjustment();
908
909 float y0, y1, dpi, height, bmheight;
910
911 // Get all values in px
912 Unit const *unit = units.getUnit();
913 y0 = unit->convert(y0_adj->get_value(), "px");
914 y1 = unit->convert(y1_adj->get_value(), "px");
915 height = unit->convert(height_adj->get_value(), "px");
916 bmheight = spin_buttons[SPIN_BMHEIGHT]->get_value();
917 dpi = spin_buttons[SPIN_DPI]->get_value();
918
919 switch (type) {
920 case SPIN_Y0:
921 bmheight = (y1 - y0) * dpi / DPI_BASE;
922 if (bmheight < SP_EXPORT_MIN_SIZE) {
923 y0 = y1 - (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
924 }
925 break;
926 case SPIN_Y1:
927 bmheight = (y1 - y0) * dpi / DPI_BASE;
928 if (bmheight < SP_EXPORT_MIN_SIZE) {
929 y1 = y0 + (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
930 }
931 break;
932 case SPIN_HEIGHT:
933 bmheight = height * dpi / DPI_BASE;
934 if (bmheight < SP_EXPORT_MIN_SIZE) {
935 height = (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
936 }
937 y1 = y0 + height;
938 break;
939 default:
940 break;
941 }
942
943 height = y1 - y0;
944 bmheight = floor(height * dpi / DPI_BASE + 0.5);
945
946 auto px = UnitTable::get().getUnit("px");
947 y0_adj->set_value(px->convert(y0, unit));
948 y1_adj->set_value(px->convert(y1, unit));
949 height_adj->set_value(px->convert(height, unit));
950 spin_buttons[SPIN_BMHEIGHT]->set_value(bmheight);
951}
952
954{
955 float dpi, height, width, bmheight, bmwidth;
956
957 // Get all values in px
958 Unit const *unit = units.getUnit();
959 height = unit->convert(spin_buttons[SPIN_HEIGHT]->get_value(), "px");
960 width = unit->convert(spin_buttons[SPIN_WIDTH]->get_value(), "px");
961 bmheight = spin_buttons[SPIN_BMHEIGHT]->get_value();
962 bmwidth = spin_buttons[SPIN_BMWIDTH]->get_value();
963 dpi = spin_buttons[SPIN_DPI]->get_value();
964
965 switch (type) {
966 case SPIN_BMHEIGHT:
967 if (bmheight < SP_EXPORT_MIN_SIZE) {
968 bmheight = SP_EXPORT_MIN_SIZE;
969 }
970 dpi = bmheight * DPI_BASE / height;
971 break;
972 case SPIN_BMWIDTH:
973 if (bmwidth < SP_EXPORT_MIN_SIZE) {
974 bmwidth = SP_EXPORT_MIN_SIZE;
975 }
976 dpi = bmwidth * DPI_BASE / width;
977 break;
978 case SPIN_DPI:
979 prefs->setDouble("/dialogs/export/defaultdpi/value", dpi);
980 break;
981 default:
982 break;
983 }
984
985 bmwidth = floor(width * dpi / DPI_BASE + 0.5);
986 bmheight = floor(height * dpi / DPI_BASE + 0.5);
987
988 spin_buttons[SPIN_BMHEIGHT]->set_value(bmheight);
989 spin_buttons[SPIN_BMWIDTH]->set_value(bmwidth);
990 spin_buttons[SPIN_DPI]->set_value(dpi);
991}
992
994{
995 current_key = (selection_mode)0; // default key
996 bool found = false;
997 Glib::ustring pref_key_name = prefs->getString("/dialogs/export/exportarea/value");
998 for (auto [key, name] : selection_names) {
999 if (pref_key_name == name) {
1000 current_key = key;
1001 found = true;
1002 break;
1003 }
1004 }
1005 if (!found) {
1006 pref_key_name = selection_names[current_key];
1007 }
1008
1009 if (_desktop) {
1010 if (current_key == SELECTION_SELECTION && (_desktop->getSelection())->isEmpty()) {
1012 }
1013 if ((_desktop->getSelection())->isEmpty()) {
1014 selection_buttons[SELECTION_SELECTION]->set_sensitive(false);
1015 }
1017 (spin_buttons[SPIN_HEIGHT]->get_value() == 0 || spin_buttons[SPIN_WIDTH]->get_value() == 0)) {
1019 setArea(bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y]);
1020 }
1021 } else {
1023 }
1024 selection_buttons[current_key]->set_active(true);
1025 prefs->setString("/dialogs/export/exportarea/value", pref_key_name);
1026
1028 refreshPage();
1029}
1030
1031void SingleExport::setExporting(bool exporting, Glib::ustring const &text)
1032{
1033 if (exporting) {
1034 set_sensitive(false);
1035 set_opacity(0.2);
1036 progress_box.set_visible(true);
1037 progress_bar.set_text(text);
1038 progress_bar.set_fraction(0.0);
1039 } else {
1040 set_sensitive(true);
1041 set_opacity(1.0);
1042 progress_box.set_visible(false);
1043 progress_bar.set_text("");
1044 progress_bar.set_fraction(0.0);
1045 }
1046 auto main_context = Glib::MainContext::get_default();
1047 main_context->iteration(false);
1048}
1049
1050// Called for every progress iteration
1051unsigned int SingleExport::onProgressCallback(float value, void *data)
1052{
1053 if (auto si = static_cast<SingleExport *>(data)) {
1054 si->progress_bar.set_fraction(value);
1055 auto main_context = Glib::MainContext::get_default();
1056 main_context->iteration(false);
1057 return !si->interrupted;
1058 }
1059 return false;
1060}
1061
1063{
1064 if (!_desktop) {
1066 return;
1067 }
1068
1069 std::vector<SPItem const *> selected;
1070 if (si_hide_all.get_active()) {
1071 // This is because selection items is not a std::vector yet. FIXME.
1072 auto sel_range = _desktop->getSelection()->items();
1073 selected = {sel_range.begin(), sel_range.end()};
1074 }
1075 _preview_drawing->set_shown_items(std::move(selected));
1076
1077 bool show = si_show_preview.get_active();
1078 if (!show || current_key == SELECTION_PAGE) {
1079 bool have_pages = false;
1080 for (auto const child : UI::get_children(pages_list)) {
1081 if (auto bi = dynamic_cast<BatchItem *>(child)) {
1082 bi->refresh(!show, _background_color.get_current_color().toRGBA());
1083 have_pages = true;
1084 }
1085 }
1086 if (have_pages) {
1087 // We don't want to update the main preview for pages, it's hidden
1089 return;
1090 }
1091 }
1092
1093 Unit const *unit = units.getUnit();
1094 float x0 = unit->convert(spin_buttons[SPIN_X0]->get_value(), "px");
1095 float x1 = unit->convert(spin_buttons[SPIN_X1]->get_value(), "px");
1096 float y0 = unit->convert(spin_buttons[SPIN_Y0]->get_value(), "px");
1097 float y1 = unit->convert(spin_buttons[SPIN_Y1]->get_value(), "px");
1098 preview.setBox(Geom::Rect(x0, y0, x1, y1) * _document->dt2doc());
1101}
1102
1104{
1105 if (desktop != _desktop) {
1106 _page_selected_connection.disconnect();
1107 _desktop = desktop;
1108 }
1109}
1110
1112{
1113 if (_document == document)
1114 return;
1115
1116 _document = document;
1117
1118 _page_selected_connection.disconnect();
1119 _page_modified_connection.disconnect();
1120 _page_changed_connection.disconnect();
1121
1122 if (document) {
1123 auto &pm = document->getPageManager();
1125 _page_modified_connection = pm.connectPageModified(sigc::mem_fun(*this, &SingleExport::onPagesModified));
1126 _page_changed_connection = pm.connectPagesChanged(sigc::mem_fun(*this, &SingleExport::onPagesChanged));
1128 _preview_drawing = std::make_shared<PreviewDrawing>(document);
1130
1131 // Refresh values to sync them with defaults.
1133 refreshArea();
1136 } else {
1137 preview.setDrawing({});
1138 _preview_drawing.reset();
1140 }
1141}
1142
1143SingleExport::~SingleExport() = default;
1144
1145} // namespace Inkscape::UI::Dialog
1146
1147/*
1148 Local Variables:
1149 mode:c++
1150 c-file-style:"stroustrup"
1151 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1152 indent-tabs-mode:nil
1153 fill-column:99
1154 End:
1155*/
1156// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Gtk builder utilities.
uint64_t page
Definition canvas.cpp:171
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Coord x() const noexcept
Definition point.h:104
Axis aligned, non-empty rectangle.
Definition rect.h:92
InkscapeWindow * get_active_window()
uint32_t toRGBA(double opacity=1.0) const
Return an sRGB conversion of the color in RGBA int32 format.
Definition color.cpp:117
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
bool isEmpty()
Returns true if no items are selected.
SPItem * firstItem() const
Returns the first selected item, returns nullptr if no items selected.
Geom::OptRect visualBounds() const
sigc::connection connectPageSelected(const sigc::slot< void(SPPage *)> &slot)
int getPageCount() const
Geom::Rect getSelectedPageRect() const
Returns the selected page rect, OR the viewbox rect.
double getDouble(Glib::ustring const &pref_path, double def=0.0, Glib::ustring const &unit="")
Retrieve a floating point value.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string value.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
static void syncItems(BatchItems &items, std::map< std::string, SPObject * > const &objects, Gtk::FlowBox &container, std::shared_ptr< PreviewDrawing > preview, bool isolate_items)
Add and remove batch items and their previews carefully and insert new ones into the container FlowBo...
void resetPixels(bool new_size=false)
void setBox(Geom::Rect const &bbox)
void setDrawing(std::shared_ptr< PreviewDrawing > drawing)
void setBackgroundColor(std::uint32_t bg_color)
static bool exportRaster(Geom::Rect const &area, unsigned long int const &width, unsigned long int const &height, float const &dpi, guint32 bg_color, Glib::ustring const &filename, bool overwrite, unsigned(*callback)(float, void *), void *data, Inkscape::Extension::Output *extension, std::vector< SPItem const * > *items=nullptr)
Export to raster graphics.
Definition export.cpp:221
static bool exportVector(Inkscape::Extension::Output *extension, SPDocument *doc, Glib::ustring const &filename, bool overwrite, Geom::Rect const &area)
Definition export.cpp:318
static std::string prependDirectory(const std::string &name, const std::string &orig, SPDocument *doc=nullptr)
Adds the full directory path to the final part of a file name.
Definition export.cpp:501
static std::string absolutizePath(SPDocument *doc, const std::string &filename)
Convert path to absolute path.
Definition export.cpp:153
static std::string filePathFromObject(SPDocument *doc, SPObject *obj, const std::string &file_entry_text)
Definition export.cpp:471
static bool checkOrCreateDirectory(std::string const &filename)
Checks if the directory exists and if not, tries to create the directory and if failed,...
Definition export.cpp:193
static std::string defaultFilename(SPDocument *doc, const std::string &filename_entry_text, const std::string &extension)
Definition export.cpp:530
Inkscape::Extension::Output * getExtension()
Returns the Output extension currently selected in this dropdown.
void removeExtension(std::string &filename)
Removes the file extension, if it's one of the extensions in the list.
Gtk::MenuButton * getPrefButton() const
void setExtensionFromFilename(std::string const &filename)
sigc::scoped_connection _page_selected_connection
SingleExport(BaseObjectType *cobject, const Glib::RefPtr< Gtk::Builder > &refGlade)
void setupSpinButton(Gtk::SpinButton *sb, double val, double min, double max, double step, double page, int digits, bool sensitive, void(SingleExport::*cb)(T), T param)
void onAreaTypeToggle(selection_mode key)
void selectionChanged(Inkscape::Selection *selection)
void setDesktop(SPDesktop *desktop)
sigc::scoped_connection _page_modified_connection
void setArea(double x0, double y0, double x1, double y1)
Gtk::ScrolledWindow & pages_list_box
Inkscape::UI::Widget::UnitMenu & units
void onFilenameModified()
Filename in filename entry field was changed.
std::string filepath_native
File path as returned by the file chooser.
Glib::ustring filename_entry_original_value
Last value of filename entry field that was set programmatically. Used to detect modification by the ...
std::map< selection_mode, Gtk::ToggleButton * > selection_buttons
void setFilename(std::string filename, bool modified_by_user)
Set filename and update filename entry box.
sigc::scoped_connection extensionConn
void loadExportHints()
Update suggested DPI and filename when the selection has changed.
Inkscape::Preferences * prefs
sigc::scoped_connection exportConn
static unsigned int onProgressCallback(float value, void *data)
Callback to be used in for loop to update the progress bar.
void selectionModified(Inkscape::Selection *selection, guint flags)
sigc::scoped_connection cancelConn
std::shared_ptr< PreviewDrawing > _preview_drawing
bool filename_modified_by_user
True if the value of the selected filename was changed by the user since the last export.
void setExporting(bool exporting, Glib::ustring const &text="")
std::vector< sigc::scoped_connection > spinButtonConns
sigc::scoped_connection filenameConn
std::map< sb_type, Gtk::SpinButton * > spin_buttons
sigc::scoped_connection browseConn
std::map< sb_type, Gtk::Label * > spin_labels
sigc::scoped_connection _pages_list_changed
std::map< selection_mode, Glib::ustring > selection_names
UI::Widget::ColorPicker & _background_color
void setDocument(SPDocument *document)
sigc::scoped_connection _page_changed_connection
void saveExportHints(SPObject *target)
std::vector< SPPage const * > getSelectedPages() const
Colors::Color get_current_color() const
sigc::connection connectChanged(sigc::slot< void(Colors::Color const &)> slot)
void setColor(Colors::Color const &)
Helperclass for Gtk::Entry widgets.
Definition entry.h:24
A drop down menu for choosing unit types.
Definition unit-menu.h:31
Unit const * getUnit() const
Returns the Unit object corresponding to the current selection in the dropdown widget.
Definition unit-menu.cpp:64
Glib::SignalProxyProperty signal_changed()
bool setUnit(Glib::ustring const &unit)
Sets the dropdown widget to the given unit abbreviation.
Definition unit-menu.cpp:75
bool setUnitType(UnitType unit_type, bool svg_length=false)
Adds the unit type to the widget.
Definition unit-menu.cpp:33
Glib::ustring abbr
Definition units.h:81
To do: update description of desktop.
Definition desktop.h:149
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
SPNamedView * getNamedView() const
Definition desktop.h:191
Inkscape::Selection * getSelection() const
Definition desktop.h:188
Typed SVG document implementation.
Definition document.h:101
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
std::unique_ptr< SPDocument > copy() const
Create a copy of the document, useful for modifying during save & export.
Definition document.cpp:515
const Geom::Affine & dt2doc() const
Desktop to document coordinate transformation.
Definition document.h:268
Geom::OptRect preferredBounds() const
Definition document.cpp:969
Inkscape::PageManager & getPageManager()
Definition document.h:162
SPNamedView * getNamedView()
Get the namedview for this document, creates it if it's not found.
Definition document.cpp:235
Geom::OptRect desktopVisualBounds() const
Get item's visual bbox in desktop coordinate system.
Definition sp-item.cpp:1065
Inkscape::Util::Unit const * display_units
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Glib::ustring getExportFilename() const
Get and set the exportable filename on this object.
Geom::Point getExportDpi() const
Get and set the exported DPI for this objet, if available.
void setExportDpi(Geom::Point dpi)
void setExportFilename(Glib::ustring filename)
Color picker button and window.
Editable view implementation.
TODO: insert short description here.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
auto floor(Geom::Rect const &rect)
Definition geom.h:131
Macro for icon names used in Inkscape.
SPItem * item
Inkscape - An SVG editor.
Definition desktop.h:50
bool filesystem_is_sandboxed()
Query if the filesystem is "sandboxed", e.g., by using xdg-portal in flatpak/snap.
Definition sandbox.cpp:17
Glib::ustring filesystem_get_display_path(std::optional< Glib::RefPtr< Gio::File const > > path, Glib::ustring placeholder_if_empty)
Translate raw filesystem path to a path suitable for display.
Definition sandbox.cpp:27
Dialog code.
Definition desktop.h:117
Glib::RefPtr< Gio::ListStore< Gtk::FileFilter > > create_export_filters(bool for_save)
Create a Gtk::FileFilter for all export file types.
Inkscape::Colors::Color get_export_bg_color(SPObject *object, Inkscape::Colors::Color const &default_color)
Definition export.cpp:550
static void set_sensitive(Gtk::SearchEntry2 &entry, bool const sensitive)
void set_export_bg_color(SPObject *object, Inkscape::Colors::Color const &color)
Definition export.cpp:544
constexpr auto SP_EXPORT_MIN_SIZE
constexpr auto EXPORT_COORD_PRECISION
static constexpr int height
std::vector< Gtk::Widget * > get_children(Gtk::Widget &widget)
Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
Definition util.cpp:156
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
Gtk::Widget * for_each_child(Gtk::Widget &widget, Func &&func, bool const plus_self=false, bool const recurse=false, int const level=0)
Call Func with a reference to each child of parent, until it returns _break.
Definition util.h:103
W & get_derived_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id, Args &&... args)
@ UNIT_TYPE_LINEAR
Definition units.h:32
Glib::RefPtr< Gio::File > choose_file_save(Glib::ustring const &title, Gtk::Window *parent, Glib::RefPtr< Gio::ListStore< Gtk::FileFilter > > const &filters_model, std::string const &file_name, std::string &current_folder)
Synchronously run a Gtk::FileDialog to select a file for saving data.
static cairo_user_data_key_t key
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
GList * items
Inkscape::IO::Sandbox.
SPPage – a page object.
SPRoot: SVG <svg> implementation.
static const Point data[]
Gtk::Picture & preview
SPDesktop * desktop
double width
Glib::ustring name
Definition toolbars.cpp:55
Glib::RefPtr< Gtk::Builder > builder