1 /* Dia -- an diagram creation/manipulation program
2  * Copyright (C) 1998 Alexander Larsson
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <config.h>
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <signal.h>
27 #include <locale.h>
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 
32 /* wants to be included early to avoid complains about setjmp.h */
33 #ifdef HAVE_LIBPNG
34 #include <png.h> /* just for the version stuff */
35 #endif
36 
37 #ifdef GNOME
38 #undef GTK_DISABLE_DEPRECATED
39 /* /usr/include/libgnomeui-2.0/libgnomeui/gnome-entry.h:58: error: expected specifier-qualifier-list before 'GtkCombo' */
40 #include <gnome.h>
41 #endif
42 
43 #include <gtk/gtk.h>
44 #include <gmodule.h>
45 
46 #ifdef HAVE_FREETYPE
47 #include <pango/pangoft2.h>
48 #endif
49 
50 #include <libxml/parser.h>
51 #include <libxml/xmlerror.h>
52 
53 #include <glib/gstdio.h>
54 
55 #include "intl.h"
56 #include "app_procs.h"
57 #include "object.h"
58 #include "commands.h"
59 #include "tool.h"
60 #include "interface.h"
61 #include "modify_tool.h"
62 #include "group.h"
63 #include "message.h"
64 #include "display.h"
65 #include "layer_dialog.h"
66 #include "load_save.h"
67 #include "preferences.h"
68 #include "dia_dirs.h"
69 #include "sheet.h"
70 #include "plug-ins.h"
71 #include "recent_files.h"
72 #include "authors.h"
73 #include "autosave.h"
74 #include "dynamic_refresh.h"
75 #include "persistence.h"
76 #include "sheets.h"
77 #include "exit_dialog.h"
78 #include "newgroup.h"
79 #include "dialib.h"
80 #include "diaerror.h"
81 
82 static gboolean
83 handle_initial_diagram(const char *input_file_name,
84 		       const char *export_file_name,
85 		       const char *export_file_format,
86 		       const char *size,
87 		       char *show_layers,
88 		       const char *outdir);
89 
90 static void create_user_dirs(void);
91 static PluginInitResult internal_plugin_init(PluginInfo *info);
92 static gboolean handle_all_diagrams(GSList *files, char *export_file_name,
93 				    char *export_file_format, char *size, char *show_layers,
94 				    const gchar *input_directory, const gchar *output_directory);
95 static void print_credits(gboolean credits);
96 
97 static gboolean dia_is_interactive = FALSE;
98 
99 #ifdef GNOME
100 
101 static void
session_die(gpointer client_data)102 session_die (gpointer client_data)
103 {
104   gtk_main_quit ();
105 }
106 
107 static int
save_state(GnomeClient * client,gint phase,GnomeRestartStyle save_style,gint shutdown,GnomeInteractStyle interact_style,gint fast,gpointer client_data)108 save_state (GnomeClient        *client,
109 	    gint                phase,
110 	    GnomeRestartStyle   save_style,
111 	    gint                shutdown,
112 	    GnomeInteractStyle  interact_style,
113 	    gint                fast,
114 	    gpointer            client_data)
115 {
116   gchar *argv[20];
117   gint i = 0;
118   GList *l;
119   Diagram *dia;
120 
121   argv[i++] = "dia";
122 
123   for(l = dia_open_diagrams(); l != NULL; l = g_list_next(l)) {
124     dia = (Diagram *)l->data;
125     if(!dia->unsaved) {
126       argv[i++] = dia->filename;
127     }
128   }
129 
130   gnome_client_set_restart_command (client, i, argv);
131   gnome_client_set_clone_command (client, i, argv);
132 
133   return TRUE;
134 }
135 #endif
136 
137 static char *
build_output_file_name(const char * infname,const char * format,const char * outdir)138 build_output_file_name(const char *infname, const char *format, const char *outdir)
139 {
140   const char *pe = strrchr(infname,'.');
141   const char *pp = strrchr(infname,G_DIR_SEPARATOR);
142   char *tmp;
143   if (!pp)
144     pp = infname;
145   else
146     pp += 1;
147   if (!pe)
148     return g_strconcat(outdir ? outdir : "", pp,".",format,NULL);
149 
150   tmp = g_malloc0(strlen(pp)+1+strlen(format)+1);
151   memcpy(tmp,pp,pe-pp);
152   strcat(tmp,".");
153   strcat(tmp,format);
154   if (outdir) {
155     char *ret = g_strconcat(outdir, G_DIR_SEPARATOR_S, tmp, NULL);
156     g_free(tmp);
157     return ret;
158   }
159   return tmp;
160 }
161 
162 /* Handle the string between commas. We have either of:
163  *
164  * 1. XX, the number XX
165  * 2. -XX, every number until XX
166  * 3. XX-, every number from XX until n_layers
167  * 4. XX-YY, every number between XX-YY
168  */
169 static void
show_layers_parse_numbers(DiagramData * diagdata,gboolean * visible_layers,gint n_layers,const char * str)170 show_layers_parse_numbers(DiagramData *diagdata, gboolean *visible_layers, gint n_layers, const char *str)
171 {
172   char *p;
173   unsigned long int low = 0;
174   unsigned long int high = n_layers;
175   unsigned long int i;
176 
177   if (str == NULL)
178     return;
179 
180   /* Case 2, starts with '-' */
181   if (*str == '-') {
182     str++;
183     low = 0;
184     high = strtoul(str, &p, 10)+1;
185     /* This must be a number (otherwise we would have called parse_name) */
186     g_assert(p != str);
187   }
188   else {
189     /* Case 1, 3 or 4 */
190     low = strtoul(str, &p, 10);
191     high = low+1; /* Assume case 1 */
192     g_assert(p != str);
193     if (*p == '-')
194       {
195 	/* Case 3 or 4 */
196 	str = p + 1;
197 	if (*str == '\0') /* Case 3 */
198 	  high = n_layers;
199 	else
200 	  {
201 	    high = strtoul(str, &p, 10)+1;
202 	    g_assert(p != str);
203 	  }
204       }
205   }
206 
207   if ( high <= low ) {
208     /* This is not an errror */
209     g_print(_("Warning: invalid layer range %lu - %lu\n"), low, high-1 );
210     return;
211   }
212   if (high > n_layers)
213     high = n_layers;
214 
215   /* Set the visible layers */
216   for ( i = low; i < high; i++ )
217     {
218       Layer *lay = (Layer *)g_ptr_array_index(diagdata->layers, i);
219 
220       if (visible_layers[i] == TRUE)
221 	g_print(_("Warning: Layer %lu (%s) selected more than once.\n"), i, lay->name);
222       visible_layers[i] = TRUE;
223     }
224 }
225 
226 static void
show_layers_parse_word(DiagramData * diagdata,gboolean * visible_layers,gint n_layers,const char * str)227 show_layers_parse_word(DiagramData *diagdata, gboolean *visible_layers, gint n_layers, const char *str)
228 {
229   GPtrArray *layers = diagdata->layers;
230   gboolean found = FALSE;
231 
232   /* Apply --show-layers=LAYER,LAYER,... switch. 13.3.2004 sampo@iki.fi */
233   if (layers) {
234     int len,k = 0;
235     Layer *lay;
236     char *p;
237     for (k = 0; k < layers->len; k++) {
238       lay = (Layer *)g_ptr_array_index(layers, k);
239 
240       if (lay->name) {
241 	len =  strlen(lay->name);
242 	if ((p = strstr(str, lay->name)) != NULL) {
243 	  if (((p == str) || (p[-1] == ','))    /* zap false positives */
244 	      && ((p[len] == 0) || (p[len] == ','))){
245 	    found = TRUE;
246 	    if (visible_layers[k] == TRUE)
247 	      g_print(_("Warning: Layer %d (%s) selected more than once.\n"), k, lay->name);
248 	    visible_layers[k] = TRUE;
249 	  }
250 	}
251       }
252     }
253   }
254 
255   if (found == FALSE)
256     g_print(_("Warning: There is no layer named %s\n"), str);
257 }
258 
259 static void
show_layers_parse_string(DiagramData * diagdata,gboolean * visible_layers,gint n_layers,const char * str)260 show_layers_parse_string(DiagramData *diagdata, gboolean *visible_layers, gint n_layers, const char *str)
261 {
262   gchar **pp;
263   int i;
264 
265   pp = g_strsplit(str, ",", 100);
266 
267   for (i = 0; pp[i]; i++) {
268     gchar *p = pp[i];
269 
270     /* Skip the empty string */
271     if (strlen(p) == 0)
272       continue;
273 
274     /* If the string is only numbers and '-' chars, it is parsed as a
275      * number range. Otherwise it is parsed as a layer name.
276      */
277     if (strlen(p) != strspn(p, "0123456789-") )
278       show_layers_parse_word(diagdata, visible_layers, n_layers, p);
279     else
280       show_layers_parse_numbers(diagdata, visible_layers, n_layers, p);
281   }
282 
283   g_strfreev(pp);
284 }
285 
286 
287 static void
handle_show_layers(DiagramData * diagdata,const char * show_layers)288 handle_show_layers(DiagramData *diagdata, const char *show_layers)
289 {
290   gboolean *visible_layers;
291   Layer *layer;
292   int i;
293 
294   visible_layers = g_malloc(diagdata->layers->len * sizeof(gboolean));
295   /* Assume all layers are non-visible */
296   for (i=0;i<diagdata->layers->len;i++)
297     visible_layers[i] = FALSE;
298 
299   /* Split the layer-range by commas */
300   show_layers_parse_string(diagdata, visible_layers, diagdata->layers->len,
301 			   show_layers);
302 
303   /* Set the visibility of the layers */
304   for (i=0;i<diagdata->layers->len;i++) {
305     layer = g_ptr_array_index(diagdata->layers, i);
306 
307     if (visible_layers[i] == TRUE)
308       layer->visible = TRUE;
309     else
310       layer->visible = FALSE;
311   }
312   g_free(visible_layers);
313 }
314 
315 
316 const char *argv0 = NULL;
317 
318 /** Convert infname to outfname, using input filter inf and export filter
319  * ef.  If either is null, try to guess them.
320  * size might be NULL.
321  */
322 static gboolean
do_convert(const char * infname,const char * outfname,DiaExportFilter * ef,const char * size,char * show_layers)323 do_convert(const char *infname,
324 	   const char *outfname, DiaExportFilter *ef,
325 	   const char *size,
326 	   char *show_layers)
327 {
328   DiaImportFilter *inf;
329   DiagramData *diagdata = NULL;
330 
331   inf = filter_guess_import_filter(infname);
332   if (!inf)
333     inf = &dia_import_filter;
334 
335   if (ef == NULL) {
336     ef = filter_guess_export_filter(outfname);
337     if (!ef) {
338       g_critical(_("%s error: don't know how to export into %s\n"),
339 	      argv0,outfname);
340       exit(1);
341     }
342   }
343 
344   dia_is_interactive = FALSE;
345 
346   if (0==strcmp(infname,outfname)) {
347     g_critical(_("%s error: input and output file name is identical: %s"),
348             argv0, infname);
349     exit(1);
350   }
351 
352   diagdata = g_object_new (DIA_TYPE_DIAGRAM_DATA, NULL);
353 
354   if (!inf->import_func(infname,diagdata,inf->user_data)) {
355     g_critical(_("%s error: need valid input file %s\n"),
356             argv0, infname);
357     exit(1);
358   }
359 
360   /* Apply --show-layers */
361   if (show_layers)
362     handle_show_layers(diagdata, show_layers);
363 
364   /* recalculate before export */
365   data_update_extents(diagdata);
366 
367   /* Do our best in providing the size to the filter, but don't abuse user_data
368    * too much for it. It _must not_ be changed after initialization and there
369    * are quite some filter selecting their output format by it. --hb
370    */
371   if (size) {
372     if (ef == filter_get_by_name ("png-libart")) /* the warning we get is appropriate, don't cast */
373       ef->export_func(diagdata, outfname, infname, size);
374     else {
375       g_warning ("--size parameter unsupported for %s filter",
376                  ef->unique_name ? ef->unique_name : "selected");
377       ef->export_func(diagdata, outfname, infname, ef->user_data);
378     }
379   }
380   else
381     ef->export_func(diagdata, outfname, infname, ef->user_data);
382   /* if (!quiet) */ fprintf(stdout,
383                       _("%s --> %s\n"),
384                         infname,outfname);
385   g_object_unref(diagdata);
386   return TRUE;
387 }
388 
389 void debug_break(void); /* shut gcc up */
390 int debug_break_dont_optimize = 1;
391 void
debug_break(void)392 debug_break(void)
393 {
394   if (debug_break_dont_optimize > 0)
395     debug_break_dont_optimize -= 1;
396 }
397 
398 static void
dump_dependencies(void)399 dump_dependencies(void)
400 {
401 #ifdef __GNUC__
402   g_print ("Compiler: GCC " __VERSION__ "\n");
403 #elif defined _MSC_VER
404   g_print ("Compiler: MSC %d\n", _MSC_VER);
405 #else
406   g_print ("Compiler: unknown\n");
407 #endif
408   /* some flags/defines which may be interesting */
409   g_print ("  with : "
410 #ifdef G_THREADS_ENABLED
411   "threads "
412 #endif
413 #ifdef HAVE_CAIRO
414   "cairo "
415 #endif
416 #ifdef GNOME
417   "gnome "
418 #endif
419 #ifdef HAVE_GNOMEPRINT
420   "gnomeprint "
421 #endif
422 #ifdef HAVE_LIBART
423   "libart "
424 #endif
425 #ifdef HAVE_PANGOCAIRO
426   "pangocairo "
427 #endif
428   "\n");
429 
430   /* print out all those dependies, both compile and runtime if possible
431    * Note: this is not meant to be complete but does only include libaries
432    * which may or have cause(d) us trouble in some versions
433    */
434   g_print ("Library versions (at compile time)\n");
435 #ifdef HAVE_LIBPNG
436   g_print ("libpng  : %s (%s)\n", png_get_header_ver(NULL), PNG_LIBPNG_VER_STRING);
437 #endif
438 #ifdef HAVE_FREETYPE
439   {
440     FT_Library ft_library;
441     FT_Int     ft_major_version;
442     FT_Int     ft_minor_version;
443     FT_Int     ft_micro_version;
444 
445     if (FT_Init_FreeType (&ft_library) == 0) {
446       FT_Library_Version (ft_library,
447                           &ft_major_version,
448                           &ft_minor_version,
449                           &ft_micro_version);
450 
451       g_print ("freetype: %d.%d.%d\n", ft_major_version, ft_minor_version, ft_micro_version);
452       FT_Done_FreeType (ft_library);
453     }
454     else
455       g_print ("freetype: ?.?.?\n");
456   }
457 #endif
458   {
459     const gchar* libxml_rt_version = "?";
460 #if 0
461     /* this is stupid, does not compile on Linux:
462      * app_procs.c:504: error: expected identifier before '(' token
463      *
464      * In fact libxml2 has different ABI for LIBXML_THREAD_ENABLED, this code only compiles without
465      * threads enabled, but apparently it does only work when theay are.
466      */
467     xmlInitParser();
468     if (xmlGetGlobalState())
469       libxml_rt_version = xmlGetGlobalState()->xmlParserVersion;
470 #endif
471     libxml_rt_version = xmlParserVersion;
472     if (atoi(libxml_rt_version))
473       g_print ("libxml  : %d.%d.%d (%s)\n",
474                atoi(libxml_rt_version) / 10000, atoi(libxml_rt_version) / 100 % 100, atoi(libxml_rt_version) % 100,
475 	       LIBXML_DOTTED_VERSION);
476     else /* may include "extra" */
477       g_print ("libxml  : %s (%s)\n", libxml_rt_version ? libxml_rt_version : "??", LIBXML_DOTTED_VERSION);
478   }
479   g_print ("glib    : %d.%d.%d (%d.%d.%d)\n",
480            glib_major_version, glib_minor_version, glib_micro_version,
481            GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
482 #ifdef PANGO_VERSION_ENCODE
483   g_print ("pango   : %s (%d.%d.%d)\n", pango_version_string(), PANGO_VERSION_MAJOR, PANGO_VERSION_MINOR, PANGO_VERSION_MICRO);
484 #else
485   g_print ("pango   : version not available (>= 1.14.x)\n"); /* Pango did not provide such */
486 #endif
487 #if HAVE_CAIRO
488 #  ifdef CAIRO_VERSION_STRING
489   g_print ("cairo   : %s (%s)\n", cairo_version_string(), CAIRO_VERSION_STRING);
490 #  else
491   g_print ("cairo   : %s (%d.%d.%d)\n", cairo_version_string(), CAIRO_VERSION_MAJOR, CAIRO_VERSION_MINOR, CAIRO_VERSION_MICRO);
492 #  endif
493 #endif
494 #if 0
495   {
496     gchar  linkedname[1024];
497     gint   len = 0;
498 
499     /* relying on PREFIX is wrong */
500     if ((len = readlink (PREFIX "/lib/libpango-1.0.so", linkedname, 1023)) > 0) {
501       /* man 2 readlink : does not append a  NUL  character */
502       linkedname[len] = '\0';
503       g_print ("%s/%s\n", PREFIX, linkedname);
504     }
505   }
506 #endif
507   g_print ("gtk+    : %d.%d.%d (%d.%d.%d)\n",
508            gtk_major_version, gtk_minor_version, gtk_micro_version,
509            GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION);
510 
511 #if 0
512   /* we could read $PREFIX/share/gnome-about/gnome-version.xml
513    * but is it worth the effort ? */
514   g_print ("gnome   : %d.%d.%d (%d.%d.%d)\n"
515            gnome_major_version, gnome_minor_version, gnome_micro_version,
516            GNOME_MAJOR_VERSION, GNOME_MINOR_VERSION, GNOME_MICRO_VERSION);
517 #endif
518 }
519 
520 gboolean
app_is_interactive(void)521 app_is_interactive(void)
522 {
523   return dia_is_interactive;
524 }
525 
526 /** Handle loading of diagrams given on command line, including conversions.
527  * Returns TRUE if any automatic conversions were performed.
528  * Note to future hackers:  'size' is currently the only argument that can be
529  * sent to exporters.  If more arguments are desired, please don't just add
530  * even more arguments, but create a more general system.
531  */
532 static gboolean
handle_initial_diagram(const char * in_file_name,const char * out_file_name,const char * export_file_format,const char * size,char * show_layers,const char * outdir)533 handle_initial_diagram(const char *in_file_name,
534 		       const char *out_file_name,
535 		       const char *export_file_format,
536 		       const char *size,
537 		       char* show_layers,
538 		       const char *outdir) {
539   DDisplay *ddisp = NULL;
540   Diagram *diagram = NULL;
541   gboolean made_conversions = FALSE;
542 
543   if (export_file_format) {
544     char *export_file_name = NULL;
545     DiaExportFilter *ef = NULL;
546 
547     /* First try guessing based on extension */
548     export_file_name = build_output_file_name(in_file_name, export_file_format, outdir);
549 
550     /* to make the --size hack even uglier but work again for the only filter supporting it */
551     if (   size && strcmp(export_file_format, "png") == 0)
552       ef = filter_get_by_name ("png-libart");
553     if (!ef)
554       ef = filter_guess_export_filter(export_file_name);
555     if (ef == NULL) {
556       ef = filter_get_by_name(export_file_format);
557       if (ef == NULL) {
558 	g_critical(_("Can't find output format/filter %s\n"), export_file_format);
559 	return FALSE;
560       }
561       g_free (export_file_name);
562       export_file_name = build_output_file_name(in_file_name, ef->extensions[0], outdir);
563     }
564     made_conversions |= do_convert(in_file_name,
565       (out_file_name != NULL?out_file_name:export_file_name),
566 				   ef, size, show_layers);
567     g_free(export_file_name);
568   } else if (out_file_name) {
569     DiaExportFilter *ef = NULL;
570 
571     /* if this looks like an ugly hack to you, agreed ;)  */
572     if (size && strstr(out_file_name, ".png"))
573       ef = filter_get_by_name ("png-libart");
574 
575     made_conversions |= do_convert(in_file_name, out_file_name, ef,
576 				   size, show_layers);
577   } else {
578     if (g_file_test(in_file_name, G_FILE_TEST_EXISTS)) {
579       diagram = diagram_load (in_file_name, NULL);
580     } else {
581       diagram = new_diagram (in_file_name);
582     }
583 
584     if (diagram != NULL) {
585       diagram_update_extents(diagram);
586       if (app_is_interactive()) {
587 	layer_dialog_set_diagram(diagram);
588         /* the display initial diagram holds two references */
589 	ddisp = new_display(diagram);
590       } else {
591         g_object_unref(diagram);
592       }
593     }
594   }
595   return made_conversions;
596 }
597 
598 #ifdef HAVE_FREETYPE
599 /* Translators:  This is an option, not to be translated */
600 #define EPS_PANGO "eps-pango, "
601 #else
602 #define EPS_PANGO ""
603 #endif
604 
605 #ifdef G_OS_WIN32
606 /* Translators:  This is an option, not to be translated */
607 #define WMF "wmf, "
608 #else
609 #define WMF ""
610 #endif
611 
612 static const gchar *input_directory = NULL;
613 static const gchar *output_directory = NULL;
614 
615 static gboolean
_check_option_input_directory(const gchar * option_name,const gchar * value,gpointer data,GError ** error)616 _check_option_input_directory (const gchar    *option_name,
617 			       const gchar    *value,
618 			       gpointer        data,
619 			       GError        **error)
620 {
621   gchar *directory = g_filename_from_utf8 (value, -1, NULL, NULL, NULL);
622 
623   if (g_file_test (directory, G_FILE_TEST_IS_DIR)) {
624     input_directory = directory;
625     return TRUE;
626   }
627   g_set_error (error, DIA_ERROR, DIA_ERROR_DIRECTORY,
628                _("Input-directory '%s' must exist!\n"), directory);
629   g_free (directory);
630   return FALSE;
631 }
632 static gboolean
_check_option_output_directory(const gchar * option_name,const gchar * value,gpointer data,GError ** error)633 _check_option_output_directory (const gchar    *option_name,
634 			        const gchar    *value,
635 			        gpointer        data,
636 			        GError        **error)
637 {
638   gchar *directory = g_filename_from_utf8 (value, -1, NULL, NULL, NULL);
639 
640   if (g_file_test (directory, G_FILE_TEST_IS_DIR)) {
641     output_directory = directory;
642     return TRUE;
643   }
644   g_set_error (error, DIA_ERROR, DIA_ERROR_DIRECTORY,
645                _("Output-directory '%s' must exist!\n"), directory);
646   g_free (directory);
647   return FALSE;
648 }
649 
650 static void
_setup_textdomains(void)651 _setup_textdomains (void)
652 {
653 #ifdef G_OS_WIN32
654   /* calculate runtime directory */
655   {
656     gchar* localedir = dia_get_lib_directory ("locale");
657 
658     bindtextdomain(GETTEXT_PACKAGE, localedir);
659     g_free (localedir);
660   }
661 #else
662   const gchar *localedir;
663 
664   localedir = g_getenv("DIA_LOCALE_PATH");
665   if(localedir != NULL) {
666     bindtextdomain(GETTEXT_PACKAGE, localedir);
667   } else {
668     bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
669   }
670 #endif
671 
672 #if defined ENABLE_NLS && defined HAVE_BIND_TEXTDOMAIN_CODESET
673   bind_textdomain_codeset(GETTEXT_PACKAGE,"UTF-8");
674 #endif
675   textdomain(GETTEXT_PACKAGE);
676 }
677 
678 void
app_init(int argc,char ** argv)679 app_init (int argc, char **argv)
680 {
681   static gboolean nosplash = FALSE;
682   static gboolean nonew = FALSE;
683   static gboolean use_integrated_ui = FALSE;
684   static gboolean credits = FALSE;
685   static gboolean version = FALSE;
686   static gboolean verbose = FALSE;
687   static gboolean log_to_stderr = FALSE;
688 #ifdef GNOME
689   GnomeClient *client;
690 #endif
691   static char *export_file_name = NULL;
692   static char *export_file_format = NULL;
693   static char *size = NULL;
694   static char *show_layers = NULL;
695   gboolean made_conversions = FALSE;
696   GSList *files = NULL;
697   static const gchar **filenames = NULL;
698   int i = 0;
699 
700   gchar *export_format_string =
701      /* Translators:  The argument is a list of options, not to be translated */
702     g_strdup_printf(_("Select the filter/format out of: %s"),
703 		    "cgm, dia, dxf, eps, eps-builtin, " EPS_PANGO
704 		    "fig, mp, plt, hpgl, png ("
705 #  if defined(HAVE_LIBPNG) && defined(HAVE_LIBART)
706 		    "png-libart, "
707 #  endif
708 #  ifdef HAVE_CAIRO
709 		    "cairo-png, cairo-alpha-png, "
710 #  endif
711 		    /* we always have pixbuf but don't know exactly all it's *few* save formats */
712 		    "pixbuf-png), jpg, "
713 		    "shape, svg, tex (pgf-tex, pstricks-tex), " WMF
714 		    "wpg");
715 
716   GOptionContext *context = NULL;
717   static GOptionEntry options[] =
718   {
719     {"export", 'e', 0, G_OPTION_ARG_FILENAME, NULL /* &export_file_name */,
720      N_("Export loaded file and exit"), N_("OUTPUT")},
721     {"filter",'t', 0, G_OPTION_ARG_STRING, NULL /* &export_file_format */,
722      NULL /* &export_format_string */, N_("TYPE") },
723     {"size", 's', 0, G_OPTION_ARG_STRING, NULL,
724      N_("Export graphics size"), N_("WxH")},
725     {"show-layers", 'L', 0, G_OPTION_ARG_STRING, NULL,
726      N_("Show only specified layers (e.g. when exporting). Can be either the layer name or a range of layer numbers (X-Y)"),
727      N_("LAYER,LAYER,...")},
728     {"nosplash", 'n', 0, G_OPTION_ARG_NONE, &nosplash,
729      N_("Don't show the splash screen"), NULL },
730     {"nonew", 'n', 0, G_OPTION_ARG_NONE, &nonew,
731      N_("Don't create empty diagram"), NULL },
732     {"integrated", '\0', 0, G_OPTION_ARG_NONE, &use_integrated_ui,
733      N_("Start integrated user interface (diagrams in tabs)"), NULL },
734     {"log-to-stderr", 'l', 0, G_OPTION_ARG_NONE, &log_to_stderr,
735      N_("Send error messages to stderr instead of showing dialogs."), NULL },
736     {"input-directory", 'I', 0, G_OPTION_ARG_CALLBACK, _check_option_input_directory,
737      N_("Directory containing input files"), N_("DIRECTORY")},
738     {"output-directory", 'O', 0, G_OPTION_ARG_CALLBACK, _check_option_output_directory,
739      N_("Directory containing output files"), N_("DIRECTORY")},
740     {"credits", 'c', 0, G_OPTION_ARG_NONE, &credits,
741      N_("Display credits list and exit"), NULL },
742     {"verbose", 0, 0, G_OPTION_ARG_NONE, &verbose,
743      N_("Generate verbose output"), NULL },
744     {"version", 'v', 0, G_OPTION_ARG_NONE, &version,
745      N_("Display version and exit"), NULL },
746     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, NULL /* &filenames */,
747       NULL, NULL },
748     { NULL }
749   };
750 
751   /* for users of app_init() the default is interactive */
752   dia_is_interactive = TRUE;
753 
754   options[0].arg_data = &export_file_name;
755   options[1].arg_data = &export_file_format;
756   options[1].description = export_format_string;
757   options[2].arg_data = &size;
758   options[3].arg_data = &show_layers;
759   g_assert (strcmp (options[13].long_name, G_OPTION_REMAINING) == 0);
760   options[13].arg_data = (void*)&filenames;
761 
762   argv0 = (argc > 0) ? argv[0] : "(none)";
763 
764   gtk_set_locale();
765   setlocale(LC_NUMERIC, "C");
766   _setup_textdomains ();
767 
768   context = g_option_context_new(_("[FILE...]"));
769   g_option_context_add_main_entries (context, options, GETTEXT_PACKAGE);
770   g_option_context_add_group (context, gtk_get_option_group (FALSE));
771 
772   if (argv) {
773     GError *error = NULL;
774 
775     if (!g_option_context_parse (context, &argc, &argv, &error)) {
776       if (error) { /* IMO !error here is a bug upstream, triggered e.g. with --gdk-debug=updates */
777 	g_print ("%s", error->message);
778 	g_error_free (error);
779       } else {
780 	g_print (_("Invalid option?"));
781       }
782 
783       g_option_context_free(context);
784       exit(1);
785     }
786     /* second level check of command line options, existance of input files etc. */
787     if (filenames) {
788       while (filenames[i] != NULL) {
789 	gchar *filename;
790 	gchar *testpath;
791 
792 	if (g_str_has_prefix (filenames[i], "file://")) {
793 	  filename = g_filename_from_uri (filenames[i], NULL, NULL);
794 	  if (!g_utf8_validate(filename, -1, NULL)) {
795 	    gchar *tfn = filename;
796 	    filename = g_filename_to_utf8(filename, -1, NULL, NULL, NULL);
797 	    g_free(tfn);
798 	  }
799 	} else
800 	  filename = g_filename_to_utf8 (filenames[i], -1, NULL, NULL, NULL);
801 
802 	if (!filename) {
803 	  g_print (_("Filename conversion failed: %s\n"), filenames[i]);
804 	  continue;
805 	}
806 
807 	if (g_path_is_absolute(filename))
808 	  testpath = filename;
809 	else
810 	  testpath = g_build_filename(input_directory ? input_directory : ".", filename, NULL);
811 
812 	/* we still have a problem here, if GLib's file name encoding would not be utf-8 */
813 	if (g_file_test (testpath, G_FILE_TEST_IS_REGULAR))
814 	  files = g_slist_append(files, filename);
815 	else {
816 	  g_print (_("Missing input: %s\n"), filename);
817 	  g_free (filename);
818 	}
819 	if (filename != testpath)
820 	  g_free (testpath);
821 	++i;
822       }
823     }
824     /* given some files to output, we are not starting up the UI */
825     if (export_file_name || export_file_format || size)
826       dia_is_interactive = FALSE;
827 
828   }
829 
830   if (argv && dia_is_interactive && !version) {
831 #ifdef GNOME
832     GnomeProgram *program =
833       gnome_program_init (PACKAGE, VERSION, LIBGNOMEUI_MODULE,
834 			  argc, argv,
835 			  /* haven't found a quick way to pass GOption here */
836 			  GNOME_PARAM_GOPTION_CONTEXT, context,
837 			  GNOME_PROGRAM_STANDARD_PROPERTIES,
838 			  GNOME_PARAM_NONE);
839     client = gnome_master_client();
840     if(client == NULL) {
841       g_warning(_("Can't connect to session manager!\n"));
842     }
843     else {
844       g_signal_connect(GTK_OBJECT (client), "save_yourself",
845 		       G_CALLBACK (save_state), NULL);
846       g_signal_connect(GTK_OBJECT (client), "die",
847 		       G_CALLBACK (session_die), NULL);
848     }
849 
850     /* This smaller icon is 48x48, standard Gnome size */
851     /* gnome_window_icon_set_default_from_file (GNOME_ICONDIR"/dia_gnome_icon.png");*/
852 
853 #else
854 #  ifdef G_THREADS_ENABLED
855     g_thread_init (NULL);
856 #  endif
857     gtk_init(&argc, &argv);
858 #endif
859   }
860   else {
861 #ifdef G_THREADS_ENABLED
862     g_thread_init (NULL);
863 #endif
864     g_type_init();
865     /*
866      * On Windows there is no command line without display so that gtk_init is harmless.
867      * On X11 we need gtk_init_check() to avoid exit() just because there is no display
868      * running outside of X11.
869      */
870     if (!gtk_init_check(&argc, &argv))
871       dia_log_message ("Running without display");
872   }
873 
874   /* done with option parsing, don't leak */
875   g_free(export_format_string);
876 
877   if (version) {
878 #if (defined __TIME__) && (defined __DATE__)
879     /* TRANSLATOR: 2nd and 3rd %s are time and date respectively. */
880     printf(g_locale_from_utf8(_("Dia version %s, compiled %s %s\n"), -1, NULL, NULL, NULL), VERSION, __TIME__, __DATE__);
881 #else
882     printf(g_locale_from_utf8(_("Dia version %s\n"), -1, NULL, NULL, NULL), VERSION);
883 #endif
884     if (verbose)
885       dump_dependencies();
886     exit(0);
887   }
888 
889   if (!dia_is_interactive)
890     log_to_stderr = TRUE;
891 
892   libdia_init (   (dia_is_interactive ? DIA_INTERACTIVE : 0)
893 	       | (log_to_stderr ? DIA_MESSAGE_STDERR : 0)
894 	       | (verbose ? DIA_VERBOSE : 0) );
895 
896   print_credits(credits);
897 
898   if (dia_is_interactive) {
899     create_user_dirs();
900 
901     if (!nosplash)
902       app_splash_init("");
903 
904     /* Init cursors: */
905     default_cursor = gdk_cursor_new(GDK_LEFT_PTR);
906     ddisplay_set_all_cursor(default_cursor);
907   }
908 
909   dia_register_plugins();
910   dia_register_builtin_plugin(internal_plugin_init);
911 
912   load_all_sheets();     /* new mechanism */
913 
914   dia_log_message ("object defaults");
915   dia_object_defaults_load (NULL, TRUE /* prefs.object_defaults_create_lazy */);
916 
917   debug_break();
918 
919   if (object_get_type("Standard - Box") == NULL) {
920     message_error(_("Couldn't find standard objects when looking for "
921 		  "object-libs; exiting...\n"));
922     g_critical( _("Couldn't find standard objects when looking for "
923 	    "object-libs in '%s'; exiting...\n"), dia_get_lib_directory("dia"));
924     exit(1);
925   }
926 
927   persistence_load();
928 
929   /** Must load prefs after persistence */
930   prefs_init();
931 
932   if (dia_is_interactive) {
933 
934     /* further initialization *before* reading files */
935     active_tool = create_modify_tool();
936 
937     dia_log_message ("ui creation");
938     if (use_integrated_ui) {
939       create_integrated_ui();
940     } else {
941       create_toolbox();
942       /* for the integrated ui case it is integrated */
943       persistence_register_window_create("layer_window",
944 				         (NullaryFunc*)&create_layer_dialog);
945     }
946 
947     /*fill recent file menu */
948     recent_file_history_init();
949 
950     /* Set up autosave to check every 5 minutes */
951 #if GLIB_CHECK_VERSION(2,14,0)
952     g_timeout_add_seconds(5*60, autosave_check_autosave, NULL);
953 #else
954     g_timeout_add(5*60*1000, autosave_check_autosave, NULL);
955 #endif
956 
957     /* Create Diagram Tree Window */
958     create_tree_window();
959 
960     persistence_register_window_create("sheets_main_dialog",
961 				       (NullaryFunc*)&sheets_dialog_create);
962 
963     /* In current setup, we can't find the autosaved files. */
964     /*autosave_restore_documents();*/
965 
966   }
967 
968   dia_log_message ("diagrams");
969   made_conversions = handle_all_diagrams(files, export_file_name,
970 					 export_file_format, size, show_layers,
971 					 input_directory, output_directory);
972 
973   if (dia_is_interactive && files == NULL && !nonew) {
974     if (use_integrated_ui)
975     {
976       GList * list;
977 
978       file_new_callback(NULL);
979       list = dia_open_diagrams();
980       if (list)
981       {
982         Diagram * diagram = list->data;
983         diagram_update_extents(diagram);
984         diagram->is_default = TRUE;
985       }
986     }
987     else
988     {
989       gchar *filename = g_filename_from_utf8(_("Diagram1.dia"), -1, NULL, NULL, NULL);
990       Diagram *diagram = new_diagram (filename);
991       g_free(filename);
992 
993       if (diagram != NULL) {
994         diagram_update_extents(diagram);
995         diagram->is_default = TRUE;
996         /* I think this is done in diagram_init() with a call to
997          * layer_dialog_update_diagram_list() */
998         layer_dialog_set_diagram(diagram);
999         new_display(diagram);
1000       }
1001     }
1002   }
1003   g_slist_free(files);
1004   if (made_conversions) exit(0);
1005 
1006   dynobj_refresh_init();
1007   dia_log_message ("initialized");
1008 }
1009 
1010 gboolean
app_exit(void)1011 app_exit(void)
1012 {
1013   GList *list;
1014   GSList *slist;
1015 
1016   /*
1017    * The following "solves" a crash related to a second call of app_exit,
1018    * after gtk_main_quit was called. It may be a win32 gtk-1.3.x bug only
1019    * but the check shouldn't hurt on *ix either.          --hb
1020    */
1021   static gboolean app_exit_once = FALSE;
1022 
1023   if (app_exit_once) {
1024     g_error(_("This shouldn't happen.  Please file a bug report at bugzilla.gnome.org\n"
1025 	      "describing how you can cause this message to appear.\n"));
1026     return FALSE;
1027   }
1028 
1029   if (diagram_modified_exists()) {
1030     if (is_integrated_ui ())
1031     {
1032       GtkWidget                *dialog;
1033       int                       result;
1034       exit_dialog_item_array_t *items  = NULL;
1035       GList *                   list;
1036       Diagram *                 diagram;
1037 
1038       dialog = exit_dialog_make (GTK_WINDOW (interface_get_toolbox_shell ()),
1039                                 _("Exiting Dia"));
1040 
1041       list = dia_open_diagrams();
1042       while (list)
1043       {
1044         diagram = list->data;
1045 
1046         if (diagram_is_modified (diagram))
1047         {
1048           const gchar * name = diagram_get_name (diagram);
1049           const gchar * path = diagram->filename;
1050           exit_dialog_add_item (dialog, name, path, diagram);
1051         }
1052 
1053         list = g_list_next (list);
1054       }
1055 
1056       result = exit_dialog_run (dialog, &items);
1057 
1058       gtk_widget_destroy (dialog);
1059 
1060       if (result == EXIT_DIALOG_EXIT_CANCEL)
1061       {
1062         return FALSE;
1063       }
1064       else if (result == EXIT_DIALOG_EXIT_SAVE_SELECTED)
1065       {
1066         int i;
1067         for (i = 0 ; i < items->array_size ; i++)
1068         {
1069           gchar *filename;
1070 
1071           diagram  = items->array[i].data;
1072           filename = g_filename_from_utf8 (diagram->filename, -1, NULL, NULL, NULL);
1073           diagram_update_extents (diagram);
1074           if (!diagram_save (diagram, filename)) {
1075             exit_dialog_free_items (items);
1076             return FALSE;
1077 	      }
1078           g_free (filename);
1079         }
1080         exit_dialog_free_items (items);
1081       }
1082       else if (result == EXIT_DIALOG_EXIT_NO_SAVE)
1083       {
1084         list = dia_open_diagrams();
1085         while (list) {
1086           diagram = list->data;
1087 
1088 	  /* slight hack: don't ask again */
1089           diagram_set_modified (diagram, FALSE);
1090 	  undo_clear(diagram->undo);
1091           list = g_list_next (list);
1092 	}
1093       }
1094     }
1095     else
1096     {
1097     GtkWidget *dialog;
1098     GtkWidget *button;
1099     dialog = gtk_message_dialog_new(
1100 	       NULL, GTK_DIALOG_MODAL,
1101                GTK_MESSAGE_QUESTION,
1102                GTK_BUTTONS_NONE, /* no standard buttons */
1103 	       _("Quitting without saving modified diagrams"));
1104     gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
1105 		 _("Modified diagrams exist. "
1106 		 "Are you sure you want to quit Dia "
1107  		 "without saving them?"));
1108 
1109     gtk_window_set_title (GTK_WINDOW(dialog), _("Quit Dia"));
1110 
1111     button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1112     gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_CANCEL);
1113     GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1114     gtk_dialog_set_default_response (GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
1115 
1116     button = gtk_button_new_from_stock (GTK_STOCK_QUIT);
1117     gtk_dialog_add_action_widget (GTK_DIALOG(dialog), button, GTK_RESPONSE_OK);
1118 
1119     gtk_widget_show_all (dialog);
1120 
1121     if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) {
1122       gtk_widget_destroy(dialog);
1123       return FALSE;
1124     }
1125     gtk_widget_destroy(dialog);
1126     }
1127   }
1128   prefs_save();
1129 
1130   persistence_save();
1131 
1132   dynobj_refresh_finish();
1133 
1134   dia_object_defaults_save (NULL);
1135 
1136   /* Free loads of stuff (toolbox) */
1137 
1138   list = dia_open_diagrams();
1139   while (list!=NULL) {
1140     Diagram *dia = (Diagram *)list->data;
1141     list = g_list_next(list);
1142 
1143     slist = dia->displays;
1144     while (slist!=NULL) {
1145       DDisplay *ddisp = (DDisplay *)slist->data;
1146       slist = g_slist_next(slist);
1147 
1148       gtk_widget_destroy(ddisp->shell);
1149     }
1150     /* The diagram is freed when the last display is destroyed */
1151   }
1152 
1153   /* save pluginrc */
1154   if (dia_is_interactive)
1155     dia_pluginrc_write();
1156 
1157   gtk_main_quit();
1158 
1159   /* This printf seems to prevent a race condition with unrefs. */
1160   /* Yuck.  -Lars */
1161   /* Trying to live without it. -Lars 10/8/07*/
1162   /* g_print(_("Thank you for using Dia.\n")); */
1163   app_exit_once = TRUE;
1164 
1165   return TRUE;
1166 }
1167 
create_user_dirs(void)1168 static void create_user_dirs(void)
1169 {
1170   gchar *dir, *subdir;
1171 
1172 #ifdef G_OS_WIN32
1173   /* not necessary to quit the program with g_error, everywhere else
1174    * dia_config_filename appears to be used. Spit out a warning ...
1175    */
1176   if (!g_get_home_dir())
1177   {
1178     g_warning(_("Could not create per-user Dia config directory"));
1179     return; /* ... and return. Probably removes my one and only FAQ. --HB */
1180   }
1181 #endif
1182   dir = g_strconcat(g_get_home_dir(), G_DIR_SEPARATOR_S ".dia", NULL);
1183   if (g_mkdir(dir, 0755) && errno != EEXIST) {
1184 #ifndef G_OS_WIN32
1185     g_critical(_("Could not create per-user Dia config directory"));
1186     exit(1);
1187 #else /* HB: it this really a reason to exit the program on *nix ? */
1188     g_warning(_("Could not create per-user Dia config directory. Please make "
1189         "sure that the environment variable HOME points to an existing directory."));
1190 #endif
1191   }
1192 
1193   /* it is no big deal if these directories can't be created */
1194   subdir = g_strconcat(dir, G_DIR_SEPARATOR_S "objects", NULL);
1195   g_mkdir(subdir, 0755);
1196   g_free(subdir);
1197   subdir = g_strconcat(dir, G_DIR_SEPARATOR_S "shapes", NULL);
1198   g_mkdir(subdir, 0755);
1199   g_free(subdir);
1200   subdir = g_strconcat(dir, G_DIR_SEPARATOR_S "sheets", NULL);
1201   g_mkdir(subdir, 0755);
1202   g_free(subdir);
1203 
1204   g_free(dir);
1205 }
1206 
1207 static PluginInitResult
internal_plugin_init(PluginInfo * info)1208 internal_plugin_init(PluginInfo *info)
1209 {
1210   if (!dia_plugin_info_init(info, "Internal",
1211 			    _("Objects and filters internal to dia"),
1212 			    NULL, NULL))
1213     return DIA_PLUGIN_INIT_ERROR;
1214 
1215   /* register the group object type */
1216   object_register_type(&group_type);
1217 #ifdef USE_NEWGROUP
1218   object_register_type(&newgroup_type);
1219 #endif
1220 
1221   /* register import filters */
1222   filter_register_import(&dia_import_filter);
1223 
1224   /* register export filters */
1225   /* Standard Dia format */
1226   filter_register_export(&dia_export_filter);
1227 
1228   return DIA_PLUGIN_INIT_OK;
1229 }
1230 
1231 static gboolean
handle_all_diagrams(GSList * files,char * export_file_name,char * export_file_format,char * size,char * show_layers,const gchar * input_directory,const gchar * output_directory)1232 handle_all_diagrams(GSList *files, char *export_file_name,
1233 		    char *export_file_format, char *size, char *show_layers,
1234 		    const gchar *input_directory, const gchar *output_directory)
1235 {
1236   GSList *node = NULL;
1237   gboolean made_conversions = FALSE;
1238 
1239   for (node = files; node; node = node->next) {
1240     gchar *inpath = input_directory ? g_build_filename(input_directory, node->data, NULL) : node->data;
1241     made_conversions |=
1242       handle_initial_diagram(inpath, export_file_name,
1243 			     export_file_format, size, show_layers, output_directory);
1244     if (inpath != node->data)
1245       g_free(inpath);
1246   }
1247   return made_conversions;
1248 }
1249 
1250 /* --credits option. Added by Andrew Ferrier.
1251 
1252    Hopefully we're not ignoring anything too crucial by
1253    quitting directly after the credits.
1254 
1255    The phrasing of the English here might need changing
1256    if we switch from plural to non-plural (say, only
1257    one maintainer).
1258 */
1259 static void
print_credits(gboolean credits)1260 print_credits(gboolean credits)
1261 {
1262   if (credits) {
1263       int i;
1264       const gint nauthors = (sizeof(authors) / sizeof(authors[0])) - 1;
1265       const gint ndocumentors = (sizeof(documentors) / sizeof(documentors[0])) - 1;
1266 
1267       g_print(_("The original author of Dia was:\n\n"));
1268       for (i = 0; i < NUMBER_OF_ORIG_AUTHORS; i++) {
1269           g_print("%s\n", authors[i]);
1270       }
1271 
1272       g_print(_("\nThe current maintainers of Dia are:\n\n"));
1273       for (i = NUMBER_OF_ORIG_AUTHORS; i < NUMBER_OF_ORIG_AUTHORS + NUMBER_OF_MAINTAINERS; i++) {
1274 	  g_print("%s\n", authors[i]);
1275       }
1276 
1277       g_print(_("\nOther authors are:\n\n"));
1278       for (i = NUMBER_OF_ORIG_AUTHORS + NUMBER_OF_MAINTAINERS; i < nauthors; i++) {
1279           g_print("%s\n", authors[i]);
1280       }
1281 
1282       g_print(_("\nDia is documented by:\n\n"));
1283       for (i = 0; i < ndocumentors; i++) {
1284           g_print("%s\n", documentors[i]);
1285       }
1286 
1287       exit(0);
1288   }
1289 }
1290 
app_is_embedded(void)1291 int app_is_embedded(void)
1292 {
1293   return 0;
1294 }
1295