first try to journal_store_get_latest_id() for sqlite
[k8lowj.git] / src / preview.c
blob80efe0055ad5c3029c62cb37e8bdb4106118ccf3
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
7 #include "gtk-all.h"
8 #include "util-gtk.h"
10 #include <gtkhtml/gtkhtml.h>
12 #include <ctype.h>
13 #include <fcntl.h>
14 #include <unistd.h>
15 #include <sys/stat.h>
16 #include <sys/types.h>
18 #include "icons.h"
19 #include "inline.h"
20 #include "jam.h"
21 #include "preview.h"
22 #include "spawn.h"
24 #define RESPONSE_UPDATE (1)
26 #define HTMLPREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), html_preview_get_type(), HTMLPreview))
28 static GType html_preview_get_type (void);
30 static void url_requested (GtkHTML *html, const char *url, GtkHTMLStream *handle);
31 static void link_clicked (GtkHTML *html, const char *url, gpointer data);
34 static void html_preview_init (HTMLPreview *hp) {
35 gtk_html_construct((gpointer) hp);
36 g_signal_connect(G_OBJECT(hp), "url_requested", G_CALLBACK(url_requested), NULL);
37 g_signal_connect(G_OBJECT(hp), "link_clicked", G_CALLBACK(link_clicked), NULL);
41 GtkWidget *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 GType 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, "HTMLPreview", &hp_info, 0);
65 return hp_type;
69 static void link_clicked (GtkHTML *html, const char *url, gpointer data) {
70 spawn_url(GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(html))), url);
74 #define PROVIDE_IMAGE(name) if (g_ascii_strcasecmp(url, "logjam:" #name) == 0) { gtk_html_write(html, handle, (const char *)logjam_##name##_png, sizeof(logjam_##name##_png)); } else
76 static void url_requested (GtkHTML *html, const char *url, GtkHTMLStream *handle) {
77 PROVIDE_IMAGE(ljuser)
78 PROVIDE_IMAGE(ljcomm)
79 PROVIDE_IMAGE(protected)
80 PROVIDE_IMAGE(private)
82 gtk_html_end(html, handle, GTK_HTML_STREAM_ERROR);
83 return;
85 gtk_html_end(html, handle, GTK_HTML_STREAM_OK);
88 #undef PROVIDE_IMAGE
91 /* XXX not utf8 safe, but i think we're ok because we depend on ASCII characters in HTML and usernames. */
92 static const char *parse_ljtag (const char *src, GString *dst) {
93 gboolean isuser;
94 const char *start, *end;
95 const char *p = src + 3;
96 while (*p && isspace(*p)) ++p;
98 if (*p == 'u') isuser = TRUE;
99 else if (*p == 'c') isuser = FALSE;
100 else return src;
102 /* skip forward to equals sign. */
103 while (*p != '=') {
104 if (*p == 0) return src;
105 ++p;
107 ++p;
108 while (*p && isspace(*p)) ++p; /* skip spaces. */
109 if (*p == '\'' || *p == '"') ++p; /* optional quote. */
111 start = p;
112 while (*p != '\'' && *p != '"' && *p != '>' && *p != ' ' && *p != '/') {
113 if (*p == 0) return src;
114 ++p;
116 end = p;
118 if (*p == '\'' || *p == '"') ++p;
119 while (*p && isspace(*p)) ++p;
120 if (*p == '/') ++p;
121 while (*p && isspace(*p)) ++p;
122 if (*p != '>') return src;
123 ++p;
125 g_string_append_printf(dst, "<a href='nowhere'>" "<img src='logjam:%s' align='bottom' border='0'/>" "<b>", (isuser ? "ljuser" : "ljcomm"));
126 g_string_append_len(dst, start, end - start);
127 g_string_append(dst, "</b></a>");
129 return p;
133 static GString *entry_prepare_preview (LJEntry *entry) {
134 GString *str = g_string_new(NULL);
135 const gchar *event;
136 gboolean has_time, has_security;
138 if (!entry) return str;
140 has_time = (entry->time.tm_year > 0);
141 has_security = (entry->security.type != LJ_SECURITY_PUBLIC);
143 if (has_security || entry->subject || has_time) {
144 g_string_append(str, "<table width='100%'><tr>");
145 if (has_security || entry->subject) {
146 g_string_append(str, "<td align='left'>");
147 if (has_security) {
148 char *img = (entry->security.type == LJ_SECURITY_PRIVATE ? "private" : "protected");
149 g_string_append_printf(str, "<img src='logjam:%s' align='bottom'/>", img);
151 if (entry->subject) g_string_append_printf(str, "<b>%s</b>", entry->subject);
152 g_string_append(str, "</td>");
154 if (has_time) g_string_append_printf(str, "<td align='right'>%s</td>", asctime(&entry->time));
155 g_string_append(str, "</tr></table><hr/><br/>");
158 if (entry->mood || entry->music || entry->location || entry->taglist) {
159 if (entry->mood) g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Mood"), entry->mood);
160 if (entry->music) g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Music"), entry->music);
161 if (entry->location) g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Current Location"), entry->location);
162 if (entry->taglist) g_string_append_printf(str, "<i>%s</i>: %s<br/>", _("Tags"), entry->taglist);
163 g_string_append(str, "<br/>");
166 /* insert <br/> tags (if preformmated) and fixup <lj user=foo> tags. */
167 for (event = entry->event; *event; event++) {
168 if (*event == '\n' && !entry->preformatted) {
169 g_string_append_len(str, "<br/>", 5);
170 } else if (event[0] == '<' &&
171 /* check for lj user / lj comm. */
172 /* this is not good html parsing, but it should be enough.
173 * besides, the whole <lj user='foo'> tag is yucky html. :P */
174 event[1] == 'l' && event[2] == 'j' && (event[3] == ' ' || (event[3] == 'r' && event[4] == ' '))) {
175 const char *p = parse_ljtag(event, str);
176 if (p != event) {
177 event = p-1;
178 continue;
180 } else {
181 g_string_append_c(str, *event);
185 return str;
189 void preview_update (HTMLPreview *hp) {
190 GString *str;
191 LJEntry *entry = hp->get_entry(hp->get_entry_data);
192 if (!entry) return;
193 str = entry_prepare_preview(entry);
194 /* gtkhtml whines if you give it an empty string. */
195 if (str->str[0] == '\0') g_string_append_c(str, ' ');
196 gtk_html_load_from_string(GTK_HTML(hp), str->str, -1);
197 g_string_free(str, TRUE);
198 lj_entry_free(entry);
202 static LJEntry *get_entry_from_jw (JamWin *jw) {
203 return jam_doc_get_entry(jw->doc);
207 static void response_cb (GtkWidget *dlg, gint response, PreviewUI *pui) {
208 if (response == RESPONSE_UPDATE) preview_update(pui->html); else gtk_widget_destroy(dlg);
212 static void make_window (PreviewUI *pui) {
213 pui->win = gtk_dialog_new_with_buttons(_("HTML Preview"),
214 GTK_WINDOW(pui->jw),
215 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
216 _("_Update"), RESPONSE_UPDATE, GTK_STOCK_CLOSE, GTK_RESPONSE_OK, NULL);
217 gtk_window_set_default_size(GTK_WINDOW(pui->win), 500, 400);
218 geometry_tie(GTK_WIDGET(pui->win), GEOM_PREVIEW);
219 /* this reportedly breaks some wms.
220 * gtk_window_set_type_hint(GTK_WINDOW(pui->win),
221 GDK_WINDOW_TYPE_HINT_UTILITY);*/
222 g_signal_connect(G_OBJECT(pui->win), "response", G_CALLBACK(response_cb), pui);
224 pui->html = HTMLPREVIEW(html_preview_new((GetEntryFunc) get_entry_from_jw, pui->jw));
225 preview_update(pui->html);
227 jam_dialog_set_contents(GTK_DIALOG(pui->win), scroll_wrap(GTK_WIDGET(pui->html)));
231 void preview_ui_show (JamWin *jw) {
232 PreviewUI *pui;
234 if (jw->preview) {
235 pui = jw->preview;
236 preview_update(pui->html);
237 gtk_window_present(GTK_WINDOW(pui->win));
238 return;
241 jw->preview = pui = g_new0(PreviewUI, 1);
242 pui->jw = jw;
243 make_window(pui);
244 g_signal_connect_swapped(G_OBJECT(pui->win), "destroy", G_CALLBACK(g_free), pui);
245 gtk_widget_show_all(pui->win);
247 g_object_add_weak_pointer(G_OBJECT(pui->win), &jw->preview);
250 #endif