1 /*
2 * Bluefish HTML Editor
3 * bfwin.c
4 *
5 * Copyright (C) 2002-2013 Olivier Sessink
6 * Copyright (C) 2011 James Hayward
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /*#define DEBUG*/
23
24 #ifdef MAC_INTEGRATION
25 #include <gtkosxapplication.h>
26 #include <gtk-mac-menu.h>
27 #endif
28
29 #include <string.h>
30
31 #include "config.h"
32
33 #include "bfwin.h"
34 #include "bf_lib.h"
35 #include "bfwin_uimanager.h"
36 #include "bookmark.h"
37 #include "dialog_utils.h"
38 #include "document.h"
39 #include "filebrowser2.h"
40 #include "file_dialogs.h"
41 #include "gtk_easy.h" /* widget_set_visible() */
42 #include "outputbox.h"
43 #include "pixmap.h"
44 #include "plugins.h"
45 #include "project.h"
46 #include "rcfile.h"
47 #include "snr3.h"
48 #include "bftextview2_identifier.h"
49
50 #ifdef HAVE_LIBENCHANT
51 #include "bftextview2_spell.h"
52 #endif /*HAVE_LIBENCHANT*/
53
54
55 enum {
56 TARGET_URI_LIST,
57 TARGET_STRING
58 } Tdnd_types;
59
60 void
bfwin_fullscreen_toggle(Tbfwin * bfwin,gboolean active)61 bfwin_fullscreen_toggle(Tbfwin * bfwin, gboolean active)
62 {
63 if (active) {
64 gtk_window_fullscreen(GTK_WINDOW(bfwin->main_window));
65 } else {
66 gtk_window_unfullscreen(GTK_WINDOW(bfwin->main_window));
67 }
68 }
69
70 typedef enum {
71 order_byfullpath,
72 order_byfilename
73 }Tordering_mode;
74
75 static gint
sort_doc_by_filename(gconstpointer a,gconstpointer b)76 sort_doc_by_filename(gconstpointer a,gconstpointer b)
77 {
78 gint retval=0;
79 if (!a || !b)
80 return 0;
81 return g_strcmp0(gtk_label_get_text(GTK_LABEL(DOCUMENT(a)->tab_label)) , gtk_label_get_text(GTK_LABEL(DOCUMENT(b)->tab_label)));
82 }
83
84 static gint
sort_doc_by_fullpath(gconstpointer a,gconstpointer b)85 sort_doc_by_fullpath(gconstpointer a,gconstpointer b)
86 {
87 if (!a || !b)
88 return 0;
89 return g_strcmp0(gtk_label_get_text(GTK_LABEL(DOCUMENT(a)->tab_menu)) , gtk_label_get_text(GTK_LABEL(DOCUMENT(b)->tab_menu)));
90 }
91
92
93 static void
notebooks_reorder(Tbfwin * bfwin,Tordering_mode mode)94 notebooks_reorder(Tbfwin *bfwin, Tordering_mode mode)
95 {
96 GList *tmplist, *copylist;
97 gint i=0;
98 copylist = g_list_copy(bfwin->documentlist);
99 switch(mode) {
100 case order_byfullpath:
101 copylist = g_list_sort(copylist, sort_doc_by_fullpath);
102 break;
103 case order_byfilename:
104 copylist = g_list_sort(copylist, sort_doc_by_filename);
105 break;
106 }
107 /* now sort according to the order in the copylist */
108 for (tmplist=g_list_first(copylist);tmplist;tmplist=tmplist->next) {
109 DEBUG_MSG("notebooks_reorder, move document %s to position %d\n",gtk_label_get_text(GTK_LABEL(DOCUMENT(tmplist->data)->tab_menu)), i);
110 gtk_notebook_reorder_child(GTK_NOTEBOOK(bfwin->notebook), DOCUMENT(tmplist->data)->vsplit,i);
111 i++;
112 }
113 g_list_free(copylist);
114 }
115
116 static void
notebook_move(Tbfwin * bfwin,gboolean move_left)117 notebook_move(Tbfwin * bfwin, gboolean move_left)
118 {
119 gint curpos, newpos;
120
121 if (!bfwin->current_document)
122 return;
123
124 curpos = gtk_notebook_page_num(GTK_NOTEBOOK(bfwin->notebook), bfwin->current_document->vsplit);
125 if (curpos != -1) {
126 #ifdef DEVELOPMENT
127 {
128 GList *cur;
129 cur = g_list_nth(bfwin->documentlist, curpos);
130 g_return_if_fail(cur);
131 }
132 #endif
133 newpos = curpos + ((move_left) ? -1 : 1);
134 DEBUG_MSG("notebook_move, cur=%d, new=%d (num_pages=%d)\n", curpos, newpos,
135 gtk_notebook_get_n_pages(GTK_NOTEBOOK(bfwin->notebook)));
136 if (newpos >= 0 && newpos < gtk_notebook_get_n_pages(GTK_NOTEBOOK(bfwin->notebook)))
137 gtk_notebook_reorder_child(GTK_NOTEBOOK(bfwin->notebook), bfwin->current_document->vsplit,
138 newpos);
139 }
140 }
141
142 void
bfwin_notebook_switch(Tbfwin * bfwin,guint action)143 bfwin_notebook_switch(Tbfwin * bfwin, guint action)
144 {
145 DEBUG_MSG("bfwin_notebook_switch, action=%d\n", action);
146 switch (action) {
147 case 1:
148 gtk_notebook_prev_page(GTK_NOTEBOOK(bfwin->notebook));
149 break;
150 case 2:
151 gtk_notebook_next_page(GTK_NOTEBOOK(bfwin->notebook));
152 break;
153 case 3:
154 gtk_notebook_set_current_page(GTK_NOTEBOOK(bfwin->notebook), 0);
155 break;
156 case 4:
157 gtk_notebook_set_current_page(GTK_NOTEBOOK(bfwin->notebook), -1);
158 break;
159 case 5:
160 notebook_move(bfwin, TRUE);
161 break;
162 case 6:
163 notebook_move(bfwin, FALSE);
164 break;
165 case 7:
166 g_print("bfwin_notebook_switch, should not be called for 'recent'\n");
167 break;
168 case 8:
169 notebooks_reorder(bfwin, order_byfilename);
170 break;
171 case 9:
172 notebooks_reorder(bfwin, order_byfullpath);
173 break;
174 }
175 }
176
177 void
bfwin_set_main_toolbar_visible(Tbfwin * bfwin,gboolean visible,gboolean sync_menu)178 bfwin_set_main_toolbar_visible(Tbfwin * bfwin, gboolean visible, gboolean sync_menu)
179 {
180 if (!bfwin->main_toolbar_hb)
181 return;
182 if (sync_menu) {
183 DEBUG_MSG("bfwin_set_main_toolbar_visible, trying to sync menu\n");
184 bfwin_set_menu_toggle_item_from_path(bfwin->uimanager, "/MainMenu/ViewMenu/ViewMainToolbar", visible);
185 }
186 /*if (gtk_bin_get_child(GTK_BIN(bfwin->main_toolbar_hb)) == NULL)
187 bfwin_main_toolbar_init(bfwin);*/
188
189 widget_set_visible(bfwin->main_toolbar_hb, visible);
190 }
191
192 void
bfwin_main_toolbar_show(Tbfwin * bfwin,gboolean active)193 bfwin_main_toolbar_show(Tbfwin * bfwin, gboolean active)
194 {
195 bfwin->session->view_main_toolbar = active;
196 bfwin_set_main_toolbar_visible(bfwin, active, FALSE);
197 }
198
199 void
bfwin_output_pane_show(Tbfwin * bfwin,gboolean active)200 bfwin_output_pane_show(Tbfwin * bfwin, gboolean active)
201 {
202 if (active)
203 outputbox(bfwin, NULL, 0, 0, 0, NULL);
204 else
205 outputbox_cleanup(bfwin);
206 }
207
208 static void
side_panel_cleanup(Tbfwin * bfwin)209 side_panel_cleanup(Tbfwin * bfwin)
210 {
211 DEBUG_MSG("side_panel_cleanup called for bfwin %p\n", bfwin);
212 bmark_cleanup(bfwin);
213 fb2_cleanup(bfwin);
214 if (main_v->sidepanel_destroygui) {
215 GSList *tmplist = main_v->sidepanel_destroygui;
216 while (tmplist) {
217 void *(*func) () = tmplist->data;
218 DEBUG_MSG("side_panel_cleanup, calling plugin sidepanel_destroygui func %p on bfwin %p\n", tmplist->data, bfwin);
219 func(bfwin);
220 tmplist = g_slist_next(tmplist);
221 }
222 }
223 }
224
225 static void
side_panel_notify_position(GObject * object,GParamSpec * pspec,gpointer data)226 side_panel_notify_position(GObject * object, GParamSpec * pspec, gpointer data)
227 {
228 Tbfwin *bfwin = BFWIN(data);
229 gint position;
230
231 g_object_get(object, pspec->name, &position, NULL);
232 DEBUG_MSG("side_panel_notify_position, restore_dimensions=%d, new position=%d\n",
233 main_v->props.restore_dimensions, position);
234 if (main_v->props.restore_dimensions) {
235 if (main_v->props.left_panel_left) {
236 main_v->globses.left_panel_width = position;
237 } else {
238 int w, h;
239 gtk_window_get_size(GTK_WINDOW(bfwin->main_window), &w, &h);
240 main_v->globses.left_panel_width = w - position;
241 }
242 DEBUG_MSG("side_panel_notify_position, side_panel_width=%d\n", main_v->globses.left_panel_width);
243 }
244 if (main_v->props.wrap_on_right_margin) {
245 /* all docs need to recalculate the amount of whitespace on the right */
246 g_print("side_panel_notify_position, recalc right margin\n");
247 bfwin_alldoc_recalc_right_margin(bfwin);
248 }
249 }
250
251 static void
side_panel_notebook_switch(GtkNotebook * notebook,gpointer * page,guint page_num,gpointer user_data)252 side_panel_notebook_switch(GtkNotebook * notebook, gpointer * page, guint page_num, gpointer user_data)
253 {
254 Tbfwin *bfwin = BFWIN(user_data);
255 bfwin->session->leftpanel_active_tab = page_num;
256 }
257
258 static GtkWidget *
side_panel_build(Tbfwin * bfwin)259 side_panel_build(Tbfwin * bfwin)
260 {
261 GtkWidget *bmarks;
262 GtkWidget *fb2g;
263 #if GTK_CHECK_VERSION(3,0,0)
264 GtkCssProvider *provider;
265 GError *error = NULL;
266 #endif
267
268 bfwin->leftpanel_notebook = gtk_notebook_new();
269 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(bfwin->leftpanel_notebook), main_v->props.leftpanel_tabposition);
270 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(bfwin->leftpanel_notebook), TRUE);
271 #if !GTK_CHECK_VERSION(3, 0, 0)
272 gtk_notebook_set_tab_hborder(GTK_NOTEBOOK(bfwin->leftpanel_notebook), 0);
273 gtk_notebook_set_tab_vborder(GTK_NOTEBOOK(bfwin->leftpanel_notebook), 0);
274 #else
275 gtk_notebook_set_scrollable(GTK_NOTEBOOK(bfwin->leftpanel_notebook), TRUE);
276 #endif
277 gtk_notebook_set_show_border(GTK_NOTEBOOK(bfwin->leftpanel_notebook), FALSE);
278 gtk_notebook_popup_enable(GTK_NOTEBOOK(bfwin->leftpanel_notebook));
279 DEBUG_MSG("side_panel_build, building side panel for bfwin %p\n", bfwin);
280 bmarks = bmark_gui(bfwin);
281 fb2g = fb2_init(bfwin);
282 fb2_update_settings_from_session(bfwin, NULL);
283 gtk_notebook_append_page_menu(GTK_NOTEBOOK(bfwin->leftpanel_notebook), fb2g, new_pixmap(105),
284 gtk_label_new(_("Filebrowser")));
285 gtk_notebook_append_page_menu(GTK_NOTEBOOK(bfwin->leftpanel_notebook), bmarks, new_pixmap(104),
286 gtk_label_new(_("Bookmarks")));
287
288 #if GTK_CHECK_VERSION(3,0,0)
289 provider = gtk_css_provider_new();
290 gtk_css_provider_load_from_data (GTK_CSS_PROVIDER (provider),
291 ".notebook tab {\n"
292 " padding-left: 2px; padding-right: 2px;\n"
293 "}\n", -1, &error);
294 if (error != NULL) {
295 g_critical("Could not load CSS: %s\n", error->message);
296 g_error_free(error);
297 } else {
298 gtk_style_context_add_provider(gtk_widget_get_style_context(bfwin->leftpanel_notebook),
299 GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
300 }
301 g_object_unref(provider);
302 #endif
303
304 if (main_v->sidepanel_initgui) {
305 GSList *tmplist = main_v->sidepanel_initgui;
306 while (tmplist) {
307 void *(*func) () = tmplist->data;
308 DEBUG_MSG("side_panel_build, calling plugin sidepanel_initgui func %p\n", tmplist->data);
309 func(bfwin);
310 tmplist = g_slist_next(tmplist);
311 }
312 }
313
314 gtk_widget_show_all(bfwin->leftpanel_notebook);
315 gtk_notebook_set_current_page(GTK_NOTEBOOK(bfwin->leftpanel_notebook),
316 bfwin->session->leftpanel_active_tab);
317 g_signal_connect(G_OBJECT(bfwin->leftpanel_notebook), "switch-page",
318 G_CALLBACK(side_panel_notebook_switch), bfwin);
319
320 return bfwin->leftpanel_notebook;
321 }
322
323 /**
324 * if there is a left panel, this function will rebuild all widgets
325 */
326 void
bfwin_side_panel_rebuild(Tbfwin * bfwin)327 bfwin_side_panel_rebuild(Tbfwin * bfwin)
328 {
329 if (bfwin->hpane) {
330 DEBUG_MSG("bfwin_side_panel_rebuild, destroying widgets for bfwin %p\n", bfwin);
331 gtk_widget_destroy(bfwin->leftpanel_notebook);
332 side_panel_cleanup(bfwin);
333 DEBUG_MSG("bfwin_side_panel_rebuild, re-init\n");
334 side_panel_build(bfwin);
335 #ifdef PLATFORM_DARWIN
336 if (main_v->props.left_panel_left) {
337 gtk_paned_pack1(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, FALSE, FALSE);
338 } else {
339 gtk_paned_pack2(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, FALSE, FALSE);
340 }
341 #else
342 if (main_v->props.left_panel_left) {
343 gtk_paned_pack1(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, TRUE, TRUE);
344 } else {
345 gtk_paned_pack2(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, TRUE, TRUE);
346 }
347 #endif
348 gtk_widget_show_all(bfwin->leftpanel_notebook);
349 }
350 }
351
352 gboolean
bfwin_side_panel_show_hide_toggle(Tbfwin * bfwin,gboolean first_time,gboolean show,gboolean sync_menu)353 bfwin_side_panel_show_hide_toggle(Tbfwin * bfwin, gboolean first_time, gboolean show, gboolean sync_menu)
354 {
355 if (!bfwin->middlebox || !bfwin->notebook_box)
356 return FALSE;
357
358 DEBUG_MSG("bfwin_side_panel_show_hide_toggle, bfwin=%p, first_time=%d, show=%d, sync_menu=%d\n", bfwin,
359 first_time, show, sync_menu);
360 if (sync_menu) {
361 DEBUG_MSG("bfwin_side_panel_show_hide_toggle, trying to sync menu\n");
362 bfwin_set_menu_toggle_item_from_path(bfwin->uimanager, "/MainMenu/ViewMenu/ViewSidePane", show);
363 }
364 if (!first_time && ((show && bfwin->hpane) || (!show && bfwin->hpane == NULL))) {
365 DEBUG_MSG
366 ("bfwin_side_panel_show_hide_toggle, retrurning!!, show=%d, bfwin->hpane=%p, first_time=%d\n",
367 show, bfwin->hpane, first_time);
368 return FALSE;
369 }
370
371 if (!first_time) {
372 g_object_ref(G_OBJECT(bfwin->notebook_box));
373 if (show) {
374 gtk_container_remove(GTK_CONTAINER(bfwin->middlebox), bfwin->notebook_box);
375 } else {
376 gtk_container_remove(GTK_CONTAINER(bfwin->hpane), bfwin->notebook_box);
377 DEBUG_MSG("bfwin_side_panel_show_hide_toggle, destroy bfwin->hpane\n");
378 gtk_widget_destroy(bfwin->hpane);
379 side_panel_cleanup(bfwin);
380 bfwin->hpane = NULL;
381 }
382 }
383 if (show) {
384 #if GTK_CHECK_VERSION(3,0,0)
385 bfwin->hpane = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
386 #else
387 bfwin->hpane = gtk_hpaned_new();
388 #endif
389 if (main_v->props.left_panel_left) {
390 DEBUG_MSG("set paned position to %d (left)\n", main_v->globses.left_panel_width);
391 gtk_paned_set_position(GTK_PANED(bfwin->hpane), main_v->globses.left_panel_width);
392 } else {
393 int w, h;
394 gtk_window_get_size(GTK_WINDOW(bfwin->main_window), &w, &h);
395 DEBUG_MSG("set paned position to %d (right)\n", w - main_v->globses.left_panel_width);
396 gtk_paned_set_position(GTK_PANED(bfwin->hpane), w - main_v->globses.left_panel_width);
397 }
398 g_signal_connect(G_OBJECT(bfwin->hpane), "notify::position",
399 G_CALLBACK(side_panel_notify_position), bfwin);
400 bfwin->leftpanel_notebook = side_panel_build(bfwin);
401 /* When building with gtk+3.6.4 for MacOSX there are severeal issues with resizing:
402 * 1. NSWindow does not have border, so resizing handle when pushed to the edge, gets inactive (whole window is resized insted of notebook).
403 * 2. Leftpanel_notebook is "pushed" out of screen instead of shrinking; scroll arrows never appear (notebook is set to be scrollable,
404 * so they should appear).
405 * TODO investigate what causes left notebook "push" off-screen instead of cropping and how to set minimum size */
406 #ifdef PLATFORM_DARWIN
407 if (main_v->props.left_panel_left) {
408 gtk_paned_pack1(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, FALSE, FALSE);
409 gtk_paned_pack2(GTK_PANED(bfwin->hpane), bfwin->notebook_box, TRUE, FALSE);
410 } else {
411 gtk_paned_pack1(GTK_PANED(bfwin->hpane), bfwin->notebook_box, TRUE, FALSE);
412 gtk_paned_pack2(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, FALSE, FALSE);
413 }
414 #else
415 if (main_v->props.left_panel_left) {
416 gtk_paned_pack1(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, FALSE, TRUE);
417 gtk_paned_pack2(GTK_PANED(bfwin->hpane), bfwin->notebook_box, TRUE, TRUE);
418 } else {
419 gtk_paned_pack1(GTK_PANED(bfwin->hpane), bfwin->notebook_box, TRUE, TRUE);
420 gtk_paned_pack2(GTK_PANED(bfwin->hpane), bfwin->leftpanel_notebook, FALSE, TRUE);
421 }
422 #endif
423 gtk_box_pack_start(GTK_BOX(bfwin->middlebox), bfwin->hpane, TRUE, TRUE, 0);
424 gtk_widget_show(bfwin->hpane);
425 } else {
426 bfwin->hpane = NULL;
427 bfwin->leftpanel_notebook = NULL;
428 gtk_box_pack_start(GTK_BOX(bfwin->middlebox), bfwin->notebook_box, TRUE, TRUE, 0);
429 }
430 if (!first_time) {
431 g_object_unref(G_OBJECT(bfwin->notebook_box));
432 }
433
434 return TRUE;
435 }
436
437 void
bfwin_side_panel_show(Tbfwin * bfwin,gboolean active)438 bfwin_side_panel_show(Tbfwin * bfwin, gboolean active)
439 {
440 bfwin->session->view_left_panel = active;
441 bfwin_side_panel_show_hide_toggle(bfwin, FALSE, active, FALSE);
442 }
443
444 void
bfwin_statusbar_show_hide_toggle(Tbfwin * bfwin,gboolean visible,gboolean sync_menu)445 bfwin_statusbar_show_hide_toggle(Tbfwin * bfwin, gboolean visible, gboolean sync_menu)
446 {
447 if (!bfwin->statusbar)
448 return;
449 if (sync_menu)
450 bfwin_set_menu_toggle_item_from_path(bfwin->uimanager, "/MainMenu/ViewMenu/ViewStatusbar", visible);
451
452 widget_set_visible(bfwin->statusbar, visible);
453 widget_set_visible(bfwin->statusbar_lncol, visible);
454 widget_set_visible(bfwin->statusbar_insovr, visible);
455 widget_set_visible(bfwin->statusbar_editmode, visible);
456 }
457
458 void
bfwin_statusbar_show(Tbfwin * bfwin,gboolean active)459 bfwin_statusbar_show(Tbfwin * bfwin, gboolean active)
460 {
461 bfwin->session->view_statusbar = active;
462 bfwin_statusbar_show_hide_toggle(bfwin, active, FALSE);
463 }
464
465 void
bfwin_apply_session(Tbfwin * bfwin,Tdocument * active_doc)466 bfwin_apply_session(Tbfwin * bfwin, Tdocument *active_doc)
467 {
468 bfwin_set_main_toolbar_visible(bfwin, bfwin->session->view_main_toolbar, TRUE);
469
470 if (!bfwin_side_panel_show_hide_toggle(bfwin, FALSE, bfwin->session->view_left_panel, TRUE)) {
471 if (bfwin->leftpanel_notebook) {
472 gtk_notebook_set_current_page(GTK_NOTEBOOK(bfwin->leftpanel_notebook),
473 bfwin->session->leftpanel_active_tab);
474 }
475 }
476
477 bfwin_statusbar_show_hide_toggle(bfwin, bfwin->session->view_statusbar, TRUE);
478
479 fb2_update_settings_from_session(bfwin, active_doc);
480 bfwin_recent_menu_create(bfwin, TRUE);
481
482 if (bfwin->simplesearch_combo) {
483 combobox_empty(bfwin->simplesearch_combo);
484 combobox_fill(bfwin->simplesearch_combo, NULL, bfwin->session->searchlist);
485 }
486 /* force this session in the plugins */
487 g_slist_foreach(main_v->plugins, bfplugins_enforce_session, bfwin);
488 }
489
490 void
bfwin_apply_settings(Tbfwin * bfwin)491 bfwin_apply_settings(Tbfwin * bfwin)
492 {
493 DEBUG_MSG("bfwin_apply_settings, started\n");
494 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(bfwin->notebook), main_v->props.document_tabposition);
495 /* We don't want to set the tab position if the left panel is hidden */
496 if (bfwin->leftpanel_notebook) {
497 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(bfwin->leftpanel_notebook),
498 main_v->props.leftpanel_tabposition);
499
500 /* check if the left panel needs to move over to the right */
501 if (main_v->props.left_panel_left
502 && bfwin->leftpanel_notebook == gtk_paned_get_child1(GTK_PANED(bfwin->hpane))) {
503 DEBUG_MSG("bfwin_apply_settings, left panel is on the right location\n");
504 } else {
505 bfwin_side_panel_show_hide_toggle(bfwin, FALSE, FALSE, FALSE);
506 bfwin_side_panel_show_hide_toggle(bfwin, FALSE, TRUE, FALSE);
507 }
508 }
509 }
510
bfwin_window_state_event_idle_cb(gpointer data)511 static gboolean bfwin_window_state_event_idle_cb(gpointer data) {
512 bfwin_alldoc_recalc_right_margin(data);
513 return FALSE;
514 }
515
bfwin_window_state_event(GtkWidget * widget,GdkEvent * revent,Tbfwin * bfwin)516 static gboolean bfwin_window_state_event(GtkWidget * widget, GdkEvent * revent, Tbfwin * bfwin)
517 {
518 if (main_v->props.restore_dimensions) {
519 if (gtk_widget_get_visible(bfwin->main_window)) {
520 GdkEventWindowState *event = (GdkEventWindowState *) revent;
521 DEBUG_MSG
522 ("bfwin_window_state_event, window state event, state=%d, globses_main_window_w=%d\n",
523 event->new_window_state, main_v->globses.main_window_w);
524 if ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) && main_v->globses.main_window_w > 0) {
525 main_v->globses.main_window_w = -1 * main_v->globses.main_window_w; /* negative means it is maximized !! */
526 DEBUG_MSG("bfwin_window_state_event, maximized!! storing width=%d\n",
527 main_v->globses.main_window_w);
528 } else if (!(event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
529 && main_v->globses.main_window_w < 0) {
530 main_v->globses.main_window_w = -1 * main_v->globses.main_window_w; /* make it positive again */
531 DEBUG_MSG("bfwin_window_state_event, NOT-maximized, storing width=%d\n",
532 main_v->globses.main_window_w);
533 }
534 if (event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) {
535 gboolean fullscreen = (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN);
536 if (main_v->props.hide_bars_on_fullscreen) {
537 widget_set_visible(bfwin->toolbarbox, !fullscreen);
538 widget_set_visible(gtk_widget_get_parent(bfwin->statusbar), !fullscreen);
539 }
540 sync_fullscreen_toggle(bfwin, fullscreen);
541 }
542 }
543 }
544
545 if (main_v->props.wrap_on_right_margin && ((revent->type == GDK_WINDOW_STATE && (((GdkEventWindowState *)revent)->changed_mask & (GDK_WINDOW_STATE_FULLSCREEN | GDK_WINDOW_STATE_MAXIMIZED))))) {
546 /* all docs need to recalculate the amount of whitespace on the right, but the width is not yet set,
547 so do this in an idle callback */
548 g_idle_add(bfwin_window_state_event_idle_cb, bfwin);
549 }
550 return FALSE;
551 }
552
553 static gboolean
bfwin_configure_event(GtkWidget * widget,GdkEvent * revent,Tbfwin * bfwin)554 bfwin_configure_event(GtkWidget * widget, GdkEvent * revent, Tbfwin * bfwin)
555 {
556 if (main_v->props.restore_dimensions) {
557 GdkEventConfigure *event = (GdkEventConfigure *) revent;
558 DEBUG_MSG("bfwin_configure_event, configure event, got event->width=%d, event->height=%d\n",event->width,event->height);
559 if (main_v->globses.main_window_w > 0) {
560 main_v->globses.main_window_w = event->width;
561 main_v->globses.main_window_h = event->height;
562 DEBUG_MSG("bfwin_configure_event, not maximized, storing width=%d, height=%d\n",
563 main_v->globses.main_window_w, main_v->globses.main_window_h);
564 }
565 }
566 if (main_v->props.wrap_on_right_margin) {
567 /* all docs need to recalculate the amount of whitespace on the right */
568 bfwin_alldoc_recalc_right_margin(bfwin);
569 }
570 return FALSE;
571 }
572
573 /* called *before the widget is destroyed, this function will destroy the widget */
574 void
bfwin_destroy_and_cleanup(Tbfwin * bfwin)575 bfwin_destroy_and_cleanup(Tbfwin *bfwin)
576 {
577 GList *tmplist;
578 DEBUG_MSG("bfwin_destroy_and_cleanup, started for %p\n", bfwin);
579
580 /* all documents have to be freed for this window */
581 tmplist = g_list_first(bfwin->documentlist);
582 DEBUG_MSG("bfwin_cleanup, have %d documents in window %p\n", g_list_length(bfwin->documentlist),
583 bfwin);
584 while (tmplist) {
585 DEBUG_MSG("bfwin_cleanup closing doc=%p\n", tmplist->data);
586 doc_destroy(DOCUMENT(tmplist->data), TRUE);
587 /* no this is not an indefinite loop, because the documents remove themselves
588 from the documentlist, we remove the top document untill there are no documents
589 left */
590 tmplist = g_list_first(bfwin->documentlist);
591 }
592
593 g_signal_handler_disconnect(bfwin->notebook, bfwin->notebook_switch_signal);
594 if (bfwin->update_searchhistory_idle_id != 0) {
595 g_source_remove(bfwin->update_searchhistory_idle_id);
596 }
597
598 gtk_widget_destroy(bfwin->main_window);
599 DEBUG_MSG("bfwin_destroy_and_cleanup, main_window destroyed for bfwin %p\n", bfwin);
600
601 /* the widgets are destroyed now, don't touch any widgets any more from now on */
602
603 bfwin->statusbar = NULL; /* make sure no new statusbar messages have to be popped */
604
605 if (bfwin->statusbar_pop_id != 0) {
606 g_source_remove(bfwin->statusbar_pop_id);
607 bfwin->statusbar_pop_id = 0;
608 }
609
610 #ifdef HAVE_LIBENCHANT
611 unload_spell_dictionary(bfwin);
612 #endif
613 DEBUG_MSG("bfwin_cleanup called for bfwin %p\n",bfwin);
614 side_panel_cleanup(bfwin);
615 outputbox_cleanup(bfwin);
616
617 if (bfwin->notebook_changed_doc_activate_id != 0) {
618 g_source_remove(bfwin->notebook_changed_doc_activate_id);
619 }
620
621 DEBUG_MSG("bfwin_cleanup, unref static actiongroups\n");
622 g_object_unref(G_OBJECT(bfwin->uimanager));
623 /*g_object_unref(G_OBJECT(bfwin->globalGroup));
624 g_object_unref(G_OBJECT(bfwin->documentGroup));
625 g_object_unref(G_OBJECT(bfwin->editGroup));
626 g_object_unref(G_OBJECT(bfwin->findReplaceGroup));
627 g_object_unref(G_OBJECT(bfwin->projectGroup));
628 g_object_unref(G_OBJECT(bfwin->undoGroup));
629 g_object_unref(G_OBJECT(bfwin->bookmarkGroup));
630 g_object_unref(G_OBJECT(bfwin->filebrowserGroup)); invalid unref according to valgrind */
631
632 DEBUG_MSG("unref dynamic templates actiongroups\n");
633 g_object_unref(G_OBJECT(bfwin->templates_group));
634 DEBUG_MSG("unref dynamic lang_mode actiongroups\n");
635 g_object_unref(G_OBJECT(bfwin->lang_mode_group));
636 DEBUG_MSG("unref dynamic commands actiongroups\n");
637 g_object_unref(G_OBJECT(bfwin->commands_group));
638 DEBUG_MSG("unref dynamicfilters actiongroups\n");
639 g_object_unref(G_OBJECT(bfwin->filters_group));
640 DEBUG_MSG("unref dynamic outputbox actiongroups\n");
641 g_object_unref(G_OBJECT(bfwin->outputbox_group));
642 DEBUG_MSG("unref dynamicencodings actiongroups\n");
643 g_object_unref(G_OBJECT(bfwin->encodings_group));
644 if (bfwin->fb2_filters_group) {
645 DEBUG_MSG("unref dynamic fb2_filters actiongroups\n");
646 g_object_unref(G_OBJECT(bfwin->fb2_filters_group));
647 }
648 DEBUG_MSG("bfwin_cleanup, finished unref actiongroups\n");
649 bftextview2_identifier_hash_destroy(bfwin);
650 DEBUG_MSG("bfwin_cleanup, going to free bfwin %p\n", bfwin);
651 g_free(bfwin);
652 }
653
654 gboolean
bfwin_delete_event(GtkWidget * widget,GdkEvent * event,Tbfwin * bfwin)655 bfwin_delete_event(GtkWidget * widget, GdkEvent * event, Tbfwin * bfwin)
656 {
657 /*
658 If you return FALSE in the "delete_event" signal handler GTK will emit the "destroy" signal.
659 Returning TRUE means you handled the event, and it should not be further propagated
660
661 we always handle it, so we always return TRUE
662 */
663 DEBUG_MSG("bfwin_delete_event, started for bfwin %p\n", bfwin);
664 if (!bfwin->documentlist) {
665 bfwin_destroy_and_cleanup(bfwin);
666 return TRUE;
667 }
668
669 if (have_modified_documents(bfwin->documentlist)) {
670 Tclose_mode retval = multiple_files_modified_dialog(bfwin);
671
672 switch (retval) {
673 case close_mode_per_file:
674 DEBUG_MSG("bfwin_delete_event, per file\n");
675 project_save_and_mark_closed(bfwin);
676 if (choose_per_file(bfwin, TRUE)) {
677 DEBUG_MSG("bfwin_delete_event, all saved or all closed, return TRUE\n");
678 return TRUE;
679 } else {
680 DEBUG_MSG("bfwin_delete_event, cancelled!\n");
681 if (bfwin->project)
682 bfwin->project->close = FALSE;
683 return TRUE;
684 }
685 break;
686 case close_mode_close_all:
687 DEBUG_MSG("bfwin_delete_event, close all\n");
688 if (bfwin->project) {
689 project_save_and_mark_closed(bfwin);
690 }
691 doc_close_multiple_backend(bfwin, TRUE, retval);
692 /* the last document that closes should close the window, so return TRUE */
693 return TRUE; /* this had FALSE the comment above sais it should return TRUE ??? */
694 break;
695 case close_mode_cancel:
696 DEBUG_MSG("bfwin_delete_event, cancel\n");
697 return TRUE;
698 break;
699 case close_mode_save_all:
700 default:
701 /* save all and close */
702 project_save_and_mark_closed(bfwin);
703 DEBUG_MSG("bfwin_delete_event, save all\n");
704 doc_save_all_close(bfwin);
705 return TRUE;
706 break;
707 }
708 } else {
709 DEBUG_MSG("bfwin_delete_event, nothing modified, close all\n");
710 project_save_and_mark_closed(bfwin);
711 doc_close_multiple_backend(bfwin, TRUE, close_mode_close_all);
712 /* the last document that closes should close the window, so return TRUE */
713 return TRUE;
714 }
715
716 }
717 #ifdef MAC_INTEGRATION
718 gboolean
bfwin_osx_terminate_event(GtkWidget * widget,GdkEvent * event,Tbfwin * bfwin)719 bfwin_osx_terminate_event(GtkWidget * widget, GdkEvent * event, Tbfwin * bfwin)
720 {
721 /*
722 On OSX we need this simplified function since showing dialog window on NSApplicationBlockTermination callback
723 causes bluefish to crash. Probably this is bug in gtk-mac-integration. So, we save all modified documents
724 by default without asking if user really wants to do it.
725 */
726
727 DEBUG_MSG("bfwin_osx_terminate_event, started for bfwin %p\n", bfwin);
728 if (!bfwin->documentlist) {
729 g_print("bfwin_osx_terminate_event, document list is empty\n");
730 bfwin_destroy_and_cleanup(bfwin);
731 return TRUE;
732 }
733
734 if (have_modified_documents(bfwin->documentlist)) {
735 DEBUG_MSG("bfwin_osx_terminate_event, have modified documents\n");
736 if (bfwin->project && bfwin->project->uri) {
737 project_save_and_mark_closed(bfwin);
738 }
739 doc_save_all_close(bfwin);
740 } else {
741 DEBUG_MSG("bfwin_osx_terminate_event, nothing modified, close all\n");
742 if (bfwin->project && bfwin->project->uri) {
743 project_save_and_mark_closed(bfwin);
744 }
745 doc_close_multiple_backend(bfwin, TRUE, close_mode_close_all);
746 }
747 return TRUE;
748 }
749 #endif
750
751 void
bfwin_gotoline_from_clipboard(Tbfwin * bfwin)752 bfwin_gotoline_from_clipboard(Tbfwin * bfwin)
753 {
754 gchar *string;
755 GtkClipboard *cb;
756 DEBUG_MSG("bfwin_gotoline_from_clipboard, called!\n");
757 if (!gtk_widget_get_visible(bfwin->gotoline_frame))
758 gtk_widget_show(bfwin->gotoline_frame);
759
760 cb = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
761 string = gtk_clipboard_wait_for_text(cb);
762 if (string) {
763 gtk_entry_set_text(GTK_ENTRY(bfwin->gotoline_entry),string);
764 gtk_editable_select_region(GTK_EDITABLE(bfwin->gotoline_entry),0,-1);
765 g_free(string);
766 }
767 }
768
769 void
bfwin_gotoline_frame_show(Tbfwin * bfwin)770 bfwin_gotoline_frame_show(Tbfwin * bfwin)
771 {
772 if (!gtk_widget_get_visible(bfwin->gotoline_frame))
773 gtk_widget_show(bfwin->gotoline_frame);
774
775 gtk_widget_grab_focus(bfwin->gotoline_entry);
776 }
777
778 static void simplesearch_combo_entry_activated(gpointer widget, Tbfwin * bfwin);
779
780 void
bfwin_simplesearch_show(Tbfwin * bfwin)781 bfwin_simplesearch_show(Tbfwin *bfwin)
782 {
783 GtkTextIter itstart,itend;
784 if (!gtk_widget_get_visible(bfwin->gotoline_frame))
785 gtk_widget_show(bfwin->gotoline_frame);
786
787 /* see if there is a selection within a single line */
788 if (gtk_text_buffer_get_selection_bounds(bfwin->current_document->buffer, &itstart, &itend)) {
789 if (gtk_text_iter_get_line(&itstart)==gtk_text_iter_get_line(&itend)) {
790 gchar *tmpstr = gtk_text_buffer_get_text(bfwin->current_document->buffer,&itstart,&itend,TRUE);
791 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo))),tmpstr);
792 g_free(tmpstr);
793 /* TODO: mark the current selection as the 'current' search result */
794 }
795 }
796
797 simplesearch_combo_entry_activated(bfwin->simplesearch_regex, bfwin);
798 gtk_widget_grab_focus(bfwin->simplesearch_combo);
799 gtk_editable_select_region(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo))),0,-1);
800 }
801
802 void
bfwin_simplesearch_from_clipboard(Tbfwin * bfwin)803 bfwin_simplesearch_from_clipboard(Tbfwin *bfwin)
804 {
805 gchar *string;
806 GtkClipboard *cb;
807 DEBUG_MSG("bfwin_simplesearch_from_clipboard, called!\n");
808 if (!gtk_widget_get_visible(bfwin->gotoline_frame))
809 gtk_widget_show(bfwin->gotoline_frame);
810
811 cb = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
812 string = gtk_clipboard_wait_for_text(cb);
813 if (string) {
814 gtk_entry_set_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo))),string);
815 gtk_editable_select_region(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo))),0,-1);
816 /* TODO: mark the current selection as the 'current' search result */
817 g_free(string);
818 }
819 simplesearch_combo_entry_activated(bfwin->simplesearch_regex, bfwin);
820 gtk_widget_grab_focus(bfwin->simplesearch_combo);
821 }
822
823 static void
gotoline_entry_changed(GtkEditable * editable,Tbfwin * bfwin)824 gotoline_entry_changed(GtkEditable * editable, Tbfwin * bfwin)
825 {
826 gchar *linestr;
827 gint linenum;
828 guint linecount;
829 GtkTextIter iter;
830
831 if (!bfwin->current_document)
832 return;
833 if (!gtk_widget_get_visible(bfwin->gotoline_frame))
834 return;
835
836 DEBUG_MSG("gotoline_entry_changed, called!\n");
837 linestr = gtk_editable_get_chars(editable, 0, -1);
838 linenum = get_int_from_string(linestr);
839 g_free(linestr);
840
841 linecount = gtk_text_buffer_get_line_count(GTK_TEXT_BUFFER(bfwin->current_document->buffer));
842
843 if (linenum == -1)
844 gtk_text_buffer_get_start_iter(GTK_TEXT_BUFFER(bfwin->current_document->buffer), &iter);
845 else if (linenum >= linecount)
846 gtk_text_buffer_get_end_iter(GTK_TEXT_BUFFER(bfwin->current_document->buffer), &iter);
847 else
848 gtk_text_buffer_get_iter_at_line(GTK_TEXT_BUFFER(bfwin->current_document->buffer), &iter,
849 linenum - 1);
850
851 gtk_text_buffer_place_cursor(GTK_TEXT_BUFFER(bfwin->current_document->buffer), &iter);
852 doc_scroll_to_cursor(bfwin->current_document);
853 }
854
855 static void
gotoline_entry_insert_text(GtkEditable * editable,gchar * text,gint length,gint * position,gpointer data)856 gotoline_entry_insert_text(GtkEditable * editable, gchar * text, gint length, gint * position, gpointer data)
857 {
858 gunichar c;
859 const gchar *p;
860 const gchar *end;
861 const gchar *next;
862
863 p = text;
864 end = text + length;
865
866 if (p == end)
867 return;
868
869 while (p != end) {
870 next = g_utf8_next_char(p);
871
872 c = g_utf8_get_char(p);
873
874 if (!g_unichar_isdigit(c)) {
875 g_signal_stop_emission_by_name(editable, "insert_text");
876 break;
877 }
878
879 p = next;
880 }
881 }
882
883 void
bfwin_gotoline_search_bar_close(Tbfwin * bfwin,gboolean clean_entry_only)884 bfwin_gotoline_search_bar_close(Tbfwin *bfwin, gboolean clean_entry_only)
885 {
886 if (!clean_entry_only) {
887 DEBUG_MSG("bfwin_gotoline_search_bar_close, called!\n");
888 gtk_widget_hide(bfwin->gotoline_frame);
889
890 if (bfwin->simplesearch_snr3run) {
891 DEBUG_MSG("free simple search run %p\n", bfwin->simplesearch_snr3run);
892 simple_search_cancel_free(bfwin->simplesearch_snr3run);
893 bfwin->simplesearch_snr3run = NULL;
894 }
895 }
896 /*gtk_editable_delete_text(GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo))), 0, -1);*/
897 g_signal_handlers_block_matched(bfwin->gotoline_entry,
898 G_SIGNAL_MATCH_FUNC, 0, 0, NULL, gotoline_entry_changed, NULL);
899
900 gtk_editable_delete_text(GTK_EDITABLE(bfwin->gotoline_entry), 0, -1);
901
902 g_signal_handlers_unblock_matched(bfwin->gotoline_entry,
903 G_SIGNAL_MATCH_FUNC, 0, 0, NULL, gotoline_entry_changed, NULL);
904 }
905
906 static void
gotoline_close_button_clicked(GtkButton * button,Tbfwin * bfwin)907 gotoline_close_button_clicked(GtkButton * button, Tbfwin * bfwin)
908 {
909 DEBUG_MSG("gotoline_close_button_clicked, called!\n");
910 bfwin_gotoline_search_bar_close(bfwin, FALSE);
911 if (bfwin->current_document)
912 gtk_widget_grab_focus(bfwin->current_document->view);
913 }
914
915 static gboolean
simpleasearch_update_search_history(gpointer data)916 simpleasearch_update_search_history(gpointer data)
917 {
918 DEBUG_MSG("simpleasearch_update_search_history, called!\n");
919 if (BFWIN(data)->simplesearch_combo) {
920 const gchar *tmpstr = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(BFWIN(data)->simplesearch_combo))));
921 DEBUG_MSG("currenbt string=%s\n",tmpstr);
922 combobox_empty(BFWIN(data)->simplesearch_combo);
923 combobox_fill(BFWIN(data)->simplesearch_combo, tmpstr, BFWIN(data)->session->searchlist);
924 BFWIN(data)->update_searchhistory_idle_id = 0;
925 }
926 return FALSE;
927 }
928
929 static gboolean
simplesearch_start(Tbfwin * bfwin,gboolean allow_single_char_search)930 simplesearch_start(Tbfwin *bfwin, gboolean allow_single_char_search) {
931 const gchar *tmpstr;
932 gboolean retval=FALSE;
933
934 if (!bfwin->current_document)
935 return FALSE;
936 tmpstr = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo))));
937 if (bfwin->simplesearch_snr3run) {
938 DEBUG_MSG("free simple search run %p\n", bfwin->simplesearch_snr3run);
939 simple_search_cancel_free(bfwin->simplesearch_snr3run);
940 bfwin->simplesearch_snr3run=NULL;
941 }
942 if (tmpstr && tmpstr[0]!='\0' && (allow_single_char_search || tmpstr[1] != '\0')) {
943 gpointer before;
944 bfwin->session->ssearch_regex = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_regex));
945 bfwin->session->ssearch_casesens = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_casesens));
946 bfwin->session->ssearch_dotmatchall = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_dotmatchall));
947 bfwin->session->ssearch_unescape = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_unescape));
948 g_free(bfwin->session->ssearch_text);
949 bfwin->session->ssearch_text = g_strdup(tmpstr);
950 DEBUG_MSG("start simple search run with %s\n",tmpstr);
951 before = bfwin->session->searchlist;
952 bfwin->simplesearch_snr3run = simple_search_run(bfwin, tmpstr
953 , bfwin->session->ssearch_regex ? snr3type_pcre :snr3type_string
954 , bfwin->session->ssearch_casesens
955 , bfwin->session->ssearch_dotmatchall
956 , bfwin->session->ssearch_unescape);
957 if (before != bfwin->session->searchlist) {
958 /* update the combo with the new history */
959 if (bfwin->update_searchhistory_idle_id) {
960 g_source_remove(bfwin->update_searchhistory_idle_id);
961 }
962 bfwin->update_searchhistory_idle_id = g_timeout_add_seconds(4, simpleasearch_update_search_history,bfwin);
963 }
964 retval = TRUE;
965 }
966 return retval;
967 }
968
969 static void
simplesearch_forward_clicked(GtkButton * button,Tbfwin * bfwin)970 simplesearch_forward_clicked(GtkButton * button, Tbfwin * bfwin)
971 {
972 if (!bfwin->simplesearch_snr3run && !simplesearch_start(bfwin, TRUE))
973 return;
974 snr3_run_go(bfwin->simplesearch_snr3run, TRUE);
975 }
976
977 static void
simplesearch_back_clicked(GtkButton * button,Tbfwin * bfwin)978 simplesearch_back_clicked(GtkButton * button, Tbfwin * bfwin)
979 {
980 if (!bfwin->simplesearch_snr3run && !simplesearch_start(bfwin, TRUE))
981 return;
982 snr3_run_go(bfwin->simplesearch_snr3run, FALSE);
983 }
984
985 static void
simplesearch_combo_entry_changed(gpointer widget,Tbfwin * bfwin)986 simplesearch_combo_entry_changed(gpointer widget, Tbfwin * bfwin)
987 {
988 if (!gtk_widget_get_visible(bfwin->gotoline_frame))
989 return;
990 DEBUG_MSG("simplesearch_combo_entry_changed, call simplesearch_start()\n");
991 simplesearch_start(bfwin, FALSE);
992 }
993
994 static void
simplesearch_combo_entry_activated(gpointer widget,Tbfwin * bfwin)995 simplesearch_combo_entry_activated(gpointer widget, Tbfwin * bfwin)
996 {
997 if (!gtk_widget_get_visible(bfwin->gotoline_frame))
998 return;
999 DEBUG_MSG("simplesearch_combo_entry_activated, call simplesearch_start()\n");
1000 simplesearch_forward_clicked(NULL, bfwin);
1001 }
1002
1003 static void
simplesearch_option_toggled(gpointer widget,Tbfwin * bfwin)1004 simplesearch_option_toggled(gpointer widget, Tbfwin * bfwin)
1005 {
1006 DEBUG_MSG("simplesearch_option_toggled, call simplesearch_start()\n");
1007 simplesearch_start(bfwin, FALSE);
1008 if (widget == bfwin->simplesearch_regex) {
1009 gboolean regex = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_regex));
1010 gtk_widget_set_sensitive(bfwin->simplesearch_dotmatchall, regex);
1011 gtk_widget_set_sensitive(bfwin->simplesearch_unescape, !regex);
1012 }
1013 }
1014
1015 static void
simplesearch_advanced_clicked(GtkButton * button,Tbfwin * bfwin)1016 simplesearch_advanced_clicked(GtkButton * button, Tbfwin * bfwin)
1017 {
1018 const gchar *tmpstr = gtk_entry_get_text(GTK_ENTRY(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo))));
1019 snr3_advanced_dialog(bfwin, tmpstr?tmpstr:"");
1020 gotoline_close_button_clicked(NULL, bfwin);
1021 }
1022
1023 static gboolean
gotoline_entries_key_press_event(GtkWidget * widget,GdkEventKey * event,Tbfwin * bfwin)1024 gotoline_entries_key_press_event(GtkWidget * widget, GdkEventKey * event, Tbfwin * bfwin)
1025 {
1026 if (event->keyval == GDK_Escape) {
1027 gotoline_close_button_clicked(NULL, bfwin);
1028 return TRUE;
1029 }
1030 if (event->keyval == GDK_Return && widget == bfwin->gotoline_entry) {
1031 gotoline_entry_changed(GTK_EDITABLE(widget), bfwin);
1032 /* the close button clicked function automatically switches the focus to the text widget */
1033 gotoline_close_button_clicked(NULL, bfwin);
1034 return TRUE;
1035 }
1036 return FALSE;
1037 }
1038 static void
gotoline_frame_create(Tbfwin * bfwin)1039 gotoline_frame_create(Tbfwin * bfwin)
1040 {
1041 GtkWidget *button, *hbox;
1042 GtkWidget *menu;
1043
1044 bfwin->gotoline_frame = gtk_frame_new(NULL);
1045 gtk_frame_set_shadow_type(GTK_FRAME(bfwin->gotoline_frame), GTK_SHADOW_NONE);
1046 hbox = gtk_hbox_new(FALSE, 6);
1047 gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
1048 gtk_container_add(GTK_CONTAINER(bfwin->gotoline_frame), hbox);
1049
1050 button = bluefish_small_close_button_new();
1051 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 6);
1052 g_signal_connect(button, "clicked", G_CALLBACK(gotoline_close_button_clicked), bfwin);
1053
1054 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("Goto Line:")), FALSE, FALSE, 0);
1055 bfwin->gotoline_entry = gtk_entry_new();
1056 gtk_box_pack_start(GTK_BOX(hbox), bfwin->gotoline_entry, FALSE, FALSE, 0);
1057 g_signal_connect(bfwin->gotoline_entry, "changed", G_CALLBACK(gotoline_entry_changed), bfwin);
1058 g_signal_connect(bfwin->gotoline_entry, "insert-text", G_CALLBACK(gotoline_entry_insert_text), NULL);
1059 g_signal_connect(G_OBJECT(bfwin->gotoline_entry), "key-press-event", G_CALLBACK(gotoline_entries_key_press_event), bfwin);
1060 #if (GTK_CHECK_VERSION(3,0,0))
1061 gtk_box_pack_start(GTK_BOX(hbox), gtk_separator_new(GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 6);
1062 #else
1063 gtk_box_pack_start(GTK_BOX(hbox), gtk_vseparator_new(), FALSE, FALSE, 6);
1064 #endif
1065 gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(_("Find:")), FALSE, FALSE, 0);
1066 DEBUG_MSG("ssearch_text=%s\n",bfwin->session->ssearch_text);
1067 bfwin->simplesearch_combo = combobox_with_popdown(bfwin->session->ssearch_text, bfwin->session->searchlist, TRUE);
1068 gtk_box_pack_start(GTK_BOX(hbox), bfwin->simplesearch_combo, FALSE, FALSE, 0);
1069 button = (GtkWidget *)gtk_tool_button_new_from_stock(GTK_STOCK_GO_BACK);
1070 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(simplesearch_back_clicked), bfwin);
1071 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1072 button = (GtkWidget *)gtk_menu_tool_button_new_from_stock(GTK_STOCK_GO_FORWARD);
1073 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(simplesearch_forward_clicked), bfwin);
1074 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1075 menu = gtk_menu_new();
1076 bfwin->simplesearch_regex = gtk_check_menu_item_new_with_label(_("Regular expression (pcre)"));
1077 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_regex), bfwin->session->ssearch_regex);
1078 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(bfwin->simplesearch_regex));
1079 bfwin->simplesearch_casesens = gtk_check_menu_item_new_with_label(_("Case sensitive"));
1080 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_casesens), bfwin->session->ssearch_casesens);
1081 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(bfwin->simplesearch_casesens));
1082 bfwin->simplesearch_dotmatchall = gtk_check_menu_item_new_with_label(_("Dot character matches newlines"));
1083 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_dotmatchall), bfwin->session->ssearch_dotmatchall);
1084 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(bfwin->simplesearch_dotmatchall));
1085 bfwin->simplesearch_unescape = gtk_check_menu_item_new_with_label(_("Pattern contains backslash escaped sequences"));
1086 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(bfwin->simplesearch_unescape), bfwin->session->ssearch_unescape);
1087 gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(bfwin->simplesearch_unescape));
1088 gtk_menu_tool_button_set_menu(GTK_MENU_TOOL_BUTTON(button), GTK_WIDGET(menu));
1089 gtk_widget_show_all(GTK_WIDGET(menu));
1090 g_signal_connect(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo)), "key-press-event", G_CALLBACK(gotoline_entries_key_press_event), bfwin);
1091 g_signal_connect(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo)), "changed", G_CALLBACK(simplesearch_combo_entry_changed), bfwin);
1092 g_signal_connect(gtk_bin_get_child(GTK_BIN(bfwin->simplesearch_combo)), "activate", G_CALLBACK(simplesearch_combo_entry_activated), bfwin);
1093 g_signal_connect(bfwin->simplesearch_regex, "toggled", G_CALLBACK(simplesearch_option_toggled), bfwin);
1094 g_signal_connect(bfwin->simplesearch_casesens, "toggled", G_CALLBACK(simplesearch_option_toggled), bfwin);
1095 g_signal_connect(bfwin->simplesearch_dotmatchall, "toggled", G_CALLBACK(simplesearch_option_toggled), bfwin);
1096 g_signal_connect(bfwin->simplesearch_unescape, "toggled", G_CALLBACK(simplesearch_option_toggled), bfwin);
1097 button = (GtkWidget *)gtk_tool_button_new(NULL,_("Advanced"));
1098 g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(simplesearch_advanced_clicked),bfwin);
1099 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1100
1101 gtk_box_pack_start(GTK_BOX(bfwin->notebook_box), bfwin->gotoline_frame, FALSE, FALSE, 2);
1102 gtk_widget_show_all(hbox);
1103 }
1104
1105 static void
bfwin_destroy_event(GtkWidget * widget,Tbfwin * bfwin)1106 bfwin_destroy_event(GtkWidget * widget, Tbfwin * bfwin)
1107 {
1108 DEBUG_MSG("bfwin_destroy_event, started for bfwin=%p, first cleanup any project (%p)\n", bfwin,
1109 bfwin->project);
1110 if (bfwin->project) {
1111 project_final_close(bfwin, TRUE);
1112 bfwin->session = NULL;
1113 }
1114 main_v->bfwinlist = g_list_remove(main_v->bfwinlist, bfwin);
1115 DEBUG_MSG("bfwin_destroy_event, bfwin(%p) is removed from bfwinlist\n", bfwin);
1116 #ifndef MAC_INTEGRATION
1117 if (NULL == main_v->bfwinlist) {
1118 rcfile_save_global_session();
1119 gtk_main_quit();
1120 }
1121 #else
1122 if (NULL == main_v->bfwinlist) {
1123 if (main_v->osx_status == 0 ) {
1124 main_v->osx_status = 2;
1125 bfwin_window_new();
1126 return;
1127 }
1128 if (main_v->osx_status == 1 ) {
1129 rcfile_save_global_session();
1130 gtk_main_quit();
1131 return;
1132 }
1133 }
1134 #endif
1135 }
1136
1137 static void
bfwin_on_drag_data(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint info,guint time,Tbfwin * bfwin)1138 bfwin_on_drag_data(GtkWidget * widget, GdkDragContext * context, gint x, gint y,
1139 GtkSelectionData * data, guint info, guint time, Tbfwin * bfwin)
1140 {
1141 int mode = 0;
1142 gchar *stringdata;
1143
1144 if ((gtk_selection_data_get_length(data) == 0) || (gtk_selection_data_get_format(data) != 8)
1145 || ((info != TARGET_STRING) && (info != TARGET_URI_LIST))) {
1146 DEBUG_MSG("bfwin_on_drag_data, currently unknown DnD object, need to do string comparision\n");
1147 gtk_drag_finish(context, FALSE, TRUE, time);
1148 return;
1149 }
1150 stringdata = g_strndup((gchar *) gtk_selection_data_get_data(data), gtk_selection_data_get_length(data));
1151 if (strchr(stringdata, '\n')) {
1152 gchar **arr, **tmp;
1153 tmp = arr = g_strsplit(stringdata, "\n", -1);
1154 while (*tmp) {
1155 gchar *stripped = g_strstrip(*tmp);
1156 if (stripped[0] != '\0') {
1157 doc_new_from_input(bfwin, stripped, FALSE, FALSE, -1);
1158 }
1159 tmp++;
1160 }
1161 g_strfreev(arr);
1162 } else {
1163 doc_new_from_input(bfwin, stringdata, FALSE, FALSE, -1);
1164 }
1165 g_free(stringdata);
1166 gtk_drag_finish(context, TRUE, (mode == GDK_ACTION_COPY), time);
1167 }
1168
1169 static void
notebook_switch_by_key(GtkWidget * widget,gpointer data)1170 notebook_switch_by_key(GtkWidget * widget, gpointer data)
1171 {
1172 DEBUG_MSG("notebook_switch_by_key, gtk_notebook_set_current_page\n");
1173 gtk_notebook_set_current_page(GTK_NOTEBOOK(widget), GPOINTER_TO_INT(data) - 1);
1174 }
1175
1176 void
notebook_unbind_tab_signals(Tbfwin * bfwin)1177 notebook_unbind_tab_signals(Tbfwin * bfwin)
1178 {
1179 g_signal_handlers_disconnect_matched(bfwin->notebook, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, notebook_switch_by_key, NULL);
1180 }
1181
1182 void
notebook_bind_tab_signals(Tbfwin * bfwin)1183 notebook_bind_tab_signals(Tbfwin * bfwin)
1184 {
1185 DEBUG_MSG("notebook_bind_signals, connect <Alt>X trigered events\n");
1186 g_signal_connect(G_OBJECT(bfwin->notebook), "tab-last", G_CALLBACK(notebook_switch_by_key),
1187 (gpointer) - 1);
1188 g_signal_connect(G_OBJECT(bfwin->notebook), "tab-first", G_CALLBACK(notebook_switch_by_key),
1189 (gpointer) 1);
1190 g_signal_connect(G_OBJECT(bfwin->notebook), "tab2", G_CALLBACK(notebook_switch_by_key), (gpointer) 2);
1191 g_signal_connect(G_OBJECT(bfwin->notebook), "tab3", G_CALLBACK(notebook_switch_by_key), (gpointer) 3);
1192 g_signal_connect(G_OBJECT(bfwin->notebook), "tab4", G_CALLBACK(notebook_switch_by_key), (gpointer) 4);
1193 g_signal_connect(G_OBJECT(bfwin->notebook), "tab5", G_CALLBACK(notebook_switch_by_key), (gpointer) 5);
1194 g_signal_connect(G_OBJECT(bfwin->notebook), "tab6", G_CALLBACK(notebook_switch_by_key), (gpointer) 6);
1195 g_signal_connect(G_OBJECT(bfwin->notebook), "tab7", G_CALLBACK(notebook_switch_by_key), (gpointer) 7);
1196 g_signal_connect(G_OBJECT(bfwin->notebook), "tab8", G_CALLBACK(notebook_switch_by_key), (gpointer) 8);
1197 g_signal_connect(G_OBJECT(bfwin->notebook), "tab9", G_CALLBACK(notebook_switch_by_key), (gpointer) 9);
1198 }
1199
1200 void
notebook_set_tab_accels(Tbfwin * bfwin)1201 notebook_set_tab_accels(Tbfwin * bfwin)
1202 {
1203 static gsize create_signals_init = 0;
1204 GtkAccelGroup *tab_accels;
1205
1206 if (g_once_init_enter(&create_signals_init)) {
1207 DEBUG_MSG("notebook_set_tab_accels, g_signal_new for <Alt>X triggered events\n");
1208 g_signal_new("tab-last", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1209 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1210 g_signal_new("tab-first", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1211 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1212 g_signal_new("tab2", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1213 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1214 g_signal_new("tab3", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1215 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1216 g_signal_new("tab4", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1217 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1218 g_signal_new("tab5", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1219 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1220 g_signal_new("tab6", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1221 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1222 g_signal_new("tab7", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1223 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1224 g_signal_new("tab8", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1225 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1226 g_signal_new("tab9", gtk_widget_get_type(), G_SIGNAL_ACTION, 0, NULL, NULL,
1227 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
1228 g_once_init_leave(&create_signals_init, TRUE);
1229 }
1230
1231 tab_accels = gtk_accel_group_new();
1232 DEBUG_MSG("notebook_set_tab_accels, gtk_window_add_accel_group\n");
1233 gtk_window_add_accel_group(GTK_WINDOW(bfwin->main_window), tab_accels);
1234 DEBUG_MSG("notebook_set_tab_accels, gtk_widget_add_accelerator\n");
1235 /* MacOSX uses alt+0 .. alt+9 for alternative keyboard mapping, so we move these shorcuts to Command+1 etc. */
1236 #ifndef PLATFORM_DARWIN
1237 gtk_widget_add_accelerator(bfwin->notebook, "tab-last", tab_accels, GDK_0, GDK_MOD1_MASK,
1238 GTK_ACCEL_VISIBLE);
1239 gtk_widget_add_accelerator(bfwin->notebook, "tab-first", tab_accels, GDK_1, GDK_MOD1_MASK,
1240 GTK_ACCEL_VISIBLE);
1241 gtk_widget_add_accelerator(bfwin->notebook, "tab2", tab_accels, GDK_2, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1242 gtk_widget_add_accelerator(bfwin->notebook, "tab3", tab_accels, GDK_3, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1243 gtk_widget_add_accelerator(bfwin->notebook, "tab4", tab_accels, GDK_4, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1244 gtk_widget_add_accelerator(bfwin->notebook, "tab5", tab_accels, GDK_5, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1245 gtk_widget_add_accelerator(bfwin->notebook, "tab6", tab_accels, GDK_6, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1246 gtk_widget_add_accelerator(bfwin->notebook, "tab7", tab_accels, GDK_7, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1247 gtk_widget_add_accelerator(bfwin->notebook, "tab8", tab_accels, GDK_8, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1248 gtk_widget_add_accelerator(bfwin->notebook, "tab9", tab_accels, GDK_9, GDK_MOD1_MASK, GTK_ACCEL_VISIBLE);
1249 #else
1250 gtk_widget_add_accelerator(bfwin->notebook, "tab-last", tab_accels, GDK_0, GDK_META_MASK,
1251 GTK_ACCEL_VISIBLE);
1252 gtk_widget_add_accelerator(bfwin->notebook, "tab-first", tab_accels, GDK_1, GDK_META_MASK,
1253 GTK_ACCEL_VISIBLE);
1254 gtk_widget_add_accelerator(bfwin->notebook, "tab2", tab_accels, GDK_2, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1255 gtk_widget_add_accelerator(bfwin->notebook, "tab3", tab_accels, GDK_3, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1256 gtk_widget_add_accelerator(bfwin->notebook, "tab4", tab_accels, GDK_4, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1257 gtk_widget_add_accelerator(bfwin->notebook, "tab5", tab_accels, GDK_5, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1258 gtk_widget_add_accelerator(bfwin->notebook, "tab6", tab_accels, GDK_6, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1259 gtk_widget_add_accelerator(bfwin->notebook, "tab7", tab_accels, GDK_7, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1260 gtk_widget_add_accelerator(bfwin->notebook, "tab8", tab_accels, GDK_8, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1261 gtk_widget_add_accelerator(bfwin->notebook, "tab9", tab_accels, GDK_9, GDK_META_MASK, GTK_ACCEL_VISIBLE);
1262 #endif
1263 }
1264
1265 static gboolean
notebook_changed_activate_current_document(gpointer data)1266 notebook_changed_activate_current_document(gpointer data)
1267 {
1268 DEBUG_SIG("notebook_changed_activate_current_document, priority=%d\n",NOTEBOOKCHANGED_DOCACTIVATE_PRIORITY);
1269 DEBUG_MSG("notebook_changed_activate_current_document, current_document=%p\n",BFWIN(data)->current_document);
1270 if (BFWIN(data)->current_document)
1271 doc_activate(BFWIN(data)->current_document);
1272
1273 BFWIN(data)->notebook_changed_doc_activate_id = 0;
1274 DEBUG_MSG("notebook_changed_doc_activate_id=%d\n",BFWIN(data)->notebook_changed_doc_activate_id);
1275 return FALSE;
1276 }
1277
1278 void
bfwin_notebook_block_signals(Tbfwin * bfwin)1279 bfwin_notebook_block_signals(Tbfwin * bfwin)
1280 {
1281 g_signal_handler_block(G_OBJECT(bfwin->notebook), bfwin->notebook_switch_signal);
1282 }
1283
1284 void
bfwin_notebook_unblock_signals(Tbfwin * bfwin)1285 bfwin_notebook_unblock_signals(Tbfwin * bfwin)
1286 {
1287 g_signal_handler_unblock(G_OBJECT(bfwin->notebook), bfwin->notebook_switch_signal);
1288 }
1289
1290 static void
bfwin_current_document_changed_notify(Tbfwin * bfwin,Tdocument * olddoc,Tdocument * newdoc)1291 bfwin_current_document_changed_notify(Tbfwin *bfwin, Tdocument *olddoc, Tdocument *newdoc) {
1292 GSList *tmpslist;
1293 for (tmpslist=bfwin->curdoc_changed;tmpslist;tmpslist=g_slist_next(tmpslist)) {
1294 Tcallback *cb=tmpslist->data;
1295 DEBUG_MSG("bfwin_current_document_changed_notify, call %p, data=%p, bfwin=%p, olddoc=%p, newdoc=%p\n",cb->func,cb->data, bfwin,olddoc,newdoc);
1296 ((CurdocChangedCallback)cb->func)(bfwin, olddoc, newdoc, cb->data);
1297 }
1298 }
1299
1300 void
bfwin_current_document_change_register(Tbfwin * bfwin,CurdocChangedCallback func,gpointer data)1301 bfwin_current_document_change_register(Tbfwin *bfwin, CurdocChangedCallback func, gpointer data) {
1302 callback_register(&bfwin->curdoc_changed, func, data);
1303 }
1304
1305 void
bfwin_current_document_change_remove_by_data(Tbfwin * bfwin,gpointer data)1306 bfwin_current_document_change_remove_by_data(Tbfwin *bfwin, gpointer data) {
1307 callback_remove_by_data(&bfwin->curdoc_changed, data);
1308 }
1309
1310 void
bfwin_current_document_change_remove_all(Tbfwin * bfwin)1311 bfwin_current_document_change_remove_all(Tbfwin *bfwin) {
1312 callback_remove_all(&bfwin->curdoc_changed);
1313 }
1314
1315 void
bfwin_document_insert_text_register(Tbfwin * bfwin,DocInsertTextCallback func,gpointer data)1316 bfwin_document_insert_text_register(Tbfwin *bfwin, DocInsertTextCallback func, gpointer data) {
1317 callback_register(&bfwin->doc_insert_text, func, data);
1318 }
1319
1320 void
bfwin_document_insert_text_remove_by_data(Tbfwin * bfwin,gpointer data)1321 bfwin_document_insert_text_remove_by_data(Tbfwin *bfwin, gpointer data) {
1322 callback_remove_by_data(&bfwin->doc_insert_text, data);
1323 }
1324
1325 void
bfwin_document_delete_range_register(Tbfwin * bfwin,DocDeleteRangeCallback func,gpointer data)1326 bfwin_document_delete_range_register(Tbfwin *bfwin, DocDeleteRangeCallback func, gpointer data) {
1327 callback_register(&bfwin->doc_delete_range, func, data);
1328 }
1329
1330 void
bfwin_document_delete_range_remove_by_data(Tbfwin * bfwin,gpointer data)1331 bfwin_document_delete_range_remove_by_data(Tbfwin *bfwin, gpointer data) {
1332 callback_remove_by_data(&bfwin->doc_delete_range, data);
1333 }
1334
1335 void
bfwin_document_destroy_register(Tbfwin * bfwin,DocDestroyCallback func,gpointer data)1336 bfwin_document_destroy_register(Tbfwin *bfwin, DocDestroyCallback func, gpointer data)
1337 {
1338 callback_register(&bfwin->doc_destroy, func, data);
1339 }
1340 void
bfwin_document_destroy_remove_by_data(Tbfwin * bfwin,gpointer data)1341 bfwin_document_destroy_remove_by_data(Tbfwin *bfwin, gpointer data)
1342 {
1343 callback_remove_by_data(&bfwin->doc_destroy, data);
1344 }
1345
1346 void
bfwin_notebook_changed(Tbfwin * bfwin,gint newpage)1347 bfwin_notebook_changed(Tbfwin * bfwin, gint newpage)
1348 {
1349 gint cur = newpage;
1350 gint doclistlen;
1351 Tdocument *prev_document;
1352 DEBUG_MSG
1353 ("bfwin_notebook_changed, doclistlen=%d, newpage=%d, notebook_curpage=%d, last_notebook_page=%d, curdoc=%p\n",
1354 g_list_length(bfwin->documentlist)
1355 , newpage, gtk_notebook_get_current_page(GTK_NOTEBOOK(bfwin->notebook))
1356 , bfwin->last_notebook_page, bfwin->current_document);
1357 if (newpage == -1) {
1358 /* this returns -1 if there is no current page */
1359 cur = gtk_notebook_get_current_page(GTK_NOTEBOOK(bfwin->notebook));
1360 }
1361 if ((bfwin->last_notebook_page == cur)
1362 && (bfwin->current_document != NULL)
1363 && (bfwin->current_document == g_list_nth_data(bfwin->documentlist, cur))) {
1364 if (bfwin->project && bfwin->project->close)
1365 project_final_close(bfwin, FALSE);
1366 DEBUG_MSG
1367 ("bfwin_notebook_changed, NOT CHANGED cur=%d, documentlist[cur]==current_document (=%p), RETURNING\n",
1368 cur, bfwin->current_document);
1369 return;
1370 }
1371 doclistlen = g_list_length(bfwin->documentlist);
1372 if (cur == -1) {
1373 if (doclistlen > 0) {
1374 DEBUG_MSG("bfwin_notebook_changed, WEIRD 1 cur=%d, but doclistlen=%d RETURNING\n", cur,
1375 doclistlen);
1376 bfwin->last_notebook_page = -2;
1377 return;
1378 }
1379 }
1380 if (doclistlen == 0) {
1381 DEBUG_MSG("bfwin_notebook_changed, doclistlen=%d, before doc_new()!\n", doclistlen);
1382 if (bfwin->project && bfwin->project->close)
1383 project_final_close(bfwin, FALSE);
1384 bfwin->current_document = doc_new(bfwin, TRUE);
1385 bfwin_current_document_changed_notify(bfwin, NULL, bfwin->current_document);
1386 bfwin->last_notebook_page = 1;
1387 DEBUG_MSG("bfwin_notebook_changed, after doc_new(), returning\n");
1388 return;
1389 }
1390 /* if the documentlist has length 1, cur should not be larger then 0, if 2, cur should not be larger then 1, etc. */
1391 if (cur >= doclistlen) {
1392 DEBUG_MSG("bfwin_notebook_changed, DOCALREADYCLOSED, cur=%d, doclistlen=%d, RETURNING\n", cur,
1393 doclistlen);
1394 bfwin->last_notebook_page = -2;
1395 return;
1396 }
1397 if (bfwin->current_document) {
1398 /*g_print("notebook_changed, set enable_scanner to FALSE for doc %p\n",bfwin->current_document); */
1399 BLUEFISH_TEXT_VIEW(bfwin->current_document->view)->enable_scanner = FALSE;
1400 }
1401 prev_document = bfwin->current_document;
1402 bfwin->current_document = g_list_nth_data(bfwin->documentlist, cur);
1403 if (bfwin->current_document == NULL) {
1404 DEBUG_MSG("bfwin_notebook_changed, WEIRD 2, doclist[%d] == NULL, RETURNING\n", cur);
1405 return;
1406 }
1407 bfwin_current_document_changed_notify(bfwin, prev_document, bfwin->current_document);
1408
1409 bfwin->last_notebook_page = cur;
1410 DEBUG_MSG("bfwin_notebook_changed, current_document=%p, idle callback for doc activate = %d\n",
1411 bfwin->current_document, bfwin->notebook_changed_doc_activate_id);
1412 /* slightly lower than default priority so we make sure any events are handled first */
1413 if (bfwin->notebook_changed_doc_activate_id == 0) {
1414 bfwin->notebook_changed_doc_activate_id =
1415 g_idle_add_full(NOTEBOOKCHANGED_DOCACTIVATE_PRIORITY, notebook_changed_activate_current_document,
1416 bfwin, NULL);
1417 }
1418 }
1419
1420 /*
1421 child : the child GtkWidget affected
1422 page_num : the new page number for child
1423 */
1424 static void
notebook_reordered(GtkNotebook * notebook,GtkWidget * child,guint page_num,gpointer user_data)1425 notebook_reordered(GtkNotebook * notebook, GtkWidget * child, guint page_num, gpointer user_data)
1426 {
1427 Tbfwin *bfwin = BFWIN(user_data);
1428 Tdocument *doc = NULL;
1429 GList *tmplist = g_list_first(bfwin->documentlist);
1430
1431 DEBUG_MSG("notebook_reordered, started\n");
1432 /* look where this child (the GtkVPaned) is in the documentlist */
1433 while (tmplist) {
1434 if (DOCUMENT(tmplist->data)->vsplit == child) {
1435 doc = DOCUMENT(tmplist->data);
1436 break;
1437 }
1438 tmplist = g_list_next(tmplist);
1439 }
1440 if (!doc)
1441 return;
1442 bfwin->documentlist = g_list_remove(bfwin->documentlist, doc);
1443 DEBUG_MSG("notebook_reordered, moving doc %p to position %d in the documentlist\n", doc, page_num);
1444 bfwin->documentlist = g_list_insert(bfwin->documentlist, doc, page_num);
1445 DEBUG_MSG("notebook_reordered, done\n");
1446 }
1447
1448 /* Restores tab scrolling feature for gtk+3 builds */
1449 #if GTK_CHECK_VERSION (3, 0, 0)
1450 static gboolean
notebook_scroll_event_lcb(GtkNotebook * notebook,GdkEventScroll * event,gpointer user_data)1451 notebook_scroll_event_lcb (GtkNotebook * notebook, GdkEventScroll *event, gpointer user_data)
1452 {
1453 DEBUG_MSG("notebook_scroll_event, started for notebook=%p\n", notebook);
1454
1455 /* Some sanity check, which is required when document does not have scroll bar and scroll-events are not consumed by textview*/
1456 GtkWidget *child, *event_widget;
1457 child = gtk_notebook_get_nth_page (notebook, gtk_notebook_get_current_page (notebook));
1458 if (child == NULL)
1459 return FALSE;
1460 event_widget = gtk_get_event_widget ((GdkEvent *) event);
1461 if (event_widget == NULL || event_widget == child || gtk_widget_is_ancestor (event_widget, child))
1462 return FALSE;
1463
1464 switch (event->direction) {
1465 case GDK_SCROLL_RIGHT:
1466 case GDK_SCROLL_DOWN:
1467 gtk_notebook_next_page (notebook);
1468 break;
1469 case GDK_SCROLL_LEFT:
1470 case GDK_SCROLL_UP:
1471 gtk_notebook_prev_page (notebook);
1472 break;
1473 default:
1474 break;
1475 }
1476
1477 return TRUE;
1478 }
1479 #endif
1480
1481 static void
notebook_switch_page(GtkWidget * notebook,gpointer * page,gint page_num,Tbfwin * bfwin)1482 notebook_switch_page(GtkWidget * notebook, gpointer * page, gint page_num, Tbfwin * bfwin)
1483 {
1484 DEBUG_MSG("notebook_switch_page_lcb, page=%d\n", page_num);
1485 bfwin_notebook_changed(bfwin, page_num);
1486 }
1487
1488 static void
notebook_connect_signals(Tbfwin * bfwin)1489 notebook_connect_signals(Tbfwin *bfwin)
1490 {
1491 /* We have to know when the notebook changes */
1492 if (bfwin->notebook_switch_signal == 0) {
1493 bfwin->notebook_switch_signal =
1494 g_signal_connect_after(G_OBJECT(bfwin->notebook), "switch-page", G_CALLBACK(notebook_switch_page),
1495 bfwin);
1496 }
1497 }
1498
1499 static void
notebook_disconnect_signals(Tbfwin * bfwin)1500 notebook_disconnect_signals(Tbfwin *bfwin)
1501 {
1502 if (bfwin->notebook_switch_signal > 0) {
1503 g_signal_handler_disconnect(bfwin->notebook, bfwin->notebook_switch_signal);
1504 bfwin->notebook_switch_signal = 0;
1505 }
1506 }
1507
1508 static gboolean
bfwin_key_press_event(GtkWidget * widget,GdkEventKey * kevent,gpointer user_data)1509 bfwin_key_press_event(GtkWidget *widget,GdkEventKey *kevent,gpointer user_data)
1510 {
1511 /*g_print("bfwin_key_press_event, key=%d, state=%d\n",kevent->keyval,kevent->state);*/
1512 if (kevent->keyval == GDK_Tab && (kevent->state & GDK_CONTROL_MASK)) {
1513 gint i;
1514 Tdocument *doc;
1515 GList *tmplist;
1516 DEBUG_MSG("bfwin_key_press_event, control tab pressed\n");
1517 /* switch to the next recent document, without activating it */
1518 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(BFWIN(user_data)->notebook));
1519 /* we cannot use the bfwin->current_document because if we tab multiple documents
1520 with control-tab the current notebook page and the current document might be out
1521 of sync */
1522 doc = g_list_nth_data(BFWIN(user_data)->documentlist, i);
1523 if (!doc)
1524 return FALSE;
1525 tmplist = g_list_next(doc->recentpos);
1526 if (!tmplist)
1527 return FALSE;
1528 i = g_list_index(BFWIN(user_data)->documentlist, tmplist->data);
1529 if (i==-1)
1530 return FALSE;
1531 DEBUG_MSG("bfwin_key_press_event, set notebook page %d\n", i);
1532 notebook_disconnect_signals(BFWIN(user_data));
1533 gtk_notebook_set_current_page(GTK_NOTEBOOK(BFWIN(user_data)->notebook), i);
1534 return TRUE;
1535 }
1536 /*if (kevent->keyval == GDK_Control_L || kevent->keyval == GDK_Control_R) {
1537 g_print("control pressed\n");
1538 }*/
1539 return FALSE;
1540 }
1541
1542 static gboolean
bfwin_key_release_event(GtkWidget * widget,GdkEventKey * kevent,gpointer user_data)1543 bfwin_key_release_event(GtkWidget *widget, GdkEventKey *kevent, gpointer user_data)
1544 {
1545 /*g_print("bfwin_key_release_event, key=%d, state=%d\n",kevent->keyval,kevent->state);*/
1546 /*if (kevent->keyval == GDK_Tab && kevent->state & GDK_CONTROL_MASK) {
1547 g_print("control tab released\n");
1548 }*/
1549 if (kevent->keyval == GDK_Control_L || kevent->keyval == GDK_Control_R) {
1550 /* if we did switch to a recent document without activating it, we should activate it now */
1551 DEBUG_MSG("bfwin_key_release_event, control released\n");
1552 if (BFWIN(user_data)->notebook_switch_signal == 0) {
1553 gint i;
1554 notebook_connect_signals(BFWIN(user_data));
1555 i = gtk_notebook_get_current_page(GTK_NOTEBOOK(BFWIN(user_data)->notebook));
1556 DEBUG_MSG("bfwin_key_release_event, connect signals, and call bfwin_notebook_changed(%p, %d)\n",user_data,i);
1557 bfwin_notebook_changed(BFWIN(user_data), i);
1558 }
1559 }
1560 return FALSE;
1561 }
1562
1563 void
bfwin_create_main(Tbfwin * bfwin,gboolean with_new_doc)1564 bfwin_create_main(Tbfwin * bfwin, gboolean with_new_doc)
1565 {
1566 GtkWidget *vbox;
1567 DEBUG_MSG("bfwin_create_main, bfwin=%p, main_window_w=%d\n", bfwin, main_v->globses.main_window_w);
1568
1569 bftextview2_identifier_hash_init(bfwin);
1570 bfwin->main_window =
1571 window_full2(_("New Bluefish Window"), 0, 0, G_CALLBACK(bfwin_destroy_event),
1572 bfwin, FALSE, NULL);
1573 gtk_window_set_role(GTK_WINDOW(bfwin->main_window), "bluefish");
1574 /*#ifndef MAC_INTEGRATION
1575 gtk_widget_realize(bfwin->main_window); / * Causes half baked window entry to appear on OSX WIndow menu. Probably this is not required also on other systems * /
1576 #endif
1577 */ if (!main_v->props.leave_to_window_manager) {
1578 if (main_v->globses.main_window_w > 0) {
1579 gtk_window_set_default_size(GTK_WINDOW(bfwin->main_window), main_v->globses.main_window_w,
1580 main_v->globses.main_window_h);
1581 } else {
1582 gtk_window_set_default_size(GTK_WINDOW(bfwin->main_window), main_v->globses.main_window_w * -1,
1583 main_v->globses.main_window_h);
1584 gtk_window_maximize(GTK_WINDOW(bfwin->main_window));
1585 }
1586 }
1587 g_signal_connect(G_OBJECT(bfwin->main_window), "delete_event", G_CALLBACK(bfwin_delete_event), bfwin);
1588 g_signal_connect(G_OBJECT(bfwin->main_window), "configure-event",
1589 G_CALLBACK(bfwin_configure_event), bfwin);
1590 g_signal_connect(G_OBJECT(bfwin->main_window), "window-state-event",
1591 G_CALLBACK(bfwin_window_state_event), bfwin);
1592 g_signal_connect(G_OBJECT(bfwin->main_window), "key-press-event", G_CALLBACK(bfwin_key_press_event), bfwin);
1593 g_signal_connect(G_OBJECT(bfwin->main_window), "key-release-event", G_CALLBACK(bfwin_key_release_event), bfwin);
1594
1595 vbox = gtk_vbox_new(FALSE, 0);
1596 gtk_container_add(GTK_CONTAINER(bfwin->main_window), vbox);
1597 gtk_widget_show(vbox);
1598 #if GTK_CHECK_VERSION(3,4,0)
1599 bfwin->toolbarbox = gtk_grid_new();
1600 gtk_orientable_set_orientation(GTK_ORIENTABLE(bfwin->toolbarbox), GTK_ORIENTATION_VERTICAL);
1601 gtk_box_pack_start(GTK_BOX(vbox), bfwin->toolbarbox, FALSE, FALSE, 0);
1602 gtk_widget_show(bfwin->toolbarbox);
1603 #else
1604 bfwin->toolbarbox = gtk_vbox_new(FALSE, 0);
1605 gtk_box_pack_start(GTK_BOX(vbox), bfwin->toolbarbox, FALSE, FALSE, 0);
1606 gtk_widget_show(bfwin->toolbarbox);
1607 #endif
1608 /* first a menubar */
1609 bfwin->uimanager = gtk_ui_manager_new();
1610 #if !GTK_CHECK_VERSION(3,4,0)
1611 gtk_ui_manager_set_add_tearoffs(bfwin->uimanager, TRUE);
1612 #endif
1613 bfwin_main_ui_init(bfwin, bfwin->toolbarbox);
1614
1615 /* then the toolbars */
1616 DEBUG_MSG("bfwin_create_main, creating handles for all menu/toolbars\n");
1617
1618 if (bfwin->session->view_main_toolbar) {
1619 /*bfwin_main_toolbar_init(bfwin);*/
1620 gtk_widget_show(bfwin->main_toolbar_hb);
1621 }
1622
1623 /* the area for the middlebox and the outputbox */
1624 #if GTK_CHECK_VERSION(3,0,0)
1625 bfwin->vpane = gtk_paned_new(GTK_ORIENTATION_VERTICAL);
1626 #else
1627 bfwin->vpane = gtk_vpaned_new();
1628 #endif
1629 gtk_box_pack_start(GTK_BOX(vbox), bfwin->vpane, TRUE, TRUE, 0);
1630 /* then the area for left panel and */
1631 bfwin->middlebox = gtk_hbox_new(TRUE, 0);
1632 gtk_paned_add1(GTK_PANED(bfwin->vpane), bfwin->middlebox);
1633 gtk_widget_show(bfwin->middlebox);
1634 gtk_widget_show(bfwin->vpane);
1635
1636 /* Fake-label (for notebook_hide() and _show() ;) */
1637 bfwin->notebook_fake = gtk_label_new(_("Stand by..."));
1638
1639 /* notebook with the text widget in there */
1640 bfwin->notebook = gtk_notebook_new();
1641 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(bfwin->notebook), main_v->props.document_tabposition);
1642 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(bfwin->notebook), TRUE);
1643 gtk_notebook_set_show_border(GTK_NOTEBOOK(bfwin->notebook), FALSE);
1644 #if !GTK_CHECK_VERSION(3, 0, 0)
1645 gtk_notebook_set_tab_border(GTK_NOTEBOOK(bfwin->notebook), 0);
1646 #endif
1647 gtk_notebook_popup_enable(GTK_NOTEBOOK(bfwin->notebook));
1648
1649 /* Add notebook and its fake friend to their common hbox. */
1650 bfwin->notebook_box = gtk_vbox_new(FALSE, 0);
1651 gotoline_frame_create(bfwin);
1652 gtk_box_pack_start(GTK_BOX(bfwin->notebook_box), bfwin->notebook, TRUE, TRUE, 0);
1653 gtk_box_pack_start(GTK_BOX(bfwin->notebook_box), bfwin->notebook_fake, TRUE, TRUE, 0);
1654
1655 /* here we ask any plugins to build there gui, BEFORE WE START THE SIDEPANEL */
1656 g_slist_foreach(main_v->plugins, bfplugins_gui, bfwin);
1657
1658 /* output_box * /
1659 init_output_box(bfwin, vbox); */
1660 bfwin->outputbox = NULL;
1661
1662 bfwin_side_panel_show_hide_toggle(bfwin, TRUE, (bfwin->session->view_left_panel), FALSE);
1663
1664 /* finally the statusbar */
1665 {
1666 GtkWidget *hbox;
1667 gint onecharwidth;
1668 hbox = gtk_hbox_new(FALSE, 0);
1669 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1670 bfwin->statusbar = gtk_statusbar_new();
1671 gtk_box_pack_start(GTK_BOX(hbox), bfwin->statusbar, TRUE, TRUE, 0);
1672 bfwin->statusbar_lncol = gtk_statusbar_new();
1673 gtk_box_pack_start(GTK_BOX(hbox), bfwin->statusbar_lncol, FALSE, FALSE, 0);
1674 /* I hope the 'w' is an average width character, the characters are usually not monospaced so these values are just averages that look good on most translations too */
1675 onecharwidth = widget_get_string_size(bfwin->statusbar_lncol, "x");
1676 gtk_widget_set_size_request(GTK_WIDGET(bfwin->statusbar_lncol), onecharwidth * 30, -1);
1677 bfwin->statusbar_insovr = gtk_statusbar_new();
1678 gtk_box_pack_start(GTK_BOX(hbox), bfwin->statusbar_insovr, FALSE, FALSE, 0);
1679 gtk_widget_set_size_request(GTK_WIDGET(bfwin->statusbar_insovr), onecharwidth * 6, -1);
1680 bfwin->statusbar_editmode = gtk_statusbar_new();
1681 gtk_box_pack_start(GTK_BOX(hbox), bfwin->statusbar_editmode, FALSE, FALSE, 0);
1682 gtk_widget_set_size_request(GTK_WIDGET(bfwin->statusbar_editmode), onecharwidth * 35, -1);
1683 #if GTK_CHECK_VERSION(3, 0, 0)
1684 gtk_window_set_has_resize_grip(GTK_WINDOW(bfwin->main_window), TRUE);
1685 #else
1686 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(bfwin->statusbar_lncol), FALSE);
1687 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(bfwin->statusbar_insovr), FALSE);
1688 gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(bfwin->statusbar), FALSE);
1689 #endif /* gtk3 */
1690 if (bfwin->session->view_statusbar)
1691 gtk_widget_show_all(hbox);
1692 else
1693 gtk_widget_show(hbox);
1694 }
1695 notebook_connect_signals(bfwin);
1696 g_signal_connect(G_OBJECT(bfwin->notebook), "page-reordered", G_CALLBACK(notebook_reordered), bfwin);
1697 #if GTK_CHECK_VERSION (3, 0, 0) /* Restores tab scrolling feature for gtk+3 builds */
1698 gtk_widget_add_events (bfwin->notebook, GDK_SCROLL_MASK);
1699 g_signal_connect (G_OBJECT(bfwin->notebook), "scroll-event", G_CALLBACK (notebook_scroll_event_lcb), bfwin);
1700 #endif
1701
1702 if (main_v->props.switch_tabs_by_altx) {
1703 notebook_set_tab_accels(bfwin);
1704 notebook_bind_tab_signals(bfwin);
1705 }
1706 /* everything is ready - we can start loading documents */
1707 /* start to open an empty doc */
1708 if (with_new_doc)
1709 file_new_cb(NULL, bfwin);
1710
1711 gtk_notebook_set_scrollable(GTK_NOTEBOOK(bfwin->notebook), TRUE);
1712 /* don't use show_all since some widgets are and should be hidden */
1713 gtk_widget_show(bfwin->notebook);
1714 gtk_widget_show(bfwin->notebook_box);
1715
1716 {
1717 /* drag n drop support */
1718 const GtkTargetEntry drag_dest_types[] = {
1719 {"text/uri-list", 0, TARGET_URI_LIST},
1720 {"STRING", 0, TARGET_STRING},
1721 };
1722 gtk_drag_dest_set(bfwin->main_window, (GTK_DEST_DEFAULT_ALL)
1723 , drag_dest_types, 2, (GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE |
1724 GDK_ACTION_LINK | GDK_ACTION_PRIVATE | GDK_ACTION_ASK));
1725 g_signal_connect(G_OBJECT(bfwin->main_window), "drag_data_received",
1726 G_CALLBACK(bfwin_on_drag_data), bfwin);
1727 }
1728 }
1729
1730 #ifdef MAC_INTEGRATION
1731 /* first move all accelerators away from <control> to <command>, and then in a second run
1732 move the <alt> accelerators to <control> (alt doesn't work on osx)
1733 * Modifiers mapping on osx
1734 * MOD1 = Option/Alt
1735 * MOD2 = META = Command
1736 * CONTROL = Control
1737 */
1738 static void
osx_accel_map_foreach_controltometa_lcb(gpointer data,const gchar * accel_path,guint accel_key,GdkModifierType accel_mods,gboolean changed)1739 osx_accel_map_foreach_controltometa_lcb(gpointer data, const gchar * accel_path, guint accel_key,
1740 GdkModifierType accel_mods, gboolean changed)
1741 {
1742 if (accel_mods & GDK_CONTROL_MASK) {
1743 accel_mods &= (accel_mods & GDK_MOD1_MASK) ? ~GDK_MOD1_MASK : ~GDK_CONTROL_MASK;
1744 accel_mods |= GDK_META_MASK;
1745 if (!gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, FALSE)) {
1746 g_print("controltometa, could not change accelerator %s\n", accel_path);
1747 }
1748 }
1749 }
1750
1751 static void
osx_accel_map_foreach_mod1tocontrol_lcb(gpointer data,const gchar * accel_path,guint accel_key,GdkModifierType accel_mods,gboolean changed)1752 osx_accel_map_foreach_mod1tocontrol_lcb(gpointer data, const gchar * accel_path, guint accel_key,
1753 GdkModifierType accel_mods, gboolean changed)
1754 {
1755 if (accel_mods & GDK_MOD1_MASK) {
1756 accel_mods &= ~GDK_MOD1_MASK;
1757 accel_mods |= GDK_CONTROL_MASK;
1758 if (!gtk_accel_map_change_entry(accel_path, accel_key, accel_mods, FALSE)) {
1759 g_print("mod1tocontrol, could not change accelerator %s\n", accel_path);
1760 }
1761 }
1762 }
1763 #endif
1764
1765 void
bfwin_show_main(Tbfwin * bfwin)1766 bfwin_show_main(Tbfwin * bfwin)
1767 {
1768 #ifdef MAC_INTEGRATION
1769 GtkWidget *menuitem;
1770 GtkosxApplication *theApp = g_object_new(GTKOSX_TYPE_APPLICATION, NULL);
1771 gtk_widget_hide(bfwin->menubar);
1772 gtkosx_application_set_menu_bar(theApp, GTK_MENU_SHELL(bfwin->menubar));
1773 DEBUG_MSG("hide gtk menubar, set gtkosxapplication menubar\n");
1774 /*This arrangement gives more mackish ordering of menu. TODO Window menu does not track opened toplevels correctly; see bug #701571; for 2.2.5 temporal hack applied to the gdk/quartz/gdkeventloop-quartz.c */
1775 gint menupos = 0;
1776 if (bfwin_action_group_is_available(bfwin->uimanager, "AboutActions")) { /*Since it is plugin, user can disable it, and this leads to bf crash */
1777 menuitem = gtk_ui_manager_get_widget(bfwin->uimanager, "/MainMenu/HelpMenu");
1778 gtkosx_application_set_help_menu (theApp, menuitem);
1779 menuitem = gtk_ui_manager_get_widget(bfwin->uimanager, "/MainMenu/HelpMenu/HelpAbout");
1780 gtkosx_application_insert_app_menu_item(theApp, menuitem, 0);
1781 gtkosx_application_insert_app_menu_item (theApp, g_object_ref(gtk_separator_menu_item_new ()), 1);
1782 menupos = 2;
1783 }
1784 gtkosx_application_set_window_menu (theApp, NULL);
1785
1786 menuitem = gtk_ui_manager_get_widget(bfwin->uimanager, "/MainMenu/EditMenu/EditPreferences");
1787 gtkosx_application_insert_app_menu_item(theApp, menuitem, menupos);
1788
1789 menuitem = gtk_ui_manager_get_widget(bfwin->uimanager, "/MainMenu/FileMenu/FileQuit");
1790 gtk_widget_hide(menuitem);
1791
1792 gtkosx_application_set_use_quartz_accelerators (theApp, FALSE); /* If set to TRUE then widgets does not respond to bindings like Cmd+X|C|V TODO: find the way to make MacOSX shortcuts Cmd+M and Cmd+H work; for 2.2.5 hack applied to gdk/quartz/gdkkeys-quartz.c see bug #711019*/
1793
1794 if (main_v->osx_status == 0 && g_list_length(main_v->bfwinlist) == 1) { /* Accelarators should be moved just once, at the startup */
1795 DEBUG_MSG("bfwin_show_main, configuring accelerators for OSX\n");
1796 gtk_accel_map_foreach_unfiltered(NULL, osx_accel_map_foreach_controltometa_lcb);
1797 gtk_accel_map_foreach_unfiltered(NULL, osx_accel_map_foreach_mod1tocontrol_lcb);
1798 }
1799
1800 /*
1801 IgeMacMenuGroup *group;
1802 gtk_widget_hide(bfwin->menubar);
1803
1804 ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(bfwin->menubar));
1805
1806 ifactory = gtk_item_factory_from_widget(bfwin->menubar);
1807 menuitem = gtk_item_factory_get_widget(ifactory, _("/File/Quit"));
1808 ige_mac_menu_set_quit_menu_item(GTK_MENU_ITEM(menuitem));
1809
1810 group = ige_mac_menu_add_app_menu_group ();
1811 ige_mac_menu_add_app_menu_item(group,GTK_MENU_ITEM(menuitem),NULL);
1812
1813 menuitem = gtk_item_factory_get_widget(ifactory, _("/Edit/Preferences"));
1814 group = ige_mac_menu_add_app_menu_group();
1815 ige_mac_menu_add_app_menu_item(group,GTK_MENU_ITEM(menuitem),NULL);
1816 ige_mac_menu_set_global_key_handler_enabled(TRUE);*/
1817
1818 /* MACTODO: add focus in and focus out event so we can sync the menu
1819 when we switch to a different bluefish window */
1820
1821 if (main_v->osx_status == 2) {
1822
1823 bfwin_action_groups_set_sensitive(bfwin, FALSE);
1824
1825 return;
1826 }
1827 #endif
1828 DEBUG_MSG("bfwin_show_main, before show\n");
1829 gtk_widget_show(bfwin->main_window);
1830 /* don't use show_all since some widgets are and should be hidden */
1831 }
1832
1833 /***********************/
1834 /* statusbar functions */
1835 /***********************/
1836
1837 typedef struct {
1838 gint message_id;
1839 Tbfwin *bfwin;
1840 } Tstatusbar_remove;
1841
1842 static
1843 gboolean
statusbar_pop(gpointer data)1844 statusbar_pop(gpointer data)
1845 {
1846 if (BFWIN(data)->statusbar)
1847 gtk_statusbar_pop(GTK_STATUSBAR(BFWIN(data)->statusbar), 0);
1848
1849 BFWIN(data)->statusbar_pop_id = 0;
1850
1851 return FALSE;
1852 }
1853
1854 void
bfwin_statusbar_message(Tbfwin * bfwin,const gchar * message,gint seconds)1855 bfwin_statusbar_message(Tbfwin * bfwin, const gchar * message, gint seconds)
1856 {
1857 if (bfwin->statusbar) {
1858 if (bfwin->statusbar_pop_id != 0) {
1859 g_source_remove(bfwin->statusbar_pop_id);
1860 gtk_statusbar_pop(GTK_STATUSBAR(bfwin->statusbar), 0);
1861 }
1862 gtk_statusbar_push(GTK_STATUSBAR(bfwin->statusbar), 0, message);
1863 bfwin->statusbar_pop_id = g_timeout_add_seconds(seconds, statusbar_pop, bfwin);
1864 }
1865 }
1866
1867 void
bfwin_all_statusbar_message(const gchar * message,gint seconds)1868 bfwin_all_statusbar_message(const gchar * message, gint seconds)
1869 {
1870 GList *tmplist = g_list_first(main_v->bfwinlist);
1871 while (tmplist) {
1872 bfwin_statusbar_message(BFWIN(tmplist->data), message, seconds);
1873 tmplist = g_list_next(tmplist);
1874 }
1875 }
1876
1877 Tbfwin *
bfwin_window_new_with_project(Tproject * project,gboolean with_new_doc)1878 bfwin_window_new_with_project(Tproject * project, gboolean with_new_doc)
1879 {
1880 Tbfwin *bfwin = g_new0(Tbfwin, 1);
1881
1882 g_return_val_if_fail(bfwin != NULL, NULL);
1883 g_return_val_if_fail(project != NULL, NULL);
1884
1885 bfwin->project = project;
1886 bfwin->session = project->session;
1887 bfwin->bmarkdata = project->bmarkdata;
1888 DEBUG_MSG("bfwin_window_new_with_project, bfwin=%p, bfwin->session=%p, (from project %p)\n", bfwin,
1889 bfwin->session, project);
1890
1891 bfwin_create_main(bfwin, with_new_doc);
1892 main_v->bfwinlist = g_list_append(main_v->bfwinlist, bfwin);
1893 bfwin_show_main(bfwin);
1894
1895 return bfwin;
1896 }
1897
1898 Tbfwin *
bfwin_window_new(void)1899 bfwin_window_new(void)
1900 {
1901 Tbfwin *bfwin = g_new0(Tbfwin, 1);
1902
1903 g_return_val_if_fail(bfwin != NULL, NULL);
1904
1905 bfwin->session = main_v->session;
1906 bfwin->bmarkdata = main_v->bmarkdata;
1907 DEBUG_MSG("bfwin_window_new, bfwin=%p, (from main_v)\n", bfwin);
1908
1909 bfwin_create_main(bfwin, TRUE);
1910 main_v->bfwinlist = g_list_append(main_v->bfwinlist, bfwin);
1911 bfwin_show_main(bfwin);
1912
1913 return bfwin;
1914 }
1915
1916 void
bfwin_window_close(Tbfwin * bfwin)1917 bfwin_window_close(Tbfwin * bfwin)
1918 {
1919 bfwin_delete_event(NULL, NULL, bfwin);
1920 }
1921
1922 gboolean
bfwin_has_doc(Tbfwin * bfwin,Tdocument * doc)1923 bfwin_has_doc(Tbfwin * bfwin, Tdocument * doc)
1924 {
1925 return (g_list_index(bfwin->documentlist, doc) >= 0);
1926 }
1927
1928 void
bfwin_docs_not_complete(Tbfwin * bfwin,gboolean increase)1929 bfwin_docs_not_complete(Tbfwin * bfwin, gboolean increase)
1930 {
1931 if (increase)
1932 bfwin->num_docs_not_completed++;
1933 else
1934 bfwin->num_docs_not_completed--;
1935 DEBUG_MSG("bfwin_docs_not_complete, increase=%d, num=%d\n", increase, bfwin->num_docs_not_completed);
1936 }
1937
1938 void
bfwin_set_title(Tbfwin * bfwin,Tdocument * doc,gint num_modified_change)1939 bfwin_set_title(Tbfwin * bfwin, Tdocument * doc, gint num_modified_change)
1940 {
1941 gchar *title, *prfilepart;
1942 gint modified_docs;
1943 const gchar *tablabel;
1944
1945 if (!doc)
1946 return;
1947
1948 modified_docs = have_modified_documents(bfwin->documentlist);
1949 if (num_modified_change != 0 && (modified_docs > 1 || (modified_docs == 0 && num_modified_change != -1)
1950 || (modified_docs == 1 && num_modified_change != 1))) {
1951 /* no title change */
1952 return;
1953 }
1954 tablabel = gtk_label_get_text(GTK_LABEL(doc->tab_label));
1955 if (doc->uri) {
1956 gchar *curi;
1957 if (g_file_is_native(doc->uri)) {
1958 curi = g_file_get_parse_name(doc->uri);
1959 } else {
1960 curi = g_file_get_uri(doc->uri);
1961 }
1962 if (bfwin->project) {
1963 prfilepart = g_strconcat(bfwin->project->name, " - ", tablabel, " (", curi, ")", NULL);
1964 } else {
1965 prfilepart = g_strconcat(tablabel, " (", curi, ")", NULL);
1966 }
1967 g_free(curi);
1968 } else {
1969 if (bfwin->project) {
1970 prfilepart = g_strconcat(bfwin->project->name, " - ", tablabel, NULL);
1971 } else {
1972 prfilepart = g_strconcat(tablabel, NULL);
1973 }
1974 }
1975 if (doc->modified) {
1976 title = g_strconcat("* ", prfilepart, " - Bluefish " VERSION, NULL);
1977 } else {
1978 title = g_strconcat(prfilepart, " - Bluefish " VERSION, NULL);
1979 }
1980 if (main_v->props.max_window_title > 0 && strlen(title) > main_v->props.max_window_title)
1981 title[main_v->props.max_window_title] = '\0';
1982 gtk_window_set_title(GTK_WINDOW(bfwin->main_window), title);
1983 /*rename_window_entry_in_all_windows(bfwin, title); */
1984 g_free(title);
1985 g_free(prfilepart);
1986 }
1987
1988 /* use -1 to switch to the last page */
1989 gboolean
bfwin_switch_to_document_by_index(Tbfwin * bfwin,gint i)1990 bfwin_switch_to_document_by_index(Tbfwin * bfwin, gint i)
1991 {
1992 DEBUG_MSG("bfwin_switch_to_document_by_index, index=%d\n", i);
1993 gtk_notebook_set_current_page(GTK_NOTEBOOK(bfwin->notebook), i);
1994 return TRUE;
1995 }
1996
1997 gboolean
bfwin_switch_to_document_by_pointer(Tbfwin * bfwin,Tdocument * document)1998 bfwin_switch_to_document_by_pointer(Tbfwin * bfwin, Tdocument * document)
1999 {
2000 gint i = g_list_index(bfwin->documentlist, document);
2001 if (i==-1)
2002 return FALSE;
2003 return bfwin_switch_to_document_by_index(bfwin, i);
2004 }
2005