1
2 /*
3 * glspi_app.c - This file is part of the Lua scripting plugin for the Geany IDE
4 * See the file "geanylua.c" for copyright information.
5 */
6
7 #ifdef HAVE_CONFIG_H
8 # include "config.h"
9 #endif
10
11 #include <sys/stat.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <errno.h>
15
16
17 #define NEED_FAIL_ARG_TYPE
18 #include "glspi.h"
19
20
glspi_pluginver(lua_State * L)21 static gint glspi_pluginver(lua_State* L)
22 {
23 lua_pushfstring(L, _(
24 "%s %s: %s\n"
25 "Copyright (c) 2007-2010 "PLUGIN_AUTHOR", et al.\n"
26 "Compiled on "__DATE__" at "__TIME__" for Geany API version %d\n"
27 "Released under version 2 of the GNU General Public License.\n"
28 ),
29 PLUGIN_NAME, PLUGIN_VER, PLUGIN_DESC, MY_GEANY_API_VER);
30 return 1;
31 }
32
33
glspi_tools(lua_State * L)34 static gint glspi_tools(lua_State* L)
35 {
36 lua_newtable(L);
37 SetTableStr("browser", geany_data->tool_prefs->browser_cmd);
38 SetTableStr("term", geany_data->tool_prefs->term_cmd);
39 SetTableStr("grep", geany_data->tool_prefs->grep_cmd);
40 SetTableStr("action", geany_data->tool_prefs->context_action_cmd);
41 return 1;
42 }
43
44
glspi_template(lua_State * L)45 static gint glspi_template(lua_State* L)
46 {
47 lua_newtable(L);
48 SetTableStr("developer", geany_data->template_prefs->developer);
49 SetTableStr("company", geany_data->template_prefs->company);
50 SetTableStr("mail", geany_data->template_prefs->mail);
51 SetTableStr("initial", geany_data->template_prefs->initials);
52 SetTableStr("version", geany_data->template_prefs->version);
53 return 1;
54 }
55
56
57
glspi_project(lua_State * L)58 static gint glspi_project(lua_State* L)
59 {
60 GeanyProject *project = geany->app->project;
61
62 if (project) {
63 lua_newtable(L);
64 SetTableStr("name", project->name);
65 SetTableStr("desc", project->description);
66 SetTableStr("file", project->file_name);
67 SetTableStr("base", project->base_path);
68 if (project->file_patterns && *project->file_patterns) {
69 gchar *tmp=g_strjoinv(";", project->file_patterns);
70 SetTableStr("mask", tmp);
71 g_free(tmp);
72 }
73 return 1;
74 } else {
75 return 0;
76 }
77 }
78
79 static const gchar *glspi_script_dir = NULL;
80
glspi_appinfo(lua_State * L)81 static gint glspi_appinfo(lua_State* L)
82 {
83 lua_newtable(L);
84 SetTableBool("debug", geany->app->debug_mode);
85 SetTableStr("configdir", geany->app->configdir);
86 SetTableStr("datadir", geany->app->datadir);
87 SetTableStr("docdir", geany->app->docdir);
88 SetTableStr("scriptdir", glspi_script_dir);
89 lua_pushstring(L,"template");
90 glspi_template(L);
91 lua_rawset(L,1);
92
93 lua_pushstring(L,"tools");
94 glspi_tools(L);
95 lua_rawset(L,1);
96
97 if (geany->app->project) {
98 lua_pushstring(L,"project");
99 glspi_project(L);
100 lua_rawset(L,1);
101 }
102 return 1;
103 }
104
105
106
107 #ifndef G_OS_WIN32
108
109 #define CLIPBOARD gtk_clipboard_get(GDK_SELECTION_PRIMARY)
110
glspi_xsel(lua_State * L)111 static gint glspi_xsel(lua_State* L)
112 {
113 if (lua_gettop(L)>0) {
114 if (lua_isstring(L,1)) {
115 gsize len;
116 const gchar*txt=lua_tolstring(L,1,&len);
117 gtk_clipboard_set_text(CLIPBOARD,txt,len);
118 } else {
119 FAIL_STRING_ARG(1);
120 }
121 return 0;
122 } else {
123 gchar *txt=gtk_clipboard_wait_for_text(CLIPBOARD);
124 if (txt) {
125 lua_pushstring(L,txt);
126 g_free(txt);
127 } else {
128 lua_pushstring(L,"");
129 }
130 return 1;
131 }
132 }
133
134 #else
glspi_xsel(lua_State * L)135 static gint glspi_xsel(lua_State* L) { return 0; }
136 #endif
137
138
139
140
glspi_signal(lua_State * L)141 static gint glspi_signal(lua_State* L) {
142 const gchar*widname,*signame;
143 GtkWidget*w;
144 GType typeid;
145 guint sigid;
146 if ((lua_gettop(L)<2)||!lua_isstring(L,2) ) { return FAIL_STRING_ARG(2); }
147 if (!lua_isstring(L,1) ) { return FAIL_STRING_ARG(1); }
148 widname=lua_tostring(L,1);
149 signame=lua_tostring(L,2);
150 w=ui_lookup_widget(main_widgets->window, widname);
151 if (!w) {
152 lua_pushfstring(L, _("Error in module \"%s\" at function %s():\n"
153 "widget \"%s\" not found for argument #1.\n"),
154 LUA_MODULE_NAME, &__FUNCTION__[6], widname);
155 lua_error(L);
156 return 0;
157 }
158 typeid=G_OBJECT_TYPE(w);
159 sigid=g_signal_lookup(signame,typeid);
160 if (!sigid) {
161 lua_pushfstring(L, _("Error in module \"%s\" at function %s() argument #2:\n"
162 "widget \"%s\" has no signal named \"%s\".\n"),
163 LUA_MODULE_NAME, &__FUNCTION__[6], widname, signame);
164 lua_error(L);
165 return 0;
166 }
167
168 g_signal_emit(w, sigid, 0);
169 return 0;
170 }
171
172
173
174
175 #ifdef G_OS_WIN32
176 #define lstat stat
177 #include <io.h>
178 #endif
179
180 typedef int (*statfunc) (const char *fn, struct stat *st);
181
glspi_stat(lua_State * L)182 static gint glspi_stat(lua_State* L)
183 {
184 statfunc sf=stat;
185 const gchar*fn=NULL;
186 struct stat st;
187 if (lua_gettop(L)<1) { return FAIL_STRING_ARG(1); }
188 if (lua_gettop(L)>=2) {
189 if (!lua_isboolean(L,2)) { return FAIL_BOOL_ARG(2); }
190 sf=lua_toboolean(L,2)?lstat:stat;
191 }
192 if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
193 fn=lua_tostring(L,1);
194 if (sf(fn,&st)==0) {
195 const gchar *ft=NULL;
196 switch ( st.st_mode & S_IFMT) {
197 case S_IFBLK:ft="b"; break;
198 case S_IFCHR:ft="c"; break;
199 case S_IFDIR:ft="d"; break;
200 case S_IFIFO:ft="f"; break;
201 case S_IFREG:ft="r"; break;
202 #ifndef G_OS_WIN32
203 case S_IFLNK:ft="l"; break;
204 case S_IFSOCK:ft="s"; break;
205 #endif
206 }
207 lua_newtable(L);
208 SetTableNum("size",st.st_size);
209 SetTableNum("time",st.st_mtime);
210 SetTableStr("type",ft);
211 SetTableBool("read", (access(fn,R_OK)==0));
212 SetTableBool("write", (access(fn,W_OK)==0));
213 SetTableBool("exec", (access(fn,X_OK)==0));
214 return 1;
215 }
216 lua_pushnil(L);
217 lua_pushstring(L, strerror(errno));
218 return 2;
219 }
220
221
glspi_basename(lua_State * L)222 static gint glspi_basename(lua_State* L)
223 {
224 if (lua_gettop(L)>=1) {
225 gchar *bn=NULL;
226 const gchar *fn=NULL;
227 if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
228 fn=lua_tostring(L,1);
229 bn=g_path_get_basename(fn);
230 lua_pushstring(L,bn);
231 g_free(bn);
232 return 1;
233 }
234 return 0;
235 }
236
237
glspi_dirname(lua_State * L)238 static gint glspi_dirname(lua_State* L)
239 {
240 if (lua_gettop(L)>=1) {
241 gchar *dn=NULL;
242 const gchar *fn=NULL;
243 if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
244 fn=lua_tostring(L,1);
245 dn=g_path_get_dirname(fn);
246 lua_pushstring(L,dn);
247 g_free(dn);
248 return 1;
249 }
250 return 0;
251 }
252
253
254
glspi_fullpath(lua_State * L)255 static gint glspi_fullpath(lua_State* L)
256 {
257 if (lua_gettop(L)>=1) {
258 gchar *rp=NULL;
259 const gchar *fn=NULL;
260 if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
261 fn=lua_tostring(L,1);
262 rp=utils_get_real_path(fn);
263 if (rp) {
264 lua_pushstring(L,rp);
265 g_free(rp);
266 return 1;
267 }
268 }
269 return 0;
270 }
271
272
273
dirlist_closure(lua_State * L)274 static gint dirlist_closure(lua_State *L)
275 {
276 GDir*dir=lua_touserdata(L,lua_upvalueindex(1));
277 const gchar*entry=g_dir_read_name(dir);
278 if (entry) {
279 lua_pushstring(L,entry);
280 return 1;
281 } else {
282 g_dir_close(dir);
283 return 0;
284 }
285 }
286
287
glspi_status(lua_State * L)288 static gint glspi_status(lua_State* L)
289 {
290 const gchar *string = lua_tostring(L,1);
291
292 msgwin_status_add("%s", string);
293
294 return 0;
295 }
296
297
glspi_dirlist(lua_State * L)298 static gint glspi_dirlist(lua_State* L)
299 {
300 GDir*dir=NULL;
301 const gchar*dn=".";
302 GError*err=NULL;
303 if (lua_gettop(L)>=1) {
304 if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
305 dn=lua_tostring(L,1);
306 }
307 dir=g_dir_open(dn,0,&err);
308 if (dir) {
309 lua_pushlightuserdata(L,dir);
310 lua_pushcclosure(L,&dirlist_closure,1);
311 return 1;
312 } else {
313 lua_pushfstring(L, "Error in module \"%s\" at function %s() argument #2\n%s",
314 LUA_MODULE_NAME, &__FUNCTION__[6],err?err->message:"Error reading directory."
315 );
316 if (err) {g_error_free(err);}
317 lua_error(L);
318 return 0;
319 }
320 return 0;
321 }
322
323
324
glspi_wkdir(lua_State * L)325 static gint glspi_wkdir(lua_State* L)
326 {
327 if (lua_gettop(L)== 0 ) {
328 gchar*wd=getcwd(NULL,0);
329 if (wd) {
330 lua_pushstring(L,wd);
331 free(wd);
332 return 1;
333 } else { return 0; }
334 } else {
335 if (!lua_isstring(L,1)) { return FAIL_STRING_ARG(1); }
336 if ( chdir(lua_tostring(L,1)) == 0 ) {
337 lua_pushboolean(L,TRUE);
338 return 1;
339 } else {
340 lua_pushboolean(L,FALSE);
341 lua_pushstring(L, strerror(errno));
342 return 2;
343 }
344 }
345 }
346
347
348 #include "glspi_keycmd.h"
349
350 static GHashTable*key_cmd_hash=NULL;
351
glspi_init_key_cmd_hash(void)352 static void glspi_init_key_cmd_hash(void)
353 {
354 gint i;
355 key_cmd_hash=g_hash_table_new(g_str_hash,g_str_equal);
356 for (i=0;key_cmd_hash_entries[i].name; i++) {
357 g_hash_table_insert(
358 key_cmd_hash,(gpointer) key_cmd_hash_entries[i].name,
359 &key_cmd_hash_entries[i]);
360 }
361 }
362
363
glspi_free_key_cmd_hash(void)364 static void glspi_free_key_cmd_hash(void) {
365 if (key_cmd_hash) {
366 g_hash_table_destroy(key_cmd_hash);
367 key_cmd_hash=NULL;
368 }
369 }
370
glspi_set_key_cmd_hash(gboolean create)371 void glspi_set_key_cmd_hash(gboolean create) {
372 if (create) {
373 glspi_init_key_cmd_hash ();
374 } else {
375 glspi_free_key_cmd_hash();
376 }
377 }
378
379
380
381 #define lookup_key_cmd_str(cmd) g_hash_table_lookup(key_cmd_hash,cmd);
382
383
glspi_keycmd(lua_State * L)384 static gint glspi_keycmd(lua_State* L)
385 {
386 KeyCmdHashEntry*he=NULL;
387 if (lua_gettop(L)<1) {return FAIL_STRING_ARG(1); }
388 if (lua_isstring(L,1)) {
389 gchar cmdbuf[64];
390 gchar *cmdname;
391 gint i;
392 memset(cmdbuf,'\0', sizeof(cmdbuf));
393 strncpy(cmdbuf,lua_tostring(L,1),sizeof(cmdbuf)-1);
394 for (i=0;cmdbuf[i];i++) {cmdbuf[i]=g_ascii_toupper(cmdbuf[i]);}
395 cmdname=cmdbuf;
396 if (strncmp(cmdname,"GEANY_",6)==0) {
397 cmdname+=6;
398 if (strncmp(cmdname,"KEYS_",5)==0) {
399 cmdname+=5;
400 }
401 }
402 he=lookup_key_cmd_str(cmdname);
403 } else { return FAIL_STRING_ARG(1); }
404 if ( !he ) {
405 lua_pushfstring(
406 L, _( "Error in module \"%s\" at function %s():\n"
407 "unknown command \"%s\" given for argument #1.\n"),
408 LUA_MODULE_NAME, &__FUNCTION__[6], lua_tostring(L,1));
409 lua_error(L);
410 return 0;
411 }
412 keybindings_send_command(he->group, he->key_id);
413 return 0;
414 }
415
416
glspi_launch(lua_State * L)417 static gint glspi_launch(lua_State* L)
418 {
419 gint argc=lua_gettop(L);
420 gint i;
421 gchar **argv=NULL;
422 gboolean rv;
423 GError *err=NULL;
424 if (argc==0) { return FAIL_STRING_ARG(1); }
425 for (i=1;i<=argc;i++) {
426 if (!lua_isstring(L,i)) { return FAIL_STRING_ARG(i); }
427 }
428 argv=g_malloc0(sizeof(gchar *)*argc+1);
429 for (i=0;i<argc;i++) {
430 argv[i]=(g_strdup(lua_tostring(L,i+1)));
431 }
432 rv=g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
433 g_strfreev(argv);
434 lua_pushboolean(L,rv);
435 if (rv) { return 1; }
436 lua_pushstring(L,err->message);
437 g_error_free(err);
438 return 2;
439 }
440
441
442 static guint My_Shift_L=0;
443 static guint My_Shift_R=0;
444 static guint My_Control_L=0;
445 static guint My_Control_R=0;
446 static guint My_Alt_L=0;
447 static guint My_Alt_R=0;
448
449
450 #ifndef G_OS_WIN32
451
452 #include <X11/Xlib.h>
453 #include <X11/keysym.h>
454
455
456 #define IsShift ( (My_Shift_L == ev->xkey.keycode) || (My_Shift_R == ev->xkey.keycode) )
457
458 #define IsCtrl ( (My_Control_L == ev->xkey.keycode) || (My_Control_R == ev->xkey.keycode) )
459 #define IsAlt ( (My_Alt_L == ev->xkey.keycode) || (My_Alt_R == ev->xkey.keycode) )
460
461 #define IsCtrlAlt ( IsCtrl || IsAlt )
462
463 typedef struct _KeyGrabData {
464 gchar *prompt;
465 GdkKeymapKey km;
466 } _KeyGrabData;
467
468
keygrab_cb(GdkXEvent * xevent,GdkEvent * event,gpointer data)469 static GdkFilterReturn keygrab_cb(GdkXEvent *xevent, GdkEvent *event, gpointer data)
470 {
471 XEvent*ev = (XEvent*) xevent;
472 GdkKeymapKey *km = (GdkKeymapKey*) data;
473 switch (ev->type) {
474 case KeyPress:{
475 if (IsShift) {
476 km->level=1;
477 } else {
478 if (!IsCtrlAlt) km->group=1; /* Flag to know we have keydown before keyup */
479 }
480 return GDK_FILTER_REMOVE;
481 }
482 case KeyRelease:{
483 if (IsShift) {
484 km->level=0;
485 } else {
486 if ((km->group==1)&&(!IsCtrlAlt)) { /* OK, we already got our keydown */
487 km->group=2;
488 km->level=(ev->xkey.state & ShiftMask)?1:0;
489 km->keycode=ev->xkey.keycode;
490 }
491 }
492 return GDK_FILTER_REMOVE;
493 }
494 default:{}
495 }
496 return GDK_FILTER_CONTINUE;
497 }
498
499 #define dosleep() g_usleep(1)
500
501 #else
502 #include <windows.h>
503 #define dosleep() Sleep(1)
504
505 #define IsShift ( (My_Shift_L == msg->wParam) || (My_Shift_R == msg->wParam) )
506
507 #define IsCtrl ( (My_Control_L == msg->wParam) || (My_Control_R == msg->wParam) )
508 #define IsAlt ( (My_Alt_L == msg->wParam) || (My_Alt_R == msg->wParam) )
509
510 #define IsCtrlAlt ( IsCtrl || IsAlt )
511
512
keygrab_cb(GdkXEvent * xevent,GdkEvent * event,gpointer data)513 static GdkFilterReturn keygrab_cb(GdkXEvent *xevent, GdkEvent *event, gpointer data)
514 {
515 MSG*msg = (MSG*) xevent;
516 GdkKeymapKey *km = (GdkKeymapKey*) data;
517 switch (msg->message) {
518 case WM_KEYDOWN:{
519 if (IsShift) {
520 km->level=1;
521 } else {
522 if (!IsCtrlAlt) km->group=1; /* Flag to know we have keydown before keyup */
523 }
524 return GDK_FILTER_REMOVE;
525 }
526 case WM_KEYUP:{
527 if (IsShift) {
528 km->level=0;
529 } else {
530 if ((km->group==1)&&(!IsCtrlAlt)) { /* OK, we already got our keydown */
531 km->group=2;
532 km->level=HIBYTE(GetKeyState(VK_SHIFT))?1:0;
533 km->keycode=msg->wParam;
534 }
535 }
536 return GDK_FILTER_REMOVE;
537 }
538 default:{}
539 }
540 return GDK_FILTER_CONTINUE;
541 }
542
543
544 #endif
545
546
547 #include <gdk/gdkkeysyms.h>
init_key(guint keyval)548 static gint init_key(guint keyval){
549 GdkKeymapKey *kmk=NULL;
550 GdkKeymap *gdk_key_map=gdk_keymap_get_default();
551 gint n_keys=0;
552 gint rv=0;
553 if (gdk_keymap_get_entries_for_keyval(gdk_key_map,keyval,&kmk,&n_keys)) {
554 rv=kmk[0].keycode;
555 g_free(kmk);
556 }
557 return rv;
558 }
559
560 #define InitKey(code,value) if (!code) { code=init_key(value); }
561
glspi_keygrab(lua_State * L)562 static gint glspi_keygrab(lua_State* L)
563 {
564 GeanyDocument*doc=NULL;
565 const gchar*prompt=NULL;
566 GdkKeymapKey km={0,0,0};
567 GdkKeymap *gdk_key_map;
568 km.keycode=0;
569 km.group=0; /* Note: we hijack this field to use as a flag for first keydown. */
570 km.level=0;
571 InitKey(My_Shift_L, GDK_Shift_L);
572 InitKey(My_Shift_R, GDK_Shift_R);
573 InitKey(My_Control_L, GDK_Control_L);
574 InitKey(My_Control_R, GDK_Control_R);
575 InitKey(My_Alt_L, GDK_Alt_L);
576 InitKey(My_Alt_R, GDK_Alt_R);
577 if (lua_gettop(L)>0) {
578 if (!lua_isstring(L,1)) {return FAIL_STRING_ARG(1); }
579 prompt=lua_tostring(L,1);
580 doc=document_get_current();
581 }
582
583 if (prompt && doc && doc->is_valid ) {
584 gint fvl=scintilla_send_message(doc->editor->sci,SCI_GETFIRSTVISIBLELINE, 0,0);
585 gint pos=sci_get_position_from_line(doc->editor->sci, fvl+1);
586 scintilla_send_message(doc->editor->sci,SCI_CALLTIPSHOW,pos+3, (sptr_t)prompt);
587 }
588 gdk_window_add_filter(gtk_widget_get_window(main_widgets->window), keygrab_cb, &km);
589 do {
590 while (gtk_events_pending()) {
591 if (km.group==2) { break; }
592 gtk_main_iteration();
593 }
594 if (km.group==2) { break; }
595 dosleep();
596 } while (km.group!=2);
597
598 gdk_window_remove_filter(gtk_widget_get_window(main_widgets->window), keygrab_cb, &km);
599 if (prompt && doc && doc->is_valid) {
600 sci_send_command(doc->editor->sci, SCI_CALLTIPCANCEL);
601 }
602 km.group=0; /* reset the hijacked flag before passing to GDK */
603 gdk_key_map = gdk_keymap_get_default();
604 lua_pushstring(L, gdk_keyval_name(gdk_keymap_lookup_key(gdk_key_map, &km)));
605
606 return 1;
607 }
608
609
glspi_reloadconf(lua_State * L)610 static gint glspi_reloadconf(lua_State* L)
611 {
612 main_reload_configuration();
613 return 0;
614 }
615
616
617
618 static const struct luaL_reg glspi_app_funcs[] = {
619 {"pluginver", glspi_pluginver},
620 {"appinfo", glspi_appinfo},
621 {"xsel", glspi_xsel},
622 {"signal", glspi_signal},
623 {"stat", glspi_stat},
624 {"status", glspi_status},
625 {"basename", glspi_basename},
626 {"dirname", glspi_dirname},
627 {"fullpath", glspi_fullpath},
628 {"dirlist", glspi_dirlist},
629 {"wkdir", glspi_wkdir},
630 {"keycmd", glspi_keycmd},
631 {"launch", glspi_launch},
632 {"keygrab", glspi_keygrab},
633 {"reloadconf", glspi_reloadconf},
634 {NULL,NULL}
635 };
636
glspi_init_app_funcs(lua_State * L,const gchar * script_dir)637 void glspi_init_app_funcs(lua_State *L, const gchar*script_dir) {
638 glspi_script_dir = script_dir;
639 luaL_register(L, NULL,glspi_app_funcs);
640 }
641