1 #ifdef COPYRIGHT_INFORMATION
2 #include "gplv3.h"
3 #endif
4 /*
5 * Copyright (C) 2002-2012 Edscott Wilson Garcia
6 * EMail: edscott@users.sf.net
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program;
21 */
22 #ifdef HAVE_READLINE_HISTORY_H
23 # include <readline/history.h>
24 #endif
25
place_cursor(GtkTextView * status,gint pos)26 static void place_cursor(GtkTextView *status, gint pos){
27 GtkTextIter iter;
28 GtkTextBuffer *buffer = gtk_text_view_get_buffer (status);
29 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 2+pos);
30 gtk_text_buffer_place_cursor (buffer, &iter);
31 }
32
place_command(GtkWidget * textview,widgets_t * widgets_p,const gchar * p)33 static void place_command(GtkWidget *textview, widgets_t *widgets_p, const gchar *p){
34 rfm_status (widgets_p, "xffm/emblem_terminal", (char *) p, NULL);
35 g_object_set_data (G_OBJECT (textview), "clean", NULL);
36 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (widgets_p->status), TRUE);
37 gtk_widget_grab_focus (widgets_p->status);
38 }
39
40 static void
print_suggestion(widgets_t * widgets_p,const char * data,int i,gboolean cr)41 print_suggestion (
42 widgets_t * widgets_p,
43 const char *data,
44 int i,
45 gboolean cr
46 ) {
47 gchar *element = g_strdup_printf ("%d", i);
48 rfm_diagnostics (widgets_p, NULL, "[", NULL);
49 rfm_diagnostics (widgets_p, "xffm_tag/green", element, NULL);
50 rfm_diagnostics (widgets_p, NULL, "] ", NULL);
51 rfm_diagnostics (widgets_p, "xffm_tag/blue", data, NULL);
52 if(cr)
53 rfm_diagnostics (widgets_p, NULL, "\n", NULL);
54
55 g_free (element);
56 }
57
58 static gboolean
internal_cd(widgets_t * widgets_p,gchar ** argvp)59 internal_cd ( widgets_t * widgets_p, gchar ** argvp) {
60 view_t *view_p = widgets_p->view_p;
61 gchar *gg=NULL;
62
63 if(argvp[1]) {
64 if (*argvp[1] == '~'){
65 if (strcmp(argvp[1], "~")==0 ||
66 strncmp(argvp[1], "~/", strlen("~/"))==0){
67 gg = g_strdup_printf("%s%s", g_get_home_dir (), argvp[1]+1);
68 } else {
69 //gchar *tilde_dir = rfm_get_tilde_dir(argvp[1]);
70 gchar *tilde_dir =
71 rfm_natural(RFM_MODULE_DIR, "completion",
72 argvp[1], "rfm_get_tilde_dir");
73 if (tilde_dir) gg = g_strconcat(tilde_dir,
74 strchr(argvp[1], '/')+1, NULL);
75 else gg = g_strdup(argvp[1]);
76 g_free(tilde_dir);
77 }
78 } else {
79 gg = g_strdup(argvp[1]);
80 }
81
82 } else {
83 gg = g_strdup(g_get_home_dir ());
84 }
85 rfm_show_text (widgets_p);
86 NOOP ("CD: gg=%s argv[1]=%s\n", gg, argvp[1]);
87
88 // must allow relative paths too.
89 if (!g_path_is_absolute(gg)){
90 if(!view_p->en || ! view_p->en->path ||
91 !rfm_g_file_test_with_wait (view_p->en->path, G_FILE_TEST_IS_DIR))
92 {
93 rfm_diagnostics (widgets_p, "xffm/stock_dialog-error", NULL);
94 rfm_diagnostics (widgets_p, "xffm_tag/stderr", "* ", gg, ": ", strerror (ENOENT), "\n", NULL);
95 g_free (gg);
96 return TRUE;
97 }
98 gchar *fullpath = g_strconcat(view_p->en->path, "/", gg, NULL);
99 g_free(gg);
100 gg = fullpath;
101 NOOP("CD: fullpath=%s\n", fullpath);
102 }
103
104 if (gg[strlen(gg)-1]=='/' || strstr(gg, "/..")){
105 gchar *rpath = realpath(gg, NULL);
106 g_free(gg);
107 gg=rpath;
108 }
109 rfm_diagnostics (widgets_p, "xffm_tag/command", "cd", " ", NULL);
110 rfm_diagnostics (widgets_p, "xffm_tag/green", gg, "\n", NULL);
111
112 gchar *rpath = realpath(gg, NULL);
113 if (!rpath){
114 rfm_diagnostics (widgets_p, "xffm/stock_dialog-error", NULL);
115 rfm_diagnostics (widgets_p, "xffm_tag/stderr", gg, ": ", strerror (ENOENT), "\n", NULL);
116 g_free (gg);
117 return TRUE;
118 }
119 g_free (rpath);
120
121 gtk_widget_grab_focus (widgets_p->paper);
122
123 rodent_push_view_go_history ();
124 record_entry_t *new_en = rfm_stat_entry (gg, 0);
125 view_p->module = NULL;
126 if (!rodent_refresh (widgets_p, new_en)){
127 rfm_destroy_entry(new_en);
128 } else {
129 rfm_save_to_go_history ((gchar *) gg);
130 gchar *command = g_strdup_printf ("cd %s", gg);
131 rfm_save_sh_command_history (view_p, command);
132 }
133 g_free (gg);
134 return TRUE;
135 }
136
137 static void
print_history(widgets_t * widgets_p)138 print_history ( widgets_t * widgets_p) {
139 int i;
140 view_t *view_p = widgets_p->view_p;
141 GList *p;
142 rfm_diagnostics (widgets_p, "xffm_tag/command", "history:", "\n", NULL);
143
144 for(i = 1, p = g_list_first (view_p->sh_command); p && p->data; p = p->next, i++) {
145 print_suggestion (widgets_p, (char *) p->data, i, TRUE);
146 }
147
148 }
149
150 static void
print_tab(widgets_t * widgets_p,gchar * text,gchar * text2)151 print_tab ( widgets_t * widgets_p, gchar * text, gchar *text2) {
152 int tab_len = 18;
153 rfm_diagnostics (widgets_p, "xffm_tag/red", text, text2, NULL);
154 gint string_length = (text)?strlen(text):0 + (text2)?strlen(text2):0;
155 for(tab_len = tab_len - string_length; tab_len > 0; tab_len--)
156 rfm_diagnostics (widgets_p, NULL, " ", NULL);
157 }
158
159 static void
print_history_help(widgets_t * widgets_p)160 print_history_help ( widgets_t * widgets_p) {
161 rfm_diagnostics (widgets_p, "xffm_tag/command",
162 _("History"), " (", _("Get help..."), "):\n", NULL);
163 print_tab (widgets_p, "?",NULL);
164 rfm_diagnostics (widgets_p, "xffm_tag/green",
165 _("Show help about options"), "\n", NULL);
166
167 print_tab (widgets_p, "history",NULL);
168 rfm_diagnostics (widgets_p, "xffm_tag/green",
169 _("Show History"), "\n", NULL);
170
171 print_tab (widgets_p, "!","n");
172 rfm_diagnostics (widgets_p, "xffm_tag/green",
173 _("Line Number"), " (", _("Command Line"), ") ", NULL);
174 rfm_diagnostics (widgets_p, "xffm_tag/red",
175 "n", NULL);
176 rfm_diagnostics (widgets_p, "xffm_tag/green",
177 ".", "\n", NULL);
178
179 print_tab (widgets_p, "!",_("STRING"));
180 rfm_diagnostics (widgets_p, "xffm_tag/green",
181 _("Complete Match"), NULL);
182 rfm_diagnostics (widgets_p, "xffm_tag/red",
183 " ", _("STRING"), NULL);
184 rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
185
186 print_tab (widgets_p, "!?",_("STRING"));
187 rfm_diagnostics (widgets_p, "xffm_tag/green",
188 _("Anywhere"), NULL);
189 rfm_diagnostics (widgets_p, "xffm_tag/red", " ",
190 _("STRING"), NULL);
191 rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
192
193 print_tab (widgets_p, _("STRING"),"<CTRL+TAB>");
194 rfm_diagnostics (widgets_p, "xffm_tag/green",
195 _("Completion mode:")," ", _("Command Line"), NULL);
196 //rfm_diagnostics (widgets_p, "xffm_tag/red", _("STRING"), NULL);
197 rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
198
199 print_tab (widgets_p, _("STRING"),"<TAB>");
200 rfm_diagnostics (widgets_p, "xffm_tag/green",
201 _("Completion mode:")," ", "bash", NULL);
202 //rfm_diagnostics (widgets_p, "xffm_tag/red", _("STRING"), NULL);
203 rfm_diagnostics (widgets_p, "xffm_tag/green", ".", "\n", NULL);
204 rfm_diagnostics (widgets_p, "xffm/stock_dialog-info",NULL);
205 rfm_diagnostics (widgets_p, "xffm_tag/blue", _("Full readline library history commands available"), "\n", NULL);
206
207 #if 0
208 I have not used these nonconventional options, ever...
209 print_tab (widgets_p, "!!", NULL);
210 rfm_diagnostics (widgets_p, "xffm_tag/green",
211 _("Clear History"), " (", _("Current"), ")", "\n", NULL);
212
213 print_tab (widgets_p, "!!!", NULL);
214 rfm_diagnostics (widgets_p, "xffm_tag/green",
215 _("Clear History"), " (", _("Disk"), ")", "\n", NULL);
216 #endif
217 }
218
219 static void
suggest_command(widgets_t * widgets_p,const char * complete,gboolean anywhere)220 suggest_command (
221 widgets_t * widgets_p,
222 const char *complete,
223 gboolean anywhere
224 ) {
225 view_t *view_p = widgets_p->view_p;
226 GList *p;
227 char *suggest = NULL;
228 for(p = g_list_last (view_p->sh_command); p && p->data; p = p->prev) {
229 char *data = (char *) p->data;
230 if((anywhere && strstr (data, complete)) || (!anywhere && strncmp (complete, data, strlen (complete)) == 0)) {
231 suggest = g_strdup (data);
232 break;
233 }
234 NOOP ("COMPLETE: ?? %s\n", data);
235 }
236 if(suggest) {
237 rfm_status (widgets_p, "xffm/emblem_terminal", suggest, NULL);
238 g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
239 g_free (suggest);
240 }
241 }
242
243 #ifndef HAVE_READLINE_HISTORY_H
244
245 static gboolean
internal_history(widgets_t * widgets_p,gchar * cmd)246 internal_history ( widgets_t * widgets_p, gchar *cmd) {
247 view_t *view_p = widgets_p->view_p;
248 // internal fall back for when compiled without history library.
249
250 //
251 const char *b = cmd + 1;
252 // errno = 0;
253 long n = strtol (b, NULL, 10);
254 NOOP ("COMPLETE: n=%ld\n", n);
255 /* if (errno) {
256 rfm_diagnostics (widgets_p, "xffm/stock_dialog-warning", b, ": ",
257 strerror (errno), NULL);
258 return TRUE;
259 }*/
260 if(n > 1 && n <= g_list_length (view_p->sh_command)) {
261 GList *p = g_list_nth (view_p->sh_command, n - 1);
262 if(p && p->data) {
263 rfm_status (widgets_p, "xffm/emblem_terminal", (char *) p->data, NULL);
264 g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
265 }
266 } else {
267 if(*b != '?') {
268 suggest_command (widgets_p, b, FALSE);
269 } else {
270 suggest_command (widgets_p, b + 1, TRUE);
271 }
272 }
273 return TRUE;
274
275 }
276 #endif
277
278 #ifdef HAVE_READLINE_HISTORY_H
readline_history(widgets_t * widgets_p,gchar * cmd)279 static gchar *readline_history(widgets_t *widgets_p, gchar *cmd){
280 TRACE("readline_history: \"%s\"\n", cmd);
281 char *expansion;
282 static gchar *history = NULL;
283 if (!history) history = g_build_filename (LP_TERMINAL_HISTORY, NULL);
284
285 read_history(history);
286 using_history();
287
288
289 int result = history_expand (cmd, &expansion);
290 TRACE ("result=%d expansion=%s\n", result, expansion);
291 if (result < 0){
292 rfm_diagnostics(widgets_p, "xffm/stock_dialog-warning", expansion, "\n", NULL);
293 }
294 if (result > 0){
295 place_command(widgets_p->status, widgets_p, expansion);
296 place_cursor(GTK_TEXT_VIEW(widgets_p->status), strlen(expansion));
297 }
298 if (!expansion || !strlen(expansion) || result <= 0){
299 g_free(expansion);
300 expansion = NULL;
301 }
302 clear_history();
303 return expansion;
304 }
305 #endif
306
307 static gboolean
process_internal_commands(widgets_t * widgets_p,gchar ** command)308 process_internal_commands ( widgets_t * widgets_p, gchar ** command) {
309 TRACE("process_internal: %s\n", *command);
310
311 if (*command == NULL) return TRUE;
312 gchar *cmd = *command;
313 #ifdef HAVE_READLINE_HISTORY_H
314 // readline history?
315 if ((*cmd == '!' && cmd[1] != ' ' && cmd[1] != '=') ||
316 (*cmd =='^' && strchr(cmd+1, '^'))){
317 gchar *expansion;
318 if ((expansion = readline_history(widgets_p, *command)) != NULL){
319 TRACE("readline expansion=%s\n", expansion);
320 g_free(expansion);
321 return TRUE;
322 }
323 }
324 #else
325 if (*cmd == '!' && cmd[1] != ' ' && cmd[1] != '='){
326 internal_history(widgets_p, cmd);
327 return TRUE;
328 }
329
330
331 #endif
332 gchar *command2=NULL;
333 if (strchr(*command, ';')){
334 // split command in two.
335 command2 = g_strdup(strchr(*command, ';') + 1);
336 *strchr(*command, ';')=0;
337 }
338 gint argcp;
339 gchar **argvp;
340 GError *error = NULL;
341 if(!g_shell_parse_argv (*command, &argcp, &argvp, &error)) {
342 rfm_diagnostics (widgets_p, "xffm/stock_dialog-error", error->message, "\n", NULL);
343 g_error_free (error);
344 if (command2){
345 g_free(*command);
346 *command=command2;
347 return FALSE;
348 }
349 return TRUE;
350 } else {
351 // shortcircuit chdir and history commands
352 gboolean is_internal = FALSE;
353 if(strcmp (argvp[0], "cd") == 0) {
354 internal_cd (widgets_p, argvp);
355 NOOP ("CD: command=%s argv[1]=%s\n", *command, argvp[1]);
356 is_internal = TRUE;
357 } else if(strncmp (argvp[0], "history", strlen ("history")) == 0) {
358 rfm_show_text(widgets_p);
359 print_history (widgets_p);
360 rfm_status (widgets_p, "xffm/emblem_terminal", "", NULL);
361 g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
362 is_internal = TRUE;
363 } else if(strncmp (argvp[0], "?", strlen ("?")) == 0) {
364 print_history_help (widgets_p);
365 rfm_status (widgets_p, "xffm/emblem_terminal", "", NULL);
366 g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
367 is_internal = TRUE;
368 }
369
370 if (command2){
371 g_free(*command);
372 *command=command2;
373 return FALSE;
374 }
375 g_strfreev (argvp);
376 return is_internal;
377 }
378 }
379
380 static
sudo_fix(gchar * command)381 gchar *sudo_fix(gchar *command){
382 if (! command || !strstr(command, "sudo ")) return command;
383 if (strncmp(strstr(command, "sudo "), "sudo -A ", strlen("sudo -A "))!=0)
384 {
385 gchar *original_head=g_strdup(command);
386 gchar *pos = strstr(original_head, "sudo ");
387 if (pos){
388 *pos = 0;
389 gchar *tail=g_strdup(strstr(command, "sudo ")+strlen("sudo "));
390 tail=sudo_fix(tail);
391 gchar *new_command = g_strconcat(original_head, "sudo -A ", tail, NULL);
392 g_free(tail);
393 g_free(command);
394 command = new_command;
395 }
396 g_free(original_head);
397 }
398 return command;
399 }
400
401 static gint
on_motion_event(GtkWidget * textview,GdkEventMotion * event,gpointer data)402 on_motion_event (
403 GtkWidget * textview,
404 GdkEventMotion* event,
405 gpointer data
406 ) {
407 //widgets_t *widgets_p = (widgets_t *) data;
408 //view_t *view_p = widgets_p->view_p;
409 NOOP(stderr,"GdkEventMotion...\n");
410 if (event->x < TINY_ICON_SIZE) return TRUE;
411 return FALSE;
412 }
413 static gint
on_button_press(GtkWidget * textview,GdkEventButton * event,gpointer data)414 on_button_press (
415 GtkWidget * textview,
416 GdkEventButton * event,
417 gpointer data
418 ) {
419 widgets_t *widgets_p = (widgets_t *) data;
420 //view_t *view_p = widgets_p->view_p;
421 NOOP(stderr,"on_button_press...%lf\n", event->x);
422 if (lp_get_active(widgets_p)){
423 if (event->x < TINY_ICON_SIZE)event->x = TINY_ICON_SIZE;
424 }
425 return FALSE;
426 }
427 static gint
on_button_release(GtkWidget * textview,GdkEventButton * event,gpointer data)428 on_button_release (
429 GtkWidget * textview,
430 GdkEventButton * event,
431 gpointer data
432 ) {
433 widgets_t *widgets_p = (widgets_t *) data;
434 view_t *view_p = widgets_p->view_p;
435 NOOP("on_button_release...%lf\n", event->x);
436 if (lp_get_active(widgets_p)){
437 if (event->x < TINY_ICON_SIZE)event->x = TINY_ICON_SIZE;
438 return FALSE;
439 }
440
441
442 if(!rfm_population_try_read_lock (view_p, "on_button_release")) return FALSE;
443 rodent_unselect_all_pixbuf (view_p);
444 rodent_unsaturate_icon (view_p);
445 status_grab_focus (widgets_p->view_p, 0);
446 if (!view_p->lp_command) view_p->lp_command = g_strdup("");
447 rfm_status (widgets_p, "xffm/emblem_terminal", view_p->lp_command, NULL);
448 g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
449 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (widgets_p->status), TRUE);
450 lp_set_active(widgets_p, TRUE);
451
452 rfm_population_read_unlock (view_p, "on_button_release");
453
454 return FALSE;
455 }
456
457 static gboolean
csh_completion(GtkWidget * textview,widgets_t * widgets_p,gint direction)458 csh_completion(GtkWidget * textview, widgets_t *widgets_p, gint direction){
459 view_t *view_p = widgets_p->view_p;
460 gchar *command = get_current_text ((GtkTextView *) widgets_p->status);
461 if (!command || !strlen(command)) {
462 g_object_set_data(G_OBJECT(widgets_p->status), "csh_cmd_len", NULL);
463 g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", NULL);
464 return FALSE;
465 }
466 gint csh_cmd_len =
467 GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widgets_p->status), "csh_cmd_len"));
468 if (!csh_cmd_len) {
469 g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", NULL);
470 return FALSE;
471 }
472 gint nth = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widgets_p->status), "csh_nth"));
473
474 if (!nth) nth = g_list_length(view_p->sh_command);
475 gchar *p=NULL;
476 if (direction > 0){
477 if (nth > 1){
478 nth--;
479 GList *list = g_list_nth(view_p->sh_command, nth);
480 for (;list && list->data;list=list->prev, nth--){
481 p = list->data;
482 if (p && strncmp(command, p, csh_cmd_len)==0) break;
483 p=NULL;
484 }
485 }
486 } else {
487 nth++;
488 if (nth <= g_list_length(view_p->sh_command)){
489 GList *list = g_list_nth(view_p->sh_command, nth);
490 for (;list && list->data;list=list->next, nth++){
491 p = list->data;
492 if (p && strncmp(command, p, csh_cmd_len)==0) break;
493 p=NULL;
494 }
495 }
496 }
497 if (p){
498 TRACE("gotcha (%d): %s\n", nth, p);
499 g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", GINT_TO_POINTER(nth));
500 place_command(textview, widgets_p, p);
501 place_cursor(GTK_TEXT_VIEW(widgets_p->status), csh_cmd_len);
502 }
503 return TRUE;
504 }
505
506 static gboolean
offset_history(GtkWidget * textview,widgets_t * widgets_p,gint offset)507 offset_history(GtkWidget *textview, widgets_t *widgets_p, gint offset){
508 view_t *view_p = widgets_p->view_p;
509 void *p = g_list_nth_data (view_p->sh_command, view_p->sh_command_counter + offset);
510 NOOP ("get nth sh_command_counter=%d\n", view_p->sh_command_counter + offset);
511 if(p) {
512 view_p->sh_command_counter += offset;
513 place_command(textview, widgets_p, p);
514 }
515 return TRUE;
516 }
517
518 static gint
on_status_key_press(GtkWidget * textview,GdkEventKey * event,gpointer data)519 on_status_key_press (
520 GtkWidget * textview,
521 GdkEventKey * event,
522 gpointer data
523 ) {
524 widgets_t *widgets_p = (widgets_t *) data;
525 view_t *view_p = widgets_p->view_p;
526 if(!event) {
527 DBG ("on_status_key_press(): returning on event==0\n");
528 //status_grab_focus (widgets_p->view_p, 0);
529 return TRUE;
530 }
531
532 // backspace must not erase icon. Place_cursor is offset by 2 for this purpose.
533 TRACE ("on_status_key_press(): * on_status_key_press: got key= 0x%x\n", event->keyval);
534 if(event->keyval == GDK_KEY_Home) {
535 place_cursor(GTK_TEXT_VIEW(widgets_p->status), 0);
536 return TRUE;
537 }
538 if(event->keyval == GDK_KEY_BackSpace || event->keyval == GDK_KEY_Left) {
539 gint intval;
540 GtkTextBuffer *buffer = gtk_text_view_get_buffer ((GtkTextView *) widgets_p->status);
541 g_object_get (G_OBJECT (buffer), "cursor-position", &intval, NULL);
542 NOOP ("on_status_key_press(): cursor-position =%d\n", intval);
543 if(intval > 2) {
544 // offset==1 is the icon and offset==2 is the space after icon
545 return FALSE;
546 }
547 return TRUE;
548 }
549
550 // CTRL-TAB for command history completion
551 if(event->keyval == GDK_KEY_Tab && event->state & GDK_CONTROL_MASK) {
552 // do history completion ...
553 gchar *complete = get_current_text ((GtkTextView *) widgets_p->status);
554 if (strncmp(complete, "sudo", strlen("sudo"))==0 &&
555 strncmp(complete, "sudo -A", strlen("sudo -A"))){
556 gchar *o = complete;
557 gchar *oo = o+strlen("sudo");
558 while (*oo == ' ') oo++;
559 complete = g_strconcat("sudo -A ", oo, NULL);
560 g_free(o);
561 }
562 #if 0
563 // too nerdy?
564 // "&&" --> " && "
565 // "||" --> " || "
566 // ";" --> "; "
567 // "|" --> " | "
568 if (command && strlen(command)){
569 command = subst(complete, "&&", " && ");
570 command = subst(complete, "||", " || ");
571 command = subst(complete, ">>", " >> ");
572 command = subst(complete, ";", "; ");
573 while (strchr(complete, " ")) command = subst(complete, " ", " ");
574 }
575 #endif
576 gchar *suggest = rfm_rational(RFM_MODULE_DIR,
577 "completion", widgets_p, complete,
578 "rfm_history_completion");
579 g_free (complete);
580 if(suggest) {
581 rfm_status (widgets_p, "xffm/emblem_terminal", suggest, NULL);
582 g_free (suggest);
583 }
584 g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
585 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (widgets_p->status), TRUE);
586 return TRUE;
587 }
588 // tab for bash completion
589 if(event->keyval == GDK_KEY_Tab) {
590 gchar *head=get_text_to_cursor(GTK_TEXT_VIEW(widgets_p->status));
591 gint head_len = strlen(head);
592 gchar *token = get_current_text (GTK_TEXT_VIEW(widgets_p->status));
593 gint token_len = strlen(token);
594
595 //gchar *tail=get_text_from_cursor(GTK_TEXT_VIEW(widgets_p->status));
596 g_free (head);
597 //gchar *suggest = rfm_bash_complete(widgets_p, token, head_len);
598 gchar *suggest = rfm_complex(RFM_MODULE_DIR, "completion",
599 widgets_p, token, GINT_TO_POINTER(head_len),
600 "rfm_bash_complete");
601 g_free (token);
602
603 if (suggest) {
604 gint suggest_len = strlen(suggest);
605 GtkTextIter end;
606 GtkTextBuffer *buffer =
607 gtk_text_view_get_buffer (GTK_TEXT_VIEW(widgets_p->status));
608 gint offset = -1;
609 // +2 is icon and space...
610 offset = head_len + (suggest_len - token_len) + 2;
611 rfm_status (widgets_p, "xffm/emblem_terminal", suggest, NULL);
612 gtk_text_buffer_get_iter_at_offset (buffer, &end, offset);
613 gtk_text_buffer_place_cursor(buffer, &end);
614 }
615 g_free(suggest);
616
617 g_object_set_data (G_OBJECT (widgets_p->status), "clean", NULL);
618 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (widgets_p->status), TRUE);
619 return TRUE;
620 }
621 if ((event->state & GDK_SHIFT_MASK)
622 &&
623 ((event->keyval == GDK_KEY_Page_Up) ||
624 (event->keyval == GDK_KEY_Page_Down) ||
625 (event->keyval == GDK_KEY_Up) ||
626 (event->keyval == GDK_KEY_Down)) )
627 {
628 // Show text.
629 rfm_show_text (widgets_p);
630 // Get diagnostics area height.
631 // Scroll up or down.
632 gboolean page = (event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Page_Down);
633 gboolean up = (event->keyval == GDK_KEY_Page_Up || event->keyval == GDK_KEY_Up);
634 NOOP(stderr, "scroll \n");
635 rfm_scroll(widgets_p, up, page);
636
637 return TRUE;
638 }
639 if(event->keyval == GDK_KEY_Up) {
640 // csh command completion
641 if (csh_completion(textview, widgets_p, 1)) return TRUE;
642 // push/pop history
643 offset_history(textview, widgets_p, -1);
644 return TRUE;
645 }
646
647
648 if(event->keyval == GDK_KEY_Down) {
649 // csh command completion
650 if (csh_completion(textview, widgets_p, -1)) return TRUE;
651 // push/pop history
652 offset_history(textview, widgets_p, 1);
653 return TRUE;
654 }
655 if(event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
656 if(g_object_get_data (G_OBJECT (textview), "clean") == NULL) {
657 g_object_set_data(G_OBJECT(widgets_p->status), "csh_cmd_len", NULL);
658 g_object_set_data(G_OBJECT(widgets_p->status), "csh_nth", NULL);
659 // get the command
660 gchar *command = get_current_text ((GtkTextView *) widgets_p->status);
661 if(command && strlen (command)) {
662 // show the lp output area
663 rfm_show_text (widgets_p);
664
665 // first process internal commands for cd and history
666 // Internal commands will return focus to paper
667 // (cd is a threaded reload which will overwrite
668 // the status line when done)
669 if(process_internal_commands (widgets_p, &command)){
670 // probably not the best choice to send focus to paper...
671 // lp_set_active(widgets_p, FALSE);
672 // rfm_update_status_line (widgets_p->view_p);
673 return TRUE;
674 }
675
676 // command is now external for /bin/sh
677 // printstatus with the run icon run.png
678 // this is already done in run.c
679 // rfm_diagnostics(widgets_p, "run.png",command,"\n",NULL);
680
681 // run the command (in a shell)
682 // XXX This will block if located at a remote directory with
683 // a broken network connection.
684 if(widgets_p->workdir) {
685 g_free (widgets_p->workdir);
686 }
687 view_t *view_p = widgets_p->view_p;
688 if (!view_p->en || !view_p->en->path || ! rfm_g_file_test(view_p->en->path, G_FILE_TEST_IS_DIR)){
689 widgets_p->workdir = g_strdup (g_get_home_dir());
690 ;
691 } else {
692 widgets_p->workdir = g_strdup (view_p->en->path);
693 }
694 // Fix any sudo commands to use the -A option
695 command = sudo_fix(command);
696 RFM_THREAD_RUN (widgets_p, command, FALSE);
697 g_free (command);
698 }
699 }
700 rfm_status (widgets_p, "xffm/emblem_terminal", NULL);
701 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (widgets_p->status), TRUE);
702 g_object_set_data (G_OBJECT (textview), "clean", NULL);
703 return TRUE;
704 }
705
706 if(g_object_get_data (G_OBJECT (textview), "clean")) {
707 rfm_status (widgets_p, "xffm/emblem_terminal", NULL);
708 }
709 // set unclean status...
710 g_object_set_data (G_OBJECT (textview), "clean", NULL);
711 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (widgets_p->status), TRUE);
712 // Allow default handler for text buffer
713 if (event->keyval >= GDK_KEY_space && event->keyval <= GDK_KEY_asciitilde){
714 gchar *command = get_current_text ((GtkTextView *) widgets_p->status);
715 gint csh_cmd_len = strlen(command)+1;
716 g_object_set_data(G_OBJECT(widgets_p->status), "csh_cmd_len", GINT_TO_POINTER(csh_cmd_len));
717 }
718
719 return FALSE;
720 }
721
722
723 static gboolean
lp_get_active(widgets_t * widgets_p)724 lp_get_active(widgets_t *widgets_p){
725 if(g_object_get_data (G_OBJECT (widgets_p->status), "active")) return TRUE;
726 return FALSE;
727 }
728
lp_set_active(widgets_t * widgets_p,gboolean state)729 static void lp_set_active(widgets_t *widgets_p, gboolean state){
730 if (state) g_object_set_data (G_OBJECT (widgets_p->status), "active", GINT_TO_POINTER(1));
731 else g_object_set_data (G_OBJECT (widgets_p->status), "active", NULL);
732 return;
733 }
734
735 // Shared keybindings with either iconview or callback
736 typedef struct lpkey_t{
737 guint key;
738 guint mask;
739 } lpkey_t;
740 /*
741 Ctrl-Left Word left
742 Ctrl-Right Word right
743 Ctrl-Y Delete line
744 Ctrl-K Delete to end of line
745 Ctrl-BS Delete word left
746 Ctrl-Del Delete word right
747 Ctrl-A Select all text
748 Ctrl-U Deselect block
749 Ctrl-V Paste block from clipboard
750 Ctrl-X Cut block
751 Ctrl-C Copy block to clipboard
752 */
753
754 #if 10
755 static gboolean
lp_is_key(GdkEventKey * event)756 lp_is_key(GdkEventKey * event){
757 // No mask, send to lp
758 if (event->state == 0) return TRUE;
759 // Shift page up/down, send to lp
760 if ((event->state & GDK_SHIFT_MASK)
761 &&
762 ((event->keyval == GDK_KEY_Page_Up) ||
763 (event->keyval == GDK_KEY_Page_Down))
764 ){
765 return TRUE;
766 }
767 // Control mask exceptions
768 gint keys[] = {
769 GDK_KEY_Tab,
770 GDK_KEY_Right,
771 GDK_KEY_Left,
772 GDK_KEY_y,
773 GDK_KEY_Y,
774 GDK_KEY_k,
775 GDK_KEY_K,
776 GDK_KEY_Delete,
777 GDK_KEY_BackSpace,
778 GDK_KEY_a,
779 GDK_KEY_A,
780 GDK_KEY_u,
781 GDK_KEY_U,
782 GDK_KEY_v,
783 GDK_KEY_V,
784 GDK_KEY_x,
785 GDK_KEY_X,
786 GDK_KEY_c,
787 GDK_KEY_C,
788 -1
789 };
790 gint *key=keys;
791 for (;key && *key>0; key++){
792 if (event->keyval == *key) {
793 return TRUE;
794 }
795 }
796 return FALSE;
797 }
798 #endif
799
800