about.c: cosmetix
[k8lowj.git] / src / preview.c
blob9fd7c46c9d244360f4553583b1fee2da86a919b4
1 /* logjam - a GTK client for LiveJournal.
2 * Copyright (C) 2000-2003 Evan Martin <evan@livejournal.com>
4 * vim: tabstop=4 shiftwidth=4 noexpandtab :
5 */
6 #ifdef HAVE_GTKHTML330
8 #include "gtk-all.h"
9 #include "util-gtk.h"
10 #include <gtkhtml/gtkhtml.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15 #include <unistd.h>
17 #include "jam.h"
18 #include "icons.h"
19 #include "preview.h"
20 #include "spawn.h"
21 #include "inline.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);
31 static void
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);
40 GtkWidget*
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);
48 GType
49 html_preview_get_type(void) {
50 static GType hp_type = 0;
51 if (!hp_type) {
52 const GTypeInfo hp_info = {
53 sizeof (GtkHTMLClass),
54 NULL,
55 NULL,
56 NULL,
57 NULL,
58 NULL,
59 sizeof (HTMLPreview),
61 (GInstanceInitFunc) html_preview_init,
63 hp_type = g_type_register_static(GTK_TYPE_HTML,
64 "HTMLPreview", &hp_info, 0);
66 return hp_type;
69 static void
70 link_clicked(GtkHTML *html, const char *url, gpointer data) {
71 spawn_url(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(html))), url);
74 static void
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)); }
81 PROVIDE_IMAGE(ljuser)
82 else
83 PROVIDE_IMAGE(ljcomm)
84 else
85 PROVIDE_IMAGE(protected)
86 else
87 PROVIDE_IMAGE(private)
88 else {
89 gtk_html_end(html, handle, GTK_HTML_STREAM_ERROR);
90 return;
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. */
97 static char*
98 parse_ljtag(char *src, GString *dst) {
99 gboolean isuser;
100 char *start, *end;
102 char *p = src + 3;
103 while (*p == ' ') p++;
105 if (*p == 'u')
106 isuser = TRUE;
107 else if (*p == 'c')
108 isuser = FALSE;
109 else
110 return src;
112 /* skip forward to equals sign. */
113 while (*p != '=') {
114 if (*p == 0) return src;
115 p++;
117 p++;
118 while (*p == ' ') p++; /* skip spaces. */
119 if (*p == '\'' || *p == '"') /* optional quote. */
120 p++;
122 start = p;
123 while (*p != '\'' && *p != '"' && *p != '>' && *p != ' ' && *p != '/') {
124 if (*p == 0) return src;
125 p++;
127 end = p;
129 if (*p == '\'' || *p == '"') p++;
130 while (*p == ' ') p++;
131 if (*p == '/') p++;
132 while (*p == ' ') p++;
133 if (*p != '>')
134 return src;
135 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>");
143 return p;
146 static GString*
147 entry_prepare_preview(LJEntry *entry) {
148 GString *str = g_string_new(NULL);
149 gchar *event;
150 gboolean has_time, has_security;
152 if (!entry)
153 return str;
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'>");
162 if (has_security) {
163 char *img;
164 if (entry->security.type == LJ_SECURITY_PRIVATE)
165 img = "private";
166 else
167 img = "protected";
168 g_string_append_printf(str,
169 "<img src='logjam:%s' align='bottom'/>", img);
171 if (entry->subject)
172 g_string_append_printf(str, "<b>%s</b>", entry->subject);
173 g_string_append(str, "</td>");
175 if (has_time) {
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) {
183 if (entry->mood)
184 g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Mood"), entry->mood);
185 if (entry->music)
186 g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Music"), entry->music);
187 if (entry->location)
188 g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Location"), entry->location);
189 if (entry->taglist)
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);
204 if (p != event) {
205 event = p-1;
206 continue;
208 } else {
209 g_string_append_c(str, *event);
213 return str;
216 void
217 preview_update(HTMLPreview *hp) {
218 LJEntry *entry;
219 GString *str;
221 entry = hp->get_entry(hp->get_entry_data);
222 if (!entry)
223 return;
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);
235 static LJEntry*
236 get_entry_from_jw(JamWin *jw) {
237 return jam_doc_get_entry(jw->doc);
240 static void
241 response_cb(GtkWidget *dlg, gint response, PreviewUI *pui) {
242 if (response == RESPONSE_UPDATE)
243 preview_update(pui->html);
244 else
245 gtk_widget_destroy(dlg);
248 static void
249 make_window(PreviewUI *pui) {
250 pui->win = gtk_dialog_new_with_buttons(_("HTML Preview"),
251 GTK_WINDOW(pui->jw),
252 GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR,
253 _("_Update"), RESPONSE_UPDATE,
254 GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
255 NULL);
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)));
272 void
273 preview_ui_show(JamWin *jw) {
274 PreviewUI *pui;
276 if (jw->preview) {
277 pui = jw->preview;
278 preview_update(pui->html);
279 gtk_window_present(GTK_WINDOW(pui->win));
280 return;
283 jw->preview = pui = g_new0(PreviewUI, 1);
284 pui->jw = jw;
285 make_window(pui);
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);
293 #endif