1 /*
2  *  extrasel.c
3  *
4  *  Copyright 2010 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23 
24 #include <gdk/gdkkeysyms.h>
25 
26 #include <geanyplugin.h>
27 
28 GeanyPlugin	*geany_plugin;
29 GeanyData	*geany_data;
30 
31 PLUGIN_VERSION_CHECK(224)
32 
33 PLUGIN_SET_TRANSLATABLE_INFO(LOCALEDIR, GETTEXT_PACKAGE,
34 	_("Extra Selection"), _("Column mode, select to line / brace / anchor."
35 	"\nThis plugin currently has no maintainer. Would you like to help by "
36 	"contributing to this plugin?"),
37 	"0.52", "Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>")
38 
39 /* Keybinding(s) */
40 enum
41 {
42 	COLUMN_MODE_KB,
43 	GOTO_LINE_EXTEND_KB,
44 	BRACE_MATCH_EXTEND_KB,
45 	CONVERT_SELECTION_KB,
46 	SET_ANCHOR_KB,
47 	ANCHOR_EXTEND_KB,
48 	ANCHOR_RECTEXTEND_KB,
49 	COUNT_KB
50 };
51 
52 static GtkWidget *main_menu_item = NULL;
53 static GtkCheckMenuItem *column_mode_item;
54 static GtkWidget *anchor_rect_select_item;
55 static gpointer *go_to_line1_item = NULL;
56 
57 static gint column_mode = FALSE;
58 static gint plugin_internal_callback = FALSE;
59 static gint select_anchor = 0, select_space = 0;
60 
61 /* Common functions / macros */
62 
scintilla_get_current(void)63 static ScintillaObject *scintilla_get_current(void)
64 {
65 	GeanyDocument *doc = document_get_current();
66 	return doc ? doc->editor->sci : NULL;
67 }
68 
69 #define SSM(s, m, w, l) scintilla_send_message(s, m, w, l)
70 #define sci_get_anchor(sci) SSM(sci, SCI_GETANCHOR, 0, 0)
71 #define sci_set_anchor(sci, anchor) SSM(sci, SCI_SETANCHOR, anchor, 0)
72 #define sci_get_main_selection(sci) SSM(sci, SCI_GETMAINSELECTION, 0, 0)
73 #define sci_rectangle_selection(sci) (sci_get_selection_mode(sci) == SC_SEL_RECTANGLE || \
74 	sci_get_selection_mode(sci) == SC_SEL_THIN)
75 #define sci_virtual_space(sci, ACTION, OBJECT, space) \
76 	(sci_rectangle_selection(sci) ? \
77 	SSM(sci, ACTION##RECTANGULARSELECTION##OBJECT##VIRTUALSPACE, space, 0) : \
78 	SSM(sci, ACTION##SELECTIONN##OBJECT##VIRTUALSPACE, sci_get_main_selection(sci), space))
79 #define sci_set_virtual_space(sci, OBJECT, space) \
80 	do { if (space) sci_virtual_space(sci, SCI_SET, OBJECT, space); } while(0)
81 #define sci_get_anchor_space(sci) sci_virtual_space(sci, SCI_GET, ANCHOR, 0)
82 #define sci_set_anchor_space(sci, space) sci_set_virtual_space(sci, ANCHOR, space)
83 #define sci_get_cursor_space(sci) sci_virtual_space(sci, SCI_GET, CARET, 0)
84 #define sci_set_cursor_space(sci, space) sci_set_virtual_space(sci, CARET, space)
85 
create_selection(ScintillaObject * sci,int anchor,int anchor_space,gboolean rectangle)86 static void create_selection(ScintillaObject *sci, int anchor, int anchor_space,
87 	gboolean rectangle)
88 {
89 	int cursor = sci_get_current_position(sci);
90 	int cursor_space = sci_get_cursor_space(sci);
91 
92 	if (rectangle)
93 	{
94 		sci_set_selection_mode(sci, SC_SEL_RECTANGLE);
95 		sci_set_anchor(sci, anchor);
96 		/* sci_set_current_position() sets anchor = cursor, bypass */
97 		scintilla_send_message(sci, SCI_SETCURRENTPOS, cursor, 0);
98 	}
99 	else
100 	{
101 		sci_set_selection_mode(sci, SC_SEL_STREAM);
102 		scintilla_send_message(sci, SCI_SETSEL, anchor, cursor);
103 	}
104 
105 	sci_set_anchor_space(sci, anchor_space);
106 	sci_set_cursor_space(sci, cursor_space);
107 	sci_send_command(sci, SCI_CANCEL);
108 }
109 
convert_selection(ScintillaObject * sci,gboolean rectangle)110 static void convert_selection(ScintillaObject *sci, gboolean rectangle)
111 {
112 	if (sci_has_selection(sci))
113 		create_selection(sci, sci_get_anchor(sci), sci_get_anchor_space(sci), rectangle);
114 }
115 
116 /* New rectangle selection keys */
117 
118 typedef struct _command_key
119 {
120 	guint key;
121 	guint keypad;
122 	int command;
123 } command_key;
124 
125 static const command_key command_keys[] =
126 {
127 	{ GDK_Up,     GDK_KP_Up,     SCI_PARAUP },
128 	{ GDK_Down,   GDK_KP_Down,   SCI_PARADOWN },
129 	{ GDK_Left,   GDK_KP_Left,   SCI_WORDLEFT },
130 	{ GDK_Right,  GDK_KP_Right,  SCI_WORDRIGHTEND },
131 	{ GDK_Home,   GDK_KP_Home,   SCI_DOCUMENTSTART },
132 	{ GDK_End,    GDK_KP_End,    SCI_DOCUMENTEND },
133 	{ GDK_Prior,  GDK_KP_Prior,  0 },
134 	{ GDK_Next,   GDK_KP_Next,   0 },
135 	{ 0, 0, 0 }
136 };
137 
column_mode_command(ScintillaObject * sci,int command)138 static void column_mode_command(ScintillaObject *sci, int command)
139 {
140 	/* In the current SCI versions, using the command_keys->command for
141 	   rectangular selection creates various problems. So we select a
142 	   stream instead, and convert it to rectangle. It's slower, but all
143 	   command-s move the cursor at least a word. */
144 	int anchor = sci_get_anchor(sci);
145 	int anchor_space = sci_get_anchor_space(sci);
146 
147 	sci_set_selection_mode(sci, SC_SEL_STREAM);
148 	sci_send_command(sci, command);
149 	create_selection(sci, anchor, anchor_space, TRUE);
150 }
151 
on_key_press_event(GtkWidget * widget,GdkEventKey * event,G_GNUC_UNUSED gpointer gdata)152 static gboolean on_key_press_event(GtkWidget *widget, GdkEventKey *event,
153 	G_GNUC_UNUSED gpointer gdata)
154 {
155 	guint mask = GDK_CONTROL_MASK | GDK_SHIFT_MASK | (column_mode ? 0 : GDK_MOD1_MASK);
156 	guint state = event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK | GDK_MOD1_MASK);
157 
158 	if (state == mask)
159 	{
160 		const command_key *ck;
161 
162 		for (ck = command_keys; ck->command; ck++)
163 		{
164 			if (event->keyval == ck->key || event->keyval == ck->keypad)
165 			{
166 				ScintillaObject *sci = scintilla_get_current();
167 
168 				if (sci && gtk_window_get_focus(GTK_WINDOW(widget)) == GTK_WIDGET(sci))
169 				{
170 					column_mode_command(sci, ck->command);
171 					return TRUE;
172 				}
173 				break;
174 			}
175 		}
176 	}
177 	else if (!column_mode && state == GDK_SHIFT_MASK)
178 	{
179 		const command_key *ck;
180 
181 		for (ck = command_keys; ck->key; ck++)
182 		{
183 			if (event->keyval == ck->key || event->keyval == ck->keypad)
184 			{
185 				ScintillaObject *sci = scintilla_get_current();
186 
187 				if (sci && sci_has_selection(sci) && sci_rectangle_selection(sci) &&
188 					gtk_window_get_focus(GTK_WINDOW(widget)) == GTK_WIDGET(sci))
189 				{
190 					/* not exactly a bug, but... */
191 					convert_selection(sci, FALSE);
192 				}
193 				break;
194 			}
195 		}
196 	}
197 
198 	return FALSE;
199 }
200 
201 /* Column mode */
202 
203 typedef struct _select_key
204 {
205 	int key;
206 	int stream;
207 	int rectangle;
208 } select_key;
209 
210 #define sci_clear_cmdkey(sci, key) scintilla_send_message((sci), SCI_CLEARCMDKEY, (key), 0)
211 #define sci_assign_cmdkey(sci, key, command) \
212 	scintilla_send_message((sci), SCI_ASSIGNCMDKEY, (key), (command))
213 
214 static select_key select_keys[] =
215 {
216 	{ SCK_HOME  | (SCMOD_SHIFT << 16),  SCI_NULL,             SCI_NULL },
217 	{ SCK_UP    | (SCMOD_SHIFT << 16),  SCI_LINEUPEXTEND,     SCI_LINEUPRECTEXTEND },
218 	{ SCK_DOWN  | (SCMOD_SHIFT << 16),  SCI_LINEDOWNEXTEND,   SCI_LINEDOWNRECTEXTEND },
219 	{ SCK_LEFT  | (SCMOD_SHIFT << 16),  SCI_CHARLEFTEXTEND,   SCI_CHARLEFTRECTEXTEND  },
220 	{ SCK_RIGHT | (SCMOD_SHIFT << 16),  SCI_CHARRIGHTEXTEND,  SCI_CHARRIGHTRECTEXTEND },
221 	{ SCK_END   | (SCMOD_SHIFT << 16),  SCI_LINEENDEXTEND,    SCI_LINEENDRECTEXTEND },
222 	{ SCK_PRIOR | (SCMOD_SHIFT << 16),  SCI_PAGEUPEXTEND,     SCI_PAGEUPRECTEXTEND },
223 	{ SCK_NEXT  | (SCMOD_SHIFT << 16),  SCI_PAGEDOWNEXTEND,   SCI_PAGEDOWNRECTEXTEND },
224 	{ 0, 0, 0 }
225 };
226 
assign_select_keys(ScintillaObject * sci)227 static void assign_select_keys(ScintillaObject *sci)
228 {
229 	const select_key *sk;
230 
231 	for (sk = select_keys; sk->key; sk++)
232 	{
233 		if (column_mode)
234 		{
235 			sci_clear_cmdkey(sci, sk->key | (SCMOD_ALT << 16));
236 			sci_assign_cmdkey(sci, sk->key, sk->rectangle);
237 		}
238 		else
239 		{
240 			sci_assign_cmdkey(sci, sk->key, sk->stream);
241 			sci_assign_cmdkey(sci, sk->key | (SCMOD_ALT << 16), sk->rectangle);
242 		}
243 	}
244 }
245 
on_column_mode_toggled(G_GNUC_UNUSED GtkMenuItem * menuitem)246 static void on_column_mode_toggled(G_GNUC_UNUSED GtkMenuItem *menuitem)
247 {
248 	ScintillaObject *sci = scintilla_get_current();
249 
250 	if (sci)
251 	{
252 		column_mode = gtk_check_menu_item_get_active(column_mode_item);
253 		gtk_widget_set_sensitive(anchor_rect_select_item, !column_mode);
254 
255 		if (!plugin_internal_callback)
256 		{
257 			assign_select_keys(sci);
258 			g_object_set_data(G_OBJECT(sci), "column_mode", GINT_TO_POINTER(column_mode));
259 			if (sci_has_selection(sci) && sci_rectangle_selection(sci) != column_mode)
260 				convert_selection(sci, column_mode);
261 		}
262 	}
263 }
264 
on_column_mode_key(G_GNUC_UNUSED guint key_id)265 static void on_column_mode_key(G_GNUC_UNUSED guint key_id)
266 {
267 	gtk_check_menu_item_set_active(column_mode_item, !column_mode);
268 }
269 
on_document_create(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GeanyDocument * doc,G_GNUC_UNUSED gpointer gdata)270 static void on_document_create(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GeanyDocument *doc,
271 	G_GNUC_UNUSED gpointer gdata)
272 {
273 	select_anchor = select_space = 0;
274 	plugin_internal_callback = TRUE;
275 	gtk_check_menu_item_set_active(column_mode_item, FALSE);
276 	plugin_internal_callback = FALSE;
277 }
278 
on_document_activate(G_GNUC_UNUSED GObject * obj,GeanyDocument * doc,G_GNUC_UNUSED gpointer gdata)279 static void on_document_activate(G_GNUC_UNUSED GObject *obj, GeanyDocument *doc,
280 	G_GNUC_UNUSED gpointer gdata)
281 {
282 	ScintillaObject *sci = doc->editor->sci;
283 
284 	plugin_internal_callback = TRUE;
285 	column_mode = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sci), "column_mode"));
286 	gtk_check_menu_item_set_active(column_mode_item, column_mode);
287 	plugin_internal_callback = FALSE;
288 	select_anchor = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sci), "select_anchor"));
289 	select_space = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sci), "select_space"));
290 }
291 
update_home_key(void)292 static void update_home_key(void)
293 {
294 	if (geany_data->editor_prefs->smart_home_key)
295 	{
296 		select_keys->stream = SCI_VCHOMEEXTEND;
297 		select_keys->rectangle = SCI_VCHOMERECTEXTEND;
298 	}
299 	else
300 	{
301 		select_keys->stream = SCI_HOMEEXTEND;
302 		select_keys->rectangle = SCI_HOMERECTEXTEND;
303 	}
304 }
305 
on_settings_change(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GKeyFile * keyfile,G_GNUC_UNUSED gpointer gdata)306 static void on_settings_change(G_GNUC_UNUSED GObject *obj, G_GNUC_UNUSED GKeyFile *keyfile,
307 	G_GNUC_UNUSED gpointer gdata)
308 {
309 	update_home_key();
310 
311 	if (column_mode)
312 	{
313 		guint i;
314 
315 		foreach_document(i)
316 			assign_select_keys(documents[i]->editor->sci);
317 	}
318 }
319 
320 /* Select to line / matching brace */
321 
doit_and_select(guint group_id,guint key_id)322 static void doit_and_select(guint group_id, guint key_id)
323 {
324 	ScintillaObject *sci = scintilla_get_current();
325 
326 	if (sci)
327 	{
328 		int before = sci_get_current_position(sci), after;
329 
330 		if (key_id != GEANY_KEYS_GOTO_LINE || !geany_data->toolbar_prefs->visible)
331 			keybindings_send_command(group_id, key_id);
332 		else if (go_to_line1_item)
333 			g_signal_emit_by_name(go_to_line1_item, "activate");
334 		else
335 		{
336 			if (geany_data->prefs->beep_on_errors)
337 #if GTK_CHECK_VERSION(2, 2, 0)
338 				gdk_display_beep(gdk_display_get_default());
339 #else
340 				gdk_beep();
341 #endif
342 			return;
343 		}
344 
345 		after = sci_get_current_position(sci);
346 		if (before != after)
347 			sci_set_anchor(sci, before);
348 	}
349 }
350 
on_goto_line_activate(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)351 static void on_goto_line_activate(G_GNUC_UNUSED GtkMenuItem *menuitem,
352 	G_GNUC_UNUSED gpointer gdata)
353 {
354 	doit_and_select(GEANY_KEY_GROUP_GOTO, GEANY_KEYS_GOTO_LINE);
355 }
356 
on_goto_line_key(G_GNUC_UNUSED guint key_id)357 static void on_goto_line_key(G_GNUC_UNUSED guint key_id)
358 {
359 	on_goto_line_activate(NULL, NULL);
360 }
361 
on_brace_match_activate(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)362 static void on_brace_match_activate(G_GNUC_UNUSED GtkMenuItem *menuitem,
363 	G_GNUC_UNUSED gpointer gdata)
364 {
365 	doit_and_select(GEANY_KEY_GROUP_GOTO, GEANY_KEYS_GOTO_MATCHINGBRACE);
366 }
367 
on_brace_match_key(G_GNUC_UNUSED guint key_id)368 static void on_brace_match_key(G_GNUC_UNUSED guint key_id)
369 {
370 	on_brace_match_activate(NULL, NULL);
371 }
372 
on_convert_selection_activate(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)373 static void on_convert_selection_activate(G_GNUC_UNUSED GtkMenuItem *menuitem,
374 	G_GNUC_UNUSED gpointer gdata)
375 {
376 	ScintillaObject *sci = scintilla_get_current();
377 
378 	if (sci)
379 		convert_selection(sci, !sci_rectangle_selection(sci));
380 }
381 
on_convert_selection_key(G_GNUC_UNUSED guint key_id)382 static void on_convert_selection_key(G_GNUC_UNUSED guint key_id)
383 {
384 	on_convert_selection_activate(NULL, NULL);
385 }
386 
387 /* Set anchor / select to anchor */
388 
save_selection(ScintillaObject * sci)389 static void save_selection(ScintillaObject *sci)
390 {
391 	g_object_set_data(G_OBJECT(sci), "select_anchor", GINT_TO_POINTER(select_anchor));
392 	g_object_set_data(G_OBJECT(sci), "select_space", GINT_TO_POINTER(select_space));
393 }
394 
on_editor_notify(G_GNUC_UNUSED GObject * obj,GeanyEditor * editor,SCNotification * nt,G_GNUC_UNUSED gpointer gdata)395 static gboolean on_editor_notify(G_GNUC_UNUSED GObject *obj, GeanyEditor *editor,
396 	SCNotification *nt, G_GNUC_UNUSED gpointer gdata)
397 {
398 	if (nt->nmhdr.code == SCN_MODIFIED)
399 	{
400 		if (nt->modificationType & SC_MOD_INSERTTEXT)
401 		{
402 			if (nt->position < select_anchor)
403 			{
404 				select_anchor += nt->length;
405 				select_space = 0;
406 				save_selection(editor->sci);
407 			}
408 		}
409 		else if (nt->modificationType & SC_MOD_DELETETEXT)
410 		{
411 			if (nt->position < select_anchor)
412 			{
413 				if (nt->position + nt->length < select_anchor)
414 					select_anchor -= nt->length;
415 				else
416 					select_anchor = nt->position;
417 				select_space = 0;
418 				save_selection(editor->sci);
419 			}
420 		}
421 	}
422 
423 	return FALSE;
424 }
425 
on_set_anchor_activate(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)426 static void on_set_anchor_activate(G_GNUC_UNUSED GtkMenuItem *menuitem,
427 	G_GNUC_UNUSED gpointer gdata)
428 {
429 	ScintillaObject *sci = scintilla_get_current();
430 
431 	if (sci)
432 	{
433 		select_anchor = sci_get_current_position(sci);
434 		select_space = sci_get_cursor_space(sci);
435 		save_selection(sci);
436 	}
437 }
438 
on_set_anchor_key(G_GNUC_UNUSED guint key_id)439 static void on_set_anchor_key(G_GNUC_UNUSED guint key_id)
440 {
441 	on_set_anchor_activate(NULL, NULL);
442 }
443 
select_to_anchor(gboolean rectangle)444 static void select_to_anchor(gboolean rectangle)
445 {
446 	ScintillaObject *sci = scintilla_get_current();
447 
448 	if (sci)
449 		create_selection(sci, select_anchor, select_space, rectangle);
450 }
451 
on_select_to_anchor_activate(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)452 static void on_select_to_anchor_activate(G_GNUC_UNUSED GtkMenuItem *menuitem,
453 	G_GNUC_UNUSED gpointer gdata)
454 {
455 	select_to_anchor(column_mode);
456 }
457 
on_select_to_anchor_key(G_GNUC_UNUSED guint key_id)458 static void on_select_to_anchor_key(G_GNUC_UNUSED guint key_id)
459 {
460 	on_select_to_anchor_activate(NULL, NULL);
461 }
462 
on_select_rectangle_activate(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)463 static void on_select_rectangle_activate(G_GNUC_UNUSED GtkMenuItem *menuitem,
464 	G_GNUC_UNUSED gpointer gdata)
465 {
466 	if (!column_mode)
467 		select_to_anchor(TRUE);
468 }
469 
on_select_rectangle_key(G_GNUC_UNUSED guint key_id)470 static void on_select_rectangle_key(G_GNUC_UNUSED guint key_id)
471 {
472 	on_select_rectangle_activate(NULL, NULL);
473 }
474 
475 /* Plugin globals */
476 
477 PluginCallback plugin_callbacks[] =
478 {
479 	{ "document-new", G_CALLBACK(on_document_create), FALSE, NULL },
480 	{ "document-open", G_CALLBACK(on_document_create), FALSE, NULL },
481 	{ "document-activate", G_CALLBACK(on_document_activate), FALSE, NULL },
482 	{ "save-settings", G_CALLBACK(on_settings_change), FALSE, NULL },
483 	{ "editor-notify", G_CALLBACK(on_editor_notify), FALSE, NULL },
484 	{ NULL, NULL, FALSE, NULL }
485 };
486 
on_extra_select_activate(G_GNUC_UNUSED GtkMenuItem * menuitem,gpointer gdata)487 static void on_extra_select_activate(G_GNUC_UNUSED GtkMenuItem *menuitem, gpointer gdata)
488 {
489 	gtk_widget_set_sensitive(GTK_WIDGET(gdata), sci_has_selection(scintilla_get_current()));
490 }
491 
plugin_init(G_GNUC_UNUSED GeanyData * data)492 void plugin_init(G_GNUC_UNUSED GeanyData *data)
493 {
494 	GtkContainer *menu;
495 	GtkWidget *item;
496 	GeanyKeyGroup *plugin_key_group;
497 
498 	plugin_key_group = plugin_set_key_group(geany_plugin, "extra_select", COUNT_KB, NULL);
499 
500 	item = gtk_menu_item_new_with_mnemonic(_("E_xtra Selection"));
501 	main_menu_item = item;
502 	gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), item);
503 	ui_add_document_sensitive(item);
504 	menu = GTK_CONTAINER(gtk_menu_new());
505 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), GTK_WIDGET(menu));
506 
507 	item = gtk_check_menu_item_new_with_mnemonic(_("_Column Mode"));
508 	column_mode_item = GTK_CHECK_MENU_ITEM(item);
509 	gtk_container_add(menu, item);
510 	g_signal_connect(item, "toggled", G_CALLBACK(on_column_mode_toggled), NULL);
511 	keybindings_set_item(plugin_key_group, COLUMN_MODE_KB, on_column_mode_key, 0, 0,
512 		"column_mode", _("Column mode"), item);
513 
514 	item = gtk_menu_item_new_with_mnemonic(_("Select to _Line"));
515 	gtk_container_add(menu, item);
516 	g_signal_connect(item, "activate", G_CALLBACK(on_goto_line_activate), NULL);
517 	keybindings_set_item(plugin_key_group, GOTO_LINE_EXTEND_KB, on_goto_line_key, 0, 0,
518 		"goto_line_extend", _("Select to line"), item);
519 
520 	item = gtk_menu_item_new_with_mnemonic(_("Select to Matching _Brace"));
521 	gtk_container_add(menu, item);
522 	g_signal_connect(item, "activate", G_CALLBACK(on_brace_match_activate), NULL);
523 	keybindings_set_item(plugin_key_group, BRACE_MATCH_EXTEND_KB, on_brace_match_key, 0, 0,
524 		"brace_match_extend", _("Select to matching brace"), item);
525 
526 	item = gtk_menu_item_new_with_mnemonic(_("_Toggle Stream/Rectangular"));
527 	gtk_container_add(menu, item);
528 	g_signal_connect(item, "activate", G_CALLBACK(on_convert_selection_activate), NULL);
529 	keybindings_set_item(plugin_key_group, CONVERT_SELECTION_KB, on_convert_selection_key,
530 		0, 0, "convert_selection", _("Convert selection"), item);
531 	g_signal_connect(main_menu_item, "activate", G_CALLBACK(on_extra_select_activate), item);
532 
533 	gtk_container_add(menu, gtk_separator_menu_item_new());
534 
535 	item = gtk_menu_item_new_with_mnemonic(_("_Set Anchor"));
536 	gtk_container_add(menu, item);
537 	g_signal_connect(item, "activate", G_CALLBACK(on_set_anchor_activate), NULL);
538 	keybindings_set_item(plugin_key_group, SET_ANCHOR_KB, on_set_anchor_key, 0, 0,
539 		"set_anchor", _("Set anchor"), item);
540 
541 	item = gtk_menu_item_new_with_mnemonic(_("Select to _Anchor"));
542 	gtk_container_add(menu, item);
543 	g_signal_connect(item, "activate", G_CALLBACK(on_select_to_anchor_activate), NULL);
544 	keybindings_set_item(plugin_key_group, ANCHOR_EXTEND_KB, on_select_to_anchor_key, 0, 0,
545 		"select_to_anchor", _("Select to anchor"), item);
546 
547 	item = gtk_menu_item_new_with_mnemonic(_("_Rectangle Select to Anchor"));
548 	anchor_rect_select_item = item;
549 	gtk_container_add(menu, item);
550 	g_signal_connect(item, "activate", G_CALLBACK(on_select_rectangle_activate), NULL);
551 	keybindings_set_item(plugin_key_group, ANCHOR_RECTEXTEND_KB, on_select_rectangle_key, 0,
552 		0, "rect_select_to_anchor", _("Rectangle select to anchor"), item);
553 
554 	gtk_widget_show_all(main_menu_item);
555 
556 	go_to_line1_item = g_object_get_data((gpointer) geany->main_widgets->window,
557 		"go_to_line1");
558 
559 	update_home_key();
560 	plugin_signal_connect(geany_plugin, G_OBJECT(geany->main_widgets->window),
561 		"key-press-event", FALSE, G_CALLBACK(on_key_press_event), NULL);
562 }
563 
plugin_cleanup(void)564 void plugin_cleanup(void)
565 {
566 	guint i = 0;
567 
568 	gtk_widget_destroy(main_menu_item);
569 	column_mode = FALSE;
570 
571 	foreach_document(i)
572 	{
573 		ScintillaObject *sci = documents[i]->editor->sci;
574 
575 		assign_select_keys(sci);
576 		g_object_steal_data(G_OBJECT(sci), "column_mode");
577 		g_object_steal_data(G_OBJECT(sci), "select_anchor");
578 		g_object_steal_data(G_OBJECT(sci), "select_space");
579 	}
580 }
581