1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
10 #include <gtkhtml/gtkhtml.h>
12 #include <sys/types.h>
23 #define RESPONSE_UPDATE 1
25 #define HTMLPREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), html_preview_get_type(), HTMLPreview))
26 static GType
html_preview_get_type(void);
28 static void url_requested(GtkHTML
*html
, const char *url
, GtkHTMLStream
*handle
);
29 static void link_clicked(GtkHTML
*html
, const char *url
, gpointer data
);
32 html_preview_init(HTMLPreview
*hp
) {
33 gtk_html_construct((gpointer
)hp
);
34 g_signal_connect(G_OBJECT(hp
), "url_requested",
35 G_CALLBACK(url_requested
), NULL
);
36 g_signal_connect(G_OBJECT(hp
), "link_clicked",
37 G_CALLBACK(link_clicked
), NULL
);
41 html_preview_new(GetEntryFunc get_entry_f
, gpointer get_entry_data
) {
42 HTMLPreview
*hp
= HTMLPREVIEW(g_object_new(html_preview_get_type(), NULL
));
43 hp
->get_entry
= get_entry_f
;
44 hp
->get_entry_data
= get_entry_data
;
45 return GTK_WIDGET(hp
);
49 html_preview_get_type(void) {
50 static GType hp_type
= 0;
52 const GTypeInfo hp_info
= {
53 sizeof (GtkHTMLClass
),
61 (GInstanceInitFunc
) html_preview_init
,
63 hp_type
= g_type_register_static(GTK_TYPE_HTML
,
64 "HTMLPreview", &hp_info
, 0);
70 link_clicked(GtkHTML
*html
, const char *url
, gpointer data
) {
71 spawn_url(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(html
))), url
);
75 url_requested(GtkHTML
*html
, const char *url
, GtkHTMLStream
*handle
) {
76 #define PROVIDE_IMAGE(name) \
77 if (g_ascii_strcasecmp(url, "logjam:" #name) == 0) { \
78 gtk_html_write(html, handle, \
79 (char*)logjam_##name##_png, sizeof(logjam_##name##_png)); }
85 PROVIDE_IMAGE(protected)
87 PROVIDE_IMAGE(private)
89 gtk_html_end(html
, handle
, GTK_HTML_STREAM_ERROR
);
92 gtk_html_end(html
, handle
, GTK_HTML_STREAM_OK
);
95 /* XXX not utf8 safe, but i think we're ok because we depend on ASCII
96 * characters in HTML and usernames. */
98 parse_ljtag(char *src
, GString
*dst
) {
103 while (*p
== ' ') p
++;
112 /* skip forward to equals sign. */
114 if (*p
== 0) return src
;
118 while (*p
== ' ') p
++; /* skip spaces. */
119 if (*p
== '\'' || *p
== '"') /* optional quote. */
123 while (*p
!= '\'' && *p
!= '"' && *p
!= '>' && *p
!= ' ' && *p
!= '/') {
124 if (*p
== 0) return src
;
129 if (*p
== '\'' || *p
== '"') p
++;
130 while (*p
== ' ') p
++;
132 while (*p
== ' ') p
++;
137 g_string_append_printf(dst
, "<a href='nowhere'>"
138 "<img src='logjam:%s' align='bottom' border='0'/>"
139 "<b>", isuser
? "ljuser" : "ljcomm");
140 g_string_append_len(dst
, start
, end
-start
);
141 g_string_append(dst
, "</b></a>");
147 entry_prepare_preview(LJEntry
*entry
) {
148 GString
*str
= g_string_new(NULL
);
150 gboolean has_time
, has_security
;
155 has_time
= entry
->time
.tm_year
> 0;
156 has_security
= entry
->security
.type
!= LJ_SECURITY_PUBLIC
;
158 if (has_security
|| entry
->subject
|| has_time
) {
159 g_string_append(str
, "<table width='100%'><tr>");
160 if (has_security
|| entry
->subject
) {
161 g_string_append(str
, "<td align='left'>");
164 if (entry
->security
.type
== LJ_SECURITY_PRIVATE
)
168 g_string_append_printf(str
,
169 "<img src='logjam:%s' align='bottom'/>", img
);
172 g_string_append_printf(str
, "<b>%s</b>", entry
->subject
);
173 g_string_append(str
, "</td>");
176 g_string_append_printf(str
,
177 "<td align='right'>%s</td>", asctime(&entry
->time
));
179 g_string_append(str
, "</tr></table><hr/><br/>");
182 if (entry
->mood
|| entry
->music
|| entry
->location
|| entry
->taglist
) {
184 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Current Mood"), entry
->mood
);
186 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Current Music"), entry
->music
);
188 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Current Location"), entry
->location
);
190 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Tags"), entry
->taglist
);
191 g_string_append(str
, "<br/>");
194 /* insert <br/> tags (if preformmated) and fixup <lj user=foo> tags. */
195 for (event
= entry
->event
; *event
; event
++) {
196 if (*event
== '\n' && !entry
->preformatted
) {
197 g_string_append_len(str
, "<br/>", 5);
198 } else if (event
[0] == '<' &&
199 /* check for lj user / lj comm. */
200 /* this is not good html parsing, but it should be enough.
201 * besides, the whole <lj user='foo'> tag is yucky html. :P */
202 event
[1] == 'l' && event
[2] == 'j' && event
[3] == ' ') {
203 char *p
= parse_ljtag(event
, str
);
209 g_string_append_c(str
, *event
);
217 preview_update(HTMLPreview
*hp
) {
221 entry
= hp
->get_entry(hp
->get_entry_data
);
224 str
= entry_prepare_preview(entry
);
225 /* gtkhtml whines if you give it an empty string. */
226 if (str
->str
[0] == '\0')
227 g_string_append_c(str
, ' ');
229 gtk_html_load_from_string(GTK_HTML(hp
), str
->str
, -1);
231 g_string_free(str
, TRUE
);
232 lj_entry_free(entry
);
236 get_entry_from_jw(JamWin
*jw
) {
237 return jam_doc_get_entry(jw
->doc
);
241 response_cb(GtkWidget
*dlg
, gint response
, PreviewUI
*pui
) {
242 if (response
== RESPONSE_UPDATE
)
243 preview_update(pui
->html
);
245 gtk_widget_destroy(dlg
);
249 make_window(PreviewUI
*pui
) {
250 pui
->win
= gtk_dialog_new_with_buttons(_("HTML Preview"),
252 GTK_DIALOG_DESTROY_WITH_PARENT
|GTK_DIALOG_NO_SEPARATOR
,
253 _("_Update"), RESPONSE_UPDATE
,
254 GTK_STOCK_CLOSE
, GTK_RESPONSE_OK
,
256 gtk_window_set_default_size(GTK_WINDOW(pui
->win
), 500, 400);
257 geometry_tie(GTK_WIDGET(pui
->win
), GEOM_PREVIEW
);
258 /* this reportedly breaks some wms.
259 * gtk_window_set_type_hint(GTK_WINDOW(pui->win),
260 GDK_WINDOW_TYPE_HINT_UTILITY);*/
261 g_signal_connect(G_OBJECT(pui
->win
), "response",
262 G_CALLBACK(response_cb
), pui
);
264 pui
->html
= HTMLPREVIEW(html_preview_new(
265 (GetEntryFunc
)get_entry_from_jw
, pui
->jw
));
266 preview_update(pui
->html
);
268 jam_dialog_set_contents(GTK_DIALOG(pui
->win
),
269 scroll_wrap(GTK_WIDGET(pui
->html
)));
273 preview_ui_show(JamWin
*jw
) {
278 preview_update(pui
->html
);
279 gtk_window_present(GTK_WINDOW(pui
->win
));
283 jw
->preview
= pui
= g_new0(PreviewUI
, 1);
286 g_signal_connect_swapped(G_OBJECT(pui
->win
), "destroy",
287 G_CALLBACK(g_free
), pui
);
288 gtk_widget_show_all(pui
->win
);
290 g_object_add_weak_pointer(G_OBJECT(pui
->win
), &jw
->preview
);