Contents of /trunk/src/html.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 163 - (hide annotations)
Thu Nov 5 15:12:37 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 14190 byte(s)
Textview color handling
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of GPXView.
5     *
6     * GPXView is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * GPXView is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "gpxview.h"
21    
22     typedef struct load_context {
23     int active;
24     GMutex *mutex;
25     GtkWidget *view;
26     char *url, *path;
27     GtkHTMLStream *stream;
28     struct load_context *next;
29     } load_context_t;
30    
31     typedef struct {
32     appdata_t *appdata;
33     cache_t *cache;
34     GtkWidget *view;
35     load_context_t *load_context;
36     } http_context_t;
37    
38     static unsigned long name_hash(const char *name) {
39     unsigned long val = 0;
40    
41     while(*name) {
42     val = (val<<8) ^ (val >> 24) ^ (*name & 0xff);
43     name++;
44     }
45    
46     return val;
47     }
48    
49     void release_load_context(GThread *self, load_context_t *context) {
50     /* this must be atomar, so acquire a lock */
51    
52     printf("%p: freeing context at %p\n", self, context);
53    
54     g_mutex_lock(context->mutex);
55    
56     /* don't do much if the other thread still uses this */
57     if(context->active) {
58     printf(" still active -> just close link\n");
59    
60     /* close link to view as the thread is either done or */
61     /* main wants to close the view */
62     gtk_html_end(GTK_HTML(context->view), context->stream,
63     GTK_HTML_STREAM_OK);
64    
65     context->active = FALSE;
66     g_mutex_unlock(context->mutex);
67     return;
68     }
69    
70     /* ok, other thread is also gone -> we can destroy everything */
71     printf(" not active -> destroy\n");
72    
73     /* just free everything */
74     g_mutex_unlock(context->mutex);
75     free(context->url);
76     free(context->path);
77     free(context);
78     }
79    
80     gpointer loader_thread(gpointer data) {
81     GThread *self = g_thread_self();
82    
83     GnomeVFSResult result;
84     GnomeVFSHandle *handle;
85     char buffer[4096];
86     GnomeVFSFileSize bytes_read;
87    
88     load_context_t *context = (load_context_t*)data;
89    
90     printf("%p: loader thread for %s running\n", self, context->url);
91    
92     result = gnome_vfs_open(&handle, context->url, GNOME_VFS_OPEN_READ);
93     if(result != GNOME_VFS_OK) {
94     g_print("%p: open error: %s\n", self, gnome_vfs_result_to_string(result));
95    
96     release_load_context(self, context);
97     return NULL;
98     }
99    
100     /* try to open file for writing */
101     FILE *f = fopen(context->path, "wb");
102     int running = TRUE;
103    
104     do {
105     result = gnome_vfs_read(handle, buffer, sizeof(buffer)-1, &bytes_read);
106     if((result == GNOME_VFS_OK) && (bytes_read > 0)) {
107    
108     /* update local "running" variable from shared variable */
109     if(running) {
110     g_mutex_lock(context->mutex);
111     running = context->active;
112     g_mutex_unlock(context->mutex);
113     }
114    
115     if(running)
116     gtk_html_write(GTK_HTML(context->view),
117     context->stream, buffer, bytes_read);
118    
119     /* and also write local file */
120     if(f) fwrite(buffer, 1l, bytes_read, f);
121     }
122     } while((result == GNOME_VFS_OK) && (bytes_read > 0) && running);
123    
124     if(f) fclose(f);
125    
126     gnome_vfs_close(handle);
127    
128     /* this file is likely incomplete, remove it */
129     if(!running) {
130     printf("%p: thread killed, removing imcomplete cached image\n", self);
131     remove(context->path);
132     release_load_context(self, context);
133     return NULL;
134     }
135    
136     printf("%p: loader thread successfully finished\n", self);
137     release_load_context(self, context);
138     return NULL;
139     }
140    
141     static void on_request_url(GtkHTML *html, const gchar *url,
142     GtkHTMLStream *stream, gpointer data) {
143     char buffer[4096];
144     GnomeVFSFileSize bytes_read;
145    
146     http_context_t *context = (http_context_t*)data;
147    
148     if(context->cache) {
149     /* try to build local path */
150     char *path = malloc(strlen(context->appdata->image_path)+
151     strlen(context->cache->id)+
152     14); /* strlen("/xxxxxxxx.xxx\0") == 14 */
153    
154     strcpy(path, context->appdata->image_path);
155     strcat(path, context->cache->id);
156     sprintf(path+strlen(path), "/%lx", name_hash(url));
157    
158     /* only append extension if there's a useful one up to three chars ... */
159     char *dot = strrchr(url, '.');
160     if(dot)
161     if(strlen(dot) <= 4)
162     strcat(path, dot);
163    
164     printf("cache name = %s\n", path);
165     if(g_file_test(path, G_FILE_TEST_EXISTS)) {
166     printf("image file exists!\n");
167    
168     FILE *f = fopen(path, "rb");
169    
170     while ((bytes_read = fread(buffer, 1, sizeof(buffer), f)) != 0) {
171    
172     gtk_html_write(GTK_HTML(context->view),
173     stream, buffer, bytes_read);
174    
175     while (gtk_events_pending ())
176     gtk_main_iteration ();
177     }
178     fclose(f);
179    
180     } else {
181 harbaum 12 printf("image file doesn't exist, starting extra thread!\n");
182 harbaum 1
183 harbaum 12 checkdir(path);
184    
185     /* walk to end of list */
186     load_context_t **load_context = &(context->load_context);
187     while(*load_context)
188     load_context = &(*load_context)->next;
189    
190     *load_context = g_new0(load_context_t, 1);
191    
192     (*load_context)->url = strdup(url);
193     (*load_context)->path = strdup(path);
194     (*load_context)->view = context->view;
195     (*load_context)->stream = stream;
196     (*load_context)->next = NULL;
197     (*load_context)->active = TRUE;
198     (*load_context)->mutex = g_mutex_new();
199    
200     g_thread_create(loader_thread, *load_context, TRUE, NULL);
201     return;
202 harbaum 1 }
203     } else {
204     /* not a cache, maybe help, so load images from icon directory */
205    
206     /* try to build local path */
207     char *path = malloc(strlen(ICONPATH)+strlen(url)+1);
208    
209     strcpy(path, ICONPATH);
210     strcat(path, url);
211     if(!g_file_test(path, G_FILE_TEST_EXISTS)) {
212     strcpy(path, "./icons/");
213     strcat(path, url);
214     }
215    
216     if(g_file_test(path, G_FILE_TEST_EXISTS)) {
217     FILE *f = fopen(path, "rb");
218    
219     while ((bytes_read = fread(buffer, 1, sizeof(buffer), f)) != 0) {
220     gtk_html_write(GTK_HTML(context->view),
221     stream, buffer, bytes_read);
222    
223     while (gtk_events_pending ())
224     gtk_main_iteration ();
225     }
226     fclose(f);
227     }
228     }
229    
230     gtk_html_end(GTK_HTML(context->view), stream, GTK_HTML_STREAM_OK);
231     }
232    
233     /* notify all open html views of zoom event */
234     void html_zoom(appdata_t *appdata, gboolean in) {
235     struct html_view *html_view = appdata->html_view;
236    
237     while(html_view) {
238     printf("zoom notify view %p\n", html_view->view);
239    
240     if(in) gtk_html_zoom_in(GTK_HTML(html_view->view));
241     else gtk_html_zoom_out(GTK_HTML(html_view->view));
242    
243     html_view = html_view->next;
244     }
245     }
246    
247 harbaum 2 #ifndef NO_COPY_N_PASTE
248 harbaum 1 static void on_destroy_textview(GtkWidget *widget, gpointer data) {
249     appdata_t *appdata = (appdata_t*)data;
250     int destroy_active = FALSE;
251    
252     /* only do this if main windows hasn't already been destroyed */
253     if(!appdata->window) {
254     printf("detroy_textview: main window is gone\n");
255     return;
256     }
257    
258     if(!appdata->active_buffer)
259     printf("Destroy, but there was no active buffer!\n");
260     else {
261     if(GTK_WIDGET_TYPE(widget) == GTK_TYPE_TEXT_VIEW) {
262     printf("destroying textview\n");
263    
264     if(appdata->active_buffer ==
265     gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget))) {
266     printf("This was the active buffer\n");
267     destroy_active = TRUE;
268     }
269     } else {
270     if(widget == (GtkWidget*)appdata->active_buffer) {
271     printf("This was the active html view\n");
272     destroy_active = TRUE;
273     }
274     }
275     }
276    
277     if(destroy_active) {
278     appdata->active_buffer = NULL;
279    
280     gtk_widget_set_sensitive(appdata->menu_cut, FALSE);
281     gtk_widget_set_sensitive(appdata->menu_copy, FALSE);
282     gtk_widget_set_sensitive(appdata->menu_paste, FALSE);
283     }
284     }
285 harbaum 2 #endif
286 harbaum 1
287     static void on_destroy_htmlview(GtkWidget *widget, gpointer data) {
288     http_context_t *context = (http_context_t*)data;
289    
290     printf("destroying html context\n");
291     // printf("associated view = %p\n", context->view);
292    
293     struct html_view **h = &(context->appdata->html_view);
294    
295     while(*h && (*h)->view != context->view)
296     h = &((*h)->next);
297    
298     g_assert(h);
299    
300     /* remove entry from chain */
301     void *tmp = *h;
302     *h = (*h)->next;
303     free(tmp);
304    
305     load_context_t *load_context = context->load_context;
306     while(load_context) {
307     printf("found an associated thread\n");
308    
309     load_context_t *tmp_context = load_context->next;
310     release_load_context(NULL, load_context);
311     load_context = tmp_context;
312     }
313    
314 harbaum 2 #ifndef NO_COPY_N_PASTE
315 harbaum 1 on_destroy_textview(widget, context->appdata);
316 harbaum 2 #endif
317 harbaum 1
318     /* destroy context */
319     free(data);
320     }
321    
322 harbaum 2 #ifndef NO_COPY_N_PASTE
323 harbaum 1 static gboolean focus_in(GtkWidget *widget, GdkEventFocus *event,
324     gpointer data) {
325     appdata_t *appdata = (appdata_t*)data;
326    
327     printf("focus in!\n");
328    
329     /* these buffers are read-only, thus only "copy" is enabled */
330     gtk_widget_set_sensitive(appdata->menu_cut, FALSE);
331     gtk_widget_set_sensitive(appdata->menu_copy, TRUE);
332     gtk_widget_set_sensitive(appdata->menu_paste, FALSE);
333    
334     if(GTK_WIDGET_TYPE(widget) == GTK_TYPE_TEXT_VIEW) {
335     appdata->active_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
336     } else if(GTK_WIDGET_TYPE(widget) == gtk_html_get_type()) {
337     appdata->active_buffer = (GtkTextBuffer*)widget;
338     } else {
339     printf("not a text nor a html view\n");
340     }
341    
342     return FALSE;
343     }
344    
345     void html_copy_to_clipboard(appdata_t *appdata) {
346     gtk_html_copy(GTK_HTML(appdata->active_buffer));
347     }
348 harbaum 2 #endif
349 harbaum 1
350     #ifdef PANNABLE_HTML
351     /* eat the button events */
352     static gboolean on_button_press(GtkWidget *widget, GdkEventButton *event,
353     gpointer user_data) {
354     return TRUE;
355     }
356     #endif
357    
358     /* the cache descriptions are not valid html and need some surrounding stuff */
359     static const char *html_start = "<html><head>"
360     "<meta http-equiv=content-type content=\"text/html; charset=UTF-8\">"
361     "</head></body>";
362     static const char *html_end = "</body></html>";
363    
364     GtkWidget *html_view(appdata_t *appdata, char *text,
365 harbaum 140 html_mode_t mode, gboolean scrollwin,
366 harbaum 1 cache_t *cache, char *anchor) {
367     GtkWidget *view;
368    
369 harbaum 140 if(mode == HTML_HTML) {
370 harbaum 1 http_context_t *context = g_new0(http_context_t, 1);
371     context->appdata = appdata;
372     context->cache = cache;
373    
374     context->view = view = gtk_html_new();
375    
376     /* create a callback to load images only if a cache has been given */
377     /* so that images can be cached/stored appropriately */
378     g_signal_connect(G_OBJECT(view), "url_requested",
379     G_CALLBACK(on_request_url), context);
380    
381     GtkHTMLStream *stream = gtk_html_begin(GTK_HTML(view));
382    
383     gtk_html_write(GTK_HTML(view), stream, html_start, strlen(html_start));
384     gtk_html_write(GTK_HTML(view), stream, text, strlen(text));
385     gtk_html_write(GTK_HTML(view), stream, html_end, strlen(html_end));
386     gtk_html_end(GTK_HTML(view), stream, GTK_HTML_STREAM_OK);
387    
388     if(anchor) {
389     while(gtk_events_pending())
390     gtk_main_iteration ();
391    
392     gtk_html_jump_to_anchor(GTK_HTML(context->view), anchor);
393     }
394    
395     /* register this html view */
396     struct html_view **h = &(appdata->html_view);
397     while(*h) h = &((*h)->next);
398     *h = g_new0(struct html_view, 1);
399     (*h)->view = view;
400    
401     #ifdef PANNABLE_HTML
402     /* this causes finger scrolling to work nicely but also prevents */
403     /* copy'n paste from working correctly */
404     gtk_widget_set_sensitive(GTK_WIDGET(view), FALSE);
405    
406     g_signal_connect(G_OBJECT(view), "button-press-event",
407     G_CALLBACK(on_button_press), NULL);
408     #endif
409    
410     g_signal_connect(G_OBJECT(view), "destroy",
411     G_CALLBACK(on_destroy_htmlview), context);
412     } else {
413     GtkTextBuffer *buffer = gtk_text_buffer_new(NULL);
414     gtk_text_buffer_set_text(buffer, text, strlen(text));
415    
416 harbaum 8 #ifndef USE_HILDON_TEXT_VIEW
417 harbaum 1 view = gtk_text_view_new_with_buffer(buffer);
418     #else
419     view = hildon_text_view_new();
420     hildon_text_view_set_buffer(HILDON_TEXT_VIEW(view), buffer);
421     #endif
422    
423     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(view), GTK_WRAP_WORD);
424     gtk_text_view_set_editable(GTK_TEXT_VIEW(view), FALSE);
425     gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
426    
427 harbaum 163 // #if MAEMO_VERSION_MAJOR >= 5
428     #if 1
429     /* make this look nicer in fremantle and not just black-on-white */
430    
431     #if 0
432     /* this causes a black background */
433     gtk_widget_modify_base(view, GTK_STATE_NORMAL,
434     &GTK_WIDGET(appdata->window)->style->bg[GTK_STATE_NORMAL]);
435     /* and this causes white letters */
436     gtk_widget_modify_text(view, GTK_STATE_NORMAL,
437     &GTK_WIDGET(appdata->window)->style->text[GTK_STATE_NORMAL]);
438     #else
439     /* just use the default style */
440     #if 1
441     gtk_widget_set_style(view, GTK_WIDGET(appdata->window)->style);
442     #else
443     GtkStyle *style = gtk_rc_get_style(GTK_WIDGET(appdata->window));
444     gtk_widget_set_style(view, style);
445     #endif
446     #endif
447    
448     #endif
449    
450 harbaum 2 #ifndef NO_COPY_N_PASTE
451 harbaum 1 g_signal_connect(G_OBJECT(view), "destroy",
452     G_CALLBACK(on_destroy_textview), appdata);
453 harbaum 2 #endif
454 harbaum 1 }
455    
456 harbaum 2 #ifndef NO_COPY_N_PASTE
457 harbaum 1 g_signal_connect(G_OBJECT(view), "focus-in-event",
458     G_CALLBACK(focus_in), appdata);
459 harbaum 2 #endif
460 harbaum 1
461     if(scrollwin) {
462     #ifndef USE_PANNABLE_AREA
463     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
464     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window),
465     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
466     gtk_container_add(GTK_CONTAINER(scrolled_window), view);
467    
468     #if 1
469     return scrolled_window;
470     #else
471     GtkWidget *fixed = gtk_fixed_new();
472     gtk_fixed_put(GTK_FIXED(fixed), scrolled_window, 0, 0);
473     GtkWidget *tbutton = gtk_toggle_button_new_with_label("sel");
474     gtk_fixed_put(GTK_FIXED(fixed), tbutton, 0, 0);
475     return fixed;
476     #endif
477     #else
478     #ifndef PANNABLE_HTML
479 harbaum 140 if(mode == HTML_HTML) {
480 harbaum 1 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
481     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (scrolled_window),
482     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
483     gtk_container_add(GTK_CONTAINER(scrolled_window), view);
484     return scrolled_window;
485     } else
486     #endif
487     {
488     GtkWidget *pannable_area = hildon_pannable_area_new();
489     gtk_container_add(GTK_CONTAINER(pannable_area), view);
490     return pannable_area;
491     }
492     #endif
493     }
494    
495     return view;
496     }
497