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