1 /*
2 * This file is part of GNOME LaTeX.
3 *
4 * From gedit-utils.c:
5 * Copyright (C) 1998, 1999 - Alex Roberts, Evan Lawrence
6 * Copyright (C) 2000, 2002 - Chema Celorio, Paolo Maggi
7 * Copyright (C) 2003-2005 - Paolo Maggi
8 *
9 * Copyright (C) 2014-2020 - Sébastien Wilmet <swilmet@gnome.org>
10 *
11 * GNOME LaTeX is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * GNOME LaTeX is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with GNOME LaTeX. If not, see <http://www.gnu.org/licenses/>.
23 */
24
25 /**
26 * SECTION:utils
27 * @title: LatexilaUtils
28 * @short_description: Utility functions
29 *
30 * Various utility functions.
31 */
32
33 #include "config.h"
34 #include "latexila-utils.h"
35 #include <string.h>
36 #include <tepl/tepl.h>
37 #include "latexila-synctex.h"
38
39 #if HAVE_DCONF_MIGRATION
40 #include "dh-dconf-migration.h"
41 #endif
42
43 /**
44 * latexila_utils_register_icons:
45 *
46 * Register the GNOME LaTeX icons to the #GtkIconTheme as built-in icons.
47 *
48 * TODO: prefix icon names with "glatex-", so for example badbox.png would be
49 * "glatex-badbox".
50 */
51 void
latexila_utils_register_icons(void)52 latexila_utils_register_icons (void)
53 {
54 gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (),
55 "/org/gnome/gnome-latex/stock-icons/");
56 }
57
58 /**
59 * latexila_utils_get_pixbuf_from_icon_name:
60 * @icon_name: an icon name.
61 * @icon_size: an icon size.
62 *
63 * Returns: (nullable) (transfer full): the corresponding #GdkPixbuf.
64 */
65 GdkPixbuf *
latexila_utils_get_pixbuf_from_icon_name(const gchar * icon_name,GtkIconSize icon_size)66 latexila_utils_get_pixbuf_from_icon_name (const gchar *icon_name,
67 GtkIconSize icon_size)
68 {
69 gint size;
70 GdkPixbuf *pixbuf;
71 GError *error = NULL;
72
73 gtk_icon_size_lookup (icon_size, &size, NULL);
74
75 pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
76 icon_name,
77 size,
78 0,
79 &error);
80
81 if (error != NULL)
82 {
83 g_warning ("Error when loading icon \"%s\": %s", icon_name, error->message);
84 g_error_free (error);
85 }
86
87 return pixbuf;
88 }
89
90 static gboolean
default_document_viewer_is_evince(const gchar * uri)91 default_document_viewer_is_evince (const gchar *uri)
92 {
93 GFile *file;
94 GAppInfo *app_info;
95 const gchar *executable;
96 gboolean ret;
97 GError *error = NULL;
98
99 file = g_file_new_for_uri (uri);
100 app_info = g_file_query_default_handler (file, NULL, &error);
101 g_object_unref (file);
102
103 if (error != NULL)
104 {
105 g_warning ("Impossible to know if evince is the default document viewer: %s",
106 error->message);
107
108 g_error_free (error);
109 return FALSE;
110 }
111
112 executable = g_app_info_get_executable (app_info);
113 ret = strstr (executable, "evince") != NULL;
114
115 g_object_unref (app_info);
116 return ret;
117 }
118
119 /**
120 * latexila_utils_show_uri:
121 * @widget: (nullable): the associated #GtkWidget, or %NULL.
122 * @uri: the URI to show.
123 * @timestamp: a timestamp.
124 * @error: (out) (optional): a %NULL #GError, or %NULL.
125 *
126 * Shows the @uri. If the URI is a PDF file and if Evince is the default
127 * document viewer, this function also connects the Evince window so the
128 * backward search works (switch from the PDF to the source file).
129 */
130 void
latexila_utils_show_uri(GtkWidget * widget,const gchar * uri,guint32 timestamp,GError ** error)131 latexila_utils_show_uri (GtkWidget *widget,
132 const gchar *uri,
133 guint32 timestamp,
134 GError **error)
135 {
136 GtkWindow *parent = NULL;
137
138 g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
139 g_return_if_fail (uri != NULL);
140 g_return_if_fail (error == NULL || *error == NULL);
141
142 if (widget != NULL)
143 {
144 GtkWidget *toplevel;
145
146 toplevel = gtk_widget_get_toplevel (widget);
147 if (gtk_widget_is_toplevel (toplevel) &&
148 GTK_IS_WINDOW (toplevel))
149 {
150 parent = GTK_WINDOW (toplevel);
151 }
152 }
153
154 if (gtk_show_uri_on_window (parent, uri, timestamp, error))
155 {
156 gchar *extension = tepl_utils_get_file_extension (uri);
157
158 if (g_strcmp0 (extension, ".pdf") == 0 &&
159 default_document_viewer_is_evince (uri))
160 {
161 LatexilaSynctex *synctex = latexila_synctex_get_instance ();
162 latexila_synctex_connect_evince_window (synctex, uri);
163 }
164
165 g_free (extension);
166 }
167 }
168
169 /**
170 * latexila_utils_get_dialog_component:
171 * @title: the title of the dialog component.
172 * @widget: the widget displayed below the title.
173 *
174 * Gets a #GtkDialog component. When a dialog contains several components, or
175 * logical groups, this function is useful to attach the @widget with a @title.
176 * The title will be in bold, left-aligned, and the widget will have a left
177 * margin.
178 *
179 * Returns: (transfer floating): the dialog component containing the @title and
180 * the @widget.
181 */
182 GtkWidget *
latexila_utils_get_dialog_component(const gchar * title,GtkWidget * widget)183 latexila_utils_get_dialog_component (const gchar *title,
184 GtkWidget *widget)
185 {
186 GtkContainer *grid;
187 GtkWidget *label;
188 gchar *markup;
189
190 grid = GTK_CONTAINER (gtk_grid_new ());
191 gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL);
192 gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
193 gtk_container_set_border_width (grid, 6);
194
195 /* Title in bold, left-aligned. */
196 label = gtk_label_new (NULL);
197 markup = g_strdup_printf ("<b>%s</b>", title);
198 gtk_label_set_markup (GTK_LABEL (label), markup);
199 gtk_widget_set_halign (label, GTK_ALIGN_START);
200 gtk_container_add (grid, label);
201
202 /* Left margin for the widget. */
203 gtk_widget_set_margin_start (widget, 12);
204 gtk_container_add (grid, widget);
205
206 g_free (markup);
207 return GTK_WIDGET (grid);
208 }
209
210 /**
211 * latexila_utils_join_widgets:
212 * @widget_top: the #GtkWidget at the top.
213 * @widget_bottom: the #GtkWidget at the bottom.
214 *
215 * Joins two widgets vertically, with junction sides.
216 *
217 * Returns: (transfer floating): a #GtkContainer containing @widget_top and
218 * @widget_bottom.
219 */
220 /* TODO try to simply add the GTK_STYLE_CLASS_LINKED class to the
221 * GtkStyleContext of a GtkBox. Test also in RTL (right-to-left) text, although
222 * here it's a vertical container so it should not matter.
223 */
224 GtkWidget *
latexila_utils_join_widgets(GtkWidget * widget_top,GtkWidget * widget_bottom)225 latexila_utils_join_widgets (GtkWidget *widget_top,
226 GtkWidget *widget_bottom)
227 {
228 GtkStyleContext *context;
229 GtkBox *vbox;
230
231 g_return_val_if_fail (GTK_IS_WIDGET (widget_top), NULL);
232 g_return_val_if_fail (GTK_IS_WIDGET (widget_bottom), NULL);
233
234 context = gtk_widget_get_style_context (widget_top);
235 gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
236
237 context = gtk_widget_get_style_context (widget_bottom);
238 gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
239
240 vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
241 gtk_box_pack_start (vbox, widget_top, TRUE, TRUE, 0);
242 gtk_box_pack_start (vbox, widget_bottom, FALSE, FALSE, 0);
243
244 return GTK_WIDGET (vbox);
245 }
246
247 static void
migrate_latexila_to_gnome_latex_gsettings(void)248 migrate_latexila_to_gnome_latex_gsettings (void)
249 {
250 #if HAVE_DCONF_MIGRATION
251 DhDconfMigration *migration;
252 gint i;
253
254 const gchar *keys[] =
255 {
256 "preferences/editor/auto-save",
257 "preferences/editor/auto-save-interval",
258 "preferences/editor/bracket-matching",
259 "preferences/editor/create-backup-copy",
260 "preferences/editor/display-line-numbers",
261 "preferences/editor/editor-font",
262 "preferences/editor/forget-no-tabs",
263 "preferences/editor/highlight-current-line",
264 "preferences/editor/highlight-misspelled-words",
265 "preferences/editor/insert-spaces",
266 "preferences/editor/reopen-files",
267 "preferences/editor/scheme",
268 "preferences/editor/spell-checking-language",
269 "preferences/editor/tabs-size",
270 "preferences/editor/use-default-font",
271 "preferences/file-browser/current-directory",
272 "preferences/file-browser/show-build-files",
273 "preferences/file-browser/show-hidden-files",
274 "preferences/latex/automatic-clean",
275 "preferences/latex/clean-extensions",
276 "preferences/latex/disabled-default-build-tools",
277 "preferences/latex/enabled-default-build-tools",
278 "preferences/latex/interactive-completion",
279 "preferences/latex/interactive-completion-num",
280 "preferences/latex/no-confirm-clean",
281 "preferences/ui/bottom-panel-visible",
282 "preferences/ui/edit-toolbar-visible",
283 "preferences/ui/main-toolbar-visible",
284 "preferences/ui/show-build-badboxes",
285 "preferences/ui/show-build-warnings",
286 "preferences/ui/side-panel-component",
287 "preferences/ui/side-panel-visible",
288 "state/window/documents",
289 "state/window/side-panel-size",
290 "state/window/size",
291 "state/window/state",
292 "state/window/structure-paned-position",
293 "state/window/vertical-paned-position",
294 NULL
295 };
296
297 migration = _dh_dconf_migration_new ();
298
299 for (i = 0; keys[i] != NULL; i++)
300 {
301 const gchar *cur_key = keys[i];
302 gchar *cur_glatex_key;
303 gchar *cur_latexila_key;
304
305 cur_glatex_key = g_strconcat ("/org/gnome/gnome-latex/", cur_key, NULL);
306 cur_latexila_key = g_strconcat ("/org/gnome/latexila/", cur_key, NULL);
307
308 _dh_dconf_migration_migrate_key (migration,
309 cur_glatex_key,
310 cur_latexila_key,
311 NULL);
312
313 g_free (cur_glatex_key);
314 g_free (cur_latexila_key);
315 }
316
317 _dh_dconf_migration_sync_and_free (migration);
318 #else
319 g_warning ("LaTeXila -> GNOME LaTeX dconf migration not supported.");
320 #endif
321 }
322
323 static void
migrate_latexila_to_gnome_latex_copy_file(GFile * latexila_file,GFile * glatex_file)324 migrate_latexila_to_gnome_latex_copy_file (GFile *latexila_file,
325 GFile *glatex_file)
326 {
327 GError *error = NULL;
328
329 tepl_utils_create_parent_directories (glatex_file, NULL, &error);
330 if (error != NULL)
331 {
332 goto out;
333 }
334
335 g_file_copy (latexila_file,
336 glatex_file,
337 G_FILE_COPY_TARGET_DEFAULT_PERMS,
338 NULL, NULL, NULL,
339 &error);
340
341 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) ||
342 g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
343 {
344 g_clear_error (&error);
345 }
346
347 out:
348 if (error != NULL)
349 {
350 g_warning ("Error when migrating LaTeXila to GNOME LaTeX user data file: %s",
351 error->message);
352 g_clear_error (&error);
353 }
354 }
355
356 static void
migrate_latexila_to_gnome_latex_most_used_symbols(void)357 migrate_latexila_to_gnome_latex_most_used_symbols (void)
358 {
359 GFile *latexila_file;
360 GFile *glatex_file;
361
362 latexila_file = g_file_new_build_filename (g_get_user_data_dir (),
363 "latexila",
364 "most_used_symbols.xml",
365 NULL);
366
367 glatex_file = g_file_new_build_filename (g_get_user_data_dir (),
368 "gnome-latex",
369 "most_used_symbols.xml",
370 NULL);
371
372 migrate_latexila_to_gnome_latex_copy_file (latexila_file, glatex_file);
373
374 g_object_unref (latexila_file);
375 g_object_unref (glatex_file);
376 }
377
378 static void
migrate_latexila_to_gnome_latex_projects(void)379 migrate_latexila_to_gnome_latex_projects (void)
380 {
381 GFile *latexila_file;
382 GFile *glatex_file;
383
384 latexila_file = g_file_new_build_filename (g_get_user_data_dir (),
385 "latexila",
386 "projects.xml",
387 NULL);
388
389 glatex_file = g_file_new_build_filename (g_get_user_data_dir (),
390 "gnome-latex",
391 "projects.xml",
392 NULL);
393
394 migrate_latexila_to_gnome_latex_copy_file (latexila_file, glatex_file);
395
396 g_object_unref (latexila_file);
397 g_object_unref (glatex_file);
398 }
399
400 static void
migrate_latexila_to_gnome_latex_personal_build_tools(void)401 migrate_latexila_to_gnome_latex_personal_build_tools (void)
402 {
403 GFile *latexila_file;
404 GFile *glatex_file;
405
406 latexila_file = g_file_new_build_filename (g_get_user_config_dir (),
407 "latexila",
408 "build_tools.xml",
409 NULL);
410
411 glatex_file = g_file_new_build_filename (g_get_user_config_dir (),
412 "gnome-latex",
413 "build_tools.xml",
414 NULL);
415
416 migrate_latexila_to_gnome_latex_copy_file (latexila_file, glatex_file);
417
418 g_object_unref (latexila_file);
419 g_object_unref (glatex_file);
420 }
421
422 static void
migrate_latexila_to_gnome_latex_personal_templates_tex_files(void)423 migrate_latexila_to_gnome_latex_personal_templates_tex_files (void)
424 {
425 GFile *latexila_dir;
426 GFile *glatex_dir;
427 GFileEnumerator *enumerator;
428 GError *error = NULL;
429
430 latexila_dir = g_file_new_build_filename (g_get_user_data_dir (),
431 "latexila",
432 NULL);
433 glatex_dir = g_file_new_build_filename (g_get_user_data_dir (),
434 "gnome-latex",
435 NULL);
436
437 enumerator = g_file_enumerate_children (latexila_dir,
438 G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
439 G_FILE_QUERY_INFO_NONE,
440 NULL,
441 &error);
442
443 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
444 {
445 g_clear_error (&error);
446 goto out;
447 }
448
449 if (enumerator == NULL || error != NULL)
450 {
451 goto out;
452 }
453
454 while (TRUE)
455 {
456 GFileInfo *child_file_info;
457 GFile *child_file;
458 const gchar *child_name;
459 GFile *glatex_child_file;
460
461 g_file_enumerator_iterate (enumerator, &child_file_info, &child_file, NULL, &error);
462 if (child_file == NULL || error != NULL)
463 {
464 break;
465 }
466
467 child_name = g_file_info_get_display_name (child_file_info);
468 if (child_name == NULL || !g_str_has_suffix (child_name, ".tex"))
469 {
470 continue;
471 }
472
473 glatex_child_file = g_file_get_child (glatex_dir, child_name);
474 migrate_latexila_to_gnome_latex_copy_file (child_file, glatex_child_file);
475 g_object_unref (glatex_child_file);
476 }
477
478 out:
479 if (error != NULL)
480 {
481 g_warning ("Error when migrating LaTeXila to GNOME LaTeX personal templates: %s",
482 error->message);
483 g_clear_error (&error);
484 }
485
486 g_object_unref (latexila_dir);
487 g_object_unref (glatex_dir);
488 g_clear_object (&enumerator);
489 }
490
491 #define TEMPLATES_RC_FILE_OLD_GROUP_NAME "[LaTeXila]\n"
492 #define TEMPLATES_RC_FILE_NEW_GROUP_NAME "[Personal templates]\n"
493
494 static void
migrate_latexila_to_gnome_latex_personal_templates_rc_file(void)495 migrate_latexila_to_gnome_latex_personal_templates_rc_file (void)
496 {
497 GFile *latexila_file;
498 gchar *content = NULL;
499 GFile *glatex_file = NULL;
500 GFileOutputStream *output_stream = NULL;
501 GError *error = NULL;
502
503 /* Load old RC file. */
504 latexila_file = g_file_new_build_filename (g_get_user_data_dir (),
505 "latexila",
506 "templatesrc",
507 NULL);
508
509 g_file_load_contents (latexila_file, NULL, &content, NULL, NULL, &error);
510
511 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
512 {
513 g_clear_error (&error);
514 goto out;
515 }
516
517 if (error != NULL || content == NULL)
518 {
519 goto out;
520 }
521
522 /* Modify group name. */
523 if (g_str_has_prefix (content, TEMPLATES_RC_FILE_OLD_GROUP_NAME))
524 {
525 gchar *modified_content;
526
527 modified_content = g_strconcat (TEMPLATES_RC_FILE_NEW_GROUP_NAME,
528 content + strlen (TEMPLATES_RC_FILE_OLD_GROUP_NAME),
529 NULL);
530 g_free (content);
531 content = modified_content;
532 }
533
534 /* Save to new location. */
535 glatex_file = g_file_new_build_filename (g_get_user_data_dir (),
536 "gnome-latex",
537 "templatesrc",
538 NULL);
539
540 output_stream = g_file_create (glatex_file, G_FILE_CREATE_NONE, NULL, &error);
541
542 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
543 {
544 g_clear_error (&error);
545 goto out;
546 }
547
548 if (error != NULL || output_stream == NULL)
549 {
550 goto out;
551 }
552
553 g_output_stream_write_all (G_OUTPUT_STREAM (output_stream),
554 content,
555 strlen (content),
556 NULL,
557 NULL,
558 &error);
559
560 out:
561 if (error != NULL)
562 {
563 g_warning ("Error when migrating LaTeXila to GNOME LaTeX personal templates: %s",
564 error->message);
565 g_clear_error (&error);
566 }
567
568 g_object_unref (latexila_file);
569 g_free (content);
570 g_clear_object (&glatex_file);
571 g_clear_object (&output_stream);
572 }
573
574 static void
migrate_latexila_to_gnome_latex_personal_templates(void)575 migrate_latexila_to_gnome_latex_personal_templates (void)
576 {
577 migrate_latexila_to_gnome_latex_personal_templates_tex_files ();
578 migrate_latexila_to_gnome_latex_personal_templates_rc_file ();
579 }
580
581 /**
582 * latexila_utils_migrate_latexila_to_gnome_latex:
583 *
584 * Migrates the #GSettings values and user data/config files from LaTeXila to
585 * GNOME LaTeX, so that users don't lose all their settings.
586 *
587 * TODO in >= 2025: delete this code.
588 */
589 void
latexila_utils_migrate_latexila_to_gnome_latex(void)590 latexila_utils_migrate_latexila_to_gnome_latex (void)
591 {
592 GSettings *settings;
593
594 settings = g_settings_new ("org.gnome.gnome-latex");
595
596 if (!g_settings_get_boolean (settings, "latexila-to-gnome-latex-migration-done"))
597 {
598 migrate_latexila_to_gnome_latex_gsettings ();
599 migrate_latexila_to_gnome_latex_most_used_symbols ();
600 migrate_latexila_to_gnome_latex_projects ();
601 migrate_latexila_to_gnome_latex_personal_build_tools ();
602 migrate_latexila_to_gnome_latex_personal_templates ();
603
604 g_settings_set_boolean (settings, "latexila-to-gnome-latex-migration-done", TRUE);
605 }
606
607 g_object_unref (settings);
608 }
609