Code

Workaround for some segfaults: Store perspectives globally instead of in each desktop...
[inkscape.git] / src / inkscape.cpp
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)
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;
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)
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;
289 static void
290 inkscape_init (SPObject * object)
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;
309 static void
310 inkscape_dispose (GObject *object)
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 ();
337 void
338 inkscape_ref (void)
340     if (inkscape)
341         g_object_ref (G_OBJECT (inkscape));
345 void
346 inkscape_unref (void)
348     if (inkscape)
349         g_object_unref (G_OBJECT (inkscape));
353 static void
354 inkscape_activate_desktop_private (Inkscape::Application *inkscape, SPDesktop *desktop)
356     desktop->set_active (true);
360 static void
361 inkscape_deactivate_desktop_private (Inkscape::Application *inkscape, SPDesktop *desktop)
363     desktop->set_active (false);
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)
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
392     
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 */
562 void
563 inkscape_application_init (const gchar *argv0, gboolean use_gui)
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;
619 /**
620  *  Returns the current Inkscape::Application global object
621  */
622 Inkscape::Application *
623 inkscape_get_instance()
625         return inkscape;
628 gboolean inkscape_app_use_gui( Inkscape::Application const * app )
630     return app->use_gui;
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)
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;
710 /**
711  *  Menus management
712  *
713  */
714 bool
715 inkscape_load_menus (Inkscape::Application *inkscape)
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;
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)
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;
791 void
792 inkscape_selection_modified (Inkscape::Selection *selection, guint flags)
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     }
806 void
807 inkscape_selection_changed (Inkscape::Selection * selection)
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     }
820 void
821 inkscape_subselection_changed (SPDesktop *desktop)
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     }
835 void
836 inkscape_selection_set (Inkscape::Selection * selection)
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     }
851 void
852 inkscape_eventcontext_set (SPEventContext * eventcontext)
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     }
867 void
868 inkscape_add_desktop (SPDesktop * desktop)
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     }
893 void
894 inkscape_remove_desktop (SPDesktop * desktop)
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     }
933 void
934 inkscape_activate_desktop (SPDesktop * desktop)
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));
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)
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);
984 SPDesktop *
985 inkscape_find_desktop_by_dkey (unsigned int dkey)
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;
997 unsigned int
998 inkscape_maximum_dkey()
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;
1012 SPDesktop *
1013 inkscape_next_desktop ()
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;
1043 SPDesktop *
1044 inkscape_prev_desktop ()
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;
1070 void
1071 inkscape_switch_desktops_next ()
1073     inkscape_next_desktop()->presentWindow();
1078 void
1079 inkscape_switch_desktops_prev ()
1081     inkscape_prev_desktop()->presentWindow();
1086 void
1087 inkscape_dialogs_hide ()
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     }
1100 void
1101 inkscape_dialogs_unhide ()
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     }
1114 void
1115 inkscape_dialogs_toggle ()
1117     if (inkscape->dialogs_toggle) {
1118         inkscape_dialogs_hide ();
1119     } else {
1120         inkscape_dialogs_unhide ();
1121     }
1124 void
1125 inkscape_external_change ()
1127     g_return_if_fail (inkscape != NULL);
1129     g_signal_emit (G_OBJECT (inkscape), inkscape_signals[EXTERNAL_CHANGE], 0);
1132 /**
1133  * fixme: These need probably signals too
1134  */
1135 void
1136 inkscape_add_document (SPDocument *document)
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() ) {
1143     
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     }
1156 void
1157 inkscape_remove_document (SPDocument *document)
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;
1176 SPDesktop *
1177 inkscape_active_desktop (void)
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;
1189 SPDocument *
1190 inkscape_active_document (void)
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;
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;
1217 SPEventContext *
1218 inkscape_active_event_context (void)
1220     if (SP_ACTIVE_DESKTOP) {
1221         return sp_desktop_event_context (SP_ACTIVE_DESKTOP);
1222     }
1224     return NULL;
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)
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;
1347 void
1348 inkscape_refresh_display (Inkscape::Application *inkscape)
1350     for (GSList *l = inkscape->desktops; l != NULL; l = l->next) {
1351         (static_cast<Inkscape::UI::View::View*>(l->data))->requestRedraw();
1352     }
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)
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 ();
1372 gchar *
1373 homedir_path(const char *filename)
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);
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)
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);
1465 Inkscape::XML::Node *
1466 inkscape_get_menus (Inkscape::Application * inkscape)
1468     Inkscape::XML::Node *repr = inkscape->menus->root();
1469     g_assert (!(strcmp (repr->name(), "inkscape")));
1470     return repr->firstChild();
1473 void
1474 inkscape_get_all_desktops(std::list< SPDesktop* >& listbuf)
1476     for(GSList* l = inkscape->desktops; l != NULL; l = l->next) {
1477         listbuf.push_back(static_cast< SPDesktop* >(l->data));
1478     }
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 :