summaryrefslogtreecommitdiff
path: root/src/editor.c
diff options
context:
space:
mode:
authorsabetts <sabetts>2003-05-27 07:51:03 +0000
committersabetts <sabetts>2003-05-27 07:51:03 +0000
commit6cc80f10f7cb3de33626cf178bda532d741cf388 (patch)
tree87c04a9e6b4ecee8c49dcff7ad9be54b3fceb874 /src/editor.c
parent785627843a7aa83ebf451add7b9a97e0b800e390 (diff)
downloadratpoison-6cc80f10f7cb3de33626cf178bda532d741cf388.zip
* src/editor.c (saved_command): new local global
(edit_binding): new typedef (edit_binding): new struct (edit_bindings): new local global (input_line_new): new function (input_line_free): likewise (execute_edit_action): likewise (editor_forward_char): likewise (editor_backward_char): likewise (editor_forward_word): likewise (editor_backward_word): likewise (editor_beginning_of_line): likewise (editor_end_of_line): likewise (editor_delete_char): likewise (editor_backward_delete_char): likewise (editor_kill_word): likewise (editor_backward_kill_word): likewise (editor_kill_line): likewise (editor_backward_kill_line): likewise (editor_history_previous): likewise (editor_history_next): likewise (editor_abort): likewise (editor_no_action): likewise (editor_insert): likewise (editor_enter): likewise (paste_cut_buffer): likewise (paste_primary_selection): likewise (editor_paste_selection): likewise (editor_complete): likewise (editor_forward_char): new prototype (editor_backward_char): likewise (editor_forward_word): likewise (editor_backward_word): likewise (editor_beginning_of_line): likewise (editor_end_of_line): likewise (editor_delete_char): likewise (editor_backward_delete_char): likewise (editor_kill_word): likewise (editor_backward_kill_word): likewise (editor_kill_line): likewise (editor_paste_selection): likewise (editor_abort): likewise (editor_no_action): likewise (editor_enter): likewise (editor_history_previous): likewise (editor_history_next): likewise (editor_complete): likewise (editor_backward_kill_line): likewise * src/sbuf.h (sbuf): add node field. * src/main.c (xrealloc): don't print debugger output (init_defaults): init history_size (main): initialize rp_selection (main): load history (clean_up): save history * src/linkedlist.h (list_first): new macro * src/input.h (free_history): remove prototype (ring_bell): new function * src/input.c: include unistd.h (input_history): remove (input_num_history_entries): likewise (update_input_window): remove prompt, input, and input_len arguments. add line argument. (update_input_window): use line argument. (ring_bell): new function (get_input): take completion_fn argument. prototype and callers updated. (free_history): remove function (get_more_input): take completion_fn argument. prototype and callers updated. use line structure and its functionality. * src/globals.h (MAX_FONT_WIDTH): new define (rp_selection): new extern * src/globals.c (rp_selection): new global * src/completions.h (completions_new): new prototype (completions_free): likewise (completions_assign): likewise (completions_update): likewise (completions_next_completion): likewise * src/completions.c (completions_new): new function (completions_free): likewise (completions_assign): likewise (completions_update): likewise (completions_next_completion): likewise * src/Makefile.am (ratpoison_SOURCES): add editor.c editor.h history.h and history.c * src/data.h (rp_completions): new typedef (rp_input_line): likewise (completion_fn): likewise (rp_defaults): new field, history_size (rp_completions): new struct (rp_input_line): new struct * src/conf.h (MAX_HISTORY_SIZE): new define (HISTORY_FILE): likewise (VISUAL_BELL): likewise (MODIFIER_PREFIX): set to RP_CONTROL_MASK (INPUT_ABORT_MODIFIER): likewise (INPUT_PREV_HISTORY_MODIFIER): likewise (INPUT_NEXT_HISTORY_MODIFIER): likewise (RESIZE_VGROW_MODIFIER): likewise (RESIZE_VSHRINK_MODIFIER): likewise (RESIZE_HGROW_MODIFIER): likewise (RESIZE_HSHRINK_MODIFIER): likewise * src/actions.c (trivial_completions): new function (window_completions): likewise (colon_completions): likewise (exec_completions): likewise (cmd_select): pass window_completions to get_input (cmd_rename): pass trivial_completions to get_input (cmd_colon): pass colon_completions to get_input and get_more_input (cmd_exec): pass exec_completions to get_input (cmd_newwm): pass trivial_completions to get_input (cmd_resize): convert the keysym modifier to something ratpoison understands.
Diffstat (limited to 'src/editor.c')
-rw-r--r--src/editor.c576
1 files changed, 576 insertions, 0 deletions
diff --git a/src/editor.c b/src/editor.c
new file mode 100644
index 0000000..f300bb3
--- /dev/null
+++ b/src/editor.c
@@ -0,0 +1,576 @@
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <X11/Xlib.h>
+#include <X11/keysym.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+
+#include "ratpoison.h"
+
+/* bind functions */
+static edit_status editor_forward_char (rp_input_line *line);
+static edit_status editor_backward_char (rp_input_line *line);
+static edit_status editor_forward_word (rp_input_line *line);
+static edit_status editor_backward_word (rp_input_line *line);
+static edit_status editor_beginning_of_line (rp_input_line *line);
+static edit_status editor_end_of_line (rp_input_line *line);
+static edit_status editor_delete_char (rp_input_line *line);
+static edit_status editor_backward_delete_char (rp_input_line *line);
+static edit_status editor_kill_word (rp_input_line *line);
+static edit_status editor_backward_kill_word (rp_input_line *line);
+static edit_status editor_kill_line (rp_input_line *line);
+static edit_status editor_paste_selection (rp_input_line *line);
+static edit_status editor_abort (rp_input_line *line);
+static edit_status editor_no_action (rp_input_line *line);
+static edit_status editor_enter (rp_input_line *line);
+static edit_status editor_history_previous (rp_input_line *line);
+static edit_status editor_history_next (rp_input_line *line);
+static edit_status editor_complete (rp_input_line *line);
+static edit_status editor_backward_kill_line (rp_input_line *line);
+
+/* default edit action */
+edit_status editor_insert (rp_input_line *line, char *keysym_buf);
+
+
+static char *saved_command = NULL;
+
+typedef struct edit_binding edit_binding;
+
+struct edit_binding
+{
+ struct rp_key key;
+ edit_status (*func)(rp_input_line *);
+};
+
+static edit_binding edit_bindings[] =
+ { {{XK_g, RP_CONTROL_MASK}, editor_abort},
+ {{XK_Escape, 0}, editor_abort},
+ {{XK_f, RP_CONTROL_MASK}, editor_forward_char},
+ {{XK_Right, 0}, editor_forward_char},
+ {{XK_b, RP_CONTROL_MASK}, editor_backward_char},
+ {{XK_Left, 0}, editor_backward_char},
+ {{XK_f, RP_META_MASK}, editor_forward_word},
+ {{XK_b, RP_META_MASK}, editor_backward_word},
+ {{XK_a, RP_CONTROL_MASK}, editor_beginning_of_line},
+ {{XK_Home, 0}, editor_beginning_of_line},
+ {{XK_e, RP_CONTROL_MASK}, editor_end_of_line},
+ {{XK_End, 0}, editor_end_of_line},
+ {{XK_d, RP_CONTROL_MASK}, editor_delete_char},
+ {{XK_Delete, 0}, editor_delete_char},
+ {{XK_BackSpace, 0}, editor_backward_delete_char},
+ {{XK_BackSpace, RP_META_MASK}, editor_backward_kill_word},
+ {{XK_d, RP_META_MASK}, editor_kill_word},
+ {{XK_k, RP_CONTROL_MASK}, editor_kill_line},
+ {{XK_u, RP_CONTROL_MASK}, editor_backward_kill_line},
+ {{XK_y, RP_CONTROL_MASK}, editor_paste_selection},
+ {{XK_Insert, 0}, editor_paste_selection},
+ {{XK_p, RP_CONTROL_MASK}, editor_history_previous},
+ {{XK_Up, 0}, editor_history_previous},
+ {{XK_n, RP_CONTROL_MASK}, editor_history_next},
+ {{XK_Down, 0}, editor_history_next},
+ {{XK_Return, 0}, editor_enter},
+ {{XK_KP_Enter, 0}, editor_enter},
+ {{XK_Tab, 0}, editor_complete},
+ { {0, 0}, 0} };
+
+rp_input_line *
+input_line_new (char *prompt, char *preinput, completion_fn fn)
+{
+ rp_input_line *line;
+
+ line = xmalloc (sizeof (rp_input_line));
+ line->prompt = prompt;
+ line->compl = completions_new (fn);
+
+ /* Allocate some memory to start with */
+ line->size = strlen (preinput) + 100;
+ line->buffer = (char *) xmalloc (line->size);
+
+ /* load in the preinput */
+ strcpy (line->buffer, preinput);
+ line->position = line->length = strlen (preinput);
+
+ return line;
+}
+
+void
+input_line_free (rp_input_line *line)
+{
+/* completions_free (line->compl); */
+ free (line->buffer);
+ free (line);
+}
+
+edit_status
+execute_edit_action (rp_input_line *line, KeySym ch, unsigned int modifier, char *keysym_buf)
+{
+ struct edit_binding *binding = NULL;
+ int found_binding = 0;
+ edit_status status;
+
+ for (binding = edit_bindings; binding->func; binding++)
+ {
+ if (ch == binding->key.sym && modifier == binding->key.state)
+ {
+ found_binding = 1;
+ break;
+ }
+ }
+
+ if (found_binding)
+ status = binding->func (line);
+ else if (modifier)
+ status = editor_no_action (line);
+ else
+ status = editor_insert (line, keysym_buf);
+
+ return status;
+}
+
+static edit_status
+editor_forward_char (rp_input_line *line)
+{
+ if (line->position < line->length)
+ {
+ line->position++;
+ return EDIT_MOVE;
+ }
+ else
+ return EDIT_NO_OP;
+
+}
+
+static edit_status
+editor_backward_char (rp_input_line *line)
+{
+ if (line->position > 0)
+ {
+ line->position--;
+ return EDIT_MOVE;
+ }
+ else
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_forward_word (rp_input_line *line)
+{
+ if (line->position < line->length)
+ {
+ for (; line->position < line->length && !isalnum (line->buffer[line->position]); line->position++);
+ for (; line->position < line->length && isalnum (line->buffer[line->position]); line->position++);
+ }
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_backward_word (rp_input_line *line)
+{
+ if (line->position > 0)
+ {
+ for (; line->position > 0 && !isalnum (line->buffer[line->position - 1]); line->position--);
+ for (; line->position > 0 && isalnum (line->buffer[line->position - 1]); line->position--);
+ }
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_beginning_of_line (rp_input_line *line)
+{
+ if (line->position > 0)
+ line->position = 0;
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_end_of_line (rp_input_line *line)
+{
+ if (line->position < line->length)
+ line->position = line->length;
+
+ return EDIT_MOVE;
+}
+
+static edit_status
+editor_delete_char (rp_input_line *line)
+{
+ int i;
+
+ if (line->position < line->length)
+ {
+ for (i = line->position; i < line->length; i++)
+ line->buffer[i] = line->buffer[i + 1];
+
+ line->length--;
+ return EDIT_DELETE;
+ }
+ else
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_backward_delete_char (rp_input_line *line)
+{
+ int i;
+
+ if (line->position > 0)
+ {
+ for (i = line->position - 1; i < line->length; i++)
+ line->buffer[i] = line->buffer[i + 1];
+
+ line->position--;
+ line->length--;
+ return EDIT_DELETE;
+ }
+ else
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_kill_word (rp_input_line *line)
+{
+ int i, diff;
+
+ if (line->position < line->length)
+ {
+ for (i = line->position; i < line->length && !isalnum (line->buffer[i]); i++);
+ for (; i < line->length && isalnum (line->buffer[i]); i++);
+
+ diff = i - line->position;
+
+ for (i = line->position; i <= line->length - diff; i++)
+ line->buffer[i] = line->buffer[i + diff];
+
+ line->length -= diff;
+ }
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_backward_kill_word (rp_input_line *line)
+{
+ int i, diff;
+
+ if (line->position > 0)
+ {
+ for (i = line->position; i > 0 && !isalnum (line->buffer[i - 1]); i--);
+ for (; i > 0 && isalnum (line->buffer[i - 1]); i--);
+
+ diff = line->position - i;
+
+ line->position = i;
+
+ for (; i <= line->length - diff; i++)
+ line->buffer[i] = line->buffer[i + diff];
+
+ line->length -= diff;
+ }
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_kill_line (rp_input_line *line)
+{
+ if (line->position < line->length)
+ {
+ line->length = line->position;
+ line->buffer[line->length] = 0;
+ }
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_backward_kill_line (rp_input_line *line)
+{
+ int i;
+
+ if (line->position <= 0)
+ return EDIT_NO_OP;
+
+ for (i = line->position; i<= line->length; i++)
+ line->buffer[i - line->position] = line->buffer[i];
+
+ line->length -= line->position;
+ line->position = 0;
+
+ return EDIT_DELETE;
+}
+
+static edit_status
+editor_history_previous (rp_input_line *line)
+{
+ char *entry = history_previous ();
+
+ if (entry)
+ {
+ if (!saved_command)
+ {
+ line->buffer[line->length] = '\0';
+ saved_command = xstrdup (line->buffer);
+ PRINT_DEBUG (("saved current command line: \'%s\'\n", saved_command));
+ }
+
+ free (line->buffer);
+ line->buffer = xstrdup (entry);
+ line->length = strlen (line->buffer);
+ line->size = line->length + 1;
+ line->position = line->length;
+ PRINT_DEBUG (("entry: \'%s\'\n", line->buffer));
+ }
+ else
+ {
+ PRINT_DEBUG (("- do nothing -"));
+ return EDIT_NO_OP;
+ }
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_history_next (rp_input_line *line)
+{
+ char *entry = history_next ();
+
+ if (entry)
+ {
+ free (line->buffer);
+ line->buffer = xstrdup (entry);
+ PRINT_DEBUG (("entry: \'%s\'\n", line->buffer));
+ }
+ else if (saved_command)
+ {
+ free (line->buffer);
+ line->buffer = saved_command;
+ saved_command = NULL;
+ PRINT_DEBUG (("restored command line: \'%s\'\n", line->buffer));
+ }
+ else
+ {
+ PRINT_DEBUG (("- do nothing -"));
+ return EDIT_NO_OP;
+ }
+
+ line->length = strlen (line->buffer);
+ line->size = line->length + 1;
+ line->position = line->length;
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_abort (rp_input_line *line)
+{
+ return EDIT_ABORT;
+}
+
+static edit_status
+editor_no_action (rp_input_line *line)
+{
+ return EDIT_NO_OP;
+}
+
+static edit_status
+editor_insert (rp_input_line *line, char *keysym_buf)
+{
+ int nbytes;
+ int i;
+
+ PRINT_DEBUG (("keysym_buf: '%s'\n", keysym_buf));
+
+ nbytes = strlen (keysym_buf);
+ if (line->length + nbytes > line->size - 1)
+ {
+ line->size += nbytes + 100;
+ line->buffer = xrealloc (line->buffer, line->size);
+ }
+
+ for (i = line->length + nbytes; i > line->position; i--)
+ line->buffer[i] = line->buffer[i - nbytes];
+
+ strncpy (&line->buffer[line->position], keysym_buf, nbytes);
+
+ line->length += nbytes;
+ line->position += nbytes;
+
+ PRINT_DEBUG (("line->buffer: '%s'\n", line->buffer));
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_enter (rp_input_line *line)
+{
+ int result;
+ char *expansion;
+
+ line->buffer[line->length] = '\0';
+ result = history_expand_line (line->buffer, &expansion);
+
+ PRINT_DEBUG (("History Expansion - result: %d\n", result));
+ PRINT_DEBUG (("History Expansion - expansion: \'%s\'\n", expansion));
+
+ if (result == -1 || result == 2)
+ {
+ marked_message_printf (0, 0, " %s ", expansion);
+ free (expansion);
+ line->buffer = NULL;
+ }
+ else /* result == 0 || result == 1 */
+ {
+ history_add (expansion);
+ line->buffer = expansion;
+ }
+
+ return EDIT_DONE;
+}
+
+static edit_status
+paste_cut_buffer (rp_input_line *line)
+{
+ int nbytes;
+ char *data;
+ edit_status status;
+
+ PRINT_DEBUG (("trying the cut buffer\n"));
+
+ data = XFetchBytes (dpy, &nbytes);
+
+ if (data)
+ {
+/* status = editor_insert (line, data, nbytes); */
+ status = editor_insert (line, data);
+ XFree (data);
+ }
+ else
+ {
+ status = EDIT_NO_OP;
+ }
+
+ return status;
+}
+
+static edit_status
+paste_primary_selection (rp_input_line *line)
+{
+ Atom actual_type;
+ rp_screen *s = current_screen ();
+ int actual_format;
+ unsigned long nitems;
+ unsigned long offset;
+ unsigned long bytes_after;
+ unsigned char *data;
+
+ if (XGetWindowProperty (dpy, s->input_window, rp_selection, 0, 0, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data) == Success)
+ {
+ if (data)
+ {
+ XFree (data);
+ data = NULL;
+ }
+
+ PRINT_DEBUG (("actual_type = %ld, actual_format = %d, bytes_after = %ld\n", actual_type, actual_format, bytes_after));
+
+ if (actual_type != XA_STRING || actual_format != 8)
+ {
+ PRINT_DEBUG (("selection data is invalid\n"));
+ if (data)
+ XFree (data);
+ return EDIT_NO_OP;
+ }
+
+ offset = 0;
+
+ while (bytes_after > 0)
+ {
+ if (XGetWindowProperty (dpy, s->input_window, rp_selection, offset / 4, bytes_after / 4 + 1, False, XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data) != Success)
+ break;
+
+ PRINT_DEBUG (("bytes_after = %ld, nitems = %ld, data = '%s'\n", bytes_after, nitems, data));
+
+ nitems *= actual_format / 8;
+ offset += nitems;
+
+/* editor_insert (line, data, nitems); */
+ editor_insert (line, data);
+
+ PRINT_DEBUG (("bytes_after = %ld, nitems = %ld, data = '%s'\n", bytes_after, nitems, data));
+
+ XFree (data);
+ }
+ }
+
+ /* notify the owner that the data has been transferred */
+ XDeleteProperty(dpy, s->input_window, rp_selection);
+
+ return EDIT_INSERT;
+}
+
+static edit_status
+editor_paste_selection (rp_input_line *line)
+{
+ Atom property;
+ XEvent ev;
+ rp_screen *s = current_screen ();
+ int loops = 1000;
+
+ /* be a good icccm citizen */
+ XDeleteProperty (dpy, s->input_window, rp_selection);
+ /* TODO: we shouldn't use CurrentTime here, use the time of the XKeyEvent, should we fake it? */
+ XConvertSelection (dpy, XA_PRIMARY, XA_STRING, rp_selection, s->input_window, CurrentTime);
+
+ while (!XCheckTypedWindowEvent (dpy, s->input_window, SelectionNotify, &ev))
+ {
+ if (loops == 0)
+ {
+ PRINT_ERROR (("selection request timed out\n"));
+ return EDIT_NO_OP;
+ }
+ usleep (10000);
+ loops--;
+ }
+
+ PRINT_DEBUG (("SelectionNotify event\n"));
+
+ property = ev.xselection.property;
+
+ if (property != None)
+ return paste_primary_selection (line);
+ else
+ return paste_cut_buffer (line);
+}
+
+static edit_status
+editor_complete (rp_input_line *line)
+{
+ char *tmp;
+ char *s;
+
+ /* Create our partial string that will be used for completion. It is
+ the characters up to the position of the cursor. */
+ tmp = xmalloc ((line->position + 1) * sizeof (char));
+ strncpy (tmp, line->buffer, line->position);
+ tmp[line->position] = '\0';
+
+ /* We don't need to free s because it's a string from the completion
+ list. */
+ s = completions_next_completion (line->compl, tmp);
+ free (tmp);
+
+ if (s == NULL)
+ return EDIT_NO_OP;
+
+ /* Insert the completion. */
+ editor_backward_kill_line (line);
+ editor_insert (line, s);
+
+ return EDIT_COMPLETE;
+}