2 * Copyright (c) 2012 Elias Norberg <xyzzy@kudzu.se>
3 * Copyright (c) 2012 Josh Rickmar <jrick@devio.us>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #if WEBKIT_CHECK_VERSION(1, 5, 0)
21 /* we got the DOM API we need */
23 struct edit_src_cb_args
{
24 WebKitWebFrame
*frame
;
25 WebKitWebDataSource
*data_src
;
28 struct external_editor_args
{
33 int (*callback
)(const char *,gpointer
);
38 update_contents(struct external_editor_args
*args
)
43 GString
*contents
= NULL
;
44 char buf
[XT_EE_BUFSZ
];
46 rv
= stat(args
->path
, &st
);
47 if (rv
== -1 && errno
== ENOENT
)
49 else if (rv
== 0 && st
.st_mtime
> args
->mtime
) {
50 DPRINTF("File %s has been modified\n", args
->path
);
51 args
->mtime
= st
.st_mtime
;
53 contents
= g_string_sized_new(XT_EE_BUFSZ
);
54 fd
= open(args
->path
, O_RDONLY
);
56 DPRINTF("open_external_editor_cb, open error, %s\n",
62 nb
= read(fd
, buf
, XT_EE_BUFSZ
);
64 g_string_free(contents
, TRUE
);
65 show_oops(args
->tab
, strerror(errno
));
70 contents
= g_string_append(contents
, buf
);
77 DPRINTF("external_editor_cb: contents updated\n");
79 args
->callback(contents
->str
, args
->cb_data
);
81 g_string_free(contents
, TRUE
);
93 external_editor_closed(GPid pid
, gint status
, gpointer data
)
95 struct external_editor_args
*args
;
99 args
= (struct external_editor_args
*)data
;
101 TAILQ_FOREACH(t
, &tabs
, entry
)
102 if (t
== args
->tab
) {
107 /* Tab was deleted */
112 * unfortunately we can't check the exit status in glib < 2.34,
113 * otherwise a check and warning would be nice here
115 update_contents(args
);
119 g_spawn_close_pid(pid
);
123 open_external_editor_cb(gpointer data
)
125 struct external_editor_args
*args
;
129 args
= (struct external_editor_args
*)data
;
131 /* Check if tab is still open */
132 TAILQ_FOREACH(t
, &tabs
, entry
)
133 if (t
== args
->tab
) {
138 /* Tab was deleted */
142 if (update_contents(args
))
147 /* cleanup and remove from event loop */
149 g_free(args
->cb_data
);
156 open_external_editor(struct tab
*t
, const char *contents
,
157 int (*callback
)(const char *, gpointer
), gpointer cb_data
)
160 struct external_editor_args
*a
;
163 char filename
[PATH_MAX
];
169 if (external_editor
== NULL
)
172 snprintf(filename
, sizeof filename
, "%s" PS
"xombreroXXXXXX", temp_dir
);
174 /* Create a temporary file */
175 fd
= g_mkstemp(filename
);
177 show_oops(t
, "Cannot create temporary file");
182 while (contents
&& nb
< strlen(contents
)) {
183 if (strlen(contents
) - nb
> XT_EE_BUFSZ
)
186 cnt
= strlen(contents
) - nb
;
188 rv
= write(fd
, contents
+nb
, cnt
);
191 show_oops(t
,strerror(errno
));
201 show_oops(t
,"Cannot stat file: %s\n", strerror(errno
));
206 DPRINTF("edit_src: external_editor: %s\n", external_editor
);
208 sv
= g_strsplit(external_editor
, "<file>", -1);
209 cmdstr
= g_strjoinv(filename
, sv
);
211 sv
= g_strsplit_set(cmdstr
, " \t", -1);
212 if (!g_spawn_async(NULL
, sv
, NULL
,
213 (G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
), NULL
, NULL
, &pid
,
215 show_oops(t
, "%s: could not spawn process");
224 a
= g_malloc(sizeof(struct external_editor_args
));
226 a
->path
= g_strdup(filename
);
228 a
->mtime
= st
.st_mtime
;
229 a
->callback
= callback
;
230 a
->cb_data
= cb_data
;
232 /* Check every 100 ms if file has changed */
233 g_timeout_add(100, (GSourceFunc
)open_external_editor_cb
,
236 /* Stop loop child has terminated */
237 g_child_watch_add(pid
, external_editor_closed
, (gpointer
)a
);
243 edit_src_cb(const char *contents
, gpointer data
)
245 struct edit_src_cb_args
*args
;
247 args
= (struct edit_src_cb_args
*)data
;
249 webkit_web_frame_load_string(args
->frame
, contents
, NULL
,
250 webkit_web_data_source_get_encoding(args
->data_src
),
251 webkit_web_frame_get_uri(args
->frame
));
256 edit_src(struct tab
*t
, struct karg
*args
)
258 WebKitWebFrame
*frame
;
259 WebKitWebDataSource
*ds
;
261 struct edit_src_cb_args
*ext_args
;
263 if (external_editor
== NULL
){
264 show_oops(t
,"Setting external_editor not set");
268 frame
= webkit_web_view_get_focused_frame(t
->wv
);
269 ds
= webkit_web_frame_get_data_source(frame
);
270 if (webkit_web_data_source_is_loading(ds
)) {
271 show_oops(t
,"Webpage is still loading.");
275 contents
= webkit_web_data_source_get_data(ds
);
277 show_oops(t
,"No contents - opening empty file");
279 ext_args
= g_malloc(sizeof(struct edit_src_cb_args
));
280 ext_args
->frame
= frame
;
281 ext_args
->data_src
= ds
;
283 /* Check every 100 ms if file has changed */
284 open_external_editor(t
, contents
? contents
->str
: "", &edit_src_cb
,
289 struct edit_element_cb_args
{
290 WebKitDOMElement
*active
;
295 edit_element_cb(const char *contents
, gpointer data
)
297 struct edit_element_cb_args
*args
;
298 WebKitDOMHTMLTextAreaElement
*ta
;
299 WebKitDOMHTMLInputElement
*el
;
301 args
= (struct edit_element_cb_args
*)data
;
303 if (!args
|| !args
->active
)
306 el
= (WebKitDOMHTMLInputElement
*)args
->active
;
307 ta
= (WebKitDOMHTMLTextAreaElement
*)args
->active
;
309 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el
))
310 webkit_dom_html_input_element_set_value(el
, contents
);
311 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta
))
312 webkit_dom_html_text_area_element_set_value(ta
, contents
);
318 edit_element(struct tab
*t
, struct karg
*a
)
320 WebKitDOMHTMLDocument
*doc
;
321 WebKitDOMElement
*active_element
;
322 WebKitDOMHTMLTextAreaElement
*ta
;
323 WebKitDOMHTMLInputElement
*el
;
325 struct edit_element_cb_args
*args
;
327 if (external_editor
== NULL
){
328 show_oops(t
,"Setting external_editor not set");
332 doc
= (WebKitDOMHTMLDocument
*)webkit_web_view_get_dom_document(t
->wv
);
333 active_element
= webkit_dom_html_document_get_active_element(doc
);
334 el
= (WebKitDOMHTMLInputElement
*)active_element
;
335 ta
= (WebKitDOMHTMLTextAreaElement
*)active_element
;
337 if (doc
== NULL
|| active_element
== NULL
||
338 (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el
) == 0 &&
339 WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta
) == 0)) {
340 show_oops(t
, "No active text element!");
345 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el
))
346 contents
= webkit_dom_html_input_element_get_value(el
);
347 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta
))
348 contents
= webkit_dom_html_text_area_element_get_value(ta
);
350 if ((args
= g_malloc(sizeof(struct edit_element_cb_args
))) == NULL
)
354 args
->active
= active_element
;
356 open_external_editor(t
, contents
, &edit_element_cb
, args
);
361 #else /* Just to make things compile. */
364 edit_element(struct tab
*t
, struct karg
*a
)
366 show_oops(t
, "external editor feature requires webkit >= 1.5.0");
371 edit_src(struct tab
*t
, struct karg
*args
)
373 show_oops(t
, "external editor feature requires webkit >= 1.5.0");