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