1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
9 #include <gtkhtml/gtkhtml.h>
11 #include <sys/types.h>
22 #define RESPONSE_UPDATE 1
24 #define HTMLPREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), html_preview_get_type(), HTMLPreview))
25 static GType
html_preview_get_type(void);
27 static void url_requested(GtkHTML
*html
, const char *url
, GtkHTMLStream
*handle
);
28 static void link_clicked(GtkHTML
*html
, const char *url
, gpointer data
);
31 html_preview_init(HTMLPreview
*hp
) {
32 gtk_html_construct((gpointer
)hp
);
33 g_signal_connect(G_OBJECT(hp
), "url_requested",
34 G_CALLBACK(url_requested
), NULL
);
35 g_signal_connect(G_OBJECT(hp
), "link_clicked",
36 G_CALLBACK(link_clicked
), NULL
);
40 html_preview_new(GetEntryFunc get_entry_f
, gpointer get_entry_data
) {
41 HTMLPreview
*hp
= HTMLPREVIEW(g_object_new(html_preview_get_type(), NULL
));
42 hp
->get_entry
= get_entry_f
;
43 hp
->get_entry_data
= get_entry_data
;
44 return GTK_WIDGET(hp
);
48 html_preview_get_type(void) {
49 static GType hp_type
= 0;
51 const GTypeInfo hp_info
= {
52 sizeof (GtkHTMLClass
),
60 (GInstanceInitFunc
) html_preview_init
,
62 hp_type
= g_type_register_static(GTK_TYPE_HTML
,
63 "HTMLPreview", &hp_info
, 0);
69 link_clicked(GtkHTML
*html
, const char *url
, gpointer data
) {
70 spawn_url(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(html
))), url
);
74 url_requested(GtkHTML
*html
, const char *url
, GtkHTMLStream
*handle
) {
75 #define PROVIDE_IMAGE(name) \
76 if (g_ascii_strcasecmp(url, "logjam:" #name) == 0) { \
77 gtk_html_write(html, handle, \
78 (char*)logjam_##name##_png, sizeof(logjam_##name##_png)); }
84 PROVIDE_IMAGE(protected)
86 PROVIDE_IMAGE(private)
88 gtk_html_end(html
, handle
, GTK_HTML_STREAM_ERROR
);
91 gtk_html_end(html
, handle
, GTK_HTML_STREAM_OK
);
94 /* XXX not utf8 safe, but i think we're ok because we depend on ASCII
95 * characters in HTML and usernames. */
97 parse_ljtag(char *src
, GString
*dst
) {
102 while (*p
== ' ') p
++;
111 /* skip forward to equals sign. */
113 if (*p
== 0) return src
;
117 while (*p
== ' ') p
++; /* skip spaces. */
118 if (*p
== '\'' || *p
== '"') /* optional quote. */
122 while (*p
!= '\'' && *p
!= '"' && *p
!= '>' && *p
!= ' ' && *p
!= '/') {
123 if (*p
== 0) return src
;
128 if (*p
== '\'' || *p
== '"') p
++;
129 while (*p
== ' ') p
++;
131 while (*p
== ' ') p
++;
136 g_string_append_printf(dst
, "<a href='nowhere'>"
137 "<img src='logjam:%s' align='bottom' border='0'/>"
138 "<b>", isuser
? "ljuser" : "ljcomm");
139 g_string_append_len(dst
, start
, end
-start
);
140 g_string_append(dst
, "</b></a>");
146 entry_prepare_preview(LJEntry
*entry
) {
147 GString
*str
= g_string_new(NULL
);
149 gboolean has_time
, has_security
;
154 has_time
= entry
->time
.tm_year
> 0;
155 has_security
= entry
->security
.type
!= LJ_SECURITY_PUBLIC
;
157 if (has_security
|| entry
->subject
|| has_time
) {
158 g_string_append(str
, "<table width='100%'><tr>");
159 if (has_security
|| entry
->subject
) {
160 g_string_append(str
, "<td align='left'>");
163 if (entry
->security
.type
== LJ_SECURITY_PRIVATE
)
167 g_string_append_printf(str
,
168 "<img src='logjam:%s' align='bottom'/>", img
);
171 g_string_append_printf(str
, "<b>%s</b>", entry
->subject
);
172 g_string_append(str
, "</td>");
175 g_string_append_printf(str
,
176 "<td align='right'>%s</td>", asctime(&entry
->time
));
178 g_string_append(str
, "</tr></table><hr/><br/>");
181 if (entry
->mood
|| entry
->music
|| entry
->location
|| entry
->taglist
) {
183 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Current Mood"), entry
->mood
);
185 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Current Music"), entry
->music
);
187 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Current Location"), entry
->location
);
189 g_string_append_printf(str
, "<i>%s</i>: %s<br/>", _("Tags"), entry
->taglist
);
190 g_string_append(str
, "<br/>");
193 /* insert <br/> tags (if preformmated) and fixup <lj user=foo> tags. */
194 for (event
= entry
->event
; *event
; event
++) {
195 if (*event
== '\n' && !entry
->preformatted
) {
196 g_string_append_len(str
, "<br/>", 5);
197 } else if (event
[0] == '<' &&
198 /* check for lj user / lj comm. */
199 /* this is not good html parsing, but it should be enough.
200 * besides, the whole <lj user='foo'> tag is yucky html. :P */
201 event
[1] == 'l' && event
[2] == 'j' && event
[3] == ' ') {
202 char *p
= parse_ljtag(event
, str
);
208 g_string_append_c(str
, *event
);
216 preview_update(HTMLPreview
*hp
) {
220 entry
= hp
->get_entry(hp
->get_entry_data
);
223 str
= entry_prepare_preview(entry
);
224 /* gtkhtml whines if you give it an empty string. */
225 if (str
->str
[0] == '\0')
226 g_string_append_c(str
, ' ');
228 gtk_html_load_from_string(GTK_HTML(hp
), str
->str
, -1);
230 g_string_free(str
, TRUE
);
231 lj_entry_free(entry
);
235 get_entry_from_jw(JamWin
*jw
) {
236 return jam_doc_get_entry(jw
->doc
);
240 response_cb(GtkWidget
*dlg
, gint response
, PreviewUI
*pui
) {
241 if (response
== RESPONSE_UPDATE
)
242 preview_update(pui
->html
);
244 gtk_widget_destroy(dlg
);
248 make_window(PreviewUI
*pui
) {
249 pui
->win
= gtk_dialog_new_with_buttons(_("HTML Preview"),
251 GTK_DIALOG_DESTROY_WITH_PARENT
|GTK_DIALOG_NO_SEPARATOR
,
252 _("_Update"), RESPONSE_UPDATE
,
253 GTK_STOCK_CLOSE
, GTK_RESPONSE_OK
,
255 gtk_window_set_default_size(GTK_WINDOW(pui
->win
), 500, 400);
256 geometry_tie(GTK_WIDGET(pui
->win
), GEOM_PREVIEW
);
257 /* this reportedly breaks some wms.
258 * gtk_window_set_type_hint(GTK_WINDOW(pui->win),
259 GDK_WINDOW_TYPE_HINT_UTILITY);*/
260 g_signal_connect(G_OBJECT(pui
->win
), "response",
261 G_CALLBACK(response_cb
), pui
);
263 pui
->html
= HTMLPREVIEW(html_preview_new(
264 (GetEntryFunc
)get_entry_from_jw
, pui
->jw
));
265 preview_update(pui
->html
);
267 jam_dialog_set_contents(GTK_DIALOG(pui
->win
),
268 scroll_wrap(GTK_WIDGET(pui
->html
)));
272 preview_ui_show(JamWin
*jw
) {
277 preview_update(pui
->html
);
278 gtk_window_present(GTK_WINDOW(pui
->win
));
282 jw
->preview
= pui
= g_new0(PreviewUI
, 1);
285 g_signal_connect_swapped(G_OBJECT(pui
->win
), "destroy",
286 G_CALLBACK(g_free
), pui
);
287 gtk_widget_show_all(pui
->win
);
289 g_object_add_weak_pointer(G_OBJECT(pui
->win
), &jw
->preview
);