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