export.cpp revision b513dfd5ec351c3b4108889df5dfe3445855529e
/* Authors:
* Lauris Kaplinski <lauris@kaplinski.com>
* bulia byak <buliabyak@users.sf.net>
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Peter Bostrom
* Jon A. Cruz <jon@joncruz.org>
* Abhishek Sharma
* Kris De Gussem <Kris.DeGussem@gmail.com>
*
* Copyright (C) 1999-2007, 2012 Authors
* Copyright (C) 2001-2002 Ximian, Inc.
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
// This has to be included prior to anything that includes setjmp.h, it croaks otherwise
#include <png.h>
#endif
#include <gtkmm/buttonbox.h>
#include <gtkmm/spinbutton.h>
#if WITH_GTKMM_3_0
#else
#endif
#include <gtkmm/togglebutton.h>
#ifdef WITH_GNOME_VFS
#endif
#include <glibmm/miscutils.h>
#include "ui/widget/unit-menu.h"
#include "inkscape-private.h"
#include "document.h"
#include "document-undo.h"
#include "desktop-handles.h"
#include "sp-item.h"
#include "selection.h"
#include "file.h"
#include "macros.h"
#include "sp-namedview.h"
#include "selection-chemistry.h"
#include "dialogs/dialog-events.h"
#include "preferences.h"
#include "verbs.h"
#include "interface.h"
#include "sp-root.h"
#include "helper/png-write.h"
#if WITH_EXT_GDL
#include <gdl/gdl-dock-item.h>
#else
#include "libgdl/gdl-dock-item.h"
#endif
// required to set status message after export
#include "desktop.h"
#include "message-stack.h"
#ifdef WIN32
#include <windows.h>
#include <commdlg.h>
#include <gdk/gdkwin32.h>
#include <glibmm/fileutils.h>
#endif
#define SP_EXPORT_MIN_SIZE 1.0
#define EXPORT_COORD_PRECISION 3
#include "../../desktop-handles.h"
#include "../../document.h"
#include "../../document-undo.h"
#include "verbs.h"
#include "export.h"
namespace {
class MessageCleaner
{
public:
{
}
{
if (_messageId && _desktop) {
}
}
private:
};
} // namespace
namespace Inkscape {
namespace UI {
namespace Dialog {
/** 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] = {
};
filename_modified(false),
was_empty(true),
update(false),
togglebox(true, 0),
area_box(false, 3),
singleexport_box(false, 0),
size_box(false, 3),
file_box(false, 3),
unitbox(false, 0),
units_label(_("Units:")),
filename_box(false, 5),
batch_box(false, 5),
batch_export(_("B_atch export all selected objects"), _("Export each selected object into its own PNG file, using export hints if any (caution, overwrites without asking!)")),
hide_box(false, 5),
hide_export(_("Hide a_ll except selected"), _("In the exported image, hide all objects except those that are selected")),
button_box(false, 3),
_prog(),
interrupted(false),
deskTrack(),
{
/* Export area frame */
{
lbl->set_use_markup(true);
/* Units box */
/* gets added to the vbox later, but the unit selector is needed
earlier than that */
if (desktop) {
}
unitChangedConn = unit_selector.signal_changed().connect(sigc::mem_fun(*this, &Export::onUnitChanged));
for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
if (i > 0) {
}
selectiontype_buttons[i]->set_mode(false);
}
#if WITH_GTKMM_3_0
t->set_row_spacing(4);
t->set_column_spacing(4);
#else
t->set_row_spacings (4);
t->set_col_spacings (4);
#endif
&Export::onAreaX0Change);
&Export::onAreaX1Change);
&Export::onAreaY0Change);
&Export::onAreaY1Change);
area_box.pack_start(*t, false, false, 0);
} // end of area box
/* Bitmap size frame */
{
bm_label->set_use_markup(true);
#if WITH_GTKMM_3_0
t->set_row_spacing(4);
t->set_column_spacing(4);
#else
t->set_row_spacings (4);
t->set_col_spacings (4);
#endif
size_box.pack_start(*t);
t, 0, 0,
_("_Width:"), _("pixels at"), 0, 1,
0.01, 100000.0, 0.1, 1.0, t, 3, 0,
"", _("dp_i"), 2, 1,
t, 0, 1,
_("_Height:"), _("pixels at"), 0, 1,
/** 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,
}
/* File entry */
{
flabel->set_use_markup(true);
// focus is in the filename initially:
// mnemonic in frame label moves focus to filename:
}
batch_export.set_sensitive(true);
hide_export.set_sensitive(true);
/* Export Button row */
/* Main dialog */
contents->set_spacing(0);
/* Signal handlers */
// pressing enter in the filename field is the same as clicking export:
desktopChangeConn = deskTrack.connectDesktopChanged( sigc::mem_fun(*this, &Export::setTargetDesktop) );
setExporting(false);
}
{
}
{
}
{
if (this->desktop) {
}
selectChangedConn = desktop->selection->connectChanged(sigc::hide(sigc::mem_fun(*this, &Export::onSelectionChanged)));
subselChangedConn = desktop->connectToolSubselectionChanged(sigc::hide(sigc::mem_fun(*this, &Export::onSelectionChanged)));
//// Must check flags, so can't call widget_setup() directly.
selectModifiedConn = desktop->selection->connectModified(sigc::hide<0>(sigc::mem_fun(*this, &Export::onSelectionModified)));
}
}
}
/*
* 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 *text_extension = get_file_save_extension (Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS).c_str();
if (text_extension != NULL) {
oextension = dynamic_cast<Inkscape::Extension::Output *>(Inkscape::Extension::db.get(text_extension));
}
if (oextension != NULL) {
gchar * final_name;
extension_point[0] = '\0';
}
} else {
}
}
}
#if WITH_GTKMM_3_0
Glib::RefPtr<Gtk::Adjustment> Export::createSpinbutton( gchar const * /*key*/, float val, float min, float max,
#else
#endif
{
#if WITH_GTKMM_3_0
#else
#endif
int pos = 0;
#if WITH_GTKMM_3_0
l->set_hexpand();
l->set_vexpand();
#else
#endif
l->set_sensitive(sensitive);
pos++;
}
#if WITH_GTKMM_3_0
sb->set_hexpand();
sb->set_vexpand();
#else
#endif
pos++;
l->set_mnemonic_widget(*sb);
}
#if WITH_GTKMM_3_0
l->set_hexpand();
l->set_vexpand();
#else
#endif
l->set_sensitive (sensitive);
pos++;
l->set_mnemonic_widget (*sb);
}
if (cb) {
}
return adj;
} // end of createSpinbutton()
Glib::ustring Export::create_filepath_from_id (Glib::ustring id, const Glib::ustring &file_entry_text)
{
{ /* This should never happen */
id = "bitmap";
}
if (!file_entry_text.empty()) {
}
/* Grab document directory */
if (docURI) {
}
}
}
return filename;
}
void Export::onBatchClicked ()
{
if (batch_export.get_active()) {
singleexport_box.set_sensitive(false);
} else {
singleexport_box.set_sensitive(true);
}
}
void Export::updateCheckbuttons ()
{
if (num >= 2) {
batch_export.set_sensitive(true);
batch_export.set_label(g_strdup_printf (ngettext("B_atch export %d selected object","B_atch export %d selected objects",num), num));
} else {
batch_export.set_active (false);
batch_export.set_sensitive(false);
}
//hide_export.set_sensitive (num > 0);
}
inline void Export::findDefaultSelection()
{
}
/* Try using the preferences */
if (key == SELECTION_NUMBER_OF) {
int i = SELECTION_NUMBER_OF;
for (i = 0; i < SELECTION_NUMBER_OF; i++) {
if (what == selection_names[i]) {
break;
}
}
}
key = (selection_type)i;
}
if (key == SELECTION_NUMBER_OF) {
}
current_key = key;
}
/**
* If selection changed or a different document activated, we must
* recalculate any chosen areas.
*/
void Export::onSelectionChanged()
{
was_empty) {
}
if ( selection &&
SELECTION_CUSTOM != current_key) {
}
}
{
switch (current_key) {
case SELECTION_DRAWING:
if ( SP_ACTIVE_DESKTOP ) {
if (bbox) {
}
}
break;
case SELECTION_SELECTION:
if (bbox)
{
}
}
break;
default:
/* Do nothing for page or for custom */
break;
}
return;
}
/// Called when one of the selection buttons was toggled.
void Export::onAreaToggled ()
{
if (update) {
return;
}
/* Find which button is active */
for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
if (selectiontype_buttons[i]->get_active()) {
key = (selection_type)i;
}
}
if ( SP_ACTIVE_DESKTOP )
{
/* Notice how the switch is used to 'fall through' here to get
various backups. If you modify this without noticing you'll
probabaly screw something up. */
switch (key) {
case SELECTION_SELECTION:
{
/* 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;
break;
}
case SELECTION_DRAWING:
/** \todo
* This returns wrong values if the document has a viewBox.
*/
/* 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;
break;
}
case SELECTION_PAGE:
// std::cout << "Using selection: PAGE" << std::endl;
break;
case SELECTION_CUSTOM:
default:
break;
} // switch
current_key = key;
// remember area setting
}
} // end of if ( SP_ACTIVE_DESKTOP )
if (SP_ACTIVE_DESKTOP && !filename_modified) {
switch (key) {
case SELECTION_PAGE:
case SELECTION_DRAWING: {
if (!doc_export_name.empty()) {
}
}
break;
}
case SELECTION_SELECTION:
/* If we still don't have a filename -- let's build
one that's nice */
break;
}
}
}
}
break;
case SELECTION_CUSTOM:
default:
break;
}
}
if (xdpi != 0.0) {
}
/* 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 */
}
}
return;
} // end of sp_export_area_toggled()
/// Called when dialog is deleted
{
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
{
return FALSE;
}
if (total > 0) {
}
if (self) {
}
int evtcount = 0;
evtcount += 1;
}
return TRUE;
} // end of sp_export_progress_callback()
{
if (exporting) {
_prog.set_sensitive(true);
export_button.set_sensitive(false);
} else {
_prog.set_sensitive(false);
export_button.set_sensitive(true);
}
}
#if GTK_CHECK_VERSION(3,0,0)
#else
#endif
return dlg;
}
// FIXME: Some lib function should be available to do this ...
{
if ( !dot )
{
}
else
{
{
return filename;
}
else
{
}
}
}
Glib::ustring Export::absolutize_path_from_document_location (SPDocument *doc, const Glib::ustring &filename)
{
//Make relative paths go from the document location, if possible:
}
}
}
return path;
}
// Called when unit is changed
void Export::onUnitChanged()
{
}
void Export::onHideExceptSelected ()
{
}
/// Called when export button is clicked
{
if (!desktop) return;
bool exportSuccessful = false;
if (batch_export.get_active ()) {
// Batch export of selected objects
gint n = 0;
if (num < 1) {
return;
}
gint export_count = 0;
for (GSList *i = const_cast<GSList *>(sp_desktop_selection(desktop)->itemList()); i && !interrupted; i = i->next) {
// retrieve export filename hint
if (!filename) {
} else {
}
// retrieve export dpi hints
const gchar *dpi_hint = item->getRepr()->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
if (dpi_hint) {
}
if (dpi == 0.0) {
}
if (area) {
// Do export
onProgressCallback, (void*)prog_dlg,
TRUE, // overwrite without asking
)) {
_("Could not export to filename <b>%s</b>."), safeFile);
} else {
++export_count; // one more item exported successfully
}
}
}
n++;
}
setExporting(false);
delete prog_dlg;
interrupted = false;
exportSuccessful = (export_count > 0);
} else {
sp_ui_error_dialog(_("You have to enter a filename"));
return;
}
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;
}
// make sure that .png is the extension of the file:
|| !Inkscape::IO::file_test(dirname.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) )
{
safeDir);
return;
}
/* 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));
/* Do export */
onProgressCallback, (void*)prog_dlg,
);
if (status == EXPORT_ERROR) {
exportSuccessful = true;
desktop->messageStack()->flashF(Inkscape::INFORMATION_MESSAGE, _("Drawing exported to <b>%s</b>."), safeFile);
} else {
}
/* Reset the filename so that it can be changed again by changing
selections and all that */
filename_modified = false;
setExporting(false);
delete prog_dlg;
interrupted = false;
/* Setup the values in the document */
switch (current_key) {
case SELECTION_PAGE:
case SELECTION_DRAWING: {
bool modified = false;
modified = true;
}
modified = true;
}
modified = true;
}
if (modified) {
}
break;
}
case SELECTION_SELECTION: {
bool modified = false;
const gchar * temp_string;
if (docURI)
{
}
( !docURI ||
modified = true;
}
}
modified = true;
}
modified = true;
}
}
if (modified) {
}
break;
}
default:
break;
}
}
if (item) {
}
break;
}
}
}
} // end of sp_export_export_clicked()
/// Called when Browse button is clicked
/// @todo refactor this code to use ui/dialogs/filedialog.cpp
{
_("_Cancel"), GTK_RESPONSE_CANCEL,
_("_Save"), GTK_RESPONSE_ACCEPT,
NULL );
#ifdef WITH_GNOME_VFS
if (gnome_vfs_initialized()) {
}
#endif
}
#ifdef WIN32
// code in this section is borrowed from ui/dialogs/filedialogimpl-win32.cpp
WCHAR* title_string = (WCHAR*)g_utf8_to_utf16(_("Select a filename for exporting"), -1, NULL, NULL, NULL);
// Copy the selected file name, converting from UTF-8 to UTF-16
{
}
#if WITH_GTKMM_3_0
#else
#endif
opf.lpstrCustomFilter = 0;
opf.nMaxCustFilter = 0L;
opf.nMaxFileTitle=0;
opf.lpstrInitialDir = 0;
opf.nFileOffset = 0;
if (GetSaveFileNameW(&opf) != 0)
{
// Copy the selected file name, converting from UTF-16 to UTF-8
}
#else
{
}
#endif
return;
} // end of sp_export_browse_clicked()
// TODO: Move this to nr-rect-fns.h.
{
return (
);
}
/**
*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' (atleast 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 acurrate 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};
getValuePx(y0_adj));
getValuePx(y1_adj));
this_test[0] = current_key;
for (int i = 0; i < SELECTION_NUMBER_OF; i++) {
}
for (int i = 0;
i < SELECTION_NUMBER_OF + 1 &&
key == SELECTION_NUMBER_OF &&
i++) {
switch (this_test[i]) {
case SELECTION_SELECTION:
}
}
break;
case SELECTION_DRAWING: {
}
break;
}
case SELECTION_PAGE: {
}
break;
}
default:
break;
}
}
// std::cout << std::endl;
if (key == SELECTION_NUMBER_OF) {
}
current_key = key;
return;
} /* sp_export_detect_size */
/// Called when area x0 value is changed
#if WITH_GTKMM_3_0
#else
#endif
{
if (update) {
return;
}
update = true;
if (width < SP_EXPORT_MIN_SIZE) {
} else {
}
}
detectSize();
update = false;
return;
} // end of sp_export_area_x_value_changed()
/// Called when area y0 value is changed.
#if WITH_GTKMM_3_0
#else
#endif
{
if (update) {
return;
}
update = true;
if (height < SP_EXPORT_MIN_SIZE) {
//const gchar *key;
//key = (const gchar *)g_object_get_data(G_OBJECT (adj), "key");
//if (!strcmp (key, "y0")) {
} else {
}
}
detectSize();
update = false;
return;
} // end of sp_export_area_y_value_changed()
/// Called when x1-x0 or area width is changed
void Export::onAreaWidthChange()
{
if (update) {
return;
}
update = true;
if (bmwidth < SP_EXPORT_MIN_SIZE) {
}
update = false;
return;
} // end of sp_export_area_width_value_changed()
/// Called when y1-y0 or area height is changed.
void Export::onAreaHeightChange()
{
if (update) {
return;
}
update = true;
//float y1 = sp_export_value_get_px(y1_adj);
if (bmheight < SP_EXPORT_MIN_SIZE) {
}
update = 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.
*/
{
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.
*
*/
{
return;
} // end of setImageX()
/// Called when pixel width is changed
void Export::onBitmapWidthChange ()
{
if (update) {
return;
}
update = true;
if (bmwidth < SP_EXPORT_MIN_SIZE) {
}
setImageY ();
update = false;
return;
} // end of sp_export_bitmap_width_value_changed()
/// Called when pixel height is changed
void Export::onBitmapHeightChange ()
{
if (update) {
return;
}
update = true;
if (bmheight < SP_EXPORT_MIN_SIZE) {
}
setImageX ();
update = 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 outputing 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()
{
if (update) {
return;
}
update = true;
// remember xdpi setting
if (bmwidth < SP_EXPORT_MIN_SIZE) {
else
}
setImageY ();
update = 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 convient 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.
*/
{
update = true;
update = false;
return;
}
/**
* Sets the value of an adjustment.
*
* @param adj The adjustment widget
* @param val What value to set it to.
*/
#if WITH_GTKMM_3_0
#else
#endif
{
if (adj) {
}
}
/**
* 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.
*/
#if WITH_GTKMM_3_0
#else
#endif
{
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.
*/
#if WITH_GTKMM_3_0
#else
#endif
{
if (!adj) {
g_message("sp_export_value_get : adj is NULL");
return 0.0;
}
}
/**
* 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.
*/
#if WITH_GTKMM_3_0
#else
#endif
{
} // 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()
{
filename_modified = false;
} else {
filename_modified = true;
}
return;
} // end sp_export_filename_modified
}
}
}
/*
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 :