1 #define __INKSCAPE_C__
3 /*
4 * Interface to main application
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * Copyright (C) 1999-2005 authors
11 * g++ port Copyright (C) 2003 Nathan Hurst
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
21 #include <set>
22 #include "debug/simple-event.h"
23 #include "debug/event-tracker.h"
25 #ifndef WIN32
26 # define HAS_PROC_SELF_EXE //to get path of executable
27 #else
29 // For now to get at is_os_wide().
30 # include "extension/internal/win32.h"
31 using Inkscape::Extension::Internal::PrintWin32;
33 #define _WIN32_IE 0x0400
34 //#define HAS_SHGetSpecialFolderPath
35 #define HAS_SHGetSpecialFolderLocation
36 #define HAS_GetModuleFileName
37 # include <shlobj.h>
38 #endif
40 #include <signal.h>
42 #include <gtk/gtkmain.h>
43 #include <gtk/gtkmessagedialog.h>
45 #include <glibmm/i18n.h>
46 #include "helper/sp-marshal.h"
47 #include "dialogs/debugdialog.h"
48 #include "application/application.h"
49 #include "application/editor.h"
50 #include "preferences.h"
53 #include "document.h"
54 #include "desktop.h"
55 #include "desktop-handles.h"
56 #include "selection.h"
57 #include "event-context.h"
58 #include "inkscape-private.h"
59 #include "prefs-utils.h"
60 #include "xml/repr.h"
61 #include "io/sys.h"
62 #include "perspective3d.h"
64 #include "extension/init.h"
66 static Inkscape::Application *inkscape = NULL;
68 /* Backbones of configuration xml data */
69 #include "menus-skeleton.h"
71 enum {
72 MODIFY_SELECTION, // global: one of selections modified
73 CHANGE_SELECTION, // global: one of selections changed
74 CHANGE_SUBSELECTION, // global: one of subselections (text selection, gradient handle, etc) changed
75 SET_SELECTION, // global: one of selections set
76 SET_EVENTCONTEXT, // tool switched
77 ACTIVATE_DESKTOP, // some desktop got focus
78 DEACTIVATE_DESKTOP, // some desktop lost focus
79 SHUTDOWN_SIGNAL, // inkscape is quitting
80 DIALOGS_HIDE, // user pressed F12
81 DIALOGS_UNHIDE, // user pressed F12
82 EXTERNAL_CHANGE, // a document was changed by some external means (undo or XML editor); this
83 // may not be reflected by a selection change and thus needs a separate signal
84 LAST_SIGNAL
85 };
87 #define DESKTOP_IS_ACTIVE(d) ((d) == inkscape->desktops->data)
90 /*################################
91 # FORWARD DECLARATIONS
92 ################################*/
94 gboolean inkscape_app_use_gui( Inkscape::Application const * app );
96 static void inkscape_class_init (Inkscape::ApplicationClass *klass);
97 static void inkscape_init (SPObject *object);
98 static void inkscape_dispose (GObject *object);
100 static void inkscape_activate_desktop_private (Inkscape::Application *inkscape, SPDesktop *desktop);
101 static void inkscape_deactivate_desktop_private (Inkscape::Application *inkscape, SPDesktop *desktop);
103 static bool inkscape_init_config (Inkscape::XML::Document *doc, const gchar *config_name, const gchar *skeleton,
104 unsigned int skel_size,
105 const gchar *e_mkdir,
106 const gchar *e_notdir,
107 const gchar *e_ccf,
108 const gchar *e_cwf,
109 const gchar *warn);
111 struct Inkscape::Application {
112 GObject object;
113 Inkscape::XML::Document *menus;
114 std::multiset<SPDocument *> document_set;
115 GSList *documents;
116 GSList *desktops;
117 gchar *argv0;
118 gboolean dialogs_toggle;
119 gboolean use_gui; // may want to consider a virtual function
120 // for overriding things like the warning dlg's
121 };
123 struct Inkscape::ApplicationClass {
124 GObjectClass object_class;
126 /* Signals */
127 void (* change_selection) (Inkscape::Application * inkscape, Inkscape::Selection * selection);
128 void (* change_subselection) (Inkscape::Application * inkscape, SPDesktop *desktop);
129 void (* modify_selection) (Inkscape::Application * inkscape, Inkscape::Selection * selection, guint flags);
130 void (* set_selection) (Inkscape::Application * inkscape, Inkscape::Selection * selection);
131 void (* set_eventcontext) (Inkscape::Application * inkscape, SPEventContext * eventcontext);
132 void (* activate_desktop) (Inkscape::Application * inkscape, SPDesktop * desktop);
133 void (* deactivate_desktop) (Inkscape::Application * inkscape, SPDesktop * desktop);
134 void (* destroy_document) (Inkscape::Application *inkscape, SPDocument *doc);
135 void (* color_set) (Inkscape::Application *inkscape, SPColor *color, double opacity);
136 void (* shut_down) (Inkscape::Application *inkscape);
137 void (* dialogs_hide) (Inkscape::Application *inkscape);
138 void (* dialogs_unhide) (Inkscape::Application *inkscape);
139 void (* external_change) (Inkscape::Application *inkscape);
140 };
142 static GObjectClass * parent_class;
143 static guint inkscape_signals[LAST_SIGNAL] = {0};
145 static void (* segv_handler) (int) = SIG_DFL;
146 static void (* abrt_handler) (int) = SIG_DFL;
147 static void (* fpe_handler) (int) = SIG_DFL;
148 static void (* ill_handler) (int) = SIG_DFL;
149 static void (* bus_handler) (int) = SIG_DFL;
151 #ifdef WIN32
152 #define INKSCAPE_PROFILE_DIR "Inkscape"
153 #else
154 #define INKSCAPE_PROFILE_DIR ".inkscape"
155 #endif
157 #define MENUS_FILE "menus.xml"
160 /**
161 * Retrieves the GType for the Inkscape Application object.
162 */
163 GType
164 inkscape_get_type (void)
165 {
166 static GType type = 0;
167 if (!type) {
168 GTypeInfo info = {
169 sizeof (Inkscape::ApplicationClass),
170 NULL, NULL,
171 (GClassInitFunc) inkscape_class_init,
172 NULL, NULL,
173 sizeof (Inkscape::Application),
174 4,
175 (GInstanceInitFunc) inkscape_init,
176 NULL
177 };
178 type = g_type_register_static (G_TYPE_OBJECT, "Inkscape_Application", &info, (GTypeFlags)0);
179 }
180 return type;
181 }
184 /**
185 * Initializes the inkscape class, registering all of its signal handlers
186 * and virtual functions
187 */
188 static void
189 inkscape_class_init (Inkscape::ApplicationClass * klass)
190 {
191 GObjectClass * object_class;
193 object_class = (GObjectClass *) klass;
195 parent_class = (GObjectClass *)g_type_class_peek_parent (klass);
197 inkscape_signals[MODIFY_SELECTION] = g_signal_new ("modify_selection",
198 G_TYPE_FROM_CLASS (klass),
199 G_SIGNAL_RUN_FIRST,
200 G_STRUCT_OFFSET (Inkscape::ApplicationClass, modify_selection),
201 NULL, NULL,
202 sp_marshal_NONE__POINTER_UINT,
203 G_TYPE_NONE, 2,
204 G_TYPE_POINTER, G_TYPE_UINT);
205 inkscape_signals[CHANGE_SELECTION] = g_signal_new ("change_selection",
206 G_TYPE_FROM_CLASS (klass),
207 G_SIGNAL_RUN_FIRST,
208 G_STRUCT_OFFSET (Inkscape::ApplicationClass, change_selection),
209 NULL, NULL,
210 sp_marshal_NONE__POINTER,
211 G_TYPE_NONE, 1,
212 G_TYPE_POINTER);
213 inkscape_signals[CHANGE_SUBSELECTION] = g_signal_new ("change_subselection",
214 G_TYPE_FROM_CLASS (klass),
215 G_SIGNAL_RUN_FIRST,
216 G_STRUCT_OFFSET (Inkscape::ApplicationClass, change_subselection),
217 NULL, NULL,
218 sp_marshal_NONE__POINTER,
219 G_TYPE_NONE, 1,
220 G_TYPE_POINTER);
221 inkscape_signals[SET_SELECTION] = g_signal_new ("set_selection",
222 G_TYPE_FROM_CLASS (klass),
223 G_SIGNAL_RUN_FIRST,
224 G_STRUCT_OFFSET (Inkscape::ApplicationClass, set_selection),
225 NULL, NULL,
226 sp_marshal_NONE__POINTER,
227 G_TYPE_NONE, 1,
228 G_TYPE_POINTER);
229 inkscape_signals[SET_EVENTCONTEXT] = g_signal_new ("set_eventcontext",
230 G_TYPE_FROM_CLASS (klass),
231 G_SIGNAL_RUN_FIRST,
232 G_STRUCT_OFFSET (Inkscape::ApplicationClass, set_eventcontext),
233 NULL, NULL,
234 sp_marshal_NONE__POINTER,
235 G_TYPE_NONE, 1,
236 G_TYPE_POINTER);
237 inkscape_signals[ACTIVATE_DESKTOP] = g_signal_new ("activate_desktop",
238 G_TYPE_FROM_CLASS (klass),
239 G_SIGNAL_RUN_FIRST,
240 G_STRUCT_OFFSET (Inkscape::ApplicationClass, activate_desktop),
241 NULL, NULL,
242 sp_marshal_NONE__POINTER,
243 G_TYPE_NONE, 1,
244 G_TYPE_POINTER);
245 inkscape_signals[DEACTIVATE_DESKTOP] = g_signal_new ("deactivate_desktop",
246 G_TYPE_FROM_CLASS (klass),
247 G_SIGNAL_RUN_FIRST,
248 G_STRUCT_OFFSET (Inkscape::ApplicationClass, deactivate_desktop),
249 NULL, NULL,
250 sp_marshal_NONE__POINTER,
251 G_TYPE_NONE, 1,
252 G_TYPE_POINTER);
253 inkscape_signals[SHUTDOWN_SIGNAL] = g_signal_new ("shut_down",
254 G_TYPE_FROM_CLASS (klass),
255 G_SIGNAL_RUN_FIRST,
256 G_STRUCT_OFFSET (Inkscape::ApplicationClass, shut_down),
257 NULL, NULL,
258 g_cclosure_marshal_VOID__VOID,
259 G_TYPE_NONE, 0);
260 inkscape_signals[DIALOGS_HIDE] = g_signal_new ("dialogs_hide",
261 G_TYPE_FROM_CLASS (klass),
262 G_SIGNAL_RUN_FIRST,
263 G_STRUCT_OFFSET (Inkscape::ApplicationClass, dialogs_hide),
264 NULL, NULL,
265 g_cclosure_marshal_VOID__VOID,
266 G_TYPE_NONE, 0);
267 inkscape_signals[DIALOGS_UNHIDE] = g_signal_new ("dialogs_unhide",
268 G_TYPE_FROM_CLASS (klass),
269 G_SIGNAL_RUN_FIRST,
270 G_STRUCT_OFFSET (Inkscape::ApplicationClass, dialogs_unhide),
271 NULL, NULL,
272 g_cclosure_marshal_VOID__VOID,
273 G_TYPE_NONE, 0);
274 inkscape_signals[EXTERNAL_CHANGE] = g_signal_new ("external_change",
275 G_TYPE_FROM_CLASS (klass),
276 G_SIGNAL_RUN_FIRST,
277 G_STRUCT_OFFSET (Inkscape::ApplicationClass, external_change),
278 NULL, NULL,
279 g_cclosure_marshal_VOID__VOID,
280 G_TYPE_NONE, 0);
282 object_class->dispose = inkscape_dispose;
284 klass->activate_desktop = inkscape_activate_desktop_private;
285 klass->deactivate_desktop = inkscape_deactivate_desktop_private;
286 }
289 static void
290 inkscape_init (SPObject * object)
291 {
292 if (!inkscape) {
293 inkscape = (Inkscape::Application *) object;
294 } else {
295 g_assert_not_reached ();
296 }
298 new (&inkscape->document_set) std::multiset<SPDocument *>();
300 inkscape->menus = sp_repr_read_mem (_(menus_skeleton), MENUS_SKELETON_SIZE, NULL);
302 inkscape->documents = NULL;
303 inkscape->desktops = NULL;
305 inkscape->dialogs_toggle = TRUE;
306 }
309 static void
310 inkscape_dispose (GObject *object)
311 {
312 Inkscape::Application *inkscape = (Inkscape::Application *) object;
314 while (inkscape->documents) {
315 // we don't otherwise unref, so why here?
316 sp_document_unref((SPDocument *)inkscape->documents->data);
317 }
319 g_assert (!inkscape->desktops);
321 Inkscape::Preferences::save();
323 if (inkscape->menus) {
324 /* fixme: This is not the best place */
325 Inkscape::GC::release(inkscape->menus);
326 inkscape->menus = NULL;
327 }
329 inkscape->document_set.~multiset();
331 G_OBJECT_CLASS (parent_class)->dispose (object);
333 gtk_main_quit ();
334 }
337 void
338 inkscape_ref (void)
339 {
340 if (inkscape)
341 g_object_ref (G_OBJECT (inkscape));
342 }
345 void
346 inkscape_unref (void)
347 {
348 if (inkscape)
349 g_object_unref (G_OBJECT (inkscape));
350 }
353 static void
354 inkscape_activate_desktop_private (Inkscape::Application *inkscape, SPDesktop *desktop)
355 {
356 desktop->set_active (true);
357 }
360 static void
361 inkscape_deactivate_desktop_private (Inkscape::Application *inkscape, SPDesktop *desktop)
362 {
363 desktop->set_active (false);
364 }
367 /* fixme: This is EVIL, and belongs to main after all */
369 #define SP_INDENT 8
372 static void
373 inkscape_crash_handler (int signum)
374 {
375 using Inkscape::Debug::SimpleEvent;
376 using Inkscape::Debug::EventTracker;
377 using Inkscape::Debug::Logger;
379 static gint recursion = FALSE;
381 /*
382 * reset all signal handlers: any further crashes should just be allowed
383 * to crash normally.
384 * */
385 signal (SIGSEGV, segv_handler );
386 signal (SIGABRT, abrt_handler );
387 signal (SIGFPE, fpe_handler );
388 signal (SIGILL, ill_handler );
389 #ifndef WIN32
390 signal (SIGBUS, bus_handler );
391 #endif
393 /* Stop bizarre loops */
394 if (recursion) {
395 abort ();
396 }
397 recursion = TRUE;
399 EventTracker<SimpleEvent<Inkscape::Debug::Event::CORE> > tracker("crash");
400 tracker.set<SimpleEvent<> >("emergency-save");
402 fprintf(stderr, "\nEmergency save activated!\n");
404 time_t sptime = time (NULL);
405 struct tm *sptm = localtime (&sptime);
406 gchar sptstr[256];
407 strftime (sptstr, 256, "%Y_%m_%d_%H_%M_%S", sptm);
409 gint count = 0;
410 GSList *savednames = NULL;
411 GSList *failednames = NULL;
412 for (GSList *l = inkscape->documents; l != NULL; l = l->next) {
413 SPDocument *doc;
414 Inkscape::XML::Node *repr;
415 doc = (SPDocument *) l->data;
416 repr = sp_document_repr_root (doc);
417 if (repr->attribute("sodipodi:modified")) {
418 const gchar *docname, *d0, *d;
419 gchar n[64], c[1024];
420 FILE *file;
422 /* originally, the document name was retrieved from
423 * the sodipod:docname attribute */
424 docname = doc->name;
425 if (docname) {
426 /* fixme: Quick hack to remove emergency file suffix */
427 d0 = strrchr ((char*)docname, '.');
428 if (d0 && (d0 > docname)) {
429 d0 = strrchr ((char*)(d0 - 1), '.');
430 if (d0 && (d0 > docname)) {
431 d = d0;
432 while (isdigit (*d) || (*d == '.') || (*d == '_')) d += 1;
433 if (*d) {
434 memcpy (n, docname, MIN (d0 - docname - 1, 64));
435 n[63] = '\0';
436 docname = n;
437 }
438 }
439 }
440 }
442 if (!docname || !*docname) docname = "emergency";
443 // try saving to the profile location
444 g_snprintf (c, 1024, "%.256s.%s.%d", docname, sptstr, count);
445 gchar * location = homedir_path(c);
446 Inkscape::IO::dump_fopen_call(location, "E");
447 file = Inkscape::IO::fopen_utf8name(location, "w");
448 g_free(location);
449 if (!file) {
450 // try saving to /tmp
451 g_snprintf (c, 1024, "/tmp/inkscape-%.256s.%s.%d", docname, sptstr, count);
452 Inkscape::IO::dump_fopen_call(c, "G");
453 file = Inkscape::IO::fopen_utf8name(c, "w");
454 }
455 if (!file) {
456 // try saving to the current directory
457 g_snprintf (c, 1024, "inkscape-%.256s.%s.%d", docname, sptstr, count);
458 Inkscape::IO::dump_fopen_call(c, "F");
459 file = Inkscape::IO::fopen_utf8name(c, "w");
460 }
461 if (file) {
462 sp_repr_save_stream (repr->document(), file, SP_SVG_NS_URI);
463 savednames = g_slist_prepend (savednames, g_strdup (c));
464 fclose (file);
465 } else {
466 docname = repr->attribute("sodipodi:docname");
467 failednames = g_slist_prepend (failednames, (docname) ? g_strdup (docname) : g_strdup (_("Untitled document")));
468 }
469 count++;
470 }
471 }
473 savednames = g_slist_reverse (savednames);
474 failednames = g_slist_reverse (failednames);
475 if (savednames) {
476 fprintf (stderr, "\nEmergency save document locations:\n");
477 for (GSList *l = savednames; l != NULL; l = l->next) {
478 fprintf (stderr, " %s\n", (gchar *) l->data);
479 }
480 }
481 if (failednames) {
482 fprintf (stderr, "\nFailed to do emergency save for documents:\n");
483 for (GSList *l = failednames; l != NULL; l = l->next) {
484 fprintf (stderr, " %s\n", (gchar *) l->data);
485 }
486 }
488 Inkscape::Preferences::save();
490 fprintf (stderr, "Emergency save completed. Inkscape will close now.\n");
491 fprintf (stderr, "If you can reproduce this crash, please file a bug at www.inkscape.org\n");
492 fprintf (stderr, "with a detailed description of the steps leading to the crash, so we can fix it.\n");
494 /* Show nice dialog box */
496 char const *istr = _("Inkscape encountered an internal error and will close now.\n");
497 char const *sstr = _("Automatic backups of unsaved documents were done to the following locations:\n");
498 char const *fstr = _("Automatic backup of the following documents failed:\n");
499 gint nllen = strlen ("\n");
500 gint len = strlen (istr) + strlen (sstr) + strlen (fstr);
501 for (GSList *l = savednames; l != NULL; l = l->next) {
502 len = len + SP_INDENT + strlen ((gchar *) l->data) + nllen;
503 }
504 for (GSList *l = failednames; l != NULL; l = l->next) {
505 len = len + SP_INDENT + strlen ((gchar *) l->data) + nllen;
506 }
507 len += 1;
508 gchar *b = g_new (gchar, len);
509 gint pos = 0;
510 len = strlen (istr);
511 memcpy (b + pos, istr, len);
512 pos += len;
513 if (savednames) {
514 len = strlen (sstr);
515 memcpy (b + pos, sstr, len);
516 pos += len;
517 for (GSList *l = savednames; l != NULL; l = l->next) {
518 memset (b + pos, ' ', SP_INDENT);
519 pos += SP_INDENT;
520 len = strlen ((gchar *) l->data);
521 memcpy (b + pos, l->data, len);
522 pos += len;
523 memcpy (b + pos, "\n", nllen);
524 pos += nllen;
525 }
526 }
527 if (failednames) {
528 len = strlen (fstr);
529 memcpy (b + pos, fstr, len);
530 pos += len;
531 for (GSList *l = failednames; l != NULL; l = l->next) {
532 memset (b + pos, ' ', SP_INDENT);
533 pos += SP_INDENT;
534 len = strlen ((gchar *) l->data);
535 memcpy (b + pos, l->data, len);
536 pos += len;
537 memcpy (b + pos, "\n", nllen);
538 pos += nllen;
539 }
540 }
541 *(b + pos) = '\0';
543 if ( inkscape_get_instance() && inkscape_app_use_gui( inkscape_get_instance() ) ) {
544 GtkWidget *msgbox = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", b);
545 gtk_dialog_run (GTK_DIALOG (msgbox));
546 gtk_widget_destroy (msgbox);
547 }
548 else
549 {
550 g_message( "Error: %s", b );
551 }
552 g_free (b);
554 tracker.clear();
555 Logger::shutdown();
557 /* on exit, allow restored signal handler to take over and crash us */
558 }
562 void
563 inkscape_application_init (const gchar *argv0, gboolean use_gui)
564 {
565 inkscape = (Inkscape::Application *)g_object_new (SP_TYPE_INKSCAPE, NULL);
566 /* fixme: load application defaults */
568 segv_handler = signal (SIGSEGV, inkscape_crash_handler);
569 abrt_handler = signal (SIGABRT, inkscape_crash_handler);
570 fpe_handler = signal (SIGFPE, inkscape_crash_handler);
571 ill_handler = signal (SIGILL, inkscape_crash_handler);
572 #ifndef WIN32
573 bus_handler = signal (SIGBUS, inkscape_crash_handler);
574 #endif
576 inkscape->use_gui = use_gui;
577 inkscape->argv0 = g_strdup(argv0);
579 /* Attempt to load the preferences, and set the save_preferences flag to TRUE
580 if we could, or FALSE if we couldn't */
581 Inkscape::Preferences::load();
582 inkscape_load_menus(inkscape);
584 /* DebugDialog redirection. On Linux, default to OFF, on Win32, default to ON.
585 * Use only if use_gui is enabled
586 */
587 #ifdef WIN32
588 #define DEFAULT_LOG_REDIRECT true
589 #else
590 #define DEFAULT_LOG_REDIRECT false
591 #endif
593 if (use_gui == TRUE && prefs_get_int_attribute("dialogs.debug", "redirect", DEFAULT_LOG_REDIRECT))
594 {
595 Inkscape::UI::Dialogs::DebugDialog::getInstance()->captureLogMessages();
596 }
598 /* Initialize the extensions */
599 Inkscape::Extension::init();
601 /* Create an initial perspective, append it to the list of existing perspectives and make it current */
602 Box3D::Perspective3D *initial_persp = new Box3D::Perspective3D (
603 // VP in x-direction
604 Box3D::VanishingPoint( NR::Point(-50.0, 600.0),
605 NR::Point( -1.0, 0.0), Box3D::VP_FINITE),
606 // VP in y-direction
607 Box3D::VanishingPoint( NR::Point(500.0,1000.0),
608 NR::Point( 0.0, 1.0), Box3D::VP_INFINITE),
609 // VP in z-direction
610 Box3D::VanishingPoint( NR::Point(700.0, 600.0),
611 NR::Point(sqrt(3.0),1.0), Box3D::VP_FINITE));
613 Box3D::Perspective3D::current_perspective = initial_persp;
614 Box3D::Perspective3D::add_perspective (initial_persp);
616 return;
617 }
619 /**
620 * Returns the current Inkscape::Application global object
621 */
622 Inkscape::Application *
623 inkscape_get_instance()
624 {
625 return inkscape;
626 }
628 gboolean inkscape_app_use_gui( Inkscape::Application const * app )
629 {
630 return app->use_gui;
631 }
633 /**
634 * Preference management
635 * We use '.' as separator
636 *
637 * Returns TRUE if the config file was successfully loaded, FALSE if not.
638 */
639 bool
640 inkscape_load_config (const gchar *filename, Inkscape::XML::Document *config, const gchar *skeleton,
641 unsigned int skel_size, const gchar *e_notreg, const gchar *e_notxml,
642 const gchar *e_notsp, const gchar *warn)
643 {
644 gchar *fn = profile_path(filename);
645 if (!Inkscape::IO::file_test(fn, G_FILE_TEST_EXISTS)) {
646 bool result;
647 /* No such file */
648 result = inkscape_init_config (config, filename, skeleton,
649 skel_size,
650 _("Cannot create directory %s.\n%s"),
651 _("%s is not a valid directory.\n%s"),
652 _("Cannot create file %s.\n%s"),
653 _("Cannot write file %s.\n%s"),
654 _("Although Inkscape will run, it will use default settings,\n"
655 "and any changes made in preferences will not be saved."));
656 g_free (fn);
657 return result;
658 }
660 if (!Inkscape::IO::file_test(fn, G_FILE_TEST_IS_REGULAR)) {
661 /* Not a regular file */
662 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
663 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, e_notreg, safeFn, warn);
664 gtk_dialog_run (GTK_DIALOG (w));
665 gtk_widget_destroy (w);
666 g_free(safeFn);
667 g_free (fn);
668 return false;
669 }
671 Inkscape::XML::Document *doc = sp_repr_read_file (fn, NULL);
672 if (doc == NULL) {
673 /* Not an valid xml file */
674 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
675 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, e_notxml, safeFn, warn);
676 gtk_dialog_run (GTK_DIALOG (w));
677 gtk_widget_destroy (w);
678 g_free(safeFn);
679 g_free (fn);
680 return false;
681 }
683 Inkscape::XML::Node *root = doc->root();
684 if (strcmp (root->name(), "inkscape")) {
685 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
686 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, e_notsp, safeFn, warn);
687 gtk_dialog_run (GTK_DIALOG (w));
688 gtk_widget_destroy (w);
689 Inkscape::GC::release(doc);
690 g_free(safeFn);
691 g_free (fn);
692 return false;
693 }
695 /** \todo this is a hack, need to figure out how to get
696 * a reasonable merge working with the menus.xml file */
697 if (skel_size == MENUS_SKELETON_SIZE) {
698 if (INKSCAPE)
699 INKSCAPE->menus = doc;
700 doc = config;
701 } else {
702 config->root()->mergeFrom(doc->root(), "id");
703 }
705 Inkscape::GC::release(doc);
706 g_free (fn);
707 return true;
708 }
710 /**
711 * Menus management
712 *
713 */
714 bool
715 inkscape_load_menus (Inkscape::Application *inkscape)
716 {
717 gchar *fn = profile_path(MENUS_FILE);
718 bool retval = false;
719 if (Inkscape::IO::file_test(fn, G_FILE_TEST_EXISTS)) {
720 retval = inkscape_load_config (MENUS_FILE,
721 inkscape->menus,
722 menus_skeleton,
723 MENUS_SKELETON_SIZE,
724 _("%s is not a regular file.\n%s"),
725 _("%s not a valid XML file, or\n"
726 "you don't have read permissions on it.\n%s"),
727 _("%s is not a valid menus file.\n%s"),
728 _("Inkscape will run with default menus.\n"
729 "New menus will not be saved."));
730 } else {
731 INKSCAPE->menus = sp_repr_read_mem(menus_skeleton, MENUS_SKELETON_SIZE, NULL);
732 if (INKSCAPE->menus != NULL)
733 retval = true;
734 }
735 g_free(fn);
736 return retval;
737 }
739 /**
740 * We use '.' as separator
741 * \param inkscape Unused
742 */
743 Inkscape::XML::Node *
744 inkscape_get_repr (Inkscape::Application *inkscape, const gchar *key)
745 {
746 if ( (key == NULL) || (inkscape == NULL) ) {
747 return NULL;
748 }
750 Inkscape::XML::Node *prefs = Inkscape::Preferences::get();
751 if ( !prefs ) {
752 return NULL;
753 }
755 Inkscape::XML::Node *repr = prefs->root();
756 if (!repr) return NULL;
757 g_assert (!(strcmp (repr->name(), "inkscape")));
759 gchar const *s = key;
760 while ((s) && (*s)) {
762 /* Find next name */
763 gchar const *e = strchr (s, '.');
764 guint len;
765 if (e) {
766 len = e++ - s;
767 } else {
768 len = strlen (s);
769 }
771 Inkscape::XML::Node* child;
772 for (child = repr->firstChild(); child != NULL; child = child->next()) {
773 gchar const *id = child->attribute("id");
774 if ((id) && (strlen (id) == len) && (!strncmp (id, s, len)))
775 {
776 break;
777 }
778 }
779 if (child == NULL) {
780 return NULL;
781 }
783 repr = child;
784 s = e;
785 }
786 return repr;
787 }
791 void
792 inkscape_selection_modified (Inkscape::Selection *selection, guint flags)
793 {
794 if (Inkscape::NSApplication::Application::getNewGui()) {
795 Inkscape::NSApplication::Editor::selectionModified (selection, flags);
796 return;
797 }
798 g_return_if_fail (selection != NULL);
800 if (DESKTOP_IS_ACTIVE (selection->desktop())) {
801 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[MODIFY_SELECTION], 0, selection, flags);
802 }
803 }
806 void
807 inkscape_selection_changed (Inkscape::Selection * selection)
808 {
809 if (Inkscape::NSApplication::Application::getNewGui()) {
810 Inkscape::NSApplication::Editor::selectionChanged (selection);
811 return;
812 }
813 g_return_if_fail (selection != NULL);
815 if (DESKTOP_IS_ACTIVE (selection->desktop())) {
816 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[CHANGE_SELECTION], 0, selection);
817 }
818 }
820 void
821 inkscape_subselection_changed (SPDesktop *desktop)
822 {
823 if (Inkscape::NSApplication::Application::getNewGui()) {
824 Inkscape::NSApplication::Editor::subSelectionChanged (desktop);
825 return;
826 }
827 g_return_if_fail (desktop != NULL);
829 if (DESKTOP_IS_ACTIVE (desktop)) {
830 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[CHANGE_SUBSELECTION], 0, desktop);
831 }
832 }
835 void
836 inkscape_selection_set (Inkscape::Selection * selection)
837 {
838 if (Inkscape::NSApplication::Application::getNewGui()) {
839 Inkscape::NSApplication::Editor::selectionSet (selection);
840 return;
841 }
842 g_return_if_fail (selection != NULL);
844 if (DESKTOP_IS_ACTIVE (selection->desktop())) {
845 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_SELECTION], 0, selection);
846 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[CHANGE_SELECTION], 0, selection);
847 }
848 }
851 void
852 inkscape_eventcontext_set (SPEventContext * eventcontext)
853 {
854 if (Inkscape::NSApplication::Application::getNewGui()) {
855 Inkscape::NSApplication::Editor::eventContextSet (eventcontext);
856 return;
857 }
858 g_return_if_fail (eventcontext != NULL);
859 g_return_if_fail (SP_IS_EVENT_CONTEXT (eventcontext));
861 if (DESKTOP_IS_ACTIVE (eventcontext->desktop)) {
862 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_EVENTCONTEXT], 0, eventcontext);
863 }
864 }
867 void
868 inkscape_add_desktop (SPDesktop * desktop)
869 {
870 g_return_if_fail (desktop != NULL);
872 if (Inkscape::NSApplication::Application::getNewGui())
873 {
874 Inkscape::NSApplication::Editor::addDesktop (desktop);
875 return;
876 }
877 g_return_if_fail (inkscape != NULL);
879 g_assert (!g_slist_find (inkscape->desktops, desktop));
881 inkscape->desktops = g_slist_append (inkscape->desktops, desktop);
883 if (DESKTOP_IS_ACTIVE (desktop)) {
884 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[ACTIVATE_DESKTOP], 0, desktop);
885 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_EVENTCONTEXT], 0, sp_desktop_event_context (desktop));
886 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_SELECTION], 0, sp_desktop_selection (desktop));
887 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[CHANGE_SELECTION], 0, sp_desktop_selection (desktop));
888 }
889 }
893 void
894 inkscape_remove_desktop (SPDesktop * desktop)
895 {
896 g_return_if_fail (desktop != NULL);
897 if (Inkscape::NSApplication::Application::getNewGui())
898 {
899 Inkscape::NSApplication::Editor::removeDesktop (desktop);
900 return;
901 }
902 g_return_if_fail (inkscape != NULL);
904 g_assert (g_slist_find (inkscape->desktops, desktop));
906 if (DESKTOP_IS_ACTIVE (desktop)) {
907 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[DEACTIVATE_DESKTOP], 0, desktop);
908 if (inkscape->desktops->next != NULL) {
909 SPDesktop * new_desktop = (SPDesktop *) inkscape->desktops->next->data;
910 inkscape->desktops = g_slist_remove (inkscape->desktops, new_desktop);
911 inkscape->desktops = g_slist_prepend (inkscape->desktops, new_desktop);
912 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[ACTIVATE_DESKTOP], 0, new_desktop);
913 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_EVENTCONTEXT], 0, sp_desktop_event_context (new_desktop));
914 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_SELECTION], 0, sp_desktop_selection (new_desktop));
915 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[CHANGE_SELECTION], 0, sp_desktop_selection (new_desktop));
916 } else {
917 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_EVENTCONTEXT], 0, NULL);
918 if (sp_desktop_selection(desktop))
919 sp_desktop_selection(desktop)->clear();
920 }
921 }
923 inkscape->desktops = g_slist_remove (inkscape->desktops, desktop);
925 // if this was the last desktop, shut down the program
926 if (inkscape->desktops == NULL) {
927 inkscape_exit (inkscape);
928 }
929 }
933 void
934 inkscape_activate_desktop (SPDesktop * desktop)
935 {
936 g_return_if_fail (desktop != NULL);
937 if (Inkscape::NSApplication::Application::getNewGui())
938 {
939 Inkscape::NSApplication::Editor::activateDesktop (desktop);
940 return;
941 }
942 g_return_if_fail (inkscape != NULL);
944 if (DESKTOP_IS_ACTIVE (desktop)) {
945 return;
946 }
948 g_assert (g_slist_find (inkscape->desktops, desktop));
950 SPDesktop *current = (SPDesktop *) inkscape->desktops->data;
952 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[DEACTIVATE_DESKTOP], 0, current);
954 inkscape->desktops = g_slist_remove (inkscape->desktops, desktop);
955 inkscape->desktops = g_slist_prepend (inkscape->desktops, desktop);
957 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[ACTIVATE_DESKTOP], 0, desktop);
958 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_EVENTCONTEXT], 0, sp_desktop_event_context (desktop));
959 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[SET_SELECTION], 0, sp_desktop_selection (desktop));
960 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[CHANGE_SELECTION], 0, sp_desktop_selection (desktop));
961 }
964 /**
965 * Resends ACTIVATE_DESKTOP for current desktop; needed when a new desktop has got its window that dialogs will transientize to
966 */
967 void
968 inkscape_reactivate_desktop (SPDesktop * desktop)
969 {
970 g_return_if_fail (desktop != NULL);
971 if (Inkscape::NSApplication::Application::getNewGui())
972 {
973 Inkscape::NSApplication::Editor::reactivateDesktop (desktop);
974 return;
975 }
976 g_return_if_fail (inkscape != NULL);
978 if (DESKTOP_IS_ACTIVE (desktop))
979 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[ACTIVATE_DESKTOP], 0, desktop);
980 }
984 SPDesktop *
985 inkscape_find_desktop_by_dkey (unsigned int dkey)
986 {
987 for (GSList *r = inkscape->desktops; r; r = r->next) {
988 if (((SPDesktop *) r->data)->dkey == dkey)
989 return ((SPDesktop *) r->data);
990 }
991 return NULL;
992 }
997 unsigned int
998 inkscape_maximum_dkey()
999 {
1000 unsigned int dkey = 0;
1002 for (GSList *r = inkscape->desktops; r; r = r->next) {
1003 if (((SPDesktop *) r->data)->dkey > dkey)
1004 dkey = ((SPDesktop *) r->data)->dkey;
1005 }
1007 return dkey;
1008 }
1012 SPDesktop *
1013 inkscape_next_desktop ()
1014 {
1015 SPDesktop *d = NULL;
1016 unsigned int dkey_current = ((SPDesktop *) inkscape->desktops->data)->dkey;
1018 if (dkey_current < inkscape_maximum_dkey()) {
1019 // find next existing
1020 for (unsigned int i = dkey_current + 1; i <= inkscape_maximum_dkey(); i++) {
1021 d = inkscape_find_desktop_by_dkey (i);
1022 if (d) {
1023 break;
1024 }
1025 }
1026 } else {
1027 // find first existing
1028 for (unsigned int i = 0; i <= inkscape_maximum_dkey(); i++) {
1029 d = inkscape_find_desktop_by_dkey (i);
1030 if (d) {
1031 break;
1032 }
1033 }
1034 }
1036 g_assert (d);
1038 return d;
1039 }
1043 SPDesktop *
1044 inkscape_prev_desktop ()
1045 {
1046 SPDesktop *d = NULL;
1047 unsigned int dkey_current = ((SPDesktop *) inkscape->desktops->data)->dkey;
1049 if (dkey_current > 0) {
1050 // find prev existing
1051 for (signed int i = dkey_current - 1; i >= 0; i--) {
1052 d = inkscape_find_desktop_by_dkey (i);
1053 if (d) {
1054 break;
1055 }
1056 }
1057 }
1058 if (!d) {
1059 // find last existing
1060 d = inkscape_find_desktop_by_dkey (inkscape_maximum_dkey());
1061 }
1063 g_assert (d);
1065 return d;
1066 }
1070 void
1071 inkscape_switch_desktops_next ()
1072 {
1073 inkscape_next_desktop()->presentWindow();
1074 }
1078 void
1079 inkscape_switch_desktops_prev ()
1080 {
1081 inkscape_prev_desktop()->presentWindow();
1082 }
1086 void
1087 inkscape_dialogs_hide ()
1088 {
1089 if (Inkscape::NSApplication::Application::getNewGui())
1090 Inkscape::NSApplication::Editor::hideDialogs();
1091 else
1092 {
1093 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[DIALOGS_HIDE], 0);
1094 inkscape->dialogs_toggle = FALSE;
1095 }
1096 }
1100 void
1101 inkscape_dialogs_unhide ()
1102 {
1103 if (Inkscape::NSApplication::Application::getNewGui())
1104 Inkscape::NSApplication::Editor::unhideDialogs();
1105 else
1106 {
1107 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[DIALOGS_UNHIDE], 0);
1108 inkscape->dialogs_toggle = TRUE;
1109 }
1110 }
1114 void
1115 inkscape_dialogs_toggle ()
1116 {
1117 if (inkscape->dialogs_toggle) {
1118 inkscape_dialogs_hide ();
1119 } else {
1120 inkscape_dialogs_unhide ();
1121 }
1122 }
1124 void
1125 inkscape_external_change ()
1126 {
1127 g_return_if_fail (inkscape != NULL);
1129 g_signal_emit (G_OBJECT (inkscape), inkscape_signals[EXTERNAL_CHANGE], 0);
1130 }
1132 /**
1133 * fixme: These need probably signals too
1134 */
1135 void
1136 inkscape_add_document (SPDocument *document)
1137 {
1138 g_return_if_fail (document != NULL);
1140 if (!Inkscape::NSApplication::Application::getNewGui())
1141 {
1142 if ( inkscape->document_set.find(document) != inkscape->document_set.end() ) {
1144 inkscape->documents = g_slist_append (inkscape->documents, document);
1145 }
1146 inkscape->document_set.insert(document);
1147 }
1148 else
1149 {
1150 Inkscape::NSApplication::Editor::addDocument (document);
1151 }
1152 }
1156 void
1157 inkscape_remove_document (SPDocument *document)
1158 {
1159 g_return_if_fail (document != NULL);
1161 if (!Inkscape::NSApplication::Application::getNewGui())
1162 {
1163 inkscape->document_set.erase(document);
1164 if ( inkscape->document_set.find(document) == inkscape->document_set.end() ) {
1165 inkscape->documents = g_slist_remove (inkscape->documents, document);
1166 }
1167 }
1168 else
1169 {
1170 Inkscape::NSApplication::Editor::removeDocument (document);
1171 }
1173 return;
1174 }
1176 SPDesktop *
1177 inkscape_active_desktop (void)
1178 {
1179 if (Inkscape::NSApplication::Application::getNewGui())
1180 return Inkscape::NSApplication::Editor::getActiveDesktop();
1182 if (inkscape->desktops == NULL) {
1183 return NULL;
1184 }
1186 return (SPDesktop *) inkscape->desktops->data;
1187 }
1189 SPDocument *
1190 inkscape_active_document (void)
1191 {
1192 if (Inkscape::NSApplication::Application::getNewGui())
1193 return Inkscape::NSApplication::Editor::getActiveDocument();
1195 if (SP_ACTIVE_DESKTOP) {
1196 return sp_desktop_document (SP_ACTIVE_DESKTOP);
1197 }
1199 return NULL;
1200 }
1202 bool inkscape_is_sole_desktop_for_document(SPDesktop const &desktop) {
1203 SPDocument const* document = desktop.doc();
1204 if (!document) {
1205 return false;
1206 }
1207 for ( GSList *iter = inkscape->desktops ; iter ; iter = iter->next ) {
1208 SPDesktop *other_desktop=(SPDesktop *)iter->data;
1209 SPDocument *other_document=other_desktop->doc();
1210 if ( other_document == document && other_desktop != &desktop ) {
1211 return false;
1212 }
1213 }
1214 return true;
1215 }
1217 SPEventContext *
1218 inkscape_active_event_context (void)
1219 {
1220 if (SP_ACTIVE_DESKTOP) {
1221 return sp_desktop_event_context (SP_ACTIVE_DESKTOP);
1222 }
1224 return NULL;
1225 }
1229 /*#####################
1230 # HELPERS
1231 #####################*/
1233 static bool
1234 inkscape_init_config (Inkscape::XML::Document *doc, const gchar *config_name, const gchar *skeleton,
1235 unsigned int skel_size,
1236 const gchar *e_mkdir,
1237 const gchar *e_notdir,
1238 const gchar *e_ccf,
1239 const gchar *e_cwf,
1240 const gchar *warn)
1241 {
1242 gchar *dn = profile_path(NULL);
1243 bool use_gui = (Inkscape::NSApplication::Application::getNewGui())? Inkscape::NSApplication::Application::getUseGui() : inkscape->use_gui;
1244 if (!Inkscape::IO::file_test(dn, G_FILE_TEST_EXISTS)) {
1245 if (Inkscape::IO::mkdir_utf8name(dn))
1246 {
1247 if (use_gui) {
1248 // Cannot create directory
1249 gchar *safeDn = Inkscape::IO::sanitizeString(dn);
1250 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, e_mkdir, safeDn, warn);
1251 gtk_dialog_run (GTK_DIALOG (w));
1252 gtk_widget_destroy (w);
1253 g_free(safeDn);
1254 g_free (dn);
1255 return false;
1256 } else {
1257 g_warning(e_mkdir, dn, warn);
1258 g_free (dn);
1259 return false;
1260 }
1261 }
1263 // Also create (empty for now) subdirectories for the user's stuff
1264 {
1265 gchar *temp_dn = profile_path("templates");
1266 Inkscape::IO::mkdir_utf8name(temp_dn);
1267 }
1268 {
1269 gchar *temp_dn = profile_path("keys");
1270 Inkscape::IO::mkdir_utf8name(temp_dn);
1271 }
1272 {
1273 gchar *temp_dn = profile_path("icons");
1274 Inkscape::IO::mkdir_utf8name(temp_dn);
1275 }
1276 {
1277 gchar *temp_dn = profile_path("extensions");
1278 Inkscape::IO::mkdir_utf8name(temp_dn);
1279 }
1280 {
1281 gchar *temp_dn = profile_path("palettes");
1282 Inkscape::IO::mkdir_utf8name(temp_dn);
1283 }
1285 } else if (!Inkscape::IO::file_test(dn, G_FILE_TEST_IS_DIR)) {
1286 if (use_gui) {
1287 // Not a directory
1288 gchar *safeDn = Inkscape::IO::sanitizeString(dn);
1289 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, e_notdir, safeDn, warn);
1290 gtk_dialog_run (GTK_DIALOG (w));
1291 gtk_widget_destroy (w);
1292 g_free( safeDn );
1293 g_free (dn);
1294 return false;
1295 } else {
1296 g_warning(e_notdir, dn, warn);
1297 g_free(dn);
1298 return false;
1299 }
1300 }
1301 g_free (dn);
1303 gchar *fn = profile_path(config_name);
1305 Inkscape::IO::dump_fopen_call(fn, "H");
1306 FILE *fh = Inkscape::IO::fopen_utf8name(fn, "w");
1307 if (!fh) {
1308 if (use_gui) {
1309 /* Cannot create file */
1310 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
1311 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, e_ccf, safeFn, warn);
1312 gtk_dialog_run (GTK_DIALOG (w));
1313 gtk_widget_destroy (w);
1314 g_free(safeFn);
1315 g_free (fn);
1316 return false;
1317 } else {
1318 g_warning(e_ccf, fn, warn);
1319 g_free(fn);
1320 return false;
1321 }
1322 }
1323 if ( fwrite(skeleton, 1, skel_size, fh) != skel_size ) {
1324 if (use_gui) {
1325 /* Cannot create file */
1326 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
1327 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, e_cwf, safeFn, warn);
1328 gtk_dialog_run (GTK_DIALOG (w));
1329 gtk_widget_destroy (w);
1330 g_free(safeFn);
1331 g_free (fn);
1332 fclose(fh);
1333 return false;
1334 } else {
1335 g_warning(e_cwf, fn, warn);
1336 g_free(fn);
1337 fclose(fh);
1338 return false;
1339 }
1340 }
1342 g_free(fn);
1343 fclose(fh);
1344 return true;
1345 }
1347 void
1348 inkscape_refresh_display (Inkscape::Application *inkscape)
1349 {
1350 for (GSList *l = inkscape->desktops; l != NULL; l = l->next) {
1351 (static_cast<Inkscape::UI::View::View*>(l->data))->requestRedraw();
1352 }
1353 }
1356 /**
1357 * Handler for Inkscape's Exit verb. This emits the shutdown signal,
1358 * saves the preferences if appropriate, and quits.
1359 */
1360 void
1361 inkscape_exit (Inkscape::Application *inkscape)
1362 {
1363 g_assert (INKSCAPE);
1365 //emit shutdown signal so that dialogs could remember layout
1366 g_signal_emit (G_OBJECT (INKSCAPE), inkscape_signals[SHUTDOWN_SIGNAL], 0);
1368 Inkscape::Preferences::save();
1369 gtk_main_quit ();
1370 }
1372 gchar *
1373 homedir_path(const char *filename)
1374 {
1375 static const gchar *homedir = NULL;
1376 if (!homedir) {
1377 homedir = g_get_home_dir();
1378 gchar* utf8Path = g_filename_to_utf8( homedir, -1, NULL, NULL, NULL );
1379 if ( utf8Path )
1380 {
1381 homedir = utf8Path;
1382 if (!g_utf8_validate(homedir, -1, NULL)) {
1383 g_warning( "g_get_home_dir() post A IS NOT UTF-8" );
1384 }
1385 }
1386 }
1387 if (!homedir) {
1388 gchar * path = g_path_get_dirname(INKSCAPE->argv0);
1389 gchar* utf8Path = g_filename_to_utf8( path, -1, NULL, NULL, NULL );
1390 g_free(path);
1391 if ( utf8Path )
1392 {
1393 homedir = utf8Path;
1394 if (!g_utf8_validate(homedir, -1, NULL)) {
1395 g_warning( "g_get_home_dir() post B IS NOT UTF-8" );
1396 }
1397 }
1398 }
1399 return g_build_filename(homedir, filename, NULL);
1400 }
1403 /**
1404 * Get, or guess, or decide the location where the preferences.xml
1405 * file should be located.
1406 */
1407 gchar *
1408 profile_path(const char *filename)
1409 {
1410 static const gchar *prefdir = NULL;
1411 if (!prefdir) {
1412 #ifdef HAS_SHGetSpecialFolderLocation
1413 // prefer c:\Documents and Settings\UserName\Application Data\ to
1414 // c:\Documents and Settings\userName\;
1415 if (!prefdir) {
1416 ITEMIDLIST *pidl = 0;
1417 if ( SHGetSpecialFolderLocation( NULL, CSIDL_APPDATA, &pidl ) == NOERROR ) {
1418 gchar * utf8Path = NULL;
1420 if ( PrintWin32::is_os_wide() ) {
1421 wchar_t pathBuf[MAX_PATH+1];
1422 g_assert(sizeof(wchar_t) == sizeof(gunichar2));
1424 if ( SHGetPathFromIDListW( pidl, pathBuf ) ) {
1425 utf8Path = g_utf16_to_utf8( (gunichar2*)(&pathBuf[0]), -1, NULL, NULL, NULL );
1426 }
1427 } else {
1428 char pathBuf[MAX_PATH+1];
1430 if ( SHGetPathFromIDListA( pidl, pathBuf ) ) {
1431 utf8Path = g_filename_to_utf8( pathBuf, -1, NULL, NULL, NULL );
1432 }
1433 }
1435 if ( utf8Path ) {
1436 if (!g_utf8_validate(utf8Path, -1, NULL)) {
1437 g_warning( "SHGetPathFromIDList%c() resulted in invalid UTF-8", (PrintWin32::is_os_wide() ? 'W' : 'A') );
1438 g_free( utf8Path );
1439 utf8Path = 0;
1440 } else {
1441 prefdir = utf8Path;
1442 }
1443 }
1446 /* not compiling yet...
1448 // Remember to free the list pointer
1449 IMalloc * imalloc = 0;
1450 if ( SHGetMalloc(&imalloc) == NOERROR) {
1451 imalloc->lpVtbl->Free( imalloc, pidl );
1452 imalloc->lpVtbl->Release( imalloc );
1453 }
1454 */
1455 }
1456 }
1457 #endif
1458 if (!prefdir) {
1459 prefdir = homedir_path(NULL);
1460 }
1461 }
1462 return g_build_filename(prefdir, INKSCAPE_PROFILE_DIR, filename, NULL);
1463 }
1465 Inkscape::XML::Node *
1466 inkscape_get_menus (Inkscape::Application * inkscape)
1467 {
1468 Inkscape::XML::Node *repr = inkscape->menus->root();
1469 g_assert (!(strcmp (repr->name(), "inkscape")));
1470 return repr->firstChild();
1471 }
1473 void
1474 inkscape_get_all_desktops(std::list< SPDesktop* >& listbuf)
1475 {
1476 for(GSList* l = inkscape->desktops; l != NULL; l = l->next) {
1477 listbuf.push_back(static_cast< SPDesktop* >(l->data));
1478 }
1479 }
1483 /*
1484 Local Variables:
1485 mode:c++
1486 c-file-style:"stroustrup"
1487 c-file-offsets:((innamespace . 0)(inline-open . 0))
1488 indent-tabs-mode:nil
1489 fill-column:99
1490 End:
1491 */
1492 // vim: expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :