From: Daniel Carl Date: Sun, 14 Apr 2013 18:01:20 +0000 (+0200) Subject: Added command to open input boxes or text areas with editor (#15). X-Git-Url: https://git.owens.tech/assets/static/git.owens.tech/assets/static/git.owens.tech/git?a=commitdiff_plain;h=07c0a84ec428f6e47226d74db4869d1f0b8b086f;p=vimb.git Added command to open input boxes or text areas with editor (#15). --- diff --git a/doc/vimb.1.txt b/doc/vimb.1.txt index 4c1ed75..caf0001 100644 --- a/doc/vimb.1.txt +++ b/doc/vimb.1.txt @@ -529,6 +529,12 @@ Search for \fIN\fPnth next search result. .TP .BI [ N ]N Search for \fIN\fPnth previous search result. +.SS INSERT_MODE +.TP +.B ctrl-t +If the current active form element is an inputbox or textarea, the content is +copied to temporary file and the file openen with the configured external +editor (setting `editor-command'). .SH FILES .I $XDG_CONFIG_HOME/PROJECT/config .RS diff --git a/src/command.c b/src/command.c index cdfb389..48cf36c 100644 --- a/src/command.c +++ b/src/command.c @@ -29,6 +29,13 @@ #include "bookmark.h" #include "dom.h" +typedef struct { + char *file; + Element *element; +} OpenEditorData; + +static void editor_resume(GPid pid, int status, OpenEditorData *data); + extern VbCore vb; extern const unsigned int INPUT_LENGTH; @@ -102,6 +109,7 @@ static CommandInfo cmd_list[] = { {"run", command_run_multi, {0}}, {"bookmark-add", command_bookmark, {1}}, {"eval", command_eval, {0}}, + {"editor", command_editor, {0}}, }; @@ -628,3 +636,82 @@ gboolean command_eval(const Arg *arg) return success; } + +gboolean command_editor(const Arg *arg) +{ + char *file_path = NULL; + const char *text; + char **argv; + int argc; + GPid pid; + gboolean success; + + if (!vb.config.editor_command) { + vb_echo(VB_MSG_ERROR, true, "No editor-command configured"); + return false; + } + Element* active = dom_get_active_element(vb.gui.webview); + + /* check if element is suitable for editing */ + if (!active || !dom_is_editable(active)) { + return false; + } + + text = dom_editable_element_get_value(active); + if (!text) { + return false; + } + + if (!util_create_tmp_file(text, &file_path)) { + return false; + } + + /* spawn editor */ + char* command = g_strdup_printf(vb.config.editor_command, file_path); + if (!g_shell_parse_argv(command, &argc, &argv, NULL)) { + fprintf(stderr, "Could not parse editor-command"); + g_free(command); + return false; + } + g_free(command); + + success = g_spawn_async( + NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, + NULL, NULL, &pid, NULL + ); + g_strfreev(argv); + + if (!success) { + unlink(file_path); + g_free(file_path); + return false; + } + + /* disable the active element */ + dom_editable_element_set_disable(active, true); + + OpenEditorData *data = g_new0(OpenEditorData, 1); + data->file = file_path; + data->element = active; + + g_child_watch_add(pid, (GChildWatchFunc)editor_resume, data); + + return true; +} + +static void editor_resume(GPid pid, int status, OpenEditorData *data) +{ + char *text; + if (status == 0) { + text = util_get_file_contents(data->file, NULL); + if (text) { + dom_editable_element_set_value(data->element, text); + } + g_free(text); + } + dom_editable_element_set_disable(data->element, false); + + g_free(data->file); + g_free(data); + g_spawn_close_pid(pid); +} diff --git a/src/command.h b/src/command.h index 8b81d17..a526e03 100644 --- a/src/command.h +++ b/src/command.h @@ -75,5 +75,6 @@ gboolean command_zoom(const Arg *arg); gboolean command_history(const Arg *arg); gboolean command_bookmark(const Arg *arg); gboolean command_eval(const Arg *arg); +gboolean command_editor(const Arg *arg); #endif /* end of include guard: _COMMAND_H */ diff --git a/src/config.h b/src/config.h index d6e0e16..325cf2c 100644 --- a/src/config.h +++ b/src/config.h @@ -92,6 +92,7 @@ const char *default_config[] = { "cmap =hist-next", "hmap =hint-focus-next", "hmap =hint-focus-prev", + "imap =editor", "searchengine-add dl=https://duckduckgo.com/lite/?q=%s", "searchengine-add dd=https://duckduckgo.com/?q=%s", "searchengine-default dl", @@ -148,6 +149,7 @@ const char *default_config[] = { "set home-page=https://github.com/fanglingsu/vimb", "set download-path=/tmp/vimb", "set history-max-items=2000", + "set editor-command=x-terminal-emulator -e vi %s", NULL }; diff --git a/src/dom.c b/src/dom.c index 9ded553..3c49b30 100644 --- a/src/dom.c +++ b/src/dom.c @@ -93,6 +93,39 @@ Element *dom_get_active_element(WebKitWebView *view) return get_active_element(webkit_web_view_get_dom_document(view)); } +const char *dom_editable_element_get_value(Element *element) +{ + const char *value = NULL; + + if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT((HtmlInputElement*)element)) { + value = webkit_dom_html_input_element_get_value((HtmlInputElement*)element); + } else { + /* we should check WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT, but this + * seems to return alway false */ + value = webkit_dom_html_text_area_element_get_value((HtmlTextareaElement*)element); + } + + return value; +} + +void dom_editable_element_set_value(Element *element, const char *value) +{ + if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(element)) { + webkit_dom_html_input_element_set_value((HtmlInputElement*)element, value); + } else { + webkit_dom_html_text_area_element_set_value((HtmlTextareaElement*)element, value); + } +} + +void dom_editable_element_set_disable(Element *element, gboolean value) +{ + if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(element)) { + webkit_dom_html_input_element_set_disabled((HtmlInputElement*)element, value); + } else { + webkit_dom_html_text_area_element_set_disabled((HtmlTextareaElement*)element, value); + } +} + static gboolean auto_insert(Element *element) { if (dom_is_editable(element)) { diff --git a/src/dom.h b/src/dom.h index 18f9388..0c99b45 100644 --- a/src/dom.h +++ b/src/dom.h @@ -23,11 +23,14 @@ #include // Types -#define Document WebKitDOMDocument -#define HtmlElement WebKitDOMHTMLElement -#define Element WebKitDOMElement -#define Event WebKitDOMEvent -#define EventTarget WebKitDOMEventTarget +#define Document WebKitDOMDocument +#define HtmlElement WebKitDOMHTMLElement +#define Element WebKitDOMElement +#define Node WebKitDOMNode +#define Event WebKitDOMEvent +#define EventTarget WebKitDOMEventTarget +#define HtmlInputElement WebKitDOMHTMLInputElement +#define HtmlTextareaElement WebKitDOMHTMLTextAreaElement // style #define style_compare_property(style, name, value) (!strcmp(webkit_dom_css_style_declaration_get_property_value(style, name), value)) @@ -43,5 +46,8 @@ void dom_check_auto_insert(WebKitWebView *view); void dom_clear_focus(WebKitWebView *view); gboolean dom_is_editable(Element *element); Element *dom_get_active_element(WebKitWebView *view); +const char *dom_editable_element_get_value(Element *element); +void dom_editable_element_set_value(Element *element, const char *value); +void dom_editable_element_set_disable(Element *element, gboolean value); #endif /* end of include guard: _DOM_H */ diff --git a/src/main.h b/src/main.h index 97f400e..7a644f1 100644 --- a/src/main.h +++ b/src/main.h @@ -257,6 +257,7 @@ typedef struct { char *home_page; char *download_dir; guint history_max; + char *editor_command; } Config; typedef struct { diff --git a/src/searchengine.c b/src/searchengine.c index e693df6..0724dd6 100644 --- a/src/searchengine.c +++ b/src/searchengine.c @@ -19,6 +19,7 @@ #include "main.h" #include "searchengine.h" +#include "util.h" extern VbCore vb; @@ -31,7 +32,6 @@ static GSList *searchengines; static char *default_handle = NULL; static GSList *find(const char *handle); -static gboolean is_valid_uri(const char *uri); static void free_searchengine(Searchengine *se); @@ -45,7 +45,7 @@ void searchengine_cleanup(void) gboolean searchengine_add(const char *handle, const char *uri) { /* validate if the uri contains only one %s sequence */ - if (!is_valid_uri(uri)) { + if (!util_valid_format_string(uri, 's', 1)) { return false; } Searchengine *s = g_new0(Searchengine, 1); @@ -128,22 +128,6 @@ static GSList *find(const char *handle) return NULL; } -static gboolean is_valid_uri(const char *uri) -{ - int count = 0; - - for (; *uri; uri++) { - if (*uri == '%') { - uri++; - if (*uri == 's') { - count++; - } - } - } - - return count == 1; -} - static void free_searchengine(Searchengine *se) { g_free(se->uri); diff --git a/src/setting.c b/src/setting.c index 8ed1886..671470d 100644 --- a/src/setting.c +++ b/src/setting.c @@ -42,6 +42,7 @@ static gboolean download_path(const Setting *s, const SettingType type); static gboolean proxy(const Setting *s, const SettingType type); static gboolean user_style(const Setting *s, const SettingType type); static gboolean history_max_items(const Setting *s, const SettingType type); +static gboolean editor_command(const Setting *s, const SettingType type); static Setting default_settings[] = { /* webkit settings */ @@ -103,6 +104,7 @@ static Setting default_settings[] = { {NULL, "home-page", TYPE_CHAR, home_page, {0}}, {NULL, "download-path", TYPE_CHAR, download_path, {0}}, {NULL, "history-max-items", TYPE_INTEGER, history_max_items, {0}}, + {NULL, "editor-command", TYPE_CHAR, editor_command, {0}}, }; void setting_init(void) @@ -691,3 +693,19 @@ static gboolean history_max_items(const Setting *s, const SettingType type) return true; } + +static gboolean editor_command(const Setting *s, const SettingType type) +{ + if (type == SETTING_GET) { + print_value(s, vb.config.editor_command); + + return true; + } + + if (!util_valid_format_string(s->arg.s, 's', 1)) { + return false; + } + OVERWRITE_STRING(vb.config.editor_command, s->arg.s); + + return true; +} diff --git a/src/util.c b/src/util.c index ca09c9a..ba5af22 100644 --- a/src/util.c +++ b/src/util.c @@ -119,3 +119,58 @@ next: } return NULL; } + +/** + * Checks if given printf style format string is valid. That means that it + * contains only one defined placeholder given as char. + */ +gboolean util_valid_format_string(const char *format, char type, unsigned int count) +{ + unsigned int c; + for (c = 0; *format; format++) { + if (*format == '%') { + format++; + if (*format == type) { + c++; + } + } + } + + return c == count; +} + +/** + * Creates a temporary file with given content. + * + * Upon success, and if file is non-NULL, the actual file path used is + * returned in file. This string should be freed with g_free() when not + * needed any longer. + */ +gboolean util_create_tmp_file(const char *content, char **file) +{ + int fp; + ssize_t bytes, len; + + fp = g_file_open_tmp(PROJECT "-XXXXXX", file, NULL); + if (fp == -1) { + fprintf(stderr, "Could not create temporary file %s", *file); + g_free(*file); + return false; + } + + len = strlen(content); + + /* write content into temporary file */ + bytes = write(fp, content, len); + if (bytes < len) { + close(fp); + unlink(*file); + fprintf(stderr, "Could not write temporary file %s", *file); + g_free(*file); + + return false; + } + close(fp); + + return true; +} diff --git a/src/util.h b/src/util.h index 5a52031..8255b30 100644 --- a/src/util.h +++ b/src/util.h @@ -30,5 +30,7 @@ void util_create_file_if_not_exists(const char* filename); char* util_get_file_contents(const char* filename, gsize* length); char** util_get_lines(const char* filename); char* util_strcasestr(const char* haystack, const char* needle); +gboolean util_valid_format_string(const char *format, char type, unsigned int count); +gboolean util_create_tmp_file(const char *content, char **file); #endif /* end of include guard: _UTIL_H */