diff --git a/share/extensions b/share/extensions
index 6c9b68507be427bffba23507bbaacf3f8a0f3752..d99349e15e2342dd9af99afb345a98df02ab1d1b 160000
--- a/share/extensions
+++ b/share/extensions
@@ -1 +1 @@
-Subproject commit 6c9b68507be427bffba23507bbaacf3f8a0f3752
+Subproject commit d99349e15e2342dd9af99afb345a98df02ab1d1b
diff --git a/share/ui/dialog-export.glade b/share/ui/dialog-export.glade
new file mode 100644
index 0000000000000000000000000000000000000000..ff23a4709eb20fcc93ada8f22776eb6680eea7db
--- /dev/null
+++ b/share/ui/dialog-export.glade
@@ -0,0 +1,1100 @@
+
+
+
+
+
+
diff --git a/share/ui/style.css b/share/ui/style.css
index 15cb1e3a29f0cc1f7a8eebbe8082ba5b659eea4d..3f9f8cabaa5dbfb29bb28bc4483f0ea46a76cd92 100644
--- a/share/ui/style.css
+++ b/share/ui/style.css
@@ -614,3 +614,14 @@ button.square-button image {
.wide-apply-button {
min-width: 150px;
}
+/*
+ * Inkscape Export Dialog
+ */
+
+#ExportDialog #export_selection{
+ border-radius:0px;
+}
+#ExportDialog #export_preview_box{
+ border:1px solid;
+}
+
diff --git a/src/object/sp-object.cpp b/src/object/sp-object.cpp
index fbae850d74ceb3e86cbe40be771a6869aeb1388d..d06f193083a3a47fd29828a8687f643cd674f455 100644
--- a/src/object/sp-object.cpp
+++ b/src/object/sp-object.cpp
@@ -16,27 +16,27 @@
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
+#include
#include
+#include
#include
#include
-#include
-
-#include
-#include "helper/sp-marshal.h"
-#include "xml/node-event-vector.h"
-#include "attributes.h"
#include "attribute-rel-util.h"
+#include "attributes.h"
#include "color-profile.h"
+#include "debug/demangle.h"
+#include "debug/event-tracker.h"
+#include "debug/simple-event.h"
#include "document.h"
-#include "preferences.h"
-#include "style.h"
+#include "helper/sp-marshal.h"
#include "live_effects/lpeobject.h"
+#include "preferences.h"
#include "sp-factory.h"
#include "sp-paint-server.h"
#include "sp-root.h"
-#include "sp-style-elem.h"
#include "sp-script.h"
+#include "sp-style-elem.h"
#include "streq.h"
#include "strneq.h"
#include "xml/node-fns.h"
@@ -44,34 +44,36 @@
#include "debug/simple-event.h"
#include "debug/demangle.h"
#include "svg/css-ostringstream.h"
+#include "style.h"
#include "util/format.h"
#include "util/longest-common-suffix.h"
+#include "xml/node-event-vector.h"
+#include "xml/node-fns.h"
#define noSP_OBJECT_DEBUG_CASCADE
#define noSP_OBJECT_DEBUG
#ifdef SP_OBJECT_DEBUG
-# define debug(f, a...) { g_print("%s(%d) %s:", \
- __FILE__,__LINE__,__FUNCTION__); \
- g_print(f, ## a); \
- g_print("\n"); \
- }
+#define debug(f, a...) \
+ { \
+ g_print("%s(%d) %s:", __FILE__, __LINE__, __FUNCTION__); \
+ g_print(f, ##a); \
+ g_print("\n"); \
+ }
#else
-# define debug(f, a...) /* */
+#define debug(f, a...) /* */
#endif
// Define to enable indented tracing of SPObject.
//#define OBJECT_TRACE
unsigned SPObject::indent_level = 0;
-Inkscape::XML::NodeEventVector object_event_vector = {
- SPObject::repr_child_added,
- SPObject::repr_child_removed,
- SPObject::repr_attr_changed,
- SPObject::repr_content_changed,
- SPObject::repr_order_changed
-};
+//clang-format off
+Inkscape::XML::NodeEventVector object_event_vector = {SPObject::repr_child_added, SPObject::repr_child_removed,
+ SPObject::repr_attr_changed, SPObject::repr_content_changed,
+ SPObject::repr_order_changed};
+//clang-format on
/**
* A friend class used to set internal members on SPObject so as to not expose settors in SPObject's public API
@@ -79,26 +81,27 @@ Inkscape::XML::NodeEventVector object_event_vector = {
class SPObjectImpl
{
public:
-
-/**
- * Null's the id member of an SPObject without attempting to free prior contents.
- *
- * @param[inout] obj Pointer to the object which's id shall be nulled.
- */
- static void setIdNull( SPObject* obj ) {
+ /**
+ * Null's the id member of an SPObject without attempting to free prior contents.
+ *
+ * @param[inout] obj Pointer to the object which's id shall be nulled.
+ */
+ static void setIdNull(SPObject *obj)
+ {
if (obj) {
obj->id = nullptr;
}
}
-/**
- * Sets the id member of an object, freeing any prior content.
- *
- * @param[inout] obj Pointer to the object which's id shall be set.
- * @param[in] id New id
- */
- static void setId( SPObject* obj, gchar const* id ) {
- if (obj && (id != obj->id) ) {
+ /**
+ * Sets the id member of an object, freeing any prior content.
+ *
+ * @param[inout] obj Pointer to the object which's id shall be set.
+ * @param[in] id New id
+ */
+ static void setId(SPObject *obj, gchar const *id)
+ {
+ if (obj && (id != obj->id)) {
if (obj->id) {
g_free(obj->id);
obj->id = nullptr;
@@ -114,14 +117,26 @@ public:
* Constructor, sets all attributes to default values.
*/
SPObject::SPObject()
- : cloned(0), clone_original(nullptr), uflags(0), mflags(0), hrefcount(0), _total_hrefcount(0),
- document(nullptr), parent(nullptr), id(nullptr), repr(nullptr), refCount(1), hrefList(std::list()),
- _successor(nullptr), _collection_policy(SPObject::COLLECT_WITH_PARENT),
- _label(nullptr), _default_label(nullptr)
+ : cloned(0)
+ , clone_original(nullptr)
+ , uflags(0)
+ , mflags(0)
+ , hrefcount(0)
+ , _total_hrefcount(0)
+ , document(nullptr)
+ , parent(nullptr)
+ , id(nullptr)
+ , repr(nullptr)
+ , refCount(1)
+ , hrefList(std::list())
+ , _successor(nullptr)
+ , _collection_policy(SPObject::COLLECT_WITH_PARENT)
+ , _label(nullptr)
+ , _default_label(nullptr)
{
- debug("id=%p, typename=%s",this, g_type_name_from_instance((GTypeInstance*)this));
+ debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance *)this));
- //used XML Tree here.
+ // used XML Tree here.
this->getRepr(); // TODO check why this call is made
SPObjectImpl::setIdNull(this);
@@ -130,14 +145,15 @@ SPObject::SPObject()
// vg, g, defs, desc, title, symbol, use, image, switch, path, rect, circle, ellipse, line, polyline,
// polygon, text, tspan, tref, textPath, altGlyph, glyphRef, marker, linearGradient, radialGradient,
// stop, pattern, clipPath, mask, filter, feImage, a, font, glyph, missing-glyph, foreignObject
- this->style = new SPStyle( nullptr, this ); // Is it necessary to call with "this"?
+ this->style = new SPStyle(nullptr, this); // Is it necessary to call with "this"?
this->context_style = nullptr;
}
/**
* Destructor, frees the used memory and unreferences a potential successor of the object.
*/
-SPObject::~SPObject() {
+SPObject::~SPObject()
+{
g_free(this->_label);
g_free(this->_default_label);
@@ -152,11 +168,11 @@ SPObject::~SPObject() {
parent->children.erase(parent->children.iterator_to(*this));
}
- if( style == nullptr ) {
+ if (style == nullptr) {
// style pointer could be NULL if unreffed too many times.
// Conjecture: style pointer is never NULL.
std::cerr << "SPObject::~SPObject(): style pointer is NULL" << std::endl;
- } else if( style->refCount() > 1 ) {
+ } else if (style->refCount() > 1) {
// Conjecture: style pointer should be unreffed by other classes before reaching here.
// Conjecture is false for SPTSpan where ref is held by InputStreamTextSource.
// As an additional note:
@@ -165,27 +181,30 @@ SPObject::~SPObject() {
// one for the one after, along with one for each corresponding DrawingText instance.
// std::cerr << "SPObject::~SPObject(): someone else still holding ref to style" << std::endl;
//
- sp_style_unref( this->style );
+ sp_style_unref(this->style);
} else {
delete this->style;
}
}
// CPPIFY: make pure virtual
-void SPObject::read_content() {
- //throw;
+void SPObject::read_content()
+{
+ // throw;
}
-void SPObject::update(SPCtx* /*ctx*/, unsigned int /*flags*/) {
- //throw;
+void SPObject::update(SPCtx * /*ctx*/, unsigned int /*flags*/)
+{
+ // throw;
}
-void SPObject::modified(unsigned int /*flags*/) {
+void SPObject::modified(unsigned int /*flags*/)
+{
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::modified (default) (empty function)" );
- objectTrace( "SPObject::modified (default)", false );
+ objectTrace("SPObject::modified (default) (empty function)");
+ objectTrace("SPObject::modified (default)", false);
#endif
- //throw;
+ // throw;
}
namespace {
@@ -195,10 +214,11 @@ namespace Util = Inkscape::Util;
typedef Debug::SimpleEvent BaseRefCountEvent;
-class RefCountEvent : public BaseRefCountEvent {
+class RefCountEvent : public BaseRefCountEvent
+{
public:
RefCountEvent(SPObject *object, int bias, char const *name)
- : BaseRefCountEvent(name)
+ : BaseRefCountEvent(name)
{
_addProperty("object", Util::format("%p", object).pointer());
_addProperty("class", Debug::demangle(typeid(*object).name()));
@@ -206,45 +226,50 @@ public:
}
};
-class RefEvent : public RefCountEvent {
+class RefEvent : public RefCountEvent
+{
public:
RefEvent(SPObject *object)
- : RefCountEvent(object, 1, "sp-object-ref")
+ : RefCountEvent(object, 1, "sp-object-ref")
{}
};
-class UnrefEvent : public RefCountEvent {
+class UnrefEvent : public RefCountEvent
+{
public:
UnrefEvent(SPObject *object)
- : RefCountEvent(object, -1, "sp-object-unref")
+ : RefCountEvent(object, -1, "sp-object-unref")
{}
};
-}
+} // namespace
-gchar const* SPObject::getId() const {
+gchar const *SPObject::getId() const
+{
return id;
}
/**
* Returns the id as a url param, in the form 'url(#{id})'
*/
-std::string SPObject::getUrl() const {
+std::string SPObject::getUrl() const
+{
if (id) {
return std::string("url(#") + id + ")";
}
return "";
}
-Inkscape::XML::Node * SPObject::getRepr() {
+Inkscape::XML::Node *SPObject::getRepr()
+{
return repr;
}
-Inkscape::XML::Node const* SPObject::getRepr() const{
+Inkscape::XML::Node const *SPObject::getRepr() const
+{
return repr;
}
-
SPObject *sp_object_ref(SPObject *object, SPObject *owner)
{
g_return_val_if_fail(object != nullptr, NULL);
@@ -275,7 +300,7 @@ SPObject *sp_object_unref(SPObject *object, SPObject *owner)
return nullptr;
}
-void SPObject::hrefObject(SPObject* owner)
+void SPObject::hrefObject(SPObject *owner)
{
// if (owner) std::cout << " owner: " << *owner << std::endl;
@@ -285,11 +310,11 @@ void SPObject::hrefObject(SPObject* owner)
_updateTotalHRefCount(1);
}
- if(owner)
+ if (owner)
hrefList.push_front(owner);
}
-void SPObject::unhrefObject(SPObject* owner)
+void SPObject::unhrefObject(SPObject *owner)
{
g_return_if_fail(hrefcount > 0);
@@ -298,20 +323,19 @@ void SPObject::unhrefObject(SPObject* owner)
_updateTotalHRefCount(-1);
}
- if(owner)
+ if (owner)
hrefList.remove(owner);
}
-void SPObject::_updateTotalHRefCount(int increment) {
+void SPObject::_updateTotalHRefCount(int increment)
+{
SPObject *topmost_collectable = nullptr;
- for ( SPObject *iter = this ; iter ; iter = iter->parent ) {
+ for (SPObject *iter = this; iter; iter = iter->parent) {
iter->_total_hrefcount += increment;
- if ( iter->_total_hrefcount < iter->hrefcount ) {
+ if (iter->_total_hrefcount < iter->hrefcount) {
g_critical("HRefs overcounted");
}
- if ( iter->_total_hrefcount == 0 &&
- iter->_collection_policy != COLLECT_WITH_PARENT )
- {
+ if (iter->_total_hrefcount == 0 && iter->_collection_policy != COLLECT_WITH_PARENT) {
topmost_collectable = iter;
}
}
@@ -320,11 +344,12 @@ void SPObject::_updateTotalHRefCount(int increment) {
}
}
-bool SPObject::isAncestorOf(SPObject const *object) const {
+bool SPObject::isAncestorOf(SPObject const *object) const
+{
g_return_val_if_fail(object != nullptr, false);
object = object->parent;
while (object) {
- if ( object == this ) {
+ if (object == this) {
return true;
}
object = object->parent;
@@ -332,16 +357,18 @@ bool SPObject::isAncestorOf(SPObject const *object) const {
return false;
}
-SPObject const *SPObject::nearestCommonAncestor(SPObject const *object) const {
+SPObject const *SPObject::nearestCommonAncestor(SPObject const *object) const
+{
g_return_val_if_fail(object != nullptr, NULL);
using Inkscape::Algorithms::nearest_common_ancestor;
return nearest_common_ancestor(this, object, nullptr);
}
-static SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor) {
+static SPObject const *AncestorSon(SPObject const *obj, SPObject const *ancestor)
+{
SPObject const *result = nullptr;
- if ( obj && ancestor ) {
+ if (obj && ancestor) {
if (obj->parent == ancestor) {
result = obj;
} else {
@@ -357,7 +384,7 @@ int sp_object_compare_position(SPObject const *first, SPObject const *second)
if (first != second) {
SPObject const *ancestor = first->nearestCommonAncestor(second);
// Need a common ancestor to be able to compare
- if ( ancestor ) {
+ if (ancestor) {
// we have an object and its ancestor (should not happen when sorting selection)
if (ancestor == first) {
result = 1;
@@ -376,13 +403,14 @@ int sp_object_compare_position(SPObject const *first, SPObject const *second)
return result;
}
-bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second){
- return sp_object_compare_position(first,second)<0;
+bool sp_object_compare_position_bool(SPObject const *first, SPObject const *second)
+{
+ return sp_object_compare_position(first, second) < 0;
}
-
-SPObject *SPObject::appendChildRepr(Inkscape::XML::Node *repr) {
- if ( !cloned ) {
+SPObject *SPObject::appendChildRepr(Inkscape::XML::Node *repr)
+{
+ if (!cloned) {
getRepr()->appendChild(repr);
return document->getObjectByRepr(repr);
} else {
@@ -403,9 +431,10 @@ void SPObject::changeCSS(SPCSSAttr *css, gchar const *attr)
sp_repr_css_change(this->getRepr(), css, attr);
}
-std::vector SPObject::childList(bool add_ref, Action) {
- std::vector l;
- for (auto& child: children) {
+std::vector SPObject::childList(bool add_ref, Action)
+{
+ std::vector l;
+ for (auto &child : children) {
if (add_ref) {
sp_object_ref(&child);
}
@@ -414,10 +443,10 @@ std::vector SPObject::childList(bool add_ref, Action) {
return l;
}
-std::vector SPObject::ancestorList(bool root_to_tip)
+std::vector SPObject::ancestorList(bool root_to_tip)
{
std::vector ancestors;
- for (SPObject::ParentIterator iter=parent ; iter ; ++iter) {
+ for (SPObject::ParentIterator iter = parent; iter; ++iter) {
ancestors.push_back(iter);
}
if (root_to_tip) {
@@ -426,11 +455,13 @@ std::vector SPObject::ancestorList(bool root_to_tip)
return ancestors;
}
-gchar const *SPObject::label() const {
+gchar const *SPObject::label() const
+{
return _label;
}
-gchar const *SPObject::defaultLabel() const {
+gchar const *SPObject::defaultLabel() const
+{
if (_label) {
return _label;
} else {
@@ -452,8 +483,8 @@ void SPObject::setLabel(gchar const *label)
getRepr()->setAttribute("inkscape:label", label);
}
-
-void SPObject::requestOrphanCollection() {
+void SPObject::requestOrphanCollection()
+{
g_return_if_fail(document != nullptr);
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
@@ -462,7 +493,8 @@ void SPObject::requestOrphanCollection() {
// leave it
} else if (dynamic_cast(this)) {
// leave it
- } else if ((! prefs->getBool("/options/cleanupswatches/value", false)) && SP_IS_PAINT_SERVER(this) && static_cast(this)->isSwatch() ) {
+ } else if ((!prefs->getBool("/options/cleanupswatches/value", false)) && SP_IS_PAINT_SERVER(this) &&
+ static_cast(this)->isSwatch()) {
// leave it
} else if (IS_COLORPROFILE(this)) {
// leave it
@@ -485,8 +517,9 @@ void SPObject::requestOrphanCollection() {
}
}
-void SPObject::_sendDeleteSignalRecursive() {
- for (auto& child: children) {
+void SPObject::_sendDeleteSignalRecursive()
+{
+ for (auto &child : children) {
child._delete_signal.emit(&child);
child._sendDeleteSignalRecursive();
}
@@ -495,7 +528,7 @@ void SPObject::_sendDeleteSignalRecursive() {
void SPObject::deleteObject(bool propagate, bool propagate_descendants)
{
sp_object_ref(this, nullptr);
- if ( SP_IS_LPE_ITEM(this) && SP_LPE_ITEM(this)->hasPathEffect()) {
+ if (SP_IS_LPE_ITEM(this) && SP_LPE_ITEM(this)->hasPathEffect()) {
SP_LPE_ITEM(this)->removeAllPathEffects(false);
}
if (propagate) {
@@ -504,7 +537,7 @@ void SPObject::deleteObject(bool propagate, bool propagate_descendants)
if (propagate_descendants) {
this->_sendDeleteSignalRecursive();
}
-
+
Inkscape::XML::Node *repr = getRepr();
if (repr && repr->parent()) {
sp_repr_unparent(repr);
@@ -518,18 +551,60 @@ void SPObject::deleteObject(bool propagate, bool propagate_descendants)
void SPObject::cropToObject(SPObject *except)
{
- std::vector toDelete;
- for (auto& child: children) {
+ std::vector toDelete;
+ for (auto &child : children) {
if (SP_IS_ITEM(&child)) {
if (child.isAncestorOf(except)) {
child.cropToObject(except);
- } else if(&child != except) {
+ } else if (&child != except) {
sp_object_ref(&child, nullptr);
toDelete.push_back(&child);
}
}
}
- for (auto & i : toDelete) {
+ for (auto &i : toDelete) {
+ i->deleteObject(true, true);
+ sp_object_unref(i, nullptr);
+ }
+}
+
+// Removes objects which are not related to given list of objects.
+// Use Case: Group[MyRect1 , MyRect2] , MyRect3
+// List Provided: MyRect1, MyRect3
+// Output doc: Group[MyRect1], MyRect3
+// List Provided: MyRect1, Group
+// Output doc: Group[MyRect1, MyRect2] (notice MyRect2 is not deleted as it is related to Group)
+
+void SPObject::cropToObjects(std::vector except_objects)
+{
+ if (except_objects.empty()) {
+ return;
+ }
+ std::vector toDelete;
+ for (auto &child : children) {
+ if (SP_IS_ITEM(&child)) {
+ std::vector except_in_child;
+ bool child_delete_flag = true;
+ for (auto except : except_objects) {
+ if (&child == except) {
+ child_delete_flag = false;
+ except_in_child.clear();
+ break;
+ }
+ if (child.isAncestorOf(except)) {
+ except_in_child.push_back(except);
+ child_delete_flag = false;
+ }
+ }
+ if (child_delete_flag) {
+ sp_object_ref(&child, nullptr);
+ toDelete.push_back(&child);
+ } else {
+ child.cropToObjects(except_in_child);
+ }
+ }
+ }
+ for (auto &i : toDelete) {
i->deleteObject(true, true);
sp_object_unref(i, nullptr);
}
@@ -537,8 +612,8 @@ void SPObject::cropToObject(SPObject *except)
void SPObject::attach(SPObject *object, SPObject *prev)
{
- //g_return_if_fail(parent != NULL);
- //g_return_if_fail(SP_IS_OBJECT(parent));
+ // g_return_if_fail(parent != NULL);
+ // g_return_if_fail(SP_IS_OBJECT(parent));
g_return_if_fail(object != nullptr);
g_return_if_fail(SP_IS_OBJECT(object));
g_return_if_fail(!prev || SP_IS_OBJECT(prev));
@@ -559,7 +634,8 @@ void SPObject::attach(SPObject *object, SPObject *prev)
object->xml_space.value = this->xml_space.value;
}
-void SPObject::reorder(SPObject* obj, SPObject* prev) {
+void SPObject::reorder(SPObject *obj, SPObject *prev)
+{
g_return_if_fail(obj != nullptr);
g_return_if_fail(obj->parent);
g_return_if_fail(obj->parent == this);
@@ -576,8 +652,8 @@ void SPObject::reorder(SPObject* obj, SPObject* prev) {
void SPObject::detach(SPObject *object)
{
- //g_return_if_fail(parent != NULL);
- //g_return_if_fail(SP_IS_OBJECT(parent));
+ // g_return_if_fail(parent != NULL);
+ // g_return_if_fail(SP_IS_OBJECT(parent));
g_return_if_fail(object != nullptr);
g_return_if_fail(SP_IS_OBJECT(object));
g_return_if_fail(object->parent == this);
@@ -597,9 +673,9 @@ SPObject *SPObject::get_child_by_repr(Inkscape::XML::Node *repr)
SPObject *result = nullptr;
if (children.size() > 0 && children.back().getRepr() == repr) {
- result = &children.back(); // optimization for common scenario
+ result = &children.back(); // optimization for common scenario
} else {
- for (auto& child: children) {
+ for (auto &child : children) {
if (child.getRepr() == repr) {
result = &child;
break;
@@ -635,12 +711,13 @@ static SPObject *get_closest_child_by_repr(SPObject &obj, Inkscape::XML::Node *r
return nullptr;
}
-void SPObject::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) {
- SPObject* object = this;
+void SPObject::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
+{
+ SPObject *object = this;
const std::string type_string = NodeTraits::get_type_string(*child);
- SPObject* ochild = SPFactory::createObject(type_string);
+ SPObject *ochild = SPFactory::createObject(type_string);
if (ochild == nullptr) {
// Currently, there are many node types that do not have
// corresponding classes in the SPObject tree.
@@ -656,19 +733,21 @@ void SPObject::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
ochild->invoke_build(object->document, child, object->cloned);
}
-void SPObject::release() {
- SPObject* object = this;
- debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
- auto tmp = children | boost::adaptors::transformed([](SPObject& obj){return &obj;});
+void SPObject::release()
+{
+ SPObject *object = this;
+ debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance *)object));
+ auto tmp = children | boost::adaptors::transformed([](SPObject &obj) { return &obj; });
std::vector toRelease(tmp.begin(), tmp.end());
- for (auto& p: toRelease) {
+ for (auto &p : toRelease) {
object->detach(p);
}
}
-void SPObject::remove_child(Inkscape::XML::Node* child) {
- debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
+void SPObject::remove_child(Inkscape::XML::Node *child)
+{
+ debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance *)this));
SPObject *ochild = this->get_child_by_repr(child);
@@ -678,8 +757,10 @@ void SPObject::remove_child(Inkscape::XML::Node* child) {
}
}
-void SPObject::order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node * /*old_ref*/, Inkscape::XML::Node *new_ref) {
- SPObject* object = this;
+void SPObject::order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node * /*old_ref*/,
+ Inkscape::XML::Node *new_ref)
+{
+ SPObject *object = this;
SPObject *ochild = object->get_child_by_repr(child);
g_return_if_fail(ochild != nullptr);
@@ -688,19 +769,19 @@ void SPObject::order_changed(Inkscape::XML::Node *child, Inkscape::XML::Node * /
ochild->_position_changed_signal.emit(ochild);
}
-void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) {
-
+void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr)
+{
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::build" );
+ objectTrace("SPObject::build");
#endif
- SPObject* object = this;
+ SPObject *object = this;
/* Nothing specific here */
- debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance*)object));
+ debug("id=%p, typename=%s", object, g_type_name_from_instance((GTypeInstance *)object));
object->readAttr(SPAttr::XML_SPACE);
object->readAttr(SPAttr::LANG);
- object->readAttr(SPAttr::XML_LANG); // "xml:lang" overrides "lang" per spec, read it last.
+ object->readAttr(SPAttr::XML_LANG); // "xml:lang" overrides "lang" per spec, read it last.
object->readAttr(SPAttr::INKSCAPE_LABEL);
object->readAttr(SPAttr::INKSCAPE_COLLECT);
@@ -709,15 +790,15 @@ void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) {
lang = object->parent->lang;
}
- if(object->cloned && (repr->attribute("id")) ) // The cases where this happens are when the "original" has no id. This happens
- // if it is a SPString (a TextNode, e.g. in a ), or when importing
- // stuff externally modified to have no id.
+ if (object->cloned && (repr->attribute("id"))) // The cases where this happens are when the "original" has no id.
+ // This happens if it is a SPString (a TextNode, e.g. in a ),
+ // or when importing stuff externally modified to have no id.
object->clone_original = document->getObjectById(repr->attribute("id"));
- for (Inkscape::XML::Node *rchild = repr->firstChild() ; rchild != nullptr; rchild = rchild->next()) {
+ for (Inkscape::XML::Node *rchild = repr->firstChild(); rchild != nullptr; rchild = rchild->next()) {
const std::string typeString = NodeTraits::get_type_string(*rchild);
- SPObject* child = SPFactory::createObject(typeString);
+ SPObject *child = SPFactory::createObject(typeString);
if (child == nullptr) {
// Currently, there are many node types that do not have
// corresponding classes in the SPObject tree.
@@ -732,19 +813,19 @@ void SPObject::build(SPDocument *document, Inkscape::XML::Node *repr) {
}
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::build", false );
+ objectTrace("SPObject::build", false);
#endif
}
void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, unsigned int cloned)
{
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::invoke_build" );
+ objectTrace("SPObject::invoke_build");
#endif
- debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance*)this));
+ debug("id=%p, typename=%s", this, g_type_name_from_instance((GTypeInstance *)this));
- //g_assert(object != NULL);
- //g_assert(SP_IS_OBJECT(object));
+ // g_assert(object != NULL);
+ // g_assert(SP_IS_OBJECT(object));
g_assert(document != nullptr);
g_assert(repr != nullptr);
@@ -764,7 +845,7 @@ void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, uns
/* Invoke derived methods, if any */
this->build(document, repr);
- if ( !cloned ) {
+ if (!cloned) {
this->document->bindObjectToRepr(this->repr, this);
if (Inkscape::XML::id_permitted(this->repr)) {
@@ -797,12 +878,11 @@ void SPObject::invoke_build(SPDocument *document, Inkscape::XML::Node *repr, uns
g_assert(this->getId() == nullptr);
}
-
/* Signalling (should be connected AFTER processing derived methods */
sp_repr_add_listener(repr, &object_event_vector, this);
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::invoke_build", false );
+ objectTrace("SPObject::invoke_build", false);
#endif
}
@@ -811,24 +891,27 @@ int SPObject::getIntAttribute(char const *key, int def)
return getRepr()->getAttributeInt(key, def);
}
-unsigned SPObject::getPosition(){
+unsigned SPObject::getPosition()
+{
g_assert(this->repr);
return repr->position();
}
-void SPObject::appendChild(Inkscape::XML::Node *child) {
+void SPObject::appendChild(Inkscape::XML::Node *child)
+{
g_assert(this->repr);
repr->appendChild(child);
}
-SPObject* SPObject::nthChild(unsigned index) {
+SPObject *SPObject::nthChild(unsigned index)
+{
g_assert(this->repr);
if (hasChildren()) {
- std::vector l;
+ std::vector l;
unsigned counter = 0;
- for (auto& child: children) {
+ for (auto &child : children) {
if (counter == index) {
return &child;
}
@@ -838,14 +921,15 @@ SPObject* SPObject::nthChild(unsigned index) {
return nullptr;
}
-void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node * prev)
+void SPObject::addChild(Inkscape::XML::Node *child, Inkscape::XML::Node *prev)
{
g_assert(this->repr);
- repr->addChild(child,prev);
+ repr->addChild(child, prev);
}
-void SPObject::releaseReferences() {
+void SPObject::releaseReferences()
+{
g_assert(this->document);
g_assert(this->repr);
@@ -884,7 +968,6 @@ void SPObject::releaseReferences() {
this->repr = nullptr;
}
-
SPObject *SPObject::getPrev()
{
SPObject *prev = nullptr;
@@ -894,7 +977,7 @@ SPObject *SPObject::getPrev()
return prev;
}
-SPObject* SPObject::getNext()
+SPObject *SPObject::getNext()
{
SPObject *next = nullptr;
if (parent && !parent->children.empty() && &parent->children.back() != this) {
@@ -903,47 +986,49 @@ SPObject* SPObject::getNext()
return next;
}
-void SPObject::repr_child_added(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref, gpointer data)
+void SPObject::repr_child_added(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *ref,
+ gpointer data)
{
auto object = static_cast(data);
object->child_added(child, ref);
}
-void SPObject::repr_child_removed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node * /*ref*/, gpointer data)
+void SPObject::repr_child_removed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child,
+ Inkscape::XML::Node * /*ref*/, gpointer data)
{
auto object = static_cast(data);
object->remove_child(child);
}
-void SPObject::repr_order_changed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old, Inkscape::XML::Node *newer, gpointer data)
+void SPObject::repr_order_changed(Inkscape::XML::Node * /*repr*/, Inkscape::XML::Node *child, Inkscape::XML::Node *old,
+ Inkscape::XML::Node *newer, gpointer data)
{
auto object = static_cast(data);
object->order_changed(child, old, newer);
}
-void SPObject::set(SPAttr key, gchar const* value) {
-
+void SPObject::set(SPAttr key, gchar const *value)
+{
#ifdef OBJECT_TRACE
std::stringstream temp;
- temp << "SPObject::set: " << sp_attribute_name(key) << " " << (value?value:"null");
- objectTrace( temp.str() );
+ temp << "SPObject::set: " << sp_attribute_name(key) << " " << (value ? value : "null");
+ objectTrace(temp.str());
#endif
g_assert(key != SPAttr::INVALID);
- SPObject* object = this;
+ SPObject *object = this;
switch (key) {
-
case SPAttr::ID:
- //XML Tree being used here.
- if ( !object->cloned && object->getRepr()->type() == Inkscape::XML::NodeType::ELEMENT_NODE ) {
- SPDocument *document=object->document;
- SPObject *conflict=nullptr;
+ // XML Tree being used here.
+ if (!object->cloned && object->getRepr()->type() == Inkscape::XML::NodeType::ELEMENT_NODE) {
+ SPDocument *document = object->document;
+ SPObject *conflict = nullptr;
gchar const *new_id = value;
@@ -951,7 +1036,7 @@ void SPObject::set(SPAttr key, gchar const* value) {
conflict = document->getObjectById((char const *)new_id);
}
- if ( conflict && conflict != object ) {
+ if (conflict && conflict != object) {
if (!document->isSeeking()) {
sp_object_ref(conflict, nullptr);
// give the conflicting object a new ID
@@ -991,7 +1076,7 @@ void SPObject::set(SPAttr key, gchar const* value) {
break;
case SPAttr::INKSCAPE_COLLECT:
- if ( value && !std::strcmp(value, "always") ) {
+ if (value && !std::strcmp(value, "always")) {
object->setCollectionPolicy(SPObject::ALWAYS_COLLECT);
} else {
object->setCollectionPolicy(SPObject::COLLECT_WITH_PARENT);
@@ -1028,7 +1113,7 @@ void SPObject::set(SPAttr key, gchar const* value) {
break;
case SPAttr::STYLE:
- object->style->readFromObject( object );
+ object->style->readFromObject(object);
object->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_STYLE_MODIFIED_FLAG);
break;
@@ -1036,14 +1121,14 @@ void SPObject::set(SPAttr key, gchar const* value) {
break;
}
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::set", false );
+ objectTrace("SPObject::set", false);
#endif
}
void SPObject::setKeyValue(SPAttr key, gchar const *value)
{
- //g_assert(object != NULL);
- //g_assert(SP_IS_OBJECT(object));
+ // g_assert(object != NULL);
+ // g_assert(SP_IS_OBJECT(object));
this->set(key, value);
}
@@ -1062,11 +1147,11 @@ void SPObject::readAttr(SPAttr keyid)
void SPObject::readAttr(gchar const *key)
{
- //g_assert(object != NULL);
- //g_assert(SP_IS_OBJECT(object));
+ // g_assert(object != NULL);
+ // g_assert(SP_IS_OBJECT(object));
g_assert(key != nullptr);
- //XML Tree being used here.
+ // XML Tree being used here.
g_assert(this->getRepr() != nullptr);
auto keyid = sp_attribute_lookup(key);
@@ -1078,7 +1163,8 @@ void SPObject::readAttr(gchar const *key)
}
}
-void SPObject::repr_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const *key, gchar const * /*oldval*/, gchar const * /*newval*/, bool is_interactive, gpointer data)
+void SPObject::repr_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const *key, gchar const * /*oldval*/,
+ gchar const * /*newval*/, bool is_interactive, gpointer data)
{
auto object = static_cast(data);
@@ -1091,7 +1177,8 @@ void SPObject::repr_attr_changed(Inkscape::XML::Node * /*repr*/, gchar const *ke
}
}
-void SPObject::repr_content_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*oldcontent*/, gchar const * /*newcontent*/, gpointer data)
+void SPObject::repr_content_changed(Inkscape::XML::Node * /*repr*/, gchar const * /*oldcontent*/,
+ gchar const * /*newcontent*/, gpointer data)
{
auto object = static_cast(data);
@@ -1113,14 +1200,15 @@ static gchar const *sp_xml_get_space_string(unsigned int space)
}
}
-Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags) {
+Inkscape::XML::Node *SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, guint flags)
+{
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::write" );
+ objectTrace("SPObject::write");
#endif
if (!repr && (flags & SP_OBJECT_WRITE_BUILD)) {
repr = this->getRepr()->duplicate(doc);
- if (!( flags & SP_OBJECT_WRITE_EXT )) {
+ if (!(flags & SP_OBJECT_WRITE_EXT)) {
repr->removeAttribute("inkscape:collect");
}
} else if (repr) {
@@ -1132,9 +1220,7 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML
repr->setAttribute("xml:space", xml_space);
}
- if ( flags & SP_OBJECT_WRITE_EXT &&
- this->collectionPolicy() == SPObject::ALWAYS_COLLECT )
- {
+ if (flags & SP_OBJECT_WRITE_EXT && this->collectionPolicy() == SPObject::ALWAYS_COLLECT) {
repr->setAttribute("inkscape:collect", "always");
} else {
repr->removeAttribute("inkscape:collect");
@@ -1147,14 +1233,14 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML
// Write style attributes (SPStyleSrc::ATTRIBUTE) back to xml object
bool any_written = false;
auto properties = style->properties();
- for (auto * prop : properties) {
- if(prop->shall_write(SP_STYLE_FLAG_IFSET | SP_STYLE_FLAG_IFSRC, SPStyleSrc::ATTRIBUTE)) {
+ for (auto *prop : properties) {
+ if (prop->shall_write(SP_STYLE_FLAG_IFSET | SP_STYLE_FLAG_IFSRC, SPStyleSrc::ATTRIBUTE)) {
// WARNING: We don't know for sure if the css names are the same as the attribute names
repr->setAttributeOrRemoveIfEmpty(prop->name(), prop->get_value());
any_written = true;
}
}
- if(any_written) {
+ if (any_written) {
// We need to ask the object to update the style and keep things in sync
// see `case SPAttr::STYLE` above for how the style attr itself does this.
style->readFromObject(this);
@@ -1164,8 +1250,7 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML
// Check for valid attributes. This may be time consuming.
// It is useful, though, for debugging Inkscape code.
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
- if( prefs->getBool("/options/svgoutput/check_on_editing") ) {
-
+ if (prefs->getBool("/options/svgoutput/check_on_editing")) {
unsigned int flags = sp_attribute_clean_get_prefs();
style_prop = sp_attribute_clean_style(repr, style_prop.c_str(), flags);
}
@@ -1188,44 +1273,44 @@ Inkscape::XML::Node* SPObject::write(Inkscape::XML::Document *doc, Inkscape::XML
}
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::write", false );
+ objectTrace("SPObject::write", false);
#endif
return repr;
}
-Inkscape::XML::Node * SPObject::updateRepr(unsigned int flags)
+Inkscape::XML::Node *SPObject::updateRepr(unsigned int flags)
{
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1" );
+ objectTrace("SPObject::updateRepr 1");
#endif
- if ( !cloned ) {
+ if (!cloned) {
Inkscape::XML::Node *repr = getRepr();
if (repr) {
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1", false );
+ objectTrace("SPObject::updateRepr 1", false);
#endif
return updateRepr(repr->document(), repr, flags);
} else {
g_critical("Attempt to update non-existent repr");
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1", false );
+ objectTrace("SPObject::updateRepr 1", false);
#endif
return nullptr;
}
} else {
/* cloned objects have no repr */
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 1", false );
+ objectTrace("SPObject::updateRepr 1", false);
#endif
return nullptr;
}
}
-Inkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
+Inkscape::XML::Node *SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags)
{
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 2" );
+ objectTrace("SPObject::updateRepr 2");
#endif
g_assert(doc != nullptr);
@@ -1233,7 +1318,7 @@ Inkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscap
if (cloned) {
/* cloned objects have no repr */
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateRepr 2", false );
+ objectTrace("SPObject::updateRepr 2", false);
#endif
return nullptr;
}
@@ -1244,19 +1329,18 @@ Inkscape::XML::Node * SPObject::updateRepr(Inkscape::XML::Document *doc, Inkscap
#ifdef OBJECT_TRACE
Inkscape::XML::Node *node = write(doc, repr, flags);
- objectTrace( "SPObject::updateRepr 2", false );
+ objectTrace("SPObject::updateRepr 2", false);
return node;
#else
return this->write(doc, repr, flags);
#endif
-
}
/* Modification */
void SPObject::requestDisplayUpdate(unsigned int flags)
{
- g_return_if_fail( this->document != nullptr );
+ g_return_if_fail(this->document != nullptr);
#ifndef NDEBUG
// expect no nested update calls
@@ -1273,19 +1357,19 @@ void SPObject::requestDisplayUpdate(unsigned int flags)
g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestDisplayUpdate" );
+ objectTrace("SPObject::requestDisplayUpdate");
#endif
bool already_propagated = (!(this->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
- //https://stackoverflow.com/a/7841333
- if ((this->uflags & flags) != flags ) {
+ // https://stackoverflow.com/a/7841333
+ if ((this->uflags & flags) != flags) {
this->uflags |= flags;
}
/* If requestModified has already been called on this object or one of its children, then we
* don't need to set CHILD_MODIFIED on our ancestors because it's already been done.
*/
if (already_propagated) {
- if(this->document) {
+ if (this->document) {
if (parent) {
parent->requestDisplayUpdate(SP_OBJECT_CHILD_MODIFIED_FLAG);
} else {
@@ -1295,9 +1379,8 @@ void SPObject::requestDisplayUpdate(unsigned int flags)
}
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestDisplayUpdate", false );
+ objectTrace("SPObject::requestDisplayUpdate", false);
#endif
-
}
void SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
@@ -1305,13 +1388,14 @@ void SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateDisplay" );
+ objectTrace("SPObject::updateDisplay");
#endif
assert(++(document->update_in_progress));
#ifdef SP_OBJECT_DEBUG_CASCADE
- g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
+ g_print("Update %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *)this), getId(), flags, this->uflags,
+ this->mflags);
#endif
/* Get this flags */
@@ -1331,34 +1415,32 @@ void SPObject::updateDisplay(SPCtx *ctx, unsigned int flags)
if ((flags & SP_OBJECT_STYLESHEET_MODIFIED_FLAG)) {
style->readFromObject(this);
} else if (parent && (flags & SP_OBJECT_STYLE_MODIFIED_FLAG) && (flags & SP_OBJECT_PARENT_MODIFIED_FLAG)) {
- style->cascade( this->parent->style );
+ style->cascade(this->parent->style);
}
}
- try
- {
+ try {
this->update(ctx, flags);
- }
- catch(...)
- {
+ } catch (...) {
/** \todo
- * in case of catching an exception we need to inform the user somehow that the document is corrupted
- * maybe by implementing an document flag documentOk
- * or by a modal error dialog
- */
- g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
+ * in case of catching an exception we need to inform the user somehow that the document is corrupted
+ * maybe by implementing an document flag documentOk
+ * or by a modal error dialog
+ */
+ g_warning("SPObject::updateDisplay(SPCtx *ctx, unsigned int flags) : throw in ((SPObjectClass *) "
+ "G_OBJECT_GET_CLASS(this))->update(this, ctx, flags);");
}
assert((document->update_in_progress)--);
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::updateDisplay", false );
+ objectTrace("SPObject::updateDisplay", false);
#endif
}
void SPObject::requestModified(unsigned int flags)
{
- g_return_if_fail( this->document != nullptr );
+ g_return_if_fail(this->document != nullptr);
/* requestModified must be used only to set one of SP_OBJECT_MODIFIED_FLAG or
* SP_OBJECT_CHILD_MODIFIED_FLAG */
@@ -1367,7 +1449,7 @@ void SPObject::requestModified(unsigned int flags)
g_return_if_fail(!((flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_CHILD_MODIFIED_FLAG)));
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestModified" );
+ objectTrace("SPObject::requestModified");
#endif
bool already_propagated = (!(this->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG)));
@@ -1385,7 +1467,7 @@ void SPObject::requestModified(unsigned int flags)
}
}
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::requestModified", false );
+ objectTrace("SPObject::requestModified", false);
#endif
}
@@ -1395,11 +1477,12 @@ void SPObject::emitModified(unsigned int flags)
g_return_if_fail(!(flags & ~SP_OBJECT_MODIFIED_CASCADE));
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::emitModified", true, flags );
+ objectTrace("SPObject::emitModified", true, flags);
#endif
#ifdef SP_OBJECT_DEBUG_CASCADE
- g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *) this), getId(), flags, this->uflags, this->mflags);
+ g_print("Modified %s:%s %x %x %x\n", g_type_name_from_instance((GTypeInstance *)this), getId(), flags, this->uflags,
+ this->mflags);
#endif
flags |= this->mflags;
@@ -1416,7 +1499,7 @@ void SPObject::emitModified(unsigned int flags)
sp_object_unref(this);
#ifdef OBJECT_TRACE
- objectTrace( "SPObject::emitModified", false );
+ objectTrace("SPObject::emitModified", false);
#endif
}
@@ -1425,7 +1508,7 @@ gchar const *SPObject::getTagName() const
g_assert(repr != nullptr);
/// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
+ // XML Tree being used here.
return getRepr()->name();
}
@@ -1434,17 +1517,16 @@ gchar const *SPObject::getAttribute(gchar const *key) const
g_assert(this->repr != nullptr);
/// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
- return (gchar const *) getRepr()->attribute(key);
+ // XML Tree being used here.
+ return (gchar const *)getRepr()->attribute(key);
}
-void SPObject::setAttribute(Inkscape::Util::const_char_ptr key,
- Inkscape::Util::const_char_ptr value)
+void SPObject::setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
{
g_assert(this->repr != nullptr);
/// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
+ // XML Tree being used here.
getRepr()->setAttribute(key, value);
}
@@ -1457,13 +1539,13 @@ void SPObject::setAttributeDouble(Inkscape::Util::const_char_ptr key, double val
void SPObject::removeAttribute(gchar const *key)
{
/// \todo fixme: Exception if object is NULL? */
- //XML Tree being used here.
+ // XML Tree being used here.
getRepr()->removeAttribute(key);
}
-bool SPObject::storeAsDouble( gchar const *key, double *val ) const
+bool SPObject::storeAsDouble(gchar const *key, double *val) const
{
- g_assert(this->getRepr()!= nullptr);
+ g_assert(this->getRepr() != nullptr);
double nan = std::numeric_limits::quiet_NaN();
double temp_val = ((Inkscape::XML::Node *)(this->getRepr()))->getAttributeDouble(key, nan);
if (std::isnan(temp_val)) {
@@ -1474,9 +1556,7 @@ bool SPObject::storeAsDouble( gchar const *key, double *val ) const
}
/** Helper */
-gchar *
-sp_object_get_unique_id(SPObject *object,
- gchar const *id)
+gchar *sp_object_get_unique_id(SPObject *object, gchar const *id)
{
static unsigned long count = 0;
@@ -1484,7 +1564,7 @@ sp_object_get_unique_id(SPObject *object,
count++;
- //XML Tree being used here.
+ // XML Tree being used here.
gchar const *name = object->getRepr()->name();
g_assert(name != nullptr);
@@ -1501,23 +1581,24 @@ sp_object_get_unique_id(SPObject *object,
size_t const name_len = std::strlen(name);
size_t const buflen = name_len + (sizeof(count) * 10 / 4) + 1;
- gchar *const buf = (gchar *) g_malloc(buflen);
+ gchar *const buf = (gchar *)g_malloc(buflen);
std::memcpy(buf, name, name_len);
gchar *const count_buf = buf + name_len;
size_t const count_buflen = buflen - name_len;
do {
++count;
g_snprintf(count_buf, count_buflen, "%lu", count);
- } while ( object->document->getObjectById(buf) != nullptr );
+ } while (object->document->getObjectById(buf) != nullptr);
return buf;
}
-void SPObject::_requireSVGVersion(Inkscape::Version version) {
- for ( SPObject::ParentIterator iter=this ; iter ; ++iter ) {
+void SPObject::_requireSVGVersion(Inkscape::Version version)
+{
+ for (SPObject::ParentIterator iter = this; iter; ++iter) {
SPObject *object = iter;
if (SP_IS_ROOT(object)) {
SPRoot *root = SP_ROOT(object);
- if ( root->version.svg < version ) {
+ if (root->version.svg < version) {
root->version.svg = version;
}
}
@@ -1539,7 +1620,7 @@ void SPObject::_requireSVGVersion(Inkscape::Version version) {
be allowed with different localized strings.
*/
-gchar * SPObject::title() const
+gchar *SPObject::title() const
{
return getTitleOrDesc("svg:title");
}
@@ -1549,7 +1630,7 @@ bool SPObject::setTitle(gchar const *title, bool verbatim)
return setTitleOrDesc(title, "svg:title", verbatim);
}
-gchar * SPObject::desc() const
+gchar *SPObject::desc() const
{
return getTitleOrDesc("svg:desc");
}
@@ -1559,14 +1640,14 @@ bool SPObject::setDesc(gchar const *desc, bool verbatim)
return setTitleOrDesc(desc, "svg:desc", verbatim);
}
-char * SPObject::getTitleOrDesc(gchar const *svg_tagname) const
+char *SPObject::getTitleOrDesc(gchar const *svg_tagname) const
{
char *result = nullptr;
SPObject *elem = findFirstChild(svg_tagname);
- if ( elem ) {
- //This string copy could be avoided by changing
- //the return type of SPObject::getTitleOrDesc
- //to std::unique_ptr
+ if (elem) {
+ // This string copy could be avoided by changing
+ // the return type of SPObject::getTitleOrDesc
+ // to std::unique_ptr
result = g_strdup(elem->textualContent().c_str());
}
return result;
@@ -1625,12 +1706,11 @@ bool SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool
repr->addChild(xml_elem, nullptr);
elem = document->getObjectByRepr(xml_elem);
Inkscape::GC::release(xml_elem);
- }
- else {
+ } else {
// remove the current content of the 'text' or 'desc' element
- auto tmp = elem->children | boost::adaptors::transformed([](SPObject& obj) { return &obj; });
- std::vector vec(tmp.begin(), tmp.end());
- for (auto &child: vec) {
+ auto tmp = elem->children | boost::adaptors::transformed([](SPObject &obj) { return &obj; });
+ std::vector vec(tmp.begin(), tmp.end());
+ for (auto &child : vec) {
child->deleteObject();
}
}
@@ -1640,12 +1720,10 @@ bool SPObject::setTitleOrDesc(gchar const *value, gchar const *svg_tagname, bool
return true;
}
-SPObject* SPObject::findFirstChild(gchar const *tagname) const
+SPObject *SPObject::findFirstChild(gchar const *tagname) const
{
- for (auto& child: const_cast(this)->children)
- {
- if (child.repr->type() == Inkscape::XML::NodeType::ELEMENT_NODE &&
- !std::strcmp(child.repr->name(), tagname)) {
+ for (auto &child : const_cast(this)->children) {
+ if (child.repr->type() == Inkscape::XML::NodeType::ELEMENT_NODE && !std::strcmp(child.repr->name(), tagname)) {
return &child;
}
}
@@ -1656,14 +1734,12 @@ Glib::ustring SPObject::textualContent() const
{
Glib::ustring text;
- for (auto& child: children)
- {
+ for (auto &child : children) {
Inkscape::XML::NodeType child_type = child.repr->type();
if (child_type == Inkscape::XML::NodeType::ELEMENT_NODE) {
text += child.textualContent();
- }
- else if (child_type == Inkscape::XML::NodeType::TEXT_NODE) {
+ } else if (child_type == Inkscape::XML::NodeType::TEXT_NODE) {
text += child.repr->content();
}
}
@@ -1671,7 +1747,7 @@ Glib::ustring SPObject::textualContent() const
}
// For debugging: Print SP tree structure.
-void SPObject::recursivePrintTree( unsigned level )
+void SPObject::recursivePrintTree(unsigned level)
{
if (level == 0) {
std::cout << "SP Object Tree" << std::endl;
@@ -1680,24 +1756,24 @@ void SPObject::recursivePrintTree( unsigned level )
for (unsigned i = 0; i < level; ++i) {
std::cout << " ";
}
- std::cout << (getId()?getId():"No object id")
- << " clone: " << std::boolalpha << (bool)cloned
+ std::cout << (getId() ? getId() : "No object id") << " clone: " << std::boolalpha << (bool)cloned
<< " hrefcount: " << hrefcount << std::endl;
- for (auto& child: children) {
+ for (auto &child : children) {
child.recursivePrintTree(level + 1);
}
}
// Function to allow tracing of program flow through SPObject and derived classes.
// To trace function, add at entrance ('in' = true) and exit of function ('in' = false).
-void SPObject::objectTrace( std::string const &text, bool in, unsigned flags ) {
- if( in ) {
+void SPObject::objectTrace(std::string const &text, bool in, unsigned flags)
+{
+ if (in) {
for (unsigned i = 0; i < indent_level; ++i) {
std::cout << " ";
}
std::cout << text << ":"
<< " entrance: "
- << (id?id:"null")
+ << (id ? id : "null")
// << " uflags: " << uflags
// << " mflags: " << mflags
// << " flags: " << flags
@@ -1710,7 +1786,7 @@ void SPObject::objectTrace( std::string const &text, bool in, unsigned flags ) {
}
std::cout << text << ":"
<< " exit: "
- << (id?id:"null")
+ << (id ? id : "null")
// << " uflags: " << uflags
// << " mflags: " << mflags
// << " flags: " << flags
@@ -1720,11 +1796,8 @@ void SPObject::objectTrace( std::string const &text, bool in, unsigned flags ) {
std::ostream &operator<<(std::ostream &out, const SPObject &o)
{
- out << (o.getId()?o.getId():"No ID")
- << " cloned: " << std::boolalpha << (bool)o.cloned
- << " ref: " << o.refCount
- << " href: " << o.hrefcount
- << " total href: " << o._total_hrefcount;
+ out << (o.getId() ? o.getId() : "No ID") << " cloned: " << std::boolalpha << (bool)o.cloned
+ << " ref: " << o.refCount << " href: " << o.hrefcount << " total href: " << o._total_hrefcount;
return out;
}
/*
diff --git a/src/object/sp-object.h b/src/object/sp-object.h
index 42d9cff2ecdab3ed436ce76934cc2cc9f24bdacb..f6d794ea123c0b38a4c2afe2d92d280bc64ae7c8 100644
--- a/src/object/sp-object.h
+++ b/src/object/sp-object.h
@@ -466,7 +466,8 @@ public:
/**
* Removes all children except for the given object, it's children and it's ancesstors.
*/
- void cropToObject(SPObject *except);
+ void cropToObject(SPObject *except);
+ void cropToObjects(std::vector except_objects);
/**
* Connects a slot to be called when an object is deleted.
@@ -480,11 +481,10 @@ public:
*
* @see SPObject::deleteObject
*/
- sigc::connection connectDelete(sigc::slot slot) {
- return _delete_signal.connect(slot);
- }
+ sigc::connection connectDelete(sigc::slot slot) { return _delete_signal.connect(slot); }
- sigc::connection connectPositionChanged(sigc::slot slot) {
+ sigc::connection connectPositionChanged(sigc::slot slot)
+ {
return _position_changed_signal.connect(slot);
}
diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt
index 26ea2675e19cb69528b9b88e41070ac6fb98695e..73df92f79ff15dc021d1c81556de957d274c2dbd 100644
--- a/src/ui/CMakeLists.txt
+++ b/src/ui/CMakeLists.txt
@@ -113,6 +113,10 @@ set(ui_SRC
dialog/dialog-window.cpp
dialog/document-properties.cpp
dialog/export.cpp
+ dialog/export-batch.cpp
+ dialog/export-single.cpp
+ dialog/export-helper.cpp
+ dialog/export-preview.cpp
dialog/filedialog.cpp
dialog/filedialogimpl-gtkmm.cpp
dialog/fill-and-stroke.cpp
@@ -138,6 +142,7 @@ set(ui_SRC
dialog/object-properties.cpp
dialog/objects.cpp
dialog/polar-arrange-tab.cpp
+ dialog/preview-util.cpp
dialog/print.cpp
dialog/prototype.cpp
dialog/selectorsdialog.cpp
@@ -286,6 +291,10 @@ set(ui_SRC
dialog/dialog-window.h
dialog/document-properties.h
dialog/export.h
+ dialog/export-batch.h
+ dialog/export-single.h
+ dialog/export-helper.h
+ dialog/export-preview.h
dialog/filedialog.h
dialog/filedialogimpl-gtkmm.h
dialog/filedialogimpl-win32.h
@@ -312,6 +321,7 @@ set(ui_SRC
dialog/object-properties.h
dialog/objects.h
dialog/polar-arrange-tab.h
+ dialog/preview-util.h
dialog/print.h
dialog/prototype.h
dialog/selectorsdialog.h
diff --git a/src/ui/dialog/export-batch.cpp b/src/ui/dialog/export-batch.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ba3aa3e5cc6cbb6798cb38ebed12b71c6c64f9e7
--- /dev/null
+++ b/src/ui/dialog/export-batch.cpp
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Lauris Kaplinski
+ * bulia byak
+ * Johan Engelen
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 1999-2007, 2021 Authors
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "export-batch.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "export-helper.h"
+#include "export-preview.h"
+#include "extension/db.h"
+#include "file.h"
+#include "helper/png-write.h"
+#include "inkscape-window.h"
+#include "inkscape.h"
+#include "io/resource.h"
+#include "io/sys.h"
+#include "message-stack.h"
+#include "object/object-set.h"
+#include "object/sp-namedview.h"
+#include "object/sp-root.h"
+#include "preferences.h"
+#include "selection-chemistry.h"
+#include "ui/dialog-events.h"
+#include "ui/dialog/dialog-notebook.h"
+#include "ui/dialog/filedialog.h"
+#include "ui/interface.h"
+#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
+
+#ifdef _WIN32
+
+#endif
+
+using Inkscape::Util::unit_table;
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+// START OF BATCH ITEM
+
+class BatchItem : public Gtk::FlowBoxChild
+{
+public:
+ BatchItem(SPItem *item);
+ ~BatchItem() override;
+
+public:
+ Gtk::CheckButton selector;
+ SPItem *getItem() { return _item; }
+ bool isActive() { return selector.get_active(); }
+ void refresh(bool hide = false);
+ void refreshHide(const std::vector *list) { preview->refreshHide(list); }
+
+private:
+ Gtk::Grid grid;
+ Gtk::Label label;
+ ExportPreview *preview = nullptr;
+ SPItem *_item;
+};
+
+BatchItem::~BatchItem()
+{
+ if (preview) {
+ delete preview;
+ preview = nullptr;
+ }
+}
+
+BatchItem::BatchItem(SPItem *item)
+ : grid()
+ , selector()
+{
+ if (!item) {
+ return;
+ }
+ _item = item;
+ grid.attach(selector, 0, 1, 1, 1);
+ grid.set_row_spacing(5);
+ grid.set_column_spacing(5);
+
+ Glib::ustring id = _item->defaultLabel();
+ if (id.empty()) {
+ id = _item->getId();
+ }
+ Glib::ustring compactId = id.substr(0, 7);
+ if (id.length() > 7) {
+ compactId = compactId + "...";
+ }
+
+ selector.set_active(true);
+ selector.set_can_focus(false);
+ selector.set_valign(Gtk::Align::ALIGN_END);
+ selector.set_margin_start(2);
+ selector.set_margin_bottom(2);
+
+ if (!preview) {
+ preview = Gtk::manage(new ExportPreview());
+ grid.attach(*preview, 0, 0, 2, 2);
+ }
+ preview->setItem(_item);
+ preview->setDocument(_item->document);
+ preview->setSize(64);
+
+ label.set_text(compactId);
+ label.set_halign(Gtk::Align::ALIGN_CENTER);
+ grid.attach(label, 0, 2, 2, 1);
+
+ add(grid);
+ show_all_children();
+ show();
+ this->set_can_focus(false);
+ this->set_tooltip_text(id);
+}
+
+void BatchItem::refresh(bool hide)
+{
+ if (!_item) {
+ return;
+ }
+ if (hide) {
+ preview->resetPixels();
+ } else {
+ preview->queueRefresh();
+ }
+}
+
+// END OF BATCH ITEM
+
+// START OF BATCH EXPORT
+
+BatchExport::~BatchExport()
+{
+ ;
+}
+
+void BatchExport::initialise(const Glib::RefPtr &builder)
+{
+ builder->get_widget("b_s_selection", selection_buttons[SELECTION_SELECTION]);
+ selection_names[SELECTION_SELECTION] = "selection";
+ builder->get_widget("b_s_layers", selection_buttons[SELECTION_LAYER]);
+ selection_names[SELECTION_LAYER] = "layer";
+
+ builder->get_widget("b_preview_box", preview_container);
+ builder->get_widget("b_show_preview", show_preview);
+ builder->get_widget("b_num_elements", num_elements);
+ builder->get_widget("b_advance_box", adv_box);
+ builder->get_widget("b_hide_all", hide_all);
+ builder->get_widget("b_filename", filename_entry);
+ builder->get_widget("b_export", export_btn);
+ builder->get_widget("b_progress_bar", _prog);
+ builder->get_widget_derived("b_export_list", export_list);
+
+ Inkscape::UI::Widget::ScrollTransfer *temp = nullptr;
+ builder->get_widget_derived("b_pbox_scroll", temp);
+ builder->get_widget_derived("b_scroll", temp);
+}
+
+void BatchExport::selectionModified(Inkscape::Selection *selection, guint flags)
+{
+ if (!_desktop || _desktop->getSelection() != selection) {
+ return;
+ }
+ if (!(flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
+ return;
+ }
+ refreshItems();
+}
+
+void BatchExport::selectionChanged(Inkscape::Selection *selection)
+{
+ if (!_desktop || _desktop->getSelection() != selection) {
+ return;
+ }
+ if (selection->isEmpty()) {
+ selection_buttons[SELECTION_SELECTION]->set_sensitive(false);
+ if (current_key == SELECTION_SELECTION) {
+ selection_buttons[(selection_mode)0]->set_active(true); // This causes refresh area
+ // return otherwise refreshArea will be called again
+ // even though we are at default key, selection is the one which was original key.
+ prefs->setString("/dialogs/export/batchexportarea/value", selection_names[SELECTION_SELECTION]);
+ return;
+ }
+ } else {
+ selection_buttons[SELECTION_SELECTION]->set_sensitive(true);
+ Glib::ustring pref_key_name = prefs->getString("/dialogs/export/batchexportarea/value");
+ if (selection_names[SELECTION_SELECTION] == pref_key_name && current_key != SELECTION_SELECTION) {
+ selection_buttons[SELECTION_SELECTION]->set_active();
+ return;
+ }
+ }
+ refreshItems();
+ refreshExportHints();
+}
+
+// Setup Single Export.Called by export on realize
+void BatchExport::setup()
+{
+ if (setupDone) {
+ return;
+ }
+ setupDone = true;
+ prefs = Inkscape::Preferences::get();
+
+ // Setup Advance Options
+ adv_box->pack_start(advance_options, true, true, 0);
+ adv_box->show_all_children();
+
+ export_list->setup();
+
+ // set them before connecting to signals
+ setDefaultFilename();
+ setDefaultSelectionMode();
+
+ refreshExportHints();
+
+ refreshItems();
+
+ // Connect Signals
+ for (auto [key, button] : selection_buttons) {
+ button->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &BatchExport::onAreaTypeToggle), key));
+ }
+ show_preview->signal_toggled().connect(sigc::mem_fun(*this, &BatchExport::refreshPreview));
+ filenameConn = filename_entry->signal_changed().connect(sigc::mem_fun(*this, &BatchExport::onFilenameModified));
+ exportConn = export_btn->signal_clicked().connect(sigc::mem_fun(*this, &BatchExport::onExport));
+ browseConn = filename_entry->signal_icon_press().connect(sigc::mem_fun(*this, &BatchExport::onBrowse));
+ hide_all->signal_toggled().connect(sigc::mem_fun(*this, &BatchExport::refreshPreview));
+}
+
+void BatchExport::refreshItems()
+{
+ if (!_desktop) {
+ return;
+ }
+ SPDocument *doc = _desktop->getDocument();
+ if (!doc) {
+ return;
+ }
+ doc->ensureUpToDate();
+
+ // Create New List of Items
+ std::set itemsList;
+ switch (current_key) {
+ case SELECTION_SELECTION: {
+ auto items = _desktop->getSelection()->items();
+ for (auto i = items.begin(); i != items.end(); ++i) {
+ SPItem *item = *i;
+ if (item) {
+ itemsList.insert(item);
+ }
+ }
+ break;
+ }
+ case SELECTION_LAYER: {
+ auto layersList = doc->getResourceList("layer");
+ for (auto item : layersList) {
+ if (SP_IS_GROUP(item) && SP_GROUP(item)->layerMode() == SPGroup::LAYER) {
+ itemsList.insert(dynamic_cast(item));
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Number of Items
+ int num = itemsList.size();
+ Glib::ustring label_text = std::to_string(num) + " Items";
+ num_elements->set_text(label_text);
+
+ // Create a list of items which are already present but will be removed as they are not present anymore
+ std::vector toRemove;
+ for (auto &[key, val] : current_items) {
+ SPItem *item = val->getItem();
+ // if item is not present in itemList add it to remove list so that we can remove it
+ auto itemItr = itemsList.find(item);
+ if (itemItr == itemsList.end() || (*itemItr)->getId() != key) {
+ toRemove.push_back(key);
+ }
+ }
+
+ // now remove all the items
+ for (auto key : toRemove) {
+ if (current_items[key]) {
+ // Preview Boxes are GTK managed so simply removing from container will handle delete
+ preview_container->remove(*current_items[key]);
+ current_items.erase(key);
+ }
+ }
+
+ // now add which were are new
+ for (auto &item : itemsList) {
+ auto id = item->getId();
+ // If an Item with same Id is already present, Skip
+ if (current_items[id] && current_items[id]->getItem() == item) {
+ continue;
+ }
+ // Add new item to the end of list
+ current_items[id] = Gtk::manage(new BatchItem(item));
+ preview_container->insert(*current_items[id], -1);
+ }
+
+ refreshPreview();
+}
+
+void BatchExport::refreshPreview()
+{
+ if (_desktop) {
+ // For Batch Export we are now hiding all object except current object
+ // std::vector selected(_desktop->getSelection()->items().begin(),
+ // _desktop->getSelection()->items().end());
+ bool hide = hide_all->get_active();
+ for (auto &[key, val] : current_items) {
+ if (show_preview->get_active()) {
+ std::vector selected = {val->getItem()};
+ val->refreshHide(hide ? &selected : nullptr);
+ val->refresh();
+ } else {
+ val->refresh(true);
+ }
+ }
+ }
+}
+
+void BatchExport::refreshExportHints()
+{
+ ;
+}
+
+// Signals CallBack
+
+void BatchExport::onAreaTypeToggle(selection_mode key)
+{
+ // Prevent executing function twice
+ if (!selection_buttons[key]->get_active()) {
+ return;
+ }
+ // If you have reached here means the current key is active one ( not sure if multiple transitions happen but
+ // last call will change values)
+ current_key = key;
+ prefs->setString("/dialogs/export/batchexportarea/value", selection_names[current_key]);
+
+ refreshItems();
+ refreshExportHints();
+}
+
+void BatchExport::onFilenameModified()
+{
+ ;
+}
+
+void BatchExport::onExport()
+{
+ interrupted = false;
+ if (!_desktop)
+ return;
+ export_btn->set_sensitive(false);
+ bool exportSuccessful = true;
+
+ // If there are no selected button, simply flash message in status bar
+ int num = current_items.size();
+ if (current_items.size() == 0) {
+ _desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No items selected."));
+ export_btn->set_sensitive(true);
+ return;
+ }
+
+ // Find and remove any extension from filename so that we can add suffix to it.
+ Glib::ustring filename = filename_entry->get_text();
+ Glib::ustring filename_extension = get_ext_from_filename(filename);
+ if (ExtensionList::all_extensions[filename_extension]) {
+ auto extension_point = filename.rfind(filename_extension);
+ filename.erase(extension_point);
+ }
+
+ int n = 0;
+
+ // create vector of exports
+ int num_rows = export_list->get_rows();
+ std::vector suffixs;
+ std::vector extensions;
+ std::vector dpis;
+ for (int i = 0; i < num_rows; i++) {
+ suffixs.push_back(export_list->get_suffix(i));
+ extensions.push_back(export_list->get_extension(i));
+ dpis.push_back(export_list->get_dpi(i));
+ }
+
+ // We are exporting standalone items only for now
+ // std::vector selected(_desktop->getSelection()->items().begin(),
+ // _desktop->getSelection()->items().end());
+ bool hide = hide_all->get_active();
+
+ // Start Exporting Each Item
+ for (auto i = current_items.begin(); i != current_items.end() && !interrupted; ++i) {
+ BatchItem *batchItem = i->second;
+ if (!batchItem->isActive()) {
+ n++;
+ continue;
+ }
+ SPItem *item = batchItem->getItem();
+ if (!item) {
+ n++;
+ continue;
+ }
+
+ Geom::OptRect area = item->documentVisualBounds();
+ if (!area) {
+ n++;
+ continue;
+ }
+ Glib::ustring id = item->defaultLabel();
+ if (id.empty()) {
+ id = item->getId();
+ }
+ if (id.empty()) {
+ n++;
+ continue;
+ }
+ for (int i = 0; i < num_rows; i++) {
+ auto omod = ExtensionList::valid_extensions[extensions[i]];
+ float dpi = dpis[i];
+
+ Glib::ustring item_filename = filename + "_" + id;
+ if (!suffixs[i].empty()) {
+ item_filename = item_filename + "_" + suffixs[i];
+ }
+ item_filename = item_filename + "_" + std::to_string((int)dpi);
+
+ if (!omod) {
+ continue;
+ }
+ if (prog_dlg) {
+ delete prog_dlg;
+ prog_dlg = nullptr;
+ }
+ prog_dlg = create_progress_dialog(Glib::ustring::compose(_("Exporting %1 files"), num));
+ prog_dlg->set_export_panel(this);
+ setExporting(true, Glib::ustring::compose(_("Exporting %1 files"), num));
+ prog_dlg->set_current(n);
+ prog_dlg->set_total(num);
+
+ onProgressCallback(0.0, prog_dlg);
+ bool found = getNonConflictingFilename(item_filename, extensions[i]);
+ if (!found) {
+ n++;
+ continue;
+ }
+
+ if (omod->is_raster()) {
+ unsigned long int width = (int)(area->width() * dpi / DPI_BASE + 0.5);
+ unsigned long int height = (int)(area->height() * dpi / DPI_BASE + 0.5);
+
+ std::vector show_only = {item};
+ exportSuccessful = _export_raster(*area, width, height, dpi, item_filename, true, onProgressCallback,
+ prog_dlg, omod, hide ? &show_only : nullptr, &advance_options);
+ } else {
+ setExporting(true, Glib::ustring::compose(_("Exporting %1"), filename));
+ SPDocument *doc = _desktop->getDocument();
+ SPDocument *copy_doc = (doc->copy()).get();
+ std::vector items;
+ items.push_back(item);
+ exportSuccessful = _export_vector(omod, copy_doc, item_filename, true, &items);
+ }
+ setExporting(false);
+ }
+ if (prog_dlg) {
+ delete prog_dlg;
+ prog_dlg = nullptr;
+ }
+ }
+}
+
+bool BatchExport::getNonConflictingFilename(Glib::ustring &filename, Glib::ustring const extension)
+{
+ if (!_desktop) {
+ return false;
+ }
+ SPDocument *doc = _desktop->getDocument();
+ std::string path = absolutize_path_from_document_location(doc, Glib::filename_from_utf8(filename));
+ Glib::ustring test_filename = path + extension;
+ if (!Inkscape::IO::file_test(test_filename.c_str(), G_FILE_TEST_EXISTS)) {
+ filename = test_filename;
+ return true;
+ }
+ for (int i = 1; i <= 100; i++) {
+ test_filename = path + "_copy_" + std::to_string(i) + extension;
+ if (!Inkscape::IO::file_test(test_filename.c_str(), G_FILE_TEST_EXISTS)) {
+ filename = test_filename;
+ return true;
+ }
+ }
+ return false;
+}
+
+void BatchExport::onBrowse(Gtk::EntryIconPosition pos, const GdkEventButton *ev)
+{
+ if (!_app) {
+ return;
+ }
+ Gtk::Window *window = _app->get_active_window();
+ browseConn.block();
+ Glib::ustring filename = Glib::filename_from_utf8(filename_entry->get_text());
+
+ if (filename.empty()) {
+ Glib::ustring tmp;
+ filename = create_filepath_from_id(tmp, tmp);
+ }
+
+ Inkscape::UI::Dialog::FileSaveDialog *dialog = Inkscape::UI::Dialog::FileSaveDialog::create(
+ *window, filename, Inkscape::UI::Dialog::RASTER_TYPES, _("Select a filename for exporting"), "", "",
+ Inkscape::Extension::FILE_SAVE_METHOD_EXPORT);
+
+ if (dialog->show()) {
+ filename = dialog->getFilename();
+ Inkscape::Extension::Output *selection_type =
+ dynamic_cast(dialog->getSelectionType());
+ Glib::ustring extension = selection_type->get_extension();
+ ExtensionList::appendExtensionToFilename(filename, extension);
+ filename_entry->set_text(filename);
+ filename_entry->set_position(filename.length());
+ // deleting dialog before exporting is important
+ // proper delete function should be made for dialog IMO
+ delete dialog;
+ onExport();
+ } else {
+ delete dialog;
+ }
+ browseConn.unblock();
+}
+
+// Utils Functions
+
+// We first check any export hints related to document. If there is none we create a default name using document
+// name. doc_export_name is set here and will only be changed when exporting.
+void BatchExport::setDefaultFilename()
+{
+ if (!_desktop) {
+ return;
+ }
+ Glib::ustring filename;
+ float xdpi = 0.0, ydpi = 0.0;
+ SPDocument *doc = _desktop->getDocument();
+ sp_document_get_export_hints(doc, filename, &xdpi, &ydpi);
+ if (filename.empty()) {
+ Glib::ustring filename_entry_text = filename_entry->get_text();
+ Glib::ustring extension = ".png";
+ filename = get_default_filename(filename_entry_text, extension, doc);
+ }
+ doc_export_name = filename;
+ original_name = filename;
+ filename_entry->set_text(filename);
+ filename_entry->set_position(filename.length());
+}
+
+void BatchExport::setDefaultSelectionMode()
+{
+ current_key = (selection_mode)0; // default key
+ bool found = false;
+ Glib::ustring pref_key_name = prefs->getString("/dialogs/export/batchexportarea/value");
+ for (auto [key, name] : selection_names) {
+ if (pref_key_name == name) {
+ current_key = key;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ pref_key_name = selection_names[current_key];
+ }
+ if (_desktop) {
+ if (current_key == SELECTION_SELECTION && (_desktop->getSelection())->isEmpty()) {
+ current_key = (selection_mode)0;
+ }
+ if ((_desktop->getSelection())->isEmpty()) {
+ selection_buttons[SELECTION_SELECTION]->set_sensitive(false);
+ }
+ }
+ selection_buttons[current_key]->set_active(true);
+
+ // we need to set pref key because signals above will set set pref == current key but we sometimes change
+ // current key like selection key
+ prefs->setString("/dialogs/export/batchexportarea/value", pref_key_name);
+}
+
+void BatchExport::setExporting(bool exporting, Glib::ustring const &text)
+{
+ if (exporting) {
+ _prog->set_text(text);
+ _prog->set_fraction(0.0);
+ _prog->set_sensitive(true);
+ export_btn->set_sensitive(false);
+ } else {
+ _prog->set_text("");
+ _prog->set_fraction(0.0);
+ _prog->set_sensitive(false);
+ export_btn->set_sensitive(true);
+ }
+}
+
+ExportProgressDialog *BatchExport::create_progress_dialog(Glib::ustring progress_text)
+{
+ // dont forget to delete it later
+ auto dlg = new ExportProgressDialog(_("Export in progress"), true);
+ dlg->set_transient_for(*(INKSCAPE.active_desktop()->getToplevel()));
+
+ Gtk::ProgressBar *prg = Gtk::manage(new Gtk::ProgressBar());
+ prg->set_text(progress_text);
+ dlg->set_progress(prg);
+ auto CA = dlg->get_content_area();
+ CA->pack_start(*prg, FALSE, FALSE, 4);
+
+ Gtk::Button *btn = dlg->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
+
+ btn->signal_clicked().connect(sigc::mem_fun(*this, &BatchExport::onProgressCancel));
+ dlg->signal_delete_event().connect(sigc::mem_fun(*this, &BatchExport::onProgressDelete));
+
+ dlg->show_all();
+ return dlg;
+}
+
+/// Called when dialog is deleted
+bool BatchExport::onProgressDelete(GdkEventAny * /*event*/)
+{
+ interrupted = true;
+ prog_dlg->set_stopped();
+ return TRUE;
+}
+
+/// Called when progress is cancelled
+void BatchExport::onProgressCancel()
+{
+ interrupted = true;
+ prog_dlg->set_stopped();
+}
+
+/// Called for every progress iteration
+unsigned int BatchExport::onProgressCallback(float value, void *dlg)
+{
+ auto dlg2 = reinterpret_cast(dlg);
+
+ auto self = dynamic_cast(dlg2->get_export_panel());
+
+ if (!self || self->interrupted)
+ return FALSE;
+
+ auto current = dlg2->get_current();
+ auto total = dlg2->get_total();
+ if (total > 0) {
+ double completed = current;
+ completed /= static_cast(total);
+
+ value = completed + (value / static_cast(total));
+ }
+
+ auto prg = dlg2->get_progress();
+ prg->set_fraction(value);
+
+ if (self) {
+ self->_prog->set_fraction(value);
+ }
+
+ int evtcount = 0;
+ while ((evtcount < 16) && gdk_events_pending()) {
+ Gtk::Main::iteration(false);
+ evtcount += 1;
+ }
+
+ Gtk::Main::iteration(false);
+ return TRUE;
+}
+
+void BatchExport::setDocument(SPDocument *document)
+{
+ ;
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export-batch.h b/src/ui/dialog/export-batch.h
new file mode 100644
index 0000000000000000000000000000000000000000..ab8b89d44c436641eb9d3839dcf6326d99b1a497
--- /dev/null
+++ b/src/ui/dialog/export-batch.h
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Lauris Kaplinski
+ * bulia byak
+ * Johan Engelen
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 1999-2007, 2021 Authors
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SP_EXPORT_BATCH_H
+#define SP_EXPORT_BATCH_H
+
+#include
+
+#include "export-helper.h"
+#include "extension/output.h"
+#include "ui/widget/scroll-utils.h"
+#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+class BatchItem;
+
+class BatchExport : public Gtk::Box
+{
+public:
+ BatchExport(){};
+ BatchExport(BaseObjectType *cobject, const Glib::RefPtr &refGlade)
+ : Gtk::Box(cobject){};
+ ~BatchExport() override;
+
+private:
+ InkscapeApplication *_app;
+ SPDesktop *_desktop = nullptr;
+
+private:
+ bool setupDone = false; // To prevent setup() call add connections again.
+
+public:
+ void setApp(InkscapeApplication *app) { _app = app; }
+ void setDocument(SPDocument *document);
+ void setDesktop(SPDesktop *desktop) { _desktop = desktop; }
+ void selectionChanged(Inkscape::Selection *selection);
+ void selectionModified(Inkscape::Selection *selection, guint flags);
+
+private:
+ enum selection_mode
+ {
+ SELECTION_LAYER = 0, // Default is alaways placed first
+ SELECTION_SELECTION,
+ };
+
+private:
+ typedef Inkscape::UI::Widget::ScrollProtected SpinButton;
+
+ std::map selection_buttons;
+ Gtk::FlowBox *preview_container = nullptr;
+ Gtk::CheckButton *show_preview = nullptr;
+ Gtk::Label *num_elements = nullptr;
+ Gtk::Box *adv_box = nullptr;
+ Gtk::CheckButton *hide_all = nullptr;
+ Gtk::Entry *filename_entry = nullptr;
+ Gtk::Button *export_btn = nullptr;
+ Gtk::ProgressBar *_prog = nullptr;
+ ExportList *export_list = nullptr;
+
+ AdvanceOptions advance_options;
+
+private:
+ // Store all items to be displayed in flowbox
+ std::map current_items;
+
+private:
+ bool filename_modified;
+ Glib::ustring original_name;
+ Glib::ustring doc_export_name;
+
+ Inkscape::Preferences *prefs = nullptr;
+ std::map selection_names;
+ selection_mode current_key;
+
+public:
+ // initialise variables from builder
+ void initialise(const Glib::RefPtr &builder);
+ void setup();
+ bool getNonConflictingFilename(Glib::ustring &filename, Glib::ustring const extension);
+
+private:
+ void setDefaultSelectionMode();
+ void setDefaultFilename();
+
+private:
+ void onFilenameModified();
+ void onAreaTypeToggle(selection_mode key);
+ void onExport();
+ void onBrowse(Gtk::EntryIconPosition pos, const GdkEventButton *ev);
+
+public:
+ void refresh()
+ {
+ refreshItems();
+ refreshExportHints();
+ };
+
+private:
+ void refreshPreview();
+ void refreshItems();
+ void refreshExportHints();
+
+private:
+ void setExporting(bool exporting, Glib::ustring const &text = "");
+ ExportProgressDialog *create_progress_dialog(Glib::ustring progress_text);
+ /**
+ * Callback to be used in for loop to update the progress bar.
+ *
+ * @param value number between 0 and 1 indicating the fraction of progress (0.17 = 17 % progress)
+ * @param dlg void pointer to the Gtk::Dialog progress dialog
+ */
+ static unsigned int onProgressCallback(float value, void *dlg);
+
+ /**
+ * Callback for pressing the cancel button.
+ */
+ void onProgressCancel();
+
+ /**
+ * Callback invoked on closing the progress dialog.
+ */
+ bool onProgressDelete(GdkEventAny *event);
+
+private:
+ ExportProgressDialog *prog_dlg = nullptr;
+ bool interrupted;
+
+private:
+ sigc::connection filenameConn;
+ sigc::connection exportConn;
+ sigc::connection browseConn;
+ sigc::connection selectionModifiedConn;
+ sigc::connection selectionChangedConn;
+};
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export-helper.cpp b/src/ui/dialog/export-helper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a72109e372291f21b5a53c2bb2988e889a0b84d
--- /dev/null
+++ b/src/ui/dialog/export-helper.cpp
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 2021 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "export-helper.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "extension/db.h"
+#include "file.h"
+#include "helper/png-write.h"
+#include "inkscape-window.h"
+#include "inkscape.h"
+#include "io/resource.h"
+#include "io/sys.h"
+#include "message-stack.h"
+#include "object/object-set.h"
+#include "object/sp-namedview.h"
+#include "object/sp-root.h"
+#include "preferences.h"
+#include "selection-chemistry.h"
+#include "ui/dialog-events.h"
+#include "ui/dialog/dialog-notebook.h"
+#include "ui/dialog/filedialog.h"
+#include "ui/icon-loader.h"
+#include "ui/interface.h"
+#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
+
+#ifdef _WIN32
+
+#endif
+
+using Inkscape::Util::unit_table;
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+AdvanceOptions::AdvanceOptions()
+ : row(0)
+{
+ this->set_label(_("Advance"));
+ Gtk::Grid *grid = Gtk::manage(new Gtk::Grid());
+ this->add(*grid);
+ {
+ interlacing.set_label(_("Use Interlacing"));
+ grid->attach(interlacing, 0, row, 2, 1);
+ row++;
+ }
+ {
+ bit_depth_list.clear();
+ bit_depth_list.insert(bit_depth_list.end(), {{_("Gray 1"), {1, 0}},
+ {_("Gray 2"), {2, 0}},
+ {_("Gray 4"), {4, 0}},
+ {_("Gray 8"), {8, 0}},
+ {_("Gray 16"), {16, 0}},
+ {_("RGB 8"), {8, 2}},
+ {_("RGB 16"), {16, 2}},
+ {_("GrayAlpha 8"), {8, 4}},
+ {_("GrayAlpha 16"), {16, 4}},
+ {_("RGBA 8"), {8, 6}},
+ {_("RGBA 16"), {16, 6}}});
+
+ for (auto [label, depth] : bit_depth_list) {
+ bit_depth_cb.append(label);
+ }
+
+ bit_depth_cb.set_active_text(_("RGBA 8"));
+ bit_depth_cb.set_hexpand();
+ Gtk::Label *bit_depth_label = Gtk::manage(new Gtk::Label(_("Bit Depth"), Gtk::ALIGN_START));
+ grid->attach(*bit_depth_label, 0, row, 1, 1);
+ grid->attach(bit_depth_cb, 1, row, 1, 1);
+ row++;
+ }
+ {
+ compression_list.clear();
+ compression_list.insert(compression_list.end(), {{_("Z No Compression"), 0},
+ {_("Z Best Speed"), 1},
+ {_("2"), 2},
+ {_("3"), 3},
+ {_("4"), 4},
+ {_("5"), 5},
+ {_("Z Default Compression"), 6},
+ {_("7"), 7},
+ {_("8"), 8},
+ {_("Z Best Compression"), 9}});
+ for (auto [label, compress] : compression_list) {
+ compression_cb.append(label);
+ }
+
+ compression_cb.set_active_text(_("Z Default Compression"));
+ Gtk::Label *compression_label = Gtk::manage(new Gtk::Label(_("Compression"), Gtk::ALIGN_START));
+ grid->attach(*compression_label, 0, row, 1, 1);
+ grid->attach(compression_cb, 1, row, 1, 1);
+ row++;
+ }
+
+ {
+ auto pHYs_adj = Gtk::Adjustment::create(0, 0, 100000, 0.1, 1.0, 0);
+ pHYs_sb.set_adjustment(pHYs_adj);
+ pHYs_sb.set_width_chars(7);
+ pHYs_sb.set_digits(2);
+ Gtk::Label *phys_dpi_label = Gtk::manage(new Gtk::Label(_("pHYs DPI"), Gtk::ALIGN_START));
+ grid->attach(*phys_dpi_label, 0, row, 1, 1);
+ grid->attach(pHYs_sb, 1, row, 1, 1);
+ row++;
+ }
+ {
+ anti_aliasing_list.clear();
+ anti_aliasing_list.insert(anti_aliasing_list.end(), {{_("Cairo Antialias None"), 0},
+ {_("Cairo Antialias Fast"), 1},
+ {_("Cairo Antialias Good (Default)"), 2},
+ {_("Cairo Antialias Best"), 3}});
+
+ for (auto [label, anti_alias] : anti_aliasing_list) {
+ anti_aliasing_cb.append(label);
+ }
+
+ anti_aliasing_cb.set_active_text(_("Cairo Antialias Good (Default)"));
+ Gtk::Label *anti_aliasing_label = Gtk::manage(new Gtk::Label(_("Anti Aliasing"), Gtk::ALIGN_START));
+ grid->attach(*anti_aliasing_label, 0, row, 1, 1);
+ grid->attach(anti_aliasing_cb, 1, row, 1, 1);
+ row++;
+ }
+ grid->set_row_spacing(4);
+ grid->set_column_spacing(5);
+}
+
+AdvanceOptions::~AdvanceOptions()
+{
+ ;
+}
+
+bool ExtensionList::list_created{false};
+std::map ExtensionList::valid_extensions{};
+std::map ExtensionList::all_extensions{};
+
+void ExtensionList::setup()
+{
+ this->remove_all();
+ createList();
+
+ for (auto [key, omod] : valid_extensions) {
+ this->append(key);
+ }
+ this->set_active_text(".png");
+}
+void ExtensionList::createList()
+{
+ if (list_created) {
+ return;
+ }
+ Inkscape::Extension::DB::OutputList extensions;
+ Inkscape::Extension::db.get_output_list(extensions);
+ Glib::ustring extension;
+ for (auto omod : extensions) {
+ all_extensions[omod->get_extension()] = omod;
+
+ // FIXME: would be nice to grey them out instead of not listing them
+ if (omod->deactivated() || (omod->is_raster() != true))
+ continue;
+
+ extension = omod->get_extension();
+ valid_extensions[extension] = omod;
+ }
+
+ // add extentions manually
+ Inkscape::Extension::Output *manual_omod;
+ manual_omod = dynamic_cast(Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG));
+ extension = manual_omod->get_extension();
+ valid_extensions[extension] = manual_omod;
+
+ list_created = true;
+}
+
+ExtensionList::~ExtensionList()
+{
+ ;
+}
+void ExtensionList::setExtensionFromFilename(Glib::ustring const &filename)
+{
+ Glib::ustring extension = get_ext_from_filename(filename);
+ if (valid_extensions[extension]) {
+ this->set_active_text(extension);
+ return;
+ }
+}
+void ExtensionList::appendExtensionToFilename(Glib::ustring &filename)
+{
+ Glib::ustring filename_extension = get_ext_from_filename(filename);
+ Glib::ustring active_extension = this->get_active_text();
+ if (active_extension == filename_extension) {
+ return;
+ }
+ if (valid_extensions[filename_extension]) {
+ auto extension_point = filename.rfind(filename_extension);
+ filename.erase(extension_point);
+ }
+ filename = filename + active_extension;
+ return;
+}
+void ExtensionList::appendExtensionToFilename(Glib::ustring &filename, Glib::ustring &extension)
+{
+ createList();
+ Glib::ustring filename_extension = get_ext_from_filename(filename);
+ Glib::ustring active_extension = extension;
+ if (all_extensions[filename_extension]) {
+ auto extension_point = filename.rfind(filename_extension);
+ filename.erase(extension_point);
+ }
+ if (valid_extensions[filename_extension]) {
+ active_extension = filename_extension;
+ }
+ // We use ".png" as default extension. Change it to get extension from module.
+ if (!valid_extensions[active_extension]) {
+ active_extension = ".png";
+ }
+ filename = filename + active_extension;
+ return;
+}
+
+void ExportList::setup()
+{
+ if (_initialised) {
+ return;
+ }
+ _initialised = true;
+ prefs = Inkscape::Preferences::get();
+ default_dpi = prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE);
+
+ Gtk::Button *add_button = Gtk::manage(new Gtk::Button());
+ Glib::ustring label = "Add Export";
+ add_button->set_label(label);
+ this->attach(*add_button, 0, 0, 4, 1);
+
+ this->insert_row(0);
+
+ Gtk::Label *suffix_label = Gtk::manage(new Gtk::Label("Suffix"));
+ this->attach(*suffix_label, _suffix_col, 0, 1, 1);
+ suffix_label->show();
+
+ Gtk::Label *extension_label = Gtk::manage(new Gtk::Label("Format"));
+ this->attach(*extension_label, _extension_col, 0, 1, 1);
+ extension_label->show();
+
+ Gtk::Label *dpi_label = Gtk::manage(new Gtk::Label("DPI"));
+ this->attach(*dpi_label, _dpi_col, 0, 1, 1);
+ dpi_label->show();
+
+ append_row();
+
+ add_button->signal_clicked().connect(sigc::mem_fun(*this, &ExportList::append_row));
+ add_button->set_hexpand(true);
+ add_button->show();
+
+ this->set_row_spacing(5);
+ this->set_column_spacing(2);
+}
+
+ExportList::~ExportList()
+{
+ ;
+}
+
+void ExportList::append_row()
+{
+ int current_row = _num_rows + 1; // because we have label row at top
+ this->insert_row(current_row);
+
+ Gtk::Entry *suffix = Gtk::manage(new Gtk::Entry());
+ this->attach(*suffix, _suffix_col, current_row, 1, 1);
+ suffix->set_width_chars(2);
+ suffix->set_hexpand(true);
+ suffix->set_placeholder_text("Suffix");
+ suffix->show();
+
+ ExtensionList *extension = Gtk::manage(new ExtensionList());
+ extension->setup();
+ this->attach(*extension, _extension_col, current_row, 1, 1);
+ extension->show();
+
+ SpinButton *dpi_sb = Gtk::manage(new SpinButton());
+ dpi_sb->set_digits(2);
+ dpi_sb->set_increments(0.1, 1.0);
+ dpi_sb->set_range(1.0, 100000.0);
+ dpi_sb->set_value(default_dpi);
+ dpi_sb->set_sensitive(true);
+ dpi_sb->set_width_chars(6);
+ dpi_sb->set_max_width_chars(6);
+ this->attach(*dpi_sb, _dpi_col, current_row, 1, 1);
+ dpi_sb->show();
+
+ Gtk::Image *pIcon = Gtk::manage(sp_get_icon_image("window-close", Gtk::ICON_SIZE_SMALL_TOOLBAR));
+ Gtk::Button *delete_btn = Gtk::manage(new Gtk::Button());
+ delete_btn->set_relief(Gtk::RELIEF_NONE);
+ delete_btn->set_no_show_all(true);
+ if (_num_rows != 0) {
+ Gtk::Widget *d_button_0 = dynamic_cast(this->get_child_at(_delete_col, 1));
+ if (d_button_0) {
+ d_button_0->show();
+ }
+ delete_btn->show();
+ }
+ pIcon->show();
+ delete_btn->add(*pIcon);
+ this->attach(*delete_btn, _delete_col, current_row, 1, 1);
+ delete_btn->signal_clicked().connect(sigc::bind(sigc::mem_fun(*this, &ExportList::delete_row), delete_btn));
+
+ _num_rows++;
+}
+
+void ExportList::delete_row(Gtk::Widget *widget)
+{
+ if (widget == nullptr) {
+ return;
+ }
+ if (_num_rows <= 1) {
+ return;
+ }
+ int row = this->child_property_top_attach(*widget);
+ this->remove_row(row);
+ _num_rows--;
+ if (_num_rows <= 1) {
+ Gtk::Widget *d_button_0 = dynamic_cast(this->get_child_at(_delete_col, 1));
+ if (d_button_0) {
+ d_button_0->hide();
+ }
+ }
+}
+
+Glib::ustring ExportList::get_suffix(int row)
+{
+ Glib::ustring suffix = "";
+ Gtk::Entry *entry = dynamic_cast(this->get_child_at(_suffix_col, row + 1));
+ if (entry == nullptr) {
+ return suffix;
+ }
+ suffix = entry->get_text();
+ return suffix;
+}
+Glib::ustring ExportList::get_extension(int row)
+{
+ Glib::ustring extension = "";
+ ExtensionList *extension_cb = dynamic_cast(this->get_child_at(_extension_col, row + 1));
+ if (extension_cb == nullptr) {
+ return extension;
+ }
+ extension = extension_cb->get_active_text();
+ return extension;
+}
+double ExportList::get_dpi(int row)
+{
+ double dpi = default_dpi;
+ SpinButton *spin_sb = dynamic_cast(this->get_child_at(_dpi_col, row + 1));
+ if (spin_sb == nullptr) {
+ return dpi;
+ }
+ dpi = spin_sb->get_value();
+ return dpi;
+}
+
+/*
+ ******************************************
+ * HELPER FUNCTIONS NOT SPECIF TO CLASSES *
+ ******************************************
+ */
+
+float getValuePx(float value, Unit const *unit)
+{
+ return Inkscape::Util::Quantity::convert(value, unit, "px");
+}
+
+void setValuePx(Glib::RefPtr &adj, double val, Unit const *unit)
+{
+ auto value = Inkscape::Util::Quantity::convert(val, "px", unit);
+ adj->set_value(value);
+ return;
+}
+
+// We Create filename by removing already present extension in document name and replacing it with extension passed
+// as parameter if exxtension is not valid. If document doesn't have a name we use bitmap as defalt name.
+Glib::ustring get_default_filename(Glib::ustring &filename_entry_text, Glib::ustring &extension, SPDocument *doc)
+{
+ Glib::ustring filename;
+ if (doc && doc->getDocumentFilename()) {
+ filename = doc->getDocumentFilename();
+ ExtensionList::appendExtensionToFilename(filename, extension);
+ } else if (doc) {
+ filename = create_filepath_from_id(_("bitmap"), filename_entry_text);
+ filename = filename + extension;
+ }
+ return filename;
+}
+
+std::string create_filepath_from_id(Glib::ustring id, const Glib::ustring &file_entry_text)
+{
+ if (id.empty()) { /* This should never happen */
+ id = "bitmap";
+ }
+
+ std::string directory;
+
+ if (!file_entry_text.empty()) {
+ directory = Glib::path_get_dirname(Glib::filename_from_utf8(file_entry_text));
+ }
+
+ if (directory.empty()) {
+ /* Grab document directory */
+ const gchar *docFilename = SP_ACTIVE_DOCUMENT->getDocumentFilename();
+ if (docFilename) {
+ directory = Glib::path_get_dirname(docFilename);
+ }
+ }
+
+ if (directory.empty()) {
+ directory = Inkscape::IO::Resource::homedir_path(nullptr);
+ }
+
+ return Glib::build_filename(directory, Glib::filename_from_utf8(id));
+}
+
+Glib::ustring get_ext_from_filename(Glib::ustring const &filename)
+{
+ Glib::ustring extension = "";
+ if (!filename.empty()) {
+ auto extension_point = filename.rfind('.');
+ if (extension_point != Glib::ustring::npos) {
+ extension = filename.substr(extension_point);
+ }
+ }
+ return extension;
+}
+
+std::string absolutize_path_from_document_location(SPDocument *doc, const std::string &filename)
+{
+ std::string path;
+ // Make relative paths go from the document location, if possible:
+ if (!Glib::path_is_absolute(filename) && doc->getDocumentFilename()) {
+ auto dirname = Glib::path_get_dirname(doc->getDocumentFilename());
+ if (!dirname.empty()) {
+ path = Glib::build_filename(dirname, filename);
+ }
+ }
+ if (path.empty()) {
+ path = filename;
+ }
+ return path;
+}
+
+bool _export_raster(Geom::Rect const &area, unsigned long int const &width, unsigned long int const &height,
+ float const &dpi, Glib::ustring const &filename, bool overwrite,
+ unsigned (*callback)(float, void *), ExportProgressDialog *&prog_dialog,
+ Inkscape::Extension::Output *extension, std::vector *items, AdvanceOptions *adv)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (!desktop)
+ return false;
+ SPNamedView *nv = desktop->getNamedView();
+ SPDocument *doc = desktop->getDocument();
+
+ if (area.hasZeroArea() || width == 0 || height == 0) {
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The chosen area to be exported is invalid."));
+ sp_ui_error_dialog(_("The chosen area to be exported is invalid"));
+ return false;
+ }
+ if (filename.empty()) {
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("You have to enter a filename."));
+ sp_ui_error_dialog(_("You have to enter a filename"));
+ return false;
+ }
+
+ if (!extension || !extension->is_raster()) {
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Raster Export Error"));
+ sp_ui_error_dialog(_("Raster export Method is used for NON RASTER EXTENSION"));
+ return false;
+ }
+
+ // Advance Parameters default value. We will change them later if adv dialog is provided.
+ bool use_interlacing = false; // Maybe use prefs here?
+ float pHYs = dpi; // default is dpi.
+ int bit_depth = 8; // corresponds to RGBA 8
+ int color_type = 6; // corresponds to RGBA 8
+ int zlib = 6; // Z_DEFAULT_COMPRESSION
+ int antialiasing = 2; // Cairo anti aliasing
+
+ if (adv) {
+ use_interlacing = adv->get_interlacing();
+ if (adv->get_pHYs() > 0.01) {
+ pHYs = adv->get_pHYs();
+ }
+ bit_depth = adv->get_bit_depth();
+ color_type = adv->get_color();
+ zlib = adv->get_compression();
+ antialiasing = adv->get_anti_aliasing();
+ }
+
+ std::string path = absolutize_path_from_document_location(doc, Glib::filename_from_utf8(filename));
+ Glib::ustring dirname = Glib::path_get_dirname(path);
+
+ if (dirname.empty() ||
+ !Inkscape::IO::file_test(dirname.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
+ Glib::ustring safeDir = Inkscape::IO::sanitizeString(dirname.c_str());
+ Glib::ustring error =
+ g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"), safeDir.c_str());
+
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error.c_str());
+ sp_ui_error_dialog(error.c_str());
+ return false;
+ }
+
+ // Do the over-write protection now, since the png is just a temp file.
+ if (!overwrite && !sp_ui_overwrite_file(path.c_str())) {
+ return false;
+ }
+
+ auto fn = Glib::path_get_basename(path);
+ auto png_filename = path;
+ {
+ // Select the extension and set the filename to a temporary file
+ int tempfd_out = Glib::file_open_tmp(png_filename, "ink_ext_");
+ close(tempfd_out);
+ }
+
+ // Export Start Here
+ std::vector selected;
+ if (items && items->size() > 0) {
+ selected = *items;
+ }
+
+ ExportResult result = sp_export_png_file(desktop->getDocument(), png_filename.c_str(), area, width, height, pHYs,
+ pHYs, // previously xdpi, ydpi.
+ nv->pagecolor, callback, (void *)prog_dialog, true, selected,
+ use_interlacing, color_type, bit_depth, zlib, antialiasing);
+
+ bool failed = result == EXPORT_ERROR || prog_dialog->get_stopped();
+ delete prog_dialog;
+ prog_dialog = nullptr;
+ if (failed) {
+ Glib::ustring safeFile = Inkscape::IO::sanitizeString(path.c_str());
+ Glib::ustring error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile.c_str());
+
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error.c_str());
+ sp_ui_error_dialog(error.c_str());
+ return false;
+ } else if (result == EXPORT_OK) {
+ if (extension->prefs()) {
+ try {
+ extension->export_raster(doc, png_filename, path.c_str(), false);
+ } catch (Inkscape::Extension::Output::save_failed &e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ } else {
+ // Extensions have their own error popup, so this only tracks failures in the png step
+ desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Export aborted."));
+ return false;
+ }
+
+ auto recentmanager = Gtk::RecentManager::get_default();
+ if (recentmanager && Glib::path_is_absolute(path)) {
+ Glib::ustring uri = Glib::filename_to_uri(path);
+ recentmanager->add_item(uri);
+ }
+
+ Glib::ustring safeFile = Inkscape::IO::sanitizeString(path.c_str());
+ desktop->messageStack()->flashF(Inkscape::INFORMATION_MESSAGE, _("Drawing exported to %s."),
+ safeFile.c_str());
+
+ unlink(png_filename.c_str());
+ return true;
+}
+
+bool _export_vector(Inkscape::Extension::Output *extension, SPDocument *doc, Glib::ustring const &filename,
+ bool overwrite, std::vector *items)
+{
+ SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+ if (!desktop)
+ return false;
+
+ if (filename.empty()) {
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("You have to enter a filename."));
+ sp_ui_error_dialog(_("You have to enter a filename"));
+ return false;
+ }
+
+ if (!extension || extension->is_raster()) {
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Vector Export Error"));
+ sp_ui_error_dialog(_("Vector export Method is used for RASTER EXTENSION"));
+ return false;
+ }
+
+ std::string path = absolutize_path_from_document_location(doc, Glib::filename_from_utf8(filename));
+ Glib::ustring dirname = Glib::path_get_dirname(path);
+
+ if (dirname.empty() ||
+ !Inkscape::IO::file_test(dirname.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) {
+ Glib::ustring safeDir = Inkscape::IO::sanitizeString(dirname.c_str());
+ Glib::ustring error =
+ g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"), safeDir.c_str());
+
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error.c_str());
+ sp_ui_error_dialog(error.c_str());
+
+ return false;
+ }
+
+ // Do the over-write protection now
+ if (!overwrite && !sp_ui_overwrite_file(path.c_str())) {
+ return false;
+ }
+ doc->ensureUpToDate();
+ SPDocument *copy_doc = (doc->copy()).get();
+ copy_doc->ensureUpToDate();
+
+ if (items && items->size() > 0) {
+ std::vector objects_to_export;
+ std::vector objects = *items;
+ Inkscape::ObjectSet s(copy_doc);
+ for (auto &object : objects) {
+ SPObject *temp = dynamic_cast(object);
+
+ if (!temp) {
+ Glib::ustring safeFile = Inkscape::IO::sanitizeString(path.c_str());
+ Glib::ustring error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile.c_str());
+
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error.c_str());
+ sp_ui_error_dialog(error.c_str());
+ return false;
+ }
+ SPObject *obj = copy_doc->getObjectById(temp->getId());
+ if (!obj) {
+ Glib::ustring safeFile = Inkscape::IO::sanitizeString(path.c_str());
+ Glib::ustring error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile.c_str());
+
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error.c_str());
+ sp_ui_error_dialog(error.c_str());
+
+ return false;
+ }
+ copy_doc->ensureUpToDate();
+
+ s.add(obj, true);
+ objects_to_export.push_back(obj);
+ }
+ copy_doc->getRoot()->cropToObjects(objects_to_export);
+ s.fitCanvas(true, true);
+ }
+
+ copy_doc->vacuumDocument();
+ try {
+ Inkscape::Extension::save(dynamic_cast(extension), copy_doc, path.c_str(),
+ false, false, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_COPY);
+ } catch (Inkscape::Extension::Output::save_failed &e) {
+ Glib::ustring safeFile = Inkscape::IO::sanitizeString(path.c_str());
+ Glib::ustring error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile.c_str());
+
+ desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error.c_str());
+ sp_ui_error_dialog(error.c_str());
+
+ return false;
+ }
+
+ auto recentmanager = Gtk::RecentManager::get_default();
+ if (recentmanager && Glib::path_is_absolute(path)) {
+ Glib::ustring uri = Glib::filename_to_uri(path);
+ recentmanager->add_item(uri);
+ }
+
+ Glib::ustring safeFile = Inkscape::IO::sanitizeString(path.c_str());
+ desktop->messageStack()->flashF(Inkscape::INFORMATION_MESSAGE, _("Drawing exported to %s."),
+ safeFile.c_str());
+ return true;
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export-helper.h b/src/ui/dialog/export-helper.h
new file mode 100644
index 0000000000000000000000000000000000000000..6e03c7dd8f50ba40b7bfc3085226186b2767e941
--- /dev/null
+++ b/src/ui/dialog/export-helper.h
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 2021 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SP_EXPORT_HELPER_H
+#define SP_EXPORT_HELPER_H
+
+#include
+#include
+#include
+#include
+#include
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "extension/db.h"
+#include "extension/output.h"
+#include "file.h"
+#include "helper/png-write.h"
+#include "inkscape-window.h"
+#include "inkscape.h"
+#include "io/resource.h"
+#include "io/sys.h"
+#include "message-stack.h"
+#include "object/object-set.h"
+#include "object/sp-namedview.h"
+#include "object/sp-root.h"
+#include "preferences.h"
+#include "selection-chemistry.h"
+#include "ui/dialog-events.h"
+#include "ui/dialog/dialog-notebook.h"
+#include "ui/dialog/filedialog.h"
+#include "ui/interface.h"
+#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
+
+using Inkscape::Util::unit_table;
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+#define EXPORT_COORD_PRECISION 3
+#define SP_EXPORT_MIN_SIZE 1.0
+#define DPI_BASE Inkscape::Util::Quantity::convert(1, "in", "px")
+
+// Class for storing and manipulating advance options.
+class AdvanceOptions : public Gtk::Expander
+{
+public:
+ AdvanceOptions();
+ ~AdvanceOptions() override;
+
+private:
+ Gtk::CheckButton interlacing;
+
+ std::vector>> bit_depth_list;
+ Inkscape::UI::Widget::ScrollProtected bit_depth_cb;
+
+ std::vector> compression_list;
+ Inkscape::UI::Widget::ScrollProtected compression_cb;
+
+ Inkscape::UI::Widget::ScrollProtected pHYs_sb;
+ Gtk::SpinButton a;
+
+ std::vector> anti_aliasing_list;
+ Inkscape::UI::Widget::ScrollProtected anti_aliasing_cb;
+
+private:
+ int row;
+
+public:
+ int get_color() { return bit_depth_list[bit_depth_cb.get_active_row_number()].second.second; }
+ int get_bit_depth() { return bit_depth_list[bit_depth_cb.get_active_row_number()].second.first; }
+ int get_compression() { return compression_list[compression_cb.get_active_row_number()].second; }
+ int get_anti_aliasing() { return anti_aliasing_list[anti_aliasing_cb.get_active_row_number()].second; }
+ bool get_interlacing() { return interlacing.get_active(); }
+ double get_pHYs() { return pHYs_sb.get_value(); }
+};
+
+// Class for storing and manipulating extensions
+class ExtensionList : public Inkscape::UI::Widget::ScrollProtected
+{
+public:
+ ExtensionList(){};
+ ExtensionList(BaseObjectType *cobject, const Glib::RefPtr &refGlade)
+ : Inkscape::UI::Widget::ScrollProtected(cobject, refGlade){};
+ ~ExtensionList() override;
+
+public:
+ void setup();
+ void setExtensionFromFilename(Glib::ustring const &filename);
+ void appendExtensionToFilename(Glib::ustring &filename);
+ static void createList();
+ static bool list_created;
+ static void appendExtensionToFilename(Glib::ustring &filename, Glib::ustring &extension);
+
+public:
+ static std::map valid_extensions;
+ static std::map all_extensions;
+};
+
+class ExportProgressDialog : public Gtk::Dialog
+{
+private:
+ Gtk::ProgressBar *_progress = nullptr;
+ Gtk::Widget *_export_panel = nullptr;
+ int _current = 0;
+ int _total = 0;
+ bool _stopped = false;
+
+public:
+ ExportProgressDialog(const Glib::ustring &title, bool modal = false)
+ : Gtk::Dialog(title, modal)
+ {}
+
+ inline void set_export_panel(const decltype(_export_panel) export_panel) { _export_panel = export_panel; }
+ inline decltype(_export_panel) get_export_panel() const { return _export_panel; }
+
+ inline void set_progress(const decltype(_progress) progress) { _progress = progress; }
+ inline decltype(_progress) get_progress() const { return _progress; }
+
+ inline void set_current(const int current) { _current = current; }
+ inline int get_current() const { return _current; }
+
+ inline void set_total(const int total) { _total = total; }
+ inline int get_total() const { return _total; }
+
+ inline bool get_stopped() const { return _stopped; }
+ inline void set_stopped() { _stopped = true; }
+};
+
+class ExportList : public Gtk::Grid
+{
+public:
+ ExportList(){};
+ ExportList(BaseObjectType *cobject, const Glib::RefPtr &refGlade)
+ : Gtk::Grid(cobject){};
+ ~ExportList() override;
+
+public:
+ void setup();
+ void append_row();
+ void delete_row(Gtk::Widget *widget);
+ Glib::ustring get_suffix(int row);
+ Glib::ustring get_extension(int row);
+ double get_dpi(int row);
+ int get_rows() { return _num_rows; }
+
+private:
+ typedef Inkscape::UI::Widget::ScrollProtected SpinButton;
+ Inkscape::Preferences *prefs = nullptr;
+ double default_dpi = 96.00;
+
+private:
+ bool _initialised = false;
+ int _num_rows = 0;
+ int _suffix_col = 0;
+ int _extension_col = 1;
+ int _dpi_col = 2;
+ int _delete_col = 3;
+};
+
+float getValuePx(float value, Unit const *unit);
+void setValuePx(Glib::RefPtr &adj, double val, Unit const *unit);
+Glib::ustring get_default_filename(Glib::ustring &filename_entry_text, Glib::ustring &extension, SPDocument *doc);
+std::string create_filepath_from_id(Glib::ustring id, const Glib::ustring &file_entry_text);
+Glib::ustring get_ext_from_filename(Glib::ustring const &filename);
+std::string absolutize_path_from_document_location(SPDocument *doc, const std::string &filename);
+
+bool _export_raster(Geom::Rect const &area, unsigned long int const &width, unsigned long int const &height,
+ float const &dpi, Glib::ustring const &filename, bool overwrite,
+ unsigned (*callback)(float, void *), ExportProgressDialog *&prog_dialog,
+ Inkscape::Extension::Output *extension, std::vector *items = nullptr,
+ AdvanceOptions *adv = nullptr);
+
+bool _export_vector(Inkscape::Extension::Output *extension, SPDocument *doc, Glib::ustring const &filename,
+ bool overwrite, std::vector *items = nullptr);
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export-preview.cpp b/src/ui/dialog/export-preview.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..425623649842aa57611b153280fb3f7b16b4741b
--- /dev/null
+++ b/src/ui/dialog/export-preview.cpp
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 2021 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "export-preview.h"
+
+#include
+#include
+#include
+#include
+
+#include "display/cairo-utils.h"
+#include "inkscape.h"
+#include "object/sp-defs.h"
+#include "object/sp-item.h"
+#include "object/sp-namedview.h"
+#include "object/sp-root.h"
+#include "preview-util.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+ExportPreview::ExportPreview()
+ : drawing(nullptr)
+ , visionkey(0)
+ , timer(nullptr)
+ , renderTimer(nullptr)
+ , pending(false)
+ , minDelay(0.1)
+ , size(128)
+{
+ pixMem = nullptr;
+ image = nullptr;
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, size);
+ pixMem = new guchar[size * stride];
+ memset(pixMem, 0x00, size * stride);
+
+ auto pb = Gdk::Pixbuf::create_from_data(pixMem, Gdk::COLORSPACE_RGB, true, 8, size, size, stride);
+ image = Gtk::manage(new Gtk::Image(pb));
+ image->show();
+ image->set_name("export_preview_image");
+ // add this image to box here
+ this->pack_start(*image, true, true, 0);
+ show_all_children();
+ this->set_name("export_preview_box");
+ this->set_can_focus(false);
+ image->set_can_focus(false);
+}
+
+void ExportPreview::resetPixels()
+{
+ if (pixMem) {
+ delete pixMem;
+ pixMem = nullptr;
+ }
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, size);
+ pixMem = new guchar[size * stride];
+ auto pb = Gdk::Pixbuf::create_from_data(pixMem, Gdk::COLORSPACE_RGB, true, 8, size, size, stride);
+ memset(pixMem, 0x00, size * stride);
+ image->set(pb);
+ image->show();
+}
+
+ExportPreview::~ExportPreview()
+{
+ if (drawing) {
+ if (_document) {
+ _document->getRoot()->invoke_hide(visionkey);
+ }
+ delete drawing;
+ drawing = nullptr;
+ }
+ if (timer) {
+ timer->stop();
+ delete timer;
+ timer = nullptr;
+ }
+ if (renderTimer) {
+ renderTimer->stop();
+ delete renderTimer;
+ renderTimer = nullptr;
+ }
+ _item = nullptr;
+ _document = nullptr;
+}
+
+void ExportPreview::setItem(SPItem *item)
+{
+ _item = item;
+}
+void ExportPreview::setDbox(double x0, double x1, double y0, double y1)
+{
+ if (!_document) {
+ return;
+ }
+ if ((x1 - x0 == 0) || (y1 - y0) == 0) {
+ return;
+ }
+ _dbox = Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)) * _document->dt2doc();
+}
+
+void ExportPreview::setDocument(SPDocument *document)
+{
+ if (drawing) {
+ if (_document) {
+ _document->getRoot()->invoke_hide(visionkey);
+ }
+ delete drawing;
+ drawing = nullptr;
+ }
+ _document = document;
+ if (_document) {
+ drawing = new Inkscape::Drawing();
+ visionkey = SPItem::display_key_new(1);
+ DrawingItem *ai = _document->getRoot()->invoke_show(*drawing, visionkey, SP_ITEM_SHOW_DISPLAY);
+ if (ai) {
+ drawing->setRoot(ai);
+ }
+ }
+}
+
+void ExportPreview::refreshHide(const std::vector *list)
+{
+ if (_document) {
+ if (isLastHide) {
+ if (drawing) {
+ if (_document) {
+ _document->getRoot()->invoke_hide(visionkey);
+ }
+ delete drawing;
+ drawing = nullptr;
+ }
+ drawing = new Inkscape::Drawing();
+ visionkey = SPItem::display_key_new(1);
+ DrawingItem *ai = _document->getRoot()->invoke_show(*drawing, visionkey, SP_ITEM_SHOW_DISPLAY);
+ if (ai) {
+ drawing->setRoot(ai);
+ }
+ isLastHide = false;
+ }
+ if (list && !list->empty()) {
+ hide_other_items_recursively(_document->getRoot(), *list);
+ isLastHide = true;
+ }
+ }
+}
+
+void ExportPreview::hide_other_items_recursively(SPObject *o, const std::vector &list)
+{
+ if (SP_IS_ITEM(o) && !SP_IS_DEFS(o) && !SP_IS_ROOT(o) && !SP_IS_GROUP(o) &&
+ list.end() == find(list.begin(), list.end(), o)) {
+ SP_ITEM(o)->invoke_hide(visionkey);
+ }
+
+ // recurse
+ if (list.end() == find(list.begin(), list.end(), o)) {
+ for (auto &child : o->children) {
+ hide_other_items_recursively(&child, list);
+ }
+ }
+}
+
+void ExportPreview::queueRefresh()
+{
+ if (drawing == nullptr) {
+ return;
+ }
+ if (!pending) {
+ pending = true;
+ if (!timer) {
+ timer = new Glib::Timer();
+ }
+ Glib::signal_idle().connect(sigc::mem_fun(this, &ExportPreview::refreshCB), Glib::PRIORITY_DEFAULT_IDLE);
+ }
+}
+
+bool ExportPreview::refreshCB()
+{
+ bool callAgain = true;
+ if (!timer) {
+ timer = new Glib::Timer();
+ }
+ if (timer->elapsed() > minDelay) {
+ callAgain = false;
+ refreshPreview();
+ pending = false;
+ }
+ return callAgain;
+}
+
+void ExportPreview::refreshPreview()
+{
+ auto document = _document;
+ if (!timer) {
+ timer = new Glib::Timer();
+ }
+ if (timer->elapsed() < minDelay) {
+ // Do not refresh too quickly
+ queueRefresh();
+ } else if (document) {
+ renderPreview();
+ timer->reset();
+ }
+}
+
+/*
+This is main function which finally render preview. Call this after setting document, item and dbox.
+If dbox is given it will use it.
+if item is given and not dbox then item is used
+If both are not given then simply we do nothing.
+*/
+void ExportPreview::renderPreview()
+{
+ if (!renderTimer) {
+ renderTimer = new Glib::Timer();
+ }
+ renderTimer->reset();
+ if (drawing == nullptr) {
+ return;
+ }
+
+ if (_document) {
+ unsigned unused;
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, size);
+ guchar *px = nullptr;
+
+ if (_dbox) {
+ px = Inkscape::UI::PREVIEW::sp_icon_doc_icon(_document, *drawing, nullptr, size, unused, &_dbox);
+ } else if (_item) {
+ gchar const *id = _item->getId();
+ px = Inkscape::UI::PREVIEW::sp_icon_doc_icon(_document, *drawing, id, size, unused);
+ }
+
+ if (px) {
+ memcpy(pixMem, px, size * stride);
+ g_free(px);
+ px = nullptr;
+ } else {
+ memset(pixMem, 0, size * stride);
+ }
+ image->set(image->get_pixbuf());
+ image->show();
+ }
+
+ renderTimer->stop();
+ minDelay = std::max(0.1, renderTimer->elapsed() * 3.0);
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export-preview.h b/src/ui/dialog/export-preview.h
new file mode 100644
index 0000000000000000000000000000000000000000..e97c74d5679fd0c2b118c32d91085aef0431287d
--- /dev/null
+++ b/src/ui/dialog/export-preview.h
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 2021 Authors
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SP_EXPORT_PREVIEW_H
+#define SP_EXPORT_PREVIEW_H
+
+#include
+
+#include "desktop.h"
+#include "document.h"
+
+class SPObject;
+namespace Glib {
+class Timer;
+}
+
+namespace Inkscape {
+class Drawing;
+namespace UI {
+namespace Dialog {
+
+class ExportPreview : public Gtk::Box
+{
+public:
+ ExportPreview();
+ ~ExportPreview() override;
+
+private:
+ bool isLastHide = false;
+
+private:
+ SPItem *_item = nullptr;
+
+ Geom::OptRect _dbox;
+
+ SPDocument *_document = nullptr;
+
+ Drawing *drawing;
+ unsigned int visionkey;
+ Glib::Timer *timer;
+ Glib::Timer *renderTimer;
+ bool pending;
+ gdouble minDelay;
+
+ int size; // size of preview image
+
+ guchar *pixMem; // Our Itme pixels
+ Gtk::Image *image; // Our Item Image
+
+public:
+ void setDocument(SPDocument *document);
+ void refreshHide(const std::vector *list);
+ void hide_other_items_recursively(SPObject *o, const std::vector &list);
+ void setItem(SPItem *item);
+ void setDbox(double x0, double x1, double y0, double y1);
+ void queueRefresh();
+ void resetPixels();
+
+ void setSize(int newSize)
+ {
+ size = newSize;
+ resetPixels();
+ }
+
+private:
+ void refreshPreview();
+ void renderPreview();
+ bool refreshCB();
+};
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export-single.cpp b/src/ui/dialog/export-single.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..757560e71d56cf61fc9b21b81fa680961f1156ce
--- /dev/null
+++ b/src/ui/dialog/export-single.cpp
@@ -0,0 +1,957 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Lauris Kaplinski
+ * bulia byak
+ * Johan Engelen
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 1999-2007, 2021 Authors
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "export-single.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include "desktop.h"
+#include "document-undo.h"
+#include "document.h"
+#include "export-helper.h"
+#include "extension/db.h"
+#include "file.h"
+#include "helper/png-write.h"
+#include "inkscape-window.h"
+#include "inkscape.h"
+#include "io/resource.h"
+#include "io/sys.h"
+#include "message-stack.h"
+#include "object/object-set.h"
+#include "object/sp-namedview.h"
+#include "object/sp-root.h"
+#include "preferences.h"
+#include "selection-chemistry.h"
+#include "ui/dialog-events.h"
+#include "ui/dialog/dialog-notebook.h"
+#include "ui/dialog/filedialog.h"
+#include "ui/interface.h"
+#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
+#ifdef _WIN32
+
+#endif
+
+using Inkscape::Util::unit_table;
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+SingleExport::~SingleExport()
+{
+ ;
+}
+
+/**
+ * Initialise Builder Objects. Called in Export constructor.
+ */
+void SingleExport::initialise(const Glib::RefPtr &builder)
+{
+ builder->get_widget("si_s_document", selection_buttons[SELECTION_DRAWING]);
+ selection_names[SELECTION_DRAWING] = "drawing";
+ builder->get_widget("si_s_page", selection_buttons[SELECTION_PAGE]);
+ selection_names[SELECTION_PAGE] = "page";
+ builder->get_widget("si_s_selection", selection_buttons[SELECTION_SELECTION]);
+ selection_names[SELECTION_SELECTION] = "selection";
+ builder->get_widget("si_s_custom", selection_buttons[SELECTION_CUSTOM]);
+ selection_names[SELECTION_CUSTOM] = "custom";
+
+ builder->get_widget_derived("si_left_sb", spin_buttons[SPIN_X0]);
+ builder->get_widget_derived("si_right_sb", spin_buttons[SPIN_X1]);
+ builder->get_widget_derived("si_top_sb", spin_buttons[SPIN_Y0]);
+ builder->get_widget_derived("si_bottom_sb", spin_buttons[SPIN_Y1]);
+ builder->get_widget_derived("si_height_sb", spin_buttons[SPIN_HEIGHT]);
+ builder->get_widget_derived("si_width_sb", spin_buttons[SPIN_WIDTH]);
+
+ builder->get_widget("si_label_left", spin_labels[SPIN_X0]);
+ builder->get_widget("si_label_right", spin_labels[SPIN_X1]);
+ builder->get_widget("si_label_top", spin_labels[SPIN_Y0]);
+ builder->get_widget("si_label_bottom", spin_labels[SPIN_Y1]);
+ builder->get_widget("si_label_height", spin_labels[SPIN_HEIGHT]);
+ builder->get_widget("si_label_width", spin_labels[SPIN_WIDTH]);
+
+ builder->get_widget_derived("si_img_height_sb", spin_buttons[SPIN_BMHEIGHT]);
+ builder->get_widget_derived("si_img_width_sb", spin_buttons[SPIN_BMWIDTH]);
+ builder->get_widget_derived("si_dpi_sb", spin_buttons[SPIN_DPI]);
+
+ // builder->get_widget("si_show_export_area", show_export_area);
+ builder->get_widget_derived("si_units", units);
+ builder->get_widget("si_units_row", si_units_row);
+
+ builder->get_widget("si_hide_all", si_hide_all);
+ builder->get_widget("si_preview_box", si_preview_box);
+ builder->get_widget("si_show_preview", si_show_preview);
+
+ builder->get_widget_derived("si_extention", si_extension_cb);
+ builder->get_widget("si_filename", si_filename_entry);
+ builder->get_widget("si_export", si_export);
+
+ builder->get_widget("si_progress", _prog);
+
+ builder->get_widget("si_advance_box", adv_box);
+
+ Inkscape::UI::Widget::ScrollTransfer *temp = nullptr;
+ builder->get_widget_derived("s_scroll", temp);
+}
+
+// Inkscape Selection Modified CallBack
+void SingleExport::selectionModified(Inkscape::Selection *selection, guint flags)
+{
+ if (!_desktop || _desktop->getSelection() != selection) {
+ return;
+ }
+ if (!(flags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_PARENT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
+ return;
+ }
+ Geom::OptRect bbox;
+ SPDocument *doc = _desktop->getDocument();
+
+ if (!doc) {
+ return;
+ }
+
+ // Will Remove this code after testing
+
+ // switch (current_key) {
+ // case SELECTION_DRAWING:
+ // bbox = doc->getRoot()->desktopVisualBounds();
+ // if (bbox) {
+ // setArea(bbox->left(), bbox->top(), bbox->right(), bbox->bottom());
+ // }
+ // break;
+ // case SELECTION_SELECTION:
+ // if (selection->isEmpty() == false) {
+ // bbox = selection->visualBounds();
+ // if (bbox) {
+ // setArea(bbox->left(), bbox->top(), bbox->right(), bbox->bottom());
+ // }
+ // }
+ // break;
+ // default:
+ // /* Do nothing for page or for custom */
+ // break;
+ // }
+
+ refreshArea();
+ refreshExportHints();
+}
+
+void SingleExport::selectionChanged(Inkscape::Selection *selection)
+{
+ if (!_desktop || _desktop->getSelection() != selection) {
+ return;
+ }
+ Glib::ustring pref_key_name = prefs->getString("/dialogs/export/exportarea/value");
+ for (auto [key, name] : selection_names) {
+ if (name == pref_key_name && current_key != key && key != SELECTION_SELECTION) {
+ selection_buttons[key]->set_active(true);
+ current_key = key;
+ break;
+ }
+ }
+ if (selection->isEmpty()) {
+ selection_buttons[SELECTION_SELECTION]->set_sensitive(false);
+ if (current_key == SELECTION_SELECTION) {
+ selection_buttons[(selection_mode)0]->set_active(true); // This causes refresh area
+ // even though we are at default key, selection is the one which was original key.
+ prefs->setString("/dialogs/export/exportarea/value", selection_names[SELECTION_SELECTION]);
+ // return otherwise refreshArea will be called again
+ return;
+ }
+ } else {
+ selection_buttons[SELECTION_SELECTION]->set_sensitive(true);
+ if (selection_names[SELECTION_SELECTION] == pref_key_name && current_key != SELECTION_SELECTION) {
+ selection_buttons[SELECTION_SELECTION]->set_active();
+ return;
+ }
+ }
+
+ refreshArea();
+ refreshExportHints();
+}
+
+// Setup Single Export.Called by export on realize
+void SingleExport::setup()
+{
+ if (setupDone) {
+ // We need to setup only once
+ return;
+ }
+ setupDone = true;
+ prefs = Inkscape::Preferences::get();
+ si_extension_cb->setup();
+
+ // Add advance options to adv box
+ adv_box->pack_start(advance_options, true, true, 0);
+ adv_box->show_all_children();
+
+ setupUnits();
+ setupSpinButtons();
+
+ // set them before connecting to signals
+ setDefaultFilename();
+ setDefaultSelectionMode();
+
+ // Refresh values to sync them with defaults.
+ refreshArea();
+ refreshExportHints();
+
+ // Connect Signals Here
+ for (auto [key, button] : selection_buttons) {
+ button->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &SingleExport::onAreaTypeToggle), key));
+ }
+ units->signal_changed().connect(sigc::mem_fun(*this, &SingleExport::onUnitChanged));
+ filenameConn = si_filename_entry->signal_changed().connect(sigc::mem_fun(*this, &SingleExport::onFilenameModified));
+ extensionConn = si_extension_cb->signal_changed().connect(sigc::mem_fun(*this, &SingleExport::onExtensionChanged));
+ exportConn = si_export->signal_clicked().connect(sigc::mem_fun(*this, &SingleExport::onExport));
+ browseConn = si_filename_entry->signal_icon_press().connect(sigc::mem_fun(*this, &SingleExport::onBrowse));
+ si_show_preview->signal_toggled().connect(sigc::mem_fun(*this, &SingleExport::refreshPreview));
+ si_hide_all->signal_toggled().connect(sigc::mem_fun(*this, &SingleExport::refreshPreview));
+}
+
+// Setup units combobox
+void SingleExport::setupUnits()
+{
+ units->setUnitType(Inkscape::Util::UNIT_TYPE_LINEAR);
+ if (_desktop) {
+ units->setUnit(_desktop->getNamedView()->display_units->abbr);
+ }
+}
+
+// Create all spin buttons
+void SingleExport::setupSpinButtons()
+{
+ setupSpinButton(spin_buttons[SPIN_X0], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
+ &SingleExport::onAreaXChange, SPIN_X0);
+ setupSpinButton(spin_buttons[SPIN_X1], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
+ &SingleExport::onAreaXChange, SPIN_X1);
+ setupSpinButton(spin_buttons[SPIN_Y0], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
+ &SingleExport::onAreaYChange, SPIN_Y0);
+ setupSpinButton(spin_buttons[SPIN_Y1], 0.0, -1000000.0, 1000000.0, 0.1, 1.0, EXPORT_COORD_PRECISION, true,
+ &SingleExport::onAreaYChange, SPIN_Y1);
+
+ setupSpinButton(spin_buttons[SPIN_HEIGHT], 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0, EXPORT_COORD_PRECISION,
+ true, &SingleExport::onAreaYChange, SPIN_HEIGHT);
+ setupSpinButton(spin_buttons[SPIN_WIDTH], 0.0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0, EXPORT_COORD_PRECISION,
+ true, &SingleExport::onAreaXChange, SPIN_WIDTH);
+
+ setupSpinButton(spin_buttons[SPIN_BMHEIGHT], 1.0, 1.0, 1000000.0, 1.0, 10.0, 0, true,
+ &SingleExport::onDpiChange, SPIN_BMHEIGHT);
+ setupSpinButton(spin_buttons[SPIN_BMWIDTH], 1.0, 1.0, 1000000.0, 1.0, 10.0, 0, true,
+ &SingleExport::onDpiChange, SPIN_BMWIDTH);
+ setupSpinButton(spin_buttons[SPIN_DPI], prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
+ 1.0, 100000.0, 0.1, 1.0, 2, true, &SingleExport::onDpiChange, SPIN_DPI);
+}
+
+template
+void SingleExport::setupSpinButton(Gtk::SpinButton *sb, double val, double min, double max, double step, double page,
+ int digits, bool sensitive, void (SingleExport::*cb)(T), T param)
+{
+ if (sb) {
+ sb->set_digits(digits);
+ sb->set_increments(step, page);
+ sb->set_range(min, max);
+ sb->set_value(val);
+ sb->set_sensitive(sensitive);
+ sb->set_width_chars(0);
+ sb->set_max_width_chars(0);
+ if (cb) {
+ auto signal = sb->signal_value_changed().connect(sigc::bind(sigc::mem_fun(*this, cb), param));
+ // add signals to list to block all easily
+ spinButtonConns.push_back(signal);
+ }
+ }
+}
+
+void SingleExport::refreshArea()
+{
+ if (_desktop) {
+ SPDocument *doc;
+ Geom::OptRect bbox;
+ doc = _desktop->getDocument();
+ doc->ensureUpToDate();
+
+ switch (current_key) {
+ case SELECTION_SELECTION:
+ if ((_desktop->getSelection())->isEmpty() == false) {
+ bbox = _desktop->getSelection()->visualBounds();
+ break;
+ }
+ case SELECTION_DRAWING:
+ bbox = doc->getRoot()->desktopVisualBounds();
+ if (bbox) {
+ break;
+ }
+ case SELECTION_PAGE:
+ bbox = Geom::Rect(Geom::Point(0.0, 0.0),
+ Geom::Point(doc->getWidth().value("px"), doc->getHeight().value("px")));
+ break;
+ case SELECTION_CUSTOM:
+ break;
+ default:
+ break;
+ }
+ if (current_key != SELECTION_CUSTOM && bbox) {
+ setArea(bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y]);
+ }
+ }
+ refreshPreview();
+}
+
+void SingleExport::refreshExportHints()
+{
+ if (_desktop && !filename_modified) {
+ SPDocument *doc = _desktop->getDocument();
+ Glib::ustring filename;
+ float xdpi = 0.0, ydpi = 0.0;
+ switch (current_key) {
+ case SELECTION_CUSTOM:
+ case SELECTION_PAGE:
+ case SELECTION_DRAWING:
+ sp_document_get_export_hints(doc, filename, &xdpi, &ydpi);
+ if (filename.empty()) {
+ Glib::ustring filename_entry_text = si_filename_entry->get_text();
+ Glib::ustring extension_entry_text = si_extension_cb->get_active_text();
+ filename = get_default_filename(filename_entry_text, extension_entry_text, doc);
+ }
+ doc_export_name = filename;
+ break;
+ case SELECTION_SELECTION:
+ if ((_desktop->getSelection())->isEmpty()) {
+ break;
+ }
+ _desktop->getSelection()->getExportHints(filename, &xdpi, &ydpi);
+
+ /* If we still don't have a filename -- let's build
+ one that's nice */
+ if (filename.empty()) {
+ const gchar *id = "object";
+ auto reprlst = _desktop->getSelection()->xmlNodes();
+ for (auto i = reprlst.begin(); reprlst.end() != i; ++i) {
+ Inkscape::XML::Node *repr = *i;
+ if (repr->attribute("id")) {
+ id = repr->attribute("id");
+ break;
+ }
+ }
+ filename = create_filepath_from_id(id, si_filename_entry->get_text());
+ filename = filename + si_extension_cb->get_active_text();
+ }
+ break;
+ default:
+ break;
+ }
+ if (!filename.empty()) {
+ original_name = filename;
+ si_filename_entry->set_text(filename);
+ si_filename_entry->set_position(filename.length());
+ } else {
+ Glib::ustring newName = !doc_export_name.empty() ? doc_export_name : original_name;
+ if (!newName.empty()) {
+ si_filename_entry->set_text(filename);
+ si_filename_entry->set_position(filename.length());
+ }
+ }
+
+ if (xdpi != 0.0) {
+ spin_buttons[SPIN_DPI]->set_value(xdpi);
+ }
+ }
+}
+
+void SingleExport::setArea(double x0, double y0, double x1, double y1)
+{
+ blockSpinConns(true);
+
+ auto x0_adj = spin_buttons[SPIN_X0]->get_adjustment();
+ auto x1_adj = spin_buttons[SPIN_X1]->get_adjustment();
+ auto y0_adj = spin_buttons[SPIN_Y0]->get_adjustment();
+ auto y1_adj = spin_buttons[SPIN_Y1]->get_adjustment();
+
+ Unit const *unit = units->getUnit();
+ setValuePx(x1_adj, x1, unit);
+ setValuePx(y1_adj, y1, unit);
+ setValuePx(x0_adj, x0, unit);
+ setValuePx(y0_adj, y0, unit);
+
+ areaXChange(SPIN_X1);
+ areaYChange(SPIN_Y1);
+
+ blockSpinConns(false);
+}
+
+// Signals CallBack
+
+void SingleExport::onUnitChanged()
+{
+ refreshArea();
+}
+
+void SingleExport::onAreaTypeToggle(selection_mode key)
+{
+ // Prevent executing function twice
+ if (!selection_buttons[key]->get_active()) {
+ return;
+ }
+ // If you have reached here means the current key is active one ( not sure if multiple transitions happen but
+ // last call will change values)
+ current_key = key;
+ prefs->setString("/dialogs/export/exportarea/value", selection_names[current_key]);
+
+ refreshArea();
+ refreshExportHints();
+ toggleSpinButtonVisibility();
+}
+
+void SingleExport::toggleSpinButtonVisibility()
+{
+ bool show = current_key == SELECTION_CUSTOM;
+ spin_buttons[SPIN_X0]->set_visible(show);
+ spin_buttons[SPIN_X1]->set_visible(show);
+ spin_buttons[SPIN_Y0]->set_visible(show);
+ spin_buttons[SPIN_Y1]->set_visible(show);
+ spin_buttons[SPIN_WIDTH]->set_visible(show);
+ spin_buttons[SPIN_HEIGHT]->set_visible(show);
+
+ spin_labels[SPIN_X0]->set_visible(show);
+ spin_labels[SPIN_X1]->set_visible(show);
+ spin_labels[SPIN_Y0]->set_visible(show);
+ spin_labels[SPIN_Y1]->set_visible(show);
+ spin_labels[SPIN_WIDTH]->set_visible(show);
+ spin_labels[SPIN_HEIGHT]->set_visible(show);
+
+ si_units_row->set_visible(show);
+}
+
+void SingleExport::onAreaXChange(sb_type type)
+{
+ blockSpinConns(true);
+ areaXChange(type);
+ selection_buttons[SELECTION_CUSTOM]->set_active(true);
+ refreshPreview();
+ blockSpinConns(false);
+}
+void SingleExport::onAreaYChange(sb_type type)
+{
+ blockSpinConns(true);
+ areaYChange(type);
+ selection_buttons[SELECTION_CUSTOM]->set_active(true);
+ refreshPreview();
+ blockSpinConns(false);
+}
+void SingleExport::onDpiChange(sb_type type)
+{
+ blockSpinConns(true);
+ dpiChange(type);
+ blockSpinConns(false);
+}
+
+void SingleExport::onFilenameModified()
+{
+ extensionConn.block();
+ Glib::ustring filename = si_filename_entry->get_text();
+
+ if (original_name == filename) {
+ filename_modified = false;
+ } else {
+ filename_modified = true;
+ }
+
+ si_extension_cb->setExtensionFromFilename(filename);
+
+ extensionConn.unblock();
+}
+
+void SingleExport::onExtensionChanged()
+{
+ filenameConn.block();
+ Glib::ustring filename = si_filename_entry->get_text();
+ si_extension_cb->appendExtensionToFilename(filename);
+ si_filename_entry->set_text(filename);
+ si_filename_entry->set_position(filename.length());
+ filenameConn.unblock();
+}
+
+void SingleExport::onExport()
+{
+ interrupted = false;
+ if (!_desktop)
+ return;
+ si_export->set_sensitive(false);
+ bool exportSuccessful = false;
+ auto extension = si_extension_cb->get_active_text();
+ auto omod = ExtensionList::valid_extensions[extension];
+ if (!omod) {
+ si_export->set_sensitive(true);
+ return;
+ }
+
+ Unit const *unit = units->getUnit();
+
+ Glib::ustring filename = si_filename_entry->get_text();
+
+ if (omod->is_raster()) {
+ float x0 = getValuePx(spin_buttons[SPIN_X0]->get_value(), unit);
+ float x1 = getValuePx(spin_buttons[SPIN_X1]->get_value(), unit);
+ float y0 = getValuePx(spin_buttons[SPIN_Y0]->get_value(), unit);
+ float y1 = getValuePx(spin_buttons[SPIN_Y1]->get_value(), unit);
+ auto area = Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)) * _desktop->dt2doc();
+
+ unsigned long int width = int(spin_buttons[SPIN_BMWIDTH]->get_value() + 0.5);
+ unsigned long int height = int(spin_buttons[SPIN_BMHEIGHT]->get_value() + 0.5);
+
+ float dpi = spin_buttons[SPIN_DPI]->get_value();
+
+ /* TRANSLATORS: %1 will be the filename, %2 the width, and %3 the height of the image */
+ prog_dlg = create_progress_dialog(Glib::ustring::compose(_("Exporting %1 (%2 x %3)"), filename, width, height));
+ prog_dlg->set_export_panel(this);
+ setExporting(true, Glib::ustring::compose(_("Exporting %1 (%2 x %3)"), filename, width, height));
+ prog_dlg->set_current(0);
+ prog_dlg->set_total(0);
+
+ std::vector selected(_desktop->getSelection()->items().begin(),
+ _desktop->getSelection()->items().end());
+ bool hide = si_hide_all->get_active();
+
+ exportSuccessful = _export_raster(area, width, height, dpi, filename, false, onProgressCallback, prog_dlg, omod,
+ hide ? &selected : nullptr, &advance_options);
+
+ } else {
+ setExporting(true, Glib::ustring::compose(_("Exporting %1"), filename));
+ SPDocument *doc = _desktop->getDocument();
+ SPDocument *copy_doc = (doc->copy()).get();
+ if (current_key == SELECTION_DRAWING) {
+ fit_canvas_to_drawing(copy_doc, true);
+ }
+ std::vector items;
+ if (current_key == SELECTION_SELECTION) {
+ auto itemlist = _desktop->getSelection()->items();
+ for (auto i = itemlist.begin(); i != itemlist.end(); ++i) {
+ SPItem *item = *i;
+ items.push_back(item);
+ }
+ }
+ exportSuccessful = _export_vector(omod, copy_doc, filename, false, &items);
+ }
+ if (prog_dlg) {
+ delete prog_dlg;
+ prog_dlg = nullptr;
+ }
+ setExporting(false);
+ si_export->set_sensitive(true);
+ original_name = filename;
+ filename_modified = false;
+ interrupted = false;
+}
+
+void SingleExport::onBrowse(Gtk::EntryIconPosition pos, const GdkEventButton *ev)
+{
+ if (!_app) {
+ return;
+ }
+ Gtk::Window *window = _app->get_active_window();
+ browseConn.block();
+ Glib::ustring filename = Glib::filename_from_utf8(si_filename_entry->get_text());
+
+ if (filename.empty()) {
+ Glib::ustring tmp;
+ filename = create_filepath_from_id(tmp, tmp);
+ }
+
+ Inkscape::UI::Dialog::FileSaveDialog *dialog = Inkscape::UI::Dialog::FileSaveDialog::create(
+ *window, filename, Inkscape::UI::Dialog::RASTER_TYPES, _("Select a filename for exporting"), "", "",
+ Inkscape::Extension::FILE_SAVE_METHOD_EXPORT);
+
+ if (dialog->show()) {
+ filename = dialog->getFilename();
+ Inkscape::Extension::Output *selection_type =
+ dynamic_cast(dialog->getSelectionType());
+ Glib::ustring extension = selection_type->get_extension();
+ ExtensionList::appendExtensionToFilename(filename, extension);
+ si_filename_entry->set_text(filename);
+ si_filename_entry->set_position(filename.length());
+ // deleting dialog before exporting is important
+ // proper delete function should be made for dialog IMO
+ delete dialog;
+ onExport();
+ } else {
+ delete dialog;
+ }
+ browseConn.unblock();
+}
+
+// Utils Functions
+
+void SingleExport::blockSpinConns(bool status = true)
+{
+ for (auto signal : spinButtonConns) {
+ if (status) {
+ signal.block();
+ } else {
+ signal.unblock();
+ }
+ }
+}
+
+void SingleExport::areaXChange(sb_type type)
+{
+ auto x0_adj = spin_buttons[SPIN_X0]->get_adjustment();
+ auto x1_adj = spin_buttons[SPIN_X1]->get_adjustment();
+ auto width_adj = spin_buttons[SPIN_WIDTH]->get_adjustment();
+
+ float x0, x1, dpi, width, bmwidth;
+
+ // Get all values in px
+ Unit const *unit = units->getUnit();
+ x0 = getValuePx(x0_adj->get_value(), unit);
+ x1 = getValuePx(x1_adj->get_value(), unit);
+ width = getValuePx(width_adj->get_value(), unit);
+ bmwidth = spin_buttons[SPIN_BMWIDTH]->get_value();
+ dpi = spin_buttons[SPIN_DPI]->get_value();
+
+ switch (type) {
+ case SPIN_X0:
+ bmwidth = (x1 - x0) * dpi / DPI_BASE;
+ if (bmwidth < SP_EXPORT_MIN_SIZE) {
+ x0 = x1 - (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
+ }
+ break;
+ case SPIN_X1:
+ bmwidth = (x1 - x0) * dpi / DPI_BASE;
+ if (bmwidth < SP_EXPORT_MIN_SIZE) {
+ x1 = x0 + (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
+ }
+ break;
+ case SPIN_WIDTH:
+ bmwidth = width * dpi / DPI_BASE;
+ if (bmwidth < SP_EXPORT_MIN_SIZE) {
+ width = (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
+ }
+ x1 = x0 + width;
+ break;
+ default:
+ break;
+ }
+
+ width = x1 - x0;
+ bmwidth = floor(width * dpi / DPI_BASE + 0.5);
+
+ setValuePx(x0_adj, x0, unit);
+ setValuePx(x1_adj, x1, unit);
+ setValuePx(width_adj, width, unit);
+ spin_buttons[SPIN_BMWIDTH]->set_value(bmwidth);
+}
+
+void SingleExport::areaYChange(sb_type type)
+{
+ auto y0_adj = spin_buttons[SPIN_Y0]->get_adjustment();
+ auto y1_adj = spin_buttons[SPIN_Y1]->get_adjustment();
+ auto height_adj = spin_buttons[SPIN_HEIGHT]->get_adjustment();
+
+ float y0, y1, dpi, height, bmheight;
+
+ // Get all values in px
+ Unit const *unit = units->getUnit();
+ y0 = getValuePx(y0_adj->get_value(), unit);
+ y1 = getValuePx(y1_adj->get_value(), unit);
+ height = getValuePx(height_adj->get_value(), unit);
+ bmheight = spin_buttons[SPIN_BMHEIGHT]->get_value();
+ dpi = spin_buttons[SPIN_DPI]->get_value();
+
+ switch (type) {
+ case SPIN_Y0:
+ bmheight = (y1 - y0) * dpi / DPI_BASE;
+ if (bmheight < SP_EXPORT_MIN_SIZE) {
+ y0 = y1 - (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
+ }
+ break;
+ case SPIN_Y1:
+ bmheight = (y1 - y0) * dpi / DPI_BASE;
+ if (bmheight < SP_EXPORT_MIN_SIZE) {
+ y1 = y0 + (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
+ }
+ break;
+ case SPIN_HEIGHT:
+ bmheight = height * dpi / DPI_BASE;
+ if (bmheight < SP_EXPORT_MIN_SIZE) {
+ height = (SP_EXPORT_MIN_SIZE * DPI_BASE) / dpi;
+ }
+ y1 = y0 + height;
+ break;
+ default:
+ break;
+ }
+
+ height = y1 - y0;
+ bmheight = floor(height * dpi / DPI_BASE + 0.5);
+
+ setValuePx(y0_adj, y0, unit);
+ setValuePx(y1_adj, y1, unit);
+ setValuePx(height_adj, height, unit);
+ spin_buttons[SPIN_BMHEIGHT]->set_value(bmheight);
+}
+
+void SingleExport::dpiChange(sb_type type)
+{
+ float dpi, height, width, bmheight, bmwidth;
+
+ // Get all values in px
+ Unit const *unit = units->getUnit();
+ height = getValuePx(spin_buttons[SPIN_HEIGHT]->get_value(), unit);
+ width = getValuePx(spin_buttons[SPIN_WIDTH]->get_value(), unit);
+ bmheight = spin_buttons[SPIN_BMHEIGHT]->get_value();
+ bmwidth = spin_buttons[SPIN_BMWIDTH]->get_value();
+ dpi = spin_buttons[SPIN_DPI]->get_value();
+
+ switch (type) {
+ case SPIN_BMHEIGHT:
+ if (bmheight < SP_EXPORT_MIN_SIZE) {
+ bmheight = SP_EXPORT_MIN_SIZE;
+ }
+ dpi = bmheight * DPI_BASE / height;
+ break;
+ case SPIN_BMWIDTH:
+ if (bmwidth < SP_EXPORT_MIN_SIZE) {
+ bmwidth = SP_EXPORT_MIN_SIZE;
+ }
+ dpi = bmwidth * DPI_BASE / width;
+ break;
+ case SPIN_DPI:
+ prefs->setDouble("/dialogs/export/defaultdpi/value", dpi);
+ break;
+ default:
+ break;
+ }
+
+ bmwidth = floor(width * dpi / DPI_BASE + 0.5);
+ bmheight = floor(height * dpi / DPI_BASE + 0.5);
+
+ spin_buttons[SPIN_BMHEIGHT]->set_value(bmheight);
+ spin_buttons[SPIN_BMWIDTH]->set_value(bmwidth);
+ spin_buttons[SPIN_DPI]->set_value(dpi);
+}
+
+// We first check any export hints related to document. If there is none we create a default name using document
+// name. doc_export_name is set here and will only be changed when exporting.
+void SingleExport::setDefaultFilename()
+{
+ if (!_desktop) {
+ return;
+ }
+ Glib::ustring filename;
+ float xdpi = 0.0, ydpi = 0.0;
+ SPDocument *doc = _desktop->getDocument();
+ sp_document_get_export_hints(doc, filename, &xdpi, &ydpi);
+ if (filename.empty()) {
+ Glib::ustring filename_entry_text = si_filename_entry->get_text();
+ Glib::ustring extention_entry_text = si_extension_cb->get_active_text();
+ filename = get_default_filename(filename_entry_text, extention_entry_text, doc);
+ }
+ doc_export_name = filename;
+ original_name = filename;
+ si_filename_entry->set_text(filename);
+ si_filename_entry->set_position(filename.length());
+
+ si_extension_cb->setExtensionFromFilename(filename);
+
+ // We only need to check xdpi
+ if (xdpi != 0.0) {
+ spin_buttons[SPIN_DPI]->set_value(xdpi);
+ }
+}
+
+void SingleExport::setDefaultSelectionMode()
+{
+ current_key = (selection_mode)0; // default key
+ bool found = false;
+ Glib::ustring pref_key_name = prefs->getString("/dialogs/export/exportarea/value");
+ for (auto [key, name] : selection_names) {
+ if (pref_key_name == name) {
+ current_key = key;
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ pref_key_name = selection_names[current_key];
+ }
+
+ if (_desktop) {
+ if (current_key == SELECTION_SELECTION && (_desktop->getSelection())->isEmpty()) {
+ current_key = (selection_mode)0;
+ }
+ if ((_desktop->getSelection())->isEmpty()) {
+ selection_buttons[SELECTION_SELECTION]->set_sensitive(false);
+ }
+ if (current_key == SELECTION_CUSTOM &&
+ (spin_buttons[SPIN_HEIGHT]->get_value() == 0 || spin_buttons[SPIN_WIDTH]->get_value() == 0)) {
+ SPDocument *doc;
+ Geom::OptRect bbox;
+ doc = _desktop->getDocument();
+ bbox = Geom::Rect(Geom::Point(0.0, 0.0),
+ Geom::Point(doc->getWidth().value("px"), doc->getHeight().value("px")));
+ setArea(bbox->min()[Geom::X], bbox->min()[Geom::Y], bbox->max()[Geom::X], bbox->max()[Geom::Y]);
+ }
+ } else {
+ current_key = (selection_mode)0;
+ }
+ selection_buttons[current_key]->set_active(true);
+ prefs->setString("/dialogs/export/exportarea/value", pref_key_name);
+
+ toggleSpinButtonVisibility();
+}
+
+void SingleExport::setExporting(bool exporting, Glib::ustring const &text)
+{
+ if (exporting) {
+ _prog->set_text(text);
+ _prog->set_fraction(0.0);
+ _prog->set_sensitive(true);
+ si_export->set_sensitive(false);
+ } else {
+ _prog->set_text("");
+ _prog->set_fraction(0.0);
+ _prog->set_sensitive(false);
+ si_export->set_sensitive(true);
+ }
+}
+
+ExportProgressDialog *SingleExport::create_progress_dialog(Glib::ustring progress_text)
+{
+ // dont forget to delete it later
+ auto dlg = new ExportProgressDialog(_("Export in progress"), true);
+ dlg->set_transient_for(*(INKSCAPE.active_desktop()->getToplevel()));
+
+ Gtk::ProgressBar *prg = Gtk::manage(new Gtk::ProgressBar());
+ prg->set_text(progress_text);
+ dlg->set_progress(prg);
+ auto CA = dlg->get_content_area();
+ CA->pack_start(*prg, FALSE, FALSE, 4);
+
+ Gtk::Button *btn = dlg->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
+
+ btn->signal_clicked().connect(sigc::mem_fun(*this, &SingleExport::onProgressCancel));
+ dlg->signal_delete_event().connect(sigc::mem_fun(*this, &SingleExport::onProgressDelete));
+
+ dlg->show_all();
+ return dlg;
+}
+
+/// Called when dialog is deleted
+bool SingleExport::onProgressDelete(GdkEventAny * /*event*/)
+{
+ interrupted = true;
+ prog_dlg->set_stopped();
+ return TRUE;
+}
+/// Called when progress is cancelled
+void SingleExport::onProgressCancel()
+{
+ interrupted = true;
+ prog_dlg->set_stopped();
+}
+
+// Called for every progress iteration
+unsigned int SingleExport::onProgressCallback(float value, void *dlg)
+{
+ auto dlg2 = reinterpret_cast(dlg);
+
+ auto self = dynamic_cast(dlg2->get_export_panel());
+
+ if (!self || self->interrupted)
+ return FALSE;
+
+ auto current = dlg2->get_current();
+ auto total = dlg2->get_total();
+ if (total > 0) {
+ double completed = current;
+ completed /= static_cast(total);
+
+ value = completed + (value / static_cast(total));
+ }
+
+ auto prg = dlg2->get_progress();
+ prg->set_fraction(value);
+
+ if (self) {
+ self->_prog->set_fraction(value);
+ }
+
+ int evtcount = 0;
+ while ((evtcount < 16) && gdk_events_pending()) {
+ Gtk::Main::iteration(false);
+ evtcount += 1;
+ }
+
+ Gtk::Main::iteration(false);
+ return TRUE;
+}
+
+void SingleExport::refreshPreview()
+{
+ if (!_desktop) {
+ return;
+ }
+ if (!preview) {
+ preview = Gtk::manage(new ExportPreview());
+ si_preview_box->pack_start(*preview, true, true, 0);
+ si_preview_box->show_all_children();
+ }
+ if (!si_show_preview->get_active()) {
+ preview->resetPixels();
+ return;
+ }
+
+ std::vector selected(_desktop->getSelection()->items().begin(), _desktop->getSelection()->items().end());
+ bool hide = si_hide_all->get_active();
+
+ Unit const *unit = units->getUnit();
+ float x0 = getValuePx(spin_buttons[SPIN_X0]->get_value(), unit);
+ float x1 = getValuePx(spin_buttons[SPIN_X1]->get_value(), unit);
+ float y0 = getValuePx(spin_buttons[SPIN_Y0]->get_value(), unit);
+ float y1 = getValuePx(spin_buttons[SPIN_Y1]->get_value(), unit);
+ preview->setItem(nullptr);
+ preview->setDbox(x0, x1, y0, y1);
+ preview->refreshHide(hide ? &selected : nullptr);
+ preview->queueRefresh();
+}
+
+void SingleExport::setDocument(SPDocument *document)
+{
+ if (!preview) {
+ preview = Gtk::manage(new ExportPreview());
+ si_preview_box->pack_start(*preview, true, true, 0);
+ si_preview_box->show_all_children();
+ }
+ preview->setDocument(document);
+}
+
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export-single.h b/src/ui/dialog/export-single.h
new file mode 100644
index 0000000000000000000000000000000000000000..b19966e60bcc1b2699d3a56a0db6f7f9940fa3eb
--- /dev/null
+++ b/src/ui/dialog/export-single.h
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Authors:
+ * Lauris Kaplinski
+ * bulia byak
+ * Johan Engelen
+ * Anshudhar Kumar Singh
+ *
+ * Copyright (C) 1999-2007, 2021 Authors
+ * Copyright (C) 2001-2002 Ximian, Inc.
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef SP_EXPORT_SINGLE_H
+#define SP_EXPORT_SINGLE_H
+
+#include
+
+#include "export-helper.h"
+#include "export-preview.h"
+#include "extension/output.h"
+#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
+
+namespace Inkscape {
+namespace UI {
+namespace Dialog {
+
+class SingleExport : public Gtk::Box
+{
+public:
+ SingleExport(){};
+ SingleExport(BaseObjectType *cobject, const Glib::RefPtr &refGlade)
+ : Gtk::Box(cobject){};
+ ~SingleExport() override;
+
+private:
+ InkscapeApplication *_app = nullptr;
+ SPDesktop *_desktop = nullptr;
+
+private:
+ bool setupDone = false; // To prevent setup() call add connections again.
+
+public:
+ void setApp(InkscapeApplication *app) { _app = app; }
+ void setDocument(SPDocument *document);
+ void setDesktop(SPDesktop *desktop) { _desktop = desktop; }
+ void selectionChanged(Inkscape::Selection *selection);
+ void selectionModified(Inkscape::Selection *selection, guint flags);
+
+private:
+ enum sb_type
+ {
+ SPIN_X0 = 0,
+ SPIN_X1,
+ SPIN_Y0,
+ SPIN_Y1,
+ SPIN_WIDTH,
+ SPIN_HEIGHT,
+ SPIN_BMWIDTH,
+ SPIN_BMHEIGHT,
+ SPIN_DPI
+ };
+
+ enum selection_mode
+ {
+ SELECTION_PAGE = 0, // Default is alaways placed first
+ SELECTION_SELECTION,
+ SELECTION_DRAWING,
+ SELECTION_CUSTOM,
+ };
+
+private:
+ typedef Inkscape::UI::Widget::ScrollProtected SpinButton;
+
+ std::map spin_buttons;
+ std::map spin_labels;
+ std::map selection_buttons;
+
+ Gtk::Box *si_units_row = nullptr;
+ Gtk::CheckButton *show_export_area = nullptr;
+ Inkscape::UI::Widget::UnitMenu *units = nullptr;
+
+ Gtk::CheckButton *si_hide_all = nullptr;
+
+ Gtk::Box *si_preview_box = nullptr;
+ Gtk::CheckButton *si_show_preview = nullptr;
+ ExportPreview *preview = nullptr;
+
+ ExtensionList *si_extension_cb = nullptr;
+ Gtk::Entry *si_filename_entry = nullptr;
+ Gtk::Button *si_export = nullptr;
+ Gtk::Box *adv_box = nullptr;
+ Gtk::ProgressBar *_prog = nullptr;
+
+ AdvanceOptions advance_options;
+
+private:
+ bool filename_modified;
+ Glib::ustring original_name;
+ Glib::ustring doc_export_name;
+
+ Inkscape::Preferences *prefs = nullptr;
+ std::map selection_names;
+ selection_mode current_key = (selection_mode)0;
+
+public:
+ // initialise variables from builder
+ void initialise(const Glib::RefPtr &builder);
+ void setup();
+
+private:
+ void setupUnits();
+ void setupExtensionList();
+ void setupSpinButtons();
+ void toggleSpinButtonVisibility();
+
+private:
+ void refreshPreview();
+
+private:
+ // change range and callbacks to spinbuttons
+ template
+ 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);
+
+private:
+ void setDefaultSelectionMode();
+ void setDefaultFilename();
+
+private:
+ void onAreaXChange(sb_type type);
+ void onAreaYChange(sb_type type);
+ void onDpiChange(sb_type type);
+ void onAreaTypeToggle(selection_mode key);
+ void onUnitChanged();
+ void onFilenameModified();
+ void onExtensionChanged();
+ void onExport();
+ void onBrowse(Gtk::EntryIconPosition pos, const GdkEventButton *ev);
+ void on_inkscape_selection_modified(Inkscape::Selection *selection, guint flags);
+ void on_inkscape_selection_changed(Inkscape::Selection *selection);
+
+public:
+ void refresh()
+ {
+ refreshArea();
+ refreshExportHints();
+ };
+
+private:
+ void refreshArea();
+ void refreshExportHints();
+ void areaXChange(sb_type type);
+ void areaYChange(sb_type type);
+ void dpiChange(sb_type type);
+ void setArea(double x0, double y0, double x1, double y1);
+ void blockSpinConns(bool status);
+
+private:
+ void setExporting(bool exporting, Glib::ustring const &text = "");
+ ExportProgressDialog *create_progress_dialog(Glib::ustring progress_text);
+ /**
+ * Callback to be used in for loop to update the progress bar.
+ *
+ * @param value number between 0 and 1 indicating the fraction of progress (0.17 = 17 % progress)
+ * @param dlg void pointer to the Gtk::Dialog progress dialog
+ */
+ static unsigned int onProgressCallback(float value, void *dlg);
+
+ /**
+ * Callback for pressing the cancel button.
+ */
+ void onProgressCancel();
+
+ /**
+ * Callback invoked on closing the progress dialog.
+ */
+ bool onProgressDelete(GdkEventAny *event);
+
+private:
+ ExportProgressDialog *prog_dlg = nullptr;
+ bool interrupted;
+
+private:
+ // Signals
+ std::vector spinButtonConns;
+ sigc::connection filenameConn;
+ sigc::connection extensionConn;
+ sigc::connection exportConn;
+ sigc::connection browseConn;
+};
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export.cpp b/src/ui/dialog/export.cpp
index 2fefcdd94b711f148c6e2d96e5996638fa39fa07..68e3ae17a50e7370d4b7c0b69d6307296664ea20 100644
--- a/src/ui/dialog/export.cpp
+++ b/src/ui/dialog/export.cpp
@@ -7,1850 +7,183 @@
* Jon A. Cruz
* Abhishek Sharma
* Kris De Gussem
+ * Anshudhar Kumar Singh
*
- * Copyright (C) 1999-2007, 2012 Authors
+ * Copyright (C) 1999-2007, 2012, 2021 Authors
* Copyright (C) 2001-2002 Ximian, Inc.
*
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
*/
// This has to be included prior to anything that includes setjmp.h, it croaks otherwise
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include "export.h"
#include
-#include
#include
+#include
+#include
+#include
+#include "desktop.h"
#include "document-undo.h"
#include "document.h"
+#include "extension/db.h"
#include "file.h"
-#include "inkscape.h"
-#include "inkscape-window.h"
-#include "preferences.h"
-#include "selection-chemistry.h"
-
-// required to set status message after export
-#include "desktop.h"
-#include "message-stack.h"
-
#include "helper/png-write.h"
-
+#include "inkscape-window.h"
+#include "inkscape.h"
#include "io/resource.h"
#include "io/sys.h"
-
+#include "message-stack.h"
+#include "object/object-set.h"
#include "object/sp-namedview.h"
#include "object/sp-root.h"
-
+#include "preferences.h"
+#include "selection-chemistry.h"
#include "ui/dialog-events.h"
-#include "ui/interface.h"
-#include "ui/widget/unit-menu.h"
-#include "ui/widget/scrollprotected.h"
#include "ui/dialog/dialog-notebook.h"
#include "ui/dialog/filedialog.h"
-
-#include "extension/db.h"
+#include "ui/interface.h"
+#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
#ifdef _WIN32
-#include
-#include
-#include
-#include
-#endif
-
-#define SP_EXPORT_MIN_SIZE 1.0
-
-#define DPI_BASE Inkscape::Util::Quantity::convert(1, "in", "px")
-
-#define EXPORT_COORD_PRECISION 3
-#include "export.h"
+#endif
using Inkscape::Util::unit_table;
-
-namespace {
-
-class MessageCleaner
-{
-public:
- MessageCleaner(Inkscape::MessageId messageId, SPDesktop *desktop) :
- _desktop(desktop),
- _messageId(messageId)
- {
- }
-
- ~MessageCleaner()
- {
- if (_messageId && _desktop) {
- _desktop->messageStack()->cancel(_messageId);
- }
- }
-
-private:
- MessageCleaner(MessageCleaner const &other) = delete;
- MessageCleaner &operator=(MessageCleaner const &other) = delete;
-
- SPDesktop *_desktop;
- Inkscape::MessageId _messageId;
-};
-
-} // namespace
-
namespace Inkscape {
namespace UI {
namespace Dialog {
-class ExportProgressDialog : public Gtk::Dialog {
- private:
- Gtk::ProgressBar *_progress = nullptr;
- Export *_export_panel = nullptr;
- int _current = 0;
- int _total = 0;
-
- public:
- ExportProgressDialog(const Glib::ustring &title, bool modal = false)
- : Gtk::Dialog(title, modal)
- {}
-
- inline void set_export_panel(const decltype(_export_panel) export_panel) { _export_panel = export_panel; }
- inline decltype(_export_panel) get_export_panel() const { return _export_panel; }
-
- inline void set_progress(const decltype(_progress) progress) { _progress = progress; }
- inline decltype(_progress) get_progress() const { return _progress; }
-
- inline void set_current(const int current) { _current = current; }
- inline int get_current() const { return _current; }
-
- inline void set_total(const int total) { _total = total; }
- inline int get_total() const { return _total; }
-};
-
-/** A list of strings that is used both in the preferences, and in the
- data fields to describe the various values of \c selection_type. */
-static const char * selection_names[SELECTION_NUMBER_OF] = {
- "page", "drawing", "selection", "custom"
-};
-
-/** The names on the buttons for the various selection types. */
-static const char * selection_labels[SELECTION_NUMBER_OF] = {
- N_("_Page"), N_("_Drawing"), N_("_Selection"), N_("_Custom")
-};
-
Export::Export()
: DialogBase("/dialogs/export/", "Export")
- , current_key(SELECTION_PAGE)
- , manual_key(SELECTION_PAGE)
- , original_name()
- , doc_export_name()
- , filename_modified(false)
- , update_flag(false)
- , togglebox(Gtk::ORIENTATION_HORIZONTAL, 0)
- , area_box(Gtk::ORIENTATION_VERTICAL, 3)
- , singleexport_box(Gtk::ORIENTATION_VERTICAL, 0)
- , size_box(Gtk::ORIENTATION_VERTICAL, 3)
- , file_box(Gtk::ORIENTATION_VERTICAL, 3)
- , unitbox(Gtk::ORIENTATION_HORIZONTAL, 0)
- , unit_selector()
- , units_label(_("Units:"))
- , filename_box(Gtk::ORIENTATION_HORIZONTAL, 5)
- , browse_label(_("_Export As..."), true)
- , browse_image()
- , batch_box(Gtk::ORIENTATION_HORIZONTAL, 5)
- , batch_export(_("B_atch export all selected objects"))
- , interlacing(_("Use interlacing"))
- , bitdepth_label(_("Bit depth"))
- , bitdepth_cb()
- , zlib_label(_("Compression"))
- , zlib_compression()
- , pHYs_label(_("pHYs dpi"))
- , pHYs_sb(pHYs_adj, 1.0, 2)
- , antialiasing_label(_("Antialiasing"))
- , antialiasing_cb()
- , hide_box(Gtk::ORIENTATION_HORIZONTAL, 3)
- , hide_export(_("Hide all except selected"))
- , closeWhenDone(_("Close when complete"))
- , button_box(Gtk::ORIENTATION_HORIZONTAL, 3)
- , _prog()
- , prog_dlg(nullptr)
- , interrupted(false)
- , prefs(nullptr)
{
- batch_export.set_use_underline();
- batch_export.set_tooltip_text(_("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)"));
- hide_export.set_use_underline();
- hide_export.set_tooltip_text(_("In the exported image, hide all objects except those that are selected"));
- interlacing.set_use_underline();
- interlacing.set_tooltip_text(_("Enables ADAM7 interlacing for PNG output. This results in slightly larger image files, but big images can already be displayed (slightly blurry) while still loading."));
- closeWhenDone.set_use_underline();
- closeWhenDone.set_tooltip_text(_("Once the export completes, close this dialog"));
- prefs = Inkscape::Preferences::get();
-
- singleexport_box.set_border_width(0);
-
- /* Export area frame */
- {
- Gtk::Label* lbl = new Gtk::Label(_("Export area"), Gtk::ALIGN_START);
- lbl->set_use_markup(true);
- area_box.pack_start(*lbl);
+ std::string gladefile = get_filename_string(Inkscape::IO::Resource::UIS, "dialog-export.glade");
- /* Units box */
- /* gets added to the vbox later, but the unit selector is needed
- earlier than that */
- unit_selector.setUnitType(Inkscape::Util::UNIT_TYPE_LINEAR);
-
- unitChangedConn = unit_selector.signal_changed().connect(sigc::mem_fun(*this, &Export::onUnitChanged));
- unitbox.pack_end(unit_selector, false, false, 0);
- unitbox.pack_end(units_label, false, false, 3);
-
- for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
- selectiontype_buttons[i] = new Gtk::RadioButton(_(selection_labels[i]), true);
- if (i > 0) {
- Gtk::RadioButton::Group group = selectiontype_buttons[0]->get_group();
- selectiontype_buttons[i]->set_group(group);
- }
- selectiontype_buttons[i]->set_mode(false);
- togglebox.pack_start(*selectiontype_buttons[i], false, true, 0);
- selectiontype_buttons[i]->signal_clicked().connect(sigc::mem_fun(*this, &Export::onAreaTypeToggled));
- }
-
- auto t = new Gtk::Grid();
- t->set_row_spacing(4);
- t->set_column_spacing(4);
-
- x0_adj = createSpinbutton("x0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, t, 0, 0, _("_x0:"), "",
- EXPORT_COORD_PRECISION, 1, &Export::onAreaX0Change);
-
- x1_adj = createSpinbutton("x1", 0, -1000000.0, 1000000.0, 0.1, 1.0, t, 0, 1,
- _("x_1:"), "", EXPORT_COORD_PRECISION, 1, &Export::onAreaX1Change);
-
- width_adj = createSpinbutton("width", 0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0, t, 0, 2,
- _("Wid_th:"), "", EXPORT_COORD_PRECISION, 1, &Export::onAreaWidthChange);
-
- y0_adj = createSpinbutton("y0", 0.0, -1000000.0, 1000000.0, 0.1, 1.0, t, 2, 0, _("_y0:"), "",
- EXPORT_COORD_PRECISION, 1, &Export::onAreaY0Change);
-
- y1_adj = createSpinbutton("y1", 0, -1000000.0, 1000000.0, 0.1, 1.0, t, 2, 1,
- _("y_1:"), "", EXPORT_COORD_PRECISION, 1, &Export::onAreaY1Change);
-
- height_adj = createSpinbutton("height", 0, 0.0, PNG_UINT_31_MAX, 0.1, 1.0, t, 2, 2,
- _("Hei_ght:"), "", EXPORT_COORD_PRECISION, 1, &Export::onAreaHeightChange);
-
- area_box.pack_start(togglebox, false, false, 3);
- area_box.pack_start(*t, false, false, 0);
- area_box.pack_start(unitbox, false, false, 0);
-
- area_box.set_border_width(3);
- singleexport_box.pack_start(area_box, false, false, 0);
-
- } // end of area box
-
- /* Bitmap size frame */
- {
- size_box.set_border_width(3);
- bm_label = new Gtk::Label(_("Image size"), Gtk::ALIGN_START);
- bm_label->set_use_markup(true);
- size_box.pack_start(*bm_label, false, false, 0);
-
- auto t = new Gtk::Grid();
- t->set_row_spacing(4);
- t->set_column_spacing(4);
-
- size_box.pack_start(*t);
-
- bmwidth_adj = createSpinbutton("bmwidth", 0, 1.0, 1000000.0, 1.0, 10.0, t, 0, 0,
- _("_Width:"), _("pixels at"), 0, 1, &Export::onBitmapWidthChange);
-
- xdpi_adj = createSpinbutton("xdpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE), 0.01,
- 100000.0, 0.1, 1.0, t, 3, 0, "", _("dp_i"), 2, 1, &Export::onExportXdpiChange);
-
- bmheight_adj = createSpinbutton("bmheight", 0, 1.0, 1000000.0, 1.0, 10.0, t, 0, 1,
- _("_Height:"), _("pixels at"), 0, 1, &Export::onBitmapHeightChange);
-
- /** TODO
- * There's no way to set ydpi currently, so we use the defaultxdpi value here, too...
- */
- ydpi_adj = createSpinbutton ( "ydpi", prefs->getDouble("/dialogs/export/defaultxdpi/value", DPI_BASE),
- 0.01, 100000.0, 0.1, 1.0, t, 3, 1,
- "", _("dpi"), 2, 0, nullptr );
-
- singleexport_box.pack_start(size_box, Gtk::PACK_SHRINK);
- }
-
- /* File entry */
- {
- file_box.set_border_width(3);
- flabel = new Gtk::Label(_("_Filename"), Gtk::ALIGN_START, Gtk::ALIGN_CENTER, true);
- flabel->set_use_markup(true);
- file_box.pack_start(*flabel, false, false, 0);
-
- filename_box.pack_start (filename_entry, true, true, 0);
-
- Gtk::Box* browser_im_label = new Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, 3);
- browse_image.set_from_icon_name("folder", Gtk::ICON_SIZE_BUTTON);
- browser_im_label->pack_start(browse_image);
- browser_im_label->pack_start(browse_label);
- browse_button.add(*browser_im_label);
- filename_box.pack_end (browse_button, false, false);
- filename_box.pack_end(export_button, false, false);
-
- file_box.add(filename_box);
-
- // mnemonic in frame label moves focus to filename:
- flabel->set_mnemonic_widget(filename_entry);
+ try {
+ builder = Gtk::Builder::create_from_file(gladefile);
+ } catch (const Glib::Error &ex) {
+ g_error("Glade file loading failed for export screen");
+ return;
}
- batch_export.set_sensitive(true);
- batch_box.pack_start(batch_export, false, false, 3);
-
- hide_export.set_sensitive(true);
- hide_export.set_active (prefs->getBool("/dialogs/export/hideexceptselected/value", false));
- hide_box.pack_start(hide_export, false, false, 3);
-
-
- /* Export Button row */
- export_button.set_label(_("_Export"));
- export_button.set_use_underline();
- export_button.set_tooltip_text (_("Export the bitmap file with these settings"));
-
- button_box.set_border_width(3);
- button_box.pack_start(closeWhenDone, true, true, 0);
-
- /*Advanced*/
- Gtk::Label *label_advanced = Gtk::manage(new Gtk::Label(_("Advanced"),true));
- expander.set_label_widget(*label_advanced);
- expander.set_vexpand(false);
- const char* const modes_list[]={"Gray_1", "Gray_2","Gray_4","Gray_8","Gray_16","RGB_8","RGB_16","GrayAlpha_8","GrayAlpha_16","RGBA_8","RGBA_16"};
- for(auto i : modes_list)
- bitdepth_cb.append(i);
- bitdepth_cb.set_active_text("RGBA_8");
- bitdepth_cb.set_hexpand();
- const char* const zlist[]={"Z_NO_COMPRESSION","Z_BEST_SPEED","2","3","4","5","Z_DEFAULT_COMPRESSION","7","8","Z_BEST_COMPRESSION"};
- for(auto i : zlist)
- zlib_compression.append(i);
- zlib_compression.set_active_text("Z_DEFAULT_COMPRESSION");
- pHYs_adj = Gtk::Adjustment::create(0, 0, 100000, 0.1, 1.0, 0);
- pHYs_sb.set_adjustment(pHYs_adj);
- pHYs_sb.set_width_chars(7);
- pHYs_sb.set_tooltip_text( _("Will force-set the physical dpi for the png file. Set this to 72 if you're planning to work on your png with Photoshop") );
- zlib_compression.set_hexpand();
- const char* const antialising_list[] = {"CAIRO_ANTIALIAS_NONE","CAIRO_ANTIALIAS_FAST","CAIRO_ANTIALIAS_GOOD (default)","CAIRO_ANTIALIAS_BEST"};
- for(auto i : antialising_list)
- antialiasing_cb.append(i);
- antialiasing_cb.set_active_text(antialising_list[2]);
- bitdepth_label.set_halign(Gtk::ALIGN_START);
- zlib_label.set_halign(Gtk::ALIGN_START);
- pHYs_label.set_halign(Gtk::ALIGN_START);
- antialiasing_label.set_halign(Gtk::ALIGN_START);
- auto table = new Gtk::Grid();
- expander.add(*table);
- table->set_border_width(4);
- table->attach(interlacing,0,0,1,1);
- table->attach(bitdepth_label,0,1,1,1);
- table->attach(bitdepth_cb,1,1,1,1);
- table->attach(zlib_label,0,2,1,1);
- table->attach(zlib_compression,1,2,1,1);
- table->attach(pHYs_label,0,3,1,1);
- table->attach(pHYs_sb,1,3,1,1);
- table->attach(antialiasing_label,0,4,1,1);
- table->attach(antialiasing_cb,1,4,1,1);
- table->show();
-
- /* Main dialog */
- set_spacing(0);
- pack_start(singleexport_box, Gtk::PACK_SHRINK);
- pack_start(file_box, Gtk::PACK_SHRINK);
- pack_start(batch_box, Gtk::PACK_SHRINK);
- pack_start(hide_box, Gtk::PACK_SHRINK);
- pack_start(button_box, Gtk::PACK_SHRINK);
- pack_start(expander, Gtk::PACK_SHRINK);
- pack_end(_prog, Gtk::PACK_SHRINK);
-
- /* Signal handlers */
- filename_entry.signal_changed().connect( sigc::mem_fun(*this, &Export::onFilenameModified));
- // pressing enter in the filename field is the same as clicking export:
- filename_entry.signal_activate().connect(sigc::mem_fun(*this, &Export::onExport));
- browse_button.signal_clicked().connect(sigc::mem_fun(*this, &Export::onBrowse));
- batch_export.signal_clicked().connect(sigc::mem_fun(*this, &Export::onBatchClicked));
- export_button.signal_clicked().connect(sigc::mem_fun(*this, &Export::onExport));
- hide_export.signal_clicked().connect(sigc::mem_fun(*this, &Export::onHideExceptSelected));
+ prefs = Inkscape::Preferences::get();
+ builder->get_widget("Export Dialog Box", container);
+ add(*container);
show_all_children();
- setExporting(false);
-}
-
-Export::~Export ()
-{
-}
-void Export::documentReplaced()
-{
- if (auto document = getDocument()) {
- unit_selector.setUnit(document->getNamedView()->display_units->abbr);
+ builder->get_widget("Export Notebook", export_notebook);
- set_default_filename(document->getDocumentFilename());
+ // Initialise Single Export and its objects
+ builder->get_widget_derived("Single Image", single_image);
+ single_image->initialise(builder);
- // focus is in the filename initially:
- original_name = filename_entry.get_text();
- filename_entry.grab_focus();
- filename_modified = false;
+ // Initialise Batch Export and its objects
+ builder->get_widget_derived("Batch Export", batch_export);
+ batch_export->initialise(builder);
- refreshArea();
- findDefaultSelection();
+ if (single_image) {
+ single_image->setDesktop(getDesktop());
+ single_image->setApp(getApp());
}
-}
-
-/*
- * set the default filename to be that of the current path + document
- * with .png extension
- *
- * One thing to notice here is that this filename may get
- * overwritten, but it won't happen here. The filename gets
- * written into the text field, but then the button to select
- * the area gets set. In that code the filename can be changed
- * if there are some with presidence in the document. So, while
- * this code sets the name first, it may not be the one users
- * really see.
- */
-void Export::set_default_filename (const gchar *doc_filename)
-{
- if (doc_filename) {
- auto &&text_extension = get_file_save_extension(Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS);
- Inkscape::Extension::Output * oextension = nullptr;
-
- if (!text_extension.empty()) {
- oextension = dynamic_cast(Inkscape::Extension::db.get(text_extension.c_str()));
- }
-
- if (oextension != nullptr) {
- gchar * old_extension = oextension->get_extension();
- if (g_str_has_suffix(doc_filename, old_extension)) {
- gchar *filename_copy = g_strdup(doc_filename);
- gchar *extension_point = g_strrstr(filename_copy, old_extension);
- extension_point[0] = '\0';
-
- gchar *final_name = g_strconcat(filename_copy, ".png", nullptr);
- filename_entry.set_text(final_name);
- filename_entry.set_position(strlen(final_name));
-
- g_free(final_name);
- g_free(filename_copy);
- }
- } else {
- gchar *name = g_strconcat(doc_filename, ".png", nullptr);
- filename_entry.set_text(name);
- filename_entry.set_position(strlen(name));
-
- g_free(name);
- }
-
- doc_export_name = filename_entry.get_text();
- } else {
- Glib::ustring filename = create_filepath_from_id (_("bitmap"), filename_entry.get_text());
- filename_entry.set_text(filename);
- filename_entry.set_position(filename.length());
- doc_export_name = filename_entry.get_text();
+ if (batch_export) {
+ batch_export->setDesktop(getDesktop());
+ batch_export->setApp(getApp());
}
-}
-Glib::RefPtr Export::createSpinbutton( gchar const * /*key*/,
- double val, double min, double max, double step, double page,
- Gtk::Grid *t, int x, int y,
- const Glib::ustring& ll, const Glib::ustring& lr,
- int digits, unsigned int sensitive,
- void (Export::*cb)() )
-{
- auto adj = Gtk::Adjustment::create(val, min, max, step, page, 0);
-
- int pos = 0;
- Gtk::Label *l = nullptr;
-
- if (!ll.empty()) {
- l = new Gtk::Label(ll,true);
- l->set_halign(Gtk::ALIGN_END);
- l->set_valign(Gtk::ALIGN_CENTER);
- l->set_hexpand();
- t->attach(*l, x + pos, y, 1, 1);
- l->set_sensitive(sensitive);
- pos++;
- }
-
- auto sb = new Inkscape::UI::Widget::ScrollProtected(adj, 1.0, digits);
- sb->set_hexpand();
- t->attach(*sb, x + pos, y, 1, 1);
-
- sb->set_width_chars(7);
- sb->set_sensitive (sensitive);
- pos++;
-
- if (l) {
- l->set_mnemonic_widget(*sb);
- }
-
- if (!lr.empty()) {
- l = new Gtk::Label(lr,true);
- l->set_halign(Gtk::ALIGN_START);
- l->set_valign(Gtk::ALIGN_CENTER);
- l->set_hexpand();
- t->attach(*l, x + pos, y, 1, 1);
- l->set_sensitive (sensitive);
- pos++;
- l->set_mnemonic_widget (*sb);
- }
-
- if (cb) {
- adj->signal_value_changed().connect( sigc::mem_fun(*this, cb) );
- }
-
- return adj;
-} // end of createSpinbutton()
-
-
-std::string Export::create_filepath_from_id(Glib::ustring id, const Glib::ustring &file_entry_text)
-{
- if (id.empty())
- { /* This should never happen */
- id = "bitmap";
- }
-
- std::string directory;
-
- if (!file_entry_text.empty()) {
- directory = Glib::path_get_dirname(Glib::filename_from_utf8(file_entry_text));
- }
-
- if (directory.empty()) {
- /* Grab document directory */
- const gchar* docFilename = getDocument()->getDocumentFilename();
- if (docFilename) {
- directory = Glib::path_get_dirname(docFilename);
- }
- }
-
- if (directory.empty()) {
- directory = Inkscape::IO::Resource::homedir_path(nullptr);
- }
-
- return Glib::build_filename(directory, Glib::filename_from_utf8(id) + ".png");
+ // Callback when container is finally mapped on window. All intialisation like set active is done inside it.
+ container->signal_realize().connect(sigc::mem_fun(*this, &Export::onRealize));
+ export_notebook->signal_switch_page().connect(sigc::mem_fun(*this, &Export::onPageSwitch));
}
-void Export::onBatchClicked ()
-{
- if (batch_export.get_active()) {
- singleexport_box.set_sensitive(false);
- } else {
- singleexport_box.set_sensitive(true);
- }
-}
+Export::~Export() {}
-void Export::updateCheckbuttons ()
+// When conainer is visible then setup all widgets.
+// It prevents gtk_is_widget assertion warning probably.
+void Export::onRealize()
{
- gint num = (gint) boost::distance(getSelection()->items());
- if (num >= 2) {
- batch_export.set_sensitive(true);
- } else {
- batch_export.set_active (false);
- batch_export.set_sensitive(false);
- }
- gchar *l = g_strdup_printf(ngettext("B_atch export %d selected object","B_atch export %d selected objects",num), num);
- batch_export.set_label(l);
- g_free(l);
-
- //hide_export.set_sensitive (num > 0);
-}
-
-inline void Export::findDefaultSelection()
-{
- selection_type key = SELECTION_NUMBER_OF;
-
- if (getSelection()->isEmpty() == false) {
- key = SELECTION_SELECTION;
- }
-
- /* Try using the preferences */
- if (key == SELECTION_NUMBER_OF) {
-
- int i = SELECTION_NUMBER_OF;
-
- Glib::ustring what = prefs->getString("/dialogs/export/exportarea/value");
-
- if (!what.empty()) {
- for (i = 0; i < SELECTION_NUMBER_OF; i++) {
- if (what == selection_names[i]) {
- break;
- }
- }
- }
-
- key = (selection_type)i;
+ if (single_image) {
+ single_image->setDesktop(getDesktop());
+ single_image->setApp(getApp());
+ single_image->setup();
}
-
- if (key == SELECTION_NUMBER_OF) {
- key = SELECTION_PAGE;
+ if (batch_export) {
+ batch_export->setDesktop(getDesktop());
+ batch_export->setApp(getApp());
+ batch_export->setup();
}
-
- current_key = key;
- selectiontype_buttons[current_key]->set_active(true);
- updateCheckbuttons ();
+ setDefaultNotebookPage();
}
-
-/**
- * If selection changed and "Export area" is set to "Selection"
- * recalculate bounds when the selection changes
- */
-void Export::selectionChanged(Inkscape::Selection *selection)
+// Set current page based on preference/last visited page
+void Export::setDefaultNotebookPage()
{
- if (manual_key != SELECTION_CUSTOM && selection) {
- current_key = SELECTION_SELECTION;
- refreshArea();
- }
- updateCheckbuttons();
+ pages[BATCH_EXPORT] = export_notebook->page_num(*batch_export);
+ pages[SINGLE_IMAGE] = export_notebook->page_num(*single_image);
+ export_notebook->set_current_page(pages[SINGLE_IMAGE]);
}
-void Export::selectionModified (Inkscape::Selection *Sel, guint flags)
+void Export::documentReplaced()
{
- switch (current_key) {
- case SELECTION_DRAWING:
- if (auto doc = getDocument()) {
- Geom::OptRect bbox = doc->getRoot()->desktopVisualBounds();
- if (bbox) {
- setArea ( bbox->left(),
- bbox->top(),
- bbox->right(),
- bbox->bottom());
- }
- }
- break;
- case SELECTION_SELECTION:
- if (Sel->isEmpty() == false) {
- Geom::OptRect bbox = Sel->visualBounds();
- if (bbox)
- {
- setArea ( bbox->left(),
- bbox->top(),
- bbox->right(),
- bbox->bottom());
- }
- }
- break;
- default:
- /* Do nothing for page or for custom */
- break;
+ if (single_image) {
+ single_image->setDocument(getDocument());
}
-
- return;
-}
-
-/// Called when one of the selection buttons was toggled.
-void Export::onAreaTypeToggled() {
- if (update_flag) {
- return;
+ if (batch_export) {
+ batch_export->setDocument(getDocument());
}
-
- /* Find which button is active */
- selection_type key = current_key;
- for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
- if (selectiontype_buttons[i]->get_active()) {
- key = (selection_type)i;
- }
- }
- manual_key = current_key = key;
-
- refreshArea();
}
-/// Called when area needs to be refreshed
-/// Area type changed, unit changed, initialization
-void Export::refreshArea ()
-{
- if (auto doc = getDocument()) {
- Geom::OptRect bbox;
- bbox = Geom::Rect(Geom::Point(0.0, 0.0),Geom::Point(0.0, 0.0));
-
- /* Notice how the switch is used to 'fall through' here to get
- various backups. If you modify this without noticing you'll
- probably screw something up. */
- switch (current_key) {
- case SELECTION_SELECTION:
- if (!getSelection()->isEmpty())
- {
- bbox = getSelection()->visualBounds();
- /* Only if there is a selection that we can set
- do we break, otherwise we fall through to the
- drawing */
- // std::cout << "Using selection: SELECTION" << std::endl;
- current_key = SELECTION_SELECTION;
- break;
- }
- case SELECTION_DRAWING:
- if (manual_key == SELECTION_DRAWING || manual_key == SELECTION_SELECTION) {
- /** \todo
- * This returns wrong values if the document has a viewBox.
- */
- bbox = doc->getRoot()->desktopVisualBounds();
- /* If the drawing is valid, then we'll use it and break
- otherwise we drop through to the page settings */
- if (bbox) {
- // std::cout << "Using selection: DRAWING" << std::endl;
- current_key= SELECTION_DRAWING;
- break;
- }
- }
- case SELECTION_PAGE:
- if (manual_key == SELECTION_PAGE){
- bbox = Geom::Rect(Geom::Point(0.0, 0.0),
- Geom::Point(doc->getWidth().value("px"), doc->getHeight().value("px")));
-
- // std::cout << "Using selection: PAGE" << std::endl;
- current_key= SELECTION_PAGE;
- break;
- }
- case SELECTION_CUSTOM:
- current_key = SELECTION_CUSTOM;
- default:
- break;
- } // switch
-
- // remember area setting
- prefs->setString("/dialogs/export/exportarea/value", selection_names[current_key]);
-
- if ( current_key != SELECTION_CUSTOM && bbox ) {
- setArea ( bbox->min()[Geom::X],
- bbox->min()[Geom::Y],
- bbox->max()[Geom::X],
- bbox->max()[Geom::Y]);
- }
-
- }
-
- if (getDesktop() && !filename_modified) {
-
- Glib::ustring filename;
- float xdpi = 0.0, ydpi = 0.0;
-
- switch (current_key) {
- case SELECTION_PAGE:
- case SELECTION_DRAWING: {
- SPDocument * doc = getDocument();
- sp_document_get_export_hints (doc, filename, &xdpi, &ydpi);
-
- if (filename.empty()) {
- if (!doc_export_name.empty()) {
- filename = doc_export_name;
- }
- }
- break;
- }
- case SELECTION_SELECTION:
- if (!getSelection()->isEmpty()) {
- getSelection()->getExportHints(filename, &xdpi, &ydpi);
-
- /* If we still don't have a filename -- let's build
- one that's nice */
- if (filename.empty()) {
- const gchar * id = "object";
- auto reprlst = getSelection()->xmlNodes();
- for(auto i=reprlst.begin(); reprlst.end() != i; ++i) {
- Inkscape::XML::Node * repr = *i;
- if (repr->attribute("id")) {
- id = repr->attribute("id");
- break;
- }
- }
-
- filename = create_filepath_from_id (id, filename_entry.get_text());
- }
- }
- break;
- case SELECTION_CUSTOM:
- default:
- break;
- }
-
- if (!filename.empty()) {
- original_name = filename;
- filename_entry.set_text(filename);
- filename_entry.set_position(filename.length());
- }
-
- if (xdpi != 0.0) {
- setValue(xdpi_adj, xdpi);
- }
-
- /* These can't be separate, and setting x sets y, so for
- now setting this is disabled. Hopefully it won't be in
- the future */
- if (FALSE && ydpi != 0.0) {
- setValue(ydpi_adj, ydpi);
- }
- }
-
- return;
-} // end of sp_export_area_toggled()
-
-/// Called when dialog is deleted
-bool Export::onProgressDelete (GdkEventAny * /*event*/)
+void Export::desktopReplaced()
{
- interrupted = true;
- return TRUE;
-} // end of sp_export_progress_delete()
-
-
-/// Called when progress is cancelled
-void Export::onProgressCancel ()
-{
- interrupted = true;
-} // end of sp_export_progress_cancel()
-
-
-/// Called for every progress iteration
-unsigned int Export::onProgressCallback(float value, void *dlg)
-{
- auto dlg2 = reinterpret_cast(dlg);
-
- auto self = dlg2->get_export_panel();
- if (self->interrupted)
- return FALSE;
-
- auto current = dlg2->get_current();
- auto total = dlg2->get_total();
- if (total > 0) {
- double completed = current;
- completed /= static_cast(total);
-
- value = completed + (value / static_cast(total));
+ if (single_image) {
+ single_image->setDesktop(getDesktop());
}
-
- auto prg = dlg2->get_progress();
- prg->set_fraction(value);
-
- if (self) {
- self->_prog.set_fraction(value);
- }
-
- int evtcount = 0;
- while ((evtcount < 16) && gdk_events_pending()) {
- Gtk::Main::iteration(false);
- evtcount += 1;
- }
-
- Gtk::Main::iteration(false);
- return TRUE;
-} // end of sp_export_progress_callback()
-
-void Export::setExporting(bool exporting, Glib::ustring const &text)
-{
- if (exporting) {
- _prog.set_text(text);
- _prog.set_fraction(0.0);
- _prog.set_sensitive(true);
-
- export_button.set_sensitive(false);
- } else {
- _prog.set_text("");
- _prog.set_fraction(0.0);
- _prog.set_sensitive(false);
-
- export_button.set_sensitive(true);
+ if (batch_export) {
+ batch_export->setDesktop(getDesktop());
}
}
-ExportProgressDialog *
-Export::create_progress_dialog(Glib::ustring progress_text)
-{
- auto dlg = new ExportProgressDialog(_("Export in progress"), true);
- dlg->set_transient_for( *(getDesktop()->getToplevel()) );
-
- Gtk::ProgressBar *prg = new Gtk::ProgressBar ();
- prg->set_text(progress_text);
- dlg->set_progress(prg);
- auto CA = dlg->get_content_area();
- CA->pack_start(*prg, FALSE, FALSE, 4);
-
- Gtk::Button* btn = dlg->add_button (_("_Cancel"),Gtk::RESPONSE_CANCEL );
-
- btn->signal_clicked().connect( sigc::mem_fun(*this, &Export::onProgressCancel) );
- dlg->signal_delete_event().connect( sigc::mem_fun(*this, &Export::onProgressDelete) );
-
- dlg->show_all ();
- return dlg;
-}
-
-static std::string absolutize_path_from_document_location(SPDocument *doc, const std::string &filename)
+void Export::selectionChanged(Inkscape::Selection *selection)
{
- std::string path;
- //Make relative paths go from the document location, if possible:
- if (!Glib::path_is_absolute(filename) && doc->getDocumentFilename()) {
- auto dirname = Glib::path_get_dirname(doc->getDocumentFilename());
- if (!dirname.empty()) {
- path = Glib::build_filename(dirname, filename);
- }
+ auto current_page = export_notebook->get_current_page();
+ if (current_page == pages[SINGLE_IMAGE]) {
+ single_image->selectionChanged(selection);
}
- if (path.empty()) {
- path = filename;
+ if (current_page == pages[BATCH_EXPORT]) {
+ batch_export->selectionChanged(selection);
}
- return path;
-}
-
-// Called when unit is changed
-void Export::onUnitChanged()
-{
- refreshArea();
}
-
-void Export::onHideExceptSelected ()
+void Export::selectionModified(Inkscape::Selection *selection, guint flags)
{
- prefs->setBool("/dialogs/export/hideexceptselected/value", hide_export.get_active());
-}
-
-/// Called when export button is clicked
-void Export::onExport ()
-{
- _export_raster(nullptr);
-}
-
-void Export::_export_raster(Inkscape::Extension::Output *extension)
-{
- auto desktop = getDesktop();
- if (!desktop) return;
-
- SPNamedView *nv = desktop->getNamedView();
- SPDocument *doc = desktop->getDocument();
-
- bool exportSuccessful = false;
-
- bool hide = hide_export.get_active ();
-
- // Advanced parameters
- bool do_interlace = (interlacing.get_active());
- float pHYs = 0;
- int zlib = zlib_compression.get_active_row_number() ;
- int colortypes[] = {0,0,0,0,0,2,2,4,4,6,6}; //keep in sync with modes_list in Export constructor. values are from libpng doc.
- int bitdepths[] = {1,2,4,8,16,8,16,8,16,8,16};
- int color_type = colortypes[bitdepth_cb.get_active_row_number()] ;
- int bit_depth = bitdepths[bitdepth_cb.get_active_row_number()] ;
- int antialiasing = antialiasing_cb.get_active_row_number();
-
-
- if (batch_export.get_active ()) {
- // Batch export of selected objects
-
- gint num = (gint) boost::distance(desktop->getSelection()->items());
- gint n = 0;
-
- if (num < 1) {
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("No items selected."));
- return;
- }
-
- prog_dlg = create_progress_dialog(Glib::ustring::compose(_("Exporting %1 files"), num));
- prog_dlg->set_export_panel(this);
- setExporting(true, Glib::ustring::compose(_("Exporting %1 files"), num));
-
- gint export_count = 0;
-
- auto itemlist= desktop->getSelection()->items();
- for(auto i = itemlist.begin();i!=itemlist.end() && !interrupted ;++i){
- SPItem *item = *i;
-
- prog_dlg->set_current(n);
- prog_dlg->set_total(num);
- onProgressCallback(0.0, prog_dlg);
-
- // retrieve export filename hint
- const gchar *filename = item->getRepr()->attribute("inkscape:export-filename");
- std::string path;
- if (!filename) {
- auto id = item->getId();
- if (!id) {
- g_warning("object has no id");
- continue;
- }
- path = create_filepath_from_id(id, filename_entry.get_text());
- } else {
- path = absolutize_path_from_document_location(doc, filename);
- }
-
- // retrieve export dpi hints
- const gchar *dpi_hint = item->getRepr()->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
- gdouble dpi = 0.0;
- if (dpi_hint) {
- dpi = g_ascii_strtod(dpi_hint, nullptr);
- }
- if (dpi == 0.0) {
- dpi = getValue(xdpi_adj);
- }
- pHYs = (pHYs_adj->get_value() > 0.01) ? pHYs_adj->get_value() : dpi;
-
- Geom::OptRect area = item->documentVisualBounds();
- if (area) {
- gint width = (gint) (area->width() * dpi / DPI_BASE + 0.5);
- gint height = (gint) (area->height() * dpi / DPI_BASE + 0.5);
-
- if (width > 1 && height > 1) {
- // Do export
- gchar * safeFile = Inkscape::IO::sanitizeString(path.c_str());
- MessageCleaner msgCleanup(desktop->messageStack()->pushF(Inkscape::IMMEDIATE_MESSAGE,
- _("Exporting file %s..."), safeFile), desktop);
- MessageCleaner msgFlashCleanup(desktop->messageStack()->flashF(Inkscape::IMMEDIATE_MESSAGE,
- _("Exporting file %s..."), safeFile), desktop);
- std::vector x;
- std::vector selected(desktop->getSelection()->items().begin(), desktop->getSelection()->items().end());
- if (!sp_export_png_file (doc, path.c_str(),
- *area, width, height, pHYs, pHYs,
- nv->pagecolor,
- onProgressCallback, (void*)prog_dlg,
- TRUE, // overwrite without asking
- hide ? selected : x,
- do_interlace, color_type, bit_depth, zlib, antialiasing
- )) {
- gchar * error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
-
- desktop->messageStack()->flashF(Inkscape::ERROR_MESSAGE,
- _("Could not export to filename %s."), safeFile);
-
- sp_ui_error_dialog(error);
- g_free(error);
- } else {
- ++export_count; // one more item exported successfully
- }
- g_free(safeFile);
- }
- }
-
- n++;
- }
-
- desktop->messageStack()->flashF(Inkscape::INFORMATION_MESSAGE,
- _("Successfully exported %d files from %d selected items."), export_count, num);
-
- setExporting(false);
- delete prog_dlg;
- prog_dlg = nullptr;
- interrupted = false;
- exportSuccessful = (export_count > 0);
- } else {
- Glib::ustring filename = filename_entry.get_text();
-
- if (filename.empty()) {
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("You have to enter a filename."));
- sp_ui_error_dialog(_("You have to enter a filename"));
- return;
- }
-
- float const x0 = getValuePx(x0_adj);
- float const y0 = getValuePx(y0_adj);
- float const x1 = getValuePx(x1_adj);
- float const y1 = getValuePx(y1_adj);
- float const xdpi = getValue(xdpi_adj);
- float const ydpi = getValue(ydpi_adj);
- pHYs = (pHYs_adj->get_value() > 0.01) ? pHYs_adj->get_value() : xdpi;
- unsigned long int const width = int(getValue(bmwidth_adj) + 0.5);
- unsigned long int const height = int(getValue(bmheight_adj) + 0.5);
-
- if (!((x1 > x0) && (y1 > y0) && (width > 0) && (height > 0))) {
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("The chosen area to be exported is invalid."));
- sp_ui_error_dialog(_("The chosen area to be exported is invalid"));
- return;
- }
-
- std::string path = absolutize_path_from_document_location(doc, Glib::filename_from_utf8(filename));
-
- Glib::ustring dirname = Glib::path_get_dirname(path);
- if ( dirname.empty()
- || !Inkscape::IO::file_test(dirname.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
- {
- gchar *safeDir = Inkscape::IO::sanitizeString(dirname.c_str());
- gchar *error = g_strdup_printf(_("Directory %s does not exist or is not a directory.\n"),
- safeDir);
-
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error);
- sp_ui_error_dialog(error);
-
- g_free(safeDir);
- g_free(error);
- return;
- }
-
- auto fn = Glib::path_get_basename(path);
- auto area = Geom::Rect(Geom::Point(x0, y0), Geom::Point(x1, y1)) * desktop->dt2doc();
-
- // Select a raster output extension if not a png file (manual filename)
- if (!extension && !Glib::str_has_suffix(filename, ".png")) {
- Inkscape::Extension::DB::OutputList extension_list;
- Inkscape::Extension::db.get_output_list(extension_list);
- for (auto output_ext : extension_list) {
- if (output_ext->deactivated() || !output_ext->is_raster())
- continue;
- if(Glib::str_has_suffix(path.c_str(), output_ext->get_extension())) {
- // Select the extension
- extension = output_ext;
- break;
- }
- }
- }
-
- bool overwrite = false;
- auto png_filename = std::string(path.c_str());
- if (extension) {
- // Select the extension and set the filename to a temporary file
- int tempfd_out = Glib::file_open_tmp(png_filename, "ink_ext_");
- // Do the over-write protection now, since the png is just a temp file.
- if (!sp_ui_overwrite_file(filename.c_str())) {
- return;
- }
- overwrite = true;
- close(tempfd_out);
- }
-
- /* TRANSLATORS: %1 will be the filename, %2 the width, and %3 the height of the image */
- prog_dlg = create_progress_dialog (Glib::ustring::compose(_("Exporting %1 (%2 x %3)"), fn, width, height));
- prog_dlg->set_export_panel(this);
- setExporting(true, Glib::ustring::compose(_("Exporting %1 (%2 x %3)"), fn, width, height));
-
- prog_dlg->set_current(0);
- prog_dlg->set_total(0);
-
- /* Do export */
- std::vector x;
- std::vector selected(desktop->getSelection()->items().begin(), desktop->getSelection()->items().end());
- ExportResult status = sp_export_png_file(desktop->getDocument(), png_filename.c_str(),
- area, width, height, pHYs, pHYs, //previously xdpi, ydpi.
- nv->pagecolor,
- onProgressCallback, (void*)prog_dlg,
- overwrite,
- hide ? selected : x,
- do_interlace, color_type, bit_depth, zlib, antialiasing
- );
- if (status == EXPORT_ERROR) {
- gchar * safeFile = Inkscape::IO::sanitizeString(path.c_str());
- gchar * error = g_strdup_printf(_("Could not export to filename %s.\n"), safeFile);
-
- desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, error);
- sp_ui_error_dialog(error);
-
- g_free(safeFile);
- g_free(error);
- } else if (status == EXPORT_OK) {
-
- exportSuccessful = true;
- if(extension != nullptr) {
- // Remove progress dialog before showing prefs dialog.
- delete prog_dlg;
- prog_dlg = nullptr;
- if(extension->prefs()) {
- try {
- extension->export_raster(doc, png_filename, path.c_str(), false);
- } catch (Inkscape::Extension::Output::save_failed &e) {
- exportSuccessful = false;
- }
- } else {
- exportSuccessful = false;
- }
- }
-
- if (exportSuccessful) {
- auto recentmanager = Gtk::RecentManager::get_default();
- if(recentmanager && Glib::path_is_absolute(path)) {
- Glib::ustring uri = Glib::filename_to_uri(path);
- recentmanager->add_item(uri);
- }
-
- gchar *safeFile = Inkscape::IO::sanitizeString(path.c_str());
- desktop->messageStack()->flashF(Inkscape::INFORMATION_MESSAGE, _("Drawing exported to %s."), safeFile);
- g_free(safeFile);
- }
- } else {
- // Extensions have their own error popup, so this only tracks failures in the png step
- desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE, _("Export aborted."));
- }
- if (extension != nullptr) {
- unlink(png_filename.c_str());
- }
-
- /* Reset the filename so that it can be changed again by changing
- selections and all that */
- original_name = filename;
- filename_modified = false;
-
- setExporting(false);
- if(prog_dlg) {
- delete prog_dlg;
- prog_dlg = nullptr;
- }
- interrupted = false;
-
- /* Setup the values in the document */
- switch (current_key) {
- case SELECTION_PAGE:
- case SELECTION_DRAWING: {
- SPDocument * doc = getDocument();
- Inkscape::XML::Node * repr = doc->getReprRoot();
- bool modified = false;
-
- bool saved = DocumentUndo::getUndoSensitive(doc);
- DocumentUndo::setUndoSensitive(doc, false);
-
- gchar const *temp_string = repr->attribute("inkscape:export-filename");
- if (temp_string == nullptr || (filename != temp_string)) {
- repr->setAttribute("inkscape:export-filename", filename);
- modified = true;
- }
- temp_string = repr->attribute("inkscape:export-xdpi");
- if (temp_string == nullptr || xdpi != g_ascii_strtod(temp_string, nullptr)) {
- repr->setAttributeSvgDouble("inkscape:export-xdpi", xdpi);
- modified = true;
- }
- temp_string = repr->attribute("inkscape:export-ydpi");
- if (temp_string == nullptr || ydpi != g_ascii_strtod(temp_string, nullptr)) {
- repr->setAttributeSvgDouble("inkscape:export-ydpi", ydpi);
- modified = true;
- }
- DocumentUndo::setUndoSensitive(doc, saved);
-
- if (modified) {
- doc->setModifiedSinceSave();
- }
- break;
- }
- case SELECTION_SELECTION: {
- SPDocument * doc = getDocument();
- bool modified = false;
-
- bool saved = DocumentUndo::getUndoSensitive(doc);
- DocumentUndo::setUndoSensitive(doc, false);
- auto reprlst = desktop->getSelection()->xmlNodes();
-
- for(auto i=reprlst.begin(); reprlst.end() != i; ++i) {
- Inkscape::XML::Node * repr = *i;
- const gchar * temp_string;
- Glib::ustring dir = Glib::path_get_dirname(filename.c_str());
- const gchar* docFilename = doc->getDocumentFilename();
- Glib::ustring docdir;
- if (docFilename)
- {
- docdir = Glib::path_get_dirname(docFilename);
- }
- temp_string = repr->attribute("inkscape:export-filename");
- if (temp_string == nullptr || (filename != temp_string)) {
- repr->setAttribute("inkscape:export-filename", filename);
- modified = true;
- }
-
- temp_string = repr->attribute("inkscape:export-xdpi");
- if (temp_string == nullptr || xdpi != g_ascii_strtod(temp_string, nullptr)) {
- repr->setAttributeSvgDouble("inkscape:export-xdpi", xdpi);
- modified = true;
- }
- temp_string = repr->attribute("inkscape:export-ydpi");
- if (temp_string == nullptr || ydpi != g_ascii_strtod(temp_string, nullptr)) {
- repr->setAttributeSvgDouble("inkscape:export-ydpi", ydpi);
- modified = true;
- }
- }
- DocumentUndo::setUndoSensitive(doc, saved);
-
- if (modified) {
- doc->setModifiedSinceSave();
- }
- break;
- }
- default:
- break;
- }
+ auto current_page = export_notebook->get_current_page();
+ if (current_page == pages[SINGLE_IMAGE]) {
+ single_image->selectionModified(selection, flags);
}
-
- if (exportSuccessful && closeWhenDone.get_active()) {
- for (Gtk::Container *parent = get_parent(); parent; parent = parent->get_parent()) {
- DialogNotebook *notebook = dynamic_cast(parent);
- if (notebook) {
- notebook->close_tab_callback();
- break;
- }
- }
- }
-} // end of Export::onExport()
-
-/// Called when Browse button is clicked
-void Export::onBrowse ()
-{
- // Create and show the dialog
- Gtk::Window *window = getApp()->get_active_window();
- std::string filename = Glib::filename_from_utf8(filename_entry.get_text());
-
- if (filename.empty()) {
- Glib::ustring tmp;
- filename = create_filepath_from_id(tmp, tmp);
- }
-
- Inkscape::UI::Dialog::FileSaveDialog *dialog = Inkscape::UI::Dialog::FileSaveDialog::create(
- *window, filename, Inkscape::UI::Dialog::RASTER_TYPES, _("Select a filename for exporting"), "", "",
- Inkscape::Extension::FILE_SAVE_METHOD_EXPORT);
-
- if (dialog->show()) {
- auto file = dialog->getFilename();
- filename_entry.set_text(Glib::filename_to_utf8(file));
- filename_entry.set_position(-1);
- auto selection_type = dialog->getSelectionType();
- //deleting dialog before exporting is important
- //proper delete function should be made for dialog IMO
- delete dialog;
- _export_raster(dynamic_cast(selection_type));
- }else {
- delete dialog;
+ if (current_page == pages[BATCH_EXPORT]) {
+ batch_export->selectionModified(selection, flags);
}
-
}
-// TODO: Move this to nr-rect-fns.h.
-bool Export::bbox_equal(Geom::Rect const &one, Geom::Rect const &two)
+void Export::onPageSwitch(Widget *page, guint page_number)
{
- double const epsilon = pow(10.0, -EXPORT_COORD_PRECISION);
- return (
- (fabs(one.min()[Geom::X] - two.min()[Geom::X]) < epsilon) &&
- (fabs(one.min()[Geom::Y] - two.min()[Geom::Y]) < epsilon) &&
- (fabs(one.max()[Geom::X] - two.max()[Geom::X]) < epsilon) &&
- (fabs(one.max()[Geom::Y] - two.max()[Geom::Y]) < epsilon)
- );
-}
-
-/**
- *This function is used to detect the current selection setting
- * based on the values in the x0, y0, x1 and y0 fields.
- *
- * One of the most confusing parts of this function is why the array
- * is built at the beginning. What needs to happen here is that we
- * should always check the current selection to see if it is the valid
- * one. While this is a performance improvement it is also a usability
- * one during the cases where things like selections and drawings match
- * size. This way buttons change less 'randomly' (at least in the eyes
- * of the user). To do this an array is built where the current selection
- * type is placed first, and then the others in an order from smallest
- * to largest (this can be configured by reshuffling \c test_order).
- *
- * All of the values in this function are rounded to two decimal places
- * because that is what is shown to the user. While everything is kept
- * more accurate than that, the user can't control more accurate than
- * that, so for this to work for them - it needs to check on that level
- * of accuracy.
- *
- * @todo finish writing this up.
- */
-void Export::detectSize() {
- static const selection_type test_order[SELECTION_NUMBER_OF] = {SELECTION_SELECTION, SELECTION_DRAWING, SELECTION_PAGE, SELECTION_CUSTOM};
- selection_type this_test[SELECTION_NUMBER_OF + 1];
- selection_type key = SELECTION_NUMBER_OF;
-
- Geom::Point x(getValuePx(x0_adj),
- getValuePx(y0_adj));
- Geom::Point y(getValuePx(x1_adj),
- getValuePx(y1_adj));
- Geom::Rect current_bbox(x, y);
-
- this_test[0] = current_key;
- for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
- this_test[i + 1] = test_order[i];
- }
-
auto desktop = getDesktop();
- for (int i = 0;
- i < SELECTION_NUMBER_OF + 1 &&
- key == SELECTION_NUMBER_OF &&
- desktop != nullptr;
- i++) {
- switch (this_test[i]) {
- case SELECTION_SELECTION:
- if (getSelection()->isEmpty() == false) {
- Geom::OptRect bbox = getSelection()->bounds(SPItem::VISUAL_BBOX);
-
- if ( bbox && bbox_equal(*bbox,current_bbox)) {
- key = SELECTION_SELECTION;
- }
- }
- break;
- case SELECTION_DRAWING: {
- Geom::OptRect bbox = getDocument()->getRoot()->desktopVisualBounds();
-
- if ( bbox && bbox_equal(*bbox,current_bbox) ) {
- key = SELECTION_DRAWING;
- }
- break;
- }
-
- case SELECTION_PAGE: {
- auto doc = getDocument();
-
- Geom::Point x(0.0, 0.0);
- Geom::Point y(doc->getWidth().value("px"),
- doc->getHeight().value("px"));
- Geom::Rect bbox(x, y);
-
- if (bbox_equal(bbox,current_bbox)) {
- key = SELECTION_PAGE;
- }
-
- break;
- }
- default:
- break;
- }
- }
- // std::cout << std::endl;
-
- if (key == SELECTION_NUMBER_OF) {
- key = SELECTION_CUSTOM;
- }
-
- current_key = key;
- selectiontype_buttons[current_key]->set_active(true);
-
- return;
-} /* sp_export_detect_size */
-
-/// Called when area x0 value is changed
-void Export::areaXChange(Glib::RefPtr& adj)
-{
- float x0, x1, xdpi, width;
-
- if (update_flag) {
- return;
- }
-
- update_flag = true;
+ if (desktop) {
+ auto selection = desktop->getSelection();
- x0 = getValuePx(x0_adj);
- x1 = getValuePx(x1_adj);
- xdpi = getValue(xdpi_adj);
-
- width = floor ((x1 - x0) * xdpi / DPI_BASE + 0.5);
-
- if (width < SP_EXPORT_MIN_SIZE) {
- width = SP_EXPORT_MIN_SIZE;
-
- if (adj == x1_adj) {
- x1 = x0 + width * DPI_BASE / xdpi;
- setValuePx(x1_adj, x1);
- } else {
- x0 = x1 - width * DPI_BASE / xdpi;
- setValuePx(x0_adj, x0);
+ if (page_number == pages[SINGLE_IMAGE]) {
+ single_image->selectionChanged(selection);
}
- }
-
- setValuePx(width_adj, x1 - x0);
- setValue(bmwidth_adj, width);
-
- detectSize();
-
- update_flag = false;
-
- return;
-} // end of sp_export_area_x_value_changed()
-
-/// Called when area y0 value is changed.
-void Export::areaYChange(Glib::RefPtr& adj)
-{
- float y0, y1, ydpi, height;
-
- if (update_flag) {
- return;
- }
-
- update_flag = true;
-
- y0 = getValuePx(y0_adj);
- y1 = getValuePx(y1_adj);
- ydpi = getValue(ydpi_adj);
-
- height = floor ((y1 - y0) * ydpi / DPI_BASE + 0.5);
-
- if (height < SP_EXPORT_MIN_SIZE) {
- height = SP_EXPORT_MIN_SIZE;
- if (adj == y1_adj) {
- //if (!strcmp (key, "y0")) {
- y1 = y0 + height * DPI_BASE / ydpi;
- setValuePx(y1_adj, y1);
- } else {
- y0 = y1 - height * DPI_BASE / ydpi;
- setValuePx(y0_adj, y0);
+ if (page_number == pages[BATCH_EXPORT]) {
+ batch_export->selectionChanged(selection);
}
}
-
- setValuePx(height_adj, y1 - y0);
- setValue(bmheight_adj, height);
-
- detectSize();
-
- update_flag = false;
-
- return;
-} // end of sp_export_area_y_value_changed()
-
-/// Called when x1-x0 or area width is changed
-void Export::onAreaWidthChange()
-{
- if (update_flag) {
- return;
- }
-
- update_flag = true;
-
- float x0 = getValuePx(x0_adj);
- float xdpi = getValue(xdpi_adj);
- float width = getValuePx(width_adj);
- float bmwidth = floor(width * xdpi / DPI_BASE + 0.5);
-
- if (bmwidth < SP_EXPORT_MIN_SIZE) {
-
- bmwidth = SP_EXPORT_MIN_SIZE;
- width = bmwidth * DPI_BASE / xdpi;
- setValuePx(width_adj, width);
- }
-
- setValuePx(x1_adj, x0 + width);
- setValue(bmwidth_adj, bmwidth);
-
- update_flag = false;
-
- return;
-} // end of sp_export_area_width_value_changed()
-
-/// Called when y1-y0 or area height is changed.
-void Export::onAreaHeightChange()
-{
- if (update_flag) {
- return;
- }
-
- update_flag = true;
-
- float y0 = getValuePx(y0_adj);
- //float y1 = sp_export_value_get_px(y1_adj);
- float ydpi = getValue(ydpi_adj);
- float height = getValuePx(height_adj);
- float bmheight = floor (height * ydpi / DPI_BASE + 0.5);
-
- if (bmheight < SP_EXPORT_MIN_SIZE) {
- bmheight = SP_EXPORT_MIN_SIZE;
- height = bmheight * DPI_BASE / ydpi;
- setValuePx(height_adj, height);
- }
-
- setValuePx(y1_adj, y0 + height);
- setValue(bmheight_adj, bmheight);
-
- update_flag = false;
-
- return;
-} // end of sp_export_area_height_value_changed()
-
-/**
- * A function to set the ydpi.
- * @param base The export dialog.
- *
- * This function grabs all of the y values and then figures out the
- * new bitmap size based on the changing dpi value. The dpi value is
- * gotten from the xdpi setting as these can not currently be independent.
- */
-void Export::setImageY()
-{
- float y0, y1, xdpi;
-
- y0 = getValuePx(y0_adj);
- y1 = getValuePx(y1_adj);
- xdpi = getValue(xdpi_adj);
-
- setValue(ydpi_adj, xdpi);
- setValue(bmheight_adj, (y1 - y0) * xdpi / DPI_BASE);
-
- return;
-} // end of setImageY()
-
-/**
- * A function to set the xdpi.
- *
- * This function grabs all of the x values and then figures out the
- * new bitmap size based on the changing dpi value. The dpi value is
- * gotten from the xdpi setting as these can not currently be independent.
- *
- */
-void Export::setImageX()
-{
- float x0, x1, xdpi;
-
- x0 = getValuePx(x0_adj);
- x1 = getValuePx(x1_adj);
- xdpi = getValue(xdpi_adj);
-
- setValue(ydpi_adj, xdpi);
- setValue(bmwidth_adj, (x1 - x0) * xdpi / DPI_BASE);
-
- return;
-} // end of setImageX()
-
-/// Called when pixel width is changed
-void Export::onBitmapWidthChange ()
-{
- float x0, x1, bmwidth, xdpi;
-
- if (update_flag) {
- return;
- }
-
- update_flag = true;
-
- x0 = getValuePx(x0_adj);
- x1 = getValuePx(x1_adj);
- bmwidth = getValue(bmwidth_adj);
-
- if (bmwidth < SP_EXPORT_MIN_SIZE) {
- bmwidth = SP_EXPORT_MIN_SIZE;
- setValue(bmwidth_adj, bmwidth);
- }
-
- xdpi = bmwidth * DPI_BASE / (x1 - x0);
- setValue(xdpi_adj, xdpi);
-
- setImageY ();
-
- update_flag = false;
-
- return;
-} // end of sp_export_bitmap_width_value_changed()
-
-/// Called when pixel height is changed
-void Export::onBitmapHeightChange ()
-{
- float y0, y1, bmheight, xdpi;
-
- if (update_flag) {
- return;
- }
-
- update_flag = true;
-
- y0 = getValuePx(y0_adj);
- y1 = getValuePx(y1_adj);
- bmheight = getValue(bmheight_adj);
-
- if (bmheight < SP_EXPORT_MIN_SIZE) {
- bmheight = SP_EXPORT_MIN_SIZE;
- setValue(bmheight_adj, bmheight);
- }
-
- xdpi = bmheight * DPI_BASE / (y1 - y0);
- setValue(xdpi_adj, xdpi);
-
- setImageX ();
-
- update_flag = false;
-
- return;
-} // end of sp_export_bitmap_width_value_changed()
-
-/**
- * A function to adjust the bitmap width when the xdpi value changes.
- *
- * The first thing this function checks is to see if we are doing an
- * update. If we are, this function just returns because there is another
- * instance of it that will handle everything for us. If there is a
- * units change, we also assume that everyone is being updated appropriately
- * and there is nothing for us to do.
- *
- * If we're the highest level function, we set the update flag, and
- * continue on our way.
- *
- * All of the values are grabbed using the \c sp_export_value_get functions
- * (call to the _pt ones for x0 and x1 but just standard for xdpi). The
- * xdpi value is saved in the preferences for the next time the dialog
- * is opened. (does the selection dpi need to be set here?)
- *
- * A check is done to to ensure that we aren't outputting an invalid width,
- * this is set by SP_EXPORT_MIN_SIZE. If that is the case the dpi is
- * changed to make it valid.
- *
- * After all of this the bitmap width is changed.
- *
- * We also change the ydpi. This is a temporary hack as these can not
- * currently be independent. This is likely to change in the future.
- *
- */
-void Export::onExportXdpiChange()
-{
- float x0, x1, xdpi, bmwidth;
-
- if (update_flag) {
- return;
- }
-
- update_flag = true;
-
- x0 = getValuePx(x0_adj);
- x1 = getValuePx(x1_adj);
- xdpi = getValue(xdpi_adj);
-
- // remember xdpi setting
- prefs->setDouble("/dialogs/export/defaultxdpi/value", xdpi);
-
- bmwidth = (x1 - x0) * xdpi / DPI_BASE;
-
- if (bmwidth < SP_EXPORT_MIN_SIZE) {
- bmwidth = SP_EXPORT_MIN_SIZE;
- if (x1 != x0)
- xdpi = bmwidth * DPI_BASE / (x1 - x0);
- else
- xdpi = DPI_BASE;
- setValue(xdpi_adj, xdpi);
- }
-
- setValue(bmwidth_adj, bmwidth);
-
- setImageY ();
-
- update_flag = false;
-
- return;
-} // end of sp_export_xdpi_value_changed()
-
-
-/**
- * A function to change the area that is used for the exported.
- * bitmap.
- *
- * This function just calls \c sp_export_value_set_px for each of the
- * parameters that is passed in. This allows for setting them all in
- * one convenient area.
- *
- * Update is set to suspend all of the other test running while all the
- * values are being set up. This allows for a performance increase, but
- * it also means that the wrong type won't be detected with only some of
- * the values set. After all the values are set everyone is told that
- * there has been an update.
- *
- * @param x0 Horizontal upper left hand corner of the picture in points.
- * @param y0 Vertical upper left hand corner of the picture in points.
- * @param x1 Horizontal lower right hand corner of the picture in points.
- * @param y1 Vertical lower right hand corner of the picture in points.
- */
-void Export::setArea( double x0, double y0, double x1, double y1 )
-{
- update_flag = true;
- setValuePx(x1_adj, x1);
- setValuePx(y1_adj, y1);
- setValuePx(x0_adj, x0);
- setValuePx(y0_adj, y0);
- update_flag = false;
-
- areaXChange (x1_adj);
- areaYChange (y1_adj);
-
- return;
-}
-
-/**
- * Sets the value of an adjustment.
- *
- * @param adj The adjustment widget
- * @param val What value to set it to.
- */
-void Export::setValue(Glib::RefPtr& adj, double val )
-{
- if (adj) {
- adj->set_value(val);
- }
-}
-
-/**
- * A function to set a value using the units points.
- *
- * This function first gets the adjustment for the key that is passed
- * in. It then figures out what units are currently being used in the
- * dialog. After doing all of that, it then converts the incoming
- *value and sets the adjustment.
- *
- * @param adj The adjustment widget
- * @param val What the value should be in points.
- */
-void Export::setValuePx(Glib::RefPtr& adj, double val)
-{
- Unit const *unit = unit_selector.getUnit();
-
- setValue(adj, Inkscape::Util::Quantity::convert(val, "px", unit));
-
- return;
-}
-
-/**
- * Get the value of an adjustment in the export dialog.
- *
- * This function gets the adjustment from the data field in the export
- * dialog. It then grabs the value from the adjustment.
- *
- * @param adj The adjustment widget
- *
- * @return The value in the specified adjustment.
- */
-float Export::getValue(Glib::RefPtr& adj)
-{
- if (!adj) {
- g_message("sp_export_value_get : adj is NULL");
- return 0.0;
- }
- return adj->get_value();
}
-/**
- * Grabs a value in the export dialog and converts the unit
- * to points.
- *
- * This function, at its most basic, is a call to \c sp_export_value_get
- * to get the value of the adjustment. It then finds the units that
- * are being used by looking at the "units" attribute of the export
- * dialog. Using that it converts the returned value into points.
- *
- * @param adj The adjustment widget
- *
- * @return The value in the adjustment in points.
- */
-float Export::getValuePx(Glib::RefPtr& adj)
-{
- float value = getValue( adj);
- Unit const *unit = unit_selector.getUnit();
-
- return Inkscape::Util::Quantity::convert(value, unit, "px");
-} // end of sp_export_value_get_px()
-
-/**
- * This function is called when the filename is changed by
- * anyone. It resets the virgin bit.
- *
- * This function gets called when the text area is modified. It is
- * looking for the case where the text area is modified from its
- * original value. In that case it sets the "filename-modified" bit
- * to TRUE. If the text dialog returns back to the original text, the
- * bit gets reset. This should stop simple mistakes.
- */
-void Export::onFilenameModified()
-{
- if (original_name == filename_entry.get_text()) {
- filename_modified = false;
- } else {
- filename_modified = true;
- }
-
- return;
-} // end sp_export_filename_modified
-
-}
-}
-}
+} // namespace Dialog
+} // namespace UI
+} // namespace Inkscape
/*
Local Variables:
@@ -1862,3 +195,4 @@ void Export::onFilenameModified()
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
\ No newline at end of file
diff --git a/src/ui/dialog/export.h b/src/ui/dialog/export.h
index cd2f40bdd4a5cd1e63187385c0cbe6aa64c31839..cc8c0e4d2a8db31e8692ec6f3e8d596934cfc994 100644
--- a/src/ui/dialog/export.h
+++ b/src/ui/dialog/export.h
@@ -3,8 +3,9 @@
* Lauris Kaplinski
* bulia byak
* Johan Engelen
+ * Anshudhar Kumar Singh
*
- * Copyright (C) 1999-2007 Authors
+ * Copyright (C) 1999-2007, 2021 Authors
* Copyright (C) 2001-2002 Ximian, Inc.
*
* Released under GNU GPL v2+, read the file 'COPYING' for more information.
@@ -13,340 +14,64 @@
#ifndef SP_EXPORT_H
#define SP_EXPORT_H
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include "export-batch.h"
+#include "export-helper.h"
+#include "export-single.h"
#include "extension/output.h"
#include "ui/dialog/dialog-base.h"
#include "ui/widget/scrollprotected.h"
+#include "ui/widget/unit-menu.h"
namespace Inkscape {
namespace UI {
namespace Dialog {
-class ExportProgressDialog;
-
-/** What type of button is being pressed */
-enum selection_type {
- SELECTION_PAGE = 0, /**< Export the whole page */
- SELECTION_DRAWING, /**< Export everything drawn on the page */
- SELECTION_SELECTION, /**< Export everything that is selected */
- SELECTION_CUSTOM, /**< Allows the user to set the region exported */
- SELECTION_NUMBER_OF /**< A counter for the number of these guys */
+enum notebook_page
+{
+ SINGLE_IMAGE = 0,
+ BATCH_EXPORT
};
-/**
- * A dialog widget to export to various image formats such as bitmap and png.
- *
- * Creates a dialog window for exporting an image to a bitmap if one doesn't already exist and
- * shows it to the user. If the dialog has already been created, it simply shows the window.
- *
- */
class Export : public DialogBase
{
public:
- Export ();
- ~Export () override;
+ Export();
+ ~Export() override;
- static Export &getInstance() {
- return *new Export();
- }
+ static Export &getInstance() { return *new Export(); }
private:
+ Glib::RefPtr builder;
+ Gtk::Box *container = nullptr; // Main Container
+ Gtk::Notebook *export_notebook = nullptr; // Notebook Container for single and batch export
- void documentReplaced() override;
- void selectionChanged(Inkscape::Selection *selection) override;
- void selectionModified(Inkscape::Selection *selection, guint flags) override;
-
- /**
- * A function to set the xdpi.
- *
- * This function grabs all of the x values and then figures out the
- * new bitmap size based on the changing dpi value. The dpi value is
- * gotten from the xdpi setting as these can not currently be independent.
- *
- */
- void setImageX();
-
- /**
- * A function to set the ydpi.
- *
- * This function grabs all of the y values and then figures out the
- * new bitmap size based on the changing dpi value. The dpi value is
- * gotten from the xdpi setting as these can not currently be independent.
- */
- void setImageY();
- bool bbox_equal(Geom::Rect const &one, Geom::Rect const &two);
- void updateCheckbuttons ();
- inline void findDefaultSelection();
- void detectSize();
- void setArea ( double x0, double y0, double x1, double y1);
- /*
- * Getter/setter style functions for the spinbuttons
- */
- void setValue(Glib::RefPtr& adj, double val);
- void setValuePx(Glib::RefPtr& adj, double val);
- float getValue(Glib::RefPtr& adj);
- float getValuePx(Glib::RefPtr& adj);
-
- /**
- * Helper function to create, style and pack spinbuttons for the export dialog.
- *
- * Creates a new spin button for the export dialog.
- * @param key The name of the spin button
- * @param val A default value for the spin button
- * @param min Minimum value for the spin button
- * @param max Maximum value for the spin button
- * @param step The step size for the spin button
- * @param page Size of the page increment
- * @param t Table to put the spin button in
- * @param x X location in the table \c t to start with
- * @param y Y location in the table \c t to start with
- * @param ll Text to put on the left side of the spin button (optional)
- * @param lr Text to put on the right side of the spin button (optional)
- * @param digits Number of digits to display after the decimal
- * @param sensitive Whether the spin button is sensitive or not
- * @param cb Callback for when this spin button is changed (optional)
- *
- * No unit_selector is stored in the created spinbutton, relies on external unit management
- */
- Glib::RefPtr createSpinbutton( gchar const *key,
- double val, double min, double max, double step, double page,
- Gtk::Grid *t, int x, int y,
- const Glib::ustring& ll, const Glib::ustring& lr,
- int digits, unsigned int sensitive,
- void (Export::*cb)() );
-
- std::string create_filepath_from_id(Glib::ustring, const Glib::ustring &);
-
- /**
- * One of the area select radio buttons was pressed
- */
- void onAreaTypeToggled();
- void refreshArea();
-
- /**
- * Export button callback
- */
- void onExport ();
- void _export_raster(Inkscape::Extension::Output *extension);
-
- /**
- * File Browse button callback
- */
- void onBrowse ();
-
- /**
- * Area X value changed callback
- */
- void onAreaX0Change() {
- areaXChange(x0_adj);
- } ;
- void onAreaX1Change() {
- areaXChange(x1_adj);
- } ;
- void areaXChange(Glib::RefPtr& adj);
-
- /**
- * Area Y value changed callback
- */
- void onAreaY0Change() {
- areaYChange(y0_adj);
- } ;
- void onAreaY1Change() {
- areaYChange(y1_adj);
- } ;
- void areaYChange(Glib::RefPtr& adj);
-
- /**
- * Unit changed callback
- */
- void onUnitChanged();
-
- /**
- * Hide except selected callback
- */
- void onHideExceptSelected ();
-
- /**
- * Area width value changed callback
- */
- void onAreaWidthChange ();
-
- /**
- * Area height value changed callback
- */
- void onAreaHeightChange ();
-
- /**
- * Bitmap width value changed callback
- */
- void onBitmapWidthChange ();
-
- /**
- * Bitmap height value changed callback
- */
- void onBitmapHeightChange ();
-
- /**
- * Export xdpi value changed callback
- */
- void onExportXdpiChange ();
-
- /**
- * Batch export callback
- */
- void onBatchClicked ();
-
- /**
- * Filename modified callback
- */
- void onFilenameModified ();
-
- /**
- * Creates progress dialog for batch exporting.
- *
- * @param progress_text Text to be shown in the progress bar
- */
- ExportProgressDialog * create_progress_dialog(Glib::ustring progress_text);
-
- /**
- * Callback to be used in for loop to update the progress bar.
- *
- * @param value number between 0 and 1 indicating the fraction of progress (0.17 = 17 % progress)
- * @param dlg void pointer to the Gtk::Dialog progress dialog
- */
- static unsigned int onProgressCallback(float value, void *dlg);
-
- /**
- * Callback for pressing the cancel button.
- */
- void onProgressCancel ();
-
- /**
- * Callback invoked on closing the progress dialog.
- */
- bool onProgressDelete (GdkEventAny *event);
-
- /**
- * Handles state changes as exporting starts or stops.
- */
- void setExporting(bool exporting, Glib::ustring const &text = "");
-
- /*
- * Utility filename and path functions
- */
- void set_default_filename (const gchar *doc_filename);
-
- /*
- * Currently selected export area type
- * can be changed by code
- */
- selection_type current_key;
- /*
- * Manually selected export area type(only changed by buttons)
- */
- selection_type manual_key;
- /*
- * Original name for the export object
- */
- Glib::ustring original_name;
- Glib::ustring doc_export_name;
- /*
- * Was the Original name modified
- */
- bool filename_modified;
-
- /*
- * Flag to stop simultaneous updates
- */
- bool update_flag;
-
- /* Area selection radio buttons */
- Gtk::Box togglebox;
- Gtk::RadioButton *selectiontype_buttons[SELECTION_NUMBER_OF];
-
- Gtk::Box area_box;
- Gtk::Box singleexport_box;
-
- /* Custom size widgets */
- Glib::RefPtr x0_adj;
- Glib::RefPtr x1_adj;
- Glib::RefPtr y0_adj;
- Glib::RefPtr y1_adj;
- Glib::RefPtr width_adj;
- Glib::RefPtr height_adj;
-
- /* Bitmap size widgets */
- Glib::RefPtr