Statistics
| Revision:

root / src / textview.c @ 547

History | View | Annotate | Download (54.8 kB)

1 1 hiro
/*
2 1 hiro
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 27 hiro
 * Copyright (C) 1999-2005 Hiroyuki Yamamoto
4 1 hiro
 *
5 1 hiro
 * This program is free software; you can redistribute it and/or modify
6 1 hiro
 * it under the terms of the GNU General Public License as published by
7 1 hiro
 * the Free Software Foundation; either version 2 of the License, or
8 1 hiro
 * (at your option) any later version.
9 1 hiro
 *
10 1 hiro
 * This program is distributed in the hope that it will be useful,
11 1 hiro
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 1 hiro
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 1 hiro
 * GNU General Public License for more details.
14 1 hiro
 *
15 1 hiro
 * You should have received a copy of the GNU General Public License
16 1 hiro
 * along with this program; if not, write to the Free Software
17 1 hiro
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 1 hiro
 */
19 1 hiro
20 1 hiro
#ifdef HAVE_CONFIG_H
21 1 hiro
#  include "config.h"
22 1 hiro
#endif
23 1 hiro
24 1 hiro
#include "defs.h"
25 1 hiro
26 1 hiro
#include <glib.h>
27 92 hiro
#include <glib/gi18n.h>
28 1 hiro
#include <gdk/gdk.h>
29 1 hiro
#include <gdk/gdkkeysyms.h>
30 237 hiro
#include <gdk-pixbuf/gdk-pixbuf.h>
31 1 hiro
#include <gtk/gtkvbox.h>
32 1 hiro
#include <gtk/gtkscrolledwindow.h>
33 1 hiro
#include <gtk/gtksignal.h>
34 237 hiro
#include <gtk/gtkmenu.h>
35 237 hiro
#include <gtk/gtkmenuitem.h>
36 237 hiro
#include <gtk/gtkseparatormenuitem.h>
37 237 hiro
#include <gtk/gtkstock.h>
38 1 hiro
#include <stdio.h>
39 1 hiro
#include <ctype.h>
40 1 hiro
#include <string.h>
41 1 hiro
#include <stdlib.h>
42 1 hiro
43 1 hiro
#include "main.h"
44 1 hiro
#include "summaryview.h"
45 155 hiro
#include "imageview.h"
46 1 hiro
#include "procheader.h"
47 1 hiro
#include "prefs_common.h"
48 1 hiro
#include "codeconv.h"
49 1 hiro
#include "statusbar.h"
50 1 hiro
#include "utils.h"
51 1 hiro
#include "gtkutils.h"
52 1 hiro
#include "procmime.h"
53 1 hiro
#include "account.h"
54 1 hiro
#include "html.h"
55 1 hiro
#include "compose.h"
56 1 hiro
#include "displayheader.h"
57 162 hiro
#include "filesel.h"
58 1 hiro
#include "alertpanel.h"
59 1 hiro
60 1 hiro
typedef struct _RemoteURI        RemoteURI;
61 1 hiro
62 1 hiro
struct _RemoteURI
63 1 hiro
{
64 1 hiro
        gchar *uri;
65 1 hiro
66 162 hiro
        gchar *filename;
67 162 hiro
68 1 hiro
        guint start;
69 1 hiro
        guint end;
70 1 hiro
};
71 1 hiro
72 1 hiro
static GdkColor quote_colors[3] = {
73 1 hiro
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0},
74 1 hiro
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0},
75 1 hiro
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0}
76 1 hiro
};
77 1 hiro
78 1 hiro
static GdkColor uri_color = {
79 1 hiro
        (gulong)0,
80 1 hiro
        (gushort)0,
81 1 hiro
        (gushort)0,
82 1 hiro
        (gushort)0
83 1 hiro
};
84 1 hiro
85 1 hiro
static GdkColor emphasis_color = {
86 1 hiro
        (gulong)0,
87 1 hiro
        (gushort)0,
88 1 hiro
        (gushort)0,
89 1 hiro
        (gushort)0xcfff
90 1 hiro
};
91 1 hiro
92 1 hiro
#if 0
93 1 hiro
static GdkColor error_color = {
94 1 hiro
        (gulong)0,
95 1 hiro
        (gushort)0xefff,
96 1 hiro
        (gushort)0,
97 1 hiro
        (gushort)0
98 1 hiro
};
99 1 hiro
#endif
100 1 hiro
101 1 hiro
#if USE_GPGME
102 1 hiro
static GdkColor good_sig_color = {
103 1 hiro
        (gulong)0,
104 1 hiro
        (gushort)0,
105 1 hiro
        (gushort)0xbfff,
106 1 hiro
        (gushort)0
107 1 hiro
};
108 1 hiro
109 56 hiro
static GdkColor untrusted_sig_color = {
110 56 hiro
        (gulong)0,
111 56 hiro
        (gushort)0xefff,
112 56 hiro
        (gushort)0,
113 56 hiro
        (gushort)0
114 56 hiro
};
115 56 hiro
116 1 hiro
static GdkColor nocheck_sig_color = {
117 1 hiro
        (gulong)0,
118 1 hiro
        (gushort)0,
119 1 hiro
        (gushort)0,
120 1 hiro
        (gushort)0xcfff
121 1 hiro
};
122 1 hiro
123 1 hiro
static GdkColor bad_sig_color = {
124 1 hiro
        (gulong)0,
125 1 hiro
        (gushort)0xefff,
126 1 hiro
        (gushort)0,
127 1 hiro
        (gushort)0
128 1 hiro
};
129 1 hiro
#endif
130 1 hiro
131 1 hiro
#define STATUSBAR_PUSH(textview, str)                                            \
132 1 hiro
{                                                                            \
133 1 hiro
        gtk_statusbar_push(GTK_STATUSBAR(textview->messageview->statusbar), \
134 1 hiro
                           textview->messageview->statusbar_cid, str);            \
135 1 hiro
}
136 1 hiro
137 1 hiro
#define STATUSBAR_POP(textview)                                                   \
138 1 hiro
{                                                                           \
139 1 hiro
        gtk_statusbar_pop(GTK_STATUSBAR(textview->messageview->statusbar), \
140 1 hiro
                          textview->messageview->statusbar_cid);           \
141 1 hiro
}
142 1 hiro
143 101 hiro
static GdkCursor *hand_cursor = NULL;
144 101 hiro
static GdkCursor *regular_cursor = NULL;
145 101 hiro
146 101 hiro
147 1 hiro
static void textview_add_part                (TextView        *textview,
148 1 hiro
                                         MimeInfo        *mimeinfo,
149 1 hiro
                                         FILE                *fp);
150 1 hiro
static void textview_add_parts                (TextView        *textview,
151 1 hiro
                                         MimeInfo        *mimeinfo,
152 1 hiro
                                         FILE                *fp);
153 1 hiro
static void textview_write_body                (TextView        *textview,
154 1 hiro
                                         MimeInfo        *mimeinfo,
155 1 hiro
                                         FILE                *fp,
156 1 hiro
                                         const gchar        *charset);
157 1 hiro
static void textview_show_html                (TextView        *textview,
158 1 hiro
                                         FILE                *fp,
159 1 hiro
                                         CodeConverter        *conv);
160 1 hiro
161 1 hiro
static void textview_write_line                (TextView        *textview,
162 1 hiro
                                         const gchar        *str,
163 1 hiro
                                         CodeConverter        *conv);
164 1 hiro
static void textview_write_link                (TextView        *textview,
165 1 hiro
                                         const gchar        *str,
166 1 hiro
                                         const gchar        *uri,
167 1 hiro
                                         CodeConverter        *conv);
168 1 hiro
169 1 hiro
static GPtrArray *textview_scan_header        (TextView        *textview,
170 144 hiro
                                         FILE                *fp,
171 144 hiro
                                         const gchar        *encoding);
172 1 hiro
static void textview_show_header        (TextView        *textview,
173 1 hiro
                                         GPtrArray        *headers);
174 1 hiro
175 1 hiro
static gboolean textview_key_pressed                (GtkWidget        *widget,
176 1 hiro
                                                 GdkEventKey        *event,
177 1 hiro
                                                 TextView        *textview);
178 101 hiro
static gboolean textview_event_after                (GtkWidget        *widget,
179 1 hiro
                                                 GdkEvent        *event,
180 1 hiro
                                                 TextView        *textview);
181 101 hiro
static gboolean textview_motion_notify                (GtkWidget        *widget,
182 101 hiro
                                                 GdkEventMotion        *event,
183 101 hiro
                                                 TextView        *textview);
184 113 hiro
static gboolean textview_leave_notify                (GtkWidget          *widget,
185 113 hiro
                                                 GdkEventCrossing *event,
186 113 hiro
                                                 TextView          *textview);
187 101 hiro
static gboolean textview_visibility_notify        (GtkWidget        *widget,
188 101 hiro
                                                 GdkEventVisibility *event,
189 101 hiro
                                                 TextView        *textview);
190 1 hiro
191 116 hiro
static void textview_populate_popup                (GtkWidget        *widget,
192 116 hiro
                                                 GtkMenu        *menu,
193 116 hiro
                                                 TextView        *textview);
194 368 hiro
static void textview_popup_menu_activate_open_uri_cb
195 368 hiro
                                                (GtkMenuItem        *menuitem,
196 368 hiro
                                                 gpointer         data);
197 352 hiro
static void textview_popup_menu_activate_add_address_cb
198 352 hiro
                                                (GtkMenuItem        *menuitem,
199 352 hiro
                                                 gpointer         data);
200 116 hiro
static void textview_popup_menu_activate_copy_cb(GtkMenuItem        *menuitem,
201 116 hiro
                                                 gpointer         data);
202 162 hiro
static void textview_popup_menu_activate_image_cb
203 162 hiro
                                                (GtkMenuItem        *menuitem,
204 162 hiro
                                                 gpointer         data);
205 116 hiro
206 1 hiro
static void textview_smooth_scroll_do                (TextView        *textview,
207 1 hiro
                                                 gfloat                 old_value,
208 1 hiro
                                                 gfloat                 last_value,
209 1 hiro
                                                 gint                 step);
210 1 hiro
static void textview_smooth_scroll_one_line        (TextView        *textview,
211 1 hiro
                                                 gboolean         up);
212 1 hiro
static gboolean textview_smooth_scroll_page        (TextView        *textview,
213 1 hiro
                                                 gboolean         up);
214 1 hiro
215 101 hiro
static gboolean textview_get_link_tag_bounds        (TextView        *textview,
216 101 hiro
                                                 GtkTextIter        *iter,
217 101 hiro
                                                 GtkTextIter        *start,
218 101 hiro
                                                 GtkTextIter        *end);
219 101 hiro
static RemoteURI *textview_get_uri                (TextView        *textview,
220 101 hiro
                                                 GtkTextIter        *start,
221 101 hiro
                                                 GtkTextIter        *end);
222 101 hiro
static void textview_show_uri                        (TextView        *textview,
223 101 hiro
                                                 GtkTextIter        *start,
224 101 hiro
                                                 GtkTextIter        *end);
225 101 hiro
static void textview_set_cursor                        (TextView        *textview,
226 101 hiro
                                                 GtkTextView        *text,
227 101 hiro
                                                 gint                 x,
228 101 hiro
                                                 gint                 y);
229 101 hiro
230 1 hiro
static gboolean textview_uri_security_check        (TextView        *textview,
231 1 hiro
                                                 RemoteURI        *uri);
232 1 hiro
static void textview_uri_list_remove_all        (GSList                *uri_list);
233 1 hiro
234 1 hiro
235 1 hiro
TextView *textview_create(void)
236 1 hiro
{
237 1 hiro
        TextView *textview;
238 1 hiro
        GtkWidget *vbox;
239 1 hiro
        GtkWidget *scrolledwin;
240 1 hiro
        GtkWidget *text;
241 1 hiro
        GtkTextBuffer *buffer;
242 1 hiro
        GtkClipboard *clipboard;
243 1 hiro
244 1 hiro
        debug_print(_("Creating text view...\n"));
245 1 hiro
        textview = g_new0(TextView, 1);
246 1 hiro
247 1 hiro
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
248 1 hiro
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
249 97 hiro
                                       GTK_POLICY_AUTOMATIC,
250 97 hiro
                                       GTK_POLICY_AUTOMATIC);
251 4 hiro
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
252 4 hiro
                                            GTK_SHADOW_IN);
253 1 hiro
        gtk_widget_set_size_request
254 1 hiro
                (scrolledwin, prefs_common.mainview_width, -1);
255 1 hiro
256 1 hiro
        text = gtk_text_view_new();
257 113 hiro
        gtk_widget_add_events(text, GDK_LEAVE_NOTIFY_MASK);
258 1 hiro
        gtk_widget_show(text);
259 1 hiro
        gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
260 1 hiro
        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
261 3 hiro
        gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
262 3 hiro
        gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
263 1 hiro
264 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
265 1 hiro
        clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
266 1 hiro
        gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
267 1 hiro
268 1 hiro
        gtk_widget_ref(scrolledwin);
269 1 hiro
270 1 hiro
        gtk_container_add(GTK_CONTAINER(scrolledwin), text);
271 1 hiro
272 101 hiro
        g_signal_connect(G_OBJECT(text), "key-press-event",
273 1 hiro
                         G_CALLBACK(textview_key_pressed), textview);
274 101 hiro
        g_signal_connect(G_OBJECT(text), "event-after",
275 101 hiro
                         G_CALLBACK(textview_event_after), textview);
276 101 hiro
        g_signal_connect(G_OBJECT(text), "motion-notify-event",
277 101 hiro
                         G_CALLBACK(textview_motion_notify), textview);
278 113 hiro
        g_signal_connect(G_OBJECT(text), "leave-notify-event",
279 113 hiro
                         G_CALLBACK(textview_leave_notify), textview);
280 101 hiro
        g_signal_connect(G_OBJECT(text), "visibility-notify-event",
281 101 hiro
                         G_CALLBACK(textview_visibility_notify), textview);
282 116 hiro
        g_signal_connect(G_OBJECT(text), "populate-popup",
283 116 hiro
                         G_CALLBACK(textview_populate_popup), textview);
284 1 hiro
285 1 hiro
        gtk_widget_show(scrolledwin);
286 1 hiro
287 1 hiro
        vbox = gtk_vbox_new(FALSE, 0);
288 1 hiro
        gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
289 1 hiro
290 1 hiro
        gtk_widget_show(vbox);
291 1 hiro
292 1 hiro
        textview->vbox             = vbox;
293 1 hiro
        textview->scrolledwin      = scrolledwin;
294 1 hiro
        textview->text             = text;
295 1 hiro
        textview->uri_list         = NULL;
296 1 hiro
        textview->body_pos         = 0;
297 1 hiro
        textview->show_all_headers = FALSE;
298 1 hiro
299 1 hiro
        return textview;
300 1 hiro
}
301 1 hiro
302 364 hiro
static void textview_create_tags(TextView *textview)
303 1 hiro
{
304 364 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
305 1 hiro
        GtkTextBuffer *buffer;
306 101 hiro
        static PangoFontDescription *font_desc;
307 1 hiro
308 101 hiro
        if (!font_desc)
309 39 hiro
                font_desc = gtkut_get_default_font_desc();
310 39 hiro
311 1 hiro
        buffer = gtk_text_view_get_buffer(text);
312 1 hiro
313 1 hiro
        gtk_text_buffer_create_tag(buffer, "header",
314 500 hiro
                                   "pixels-above-lines", 1,
315 1 hiro
                                   "pixels-above-lines-set", TRUE,
316 1 hiro
                                   "pixels-below-lines", 0,
317 1 hiro
                                   "pixels-below-lines-set", TRUE,
318 39 hiro
                                   "font-desc", font_desc,
319 1 hiro
                                   NULL);
320 1 hiro
        gtk_text_buffer_create_tag(buffer, "header_title",
321 101 hiro
                                   "weight", PANGO_WEIGHT_BOLD,
322 1 hiro
                                   NULL);
323 364 hiro
324 500 hiro
        gtk_text_buffer_create_tag(buffer, "mimepart",
325 500 hiro
                                   "pixels-above-lines", 1,
326 500 hiro
                                   "pixels-above-lines-set", TRUE,
327 500 hiro
                                   "pixels-below-lines", 1,
328 500 hiro
                                   "pixels-below-lines-set", TRUE,
329 500 hiro
                                   "font-desc", font_desc,
330 500 hiro
                                   NULL);
331 500 hiro
332 364 hiro
        textview->quote0_tag =
333 364 hiro
                gtk_text_buffer_create_tag(buffer, "quote0",
334 364 hiro
                                           "foreground-gdk", &quote_colors[0],
335 364 hiro
                                           NULL);
336 364 hiro
        textview->quote1_tag =
337 364 hiro
                gtk_text_buffer_create_tag(buffer, "quote1",
338 364 hiro
                                           "foreground-gdk", &quote_colors[1],
339 364 hiro
                                           NULL);
340 364 hiro
        textview->quote2_tag =
341 364 hiro
                gtk_text_buffer_create_tag(buffer, "quote2",
342 364 hiro
                                           "foreground-gdk", &quote_colors[2],
343 364 hiro
                                           NULL);
344 364 hiro
        textview->link_tag =
345 364 hiro
                gtk_text_buffer_create_tag(buffer, "link",
346 364 hiro
                                           "foreground-gdk", &uri_color,
347 364 hiro
                                           NULL);
348 364 hiro
        textview->hover_link_tag =
349 364 hiro
                gtk_text_buffer_create_tag(buffer, "hover-link",
350 364 hiro
                                           "foreground-gdk", &uri_color,
351 364 hiro
                                           "underline", PANGO_UNDERLINE_SINGLE,
352 364 hiro
                                           NULL);
353 364 hiro
354 1 hiro
        gtk_text_buffer_create_tag(buffer, "emphasis",
355 1 hiro
                                   "foreground-gdk", &emphasis_color,
356 1 hiro
                                   NULL);
357 1 hiro
#if USE_GPGME
358 1 hiro
        gtk_text_buffer_create_tag(buffer, "good-signature",
359 1 hiro
                                   "foreground-gdk", &good_sig_color,
360 1 hiro
                                   NULL);
361 56 hiro
        gtk_text_buffer_create_tag(buffer, "untrusted-signature",
362 56 hiro
                                   "foreground-gdk", &untrusted_sig_color,
363 56 hiro
                                   NULL);
364 1 hiro
        gtk_text_buffer_create_tag(buffer, "bad-signature",
365 1 hiro
                                   "foreground-gdk", &bad_sig_color,
366 1 hiro
                                   NULL);
367 1 hiro
        gtk_text_buffer_create_tag(buffer, "nocheck-signature",
368 1 hiro
                                   "foreground-gdk", &nocheck_sig_color,
369 1 hiro
                                   NULL);
370 1 hiro
#endif /* USE_GPGME */
371 1 hiro
}
372 1 hiro
373 1 hiro
void textview_init(TextView *textview)
374 1 hiro
{
375 101 hiro
        if (!hand_cursor)
376 101 hiro
                hand_cursor = gdk_cursor_new(GDK_HAND2);
377 101 hiro
        if (!regular_cursor)
378 101 hiro
                regular_cursor = gdk_cursor_new(GDK_XTERM);
379 101 hiro
380 364 hiro
        textview_create_tags(textview);
381 269 hiro
        textview_reflect_prefs(textview);
382 1 hiro
        textview_set_all_headers(textview, FALSE);
383 1 hiro
        textview_set_font(textview, NULL);
384 1 hiro
}
385 1 hiro
386 364 hiro
static void textview_update_message_colors(void)
387 1 hiro
{
388 1 hiro
        GdkColor black = {0, 0, 0, 0};
389 1 hiro
390 1 hiro
        if (prefs_common.enable_color) {
391 364 hiro
                /* grab the quote colors, converting from an int to a
392 364 hiro
                   GdkColor */
393 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
394 1 hiro
                                               &quote_colors[0]);
395 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
396 1 hiro
                                               &quote_colors[1]);
397 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
398 1 hiro
                                               &quote_colors[2]);
399 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
400 1 hiro
                                               &uri_color);
401 1 hiro
        } else {
402 1 hiro
                quote_colors[0] = quote_colors[1] = quote_colors[2] =
403 1 hiro
                        uri_color = emphasis_color = black;
404 1 hiro
        }
405 1 hiro
}
406 1 hiro
407 364 hiro
static void textview_update_tags(TextView *textview)
408 364 hiro
{
409 364 hiro
        g_object_set(textview->quote0_tag, "foreground-gdk", &quote_colors[0],
410 364 hiro
                     NULL);
411 364 hiro
        g_object_set(textview->quote1_tag, "foreground-gdk", &quote_colors[1],
412 364 hiro
                     NULL);
413 364 hiro
        g_object_set(textview->quote2_tag, "foreground-gdk", &quote_colors[2],
414 364 hiro
                     NULL);
415 364 hiro
        g_object_set(textview->link_tag, "foreground-gdk", &uri_color, NULL);
416 364 hiro
        g_object_set(textview->hover_link_tag, "foreground-gdk", &uri_color,
417 364 hiro
                     NULL);
418 364 hiro
}
419 364 hiro
420 269 hiro
void textview_reflect_prefs(TextView *textview)
421 269 hiro
{
422 269 hiro
        textview_update_message_colors();
423 364 hiro
        textview_update_tags(textview);
424 269 hiro
        gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(textview->text),
425 269 hiro
                                         prefs_common.textview_cursor_visible);
426 269 hiro
}
427 269 hiro
428 1 hiro
void textview_show_message(TextView *textview, MimeInfo *mimeinfo,
429 1 hiro
                           const gchar *file)
430 1 hiro
{
431 342 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
432 342 hiro
        GtkTextBuffer *buffer;
433 342 hiro
        GtkTextMark *mark;
434 342 hiro
        GtkTextIter iter;
435 1 hiro
        FILE *fp;
436 1 hiro
        const gchar *charset = NULL;
437 1 hiro
        GPtrArray *headers = NULL;
438 1 hiro
439 342 hiro
        buffer = gtk_text_view_get_buffer(text);
440 342 hiro
441 478 hiro
        if ((fp = g_fopen(file, "rb")) == NULL) {
442 1 hiro
                FILE_OP_ERROR(file, "fopen");
443 1 hiro
                return;
444 1 hiro
        }
445 1 hiro
446 1 hiro
        if (textview->messageview->forced_charset)
447 1 hiro
                charset = textview->messageview->forced_charset;
448 1 hiro
        else if (prefs_common.force_charset)
449 1 hiro
                charset = prefs_common.force_charset;
450 1 hiro
        else if (mimeinfo->charset)
451 1 hiro
                charset = mimeinfo->charset;
452 1 hiro
453 1 hiro
        textview_set_font(textview, charset);
454 1 hiro
        textview_clear(textview);
455 1 hiro
456 1 hiro
        if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) perror("fseek");
457 144 hiro
        headers = textview_scan_header(textview, fp, charset);
458 1 hiro
        if (headers) {
459 1 hiro
                textview_show_header(textview, headers);
460 1 hiro
                procheader_header_array_destroy(headers);
461 1 hiro
462 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter);
463 1 hiro
                textview->body_pos = gtk_text_iter_get_offset(&iter);
464 1 hiro
        }
465 1 hiro
466 1 hiro
        textview_add_parts(textview, mimeinfo, fp);
467 1 hiro
468 1 hiro
        fclose(fp);
469 1 hiro
470 1 hiro
        textview_set_position(textview, 0);
471 342 hiro
        mark = gtk_text_buffer_get_insert(buffer);
472 342 hiro
        gtk_text_view_scroll_mark_onscreen(text, mark);
473 1 hiro
}
474 1 hiro
475 1 hiro
void textview_show_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
476 1 hiro
{
477 1 hiro
        gchar buf[BUFFSIZE];
478 1 hiro
        const gchar *boundary = NULL;
479 1 hiro
        gint boundary_len = 0;
480 1 hiro
        const gchar *charset = NULL;
481 1 hiro
        GPtrArray *headers = NULL;
482 1 hiro
        gboolean is_rfc822_part = FALSE;
483 1 hiro
484 1 hiro
        g_return_if_fail(mimeinfo != NULL);
485 1 hiro
        g_return_if_fail(fp != NULL);
486 1 hiro
487 1 hiro
        if (mimeinfo->mime_type == MIME_MULTIPART) {
488 1 hiro
                textview_clear(textview);
489 1 hiro
                textview_add_parts(textview, mimeinfo, fp);
490 1 hiro
                return;
491 1 hiro
        }
492 1 hiro
493 1 hiro
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
494 1 hiro
                boundary = mimeinfo->parent->boundary;
495 1 hiro
                boundary_len = strlen(boundary);
496 1 hiro
        }
497 1 hiro
498 144 hiro
        if (textview->messageview->forced_charset)
499 144 hiro
                charset = textview->messageview->forced_charset;
500 144 hiro
        else if (prefs_common.force_charset)
501 144 hiro
                charset = prefs_common.force_charset;
502 144 hiro
        else if (mimeinfo->charset)
503 144 hiro
                charset = mimeinfo->charset;
504 144 hiro
505 1 hiro
        if (!boundary && mimeinfo->mime_type == MIME_TEXT) {
506 1 hiro
                if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
507 1 hiro
                        perror("fseek");
508 144 hiro
                headers = textview_scan_header(textview, fp, charset);
509 1 hiro
        } else {
510 1 hiro
                if (mimeinfo->mime_type == MIME_TEXT && mimeinfo->parent) {
511 1 hiro
                        glong fpos;
512 1 hiro
                        MimeInfo *parent = mimeinfo->parent;
513 1 hiro
514 1 hiro
                        while (parent->parent) {
515 1 hiro
                                if (parent->main &&
516 1 hiro
                                    parent->main->mime_type ==
517 1 hiro
                                        MIME_MESSAGE_RFC822)
518 1 hiro
                                        break;
519 1 hiro
                                parent = parent->parent;
520 1 hiro
                        }
521 1 hiro
522 1 hiro
                        if ((fpos = ftell(fp)) < 0)
523 1 hiro
                                perror("ftell");
524 1 hiro
                        else if (fseek(fp, parent->fpos, SEEK_SET) < 0)
525 1 hiro
                                perror("fseek");
526 1 hiro
                        else {
527 144 hiro
                                headers = textview_scan_header
528 144 hiro
                                        (textview, fp, charset);
529 1 hiro
                                if (fseek(fp, fpos, SEEK_SET) < 0)
530 1 hiro
                                        perror("fseek");
531 1 hiro
                        }
532 1 hiro
                }
533 1 hiro
                /* skip MIME part headers */
534 1 hiro
                while (fgets(buf, sizeof(buf), fp) != NULL)
535 1 hiro
                        if (buf[0] == '\r' || buf[0] == '\n') break;
536 1 hiro
        }
537 1 hiro
538 1 hiro
        /* display attached RFC822 single text message */
539 1 hiro
        if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
540 1 hiro
                if (headers) procheader_header_array_destroy(headers);
541 1 hiro
                if (!mimeinfo->sub) {
542 1 hiro
                        textview_clear(textview);
543 1 hiro
                        return;
544 1 hiro
                }
545 144 hiro
                headers = textview_scan_header(textview, fp, charset);
546 1 hiro
                mimeinfo = mimeinfo->sub;
547 1 hiro
                is_rfc822_part = TRUE;
548 1 hiro
        }
549 1 hiro
550 1 hiro
        textview_set_font(textview, charset);
551 1 hiro
552 1 hiro
        textview_clear(textview);
553 1 hiro
554 1 hiro
        if (headers) {
555 1 hiro
                GtkTextView *text = GTK_TEXT_VIEW(textview->text);
556 1 hiro
                GtkTextBuffer *buffer;
557 1 hiro
                GtkTextIter iter;
558 1 hiro
559 1 hiro
                textview_show_header(textview, headers);
560 1 hiro
                procheader_header_array_destroy(headers);
561 1 hiro
562 1 hiro
                buffer = gtk_text_view_get_buffer(text);
563 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter);
564 1 hiro
                textview->body_pos = gtk_text_iter_get_offset(&iter);
565 1 hiro
                if (!mimeinfo->main)
566 1 hiro
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
567 1 hiro
        }
568 1 hiro
569 1 hiro
        if (mimeinfo->mime_type == MIME_MULTIPART || is_rfc822_part)
570 1 hiro
                textview_add_parts(textview, mimeinfo, fp);
571 1 hiro
        else
572 1 hiro
                textview_write_body(textview, mimeinfo, fp, charset);
573 1 hiro
}
574 1 hiro
575 1 hiro
static void textview_add_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
576 1 hiro
{
577 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
578 1 hiro
        GtkTextBuffer *buffer;
579 1 hiro
        GtkTextIter iter;
580 1 hiro
        gchar buf[BUFFSIZE];
581 1 hiro
        const gchar *boundary = NULL;
582 1 hiro
        gint boundary_len = 0;
583 1 hiro
        const gchar *charset = NULL;
584 1 hiro
        GPtrArray *headers = NULL;
585 1 hiro
586 1 hiro
        g_return_if_fail(mimeinfo != NULL);
587 1 hiro
        g_return_if_fail(fp != NULL);
588 1 hiro
589 1 hiro
        buffer = gtk_text_view_get_buffer(text);
590 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
591 1 hiro
592 1 hiro
        if (mimeinfo->mime_type == MIME_MULTIPART) return;
593 1 hiro
594 1 hiro
        if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) {
595 1 hiro
                perror("fseek");
596 1 hiro
                return;
597 1 hiro
        }
598 1 hiro
599 1 hiro
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
600 1 hiro
                boundary = mimeinfo->parent->boundary;
601 1 hiro
                boundary_len = strlen(boundary);
602 1 hiro
        }
603 1 hiro
604 1 hiro
        while (fgets(buf, sizeof(buf), fp) != NULL)
605 1 hiro
                if (buf[0] == '\r' || buf[0] == '\n') break;
606 1 hiro
607 144 hiro
        if (textview->messageview->forced_charset)
608 144 hiro
                charset = textview->messageview->forced_charset;
609 144 hiro
        else if (prefs_common.force_charset)
610 144 hiro
                charset = prefs_common.force_charset;
611 144 hiro
        else if (mimeinfo->charset)
612 144 hiro
                charset = mimeinfo->charset;
613 144 hiro
614 1 hiro
        if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
615 144 hiro
                headers = textview_scan_header(textview, fp, charset);
616 1 hiro
                if (headers) {
617 1 hiro
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
618 1 hiro
                        textview_show_header(textview, headers);
619 1 hiro
                        procheader_header_array_destroy(headers);
620 1 hiro
                }
621 1 hiro
                return;
622 1 hiro
        }
623 1 hiro
624 1 hiro
#if USE_GPGME
625 1 hiro
        if (mimeinfo->sigstatus)
626 1 hiro
                g_snprintf(buf, sizeof(buf), "\n[%s (%s)]\n",
627 1 hiro
                           mimeinfo->content_type, mimeinfo->sigstatus);
628 1 hiro
        else
629 1 hiro
#endif
630 1 hiro
        if (mimeinfo->filename || mimeinfo->name)
631 427 hiro
                g_snprintf(buf, sizeof(buf), "\n[%s  %s (%s)]\n",
632 1 hiro
                           mimeinfo->filename ? mimeinfo->filename :
633 1 hiro
                           mimeinfo->name,
634 427 hiro
                           mimeinfo->content_type,
635 427 hiro
                           to_human_readable(mimeinfo->content_size));
636 1 hiro
        else
637 427 hiro
                g_snprintf(buf, sizeof(buf), "\n[%s (%s)]\n",
638 427 hiro
                           mimeinfo->content_type,
639 427 hiro
                           to_human_readable(mimeinfo->content_size));
640 1 hiro
641 1 hiro
#if USE_GPGME
642 1 hiro
        if (mimeinfo->sigstatus) {
643 1 hiro
                const gchar *color;
644 1 hiro
                if (!strcmp(mimeinfo->sigstatus, _("Good signature")))
645 1 hiro
                        color = "good-signature";
646 56 hiro
                else if (!strcmp(mimeinfo->sigstatus, _("Valid signature (untrusted key)")))
647 56 hiro
                        color = "untrusted-signature";
648 1 hiro
                else if (!strcmp(mimeinfo->sigstatus, _("BAD signature")))
649 1 hiro
                        color = "bad-signature";
650 1 hiro
                else
651 1 hiro
                        color = "nocheck-signature";
652 1 hiro
                gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, buf, -1,
653 1 hiro
                                                         color, NULL);
654 1 hiro
        } else
655 1 hiro
#endif
656 1 hiro
        if (mimeinfo->mime_type != MIME_TEXT &&
657 1 hiro
            mimeinfo->mime_type != MIME_TEXT_HTML) {
658 500 hiro
                gtk_text_buffer_insert_with_tags_by_name
659 500 hiro
                        (buffer, &iter, buf, -1, "mimepart", NULL);
660 97 hiro
                if (mimeinfo->mime_type == MIME_IMAGE &&
661 97 hiro
                    prefs_common.inline_image) {
662 97 hiro
                        GdkPixbuf *pixbuf;
663 97 hiro
                        GError *error = NULL;
664 97 hiro
                        gchar *filename;
665 162 hiro
                        RemoteURI *uri;
666 162 hiro
                        gchar *uri_str;
667 97 hiro
668 97 hiro
                        filename = procmime_get_tmp_file_name(mimeinfo);
669 97 hiro
                        if (procmime_get_part_fp(filename, fp, mimeinfo) < 0) {
670 97 hiro
                                g_warning("Can't get the image file.");
671 97 hiro
                                g_free(filename);
672 97 hiro
                                return;
673 97 hiro
                        }
674 97 hiro
675 97 hiro
                        pixbuf = gdk_pixbuf_new_from_file(filename, &error);
676 97 hiro
                        if (error != NULL) {
677 97 hiro
                                g_warning("%s\n", error->message);
678 97 hiro
                                g_error_free(error);
679 97 hiro
                        }
680 97 hiro
                        if (!pixbuf) {
681 97 hiro
                                g_warning("Can't load the image.");
682 162 hiro
                                g_free(filename);
683 97 hiro
                                return;
684 97 hiro
                        }
685 97 hiro
686 155 hiro
                        if (prefs_common.resize_image) {
687 155 hiro
                                GdkPixbuf *scaled;
688 155 hiro
689 155 hiro
                                scaled = imageview_get_resized_pixbuf
690 155 hiro
                                        (pixbuf, textview->text, 8);
691 155 hiro
                                g_object_unref(pixbuf);
692 155 hiro
                                pixbuf = scaled;
693 155 hiro
                        }
694 155 hiro
695 162 hiro
                        uri_str = g_filename_to_uri(filename, NULL, NULL);
696 162 hiro
                        if (uri_str) {
697 162 hiro
                                uri = g_new(RemoteURI, 1);
698 162 hiro
                                uri->uri = uri_str;
699 162 hiro
                                uri->filename =
700 162 hiro
                                        procmime_get_part_file_name(mimeinfo);
701 162 hiro
                                uri->start = gtk_text_iter_get_offset(&iter);
702 162 hiro
                                uri->end = uri->start + 1;
703 162 hiro
                                textview->uri_list =
704 162 hiro
                                        g_slist_append(textview->uri_list, uri);
705 162 hiro
                        }
706 97 hiro
                        gtk_text_buffer_insert_pixbuf(buffer, &iter, pixbuf);
707 97 hiro
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
708 97 hiro
709 155 hiro
                        g_object_unref(pixbuf);
710 162 hiro
                        g_free(filename);
711 97 hiro
                }
712 1 hiro
        } else {
713 1 hiro
                if (!mimeinfo->main &&
714 1 hiro
                    mimeinfo->parent &&
715 1 hiro
                    mimeinfo->parent->children != mimeinfo)
716 500 hiro
                        gtk_text_buffer_insert_with_tags_by_name
717 500 hiro
                                (buffer, &iter, buf, -1, "mimepart", NULL);
718 1 hiro
                else
719 1 hiro
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
720 1 hiro
                textview_write_body(textview, mimeinfo, fp, charset);
721 1 hiro
        }
722 1 hiro
}
723 1 hiro
724 1 hiro
static void textview_add_parts(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
725 1 hiro
{
726 1 hiro
        gint level;
727 1 hiro
728 1 hiro
        g_return_if_fail(mimeinfo != NULL);
729 1 hiro
        g_return_if_fail(fp != NULL);
730 1 hiro
731 1 hiro
        level = mimeinfo->level;
732 1 hiro
733 1 hiro
        for (;;) {
734 1 hiro
                textview_add_part(textview, mimeinfo, fp);
735 1 hiro
                if (mimeinfo->parent && mimeinfo->parent->content_type &&
736 333 hiro
                    !g_ascii_strcasecmp(mimeinfo->parent->content_type,
737 333 hiro
                                        "multipart/alternative"))
738 1 hiro
                        mimeinfo = mimeinfo->parent->next;
739 1 hiro
                else
740 1 hiro
                        mimeinfo = procmime_mimeinfo_next(mimeinfo);
741 1 hiro
                if (!mimeinfo || mimeinfo->level <= level)
742 1 hiro
                        break;
743 1 hiro
        }
744 1 hiro
}
745 1 hiro
746 1 hiro
void textview_show_error(TextView *textview)
747 1 hiro
{
748 1 hiro
        GtkTextBuffer *buffer;
749 1 hiro
        GtkTextIter iter;
750 1 hiro
751 1 hiro
        textview_set_font(textview, NULL);
752 1 hiro
        textview_clear(textview);
753 1 hiro
754 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
755 1 hiro
        gtk_text_buffer_get_start_iter(buffer, &iter);
756 135 hiro
        gtk_text_buffer_insert(buffer, &iter,
757 135 hiro
                               _("This message can't be displayed.\n"), -1);
758 1 hiro
}
759 1 hiro
760 1 hiro
static void textview_write_body(TextView *textview, MimeInfo *mimeinfo,
761 1 hiro
                                FILE *fp, const gchar *charset)
762 1 hiro
{
763 1 hiro
        FILE *tmpfp;
764 1 hiro
        gchar buf[BUFFSIZE];
765 1 hiro
        CodeConverter *conv;
766 1 hiro
767 129 hiro
        conv = conv_code_converter_new(charset, NULL);
768 1 hiro
769 1 hiro
        tmpfp = procmime_decode_content(NULL, fp, mimeinfo);
770 1 hiro
        if (tmpfp) {
771 177 hiro
                if (mimeinfo->mime_type == MIME_TEXT_HTML &&
772 177 hiro
                    prefs_common.render_html)
773 1 hiro
                        textview_show_html(textview, tmpfp, conv);
774 1 hiro
                else
775 1 hiro
                        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
776 1 hiro
                                textview_write_line(textview, buf, conv);
777 1 hiro
                fclose(tmpfp);
778 1 hiro
        }
779 1 hiro
780 1 hiro
        conv_code_converter_destroy(conv);
781 1 hiro
}
782 1 hiro
783 1 hiro
static void textview_show_html(TextView *textview, FILE *fp,
784 1 hiro
                               CodeConverter *conv)
785 1 hiro
{
786 1 hiro
        HTMLParser *parser;
787 184 hiro
        const gchar *str;
788 1 hiro
789 1 hiro
        parser = html_parser_new(fp, conv);
790 1 hiro
        g_return_if_fail(parser != NULL);
791 1 hiro
792 1 hiro
        while ((str = html_parse(parser)) != NULL) {
793 1 hiro
                if (parser->href != NULL)
794 1 hiro
                        textview_write_link(textview, str, parser->href, NULL);
795 1 hiro
                else
796 1 hiro
                        textview_write_line(textview, str, NULL);
797 1 hiro
        }
798 184 hiro
        textview_write_line(textview, "\n", NULL);
799 184 hiro
800 1 hiro
        html_parser_destroy(parser);
801 1 hiro
}
802 1 hiro
803 1 hiro
/* get_uri_part() - retrieves a URI starting from scanpos.
804 1 hiro
                    Returns TRUE if succesful */
805 1 hiro
static gboolean get_uri_part(const gchar *start, const gchar *scanpos,
806 1 hiro
                             const gchar **bp, const gchar **ep)
807 1 hiro
{
808 1 hiro
        const gchar *ep_;
809 1 hiro
810 1 hiro
        g_return_val_if_fail(start != NULL, FALSE);
811 1 hiro
        g_return_val_if_fail(scanpos != NULL, FALSE);
812 1 hiro
        g_return_val_if_fail(bp != NULL, FALSE);
813 1 hiro
        g_return_val_if_fail(ep != NULL, FALSE);
814 1 hiro
815 1 hiro
        *bp = scanpos;
816 1 hiro
817 1 hiro
        /* find end point of URI */
818 1 hiro
        for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
819 1 hiro
                if (!isgraph(*(const guchar *)ep_) ||
820 1 hiro
                    !isascii(*(const guchar *)ep_) ||
821 416 hiro
                    strchr("()<>{}[]\"", *ep_))
822 1 hiro
                        break;
823 1 hiro
        }
824 1 hiro
825 1 hiro
        /* no punctuation at end of string */
826 1 hiro
827 1 hiro
        /* FIXME: this stripping of trailing punctuations may bite with other URIs.
828 1 hiro
         * should pass some URI type to this function and decide on that whether
829 1 hiro
         * to perform punctuation stripping */
830 1 hiro
831 1 hiro
#define IS_REAL_PUNCT(ch)        (ispunct(ch) && ((ch) != '/'))
832 1 hiro
833 1 hiro
        for (; ep_ - 1 > scanpos + 1 &&
834 1 hiro
               IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
835 1 hiro
             ep_--)
836 1 hiro
                ;
837 1 hiro
838 1 hiro
#undef IS_REAL_PUNCT
839 1 hiro
840 1 hiro
        *ep = ep_;
841 1 hiro
842 1 hiro
        return TRUE;
843 1 hiro
}
844 1 hiro
845 1 hiro
static gchar *make_uri_string(const gchar *bp, const gchar *ep)
846 1 hiro
{
847 1 hiro
        return g_strndup(bp, ep - bp);
848 1 hiro
}
849 1 hiro
850 1 hiro
/* valid mail address characters */
851 1 hiro
#define IS_RFC822_CHAR(ch) \
852 1 hiro
        (isascii(ch) && \
853 1 hiro
         (ch) > 32   && \
854 1 hiro
         (ch) != 127 && \
855 457 hiro
         !g_ascii_isspace(ch) && \
856 1 hiro
         !strchr("(),;<>\"", (ch)))
857 1 hiro
858 1 hiro
/* alphabet and number within 7bit ASCII */
859 457 hiro
#define IS_ASCII_ALNUM(ch)        (isascii(ch) && g_ascii_isalnum(ch))
860 1 hiro
861 1 hiro
/* get_email_part() - retrieves an email address. Returns TRUE if succesful */
862 1 hiro
static gboolean get_email_part(const gchar *start, const gchar *scanpos,
863 1 hiro
                               const gchar **bp, const gchar **ep)
864 1 hiro
{
865 1 hiro
        /* more complex than the uri part because we need to scan back and forward starting from
866 1 hiro
         * the scan position. */
867 1 hiro
        gboolean result = FALSE;
868 1 hiro
        const gchar *bp_;
869 1 hiro
        const gchar *ep_;
870 1 hiro
871 1 hiro
        g_return_val_if_fail(start != NULL, FALSE);
872 1 hiro
        g_return_val_if_fail(scanpos != NULL, FALSE);
873 1 hiro
        g_return_val_if_fail(bp != NULL, FALSE);
874 1 hiro
        g_return_val_if_fail(ep != NULL, FALSE);
875 1 hiro
876 1 hiro
        /* scan start of address */
877 1 hiro
        for (bp_ = scanpos - 1;
878 1 hiro
             bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
879 1 hiro
                ;
880 1 hiro
881 1 hiro
        /* TODO: should start with an alnum? */
882 1 hiro
        bp_++;
883 1 hiro
        for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
884 1 hiro
                ;
885 1 hiro
886 1 hiro
        if (bp_ != scanpos) {
887 1 hiro
                /* scan end of address */
888 1 hiro
                for (ep_ = scanpos + 1;
889 1 hiro
                     *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
890 1 hiro
                        ;
891 1 hiro
892 1 hiro
                /* TODO: really should terminate with an alnum? */
893 1 hiro
                for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
894 1 hiro
                     --ep_)
895 1 hiro
                        ;
896 1 hiro
                ep_++;
897 1 hiro
898 1 hiro
                if (ep_ > scanpos + 1) {
899 1 hiro
                        *ep = ep_;
900 1 hiro
                        *bp = bp_;
901 1 hiro
                        result = TRUE;
902 1 hiro
                }
903 1 hiro
        }
904 1 hiro
905 1 hiro
        return result;
906 1 hiro
}
907 1 hiro
908 1 hiro
#undef IS_ASCII_ALNUM
909 1 hiro
#undef IS_RFC822_CHAR
910 1 hiro
911 1 hiro
static gchar *make_email_string(const gchar *bp, const gchar *ep)
912 1 hiro
{
913 1 hiro
        /* returns a mailto: URI; mailto: is also used to detect the
914 1 hiro
         * uri type later on in the button_pressed signal handler */
915 1 hiro
        gchar *tmp;
916 1 hiro
        gchar *result;
917 1 hiro
918 1 hiro
        tmp = g_strndup(bp, ep - bp);
919 1 hiro
        result = g_strconcat("mailto:", tmp, NULL);
920 1 hiro
        g_free(tmp);
921 1 hiro
922 1 hiro
        return result;
923 1 hiro
}
924 1 hiro
925 1 hiro
#define ADD_TXT_POS(bp_, ep_, pti_) \
926 1 hiro
        if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
927 1 hiro
                last = last->next; \
928 1 hiro
                last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
929 1 hiro
                last->next = NULL; \
930 1 hiro
        } else { \
931 1 hiro
                g_warning("alloc error scanning URIs\n"); \
932 1 hiro
                gtk_text_buffer_insert_with_tags_by_name \
933 1 hiro
                        (buffer, &iter, linebuf, -1, fg_tag, NULL); \
934 1 hiro
                return; \
935 1 hiro
        }
936 1 hiro
937 1 hiro
/* textview_make_clickable_parts() - colorizes clickable parts */
938 1 hiro
static void textview_make_clickable_parts(TextView *textview,
939 1 hiro
                                          const gchar *fg_tag,
940 1 hiro
                                          const gchar *uri_tag,
941 1 hiro
                                          const gchar *linebuf)
942 1 hiro
{
943 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
944 1 hiro
        GtkTextBuffer *buffer;
945 1 hiro
        GtkTextIter iter;
946 1 hiro
947 1 hiro
        /* parse table - in order of priority */
948 1 hiro
        struct table {
949 1 hiro
                const gchar *needle; /* token */
950 1 hiro
951 1 hiro
                /* token search function */
952 1 hiro
                gchar    *(*search)        (const gchar *haystack,
953 1 hiro
                                         const gchar *needle);
954 1 hiro
                /* part parsing function */
955 1 hiro
                gboolean  (*parse)        (const gchar *start,
956 1 hiro
                                         const gchar *scanpos,
957 1 hiro
                                         const gchar **bp_,
958 1 hiro
                                         const gchar **ep_);
959 1 hiro
                /* part to URI function */
960 1 hiro
                gchar    *(*build_uri)        (const gchar *bp,
961 1 hiro
                                         const gchar *ep);
962 1 hiro
        };
963 1 hiro
964 1 hiro
        static struct table parser[] = {
965 1 hiro
                {"http://",  strcasestr, get_uri_part,   make_uri_string},
966 1 hiro
                {"https://", strcasestr, get_uri_part,   make_uri_string},
967 1 hiro
                {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
968 1 hiro
                {"www.",     strcasestr, get_uri_part,   make_uri_string},
969 1 hiro
                {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
970 1 hiro
                {"@",        strcasestr, get_email_part, make_email_string}
971 1 hiro
        };
972 1 hiro
        const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
973 1 hiro
974 153 hiro
        /* flags for search optimization */
975 153 hiro
        gboolean do_search[] = {TRUE, TRUE, TRUE, TRUE, TRUE, TRUE};
976 153 hiro
977 1 hiro
        gint  n;
978 1 hiro
        const gchar *walk, *bp, *ep;
979 1 hiro
980 1 hiro
        struct txtpos {
981 1 hiro
                const gchar        *bp, *ep;        /* text position */
982 1 hiro
                gint                 pti;                /* index in parse table */
983 1 hiro
                struct txtpos        *next;                /* next */
984 1 hiro
        } head = {NULL, NULL, 0,  NULL}, *last = &head;
985 1 hiro
986 1 hiro
        buffer = gtk_text_view_get_buffer(text);
987 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
988 1 hiro
989 1 hiro
        /* parse for clickable parts, and build a list of begin and
990 1 hiro
           end positions  */
991 1 hiro
        for (walk = linebuf, n = 0;;) {
992 1 hiro
                gint last_index = PARSE_ELEMS;
993 153 hiro
                const gchar *scanpos = NULL;
994 1 hiro
995 1 hiro
                /* FIXME: this looks phony. scanning for anything in the
996 1 hiro
                   parse table */
997 1 hiro
                for (n = 0; n < PARSE_ELEMS; n++) {
998 153 hiro
                        const gchar *tmp;
999 1 hiro
1000 153 hiro
                        if (do_search[n]) {
1001 153 hiro
                                tmp = parser[n].search(walk, parser[n].needle);
1002 153 hiro
                                if (tmp) {
1003 153 hiro
                                        if (scanpos == NULL || tmp < scanpos) {
1004 153 hiro
                                                scanpos = tmp;
1005 153 hiro
                                                last_index = n;
1006 153 hiro
                                        }
1007 153 hiro
                                } else
1008 153 hiro
                                        do_search[n] = FALSE;
1009 153 hiro
                        }
1010 1 hiro
                }
1011 1 hiro
1012 1 hiro
                if (scanpos) {
1013 1 hiro
                        /* check if URI can be parsed */
1014 1 hiro
                        if (parser[last_index].parse(walk, scanpos, &bp, &ep)
1015 1 hiro
                            && (ep - bp - 1) > strlen(parser[last_index].needle)) {
1016 1 hiro
                                        ADD_TXT_POS(bp, ep, last_index);
1017 1 hiro
                                        walk = ep;
1018 1 hiro
                        } else
1019 1 hiro
                                walk = scanpos +
1020 1 hiro
                                        strlen(parser[last_index].needle);
1021 1 hiro
                } else
1022 1 hiro
                        break;
1023 1 hiro
        }
1024 1 hiro
1025 1 hiro
        /* colorize this line */
1026 1 hiro
        if (head.next) {
1027 1 hiro
                const gchar *normal_text = linebuf;
1028 1 hiro
1029 1 hiro
                /* insert URIs */
1030 1 hiro
                for (last = head.next; last != NULL;
1031 1 hiro
                     normal_text = last->ep, last = last->next) {
1032 1 hiro
                        RemoteURI *uri;
1033 1 hiro
1034 1 hiro
                        uri = g_new(RemoteURI, 1);
1035 1 hiro
                        if (last->bp - normal_text > 0)
1036 1 hiro
                                gtk_text_buffer_insert_with_tags_by_name
1037 1 hiro
                                        (buffer, &iter,
1038 1 hiro
                                         normal_text,
1039 1 hiro
                                         last->bp - normal_text,
1040 1 hiro
                                         fg_tag, NULL);
1041 1 hiro
                        uri->uri = parser[last->pti].build_uri(last->bp,
1042 1 hiro
                                                               last->ep);
1043 162 hiro
                        uri->filename = NULL;
1044 1 hiro
                        uri->start = gtk_text_iter_get_offset(&iter);
1045 1 hiro
                        gtk_text_buffer_insert_with_tags_by_name
1046 1 hiro
                                (buffer, &iter, last->bp, last->ep - last->bp,
1047 39 hiro
                                 uri_tag, fg_tag, NULL);
1048 1 hiro
                        uri->end = gtk_text_iter_get_offset(&iter);
1049 1 hiro
                        textview->uri_list =
1050 1 hiro
                                g_slist_append(textview->uri_list, uri);
1051 1 hiro
                }
1052 1 hiro
1053 1 hiro
                if (*normal_text)
1054 153 hiro
                        gtkut_text_buffer_insert_with_tag_by_name
1055 153 hiro
                                (buffer, &iter, normal_text, -1, fg_tag);
1056 1 hiro
        } else {
1057 153 hiro
                gtkut_text_buffer_insert_with_tag_by_name
1058 153 hiro
                        (buffer, &iter, linebuf, -1, fg_tag);
1059 1 hiro
        }
1060 1 hiro
}
1061 1 hiro
1062 1 hiro
#undef ADD_TXT_POS
1063 1 hiro
1064 1 hiro
static void textview_write_line(TextView *textview, const gchar *str,
1065 1 hiro
                                CodeConverter *conv)
1066 1 hiro
{
1067 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1068 1 hiro
        GtkTextBuffer *buffer;
1069 1 hiro
        GtkTextIter iter;
1070 180 hiro
        gchar *buf;
1071 153 hiro
        gchar *fg_color = NULL;
1072 1 hiro
        gint quotelevel = -1;
1073 1 hiro
        gchar quote_tag_str[10];
1074 1 hiro
1075 1 hiro
        buffer = gtk_text_view_get_buffer(text);
1076 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
1077 1 hiro
1078 180 hiro
        if (conv) {
1079 180 hiro
                buf = conv_convert(conv, str);
1080 180 hiro
                if (!buf)
1081 185 hiro
                        buf = conv_utf8todisp(str, NULL);
1082 180 hiro
        } else
1083 180 hiro
                buf = g_strdup(str);
1084 1 hiro
1085 1 hiro
        strcrchomp(buf);
1086 1 hiro
        //if (prefs_common.conv_mb_alnum) conv_mb_alnum(buf);
1087 1 hiro
1088 1 hiro
        /* change color of quotation
1089 1 hiro
           >, foo>, _> ... ok, <foo>, foo bar>, foo-> ... ng
1090 1 hiro
           Up to 3 levels of quotations are detected, and each
1091 1 hiro
           level is colored using a different color. */
1092 1 hiro
        if (prefs_common.enable_color && strchr(buf, '>')) {
1093 1 hiro
                quotelevel = get_quote_level(buf);
1094 1 hiro
1095 1 hiro
                /* set up the correct foreground color */
1096 1 hiro
                if (quotelevel > 2) {
1097 1 hiro
                        /* recycle colors */
1098 1 hiro
                        if (prefs_common.recycle_quote_colors)
1099 1 hiro
                                quotelevel %= 3;
1100 1 hiro
                        else
1101 1 hiro
                                quotelevel = 2;
1102 1 hiro
                }
1103 1 hiro
        }
1104 1 hiro
1105 153 hiro
        if (quotelevel != -1) {
1106 1 hiro
                g_snprintf(quote_tag_str, sizeof(quote_tag_str),
1107 1 hiro
                           "quote%d", quotelevel);
1108 1 hiro
                fg_color = quote_tag_str;
1109 1 hiro
        }
1110 1 hiro
1111 1 hiro
        if (prefs_common.enable_color)
1112 1 hiro
                textview_make_clickable_parts(textview, fg_color, "link", buf);
1113 1 hiro
        else
1114 1 hiro
                textview_make_clickable_parts(textview, fg_color, NULL, buf);
1115 180 hiro
1116 180 hiro
        g_free(buf);
1117 1 hiro
}
1118 1 hiro
1119 180 hiro
static void textview_write_link(TextView *textview, const gchar *str,
1120 180 hiro
                                const gchar *uri, CodeConverter *conv)
1121 1 hiro
{
1122 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1123 1 hiro
        GtkTextBuffer *buffer;
1124 1 hiro
        GtkTextIter iter;
1125 180 hiro
        gchar *buf;
1126 1 hiro
        gchar *bufp;
1127 1 hiro
        RemoteURI *r_uri;
1128 1 hiro
1129 290 hiro
        if (!str || *str == '\0')
1130 1 hiro
                return;
1131 290 hiro
        if (!uri)
1132 290 hiro
                return;
1133 1 hiro
1134 1 hiro
        buffer = gtk_text_view_get_buffer(text);
1135 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
1136 1 hiro
1137 180 hiro
        if (conv) {
1138 180 hiro
                buf = conv_convert(conv, str);
1139 180 hiro
                if (!buf)
1140 185 hiro
                        buf = conv_utf8todisp(str, NULL);
1141 180 hiro
        } else
1142 180 hiro
                buf = g_strdup(str);
1143 1 hiro
1144 290 hiro
        if (g_utf8_validate(buf, -1, NULL) == FALSE) {
1145 290 hiro
                g_free(buf);
1146 290 hiro
                return;
1147 290 hiro
        }
1148 290 hiro
1149 1 hiro
        strcrchomp(buf);
1150 1 hiro
1151 290 hiro
        for (bufp = buf; *bufp != '\0'; bufp = g_utf8_next_char(bufp)) {
1152 290 hiro
                gunichar ch;
1153 290 hiro
1154 290 hiro
                ch = g_utf8_get_char(bufp);
1155 290 hiro
                if (!g_unichar_isspace(ch))
1156 290 hiro
                        break;
1157 290 hiro
        }
1158 180 hiro
        if (bufp > buf)
1159 180 hiro
                gtk_text_buffer_insert(buffer, &iter, buf, bufp - buf);
1160 1 hiro
1161 1 hiro
        r_uri = g_new(RemoteURI, 1);
1162 1 hiro
        r_uri->uri = g_strdup(uri);
1163 162 hiro
        r_uri->filename = NULL;
1164 1 hiro
        r_uri->start = gtk_text_iter_get_offset(&iter);
1165 1 hiro
        gtk_text_buffer_insert_with_tags_by_name
1166 1 hiro
                (buffer, &iter, bufp, -1, "link", NULL);
1167 1 hiro
        r_uri->end = gtk_text_iter_get_offset(&iter);
1168 1 hiro
        textview->uri_list = g_slist_append(textview->uri_list, r_uri);
1169 180 hiro
1170 180 hiro
        g_free(buf);
1171 1 hiro
}
1172 1 hiro
1173 1 hiro
void textview_clear(TextView *textview)
1174 1 hiro
{
1175 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1176 1 hiro
        GtkTextBuffer *buffer;
1177 1 hiro
1178 1 hiro
        buffer = gtk_text_view_get_buffer(text);
1179 1 hiro
        gtk_text_buffer_set_text(buffer, "", -1);
1180 1 hiro
1181 1 hiro
        STATUSBAR_POP(textview);
1182 1 hiro
        textview_uri_list_remove_all(textview->uri_list);
1183 1 hiro
        textview->uri_list = NULL;
1184 1 hiro
1185 1 hiro
        textview->body_pos = 0;
1186 1 hiro
}
1187 1 hiro
1188 1 hiro
void textview_destroy(TextView *textview)
1189 1 hiro
{
1190 1 hiro
        textview_uri_list_remove_all(textview->uri_list);
1191 1 hiro
        textview->uri_list = NULL;
1192 1 hiro
        g_free(textview);
1193 1 hiro
}
1194 1 hiro
1195 1 hiro
void textview_set_all_headers(TextView *textview, gboolean all_headers)
1196 1 hiro
{
1197 1 hiro
        textview->show_all_headers = all_headers;
1198 1 hiro
}
1199 1 hiro
1200 1 hiro
void textview_set_font(TextView *textview, const gchar *codeset)
1201 1 hiro
{
1202 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1203 1 hiro
1204 1 hiro
        if (prefs_common.textfont) {
1205 1 hiro
                PangoFontDescription *font_desc;
1206 1 hiro
                font_desc = pango_font_description_from_string
1207 1 hiro
                        (prefs_common.textfont);
1208 1 hiro
                if (font_desc) {
1209 1 hiro
                        gtk_widget_modify_font(textview->text, font_desc);
1210 1 hiro
                        pango_font_description_free(font_desc);
1211 1 hiro
                }
1212 1 hiro
        }
1213 1 hiro
1214 1 hiro
        gtk_text_view_set_pixels_above_lines(text, prefs_common.line_space / 2);
1215 1 hiro
        gtk_text_view_set_pixels_below_lines(text, prefs_common.line_space / 2);
1216 1 hiro
}
1217 1 hiro
1218 1 hiro
void textview_set_position(TextView *textview, gint pos)
1219 1 hiro
{
1220 1 hiro
        GtkTextBuffer *buffer;
1221 1 hiro
        GtkTextIter iter;
1222 1 hiro
1223 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
1224 1 hiro
        gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
1225 1 hiro
        gtk_text_buffer_place_cursor(buffer, &iter);
1226 1 hiro
}
1227 1 hiro
1228 144 hiro
static GPtrArray *textview_scan_header(TextView *textview, FILE *fp,
1229 144 hiro
                                       const gchar *encoding)
1230 1 hiro
{
1231 1 hiro
        gchar buf[BUFFSIZE];
1232 1 hiro
        GPtrArray *headers, *sorted_headers;
1233 1 hiro
        GSList *disphdr_list;
1234 1 hiro
        Header *header;
1235 1 hiro
        gint i;
1236 1 hiro
1237 1 hiro
        g_return_val_if_fail(fp != NULL, NULL);
1238 1 hiro
1239 1 hiro
        if (textview->show_all_headers)
1240 144 hiro
                return procheader_get_header_array_asis(fp, encoding);
1241 1 hiro
1242 1 hiro
        if (!prefs_common.display_header) {
1243 1 hiro
                while (fgets(buf, sizeof(buf), fp) != NULL)
1244 1 hiro
                        if (buf[0] == '\r' || buf[0] == '\n') break;
1245 1 hiro
                return NULL;
1246 1 hiro
        }
1247 1 hiro
1248 144 hiro
        headers = procheader_get_header_array_asis(fp, encoding);
1249 1 hiro
1250 1 hiro
        sorted_headers = g_ptr_array_new();
1251 1 hiro
1252 1 hiro
        for (disphdr_list = prefs_common.disphdr_list; disphdr_list != NULL;
1253 1 hiro
             disphdr_list = disphdr_list->next) {
1254 1 hiro
                DisplayHeaderProp *dp =
1255 1 hiro
                        (DisplayHeaderProp *)disphdr_list->data;
1256 1 hiro
1257 1 hiro
                for (i = 0; i < headers->len; i++) {
1258 1 hiro
                        header = g_ptr_array_index(headers, i);
1259 1 hiro
1260 333 hiro
                        if (!g_ascii_strcasecmp(header->name, dp->name)) {
1261 1 hiro
                                if (dp->hidden)
1262 1 hiro
                                        procheader_header_free(header);
1263 1 hiro
                                else
1264 1 hiro
                                        g_ptr_array_add(sorted_headers, header);
1265 1 hiro
1266 1 hiro
                                g_ptr_array_remove_index(headers, i);
1267 1 hiro
                                i--;
1268 1 hiro
                        }
1269 1 hiro
                }
1270 1 hiro
        }
1271 1 hiro
1272 1 hiro
        if (prefs_common.show_other_header) {
1273 1 hiro
                for (i = 0; i < headers->len; i++) {
1274 1 hiro
                        header = g_ptr_array_index(headers, i);
1275 1 hiro
                        g_ptr_array_add(sorted_headers, header);
1276 1 hiro
                }
1277 1 hiro
                g_ptr_array_free(headers, TRUE);
1278 1 hiro
        } else
1279 1 hiro
                procheader_header_array_destroy(headers);
1280 1 hiro
1281 1 hiro
1282 1 hiro
        return sorted_headers;
1283 1 hiro
}
1284 1 hiro
1285 1 hiro
static void textview_show_header(TextView *textview, GPtrArray *headers)
1286 1 hiro
{
1287 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1288 1 hiro
        GtkTextBuffer *buffer;
1289 1 hiro
        GtkTextIter iter;
1290 1 hiro
        Header *header;
1291 1 hiro
        gint i;
1292 1 hiro
1293 1 hiro
        g_return_if_fail(headers != NULL);
1294 1 hiro
1295 1 hiro
        buffer = gtk_text_view_get_buffer(text);
1296 1 hiro
1297 1 hiro
        for (i = 0; i < headers->len; i++) {
1298 1 hiro
                header = g_ptr_array_index(headers, i);
1299 1 hiro
                g_return_if_fail(header->name != NULL);
1300 1 hiro
1301 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter);
1302 1 hiro
                gtk_text_buffer_insert_with_tags_by_name
1303 1 hiro
                        (buffer, &iter, header->name, -1,
1304 1 hiro
                         "header_title", "header", NULL);
1305 1 hiro
                gtk_text_buffer_insert_with_tags_by_name
1306 1 hiro
                        (buffer, &iter, ":", 1,
1307 1 hiro
                         "header_title", "header", NULL);
1308 1 hiro
1309 333 hiro
                if (!g_ascii_strcasecmp(header->name, "Subject") ||
1310 333 hiro
                    !g_ascii_strcasecmp(header->name, "From")    ||
1311 333 hiro
                    !g_ascii_strcasecmp(header->name, "To")      ||
1312 333 hiro
                    !g_ascii_strcasecmp(header->name, "Cc"))
1313 1 hiro
                        unfold_line(header->body);
1314 1 hiro
1315 1 hiro
                if (prefs_common.enable_color &&
1316 1 hiro
                    (!strncmp(header->name, "X-Mailer", 8) ||
1317 1 hiro
                     !strncmp(header->name, "X-Newsreader", 12)) &&
1318 1 hiro
                    strstr(header->body, "Sylpheed") != NULL) {
1319 1 hiro
                        gtk_text_buffer_get_end_iter(buffer, &iter);
1320 1 hiro
                        gtk_text_buffer_insert_with_tags_by_name
1321 1 hiro
                                (buffer, &iter, header->body, -1,
1322 1 hiro
                                 "header", "emphasis", NULL);
1323 1 hiro
                } else if (prefs_common.enable_color) {
1324 1 hiro
                        textview_make_clickable_parts
1325 1 hiro
                                (textview, "header", "link", header->body);
1326 1 hiro
                } else {
1327 1 hiro
                        textview_make_clickable_parts
1328 1 hiro
                                (textview, "header", NULL, header->body);
1329 1 hiro
                }
1330 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter); //
1331 1 hiro
                gtk_text_buffer_insert_with_tags_by_name
1332 1 hiro
                        (buffer, &iter, "\n", 1, "header", NULL);
1333 1 hiro
        }
1334 1 hiro
}
1335 1 hiro
1336 1 hiro
gboolean textview_search_string(TextView *textview, const gchar *str,
1337 1 hiro
                                gboolean case_sens)
1338 1 hiro
{
1339 90 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1340 90 hiro
        GtkTextBuffer *buffer;
1341 123 hiro
        GtkTextIter iter, match_pos;
1342 90 hiro
        GtkTextMark *mark;
1343 1 hiro
        gint len;
1344 1 hiro
1345 1 hiro
        g_return_val_if_fail(str != NULL, FALSE);
1346 1 hiro
1347 90 hiro
        buffer = gtk_text_view_get_buffer(text);
1348 90 hiro
1349 90 hiro
        len = g_utf8_strlen(str, -1);
1350 1 hiro
        g_return_val_if_fail(len >= 0, FALSE);
1351 1 hiro
1352 304 hiro
        gtk_text_buffer_get_selection_bounds(buffer, NULL, &iter);
1353 1 hiro
1354 123 hiro
        if (gtkut_text_buffer_find(buffer, &iter, str, case_sens,
1355 123 hiro
                                   &match_pos)) {
1356 123 hiro
                GtkTextIter end = match_pos;
1357 123 hiro
1358 123 hiro
                gtk_text_iter_forward_chars(&end, len);
1359 123 hiro
                /* place "insert" at the last character */
1360 123 hiro
                gtk_text_buffer_select_range(buffer, &end, &match_pos);
1361 304 hiro
                mark = gtk_text_buffer_get_insert(buffer);
1362 90 hiro
                gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0);
1363 1 hiro
                return TRUE;
1364 1 hiro
        }
1365 1 hiro
1366 1 hiro
        return FALSE;
1367 1 hiro
}
1368 1 hiro
1369 1 hiro
gboolean textview_search_string_backward(TextView *textview, const gchar *str,
1370 1 hiro
                                         gboolean case_sens)
1371 1 hiro
{
1372 90 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1373 90 hiro
        GtkTextBuffer *buffer;
1374 123 hiro
        GtkTextIter iter, match_pos;
1375 90 hiro
        GtkTextMark *mark;
1376 1 hiro
        gint len;
1377 1 hiro
1378 1 hiro
        g_return_val_if_fail(str != NULL, FALSE);
1379 1 hiro
1380 90 hiro
        buffer = gtk_text_view_get_buffer(text);
1381 90 hiro
1382 123 hiro
        len = g_utf8_strlen(str, -1);
1383 123 hiro
        g_return_val_if_fail(len >= 0, FALSE);
1384 1 hiro
1385 304 hiro
        gtk_text_buffer_get_selection_bounds(buffer, &iter, NULL);
1386 90 hiro
1387 123 hiro
        if (gtkut_text_buffer_find_backward(buffer, &iter, str, case_sens,
1388 123 hiro
                                            &match_pos)) {
1389 123 hiro
                GtkTextIter end = match_pos;
1390 123 hiro
1391 123 hiro
                gtk_text_iter_forward_chars(&end, len);
1392 123 hiro
                gtk_text_buffer_select_range(buffer, &match_pos, &end);
1393 304 hiro
                mark = gtk_text_buffer_get_insert(buffer);
1394 123 hiro
                gtk_text_view_scroll_to_mark(text, mark, 0.0, FALSE, 0.0, 0.0);
1395 123 hiro
                return TRUE;
1396 1 hiro
        }
1397 1 hiro
1398 123 hiro
        return FALSE;
1399 1 hiro
}
1400 1 hiro
1401 1 hiro
void textview_scroll_one_line(TextView *textview, gboolean up)
1402 1 hiro
{
1403 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1404 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1405 1 hiro
        gfloat upper;
1406 1 hiro
1407 1 hiro
        if (prefs_common.enable_smooth_scroll) {
1408 1 hiro
                textview_smooth_scroll_one_line(textview, up);
1409 1 hiro
                return;
1410 1 hiro
        }
1411 1 hiro
1412 1 hiro
        if (!up) {
1413 1 hiro
                upper = vadj->upper - vadj->page_size;
1414 1 hiro
                if (vadj->value < upper) {
1415 7 hiro
                        vadj->value += vadj->step_increment;
1416 1 hiro
                        vadj->value = MIN(vadj->value, upper);
1417 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1418 1 hiro
                                              "value_changed", 0);
1419 1 hiro
                }
1420 1 hiro
        } else {
1421 1 hiro
                if (vadj->value > 0.0) {
1422 7 hiro
                        vadj->value -= vadj->step_increment;
1423 1 hiro
                        vadj->value = MAX(vadj->value, 0.0);
1424 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1425 1 hiro
                                              "value_changed", 0);
1426 1 hiro
                }
1427 1 hiro
        }
1428 1 hiro
}
1429 1 hiro
1430 1 hiro
gboolean textview_scroll_page(TextView *textview, gboolean up)
1431 1 hiro
{
1432 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1433 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1434 1 hiro
        gfloat upper;
1435 1 hiro
        gfloat page_incr;
1436 1 hiro
1437 1 hiro
        if (prefs_common.enable_smooth_scroll)
1438 1 hiro
                return textview_smooth_scroll_page(textview, up);
1439 1 hiro
1440 1 hiro
        if (prefs_common.scroll_halfpage)
1441 1 hiro
                page_incr = vadj->page_increment / 2;
1442 1 hiro
        else
1443 1 hiro
                page_incr = vadj->page_increment;
1444 1 hiro
1445 1 hiro
        if (!up) {
1446 1 hiro
                upper = vadj->upper - vadj->page_size;
1447 1 hiro
                if (vadj->value < upper) {
1448 1 hiro
                        vadj->value += page_incr;
1449 1 hiro
                        vadj->value = MIN(vadj->value, upper);
1450 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1451 1 hiro
                                              "value_changed", 0);
1452 1 hiro
                } else
1453 1 hiro
                        return FALSE;
1454 1 hiro
        } else {
1455 1 hiro
                if (vadj->value > 0.0) {
1456 1 hiro
                        vadj->value -= page_incr;
1457 1 hiro
                        vadj->value = MAX(vadj->value, 0.0);
1458 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1459 1 hiro
                                              "value_changed", 0);
1460 1 hiro
                } else
1461 1 hiro
                        return FALSE;
1462 1 hiro
        }
1463 1 hiro
1464 1 hiro
        return TRUE;
1465 1 hiro
}
1466 1 hiro
1467 1 hiro
static void textview_smooth_scroll_do(TextView *textview,
1468 1 hiro
                                      gfloat old_value, gfloat last_value,
1469 1 hiro
                                      gint step)
1470 1 hiro
{
1471 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1472 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1473 1 hiro
        gint change_value;
1474 1 hiro
        gboolean up;
1475 1 hiro
        gint i;
1476 1 hiro
1477 1 hiro
        if (old_value < last_value) {
1478 1 hiro
                change_value = last_value - old_value;
1479 1 hiro
                up = FALSE;
1480 1 hiro
        } else {
1481 1 hiro
                change_value = old_value - last_value;
1482 1 hiro
                up = TRUE;
1483 1 hiro
        }
1484 1 hiro
1485 1 hiro
        /* gdk_key_repeat_disable(); */
1486 1 hiro
1487 1 hiro
        for (i = step; i <= change_value; i += step) {
1488 1 hiro
                vadj->value = old_value + (up ? -i : i);
1489 1 hiro
                g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0);
1490 1 hiro
        }
1491 1 hiro
1492 1 hiro
        vadj->value = last_value;
1493 1 hiro
        g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0);
1494 1 hiro
1495 1 hiro
        /* gdk_key_repeat_restore(); */
1496 226 hiro
1497 226 hiro
        gtk_widget_queue_draw(GTK_WIDGET(text));
1498 1 hiro
}
1499 1 hiro
1500 1 hiro
static void textview_smooth_scroll_one_line(TextView *textview, gboolean up)
1501 1 hiro
{
1502 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1503 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1504 1 hiro
        gfloat upper;
1505 1 hiro
        gfloat old_value;
1506 1 hiro
        gfloat last_value;
1507 1 hiro
1508 1 hiro
        if (!up) {
1509 1 hiro
                upper = vadj->upper - vadj->page_size;
1510 1 hiro
                if (vadj->value < upper) {
1511 1 hiro
                        old_value = vadj->value;
1512 7 hiro
                        last_value = vadj->value + vadj->step_increment;
1513 1 hiro
                        last_value = MIN(last_value, upper);
1514 1 hiro
1515 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1516 1 hiro
                                                  last_value,
1517 1 hiro
                                                  prefs_common.scroll_step);
1518 1 hiro
                }
1519 1 hiro
        } else {
1520 1 hiro
                if (vadj->value > 0.0) {
1521 1 hiro
                        old_value = vadj->value;
1522 7 hiro
                        last_value = vadj->value - vadj->step_increment;
1523 1 hiro
                        last_value = MAX(last_value, 0.0);
1524 1 hiro
1525 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1526 1 hiro
                                                  last_value,
1527 1 hiro
                                                  prefs_common.scroll_step);
1528 1 hiro
                }
1529 1 hiro
        }
1530 1 hiro
}
1531 1 hiro
1532 1 hiro
static gboolean textview_smooth_scroll_page(TextView *textview, gboolean up)
1533 1 hiro
{
1534 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1535 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1536 1 hiro
        gfloat upper;
1537 1 hiro
        gfloat page_incr;
1538 1 hiro
        gfloat old_value;
1539 1 hiro
        gfloat last_value;
1540 1 hiro
1541 1 hiro
        if (prefs_common.scroll_halfpage)
1542 1 hiro
                page_incr = vadj->page_increment / 2;
1543 1 hiro
        else
1544 1 hiro
                page_incr = vadj->page_increment;
1545 1 hiro
1546 1 hiro
        if (!up) {
1547 1 hiro
                upper = vadj->upper - vadj->page_size;
1548 1 hiro
                if (vadj->value < upper) {
1549 1 hiro
                        old_value = vadj->value;
1550 1 hiro
                        last_value = vadj->value + page_incr;
1551 1 hiro
                        last_value = MIN(last_value, upper);
1552 1 hiro
1553 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1554 1 hiro
                                                  last_value,
1555 1 hiro
                                                  prefs_common.scroll_step);
1556 1 hiro
                } else
1557 1 hiro
                        return FALSE;
1558 1 hiro
        } else {
1559 1 hiro
                if (vadj->value > 0.0) {
1560 1 hiro
                        old_value = vadj->value;
1561 1 hiro
                        last_value = vadj->value - page_incr;
1562 1 hiro
                        last_value = MAX(last_value, 0.0);
1563 1 hiro
1564 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1565 1 hiro
                                                  last_value,
1566 1 hiro
                                                  prefs_common.scroll_step);
1567 1 hiro
                } else
1568 1 hiro
                        return FALSE;
1569 1 hiro
        }
1570 1 hiro
1571 1 hiro
        return TRUE;
1572 1 hiro
}
1573 1 hiro
1574 1 hiro
#define KEY_PRESS_EVENT_STOP() \
1575 101 hiro
        g_signal_stop_emission_by_name(G_OBJECT(widget), "key-press-event");
1576 1 hiro
1577 1 hiro
static gboolean textview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1578 1 hiro
                                     TextView *textview)
1579 1 hiro
{
1580 1 hiro
        SummaryView *summaryview = NULL;
1581 1 hiro
        MessageView *messageview = textview->messageview;
1582 1 hiro
1583 1 hiro
        if (!event) return FALSE;
1584 1 hiro
        if (messageview->mainwin)
1585 1 hiro
                summaryview = messageview->mainwin->summaryview;
1586 1 hiro
1587 1 hiro
        switch (event->keyval) {
1588 1 hiro
        case GDK_Tab:
1589 1 hiro
        case GDK_Home:
1590 1 hiro
        case GDK_Left:
1591 1 hiro
        case GDK_Up:
1592 1 hiro
        case GDK_Right:
1593 1 hiro
        case GDK_Down:
1594 1 hiro
        case GDK_Page_Up:
1595 1 hiro
        case GDK_Page_Down:
1596 1 hiro
        case GDK_End:
1597 1 hiro
        case GDK_Control_L:
1598 1 hiro
        case GDK_Control_R:
1599 1 hiro
                break;
1600 1 hiro
        case GDK_space:
1601 1 hiro
                if (summaryview)
1602 1 hiro
                        summary_pass_key_press_event(summaryview, event);
1603 1 hiro
                else
1604 1 hiro
                        textview_scroll_page
1605 1 hiro
                                (textview,
1606 1 hiro
                                 (event->state &
1607 1 hiro
                                  (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
1608 1 hiro
                break;
1609 1 hiro
        case GDK_BackSpace:
1610 1 hiro
                textview_scroll_page(textview, TRUE);
1611 1 hiro
                break;
1612 1 hiro
        case GDK_Return:
1613 1 hiro
                textview_scroll_one_line
1614 1 hiro
                        (textview, (event->state &
1615 1 hiro
                                    (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
1616 1 hiro
                break;
1617 1 hiro
        case GDK_Delete:
1618 1 hiro
                if (summaryview)
1619 1 hiro
                        summary_pass_key_press_event(summaryview, event);
1620 1 hiro
                break;
1621 165 hiro
        case GDK_Escape:
1622 165 hiro
                if (summaryview && textview == messageview->textview)
1623 237 hiro
                        gtk_widget_grab_focus(summaryview->treeview);
1624 165 hiro
                else if (messageview->type == MVIEW_MIME &&
1625 165 hiro
                         textview == messageview->mimeview->textview)
1626 275 hiro
                        gtk_widget_grab_focus(messageview->mimeview->treeview);
1627 165 hiro
                break;
1628 1 hiro
        case GDK_n:
1629 1 hiro
        case GDK_N:
1630 1 hiro
        case GDK_p:
1631 1 hiro
        case GDK_P:
1632 1 hiro
        case GDK_y:
1633 1 hiro
        case GDK_t:
1634 1 hiro
        case GDK_l:
1635 1 hiro
                if (messageview->type == MVIEW_MIME &&
1636 1 hiro
                    textview == messageview->mimeview->textview) {
1637 1 hiro
                        KEY_PRESS_EVENT_STOP();
1638 1 hiro
                        mimeview_pass_key_press_event(messageview->mimeview,
1639 1 hiro
                                                      event);
1640 1 hiro
                        break;
1641 1 hiro
                }
1642 1 hiro
                /* fall through */
1643 1 hiro
        default:
1644 1 hiro
                if (summaryview &&
1645 1 hiro
                    event->window != messageview->mainwin->window->window) {
1646 1 hiro
                        GdkEventKey tmpev = *event;
1647 1 hiro
1648 1 hiro
                        tmpev.window = messageview->mainwin->window->window;
1649 1 hiro
                        KEY_PRESS_EVENT_STOP();
1650 1 hiro
                        gtk_widget_event(messageview->mainwin->window,
1651 1 hiro
                                         (GdkEvent *)&tmpev);
1652 1 hiro
                }
1653 1 hiro
                break;
1654 1 hiro
        }
1655 1 hiro
1656 1 hiro
        return FALSE;
1657 1 hiro
}
1658 1 hiro
1659 101 hiro
static gboolean textview_get_link_tag_bounds(TextView *textview,
1660 101 hiro
                                             GtkTextIter *iter,
1661 101 hiro
                                             GtkTextIter *start,
1662 101 hiro
                                             GtkTextIter *end)
1663 1 hiro
{
1664 101 hiro
        GSList *tags, *cur;
1665 101 hiro
        gboolean on_link = FALSE;
1666 1 hiro
1667 101 hiro
        tags = gtk_text_iter_get_tags(iter);
1668 101 hiro
        *start = *end = *iter;
1669 1 hiro
1670 101 hiro
        for (cur = tags; cur != NULL; cur = cur->next) {
1671 101 hiro
                GtkTextTag *tag = cur->data;
1672 101 hiro
                gchar *tag_name;
1673 1 hiro
1674 101 hiro
                g_object_get(G_OBJECT(tag), "name", &tag_name, NULL);
1675 101 hiro
                if (tag_name && !strcmp(tag_name, "link")) {
1676 101 hiro
                        if (!gtk_text_iter_begins_tag(start, tag))
1677 101 hiro
                                gtk_text_iter_backward_to_tag_toggle
1678 101 hiro
                                        (start, tag);
1679 101 hiro
                        if (!gtk_text_iter_ends_tag(end, tag))
1680 101 hiro
                                gtk_text_iter_forward_to_tag_toggle(end, tag);
1681 101 hiro
                        on_link = TRUE;
1682 101 hiro
                        g_free(tag_name);
1683 101 hiro
                        break;
1684 101 hiro
                }
1685 102 hiro
                if (tag_name)
1686 102 hiro
                        g_free(tag_name);
1687 1 hiro
        }
1688 1 hiro
1689 101 hiro
        if (tags)
1690 101 hiro
                g_slist_free(tags);
1691 1 hiro
1692 101 hiro
        return on_link;
1693 101 hiro
}
1694 101 hiro
1695 101 hiro
static RemoteURI *textview_get_uri(TextView *textview, GtkTextIter *start,
1696 101 hiro
                                   GtkTextIter *end)
1697 101 hiro
{
1698 101 hiro
        gint start_pos, end_pos;
1699 101 hiro
        GSList *cur;
1700 101 hiro
        RemoteURI *uri = NULL;
1701 101 hiro
1702 101 hiro
        start_pos = gtk_text_iter_get_offset(start);
1703 101 hiro
        end_pos = gtk_text_iter_get_offset(end);
1704 101 hiro
1705 1 hiro
        for (cur = textview->uri_list; cur != NULL; cur = cur->next) {
1706 1 hiro
                RemoteURI *uri_ = (RemoteURI *)cur->data;
1707 1 hiro
1708 1 hiro
                if (start_pos == uri_->start && end_pos == uri_->end) {
1709 1 hiro
                        uri = uri_;
1710 1 hiro
                        break;
1711 1 hiro
                }
1712 1 hiro
        }
1713 1 hiro
1714 101 hiro
        return uri;
1715 101 hiro
}
1716 1 hiro
1717 101 hiro
static gboolean textview_event_after(GtkWidget *widget, GdkEvent *event,
1718 101 hiro
                                     TextView *textview)
1719 101 hiro
{
1720 101 hiro
        GdkEventButton *bevent = (GdkEventButton *)event;
1721 101 hiro
        GtkTextBuffer *buffer;
1722 101 hiro
        GtkTextIter iter, start, end;
1723 101 hiro
        gint x, y;
1724 101 hiro
        gboolean on_link;
1725 101 hiro
        RemoteURI *uri;
1726 101 hiro
1727 101 hiro
        if (event->type != GDK_BUTTON_RELEASE)
1728 101 hiro
                return FALSE;
1729 101 hiro
        if (bevent->button != 1 && bevent->button != 2)
1730 101 hiro
                return FALSE;
1731 101 hiro
1732 101 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
1733 101 hiro
1734 101 hiro
        /* don't follow a link if the user has selected something */
1735 101 hiro
        gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
1736 101 hiro
        if (!gtk_text_iter_equal(&start, &end))
1737 101 hiro
                return FALSE;
1738 101 hiro
1739 101 hiro
        gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
1740 101 hiro
                                              GTK_TEXT_WINDOW_WIDGET,
1741 101 hiro
                                              bevent->x, bevent->y, &x, &y);
1742 101 hiro
        gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y);
1743 101 hiro
        on_link = textview_get_link_tag_bounds(textview, &iter, &start, &end);
1744 101 hiro
        if (!on_link)
1745 101 hiro
                return FALSE;
1746 101 hiro
1747 101 hiro
        uri = textview_get_uri(textview, &start, &end);
1748 1 hiro
        if (!uri)
1749 1 hiro
                return FALSE;
1750 1 hiro
1751 333 hiro
        if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) {
1752 101 hiro
                PrefsAccount *ac = NULL;
1753 101 hiro
                MsgInfo *msginfo = textview->messageview->msginfo;
1754 1 hiro
1755 101 hiro
                if (msginfo && msginfo->folder)
1756 101 hiro
                        ac = account_find_from_item(msginfo->folder);
1757 101 hiro
                if (ac && ac->protocol == A_NNTP)
1758 101 hiro
                        ac = NULL;
1759 101 hiro
                compose_new(ac, msginfo->folder, uri->uri + 7, NULL);
1760 101 hiro
        } else if (textview_uri_security_check(textview, uri) == TRUE)
1761 101 hiro
                open_uri(uri->uri, prefs_common.uri_cmd);
1762 1 hiro
1763 101 hiro
        return FALSE;
1764 101 hiro
}
1765 101 hiro
1766 101 hiro
static void textview_show_uri(TextView *textview, GtkTextIter *start,
1767 101 hiro
                              GtkTextIter *end)
1768 101 hiro
{
1769 101 hiro
        RemoteURI *uri;
1770 101 hiro
1771 101 hiro
        STATUSBAR_POP(textview);
1772 101 hiro
        uri = textview_get_uri(textview, start, end);
1773 101 hiro
        if (uri) {
1774 101 hiro
                gchar *trimmed_uri;
1775 101 hiro
1776 101 hiro
                trimmed_uri = trim_string(uri->uri, 60);
1777 101 hiro
                STATUSBAR_PUSH(textview, trimmed_uri);
1778 101 hiro
                g_free(trimmed_uri);
1779 101 hiro
        }
1780 101 hiro
}
1781 101 hiro
1782 101 hiro
static void textview_set_cursor(TextView *textview, GtkTextView *text,
1783 101 hiro
                                gint x, gint y)
1784 101 hiro
{
1785 101 hiro
        GtkTextBuffer *buffer;
1786 101 hiro
        GtkTextIter iter;
1787 101 hiro
        GtkTextIter start, end;
1788 101 hiro
        GtkTextMark *start_mark, *end_mark;
1789 101 hiro
        gboolean on_link = FALSE;
1790 101 hiro
1791 101 hiro
        buffer = gtk_text_view_get_buffer(text);
1792 101 hiro
        gtk_text_view_get_iter_at_location(text, &iter, x, y);
1793 101 hiro
        on_link = textview_get_link_tag_bounds(textview, &iter, &start, &end);
1794 101 hiro
1795 101 hiro
        start_mark = gtk_text_buffer_get_mark(buffer, "hover-link-start");
1796 101 hiro
        end_mark = gtk_text_buffer_get_mark(buffer, "hover-link-end");
1797 101 hiro
        if (start_mark) {
1798 101 hiro
                GtkTextIter prev_start, prev_end;
1799 101 hiro
1800 101 hiro
                gtk_text_buffer_get_iter_at_mark(buffer, &prev_start,
1801 101 hiro
                                                 start_mark);
1802 101 hiro
                gtk_text_buffer_get_iter_at_mark(buffer, &prev_end, end_mark);
1803 101 hiro
                if (on_link) {
1804 101 hiro
                        if (gtk_text_iter_equal(&prev_start, &start))
1805 101 hiro
                                return;
1806 1 hiro
                }
1807 101 hiro
1808 101 hiro
                gtk_text_buffer_get_iter_at_mark(buffer, &prev_start,
1809 101 hiro
                                                 start_mark);
1810 101 hiro
                gtk_text_buffer_get_iter_at_mark(buffer, &prev_end, end_mark);
1811 101 hiro
                gtk_text_buffer_remove_tag_by_name(buffer, "hover-link",
1812 101 hiro
                                                   &prev_start, &prev_end);
1813 101 hiro
                gtk_text_buffer_delete_mark(buffer, start_mark);
1814 101 hiro
                gtk_text_buffer_delete_mark(buffer, end_mark);
1815 101 hiro
        } else {
1816 101 hiro
                if (!on_link)
1817 101 hiro
                        return;
1818 1 hiro
        }
1819 1 hiro
1820 101 hiro
        if (on_link) {
1821 101 hiro
                gtk_text_buffer_create_mark
1822 101 hiro
                        (buffer, "hover-link-start", &start, FALSE);
1823 101 hiro
                gtk_text_buffer_create_mark
1824 101 hiro
                        (buffer, "hover-link-end", &end, FALSE);
1825 101 hiro
                gtk_text_buffer_apply_tag_by_name
1826 101 hiro
                        (buffer, "hover-link", &start, &end);
1827 101 hiro
                gdk_window_set_cursor
1828 101 hiro
                        (gtk_text_view_get_window(text, GTK_TEXT_WINDOW_TEXT),
1829 101 hiro
                         hand_cursor);
1830 101 hiro
                textview_show_uri(textview, &start, &end);
1831 101 hiro
        } else {
1832 101 hiro
                gdk_window_set_cursor
1833 101 hiro
                        (gtk_text_view_get_window(text, GTK_TEXT_WINDOW_TEXT),
1834 101 hiro
                         regular_cursor);
1835 101 hiro
                STATUSBAR_POP(textview);
1836 101 hiro
        }
1837 101 hiro
}
1838 101 hiro
1839 101 hiro
static gboolean textview_motion_notify(GtkWidget *widget,
1840 101 hiro
                                       GdkEventMotion *event,
1841 101 hiro
                                       TextView *textview)
1842 101 hiro
{
1843 101 hiro
        gint x, y;
1844 101 hiro
1845 101 hiro
        gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
1846 101 hiro
                                              GTK_TEXT_WINDOW_WIDGET,
1847 101 hiro
                                              event->x, event->y, &x, &y);
1848 101 hiro
        textview_set_cursor(textview, GTK_TEXT_VIEW(widget), x, y);
1849 101 hiro
        gdk_window_get_pointer(widget->window, NULL, NULL, NULL);
1850 101 hiro
1851 1 hiro
        return FALSE;
1852 1 hiro
}
1853 1 hiro
1854 113 hiro
static gboolean textview_leave_notify(GtkWidget *widget,
1855 113 hiro
                                      GdkEventCrossing *event,
1856 113 hiro
                                      TextView *textview)
1857 113 hiro
{
1858 113 hiro
        textview_set_cursor(textview, GTK_TEXT_VIEW(widget), 0, 0);
1859 113 hiro
1860 113 hiro
        return FALSE;
1861 113 hiro
}
1862 113 hiro
1863 101 hiro
static gboolean textview_visibility_notify(GtkWidget *widget,
1864 101 hiro
                                           GdkEventVisibility *event,
1865 101 hiro
                                           TextView *textview)
1866 101 hiro
{
1867 101 hiro
        gint wx, wy, bx, by;
1868 113 hiro
        GdkWindow *window;
1869 101 hiro
1870 113 hiro
        window = gtk_text_view_get_window(GTK_TEXT_VIEW(widget),
1871 113 hiro
                                          GTK_TEXT_WINDOW_TEXT);
1872 113 hiro
1873 113 hiro
        /* check if the event occurred for the text window part */
1874 113 hiro
        if (window != event->window)
1875 113 hiro
                return FALSE;
1876 113 hiro
1877 101 hiro
        gdk_window_get_pointer(widget->window, &wx, &wy, NULL);
1878 101 hiro
        gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
1879 101 hiro
                                              GTK_TEXT_WINDOW_WIDGET,
1880 101 hiro
                                              wx, wy, &bx, &by);
1881 101 hiro
        textview_set_cursor(textview, GTK_TEXT_VIEW(widget), bx, by);
1882 101 hiro
1883 101 hiro
        return FALSE;
1884 101 hiro
}
1885 101 hiro
1886 368 hiro
#define ADD_MENU(label, func)                                                \
1887 368 hiro
{                                                                        \
1888 368 hiro
        menuitem = gtk_menu_item_new_with_mnemonic(label);                \
1889 368 hiro
        g_signal_connect(menuitem, "activate", G_CALLBACK(func), uri);        \
1890 368 hiro
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);                \
1891 368 hiro
        g_object_set_data(G_OBJECT(menuitem), "textview", textview);        \
1892 368 hiro
        gtk_widget_show(menuitem);                                        \
1893 368 hiro
}
1894 368 hiro
1895 116 hiro
static void textview_populate_popup(GtkWidget *widget, GtkMenu *menu,
1896 116 hiro
                                    TextView *textview)
1897 116 hiro
{
1898 116 hiro
        gint px, py, x, y;
1899 116 hiro
        GtkWidget *separator, *menuitem;
1900 116 hiro
        GtkTextBuffer *buffer;
1901 116 hiro
        GtkTextIter iter, start, end;
1902 116 hiro
        gboolean on_link;
1903 116 hiro
        RemoteURI *uri;
1904 162 hiro
        GdkPixbuf *pixbuf;
1905 116 hiro
1906 116 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
1907 116 hiro
1908 116 hiro
        gdk_window_get_pointer(widget->window, &px, &py, NULL);
1909 116 hiro
        gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget),
1910 116 hiro
                                              GTK_TEXT_WINDOW_WIDGET,
1911 116 hiro
                                              px, py, &x, &y);
1912 116 hiro
        gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y);
1913 162 hiro
        if ((pixbuf = gtk_text_iter_get_pixbuf(&iter)) != NULL) {
1914 162 hiro
                start = end = iter;
1915 162 hiro
                gtk_text_iter_forward_char(&end);
1916 162 hiro
                uri = textview_get_uri(textview, &start, &end);
1917 162 hiro
1918 162 hiro
                separator = gtk_separator_menu_item_new();
1919 162 hiro
                gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
1920 162 hiro
                gtk_widget_show(separator);
1921 162 hiro
1922 368 hiro
                ADD_MENU(_("Sa_ve this image as..."),
1923 368 hiro
                         textview_popup_menu_activate_image_cb);
1924 162 hiro
        }
1925 116 hiro
        on_link = textview_get_link_tag_bounds(textview, &iter, &start, &end);
1926 116 hiro
        if (!on_link)
1927 116 hiro
                return;
1928 116 hiro
1929 116 hiro
        uri = textview_get_uri(textview, &start, &end);
1930 116 hiro
        if (!uri)
1931 116 hiro
                return;
1932 116 hiro
1933 116 hiro
        separator = gtk_separator_menu_item_new();
1934 116 hiro
        gtk_menu_shell_append(GTK_MENU_SHELL(menu), separator);
1935 116 hiro
        gtk_widget_show(separator);
1936 116 hiro
1937 352 hiro
        if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) {
1938 368 hiro
                ADD_MENU(_("Compose _new message"),
1939 368 hiro
                         textview_popup_menu_activate_open_uri_cb);
1940 368 hiro
                ADD_MENU(_("Add to address _book..."),
1941 368 hiro
                         textview_popup_menu_activate_add_address_cb);
1942 368 hiro
                ADD_MENU(_("Copy this add_ress"),
1943 368 hiro
                         textview_popup_menu_activate_copy_cb);
1944 368 hiro
        } else {
1945 368 hiro
                ADD_MENU(_("_Open with Web browser"),
1946 368 hiro
                         textview_popup_menu_activate_open_uri_cb);
1947 368 hiro
                ADD_MENU(_("Copy this _link"),
1948 368 hiro
                         textview_popup_menu_activate_copy_cb);
1949 368 hiro
        }
1950 368 hiro
}
1951 352 hiro
1952 368 hiro
static void textview_popup_menu_activate_open_uri_cb(GtkMenuItem *menuitem,
1953 368 hiro
                                                     gpointer data)
1954 368 hiro
{
1955 368 hiro
        RemoteURI *uri = (RemoteURI *)data;
1956 368 hiro
        TextView *textview;
1957 352 hiro
1958 368 hiro
        g_return_if_fail(uri != NULL);
1959 368 hiro
1960 368 hiro
        if (!uri->uri)
1961 368 hiro
                return;
1962 368 hiro
1963 368 hiro
        textview = g_object_get_data(G_OBJECT(menuitem), "textview");
1964 368 hiro
        g_return_if_fail(textview != NULL);
1965 368 hiro
1966 368 hiro
        if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7)) {
1967 368 hiro
                PrefsAccount *ac = NULL;
1968 368 hiro
                MsgInfo *msginfo = textview->messageview->msginfo;
1969 368 hiro
1970 368 hiro
                if (msginfo && msginfo->folder)
1971 368 hiro
                        ac = account_find_from_item(msginfo->folder);
1972 368 hiro
                if (ac && ac->protocol == A_NNTP)
1973 368 hiro
                        ac = NULL;
1974 368 hiro
                compose_new(ac, msginfo->folder, uri->uri + 7, NULL);
1975 368 hiro
        } else if (textview_uri_security_check(textview, uri) == TRUE)
1976 368 hiro
                open_uri(uri->uri, prefs_common.uri_cmd);
1977 116 hiro
}
1978 116 hiro
1979 352 hiro
static void textview_popup_menu_activate_add_address_cb(GtkMenuItem *menuitem,
1980 352 hiro
                                                        gpointer data)
1981 352 hiro
{
1982 352 hiro
        RemoteURI *uri = (RemoteURI *)data;
1983 352 hiro
        const gchar *addr;
1984 352 hiro
1985 352 hiro
        g_return_if_fail(uri != NULL);
1986 352 hiro
1987 352 hiro
        if (!uri->uri)
1988 352 hiro
                return;
1989 352 hiro
1990 352 hiro
        if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7))
1991 352 hiro
                addr = uri->uri + 7;
1992 352 hiro
        else
1993 352 hiro
                addr = uri->uri;
1994 352 hiro
1995 352 hiro
        addressbook_add_contact(addr, addr, NULL);
1996 352 hiro
}
1997 352 hiro
1998 116 hiro
static void textview_popup_menu_activate_copy_cb(GtkMenuItem *menuitem,
1999 116 hiro
                                                 gpointer data)
2000 116 hiro
{
2001 116 hiro
        RemoteURI *uri = (RemoteURI *)data;
2002 116 hiro
        const gchar *uri_string;
2003 116 hiro
        GtkClipboard *clipboard;
2004 116 hiro
2005 116 hiro
        g_return_if_fail(uri != NULL);
2006 116 hiro
2007 116 hiro
        if (!uri->uri)
2008 116 hiro
                return;
2009 116 hiro
2010 333 hiro
        if (!g_ascii_strncasecmp(uri->uri, "mailto:", 7))
2011 116 hiro
                uri_string = uri->uri + 7;
2012 116 hiro
        else
2013 116 hiro
                uri_string = uri->uri;
2014 116 hiro
2015 116 hiro
        clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
2016 116 hiro
        gtk_clipboard_set_text(clipboard, uri_string, -1);
2017 116 hiro
        clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
2018 116 hiro
        gtk_clipboard_set_text(clipboard, uri_string, -1);
2019 116 hiro
}
2020 116 hiro
2021 162 hiro
static void textview_popup_menu_activate_image_cb(GtkMenuItem *menuitem,
2022 162 hiro
                                                  gpointer data)
2023 162 hiro
{
2024 162 hiro
        RemoteURI *uri = (RemoteURI *)data;
2025 162 hiro
        gchar *src;
2026 162 hiro
        gchar *dest;
2027 162 hiro
        gchar *filename;
2028 162 hiro
2029 162 hiro
        g_return_if_fail(uri != NULL);
2030 162 hiro
2031 162 hiro
        if (!uri->uri)
2032 162 hiro
                return;
2033 162 hiro
2034 162 hiro
        src = g_filename_from_uri(uri->uri, NULL, NULL);
2035 162 hiro
        g_return_if_fail(src != NULL);
2036 162 hiro
2037 162 hiro
        filename = conv_filename_to_utf8(uri->filename);
2038 162 hiro
        dest = filesel_save_as(filename);
2039 162 hiro
        if (dest) {
2040 162 hiro
                copy_file(src, dest, FALSE);
2041 162 hiro
                g_free(dest);
2042 162 hiro
        }
2043 162 hiro
        g_free(filename);
2044 162 hiro
        g_free(src);
2045 162 hiro
}
2046 162 hiro
2047 1 hiro
static gboolean textview_uri_security_check(TextView *textview, RemoteURI *uri)
2048 1 hiro
{
2049 1 hiro
        GtkTextBuffer *buffer;
2050 1 hiro
        GtkTextIter start_iter, end_iter;
2051 1 hiro
        gchar *visible_str;
2052 1 hiro
        gboolean retval = TRUE;
2053 1 hiro
2054 1 hiro
        if (is_uri_string(uri->uri) == FALSE)
2055 1 hiro
                return TRUE;
2056 1 hiro
2057 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
2058 1 hiro
        gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, uri->start);
2059 1 hiro
        gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, uri->end);
2060 1 hiro
        visible_str = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
2061 1 hiro
                                               FALSE);
2062 1 hiro
        if (visible_str == NULL)
2063 1 hiro
                return TRUE;
2064 1 hiro
2065 1 hiro
        if (strcmp(visible_str, uri->uri) != 0 && is_uri_string(visible_str)) {
2066 1 hiro
                gchar *uri_path;
2067 1 hiro
                gchar *visible_uri_path;
2068 1 hiro
2069 1 hiro
                uri_path = get_uri_path(uri->uri);
2070 1 hiro
                visible_uri_path = get_uri_path(visible_str);
2071 1 hiro
                if (strcmp(uri_path, visible_uri_path) != 0)
2072 1 hiro
                        retval = FALSE;
2073 1 hiro
        }
2074 1 hiro
2075 1 hiro
        if (retval == FALSE) {
2076 1 hiro
                gchar *msg;
2077 1 hiro
                AlertValue aval;
2078 1 hiro
2079 1 hiro
                msg = g_strdup_printf(_("The real URL (%s) is different from\n"
2080 1 hiro
                                        "the apparent URL (%s).\n"
2081 421 hiro
                                        "\n"
2082 1 hiro
                                        "Open it anyway?"),
2083 1 hiro
                                      uri->uri, visible_str);
2084 421 hiro
                aval = alertpanel_full(_("Fake URL warning"), msg,
2085 421 hiro
                                       ALERT_WARNING, G_ALERTDEFAULT, FALSE,
2086 421 hiro
                                       GTK_STOCK_YES, GTK_STOCK_NO, NULL);
2087 1 hiro
                g_free(msg);
2088 1 hiro
                if (aval == G_ALERTDEFAULT)
2089 1 hiro
                        retval = TRUE;
2090 1 hiro
        }
2091 1 hiro
2092 1 hiro
        g_free(visible_str);
2093 1 hiro
2094 1 hiro
        return retval;
2095 1 hiro
}
2096 1 hiro
2097 1 hiro
static void textview_uri_list_remove_all(GSList *uri_list)
2098 1 hiro
{
2099 1 hiro
        GSList *cur;
2100 1 hiro
2101 1 hiro
        for (cur = uri_list; cur != NULL; cur = cur->next) {
2102 162 hiro
                RemoteURI *uri = (RemoteURI *)cur->data;
2103 162 hiro
2104 162 hiro
                if (uri) {
2105 162 hiro
                        g_free(uri->uri);
2106 162 hiro
                        g_free(uri->filename);
2107 162 hiro
                        g_free(uri);
2108 1 hiro
                }
2109 1 hiro
        }
2110 1 hiro
2111 1 hiro
        g_slist_free(uri_list);
2112 1 hiro
}