1 /**
2  * @file    gui-main.c
3  * @brief
4  *
5  * Copyright (C) 2009 Gummi Developers
6  * All Rights reserved.
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following
15  * conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27  * OTHER DEALINGS IN THE SOFTWARE.
28  */
29 
30 #include "gui-main.h"
31 
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <sys/stat.h>
36 
37 #ifndef WIN32
38 #   include <unistd.h>
39 #endif
40 
41 #include <glib.h>
42 #include <gdk-pixbuf/gdk-pixbuf.h>
43 
44 #include "biblio.h"
45 #include "configfile.h"
46 #include "constants.h"
47 #include "editor.h"
48 #include "environment.h"
49 #include "importer.h"
50 #include "utils.h"
51 #include "template.h"
52 
53 #include "compile/rubber.h"
54 #include "compile/latexmk.h"
55 #include "compile/texlive.h"
56 
57 
58 extern Gummi* gummi;
59 extern GummiGui* gui;
60 
61 /* Many of the functions in this file are based on the excellent GTK+
62  * tutorials written by Micah Carrick that can be found on:
63  * http://www.micahcarrick.com/gtk-glade-tutorial-part-3.html */
64 
65 
66 /* These widgets are (de)sensitised using the gui_set_hastabs_sensitive
67  * function for when we switch from 0 to 1 tabs and vice versa */
68 const gchar* insens_widgets_str[] = {
69     "box_rightpane", "tool_save", "tool_bold", "tool_italic", "tool_unline",
70     "tool_left", "tool_center", "tool_right", "box_importers",
71     "menu_save", "menu_saveas", "menu_close", "menu_export",
72     "menu_undo", "menu_redo", "menu_cut", "menu_copy", "menu_paste",
73     "menu_delete", "menu_selectall", "menu_preferences", "menu_find",
74     "menu_prev", "menu_next", "menu_pdfcompile", "menu_compileopts",
75     "menu_runmakeindex", "menu_runbibtex", "menu_docstat", "menu_spelling",
76     "menu_snippets", "menu_edit", "menu_document", "menu_search",
77     "menu_cleanup", "menu_page_layout"
78 };
79 
80 
gui_init(GtkBuilder * builder)81 GummiGui* gui_init (GtkBuilder* builder) {
82     g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
83 
84     GtkWidget* hpaned;
85     gint i = 0, wx = 0, wy = 0, width = 0, height = 0;
86 
87     GummiGui* g = g_new0 (GummiGui, 1);
88 
89     g->builder = builder;
90 
91     g->mainwindow =
92         GTK_WINDOW (gtk_builder_get_object (builder, "mainwindow"));
93     g->toolbar =
94         GTK_WIDGET (gtk_builder_get_object (builder, "maintoolbar"));
95     g->statusbar =
96         GTK_STATUSBAR (gtk_builder_get_object (builder, "statusbar"));
97     g->rightpane =
98         GTK_BOX (gtk_builder_get_object (builder, "box_rightpane"));
99     g->errorview =
100         GTK_TEXT_VIEW (gtk_builder_get_object (builder, "errorview"));
101     g->errorbuff =
102         gtk_text_view_get_buffer (GTK_TEXT_VIEW (g->errorview));
103     g->menu_spelling =
104         GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menu_spelling"));
105     g->menu_snippets =
106         GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menu_snippets"));
107     g->menu_toolbar =
108         GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menu_toolbar"));
109     g->menu_statusbar =
110         GTK_CHECK_MENU_ITEM(gtk_builder_get_object (builder, "menu_statusbar"));
111     g->statusid =
112         gtk_statusbar_get_context_id (GTK_STATUSBAR (g->statusbar), "Gummi");
113     g->recent[0] =
114         GTK_MENU_ITEM (gtk_builder_get_object (builder, "menu_recent1"));
115     g->recent[1] =
116         GTK_MENU_ITEM (gtk_builder_get_object (builder, "menu_recent2"));
117     g->recent[2] =
118         GTK_MENU_ITEM (gtk_builder_get_object (builder, "menu_recent3"));
119     g->recent[3] =
120         GTK_MENU_ITEM (gtk_builder_get_object (builder, "menu_recent4"));
121     g->recent[4] =
122         GTK_MENU_ITEM (gtk_builder_get_object (builder, "menu_recent5"));
123 
124     g->docstatswindow =
125         GTK_WIDGET (gtk_builder_get_object (builder, "docstatswindow"));
126 
127     g->menu_runbibtex =
128         GTK_MENU_ITEM (gtk_builder_get_object (builder, "menu_runbibtex"));
129     g->menu_runmakeindex =
130         GTK_MENU_ITEM (gtk_builder_get_object (builder, "menu_runmakeindex"));
131     g->bibcompile =
132         GTK_WIDGET (gtk_builder_get_object (builder, "bibcompile"));
133 
134     g->insens_widget_size = sizeof(insens_widgets_str) / sizeof(gchar*);
135     g->insens_widgets = g_new0(GtkWidget*, g->insens_widget_size);
136 
137     for (i = 0; i < g->insens_widget_size; ++i)
138         g->insens_widgets[i] =
139             GTK_WIDGET(gtk_builder_get_object (builder, insens_widgets_str[i]));
140 
141     g->menugui = menugui_init (builder);
142     g->importgui = importgui_init (builder);
143     g->previewgui = previewgui_init (builder);
144     g->searchgui = searchgui_init (builder);
145     g->prefsgui = prefsgui_init (g->mainwindow);
146     g->snippetsgui = snippetsgui_init (g->mainwindow);
147     g->tabmanagergui = tabmanagergui_init (builder);
148     g->infoscreengui = infoscreengui_init (builder);
149     g->projectgui = projectgui_init (builder);
150 
151     gchar* icon_file = g_build_filename (GUMMI_DATA, "icons", "icon.png", NULL);
152     gtk_window_set_icon_from_file (g->mainwindow, icon_file, NULL);
153     g_free (icon_file);
154 
155     // set main window size and positioning:
156     if (config_get_boolean ("Interface", "mainwindow_max")) {
157         gtk_window_maximize (g->mainwindow);
158     }
159     else {
160         gtk_window_resize (g->mainwindow,
161                            config_get_integer ("Interface", "mainwindow_w"),
162                            config_get_integer ("Interface", "mainwindow_h"));
163 
164         wx = config_get_integer ("Interface", "mainwindow_x");
165         wy = config_get_integer ("Interface", "mainwindow_y");
166         if (wx && wy) {
167             gtk_window_move (g->mainwindow, wx, wy);
168         }
169         else {
170             gtk_window_set_position (g->mainwindow, GTK_WIN_POS_CENTER);
171         }
172     }
173 
174     // use new css styling for errorview pane:
175     gtk_widget_set_name (GTK_WIDGET (g->errorview), "errorview");
176 
177     GtkCssProvider *provider = gtk_css_provider_new ();
178     GError* error = NULL;
179 
180     const gchar* css_data =
181         "#errorview {\n"
182         "   font: 12px 'Monospace';\n"
183         "}\n";
184 
185     gtk_css_provider_load_from_data (provider, css_data, -1, &error);
186     if (error == NULL) {
187         GtkStyleContext *context;
188         context = gtk_widget_get_style_context (GTK_WIDGET (g->errorview));
189 
190         gtk_style_context_add_provider (context,
191                                         GTK_STYLE_PROVIDER (provider),
192                                         GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
193     }
194     else {
195         slog (L_ERROR, "%s\n", error->message);
196     }
197     g_object_unref (provider);
198 
199     gtk_window_get_size (g->mainwindow, &width, &height);
200 
201     hpaned= GTK_WIDGET (gtk_builder_get_object (builder, "hpaned"));
202     gtk_paned_set_position (GTK_PANED (hpaned), (width/2));
203 
204     if (config_get_boolean ("Editor", "spelling"))
205         gtk_check_menu_item_set_active (g->menu_spelling, TRUE);
206 
207     if (config_get_boolean ("Interface", "snippets")) {
208         gtk_check_menu_item_set_active (g->menu_snippets, TRUE);
209         gtk_widget_show (GTK_WIDGET (g->menu_snippets));
210     }
211     if (config_get_boolean ("Interface", "toolbar")) {
212         gtk_check_menu_item_set_active (g->menu_toolbar, TRUE);
213         gtk_widget_show (g->toolbar);
214     } else {
215         config_set_boolean ("Interface", "toolbar", FALSE);
216         gtk_check_menu_item_set_active (g->menu_toolbar, FALSE);
217         gtk_widget_hide (g->toolbar);
218     }
219 
220     if (config_get_boolean ("Interface", "statusbar")) {
221         gtk_check_menu_item_set_active (g->menu_statusbar, TRUE);
222         gtk_widget_show (GTK_WIDGET (g->statusbar));
223     } else {
224         config_set_boolean ("Interface", "statusbar", FALSE);
225         gtk_check_menu_item_set_active (g->menu_statusbar, FALSE);
226         gtk_widget_hide (GTK_WIDGET (g->statusbar));
227     }
228 
229     g->menu_autosync =
230         GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "menu_autosync"));
231 
232     if (latex_can_synctex() && config_get_boolean ("Compile", "synctex")) {
233         gtk_widget_set_sensitive (GTK_WIDGET (g->menu_autosync), TRUE);
234         gboolean async = latex_use_synctex();
235         gtk_check_menu_item_set_active (g->menu_autosync, (async? TRUE: FALSE));
236     }
237 
238     g->recent_list[0] = g_strdup (config_get_string ("Misc", "recent1"));
239     g->recent_list[1] = g_strdup (config_get_string ("Misc", "recent2"));
240     g->recent_list[2] = g_strdup (config_get_string ("Misc", "recent3"));
241     g->recent_list[3] = g_strdup (config_get_string ("Misc", "recent4"));
242     g->recent_list[4] = g_strdup (config_get_string ("Misc", "recent5"));
243 
244     display_recent_files (g);
245 
246     return g;
247 }
248 
gui_main(GtkBuilder * builder)249 void gui_main (GtkBuilder* builder) {
250     gtk_builder_connect_signals (builder, NULL);
251     gtk_widget_show_all (GTK_WIDGET (gui->mainwindow));
252 
253     gtk_main ();
254 }
255 
256 
257 
258 G_MODULE_EXPORT
on_menu_autosync_toggled(GtkCheckMenuItem * menu_autosync,void * user)259 void on_menu_autosync_toggled (GtkCheckMenuItem *menu_autosync, void* user) {
260     if (gtk_check_menu_item_get_active(menu_autosync)) {
261         config_set_boolean ("Preview", "autosync", TRUE);
262     } else {
263         config_set_boolean ("Preview", "autosync", FALSE);
264     }
265 }
266 
267 G_MODULE_EXPORT
on_tab_notebook_switch_page(GtkNotebook * notebook,GtkWidget * nbpage,int pagenr,void * data)268 void on_tab_notebook_switch_page (GtkNotebook *notebook, GtkWidget *nbpage, int pagenr, void *data) {
269     slog (L_DEBUG, "Switched to environment at page %d\n", pagenr);
270     /* Kill typesetter command */
271     motion_kill_typesetter(gummi->motion);
272 
273     /* set the active tab/editor pointers */
274     tabmanager_set_active_tab (pagenr);
275 
276     /* update the title of the mainwindow */
277     gui_set_filename_display (g_active_tab, TRUE, FALSE);
278 
279     /* clear the build log output window */
280     gui_buildlog_set_text ("");
281 
282     previewgui_reset (gui->previewgui);
283 }
284 
285 G_MODULE_EXPORT
on_right_notebook_switch_page(GtkNotebook * notebook,GtkWidget * nbpage,int page,void * data)286 void on_right_notebook_switch_page(GtkNotebook *notebook, GtkWidget *nbpage,
287                                    int page, void *data) {
288     if (page == 2) {
289         if (gummi_project_active ()) {
290             //projectgui_enable (gummi->project, gui->projectgui);
291         }
292         else {
293             //projectgui_disable (gummi->project, gui->projectgui);
294         }
295     }
296 }
297 
gui_set_filename_display(GuTabContext * tc,gboolean title,gboolean label)298 void gui_set_filename_display (GuTabContext* tc, gboolean title, gboolean label) {
299     gchar* filetext = tabmanager_get_tabname (tc);
300 
301     if (label) tabmanagergui_update_label (tc->page, filetext);
302     if (title) gui_set_window_title (tc->editor->filename, filetext);
303 }
304 
gui_set_window_title(const gchar * filename,const gchar * text)305 void gui_set_window_title (const gchar* filename, const gchar* text) {
306     gchar* dirname;
307     gchar* title;
308 
309     if (filename != NULL) {
310         dirname = g_strdup_printf("(%s)", g_path_get_dirname (filename));
311         title = g_strdup_printf ("%s %s - %s", text, dirname,
312                                                  C_PACKAGE_NAME);
313     }
314     else {
315         title = g_strdup_printf ("%s - %s", text, C_PACKAGE_NAME);
316     }
317     gtk_window_set_title (gui->mainwindow, title);
318     g_free (title);
319 }
320 
on_recovery_infobar_response(GtkInfoBar * bar,gint res,gpointer filename)321 void on_recovery_infobar_response (GtkInfoBar* bar, gint res, gpointer filename) {
322     gchar* prev_workfile = iofunctions_get_swapfile (filename);
323 
324     if (res == GTK_RESPONSE_YES) {
325         tabmanager_set_content (A_LOAD_OPT, filename, prev_workfile);
326     }
327     else { // NO
328         tabmanager_set_content (A_LOAD, filename, NULL);
329     }
330     gui_recovery_mode_disable (bar);
331 }
332 
gui_recovery_mode_enable(GuTabContext * tab,const gchar * filename)333 void gui_recovery_mode_enable (GuTabContext* tab, const gchar* filename) {
334     gchar* prev_workfile = iofunctions_get_swapfile (filename);
335 
336     slog (L_WARNING, "Swap file `%s' found.\n", prev_workfile);
337     gchar* msg = g_strdup_printf (_("Swap file exists for %s, "
338 				"do you want to recover from it?"), filename);
339     gtk_label_set_text (GTK_LABEL (tab->page->barlabel), msg);
340     g_free (msg);
341 
342     gchar* data = g_strconcat (filename, NULL);
343     tab->page->infosignal =
344         g_signal_connect (g_active_tab->page->infobar, "response",
345         G_CALLBACK (on_recovery_infobar_response), (gpointer)data);
346 
347     gtk_widget_set_sensitive (GTK_WIDGET (tab->editor->view), FALSE);
348     gtk_widget_show (tab->page->infobar);
349 }
350 
gui_recovery_mode_disable(GtkInfoBar * infobar)351 void gui_recovery_mode_disable (GtkInfoBar *infobar) {
352     gint id = g_active_tab->page->infosignal;
353     g_signal_handler_disconnect (infobar, id);
354 
355     gtk_widget_hide (GTK_WIDGET(infobar));
356     gtk_widget_set_sensitive (GTK_WIDGET (g_active_editor->view), TRUE);
357 }
358 
gui_open_file(const gchar * filename)359 void gui_open_file (const gchar* filename) {
360     if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
361         slog(L_G_ERROR, "Failed to open file '%s': No such file or "
362                 "directory\n", filename);
363         return;
364     }
365 
366     tabmanager_create_tab (A_LOAD, filename, NULL);
367     if (!gtk_widget_get_sensitive (GTK_WIDGET (gui->rightpane))) {
368         gui_set_hastabs_sensitive (TRUE);
369 	}
370 }
371 
gui_save_file(GuTabContext * tab,gboolean saveas)372 void gui_save_file (GuTabContext* tab, gboolean saveas) {
373     gboolean new = FALSE;
374     gchar* filename = NULL;
375     gchar* pdfname = NULL;
376     gchar* prev = NULL;
377     gint ret = 0;
378 
379     if (saveas || !(filename = tab->editor->filename)) {
380         if ((filename = get_save_filename (TYPE_LATEX))) {
381             new = TRUE;
382             if (!STR_EQU (filename + strlen (filename) -4, ".tex")) {
383                 prev = filename;
384                 filename = g_strdup_printf ("%s.tex", filename);
385                 g_free (prev);
386             }
387             if (utils_path_exists (filename)) {
388                 ret = utils_yes_no_dialog (
389                         _("The file already exists. Overwrite?"));
390                 if (GTK_RESPONSE_YES != ret) goto cleanup;
391             }
392         } else goto cleanup;
393     }
394 
395     gchar *text;
396     GtkWidget* focus = NULL;
397 
398     // check whether the file has been changed by (some) external program
399     double lastmod;
400     struct stat attr;
401     stat(filename, &attr);
402     lastmod = difftime (tab->editor->last_modtime, attr.st_mtime);
403 
404     if (lastmod != 0.0 && tab->editor->last_modtime != 0.0 ) {
405         // ask the user whether he want to save or reload
406         ret = utils_save_reload_dialog (
407                 _("The content of the file has been changed externally. "
408                   "Saving will remove any external modifications."));
409         if (ret == GTK_RESPONSE_YES) {
410             tabmanager_set_content (A_LOAD, filename, NULL);
411             // resets modtime
412             stat(filename, &attr);
413             tab->editor->last_modtime = attr.st_mtime;
414             goto cleanup;
415         } else if (ret != GTK_RESPONSE_NO) {
416             // cancel means: do nothing
417             goto cleanup;
418         }
419     }
420 
421     focus = gtk_window_get_focus (gummi_get_gui ()->mainwindow);
422     text = editor_grab_buffer (tab->editor);
423     gtk_widget_grab_focus (focus);
424 
425     iofunctions_save_file (gummi->io, filename, text);
426 
427     if (config_get_boolean ("File", "autoexport")) {
428         pdfname = g_strdup (filename);
429         pdfname[strlen (pdfname) -4] = 0;
430         latex_export_pdffile (gummi->latex, tab->editor, pdfname, FALSE);
431     }
432     if (new) tabmanager_update_tab (filename);
433     gui_set_filename_display (tab, TRUE, TRUE);
434     gtk_widget_grab_focus (GTK_WIDGET (tab->editor->view));
435 
436     // Resets modtime
437     stat(filename, &attr);
438     tab->editor->last_modtime = attr.st_mtime;
439 
440 cleanup:
441     if (new) g_free (filename);
442     g_free (pdfname);
443 }
444 
gui_set_hastabs_sensitive(gboolean enable)445 void gui_set_hastabs_sensitive (gboolean enable) {
446     gint i = 0;
447     for (i = 0; i < gui->insens_widget_size; ++i) {
448         gtk_widget_set_sensitive (gui->insens_widgets[i], enable);
449     }
450 }
451 
452 G_MODULE_EXPORT
on_menu_bibupdate_activate(GtkWidget * widget,void * user)453 void on_menu_bibupdate_activate (GtkWidget *widget, void * user) {
454     biblio_compile_bibliography (gummi->biblio, g_active_editor);
455 }
456 
457 G_MODULE_EXPORT
on_docstats_close_clicked(GtkWidget * widget,void * user)458 gboolean on_docstats_close_clicked (GtkWidget* widget, void* user) {
459     gtk_widget_hide (GTK_WIDGET (gui->docstatswindow));
460     return TRUE;
461 }
462 
463 G_MODULE_EXPORT
on_tool_textstyle_bold_activate(GtkWidget * widget,void * user)464 void on_tool_textstyle_bold_activate (GtkWidget* widget, void* user) {
465     editor_set_selection_textstyle (g_active_editor, "tool_bold");
466 }
467 
468 G_MODULE_EXPORT
on_tool_textstyle_italic_activate(GtkWidget * widget,void * user)469 void on_tool_textstyle_italic_activate (GtkWidget* widget, void* user) {
470     editor_set_selection_textstyle (g_active_editor, "tool_italic");
471 }
472 
473 G_MODULE_EXPORT
on_tool_textstyle_underline_activate(GtkWidget * widget,void * user)474 void on_tool_textstyle_underline_activate (GtkWidget* widget, void* user) {
475     editor_set_selection_textstyle (g_active_editor, "tool_unline");
476 }
477 
478 G_MODULE_EXPORT
on_tool_textstyle_left_activate(GtkWidget * widget,void * user)479 void on_tool_textstyle_left_activate (GtkWidget* widget, void* user) {
480     editor_set_selection_textstyle (g_active_editor, "tool_left");
481 }
482 
483 G_MODULE_EXPORT
on_tool_textstyle_center_activate(GtkWidget * widget,void * user)484 void on_tool_textstyle_center_activate (GtkWidget* widget, void* user) {
485     editor_set_selection_textstyle (g_active_editor, "tool_center");
486 }
487 
488 G_MODULE_EXPORT
on_tool_textstyle_right_activate(GtkWidget * widget,void * user)489 void on_tool_textstyle_right_activate (GtkWidget* widget, void* user) {
490     editor_set_selection_textstyle (g_active_editor, "tool_right");
491 }
492 
493 G_MODULE_EXPORT
on_button_template_add_clicked(GtkWidget * widget,void * user)494 void on_button_template_add_clicked (GtkWidget* widget, void* user) {
495     template_add_new_entry (gummi->templ);
496 }
497 
498 G_MODULE_EXPORT
on_button_template_remove_clicked(GtkWidget * widget,void * user)499 void on_button_template_remove_clicked (GtkWidget* widget, void* user) {
500     template_remove_entry (gummi->templ);
501 }
502 
503 G_MODULE_EXPORT
on_button_template_open_clicked(GtkWidget * widget,void * user)504 void on_button_template_open_clicked (GtkWidget* widget, void* user) {
505     gchar* status = NULL;
506     gchar* templ_name = template_get_selected_path (gummi->templ);
507 
508     if (templ_name) {
509         /* add Loading message to status bar */
510         status = g_strdup_printf (_("Loading template ..."));
511         statusbar_set_message (status);
512         g_free (status);
513 
514         tabmanager_create_tab (A_LOAD_OPT, NULL, templ_name);
515         gtk_widget_hide (GTK_WIDGET (gummi->templ->templatewindow));
516     }
517     g_free(templ_name);
518 
519     if (!gtk_widget_get_sensitive (GTK_WIDGET (gui->rightpane)))
520         gui_set_hastabs_sensitive (TRUE);
521 }
522 
523 G_MODULE_EXPORT
on_button_template_close_clicked(GtkWidget * widget,void * user)524 void on_button_template_close_clicked (GtkWidget* widget, void* user) {
525     gtk_widget_set_sensitive (GTK_WIDGET (gummi->templ->template_add), TRUE);
526     gtk_widget_set_sensitive (GTK_WIDGET (gummi->templ->template_remove), TRUE);
527     gtk_widget_set_sensitive (GTK_WIDGET (gummi->templ->template_open), TRUE);
528     gtk_widget_hide (GTK_WIDGET (gummi->templ->templatewindow));
529 }
530 
531 G_MODULE_EXPORT
on_template_rowitem_edited(GtkWidget * widget,gchar * path,gchar * filenm,void * user)532 void on_template_rowitem_edited (GtkWidget* widget, gchar *path, gchar* filenm,
533         void* user) {
534     GtkTreeIter iter;
535     GtkTreeModel* model = NULL;
536     GtkTreeSelection* selection = NULL;
537     gchar* text = NULL;
538 
539     gchar* filepath = g_build_filename (C_GUMMI_TEMPLATEDIR, filenm, NULL);
540 
541     model = gtk_tree_view_get_model (gummi->templ->templateview);
542     selection = gtk_tree_view_get_selection (gummi->templ->templateview);
543 
544     if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
545         gtk_list_store_set (gummi->templ->list_templates, &iter, 0, filenm, 1,
546                 filepath, -1);
547         text = editor_grab_buffer (g_active_editor);
548         template_create_file (gummi->templ, filenm, text);
549     }
550     g_free (text);
551     g_free (filepath);
552 }
553 
554 G_MODULE_EXPORT
on_template_cursor_changed(GtkTreeView * tree,gpointer data)555 void on_template_cursor_changed (GtkTreeView *tree, gpointer data) {
556     if (!gtk_tree_view_get_selection (tree) == 0) {
557         gtk_widget_set_sensitive (gummi->templ->template_open, TRUE);
558     }
559 }
560 
561 
562 G_MODULE_EXPORT
on_bibcolumn_clicked(GtkWidget * widget,void * user)563 void on_bibcolumn_clicked (GtkWidget* widget, void* user) {
564     gint id = gtk_tree_view_column_get_sort_column_id
565         (GTK_TREE_VIEW_COLUMN (widget));
566     gtk_tree_view_column_set_sort_column_id
567         (GTK_TREE_VIEW_COLUMN (widget), id);
568 }
569 
570 G_MODULE_EXPORT
on_button_biblio_compile_clicked(GtkWidget * widget,void * user)571 void on_button_biblio_compile_clicked (GtkWidget* widget, void* user) {
572     gummi->biblio->progressval = 0.0;
573 
574     gtk_widget_set_sensitive (widget, FALSE);
575     g_timeout_add (10, on_bibprogressbar_update, widget);
576 
577     if (biblio_compile_bibliography (gummi->biblio, g_active_editor)) {
578         statusbar_set_message (_("Compiling bibliography file.."));
579         // NOTE gtk3s bar doesn't place text inside the widget anymore :/
580         //gtk_progress_bar_set_text (gummi->biblio->progressbar,
581         //        _("Bibliography compiled without errors"));
582         motion_force_compile (gummi->motion);
583     } else {
584         statusbar_set_message
585         (_("Error compiling bibliography file or none detected.."));
586         // NOTE gtk3s bar doesn't place text inside the widget anymore :/
587         //gtk_progress_bar_set_text (gummi->biblio->progressbar,
588         //        _("Error compiling bibliography file"));
589     }
590 }
591 
592 G_MODULE_EXPORT
on_button_biblio_detect_clicked(GtkWidget * widget,void * user)593 void on_button_biblio_detect_clicked (GtkWidget* widget, void* user) {
594     gchar* text = 0;
595     gchar* str = 0;
596     gchar* basename = 0;
597     GError* err = NULL;
598     gint number = 0;
599 
600     gummi->biblio->progressval = 0.0;
601     g_timeout_add (2, on_bibprogressbar_update, widget);
602     gtk_list_store_clear (gummi->biblio->list_biblios);
603 
604     if (biblio_detect_bibliography (g_active_editor)) {
605         editor_insert_bib (g_active_editor, g_active_editor->bibfile);
606         if (!g_file_get_contents(g_active_editor->bibfile, &text, NULL, &err)) {
607             slog (L_G_ERROR, "g_file_get_contents (): %s\n", err->message);
608             g_error_free (err);
609             return;
610         }
611 
612         gtk_widget_set_sensitive
613                     (GTK_WIDGET(gummi->biblio->list_filter), TRUE);
614 
615         number = biblio_parse_entries (gummi->biblio, text);
616         basename = g_path_get_basename (g_active_editor->bibfile);
617         gtk_label_set_text (gummi->biblio->filenm_label, basename);
618         str = g_strdup_printf ("%d", number);
619         gtk_label_set_text (gummi->biblio->refnr_label, str);
620         g_free (str);
621         // NOTE gtk3s bar doesn't place text inside the widget anymore :/
622         //gtk_progress_bar_set_text (gummi->biblio->progressbar, str);
623         g_free (basename);
624     }
625     else {
626         gtk_widget_set_sensitive
627                     (GTK_WIDGET(gummi->biblio->list_filter), FALSE);
628         statusbar_set_message
629         (_("No bibliography file detected in document.."));
630         // NOTE gtk3s bar doesn't place text inside the widget anymore :/
631         //gtk_progress_bar_set_text (gummi->biblio->progressbar,
632         //        _("no bibliography file detected"));
633         gtk_label_set_text (gummi->biblio->filenm_label, _("none"));
634         gtk_label_set_text (gummi->biblio->refnr_label, _("N/A"));
635     }
636 }
637 
638 G_MODULE_EXPORT
on_bibreference_clicked(GtkTreeView * view,GtkTreePath * Path,GtkTreeViewColumn * column,void * user)639 void on_bibreference_clicked (GtkTreeView* view, GtkTreePath* Path,
640         GtkTreeViewColumn* column, void* user) {
641     GtkTreeIter iter;
642     gchar* value;
643     gchar* out;
644     GtkTreeModel* model = GTK_TREE_MODEL (gummi->biblio->list_biblios);
645     GtkTreeSelection* selection = gtk_tree_view_get_selection (view);
646 
647     gtk_tree_selection_get_selected (selection, &model, &iter);
648     gtk_tree_model_get (model, &iter, 0, &value, -1);
649     out = g_strdup_printf ("\\cite{%s}", value);
650     gtk_text_buffer_insert_at_cursor (g_e_buffer, out, strlen (out));
651     g_free (out);
652 }
653 
visible_func(GtkTreeModel * model,GtkTreeIter * iter,gpointer data)654 static gboolean visible_func (GtkTreeModel *model, GtkTreeIter  *iter, gpointer data) {
655     gboolean row_visible = FALSE;
656     gchar* title;
657     gchar* author;
658     gchar* year;
659 
660     /* make all entries visible again when filter buffer is empty */
661     if (strlen(data) == 0) return TRUE;
662 
663     gtk_tree_model_get (model, iter, 1, &title, 2, &author, 3, &year, -1);
664     if (utils_subinstr (data, title , TRUE)) row_visible = TRUE;
665     if (utils_subinstr (data, author , TRUE)) row_visible = TRUE;
666     if (utils_subinstr (data, year , TRUE)) row_visible = TRUE;
667 
668     g_free(title);
669     g_free(author);
670     g_free(year);
671 
672     return row_visible;
673 }
674 
675 G_MODULE_EXPORT
on_biblio_filter_changed(GtkWidget * widget,void * user)676 void on_biblio_filter_changed (GtkWidget* widget, void* user) {
677     GtkTreeModel *filter;
678 
679     const gchar *entry = gtk_entry_get_text(GTK_ENTRY(widget));
680     filter = gtk_tree_model_filter_new (
681             GTK_TREE_MODEL(gummi->biblio->list_biblios),NULL);
682     gtk_tree_model_filter_set_visible_func(
683             GTK_TREE_MODEL_FILTER(filter), visible_func, (gpointer)entry, NULL);
684     gtk_tree_view_set_model (
685             GTK_TREE_VIEW( gummi->biblio->biblio_treeview ),filter);
686     g_object_unref (G_OBJECT(filter));
687 }
688 
typesetter_setup(void)689 void typesetter_setup (void) {
690     // change the pref gui options on changing typesetter:
691     gboolean status = texlive_active();
692     gtk_widget_set_sensitive (GTK_WIDGET (gui->menu_runbibtex), status);
693     gtk_widget_set_sensitive (GTK_WIDGET (gui->menu_runmakeindex), status);
694     gtk_widget_set_sensitive (GTK_WIDGET (gui->prefsgui->opt_shellescape),
695                               status);
696 
697     if (config_get_boolean ("Compile", "synctex")) {
698         gtk_toggle_button_set_active (gui->prefsgui->opt_synctex, TRUE);
699     }
700     else {
701         gtk_toggle_button_set_active (gui->prefsgui->opt_synctex, FALSE);
702     }
703     gtk_widget_set_sensitive (GTK_WIDGET (gui->prefsgui->opt_synctex), TRUE);
704 
705     slog (L_INFO, "Typesetter %s configured\n",
706                    config_get_string ("Compile", "typesetter"));
707 }
708 
on_bibprogressbar_update(void * data)709 gboolean on_bibprogressbar_update (void* data) {
710     gummi->biblio->progressval += 0.01;
711 
712     gtk_progress_bar_set_fraction(gummi->biblio->progressbar,
713                                   gummi->biblio->progressval);
714 
715     if (gummi->biblio->progressval >= 1) {
716         gtk_widget_set_sensitive (data, TRUE);
717         return FALSE;
718     }
719     return TRUE;
720 }
721 
check_for_save(GuEditor * editor)722 gint check_for_save (GuEditor* editor) {
723     GtkWidget* dialog;
724     gint ret = 0;
725 
726     if (editor && editor_buffer_changed (editor)){
727         dialog = gtk_message_dialog_new (gui->mainwindow,
728                      GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
729                      GTK_MESSAGE_QUESTION,
730                      GTK_BUTTONS_NONE,
731                      _("This document has unsaved changes"));
732 
733         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
734                                     _("Do you want to save the changes to %s before closing?"),
735                                      editor->filename != NULL ? g_path_get_basename (editor->filename) : _("this document"));
736 
737         gtk_dialog_add_buttons (GTK_DIALOG (dialog),
738                                 _("_Close without Saving"), GTK_RESPONSE_NO,
739                                 _("_Cancel"), GTK_RESPONSE_CANCEL,
740                                 _("_Save As"), GTK_RESPONSE_YES,
741                                 NULL);
742 
743         gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_YES);
744 
745         ret = gtk_dialog_run (GTK_DIALOG (dialog));
746         gtk_widget_destroy (dialog);
747     }
748     return ret;
749 }
750 
get_open_filename(GuFilterType type)751 gchar* get_open_filename (GuFilterType type) {
752     GtkFileChooser* chooser = NULL;
753     gchar* active_cwd = NULL;
754     gchar* filename = NULL;
755 
756 
757     /* check if both active_editor and filename are defined. when zero tabs
758      * are open, the g_active_editor object is destroyed causing a segfault
759      * on the (filename != null) check. */
760     if (g_active_editor != NULL && g_active_editor->filename != NULL) {
761        active_cwd = g_path_get_dirname(g_active_editor->filename);
762     }
763 
764     const gchar* chooser_title[] = {
765         _("Open LaTeX document"),
766         "shouldn't happen",
767         "shouldn't happen",
768         _("Select an image to insert"),
769         _("Select bibliography file"),
770         _("Select project file")
771     };
772 
773     chooser = GTK_FILE_CHOOSER (gtk_file_chooser_dialog_new (
774                 chooser_title[type],
775                 gui->mainwindow,
776                 GTK_FILE_CHOOSER_ACTION_OPEN,
777                 _("_Cancel"), GTK_RESPONSE_CANCEL,
778                 _("_Open"), GTK_RESPONSE_OK,
779                 NULL));
780 
781     file_dialog_set_filter (chooser, type);
782     if (active_cwd)
783         gtk_file_chooser_set_current_folder (chooser, active_cwd);
784     else
785         gtk_file_chooser_set_current_folder (chooser, g_get_home_dir ());
786 
787     if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_OK)
788         filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
789 
790     if (filename) {
791         g_free (active_cwd);
792     }
793 
794     gtk_widget_destroy (GTK_WIDGET (chooser));
795     return filename;
796 }
797 
get_save_filename(GuFilterType type)798 gchar* get_save_filename (GuFilterType type) {
799     GtkFileChooser* chooser = NULL;
800     gchar* filename = NULL;
801 
802     const gchar* chooser_title[] = {
803         _("Save LaTeX document"),
804         _("Save as LaTeX document"),
805         _("Export to PDF"),
806         "shouldn't happen",
807         "shouldn't happen",
808         _("Create project")
809     };
810 
811     chooser = GTK_FILE_CHOOSER (gtk_file_chooser_dialog_new (
812                 chooser_title[type],
813                 gui->mainwindow,
814                 GTK_FILE_CHOOSER_ACTION_SAVE,
815                 _("_Cancel"), GTK_RESPONSE_CANCEL,
816                 _("_Save"), GTK_RESPONSE_OK,
817                 NULL));
818 
819     file_dialog_set_filter (chooser, type);
820     gtk_file_chooser_set_current_folder (chooser, g_get_home_dir ());
821 
822     if (g_active_editor->filename) {
823         gchar* dirname = g_path_get_dirname (g_active_editor->filename);
824         gchar* basename = g_path_get_basename (g_active_editor->filename);
825 
826         gtk_file_chooser_set_current_folder (chooser, dirname);
827 
828         if (TYPE_PDF == type) {
829             basename[strlen (basename) -4] = 0;
830             gchar* path = g_strdup_printf ("%s.pdf", basename);
831             gtk_file_chooser_set_current_name (chooser, path);
832             g_free (path);
833         } else if (TYPE_LATEX_SAVEAS == type)
834             gtk_file_chooser_set_current_name (chooser, basename);
835 
836         g_free (dirname);
837         g_free (basename);
838     }
839     else {
840         gchar *unsaved = g_strdup_printf (_("Unsaved Document"));
841         if (type == TYPE_PDF)
842             unsaved = g_strconcat (unsaved, ".pdf", NULL);
843         if (type == TYPE_LATEX)
844             unsaved = g_strconcat (unsaved, ".tex", NULL);
845         gtk_file_chooser_set_current_name (chooser, unsaved);
846     }
847 
848     if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_OK)
849         filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
850 
851     gtk_widget_destroy (GTK_WIDGET (chooser));
852     return filename;
853 }
854 
file_dialog_set_filter(GtkFileChooser * dialog,GuFilterType type)855 void file_dialog_set_filter (GtkFileChooser* dialog, GuFilterType type) {
856     GtkFileFilter* filter = gtk_file_filter_new ();
857 
858     switch (type) {
859         case TYPE_LATEX:
860         case TYPE_LATEX_SAVEAS:
861             gtk_file_filter_set_name (filter, _("LaTeX files"));
862             gtk_file_filter_add_pattern (filter, "*.tex");
863             gtk_file_chooser_add_filter (dialog, filter);
864             gtk_file_chooser_set_filter (dialog, filter);
865             break;
866 
867         case TYPE_PDF:
868             gtk_file_filter_set_name (filter, _(("PDF files")));
869             gtk_file_filter_add_pattern (filter, "*.pdf");
870             gtk_file_chooser_add_filter (dialog, filter);
871             gtk_file_chooser_set_filter (dialog, filter);
872             break;
873 
874         case TYPE_IMAGE:
875             /* Only \insertgraphics uses this section now. Make sure
876              * the patterns & mimes are correct before assigning it
877 			 * for other functions */
878             gtk_file_filter_set_name (filter, _("Supported Image files"));
879 
880             /* Pdflatex supports different formats than pure latex */
881             if (latex_method_active ("texpdf")) {
882 				gtk_file_filter_add_pattern (filter, "*.jpg");
883 				gtk_file_filter_add_pattern (filter, "*.jpeg");
884 				gtk_file_filter_add_pattern (filter, "*.png");
885 				gtk_file_filter_add_pattern (filter, "*.pdf");
886 			}
887 			gtk_file_filter_add_pattern (filter, "*.eps");
888             gtk_file_chooser_add_filter (dialog, filter);
889             gtk_file_chooser_set_filter (dialog, filter);
890             break;
891 
892         case TYPE_BIBLIO:
893             gtk_file_filter_set_name (filter, _("Bibtex files"));
894             gtk_file_filter_add_pattern (filter, "*.bib");
895             gtk_file_chooser_add_filter (dialog, filter);
896             gtk_file_chooser_set_filter (dialog, filter);
897             break;
898         case TYPE_PROJECT:
899             gtk_file_filter_set_name (filter, _("Gummi project files"));
900             gtk_file_filter_add_pattern (filter, "*.gummi");
901             gtk_file_chooser_add_filter (dialog, filter);
902             gtk_file_chooser_set_filter (dialog, filter);
903             break;
904     }
905 }
906 
add_to_recent_list(const gchar * filename)907 void add_to_recent_list (const gchar* filename) {
908     if (!filename) return;
909     gint i = 0;
910     /* check if it already exists */
911     for (i = 0; i < 5; ++i)
912         if (STR_EQU (filename, gui->recent_list[i]))
913             return;
914 
915     /* add to recent list */
916     g_free (gui->recent_list[RECENT_FILES_NUM -1]);
917     for (i = RECENT_FILES_NUM -2; i >= 0; --i)
918         gui->recent_list[i + 1] = gui->recent_list[i];
919     gui->recent_list[0] = g_strdup (filename);
920     display_recent_files (gui);
921 }
922 
display_recent_files(GummiGui * gui)923 void display_recent_files (GummiGui* gui) {
924     gchar* tstr = 0;
925     gchar* basename = 0;
926     gint i = 0, count = 0;
927 
928     for (i = 0; i < 5; ++i)
929         gtk_widget_hide (GTK_WIDGET (gui->recent[i]));
930 
931     for (i = 0; i < RECENT_FILES_NUM; ++i) {
932         if (!STR_EQU (gui->recent_list[i], "__NULL__")) {
933             basename = g_path_get_basename (gui->recent_list[i]);
934             tstr = g_strdup_printf ("%d. %s", count + 1, basename);
935             gtk_menu_item_set_label (gui->recent[i], tstr);
936             gtk_widget_set_tooltip_text (GTK_WIDGET (gui->recent[i]),
937                                         gui->recent_list[i]);
938             gtk_widget_show (GTK_WIDGET (gui->recent[i]));
939             g_free (tstr);
940             g_free (basename);
941             ++count;
942         }
943     }
944     // update recent files
945     for (i = 0; i < RECENT_FILES_NUM; ++i) {
946         tstr = g_strdup_printf ("recent%d", i + 1);
947         config_set_string ("Misc", tstr, gui->recent_list[i]);
948         g_free (tstr);
949     }
950 }
951 
gui_buildlog_set_text(const gchar * message)952 void gui_buildlog_set_text (const gchar *message) {
953     if (message) {
954         GtkTextIter iter;
955         GtkTextMark *mark;
956 
957         gtk_text_buffer_set_text (gui->errorbuff, message, -1);
958         gtk_text_buffer_get_end_iter (gui->errorbuff, &iter);
959 
960         // scrolling to the end used to cause bug #252 but seems ok now:
961         mark = gtk_text_buffer_create_mark(gui->errorbuff, NULL, &iter, FALSE);
962         gtk_text_view_scroll_to_mark (gui->errorview, mark, 0.25, FALSE, 0, 0);
963     }
964 }
965 
statusbar_set_message(const gchar * message)966 void statusbar_set_message (const gchar *message) {
967     gtk_statusbar_push (GTK_STATUSBAR (gui->statusbar), gui->statusid, message);
968     g_timeout_add_seconds (4, statusbar_del_message, NULL);
969 }
970 
statusbar_del_message(void * user)971 gboolean statusbar_del_message (void* user) {
972     gtk_statusbar_pop (GTK_STATUSBAR (gui->statusbar), gui->statusid);
973     return FALSE;
974 }
975 
976 /**
977  * @brief "changed" signal callback for editor->buffer
978  * Automatically check whether to start timer if buffer changed.
979  * Also set_modified for buffer
980  */
check_preview_timer(void)981 void check_preview_timer (void) {
982     g_return_if_fail (g_active_tab != NULL);
983 
984     gtk_text_buffer_set_modified (g_e_buffer, TRUE);
985     gummi->latex->modified_since_compile = TRUE;
986 
987     gui_set_filename_display (g_active_tab, TRUE, TRUE);
988 
989     motion_start_timer (gummi->motion);
990 }
991