it builds! (note that some autogenerated files are hardcoded now, and you can't build...
[k8lowj.git] / src / preview.c
blob1a76bf1d10400d56a9794d412c9dd380a91fe255
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 <gtkhtml/gtkhtml.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <unistd.h>
16 #include "jam.h"
17 #include "icons.h"
18 #include "preview.h"
19 #include "spawn.h"
20 #include "inline.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);
30 static void
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);
39 GtkWidget*
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);
47 GType
48 html_preview_get_type(void) {
49 static GType hp_type = 0;
50 if (!hp_type) {
51 const GTypeInfo hp_info = {
52 sizeof (GtkHTMLClass),
53 NULL,
54 NULL,
55 NULL,
56 NULL,
57 NULL,
58 sizeof (HTMLPreview),
60 (GInstanceInitFunc) html_preview_init,
62 hp_type = g_type_register_static(GTK_TYPE_HTML,
63 "HTMLPreview", &hp_info, 0);
65 return hp_type;
68 static void
69 link_clicked(GtkHTML *html, const char *url, gpointer data) {
70 spawn_url(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(html))), url);
73 static void
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)); }
80 PROVIDE_IMAGE(ljuser)
81 else
82 PROVIDE_IMAGE(ljcomm)
83 else
84 PROVIDE_IMAGE(protected)
85 else
86 PROVIDE_IMAGE(private)
87 else {
88 gtk_html_end(html, handle, GTK_HTML_STREAM_ERROR);
89 return;
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. */
96 static char*
97 parse_ljtag(char *src, GString *dst) {
98 gboolean isuser;
99 char *start, *end;
101 char *p = src + 3;
102 while (*p == ' ') p++;
104 if (*p == 'u')
105 isuser = TRUE;
106 else if (*p == 'c')
107 isuser = FALSE;
108 else
109 return src;
111 /* skip forward to equals sign. */
112 while (*p != '=') {
113 if (*p == 0) return src;
114 p++;
116 p++;
117 while (*p == ' ') p++; /* skip spaces. */
118 if (*p == '\'' || *p == '"') /* optional quote. */
119 p++;
121 start = p;
122 while (*p != '\'' && *p != '"' && *p != '>' && *p != ' ' && *p != '/') {
123 if (*p == 0) return src;
124 p++;
126 end = p;
128 if (*p == '\'' || *p == '"') p++;
129 while (*p == ' ') p++;
130 if (*p == '/') p++;
131 while (*p == ' ') p++;
132 if (*p != '>')
133 return src;
134 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>");
142 return p;
145 static GString*
146 entry_prepare_preview(LJEntry *entry) {
147 GString *str = g_string_new(NULL);
148 gchar *event;
149 gboolean has_time, has_security;
151 if (!entry)
152 return str;
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'>");
161 if (has_security) {
162 char *img;
163 if (entry->security.type == LJ_SECURITY_PRIVATE)
164 img = "private";
165 else
166 img = "protected";
167 g_string_append_printf(str,
168 "<img src='logjam:%s' align='bottom'/>", img);
170 if (entry->subject)
171 g_string_append_printf(str, "<b>%s</b>", entry->subject);
172 g_string_append(str, "</td>");
174 if (has_time) {
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) {
182 if (entry->mood)
183 g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Mood"), entry->mood);
184 if (entry->music)
185 g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Music"), entry->music);
186 if (entry->location)
187 g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Location"), entry->location);
188 if (entry->taglist)
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);
203 if (p != event) {
204 event = p-1;
205 continue;
207 } else {
208 g_string_append_c(str, *event);
212 return str;
215 void
216 preview_update(HTMLPreview *hp) {
217 LJEntry *entry;
218 GString *str;
220 entry = hp->get_entry(hp->get_entry_data);
221 if (!entry)
222 return;
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);
234 static LJEntry*
235 get_entry_from_jw(JamWin *jw) {
236 return jam_doc_get_entry(jw->doc);
239 static void
240 response_cb(GtkWidget *dlg, gint response, PreviewUI *pui) {
241 if (response == RESPONSE_UPDATE)
242 preview_update(pui->html);
243 else
244 gtk_widget_destroy(dlg);
247 static void
248 make_window(PreviewUI *pui) {
249 pui->win = gtk_dialog_new_with_buttons(_("HTML Preview"),
250 GTK_WINDOW(pui->jw),
251 GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_NO_SEPARATOR,
252 _("_Update"), RESPONSE_UPDATE,
253 GTK_STOCK_CLOSE, GTK_RESPONSE_OK,
254 NULL);
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)));
271 void
272 preview_ui_show(JamWin *jw) {
273 PreviewUI *pui;
275 if (jw->preview) {
276 pui = jw->preview;
277 preview_update(pui->html);
278 gtk_window_present(GTK_WINDOW(pui->win));
279 return;
282 jw->preview = pui = g_new0(PreviewUI, 1);
283 pui->jw = jw;
284 make_window(pui);
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);
292 #endif