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
);
78 DPRINTF("external_editor_cb: contents updated\n");
80 args
->callback(contents
->str
, args
->cb_data
);
82 g_string_free(contents
, TRUE
);
94 external_editor_closed(GPid pid
, gint status
, gpointer data
)
96 struct external_editor_args
*args
;
100 args
= (struct external_editor_args
*)data
;
102 TAILQ_FOREACH(t
, &tabs
, entry
)
103 if (t
== args
->tab
) {
108 /* Tab was deleted */
113 * unfortunately we can't check the exit status in glib < 2.34,
114 * otherwise a check and warning would be nice here
116 update_contents(args
);
120 g_spawn_close_pid(pid
);
124 open_external_editor_cb(gpointer data
)
126 struct external_editor_args
*args
;
130 args
= (struct external_editor_args
*)data
;
132 /* Check if tab is still open */
133 TAILQ_FOREACH(t
, &tabs
, entry
)
134 if (t
== args
->tab
) {
139 /* Tab was deleted */
143 if (update_contents(args
))
148 /* cleanup and remove from event loop */
150 g_free(args
->cb_data
);
157 open_external_editor(struct tab
*t
, const char *contents
,
158 int (*callback
)(const char *, gpointer
), gpointer cb_data
)
161 struct external_editor_args
*a
;
164 char filename
[PATH_MAX
];
170 if (external_editor
== NULL
)
173 snprintf(filename
, sizeof filename
, "%s" PS
"xombreroXXXXXX", temp_dir
);
175 /* Create a temporary file */
176 fd
= g_mkstemp(filename
);
178 show_oops(t
, "Cannot create temporary file");
183 while (contents
&& nb
< strlen(contents
)) {
184 if (strlen(contents
) - nb
> XT_EE_BUFSZ
)
187 cnt
= strlen(contents
) - nb
;
189 rv
= write(fd
, contents
+nb
, cnt
);
192 show_oops(t
,strerror(errno
));
202 show_oops(t
,"Cannot stat file: %s\n", strerror(errno
));
207 DPRINTF("edit_src: external_editor: %s\n", external_editor
);
209 sv
= g_strsplit(external_editor
, "<file>", -1);
210 cmdstr
= g_strjoinv(filename
, sv
);
212 sv
= g_strsplit_set(cmdstr
, " \t", -1);
213 if (!g_spawn_async(NULL
, sv
, NULL
,
214 (G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
), NULL
, NULL
, &pid
,
216 show_oops(t
, "%s: could not spawn process");
225 a
= g_malloc(sizeof(struct external_editor_args
));
227 a
->path
= g_strdup(filename
);
229 a
->mtime
= st
.st_mtime
;
230 a
->callback
= callback
;
231 a
->cb_data
= cb_data
;
233 /* Check every 100 ms if file has changed */
234 g_timeout_add(100, (GSourceFunc
)open_external_editor_cb
,
237 /* Stop loop child has terminated */
238 g_child_watch_add(pid
, external_editor_closed
, (gpointer
)a
);
244 edit_src_cb(const char *contents
, gpointer data
)
246 struct edit_src_cb_args
*args
;
248 args
= (struct edit_src_cb_args
*)data
;
250 webkit_web_frame_load_string(args
->frame
, contents
, NULL
,
251 webkit_web_data_source_get_encoding(args
->data_src
),
252 webkit_web_frame_get_uri(args
->frame
));
257 edit_src(struct tab
*t
, struct karg
*args
)
259 WebKitWebFrame
*frame
;
260 WebKitWebDataSource
*ds
;
262 struct edit_src_cb_args
*ext_args
;
264 if (external_editor
== NULL
){
265 show_oops(t
,"Setting external_editor not set");
269 frame
= webkit_web_view_get_focused_frame(t
->wv
);
270 ds
= webkit_web_frame_get_data_source(frame
);
271 if (webkit_web_data_source_is_loading(ds
)) {
272 show_oops(t
,"Webpage is still loading.");
276 contents
= webkit_web_data_source_get_data(ds
);
278 show_oops(t
,"No contents - opening empty file");
280 ext_args
= g_malloc(sizeof(struct edit_src_cb_args
));
281 ext_args
->frame
= frame
;
282 ext_args
->data_src
= ds
;
284 /* Check every 100 ms if file has changed */
285 open_external_editor(t
, contents
? contents
->str
: "", &edit_src_cb
,
290 struct edit_element_cb_args
{
291 WebKitDOMElement
*active
;
296 edit_element_cb(const char *contents
, gpointer data
)
298 struct edit_element_cb_args
*args
;
299 WebKitDOMHTMLTextAreaElement
*ta
;
300 WebKitDOMHTMLInputElement
*el
;
302 args
= (struct edit_element_cb_args
*)data
;
304 if (!args
|| !args
->active
)
307 el
= (WebKitDOMHTMLInputElement
*)args
->active
;
308 ta
= (WebKitDOMHTMLTextAreaElement
*)args
->active
;
310 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el
))
311 webkit_dom_html_input_element_set_value(el
, contents
);
312 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta
))
313 webkit_dom_html_text_area_element_set_value(ta
, contents
);
319 edit_element(struct tab
*t
, struct karg
*a
)
321 WebKitDOMHTMLDocument
*doc
;
322 WebKitDOMElement
*active_element
;
323 WebKitDOMHTMLTextAreaElement
*ta
;
324 WebKitDOMHTMLInputElement
*el
;
326 struct edit_element_cb_args
*args
;
328 if (external_editor
== NULL
){
329 show_oops(t
,"Setting external_editor not set");
333 doc
= (WebKitDOMHTMLDocument
*)webkit_web_view_get_dom_document(t
->wv
);
334 active_element
= webkit_dom_html_document_get_active_element(doc
);
335 el
= (WebKitDOMHTMLInputElement
*)active_element
;
336 ta
= (WebKitDOMHTMLTextAreaElement
*)active_element
;
338 if (doc
== NULL
|| active_element
== NULL
||
339 (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el
) == 0 &&
340 WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta
) == 0)) {
341 show_oops(t
, "No active text element!");
346 if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT(el
))
347 contents
= webkit_dom_html_input_element_get_value(el
);
348 else if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT(ta
))
349 contents
= webkit_dom_html_text_area_element_get_value(ta
);
351 if ((args
= g_malloc(sizeof(struct edit_element_cb_args
))) == NULL
)
355 args
->active
= active_element
;
357 open_external_editor(t
, contents
, &edit_element_cb
, args
);
362 #else /* Just to make things compile. */
365 edit_element(struct tab
*t
, struct karg
*a
)
367 show_oops(t
, "external editor feature requires webkit >= 1.5.0");
372 edit_src(struct tab
*t
, struct karg
*args
)
374 show_oops(t
, "external editor feature requires webkit >= 1.5.0");