1 /*
2  *      callbacks.c
3  *
4  *      Copyright 2010 Alexander Petukhov <devel(at)apetukhov.ru>
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, write to the Free Software
18  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *      MA 02110-1301, USA.
20  */
21 
22 /*
23  * 		Contains callbacks for the user actions as well
24  * 		as for the Geany events
25  */
26 
27 #include <string.h>
28 
29 #ifdef HAVE_CONFIG_H
30 	#include "config.h"
31 #endif
32 #include <geanyplugin.h>
33 
34 #include "callbacks.h"
35 #include "breakpoints.h"
36 #include "debug.h"
37 #include "keys.h"
38 #include "tpage.h"
39 #include "stree.h"
40 #include "markers.h"
41 #include "utils.h"
42 #include "bptree.h"
43 #include "btnpanel.h"
44 #include "dconfig.h"
45 #include "tabs.h"
46 
47 
48 /*
49  * 	Set breakpoint and stack markers for a file
50  */
set_markers_for_file(const gchar * file)51 static void set_markers_for_file(const gchar* file)
52 {
53 	GList *breaks;
54 	if ( (breaks = breaks_get_for_document(file)) )
55 	{
56 		GList *iter = breaks;
57 		while (iter)
58 		{
59 			breakpoint *bp = (breakpoint*)iter->data;
60 			markers_add_breakpoint(bp);
61 
62 			iter = iter->next;
63 		}
64 		g_list_free(breaks);
65 	}
66 
67 	/* set frames markers if exists */
68 	if (DBS_STOPPED == debug_get_state())
69 	{
70 		int active_frame_index = debug_get_active_frame();
71 
72 		GList *iter = debug_get_stack();
73 		int frame_index = 0;
74 		for (; iter; iter = iter->next, frame_index++)
75 		{
76 			if (iter)
77 			{
78 				frame *f = (frame*)iter->data;
79 				if (f->have_source && !strcmp(f->file, file))
80 				{
81 					if (active_frame_index == frame_index)
82 					{
83 						markers_add_current_instruction(f->file, f->line);
84 					}
85 					else
86 					{
87 						markers_add_frame(f->file, f->line);
88 					}
89 				}
90 			}
91 		}
92 	}
93 }
94 
95 /*
96  * 	Following group of callbacks are used for
97  * 	checking of existance of the config file
98  * 	and changing buttons state in the target page
99  */
100 
101 /*
102  * 	Occures before document is going to be saved
103  */
104 static gboolean _unexisting_file = FALSE;
on_document_before_save(GObject * obj,GeanyDocument * doc,gpointer user_data)105 void on_document_before_save(GObject *obj, GeanyDocument *doc, gpointer user_data)
106 {
107 	if (!doc->real_path)
108 	{
109 		/* we can fall here if we are saving new document
110 		 (that doesn't exists on a filesystem) or if we're "Saving As" */
111 
112 		_unexisting_file = TRUE;
113 	}
114 }
115 
116 /*
117  * 	Occures on saving document
118  */
on_document_save(GObject * obj,GeanyDocument * doc,gpointer user_data)119 void on_document_save(GObject *obj, GeanyDocument *doc, gpointer user_data)
120 {
121 	if (_unexisting_file)
122 	{
123 		/* if we are saving as - remove all markers at first */
124 		markers_remove_all(doc);
125 
126 		/* do all the markers and calltip stuff that is done on file open */
127 		on_document_open(obj, doc, user_data);
128 
129 		_unexisting_file = FALSE;
130 	}
131 }
132 
133 /*
134  * 	Occures on document opening.
135  * 	Used to set breaks markers
136  */
on_document_open(GObject * obj,GeanyDocument * doc,gpointer user_data)137 void on_document_open(GObject *obj, GeanyDocument *doc, gpointer user_data)
138 {
139 	/*set markers*/
140 	markers_set_for_document(doc->editor->sci);
141 
142 	/*set dwell interval*/
143 	scintilla_send_message(doc->editor->sci, SCI_SETMOUSEDWELLTIME, 500, 0);
144 
145 	/* set tab size for calltips */
146 	scintilla_send_message(doc->editor->sci, SCI_CALLTIPUSESTYLE, 20, (long)NULL);
147 
148 	/* set breakpoint and frame markers */
149 	set_markers_for_file(DOC_FILENAME(doc));
150 
151 	/* if debug is active - tell the debug module that a file was opened */
152 	if (DBS_IDLE != debug_get_state())
153 		debug_on_file_open(doc);
154 }
155 
156 /*
157  * 	Handles mouse leave event to check if a calltip is still present and hides it if yes
158  */
159 static gulong leave_signal = 0;
on_mouse_leave(GtkWidget * widget,GdkEvent * event,gpointer user_data)160 static gboolean on_mouse_leave(GtkWidget *widget, GdkEvent *event, gpointer user_data)
161 {
162 	ScintillaObject *so = (ScintillaObject*)widget;
163 	if (leave_signal > 0)
164 	{
165 		g_signal_handler_disconnect(G_OBJECT(widget), leave_signal);
166 		leave_signal = 0;
167 	}
168 	if (scintilla_send_message (so, SCI_CALLTIPACTIVE, 0, 0))
169 	{
170 		scintilla_send_message (so, SCI_CALLTIPCANCEL, 0, 0);
171 	}
172 	return FALSE;
173 }
174 
175 /*
176  * 	Occures on notify from editor.
177  * 	Handles margin click to set/remove breakpoint
178  */
on_editor_notify(GObject * object,GeanyEditor * editor,SCNotification * nt,gpointer data)179 gboolean on_editor_notify(
180 	GObject *object, GeanyEditor *editor,
181 	SCNotification *nt, gpointer data)
182 {
183 	if (!editor->document->real_path)
184 	{
185 		/* no other way to handle removing a file from outside of geany */
186 		markers_remove_all(editor->document);
187 	}
188 
189 	switch (nt->nmhdr.code)
190 	{
191 		case SCN_MARGINCLICK:
192 		{
193 			char* file;
194 			int line;
195 			break_state bs;
196 
197 			if (!editor->document->real_path || 1 != nt->margin)
198 				break;
199 
200 			file = editor->document->file_name;
201 			line = sci_get_line_from_position(editor->sci, nt->position) + 1;
202 
203 			bs = breaks_get_state(file, line);
204 			if (BS_NOT_SET == bs)
205 				breaks_add(file, line, NULL, TRUE, 0);
206 			else if (BS_ENABLED == bs)
207 				breaks_remove(file, line);
208 			else if (BS_DISABLED == bs)
209 				breaks_switch(file, line);
210 
211 			scintilla_send_message(editor->sci, SCI_SETFOCUS, TRUE, 0);
212 
213 			return TRUE;
214 		}
215 		case SCN_DWELLSTART:
216 		{
217 			GString *word;
218 
219 			if (DBS_STOPPED != debug_get_state ())
220 				break;
221 
222 			/* get a word under the cursor */
223 			word = get_word_at_position(editor->sci, nt->position);
224 			if (word->len)
225 			{
226 				gchar *calltip = debug_get_calltip_for_expression(word->str);
227 				if (calltip)
228 				{
229 					leave_signal = g_signal_connect(G_OBJECT(editor->sci), "leave-notify-event", G_CALLBACK(on_mouse_leave), NULL);
230 					scintilla_send_message (editor->sci, SCI_CALLTIPSHOW, nt->position, (long)calltip);
231 				}
232 			}
233 
234 			g_string_free(word, TRUE);
235 
236 			break;
237 		}
238 		case SCN_DWELLEND:
239 		{
240 			if (leave_signal > 0)
241 			{
242 				g_signal_handler_disconnect(G_OBJECT(editor->sci), leave_signal);
243 				leave_signal = 0;
244 			}
245 
246 			if (DBS_STOPPED != debug_get_state ())
247 				break;
248 
249 			if (scintilla_send_message (editor->sci, SCI_CALLTIPACTIVE, 0, 0))
250 			{
251 				scintilla_send_message (editor->sci, SCI_CALLTIPCANCEL, 0, 0);
252 			}
253 			break;
254 		}
255 		case SCN_MODIFYATTEMPTRO:
256 		{
257 			dialogs_show_msgbox(GTK_MESSAGE_INFO, _("To edit source files stop debugging session"));
258 			break;
259 		}
260 		case SCN_MODIFIED:
261 		{
262 			if(((SC_MOD_INSERTTEXT & nt->modificationType) || (SC_MOD_DELETETEXT && nt->modificationType)) && editor->document->file_name && nt->linesAdded)
263 			{
264 				int line = sci_get_line_from_position(editor->sci, nt->position) + 1;
265 
266 				GList *breaks = breaks_get_for_document(editor->document->file_name);
267 				if (breaks)
268 				{
269 					GList *iter = breaks;
270 					while (iter)
271 					{
272 						breakpoint *bp = (breakpoint*)iter->data;
273 
274 						if (nt->linesAdded > 0 && bp->line >= line)
275 						{
276 							breaks_move_to_line(bp->file, bp->line, bp->line + nt->linesAdded);
277 							bptree_update_breakpoint(bp);
278 						}
279 						else if (nt->linesAdded < 0 && bp->line >= line)
280 						{
281 							if (bp->line < line - nt->linesAdded)
282 							{
283 								breaks_remove(bp->file, bp->line);
284 							}
285 							else
286 							{
287 								breaks_move_to_line(bp->file, bp->line, bp->line + nt->linesAdded);
288 								bptree_update_breakpoint(bp);
289 							}
290 						}
291 						iter = iter->next;
292 					}
293 
294 					config_set_debug_changed();
295 
296 					g_list_free(breaks);
297 				}
298 			}
299 			break;
300 		}
301 	}
302 
303 	return FALSE;
304 }
305 
306 /*
307  * 	Occures when key is pressed.
308  * 	Handles debug Run/Stop/... and add/remove breakpoint activities
309  */
keys_callback(guint key_id)310 gboolean keys_callback(guint key_id)
311 {
312 	switch (key_id)
313 	{
314 		case KEY_RUN:
315 			debug_run();
316 			break;
317 		case KEY_STOP:
318 			debug_stop();
319 			break;
320 		case KEY_RESTART:
321 			debug_restart();
322 			break;
323 		case KEY_STEP_OVER:
324 			debug_step_over();
325 			break;
326 		case KEY_STEP_INTO:
327 			debug_step_into();
328 			break;
329 		case KEY_STEP_OUT:
330 			debug_step_out();
331 			break;
332 		case KEY_EXECUTE_UNTIL:
333 		{
334 			GeanyDocument *doc = document_get_current();
335 			if (doc)
336 			{
337 				int line = sci_get_current_line(doc->editor->sci) + 1;
338 				debug_execute_until(DOC_FILENAME(doc), line);
339 			}
340 			break;
341 		}
342 		case KEY_BREAKPOINT:
343 		{
344 			GeanyDocument *doc = document_get_current();
345 			if (doc)
346 			{
347 				int line = sci_get_current_line(doc->editor->sci) + 1;
348 				break_state	bs = breaks_get_state(DOC_FILENAME(doc), line);
349 				if (BS_NOT_SET == bs)
350 					breaks_add(DOC_FILENAME(doc), line, NULL, TRUE, 0);
351 				else if (BS_ENABLED == bs)
352 					breaks_remove(DOC_FILENAME(doc), line);
353 				else if (BS_DISABLED == bs)
354 					breaks_switch(DOC_FILENAME(doc), line);
355 
356 				scintilla_send_message(doc->editor->sci, SCI_SETFOCUS, TRUE, 0);
357 			}
358 			break;
359 		}
360 		case KEY_CURRENT_INSTRUCTION:
361 		{
362 			if (DBS_STOPPED == debug_get_state() && debug_current_instruction_have_sources())
363 			{
364 				debug_jump_to_current_instruction();
365 				gtk_widget_set_sensitive(tab_call_stack, FALSE);
366 				stree_select_first_frame(FALSE);
367 				gtk_widget_set_sensitive(tab_call_stack, TRUE);
368 			}
369 		}
370 	}
371 
372 	return TRUE;
373 }
374