Statistics
| Revision:

root / src / textview.c @ 6

History | View | Annotate | Download (42 kB)

1 1 hiro
/*
2 1 hiro
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 1 hiro
 * Copyright (C) 1999-2004 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 1 hiro
#include <gdk/gdk.h>
28 1 hiro
#include <gdk/gdkkeysyms.h>
29 1 hiro
#include <gtk/gtkvbox.h>
30 1 hiro
#include <gtk/gtkscrolledwindow.h>
31 1 hiro
#include <gtk/gtksignal.h>
32 1 hiro
#include <stdio.h>
33 1 hiro
#include <ctype.h>
34 1 hiro
#include <string.h>
35 1 hiro
#include <stdlib.h>
36 1 hiro
37 1 hiro
#include "intl.h"
38 1 hiro
#include "main.h"
39 1 hiro
#include "summaryview.h"
40 1 hiro
#include "procheader.h"
41 1 hiro
#include "prefs_common.h"
42 1 hiro
#include "codeconv.h"
43 1 hiro
#include "statusbar.h"
44 1 hiro
#include "utils.h"
45 1 hiro
#include "gtkutils.h"
46 1 hiro
#include "procmime.h"
47 1 hiro
#include "account.h"
48 1 hiro
#include "html.h"
49 1 hiro
#include "compose.h"
50 1 hiro
#include "displayheader.h"
51 1 hiro
#include "alertpanel.h"
52 1 hiro
53 1 hiro
typedef struct _RemoteURI        RemoteURI;
54 1 hiro
55 1 hiro
struct _RemoteURI
56 1 hiro
{
57 1 hiro
        gchar *uri;
58 1 hiro
59 1 hiro
        guint start;
60 1 hiro
        guint end;
61 1 hiro
};
62 1 hiro
63 1 hiro
static GdkColor quote_colors[3] = {
64 1 hiro
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0},
65 1 hiro
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0},
66 1 hiro
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0}
67 1 hiro
};
68 1 hiro
69 1 hiro
static GdkColor uri_color = {
70 1 hiro
        (gulong)0,
71 1 hiro
        (gushort)0,
72 1 hiro
        (gushort)0,
73 1 hiro
        (gushort)0
74 1 hiro
};
75 1 hiro
76 1 hiro
static GdkColor emphasis_color = {
77 1 hiro
        (gulong)0,
78 1 hiro
        (gushort)0,
79 1 hiro
        (gushort)0,
80 1 hiro
        (gushort)0xcfff
81 1 hiro
};
82 1 hiro
83 1 hiro
#if 0
84 1 hiro
static GdkColor error_color = {
85 1 hiro
        (gulong)0,
86 1 hiro
        (gushort)0xefff,
87 1 hiro
        (gushort)0,
88 1 hiro
        (gushort)0
89 1 hiro
};
90 1 hiro
#endif
91 1 hiro
92 1 hiro
#if USE_GPGME
93 1 hiro
static GdkColor good_sig_color = {
94 1 hiro
        (gulong)0,
95 1 hiro
        (gushort)0,
96 1 hiro
        (gushort)0xbfff,
97 1 hiro
        (gushort)0
98 1 hiro
};
99 1 hiro
100 1 hiro
static GdkColor nocheck_sig_color = {
101 1 hiro
        (gulong)0,
102 1 hiro
        (gushort)0,
103 1 hiro
        (gushort)0,
104 1 hiro
        (gushort)0xcfff
105 1 hiro
};
106 1 hiro
107 1 hiro
static GdkColor bad_sig_color = {
108 1 hiro
        (gulong)0,
109 1 hiro
        (gushort)0xefff,
110 1 hiro
        (gushort)0,
111 1 hiro
        (gushort)0
112 1 hiro
};
113 1 hiro
#endif
114 1 hiro
115 1 hiro
#define STATUSBAR_PUSH(textview, str)                                            \
116 1 hiro
{                                                                            \
117 1 hiro
        gtk_statusbar_push(GTK_STATUSBAR(textview->messageview->statusbar), \
118 1 hiro
                           textview->messageview->statusbar_cid, str);            \
119 1 hiro
}
120 1 hiro
121 1 hiro
#define STATUSBAR_POP(textview)                                                   \
122 1 hiro
{                                                                           \
123 1 hiro
        gtk_statusbar_pop(GTK_STATUSBAR(textview->messageview->statusbar), \
124 1 hiro
                          textview->messageview->statusbar_cid);           \
125 1 hiro
}
126 1 hiro
127 1 hiro
static void textview_add_part                (TextView        *textview,
128 1 hiro
                                         MimeInfo        *mimeinfo,
129 1 hiro
                                         FILE                *fp);
130 1 hiro
static void textview_add_parts                (TextView        *textview,
131 1 hiro
                                         MimeInfo        *mimeinfo,
132 1 hiro
                                         FILE                *fp);
133 1 hiro
static void textview_write_body                (TextView        *textview,
134 1 hiro
                                         MimeInfo        *mimeinfo,
135 1 hiro
                                         FILE                *fp,
136 1 hiro
                                         const gchar        *charset);
137 1 hiro
static void textview_show_html                (TextView        *textview,
138 1 hiro
                                         FILE                *fp,
139 1 hiro
                                         CodeConverter        *conv);
140 1 hiro
141 1 hiro
static void textview_write_line                (TextView        *textview,
142 1 hiro
                                         const gchar        *str,
143 1 hiro
                                         CodeConverter        *conv);
144 1 hiro
static void textview_write_link                (TextView        *textview,
145 1 hiro
                                         const gchar        *str,
146 1 hiro
                                         const gchar        *uri,
147 1 hiro
                                         CodeConverter        *conv);
148 1 hiro
149 1 hiro
static GPtrArray *textview_scan_header        (TextView        *textview,
150 1 hiro
                                         FILE                *fp);
151 1 hiro
static void textview_show_header        (TextView        *textview,
152 1 hiro
                                         GPtrArray        *headers);
153 1 hiro
154 1 hiro
static gboolean textview_key_pressed                (GtkWidget        *widget,
155 1 hiro
                                                 GdkEventKey        *event,
156 1 hiro
                                                 TextView        *textview);
157 1 hiro
static gboolean textview_uri_button_pressed        (GtkTextTag        *tag,
158 1 hiro
                                                 GObject        *obj,
159 1 hiro
                                                 GdkEvent        *event,
160 1 hiro
                                                 GtkTextIter        *iter,
161 1 hiro
                                                 TextView        *textview);
162 1 hiro
163 1 hiro
static void textview_smooth_scroll_do                (TextView        *textview,
164 1 hiro
                                                 gfloat                 old_value,
165 1 hiro
                                                 gfloat                 last_value,
166 1 hiro
                                                 gint                 step);
167 1 hiro
static void textview_smooth_scroll_one_line        (TextView        *textview,
168 1 hiro
                                                 gboolean         up);
169 1 hiro
static gboolean textview_smooth_scroll_page        (TextView        *textview,
170 1 hiro
                                                 gboolean         up);
171 1 hiro
172 1 hiro
static gboolean textview_uri_security_check        (TextView        *textview,
173 1 hiro
                                                 RemoteURI        *uri);
174 1 hiro
static void textview_uri_list_remove_all        (GSList                *uri_list);
175 1 hiro
176 1 hiro
177 1 hiro
TextView *textview_create(void)
178 1 hiro
{
179 1 hiro
        TextView *textview;
180 1 hiro
        GtkWidget *vbox;
181 1 hiro
        GtkWidget *scrolledwin;
182 1 hiro
        GtkWidget *text;
183 1 hiro
        GtkTextBuffer *buffer;
184 1 hiro
        GtkClipboard *clipboard;
185 1 hiro
186 1 hiro
        debug_print(_("Creating text view...\n"));
187 1 hiro
        textview = g_new0(TextView, 1);
188 1 hiro
189 1 hiro
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
190 1 hiro
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
191 1 hiro
                                       GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
192 4 hiro
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
193 4 hiro
                                            GTK_SHADOW_IN);
194 1 hiro
        gtk_widget_set_size_request
195 1 hiro
                (scrolledwin, prefs_common.mainview_width, -1);
196 1 hiro
197 1 hiro
        text = gtk_text_view_new();
198 1 hiro
        gtk_widget_show(text);
199 1 hiro
        gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
200 1 hiro
        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
201 3 hiro
        gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 6);
202 3 hiro
        gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 6);
203 1 hiro
204 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
205 1 hiro
        clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
206 1 hiro
        gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
207 1 hiro
208 1 hiro
        gtk_widget_ref(scrolledwin);
209 1 hiro
210 1 hiro
        gtk_container_add(GTK_CONTAINER(scrolledwin), text);
211 1 hiro
212 1 hiro
        g_signal_connect(G_OBJECT(text), "key_press_event",
213 1 hiro
                         G_CALLBACK(textview_key_pressed), textview);
214 1 hiro
215 1 hiro
        gtk_widget_show(scrolledwin);
216 1 hiro
217 1 hiro
        vbox = gtk_vbox_new(FALSE, 0);
218 1 hiro
        gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
219 1 hiro
220 1 hiro
        gtk_widget_show(vbox);
221 1 hiro
222 1 hiro
        textview->vbox             = vbox;
223 1 hiro
        textview->scrolledwin      = scrolledwin;
224 1 hiro
        textview->text             = text;
225 1 hiro
        textview->uri_list         = NULL;
226 1 hiro
        textview->body_pos         = 0;
227 1 hiro
        textview->show_all_headers = FALSE;
228 1 hiro
229 1 hiro
        return textview;
230 1 hiro
}
231 1 hiro
232 1 hiro
static void textview_create_tags(GtkTextView *text, TextView *textview)
233 1 hiro
{
234 1 hiro
        GtkTextBuffer *buffer;
235 1 hiro
        GtkTextTag *tag;
236 1 hiro
237 1 hiro
        buffer = gtk_text_view_get_buffer(text);
238 1 hiro
239 1 hiro
        gtk_text_buffer_create_tag(buffer, "header",
240 1 hiro
                                   "pixels-above-lines", 0,
241 1 hiro
                                   "pixels-above-lines-set", TRUE,
242 1 hiro
                                   "pixels-below-lines", 0,
243 1 hiro
                                   "pixels-below-lines-set", TRUE,
244 1 hiro
                                   //"left-margin", 0,
245 1 hiro
                                   //"left-margin-set", TRUE,
246 1 hiro
                                   NULL);
247 1 hiro
        gtk_text_buffer_create_tag(buffer, "header_title",
248 1 hiro
                                   "font", prefs_common.boldfont,
249 1 hiro
                                   NULL);
250 1 hiro
        gtk_text_buffer_create_tag(buffer, "quote0",
251 1 hiro
                                   "foreground-gdk", &quote_colors[0],
252 1 hiro
                                   NULL);
253 1 hiro
        gtk_text_buffer_create_tag(buffer, "quote1",
254 1 hiro
                                   "foreground-gdk", &quote_colors[1],
255 1 hiro
                                   NULL);
256 1 hiro
        gtk_text_buffer_create_tag(buffer, "quote2",
257 1 hiro
                                   "foreground-gdk", &quote_colors[2],
258 1 hiro
                                   NULL);
259 1 hiro
        gtk_text_buffer_create_tag(buffer, "emphasis",
260 1 hiro
                                   "foreground-gdk", &emphasis_color,
261 1 hiro
                                   NULL);
262 1 hiro
        tag = gtk_text_buffer_create_tag(buffer, "link",
263 1 hiro
                                         "foreground-gdk", &uri_color,
264 1 hiro
                                         NULL);
265 1 hiro
#if USE_GPGME
266 1 hiro
        gtk_text_buffer_create_tag(buffer, "good-signature",
267 1 hiro
                                   "foreground-gdk", &good_sig_color,
268 1 hiro
                                   NULL);
269 1 hiro
        gtk_text_buffer_create_tag(buffer, "bad-signature",
270 1 hiro
                                   "foreground-gdk", &bad_sig_color,
271 1 hiro
                                   NULL);
272 1 hiro
        gtk_text_buffer_create_tag(buffer, "nocheck-signature",
273 1 hiro
                                   "foreground-gdk", &nocheck_sig_color,
274 1 hiro
                                   NULL);
275 1 hiro
#endif /* USE_GPGME */
276 1 hiro
277 1 hiro
        g_signal_connect(G_OBJECT(tag), "event",
278 1 hiro
                         G_CALLBACK(textview_uri_button_pressed), textview);
279 1 hiro
}
280 1 hiro
281 1 hiro
void textview_init(TextView *textview)
282 1 hiro
{
283 1 hiro
        //gtkut_widget_disable_theme_engine(textview->text);
284 1 hiro
        textview_update_message_colors();
285 1 hiro
        textview_set_all_headers(textview, FALSE);
286 1 hiro
        textview_set_font(textview, NULL);
287 1 hiro
        textview_create_tags(GTK_TEXT_VIEW(textview->text), textview);
288 1 hiro
}
289 1 hiro
290 1 hiro
void textview_update_message_colors(void)
291 1 hiro
{
292 1 hiro
        GdkColor black = {0, 0, 0, 0};
293 1 hiro
294 1 hiro
        if (prefs_common.enable_color) {
295 1 hiro
                /* grab the quote colors, converting from an int to a GdkColor */
296 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
297 1 hiro
                                               &quote_colors[0]);
298 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
299 1 hiro
                                               &quote_colors[1]);
300 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
301 1 hiro
                                               &quote_colors[2]);
302 1 hiro
                gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
303 1 hiro
                                               &uri_color);
304 1 hiro
        } else {
305 1 hiro
                quote_colors[0] = quote_colors[1] = quote_colors[2] =
306 1 hiro
                        uri_color = emphasis_color = black;
307 1 hiro
        }
308 1 hiro
}
309 1 hiro
310 1 hiro
void textview_show_message(TextView *textview, MimeInfo *mimeinfo,
311 1 hiro
                           const gchar *file)
312 1 hiro
{
313 1 hiro
        FILE *fp;
314 1 hiro
        const gchar *charset = NULL;
315 1 hiro
        GPtrArray *headers = NULL;
316 1 hiro
317 1 hiro
        if ((fp = fopen(file, "rb")) == NULL) {
318 1 hiro
                FILE_OP_ERROR(file, "fopen");
319 1 hiro
                return;
320 1 hiro
        }
321 1 hiro
322 1 hiro
        if (textview->messageview->forced_charset)
323 1 hiro
                charset = textview->messageview->forced_charset;
324 1 hiro
        else if (prefs_common.force_charset)
325 1 hiro
                charset = prefs_common.force_charset;
326 1 hiro
        else if (mimeinfo->charset)
327 1 hiro
                charset = mimeinfo->charset;
328 1 hiro
329 1 hiro
        textview_set_font(textview, charset);
330 1 hiro
        textview_clear(textview);
331 1 hiro
332 1 hiro
        if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) perror("fseek");
333 1 hiro
        headers = textview_scan_header(textview, fp);
334 1 hiro
        if (headers) {
335 1 hiro
                GtkTextView *text = GTK_TEXT_VIEW(textview->text);
336 1 hiro
                GtkTextBuffer *buffer;
337 1 hiro
                GtkTextIter iter;
338 1 hiro
339 1 hiro
                textview_show_header(textview, headers);
340 1 hiro
                procheader_header_array_destroy(headers);
341 1 hiro
342 1 hiro
                buffer = gtk_text_view_get_buffer(text);
343 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter);
344 1 hiro
                textview->body_pos = gtk_text_iter_get_offset(&iter);
345 1 hiro
        }
346 1 hiro
347 1 hiro
        textview_add_parts(textview, mimeinfo, fp);
348 1 hiro
349 1 hiro
        fclose(fp);
350 1 hiro
351 1 hiro
        textview_set_position(textview, 0);
352 1 hiro
}
353 1 hiro
354 1 hiro
void textview_show_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
355 1 hiro
{
356 1 hiro
        gchar buf[BUFFSIZE];
357 1 hiro
        const gchar *boundary = NULL;
358 1 hiro
        gint boundary_len = 0;
359 1 hiro
        const gchar *charset = NULL;
360 1 hiro
        GPtrArray *headers = NULL;
361 1 hiro
        gboolean is_rfc822_part = FALSE;
362 1 hiro
363 1 hiro
        g_return_if_fail(mimeinfo != NULL);
364 1 hiro
        g_return_if_fail(fp != NULL);
365 1 hiro
366 1 hiro
        if (mimeinfo->mime_type == MIME_MULTIPART) {
367 1 hiro
                textview_clear(textview);
368 1 hiro
                textview_add_parts(textview, mimeinfo, fp);
369 1 hiro
                return;
370 1 hiro
        }
371 1 hiro
372 1 hiro
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
373 1 hiro
                boundary = mimeinfo->parent->boundary;
374 1 hiro
                boundary_len = strlen(boundary);
375 1 hiro
        }
376 1 hiro
377 1 hiro
        if (!boundary && mimeinfo->mime_type == MIME_TEXT) {
378 1 hiro
                if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
379 1 hiro
                        perror("fseek");
380 1 hiro
                headers = textview_scan_header(textview, fp);
381 1 hiro
        } else {
382 1 hiro
                if (mimeinfo->mime_type == MIME_TEXT && mimeinfo->parent) {
383 1 hiro
                        glong fpos;
384 1 hiro
                        MimeInfo *parent = mimeinfo->parent;
385 1 hiro
386 1 hiro
                        while (parent->parent) {
387 1 hiro
                                if (parent->main &&
388 1 hiro
                                    parent->main->mime_type ==
389 1 hiro
                                        MIME_MESSAGE_RFC822)
390 1 hiro
                                        break;
391 1 hiro
                                parent = parent->parent;
392 1 hiro
                        }
393 1 hiro
394 1 hiro
                        if ((fpos = ftell(fp)) < 0)
395 1 hiro
                                perror("ftell");
396 1 hiro
                        else if (fseek(fp, parent->fpos, SEEK_SET) < 0)
397 1 hiro
                                perror("fseek");
398 1 hiro
                        else {
399 1 hiro
                                headers = textview_scan_header(textview, fp);
400 1 hiro
                                if (fseek(fp, fpos, SEEK_SET) < 0)
401 1 hiro
                                        perror("fseek");
402 1 hiro
                        }
403 1 hiro
                }
404 1 hiro
                /* skip MIME part headers */
405 1 hiro
                while (fgets(buf, sizeof(buf), fp) != NULL)
406 1 hiro
                        if (buf[0] == '\r' || buf[0] == '\n') break;
407 1 hiro
        }
408 1 hiro
409 1 hiro
        /* display attached RFC822 single text message */
410 1 hiro
        if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
411 1 hiro
                if (headers) procheader_header_array_destroy(headers);
412 1 hiro
                if (!mimeinfo->sub) {
413 1 hiro
                        textview_clear(textview);
414 1 hiro
                        return;
415 1 hiro
                }
416 1 hiro
                headers = textview_scan_header(textview, fp);
417 1 hiro
                mimeinfo = mimeinfo->sub;
418 1 hiro
                is_rfc822_part = TRUE;
419 1 hiro
        }
420 1 hiro
421 1 hiro
        if (textview->messageview->forced_charset)
422 1 hiro
                charset = textview->messageview->forced_charset;
423 1 hiro
        else if (prefs_common.force_charset)
424 1 hiro
                charset = prefs_common.force_charset;
425 1 hiro
        else if (mimeinfo->charset)
426 1 hiro
                charset = mimeinfo->charset;
427 1 hiro
428 1 hiro
        textview_set_font(textview, charset);
429 1 hiro
430 1 hiro
        textview_clear(textview);
431 1 hiro
432 1 hiro
        if (headers) {
433 1 hiro
                GtkTextView *text = GTK_TEXT_VIEW(textview->text);
434 1 hiro
                GtkTextBuffer *buffer;
435 1 hiro
                GtkTextIter iter;
436 1 hiro
437 1 hiro
                textview_show_header(textview, headers);
438 1 hiro
                procheader_header_array_destroy(headers);
439 1 hiro
440 1 hiro
                buffer = gtk_text_view_get_buffer(text);
441 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter);
442 1 hiro
                textview->body_pos = gtk_text_iter_get_offset(&iter);
443 1 hiro
                if (!mimeinfo->main)
444 1 hiro
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
445 1 hiro
        }
446 1 hiro
447 1 hiro
        if (mimeinfo->mime_type == MIME_MULTIPART || is_rfc822_part)
448 1 hiro
                textview_add_parts(textview, mimeinfo, fp);
449 1 hiro
        else
450 1 hiro
                textview_write_body(textview, mimeinfo, fp, charset);
451 1 hiro
}
452 1 hiro
453 1 hiro
static void textview_add_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
454 1 hiro
{
455 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
456 1 hiro
        GtkTextBuffer *buffer;
457 1 hiro
        GtkTextIter iter;
458 1 hiro
        gchar buf[BUFFSIZE];
459 1 hiro
        const gchar *boundary = NULL;
460 1 hiro
        gint boundary_len = 0;
461 1 hiro
        const gchar *charset = NULL;
462 1 hiro
        GPtrArray *headers = NULL;
463 1 hiro
464 1 hiro
        g_return_if_fail(mimeinfo != NULL);
465 1 hiro
        g_return_if_fail(fp != NULL);
466 1 hiro
467 1 hiro
        buffer = gtk_text_view_get_buffer(text);
468 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
469 1 hiro
470 1 hiro
        if (mimeinfo->mime_type == MIME_MULTIPART) return;
471 1 hiro
472 1 hiro
        if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) {
473 1 hiro
                perror("fseek");
474 1 hiro
                return;
475 1 hiro
        }
476 1 hiro
477 1 hiro
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
478 1 hiro
                boundary = mimeinfo->parent->boundary;
479 1 hiro
                boundary_len = strlen(boundary);
480 1 hiro
        }
481 1 hiro
482 1 hiro
        while (fgets(buf, sizeof(buf), fp) != NULL)
483 1 hiro
                if (buf[0] == '\r' || buf[0] == '\n') break;
484 1 hiro
485 1 hiro
        if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
486 1 hiro
                headers = textview_scan_header(textview, fp);
487 1 hiro
                if (headers) {
488 1 hiro
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
489 1 hiro
                        textview_show_header(textview, headers);
490 1 hiro
                        procheader_header_array_destroy(headers);
491 1 hiro
                }
492 1 hiro
                return;
493 1 hiro
        }
494 1 hiro
495 1 hiro
#if USE_GPGME
496 1 hiro
        if (mimeinfo->sigstatus)
497 1 hiro
                g_snprintf(buf, sizeof(buf), "\n[%s (%s)]\n",
498 1 hiro
                           mimeinfo->content_type, mimeinfo->sigstatus);
499 1 hiro
        else
500 1 hiro
#endif
501 1 hiro
        if (mimeinfo->filename || mimeinfo->name)
502 1 hiro
                g_snprintf(buf, sizeof(buf), "\n[%s  %s (%d bytes)]\n",
503 1 hiro
                           mimeinfo->filename ? mimeinfo->filename :
504 1 hiro
                           mimeinfo->name,
505 1 hiro
                           mimeinfo->content_type, mimeinfo->size);
506 1 hiro
        else
507 1 hiro
                g_snprintf(buf, sizeof(buf), "\n[%s (%d bytes)]\n",
508 1 hiro
                           mimeinfo->content_type, mimeinfo->size);
509 1 hiro
510 1 hiro
#if USE_GPGME
511 1 hiro
        if (mimeinfo->sigstatus) {
512 1 hiro
                const gchar *color;
513 1 hiro
                if (!strcmp(mimeinfo->sigstatus, _("Good signature")))
514 1 hiro
                        color = "good-signature";
515 1 hiro
                else if (!strcmp(mimeinfo->sigstatus, _("BAD signature")))
516 1 hiro
                        color = "bad-signature";
517 1 hiro
                else
518 1 hiro
                        color = "nocheck-signature";
519 1 hiro
                gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, buf, -1,
520 1 hiro
                                                         color, NULL);
521 1 hiro
        } else
522 1 hiro
#endif
523 1 hiro
        if (mimeinfo->mime_type != MIME_TEXT &&
524 1 hiro
            mimeinfo->mime_type != MIME_TEXT_HTML) {
525 1 hiro
                gtk_text_buffer_insert(buffer, &iter, buf, -1);
526 1 hiro
        } else {
527 1 hiro
                if (!mimeinfo->main &&
528 1 hiro
                    mimeinfo->parent &&
529 1 hiro
                    mimeinfo->parent->children != mimeinfo)
530 1 hiro
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
531 1 hiro
                else
532 1 hiro
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
533 1 hiro
                if (textview->messageview->forced_charset)
534 1 hiro
                        charset = textview->messageview->forced_charset;
535 1 hiro
                else if (prefs_common.force_charset)
536 1 hiro
                        charset = prefs_common.force_charset;
537 1 hiro
                else if (mimeinfo->charset)
538 1 hiro
                        charset = mimeinfo->charset;
539 1 hiro
                textview_write_body(textview, mimeinfo, fp, charset);
540 1 hiro
        }
541 1 hiro
}
542 1 hiro
543 1 hiro
static void textview_add_parts(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
544 1 hiro
{
545 1 hiro
        gint level;
546 1 hiro
547 1 hiro
        g_return_if_fail(mimeinfo != NULL);
548 1 hiro
        g_return_if_fail(fp != NULL);
549 1 hiro
550 1 hiro
        level = mimeinfo->level;
551 1 hiro
552 1 hiro
        for (;;) {
553 1 hiro
                textview_add_part(textview, mimeinfo, fp);
554 1 hiro
                if (mimeinfo->parent && mimeinfo->parent->content_type &&
555 1 hiro
                    !strcasecmp(mimeinfo->parent->content_type,
556 1 hiro
                                "multipart/alternative"))
557 1 hiro
                        mimeinfo = mimeinfo->parent->next;
558 1 hiro
                else
559 1 hiro
                        mimeinfo = procmime_mimeinfo_next(mimeinfo);
560 1 hiro
                if (!mimeinfo || mimeinfo->level <= level)
561 1 hiro
                        break;
562 1 hiro
        }
563 1 hiro
}
564 1 hiro
565 1 hiro
#define TEXT_INSERT(str) \
566 1 hiro
        gtk_text_buffer_insert(buffer, &iter, str, -1)
567 1 hiro
568 1 hiro
void textview_show_error(TextView *textview)
569 1 hiro
{
570 1 hiro
        GtkTextBuffer *buffer;
571 1 hiro
        GtkTextIter iter;
572 1 hiro
573 1 hiro
        textview_set_font(textview, NULL);
574 1 hiro
        textview_clear(textview);
575 1 hiro
576 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
577 1 hiro
        gtk_text_buffer_get_start_iter(buffer, &iter);
578 1 hiro
        TEXT_INSERT(_("This message can't be displayed.\n"));
579 1 hiro
}
580 1 hiro
581 1 hiro
void textview_show_mime_part(TextView *textview, MimeInfo *partinfo)
582 1 hiro
{
583 1 hiro
        GtkTextBuffer *buffer;
584 1 hiro
        GtkTextIter iter;
585 1 hiro
586 1 hiro
        if (!partinfo) return;
587 1 hiro
588 1 hiro
        textview_set_font(textview, NULL);
589 1 hiro
        textview_clear(textview);
590 1 hiro
591 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
592 1 hiro
        gtk_text_buffer_get_start_iter(buffer, &iter);
593 1 hiro
594 1 hiro
        TEXT_INSERT(_("To save this part, pop up the context menu with "));
595 1 hiro
        TEXT_INSERT(_("right click and select `Save as...', "));
596 1 hiro
        TEXT_INSERT(_("or press `y' key.\n\n"));
597 1 hiro
598 1 hiro
        TEXT_INSERT(_("To display this part as a text message, select "));
599 1 hiro
        TEXT_INSERT(_("`Display as text', or press `t' key.\n\n"));
600 1 hiro
601 1 hiro
        TEXT_INSERT(_("To open this part with external program, select "));
602 1 hiro
        TEXT_INSERT(_("`Open' or `Open with...', "));
603 1 hiro
        TEXT_INSERT(_("or double-click, or click the center button, "));
604 1 hiro
        TEXT_INSERT(_("or press `l' key."));
605 1 hiro
}
606 1 hiro
607 1 hiro
#if USE_GPGME
608 1 hiro
void textview_show_signature_part(TextView *textview, MimeInfo *partinfo)
609 1 hiro
{
610 1 hiro
        GtkTextBuffer *buffer;
611 1 hiro
        GtkTextIter iter;
612 1 hiro
613 1 hiro
        if (!partinfo) return;
614 1 hiro
615 1 hiro
        textview_set_font(textview, NULL);
616 1 hiro
        textview_clear(textview);
617 1 hiro
618 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
619 1 hiro
        gtk_text_buffer_get_start_iter(buffer, &iter);
620 1 hiro
621 1 hiro
        if (partinfo->sigstatus_full == NULL) {
622 1 hiro
                TEXT_INSERT(_("This signature has not been checked yet.\n"));
623 1 hiro
                TEXT_INSERT(_("To check it, pop up the context menu with\n"));
624 1 hiro
                TEXT_INSERT(_("right click and select `Check signature'.\n"));
625 1 hiro
        } else {
626 1 hiro
                TEXT_INSERT(partinfo->sigstatus_full);
627 1 hiro
        }
628 1 hiro
}
629 1 hiro
#endif /* USE_GPGME */
630 1 hiro
631 1 hiro
#undef TEXT_INSERT
632 1 hiro
633 1 hiro
static void textview_write_body(TextView *textview, MimeInfo *mimeinfo,
634 1 hiro
                                FILE *fp, const gchar *charset)
635 1 hiro
{
636 1 hiro
        FILE *tmpfp;
637 1 hiro
        gchar buf[BUFFSIZE];
638 1 hiro
        CodeConverter *conv;
639 1 hiro
640 1 hiro
        conv = conv_code_converter_new(charset);
641 1 hiro
642 1 hiro
        tmpfp = procmime_decode_content(NULL, fp, mimeinfo);
643 1 hiro
        if (tmpfp) {
644 1 hiro
                if (mimeinfo->mime_type == MIME_TEXT_HTML)
645 1 hiro
                        textview_show_html(textview, tmpfp, conv);
646 1 hiro
                else
647 1 hiro
                        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
648 1 hiro
                                textview_write_line(textview, buf, conv);
649 1 hiro
                fclose(tmpfp);
650 1 hiro
        }
651 1 hiro
652 1 hiro
        conv_code_converter_destroy(conv);
653 1 hiro
}
654 1 hiro
655 1 hiro
static void textview_show_html(TextView *textview, FILE *fp,
656 1 hiro
                               CodeConverter *conv)
657 1 hiro
{
658 1 hiro
        HTMLParser *parser;
659 1 hiro
        gchar *str;
660 1 hiro
661 1 hiro
        parser = html_parser_new(fp, conv);
662 1 hiro
        g_return_if_fail(parser != NULL);
663 1 hiro
664 1 hiro
        while ((str = html_parse(parser)) != NULL) {
665 1 hiro
                if (parser->href != NULL)
666 1 hiro
                        textview_write_link(textview, str, parser->href, NULL);
667 1 hiro
                else
668 1 hiro
                        textview_write_line(textview, str, NULL);
669 1 hiro
        }
670 1 hiro
        html_parser_destroy(parser);
671 1 hiro
}
672 1 hiro
673 1 hiro
/* get_uri_part() - retrieves a URI starting from scanpos.
674 1 hiro
                    Returns TRUE if succesful */
675 1 hiro
static gboolean get_uri_part(const gchar *start, const gchar *scanpos,
676 1 hiro
                             const gchar **bp, const gchar **ep)
677 1 hiro
{
678 1 hiro
        const gchar *ep_;
679 1 hiro
680 1 hiro
        g_return_val_if_fail(start != NULL, FALSE);
681 1 hiro
        g_return_val_if_fail(scanpos != NULL, FALSE);
682 1 hiro
        g_return_val_if_fail(bp != NULL, FALSE);
683 1 hiro
        g_return_val_if_fail(ep != NULL, FALSE);
684 1 hiro
685 1 hiro
        *bp = scanpos;
686 1 hiro
687 1 hiro
        /* find end point of URI */
688 1 hiro
        for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
689 1 hiro
                if (!isgraph(*(const guchar *)ep_) ||
690 1 hiro
                    !isascii(*(const guchar *)ep_) ||
691 1 hiro
                    strchr("()<>\"", *ep_))
692 1 hiro
                        break;
693 1 hiro
        }
694 1 hiro
695 1 hiro
        /* no punctuation at end of string */
696 1 hiro
697 1 hiro
        /* FIXME: this stripping of trailing punctuations may bite with other URIs.
698 1 hiro
         * should pass some URI type to this function and decide on that whether
699 1 hiro
         * to perform punctuation stripping */
700 1 hiro
701 1 hiro
#define IS_REAL_PUNCT(ch)        (ispunct(ch) && ((ch) != '/'))
702 1 hiro
703 1 hiro
        for (; ep_ - 1 > scanpos + 1 &&
704 1 hiro
               IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
705 1 hiro
             ep_--)
706 1 hiro
                ;
707 1 hiro
708 1 hiro
#undef IS_REAL_PUNCT
709 1 hiro
710 1 hiro
        *ep = ep_;
711 1 hiro
712 1 hiro
        return TRUE;
713 1 hiro
}
714 1 hiro
715 1 hiro
static gchar *make_uri_string(const gchar *bp, const gchar *ep)
716 1 hiro
{
717 1 hiro
        return g_strndup(bp, ep - bp);
718 1 hiro
}
719 1 hiro
720 1 hiro
/* valid mail address characters */
721 1 hiro
#define IS_RFC822_CHAR(ch) \
722 1 hiro
        (isascii(ch) && \
723 1 hiro
         (ch) > 32   && \
724 1 hiro
         (ch) != 127 && \
725 1 hiro
         !isspace(ch) && \
726 1 hiro
         !strchr("(),;<>\"", (ch)))
727 1 hiro
728 1 hiro
/* alphabet and number within 7bit ASCII */
729 1 hiro
#define IS_ASCII_ALNUM(ch)        (isascii(ch) && isalnum(ch))
730 1 hiro
731 1 hiro
/* get_email_part() - retrieves an email address. Returns TRUE if succesful */
732 1 hiro
static gboolean get_email_part(const gchar *start, const gchar *scanpos,
733 1 hiro
                               const gchar **bp, const gchar **ep)
734 1 hiro
{
735 1 hiro
        /* more complex than the uri part because we need to scan back and forward starting from
736 1 hiro
         * the scan position. */
737 1 hiro
        gboolean result = FALSE;
738 1 hiro
        const gchar *bp_;
739 1 hiro
        const gchar *ep_;
740 1 hiro
741 1 hiro
        g_return_val_if_fail(start != NULL, FALSE);
742 1 hiro
        g_return_val_if_fail(scanpos != NULL, FALSE);
743 1 hiro
        g_return_val_if_fail(bp != NULL, FALSE);
744 1 hiro
        g_return_val_if_fail(ep != NULL, FALSE);
745 1 hiro
746 1 hiro
        /* scan start of address */
747 1 hiro
        for (bp_ = scanpos - 1;
748 1 hiro
             bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
749 1 hiro
                ;
750 1 hiro
751 1 hiro
        /* TODO: should start with an alnum? */
752 1 hiro
        bp_++;
753 1 hiro
        for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
754 1 hiro
                ;
755 1 hiro
756 1 hiro
        if (bp_ != scanpos) {
757 1 hiro
                /* scan end of address */
758 1 hiro
                for (ep_ = scanpos + 1;
759 1 hiro
                     *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
760 1 hiro
                        ;
761 1 hiro
762 1 hiro
                /* TODO: really should terminate with an alnum? */
763 1 hiro
                for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
764 1 hiro
                     --ep_)
765 1 hiro
                        ;
766 1 hiro
                ep_++;
767 1 hiro
768 1 hiro
                if (ep_ > scanpos + 1) {
769 1 hiro
                        *ep = ep_;
770 1 hiro
                        *bp = bp_;
771 1 hiro
                        result = TRUE;
772 1 hiro
                }
773 1 hiro
        }
774 1 hiro
775 1 hiro
        return result;
776 1 hiro
}
777 1 hiro
778 1 hiro
#undef IS_ASCII_ALNUM
779 1 hiro
#undef IS_RFC822_CHAR
780 1 hiro
781 1 hiro
static gchar *make_email_string(const gchar *bp, const gchar *ep)
782 1 hiro
{
783 1 hiro
        /* returns a mailto: URI; mailto: is also used to detect the
784 1 hiro
         * uri type later on in the button_pressed signal handler */
785 1 hiro
        gchar *tmp;
786 1 hiro
        gchar *result;
787 1 hiro
788 1 hiro
        tmp = g_strndup(bp, ep - bp);
789 1 hiro
        result = g_strconcat("mailto:", tmp, NULL);
790 1 hiro
        g_free(tmp);
791 1 hiro
792 1 hiro
        return result;
793 1 hiro
}
794 1 hiro
795 1 hiro
#define ADD_TXT_POS(bp_, ep_, pti_) \
796 1 hiro
        if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
797 1 hiro
                last = last->next; \
798 1 hiro
                last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
799 1 hiro
                last->next = NULL; \
800 1 hiro
        } else { \
801 1 hiro
                g_warning("alloc error scanning URIs\n"); \
802 1 hiro
                gtk_text_buffer_insert_with_tags_by_name \
803 1 hiro
                        (buffer, &iter, linebuf, -1, fg_tag, NULL); \
804 1 hiro
                return; \
805 1 hiro
        }
806 1 hiro
807 1 hiro
/* textview_make_clickable_parts() - colorizes clickable parts */
808 1 hiro
static void textview_make_clickable_parts(TextView *textview,
809 1 hiro
                                          const gchar *fg_tag,
810 1 hiro
                                          const gchar *uri_tag,
811 1 hiro
                                          const gchar *linebuf)
812 1 hiro
{
813 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
814 1 hiro
        GtkTextBuffer *buffer;
815 1 hiro
        GtkTextIter iter;
816 1 hiro
817 1 hiro
        /* parse table - in order of priority */
818 1 hiro
        struct table {
819 1 hiro
                const gchar *needle; /* token */
820 1 hiro
821 1 hiro
                /* token search function */
822 1 hiro
                gchar    *(*search)        (const gchar *haystack,
823 1 hiro
                                         const gchar *needle);
824 1 hiro
                /* part parsing function */
825 1 hiro
                gboolean  (*parse)        (const gchar *start,
826 1 hiro
                                         const gchar *scanpos,
827 1 hiro
                                         const gchar **bp_,
828 1 hiro
                                         const gchar **ep_);
829 1 hiro
                /* part to URI function */
830 1 hiro
                gchar    *(*build_uri)        (const gchar *bp,
831 1 hiro
                                         const gchar *ep);
832 1 hiro
        };
833 1 hiro
834 1 hiro
        static struct table parser[] = {
835 1 hiro
                {"http://",  strcasestr, get_uri_part,   make_uri_string},
836 1 hiro
                {"https://", strcasestr, get_uri_part,   make_uri_string},
837 1 hiro
                {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
838 1 hiro
                {"www.",     strcasestr, get_uri_part,   make_uri_string},
839 1 hiro
                {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
840 1 hiro
                {"@",        strcasestr, get_email_part, make_email_string}
841 1 hiro
        };
842 1 hiro
        const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
843 1 hiro
844 1 hiro
        gint  n;
845 1 hiro
        const gchar *walk, *bp, *ep;
846 1 hiro
847 1 hiro
        struct txtpos {
848 1 hiro
                const gchar        *bp, *ep;        /* text position */
849 1 hiro
                gint                 pti;                /* index in parse table */
850 1 hiro
                struct txtpos        *next;                /* next */
851 1 hiro
        } head = {NULL, NULL, 0,  NULL}, *last = &head;
852 1 hiro
853 1 hiro
        buffer = gtk_text_view_get_buffer(text);
854 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
855 1 hiro
856 1 hiro
        /* parse for clickable parts, and build a list of begin and
857 1 hiro
           end positions  */
858 1 hiro
        for (walk = linebuf, n = 0;;) {
859 1 hiro
                gint last_index = PARSE_ELEMS;
860 1 hiro
                gchar *scanpos = NULL;
861 1 hiro
862 1 hiro
                /* FIXME: this looks phony. scanning for anything in the
863 1 hiro
                   parse table */
864 1 hiro
                for (n = 0; n < PARSE_ELEMS; n++) {
865 1 hiro
                        gchar *tmp;
866 1 hiro
867 1 hiro
                        tmp = parser[n].search(walk, parser[n].needle);
868 1 hiro
                        if (tmp) {
869 1 hiro
                                if (scanpos == NULL || tmp < scanpos) {
870 1 hiro
                                        scanpos = tmp;
871 1 hiro
                                        last_index = n;
872 1 hiro
                                }
873 1 hiro
                        }
874 1 hiro
                }
875 1 hiro
876 1 hiro
                if (scanpos) {
877 1 hiro
                        /* check if URI can be parsed */
878 1 hiro
                        if (parser[last_index].parse(walk, scanpos, &bp, &ep)
879 1 hiro
                            && (ep - bp - 1) > strlen(parser[last_index].needle)) {
880 1 hiro
                                        ADD_TXT_POS(bp, ep, last_index);
881 1 hiro
                                        walk = ep;
882 1 hiro
                        } else
883 1 hiro
                                walk = scanpos +
884 1 hiro
                                        strlen(parser[last_index].needle);
885 1 hiro
                } else
886 1 hiro
                        break;
887 1 hiro
        }
888 1 hiro
889 1 hiro
        /* colorize this line */
890 1 hiro
        if (head.next) {
891 1 hiro
                const gchar *normal_text = linebuf;
892 1 hiro
893 1 hiro
                /* insert URIs */
894 1 hiro
                for (last = head.next; last != NULL;
895 1 hiro
                     normal_text = last->ep, last = last->next) {
896 1 hiro
                        RemoteURI *uri;
897 1 hiro
898 1 hiro
                        uri = g_new(RemoteURI, 1);
899 1 hiro
                        if (last->bp - normal_text > 0)
900 1 hiro
                                gtk_text_buffer_insert_with_tags_by_name
901 1 hiro
                                        (buffer, &iter,
902 1 hiro
                                         normal_text,
903 1 hiro
                                         last->bp - normal_text,
904 1 hiro
                                         fg_tag, NULL);
905 1 hiro
                        uri->uri = parser[last->pti].build_uri(last->bp,
906 1 hiro
                                                               last->ep);
907 1 hiro
                        uri->start = gtk_text_iter_get_offset(&iter);
908 1 hiro
                        gtk_text_buffer_insert_with_tags_by_name
909 1 hiro
                                (buffer, &iter, last->bp, last->ep - last->bp,
910 1 hiro
                                 uri_tag, NULL);
911 1 hiro
                        uri->end = gtk_text_iter_get_offset(&iter);
912 1 hiro
                        textview->uri_list =
913 1 hiro
                                g_slist_append(textview->uri_list, uri);
914 1 hiro
                }
915 1 hiro
916 1 hiro
                if (*normal_text)
917 1 hiro
                        gtk_text_buffer_insert_with_tags_by_name
918 1 hiro
                                (buffer, &iter, normal_text, -1, fg_tag, NULL);
919 1 hiro
        } else {
920 1 hiro
                gtk_text_buffer_insert_with_tags_by_name
921 1 hiro
                        (buffer, &iter, linebuf, -1, fg_tag, NULL);
922 1 hiro
        }
923 1 hiro
}
924 1 hiro
925 1 hiro
#undef ADD_TXT_POS
926 1 hiro
927 1 hiro
static void textview_write_line(TextView *textview, const gchar *str,
928 1 hiro
                                CodeConverter *conv)
929 1 hiro
{
930 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
931 1 hiro
        GtkTextBuffer *buffer;
932 1 hiro
        GtkTextIter iter;
933 1 hiro
        gchar buf[BUFFSIZE];
934 1 hiro
        gchar *fg_color;
935 1 hiro
        gint quotelevel = -1;
936 1 hiro
        gchar quote_tag_str[10];
937 1 hiro
938 1 hiro
        buffer = gtk_text_view_get_buffer(text);
939 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
940 1 hiro
941 1 hiro
        if (!conv)
942 1 hiro
                strncpy2(buf, str, sizeof(buf));
943 1 hiro
        else if (conv_convert(conv, buf, sizeof(buf), str) < 0)
944 1 hiro
                conv_localetodisp(buf, sizeof(buf), str);
945 1 hiro
946 1 hiro
        strcrchomp(buf);
947 1 hiro
        //if (prefs_common.conv_mb_alnum) conv_mb_alnum(buf);
948 1 hiro
        fg_color = NULL;
949 1 hiro
950 1 hiro
        /* change color of quotation
951 1 hiro
           >, foo>, _> ... ok, <foo>, foo bar>, foo-> ... ng
952 1 hiro
           Up to 3 levels of quotations are detected, and each
953 1 hiro
           level is colored using a different color. */
954 1 hiro
        if (prefs_common.enable_color && strchr(buf, '>')) {
955 1 hiro
                quotelevel = get_quote_level(buf);
956 1 hiro
957 1 hiro
                /* set up the correct foreground color */
958 1 hiro
                if (quotelevel > 2) {
959 1 hiro
                        /* recycle colors */
960 1 hiro
                        if (prefs_common.recycle_quote_colors)
961 1 hiro
                                quotelevel %= 3;
962 1 hiro
                        else
963 1 hiro
                                quotelevel = 2;
964 1 hiro
                }
965 1 hiro
        }
966 1 hiro
967 1 hiro
        if (quotelevel == -1)
968 1 hiro
                fg_color = NULL;
969 1 hiro
        else {
970 1 hiro
                g_snprintf(quote_tag_str, sizeof(quote_tag_str),
971 1 hiro
                           "quote%d", quotelevel);
972 1 hiro
                fg_color = quote_tag_str;
973 1 hiro
        }
974 1 hiro
975 1 hiro
        if (prefs_common.enable_color)
976 1 hiro
                textview_make_clickable_parts(textview, fg_color, "link", buf);
977 1 hiro
        else
978 1 hiro
                textview_make_clickable_parts(textview, fg_color, NULL, buf);
979 1 hiro
}
980 1 hiro
981 1 hiro
void textview_write_link(TextView *textview, const gchar *str,
982 1 hiro
                         const gchar *uri, CodeConverter *conv)
983 1 hiro
{
984 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
985 1 hiro
        GtkTextBuffer *buffer;
986 1 hiro
        GtkTextIter iter;
987 1 hiro
        gchar buf[BUFFSIZE];
988 1 hiro
        gchar *bufp;
989 1 hiro
        RemoteURI *r_uri;
990 1 hiro
991 1 hiro
        if (*str == '\0')
992 1 hiro
                return;
993 1 hiro
994 1 hiro
        buffer = gtk_text_view_get_buffer(text);
995 1 hiro
        gtk_text_buffer_get_end_iter(buffer, &iter);
996 1 hiro
997 1 hiro
        if (!conv)
998 1 hiro
                strncpy2(buf, str, sizeof(buf));
999 1 hiro
        else if (conv_convert(conv, buf, sizeof(buf), str) < 0)
1000 1 hiro
                conv_localetodisp(buf, sizeof(buf), str);
1001 1 hiro
1002 1 hiro
        strcrchomp(buf);
1003 1 hiro
1004 1 hiro
        for (bufp = buf; isspace(*(guchar *)bufp); bufp++)
1005 1 hiro
                gtk_text_buffer_insert(buffer, &iter, bufp, 1);
1006 1 hiro
1007 1 hiro
        r_uri = g_new(RemoteURI, 1);
1008 1 hiro
        r_uri->uri = g_strdup(uri);
1009 1 hiro
        r_uri->start = gtk_text_iter_get_offset(&iter);
1010 1 hiro
        gtk_text_buffer_insert_with_tags_by_name
1011 1 hiro
                (buffer, &iter, bufp, -1, "link", NULL);
1012 1 hiro
        r_uri->end = gtk_text_iter_get_offset(&iter);
1013 1 hiro
        textview->uri_list = g_slist_append(textview->uri_list, r_uri);
1014 1 hiro
}
1015 1 hiro
1016 1 hiro
void textview_clear(TextView *textview)
1017 1 hiro
{
1018 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1019 1 hiro
        GtkTextBuffer *buffer;
1020 1 hiro
1021 1 hiro
        buffer = gtk_text_view_get_buffer(text);
1022 1 hiro
        gtk_text_buffer_set_text(buffer, "", -1);
1023 1 hiro
1024 1 hiro
        STATUSBAR_POP(textview);
1025 1 hiro
        textview_uri_list_remove_all(textview->uri_list);
1026 1 hiro
        textview->uri_list = NULL;
1027 1 hiro
1028 1 hiro
        textview->body_pos = 0;
1029 1 hiro
        //textview->cur_pos  = 0;
1030 1 hiro
}
1031 1 hiro
1032 1 hiro
void textview_destroy(TextView *textview)
1033 1 hiro
{
1034 1 hiro
        textview_uri_list_remove_all(textview->uri_list);
1035 1 hiro
        textview->uri_list = NULL;
1036 1 hiro
        g_free(textview);
1037 1 hiro
}
1038 1 hiro
1039 1 hiro
void textview_set_all_headers(TextView *textview, gboolean all_headers)
1040 1 hiro
{
1041 1 hiro
        textview->show_all_headers = all_headers;
1042 1 hiro
}
1043 1 hiro
1044 1 hiro
void textview_set_font(TextView *textview, const gchar *codeset)
1045 1 hiro
{
1046 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1047 1 hiro
1048 1 hiro
        if (prefs_common.textfont) {
1049 1 hiro
                PangoFontDescription *font_desc;
1050 1 hiro
                font_desc = pango_font_description_from_string
1051 1 hiro
                        (prefs_common.textfont);
1052 1 hiro
                if (font_desc) {
1053 1 hiro
                        gtk_widget_modify_font(textview->text, font_desc);
1054 1 hiro
                        pango_font_description_free(font_desc);
1055 1 hiro
                }
1056 1 hiro
        }
1057 1 hiro
1058 1 hiro
        gtk_text_view_set_pixels_above_lines(text, prefs_common.line_space / 2);
1059 1 hiro
        gtk_text_view_set_pixels_below_lines(text, prefs_common.line_space / 2);
1060 1 hiro
}
1061 1 hiro
1062 1 hiro
void textview_set_position(TextView *textview, gint pos)
1063 1 hiro
{
1064 1 hiro
        GtkTextBuffer *buffer;
1065 1 hiro
        GtkTextIter iter;
1066 1 hiro
1067 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
1068 1 hiro
        gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
1069 1 hiro
        gtk_text_buffer_place_cursor(buffer, &iter);
1070 1 hiro
}
1071 1 hiro
1072 1 hiro
static GPtrArray *textview_scan_header(TextView *textview, FILE *fp)
1073 1 hiro
{
1074 1 hiro
        gchar buf[BUFFSIZE];
1075 1 hiro
        GPtrArray *headers, *sorted_headers;
1076 1 hiro
        GSList *disphdr_list;
1077 1 hiro
        Header *header;
1078 1 hiro
        gint i;
1079 1 hiro
1080 1 hiro
        g_return_val_if_fail(fp != NULL, NULL);
1081 1 hiro
1082 1 hiro
        if (textview->show_all_headers)
1083 1 hiro
                return procheader_get_header_array_asis(fp);
1084 1 hiro
1085 1 hiro
        if (!prefs_common.display_header) {
1086 1 hiro
                while (fgets(buf, sizeof(buf), fp) != NULL)
1087 1 hiro
                        if (buf[0] == '\r' || buf[0] == '\n') break;
1088 1 hiro
                return NULL;
1089 1 hiro
        }
1090 1 hiro
1091 1 hiro
        headers = procheader_get_header_array_asis(fp);
1092 1 hiro
1093 1 hiro
        sorted_headers = g_ptr_array_new();
1094 1 hiro
1095 1 hiro
        for (disphdr_list = prefs_common.disphdr_list; disphdr_list != NULL;
1096 1 hiro
             disphdr_list = disphdr_list->next) {
1097 1 hiro
                DisplayHeaderProp *dp =
1098 1 hiro
                        (DisplayHeaderProp *)disphdr_list->data;
1099 1 hiro
1100 1 hiro
                for (i = 0; i < headers->len; i++) {
1101 1 hiro
                        header = g_ptr_array_index(headers, i);
1102 1 hiro
1103 1 hiro
                        if (!g_strcasecmp(header->name, dp->name)) {
1104 1 hiro
                                if (dp->hidden)
1105 1 hiro
                                        procheader_header_free(header);
1106 1 hiro
                                else
1107 1 hiro
                                        g_ptr_array_add(sorted_headers, header);
1108 1 hiro
1109 1 hiro
                                g_ptr_array_remove_index(headers, i);
1110 1 hiro
                                i--;
1111 1 hiro
                        }
1112 1 hiro
                }
1113 1 hiro
        }
1114 1 hiro
1115 1 hiro
        if (prefs_common.show_other_header) {
1116 1 hiro
                for (i = 0; i < headers->len; i++) {
1117 1 hiro
                        header = g_ptr_array_index(headers, i);
1118 1 hiro
                        g_ptr_array_add(sorted_headers, header);
1119 1 hiro
                }
1120 1 hiro
                g_ptr_array_free(headers, TRUE);
1121 1 hiro
        } else
1122 1 hiro
                procheader_header_array_destroy(headers);
1123 1 hiro
1124 1 hiro
1125 1 hiro
        return sorted_headers;
1126 1 hiro
}
1127 1 hiro
1128 1 hiro
static void textview_show_header(TextView *textview, GPtrArray *headers)
1129 1 hiro
{
1130 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1131 1 hiro
        GtkTextBuffer *buffer;
1132 1 hiro
        GtkTextIter iter;
1133 1 hiro
        Header *header;
1134 1 hiro
        gint i;
1135 1 hiro
1136 1 hiro
        g_return_if_fail(headers != NULL);
1137 1 hiro
1138 1 hiro
        buffer = gtk_text_view_get_buffer(text);
1139 1 hiro
1140 1 hiro
        for (i = 0; i < headers->len; i++) {
1141 1 hiro
                header = g_ptr_array_index(headers, i);
1142 1 hiro
                g_return_if_fail(header->name != NULL);
1143 1 hiro
1144 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter);
1145 1 hiro
                gtk_text_buffer_insert_with_tags_by_name
1146 1 hiro
                        (buffer, &iter, header->name, -1,
1147 1 hiro
                         "header_title", "header", NULL);
1148 1 hiro
                gtk_text_buffer_insert_with_tags_by_name
1149 1 hiro
                        (buffer, &iter, ":", 1,
1150 1 hiro
                         "header_title", "header", NULL);
1151 1 hiro
1152 1 hiro
                if (!g_strcasecmp(header->name, "Subject") ||
1153 1 hiro
                    !g_strcasecmp(header->name, "From")    ||
1154 1 hiro
                    !g_strcasecmp(header->name, "To")      ||
1155 1 hiro
                    !g_strcasecmp(header->name, "Cc"))
1156 1 hiro
                        unfold_line(header->body);
1157 1 hiro
1158 1 hiro
#if 0
1159 1 hiro
                if (textview->text_is_mb == TRUE)
1160 1 hiro
                        conv_unreadable_locale(header->body);
1161 1 hiro
#endif
1162 1 hiro
1163 1 hiro
                if (prefs_common.enable_color &&
1164 1 hiro
                    (!strncmp(header->name, "X-Mailer", 8) ||
1165 1 hiro
                     !strncmp(header->name, "X-Newsreader", 12)) &&
1166 1 hiro
                    strstr(header->body, "Sylpheed") != NULL) {
1167 1 hiro
                        gtk_text_buffer_get_end_iter(buffer, &iter);
1168 1 hiro
                        gtk_text_buffer_insert_with_tags_by_name
1169 1 hiro
                                (buffer, &iter, header->body, -1,
1170 1 hiro
                                 "header", "emphasis", NULL);
1171 1 hiro
                } else if (prefs_common.enable_color) {
1172 1 hiro
                        textview_make_clickable_parts
1173 1 hiro
                                (textview, "header", "link", header->body);
1174 1 hiro
                } else {
1175 1 hiro
                        textview_make_clickable_parts
1176 1 hiro
                                (textview, "header", NULL, header->body);
1177 1 hiro
                }
1178 1 hiro
                gtk_text_buffer_get_end_iter(buffer, &iter); //
1179 1 hiro
                gtk_text_buffer_insert_with_tags_by_name
1180 1 hiro
                        (buffer, &iter, "\n", 1, "header", NULL);
1181 1 hiro
        }
1182 1 hiro
}
1183 1 hiro
1184 1 hiro
gboolean textview_search_string(TextView *textview, const gchar *str,
1185 1 hiro
                                gboolean case_sens)
1186 1 hiro
{
1187 1 hiro
#warning FIXME_GTK2
1188 1 hiro
#if 0
1189 1 hiro
        GtkSText *text = GTK_STEXT(textview->text);
1190 1 hiro
        gint pos;
1191 1 hiro
        gint len;
1192 1 hiro
1193 1 hiro
        g_return_val_if_fail(str != NULL, FALSE);
1194 1 hiro
1195 1 hiro
        len = get_mbs_len(str);
1196 1 hiro
        g_return_val_if_fail(len >= 0, FALSE);
1197 1 hiro
1198 1 hiro
        pos = textview->cur_pos;
1199 1 hiro
        if (pos < textview->body_pos)
1200 1 hiro
                pos = textview->body_pos;
1201 1 hiro
1202 1 hiro
        if ((pos = gtkut_stext_find(text, pos, str, case_sens)) != -1) {
1203 1 hiro
                gtk_editable_set_position(GTK_EDITABLE(text), pos + len);
1204 1 hiro
                gtk_editable_select_region(GTK_EDITABLE(text), pos, pos + len);
1205 1 hiro
                textview_set_position(textview, pos + len);
1206 1 hiro
                return TRUE;
1207 1 hiro
        }
1208 1 hiro
1209 1 hiro
#endif
1210 1 hiro
        return FALSE;
1211 1 hiro
}
1212 1 hiro
1213 1 hiro
gboolean textview_search_string_backward(TextView *textview, const gchar *str,
1214 1 hiro
                                         gboolean case_sens)
1215 1 hiro
{
1216 1 hiro
#warning FIXME_GTK2
1217 1 hiro
#if 0
1218 1 hiro
        GtkSText *text = GTK_STEXT(textview->text);
1219 1 hiro
        gint pos;
1220 1 hiro
        wchar_t *wcs;
1221 1 hiro
        gint len;
1222 1 hiro
        gint text_len;
1223 1 hiro
        gboolean found = FALSE;
1224 1 hiro
1225 1 hiro
        g_return_val_if_fail(str != NULL, FALSE);
1226 1 hiro
1227 1 hiro
        wcs = strdup_mbstowcs(str);
1228 1 hiro
        g_return_val_if_fail(wcs != NULL, FALSE);
1229 1 hiro
        len = wcslen(wcs);
1230 1 hiro
        pos = textview->cur_pos;
1231 1 hiro
        text_len = gtk_stext_get_length(text);
1232 1 hiro
        if (text_len - textview->body_pos < len) {
1233 1 hiro
                g_free(wcs);
1234 1 hiro
                return FALSE;
1235 1 hiro
        }
1236 1 hiro
        if (pos <= textview->body_pos || text_len - pos < len)
1237 1 hiro
                pos = text_len - len;
1238 1 hiro
1239 1 hiro
        for (; pos >= textview->body_pos; pos--) {
1240 1 hiro
                if (gtkut_stext_match_string(text, pos, wcs, len, case_sens)
1241 1 hiro
                    == TRUE) {
1242 1 hiro
                        gtk_editable_set_position(GTK_EDITABLE(text), pos);
1243 1 hiro
                        gtk_editable_select_region(GTK_EDITABLE(text),
1244 1 hiro
                                                   pos, pos + len);
1245 1 hiro
                        textview_set_position(textview, pos - 1);
1246 1 hiro
                        found = TRUE;
1247 1 hiro
                        break;
1248 1 hiro
                }
1249 1 hiro
                if (pos == textview->body_pos) break;
1250 1 hiro
        }
1251 1 hiro
1252 1 hiro
        g_free(wcs);
1253 1 hiro
        return found;
1254 1 hiro
#endif
1255 1 hiro
        return FALSE;
1256 1 hiro
}
1257 1 hiro
1258 1 hiro
void textview_scroll_one_line(TextView *textview, gboolean up)
1259 1 hiro
{
1260 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1261 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1262 1 hiro
        gfloat upper;
1263 1 hiro
1264 1 hiro
        if (prefs_common.enable_smooth_scroll) {
1265 1 hiro
                textview_smooth_scroll_one_line(textview, up);
1266 1 hiro
                return;
1267 1 hiro
        }
1268 1 hiro
1269 1 hiro
        if (!up) {
1270 1 hiro
                upper = vadj->upper - vadj->page_size;
1271 1 hiro
                if (vadj->value < upper) {
1272 1 hiro
                        vadj->value += vadj->step_increment * 4;
1273 1 hiro
                        vadj->value = MIN(vadj->value, upper);
1274 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1275 1 hiro
                                              "value_changed", 0);
1276 1 hiro
                }
1277 1 hiro
        } else {
1278 1 hiro
                if (vadj->value > 0.0) {
1279 1 hiro
                        vadj->value -= vadj->step_increment * 4;
1280 1 hiro
                        vadj->value = MAX(vadj->value, 0.0);
1281 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1282 1 hiro
                                              "value_changed", 0);
1283 1 hiro
                }
1284 1 hiro
        }
1285 1 hiro
}
1286 1 hiro
1287 1 hiro
gboolean textview_scroll_page(TextView *textview, gboolean up)
1288 1 hiro
{
1289 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1290 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1291 1 hiro
        gfloat upper;
1292 1 hiro
        gfloat page_incr;
1293 1 hiro
1294 1 hiro
        if (prefs_common.enable_smooth_scroll)
1295 1 hiro
                return textview_smooth_scroll_page(textview, up);
1296 1 hiro
1297 1 hiro
        if (prefs_common.scroll_halfpage)
1298 1 hiro
                page_incr = vadj->page_increment / 2;
1299 1 hiro
        else
1300 1 hiro
                page_incr = vadj->page_increment;
1301 1 hiro
1302 1 hiro
        if (!up) {
1303 1 hiro
                upper = vadj->upper - vadj->page_size;
1304 1 hiro
                if (vadj->value < upper) {
1305 1 hiro
                        vadj->value += page_incr;
1306 1 hiro
                        vadj->value = MIN(vadj->value, upper);
1307 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1308 1 hiro
                                              "value_changed", 0);
1309 1 hiro
                } else
1310 1 hiro
                        return FALSE;
1311 1 hiro
        } else {
1312 1 hiro
                if (vadj->value > 0.0) {
1313 1 hiro
                        vadj->value -= page_incr;
1314 1 hiro
                        vadj->value = MAX(vadj->value, 0.0);
1315 1 hiro
                        g_signal_emit_by_name(G_OBJECT(vadj),
1316 1 hiro
                                              "value_changed", 0);
1317 1 hiro
                } else
1318 1 hiro
                        return FALSE;
1319 1 hiro
        }
1320 1 hiro
1321 1 hiro
        return TRUE;
1322 1 hiro
}
1323 1 hiro
1324 1 hiro
static void textview_smooth_scroll_do(TextView *textview,
1325 1 hiro
                                      gfloat old_value, gfloat last_value,
1326 1 hiro
                                      gint step)
1327 1 hiro
{
1328 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1329 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1330 1 hiro
        gint change_value;
1331 1 hiro
        gboolean up;
1332 1 hiro
        gint i;
1333 1 hiro
1334 1 hiro
        if (old_value < last_value) {
1335 1 hiro
                change_value = last_value - old_value;
1336 1 hiro
                up = FALSE;
1337 1 hiro
        } else {
1338 1 hiro
                change_value = old_value - last_value;
1339 1 hiro
                up = TRUE;
1340 1 hiro
        }
1341 1 hiro
1342 1 hiro
#warning FIXME_GTK2
1343 1 hiro
        /* gdk_key_repeat_disable(); */
1344 1 hiro
1345 1 hiro
        for (i = step; i <= change_value; i += step) {
1346 1 hiro
                vadj->value = old_value + (up ? -i : i);
1347 1 hiro
                g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0);
1348 1 hiro
        }
1349 1 hiro
1350 1 hiro
        vadj->value = last_value;
1351 1 hiro
        g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0);
1352 1 hiro
1353 1 hiro
#warning FIXME_GTK2
1354 1 hiro
        /* gdk_key_repeat_restore(); */
1355 1 hiro
}
1356 1 hiro
1357 1 hiro
static void textview_smooth_scroll_one_line(TextView *textview, gboolean up)
1358 1 hiro
{
1359 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1360 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1361 1 hiro
        gfloat upper;
1362 1 hiro
        gfloat old_value;
1363 1 hiro
        gfloat last_value;
1364 1 hiro
1365 1 hiro
        if (!up) {
1366 1 hiro
                upper = vadj->upper - vadj->page_size;
1367 1 hiro
                if (vadj->value < upper) {
1368 1 hiro
                        old_value = vadj->value;
1369 1 hiro
                        last_value = vadj->value + vadj->step_increment * 4;
1370 1 hiro
                        last_value = MIN(last_value, upper);
1371 1 hiro
1372 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1373 1 hiro
                                                  last_value,
1374 1 hiro
                                                  prefs_common.scroll_step);
1375 1 hiro
                }
1376 1 hiro
        } else {
1377 1 hiro
                if (vadj->value > 0.0) {
1378 1 hiro
                        old_value = vadj->value;
1379 1 hiro
                        last_value = vadj->value - vadj->step_increment * 4;
1380 1 hiro
                        last_value = MAX(last_value, 0.0);
1381 1 hiro
1382 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1383 1 hiro
                                                  last_value,
1384 1 hiro
                                                  prefs_common.scroll_step);
1385 1 hiro
                }
1386 1 hiro
        }
1387 1 hiro
}
1388 1 hiro
1389 1 hiro
static gboolean textview_smooth_scroll_page(TextView *textview, gboolean up)
1390 1 hiro
{
1391 1 hiro
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1392 1 hiro
        GtkAdjustment *vadj = text->vadjustment;
1393 1 hiro
        gfloat upper;
1394 1 hiro
        gfloat page_incr;
1395 1 hiro
        gfloat old_value;
1396 1 hiro
        gfloat last_value;
1397 1 hiro
1398 1 hiro
        if (prefs_common.scroll_halfpage)
1399 1 hiro
                page_incr = vadj->page_increment / 2;
1400 1 hiro
        else
1401 1 hiro
                page_incr = vadj->page_increment;
1402 1 hiro
1403 1 hiro
        if (!up) {
1404 1 hiro
                upper = vadj->upper - vadj->page_size;
1405 1 hiro
                if (vadj->value < upper) {
1406 1 hiro
                        old_value = vadj->value;
1407 1 hiro
                        last_value = vadj->value + page_incr;
1408 1 hiro
                        last_value = MIN(last_value, upper);
1409 1 hiro
1410 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1411 1 hiro
                                                  last_value,
1412 1 hiro
                                                  prefs_common.scroll_step);
1413 1 hiro
                } else
1414 1 hiro
                        return FALSE;
1415 1 hiro
        } else {
1416 1 hiro
                if (vadj->value > 0.0) {
1417 1 hiro
                        old_value = vadj->value;
1418 1 hiro
                        last_value = vadj->value - page_incr;
1419 1 hiro
                        last_value = MAX(last_value, 0.0);
1420 1 hiro
1421 1 hiro
                        textview_smooth_scroll_do(textview, old_value,
1422 1 hiro
                                                  last_value,
1423 1 hiro
                                                  prefs_common.scroll_step);
1424 1 hiro
                } else
1425 1 hiro
                        return FALSE;
1426 1 hiro
        }
1427 1 hiro
1428 1 hiro
        return TRUE;
1429 1 hiro
}
1430 1 hiro
1431 1 hiro
#warning FIXME_GTK2
1432 1 hiro
#if 0
1433 1 hiro
#define KEY_PRESS_EVENT_STOP() \
1434 1 hiro
        if (gtk_signal_n_emissions_by_name \
1435 1 hiro
                (GTK_OBJECT(widget), "key_press_event") > 0) { \
1436 1 hiro
                gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), \
1437 1 hiro
                                             "key_press_event"); \
1438 1 hiro
        }
1439 1 hiro
#else
1440 1 hiro
#define KEY_PRESS_EVENT_STOP() \
1441 1 hiro
        g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1442 1 hiro
#endif
1443 1 hiro
1444 1 hiro
static gboolean textview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1445 1 hiro
                                     TextView *textview)
1446 1 hiro
{
1447 1 hiro
        SummaryView *summaryview = NULL;
1448 1 hiro
        MessageView *messageview = textview->messageview;
1449 1 hiro
1450 1 hiro
        if (!event) return FALSE;
1451 1 hiro
        if (messageview->mainwin)
1452 1 hiro
                summaryview = messageview->mainwin->summaryview;
1453 1 hiro
1454 1 hiro
        switch (event->keyval) {
1455 1 hiro
        case GDK_Tab:
1456 1 hiro
        case GDK_Home:
1457 1 hiro
        case GDK_Left:
1458 1 hiro
        case GDK_Up:
1459 1 hiro
        case GDK_Right:
1460 1 hiro
        case GDK_Down:
1461 1 hiro
        case GDK_Page_Up:
1462 1 hiro
        case GDK_Page_Down:
1463 1 hiro
        case GDK_End:
1464 1 hiro
        case GDK_Control_L:
1465 1 hiro
        case GDK_Control_R:
1466 1 hiro
                break;
1467 1 hiro
        case GDK_space:
1468 1 hiro
                if (summaryview)
1469 1 hiro
                        summary_pass_key_press_event(summaryview, event);
1470 1 hiro
                else
1471 1 hiro
                        textview_scroll_page
1472 1 hiro
                                (textview,
1473 1 hiro
                                 (event->state &
1474 1 hiro
                                  (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
1475 1 hiro
                break;
1476 1 hiro
        case GDK_BackSpace:
1477 1 hiro
                textview_scroll_page(textview, TRUE);
1478 1 hiro
                break;
1479 1 hiro
        case GDK_Return:
1480 1 hiro
                textview_scroll_one_line
1481 1 hiro
                        (textview, (event->state &
1482 1 hiro
                                    (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
1483 1 hiro
                break;
1484 1 hiro
        case GDK_Delete:
1485 1 hiro
                if (summaryview)
1486 1 hiro
                        summary_pass_key_press_event(summaryview, event);
1487 1 hiro
                break;
1488 1 hiro
        case GDK_n:
1489 1 hiro
        case GDK_N:
1490 1 hiro
        case GDK_p:
1491 1 hiro
        case GDK_P:
1492 1 hiro
        case GDK_y:
1493 1 hiro
        case GDK_t:
1494 1 hiro
        case GDK_l:
1495 1 hiro
                if (messageview->type == MVIEW_MIME &&
1496 1 hiro
                    textview == messageview->mimeview->textview) {
1497 1 hiro
                        KEY_PRESS_EVENT_STOP();
1498 1 hiro
                        mimeview_pass_key_press_event(messageview->mimeview,
1499 1 hiro
                                                      event);
1500 1 hiro
                        break;
1501 1 hiro
                }
1502 1 hiro
                /* fall through */
1503 1 hiro
        default:
1504 1 hiro
                if (summaryview &&
1505 1 hiro
                    event->window != messageview->mainwin->window->window) {
1506 1 hiro
                        GdkEventKey tmpev = *event;
1507 1 hiro
1508 1 hiro
                        tmpev.window = messageview->mainwin->window->window;
1509 1 hiro
                        KEY_PRESS_EVENT_STOP();
1510 1 hiro
                        gtk_widget_event(messageview->mainwin->window,
1511 1 hiro
                                         (GdkEvent *)&tmpev);
1512 1 hiro
                }
1513 1 hiro
                break;
1514 1 hiro
        }
1515 1 hiro
1516 1 hiro
        return FALSE;
1517 1 hiro
}
1518 1 hiro
1519 1 hiro
static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj,
1520 1 hiro
                                            GdkEvent *event, GtkTextIter *iter,
1521 1 hiro
                                            TextView *textview)
1522 1 hiro
{
1523 1 hiro
        GtkTextIter start_iter, end_iter;
1524 1 hiro
        gint start_pos, end_pos;
1525 1 hiro
        GdkEventButton *bevent;
1526 1 hiro
        RemoteURI *uri = NULL;
1527 1 hiro
        GSList *cur;
1528 1 hiro
        gchar *trimmed_uri;
1529 1 hiro
1530 1 hiro
        if (!event)
1531 1 hiro
                return FALSE;
1532 1 hiro
1533 1 hiro
        if (event->type != GDK_BUTTON_PRESS && event->type != GDK_2BUTTON_PRESS)
1534 1 hiro
                return FALSE;
1535 1 hiro
1536 1 hiro
        start_iter = *iter;
1537 1 hiro
1538 1 hiro
        if (!gtk_text_iter_backward_to_tag_toggle(&start_iter, tag)) {
1539 1 hiro
                debug_print("Can't find start point.");
1540 1 hiro
                return FALSE;
1541 1 hiro
        }
1542 1 hiro
        start_pos = gtk_text_iter_get_offset(&start_iter);
1543 1 hiro
1544 1 hiro
        end_iter = *iter;
1545 1 hiro
        if (!gtk_text_iter_forward_to_tag_toggle(&end_iter, tag)) {
1546 1 hiro
                debug_print("Can't find end");
1547 1 hiro
                return FALSE;
1548 1 hiro
        }
1549 1 hiro
        end_pos = gtk_text_iter_get_offset(&end_iter);
1550 1 hiro
1551 1 hiro
        for (cur = textview->uri_list; cur != NULL; cur = cur->next) {
1552 1 hiro
                RemoteURI *uri_ = (RemoteURI *)cur->data;
1553 1 hiro
1554 1 hiro
                if (start_pos == uri_->start && end_pos == uri_->end) {
1555 1 hiro
                        uri = uri_;
1556 1 hiro
                        break;
1557 1 hiro
                }
1558 1 hiro
        }
1559 1 hiro
1560 1 hiro
        STATUSBAR_POP(textview);
1561 1 hiro
1562 1 hiro
        if (!uri)
1563 1 hiro
                return FALSE;
1564 1 hiro
1565 1 hiro
        trimmed_uri = trim_string(uri->uri, 60);
1566 1 hiro
        STATUSBAR_PUSH(textview, trimmed_uri);
1567 1 hiro
        g_free(trimmed_uri);
1568 1 hiro
1569 1 hiro
        bevent = (GdkEventButton *)event;
1570 1 hiro
        if ((event->type == GDK_2BUTTON_PRESS && bevent->button == 1) ||
1571 1 hiro
             bevent->button == 2) {
1572 1 hiro
                if (!g_strncasecmp(uri->uri, "mailto:", 7)) {
1573 1 hiro
                        PrefsAccount *ac = NULL;
1574 1 hiro
                        MsgInfo *msginfo = textview->messageview->msginfo;
1575 1 hiro
1576 1 hiro
                        if (msginfo && msginfo->folder)
1577 1 hiro
                                ac = account_find_from_item(msginfo->folder);
1578 1 hiro
                        if (ac && ac->protocol == A_NNTP)
1579 1 hiro
                                ac = NULL;
1580 1 hiro
                        compose_new(ac, msginfo->folder, uri->uri + 7, NULL);
1581 1 hiro
                } else if (textview_uri_security_check(textview, uri) == TRUE) {
1582 1 hiro
                        open_uri(uri->uri, prefs_common.uri_cmd);
1583 1 hiro
                        return TRUE; //
1584 1 hiro
                }
1585 1 hiro
        }
1586 1 hiro
1587 1 hiro
        return FALSE;
1588 1 hiro
}
1589 1 hiro
1590 1 hiro
static gboolean textview_uri_security_check(TextView *textview, RemoteURI *uri)
1591 1 hiro
{
1592 1 hiro
        GtkTextBuffer *buffer;
1593 1 hiro
        GtkTextIter start_iter, end_iter;
1594 1 hiro
        gchar *visible_str;
1595 1 hiro
        gboolean retval = TRUE;
1596 1 hiro
1597 1 hiro
        if (is_uri_string(uri->uri) == FALSE)
1598 1 hiro
                return TRUE;
1599 1 hiro
1600 1 hiro
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
1601 1 hiro
        gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, uri->start);
1602 1 hiro
        gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, uri->end);
1603 1 hiro
        visible_str = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
1604 1 hiro
                                               FALSE);
1605 1 hiro
        if (visible_str == NULL)
1606 1 hiro
                return TRUE;
1607 1 hiro
1608 1 hiro
        if (strcmp(visible_str, uri->uri) != 0 && is_uri_string(visible_str)) {
1609 1 hiro
                gchar *uri_path;
1610 1 hiro
                gchar *visible_uri_path;
1611 1 hiro
1612 1 hiro
                uri_path = get_uri_path(uri->uri);
1613 1 hiro
                visible_uri_path = get_uri_path(visible_str);
1614 1 hiro
                if (strcmp(uri_path, visible_uri_path) != 0)
1615 1 hiro
                        retval = FALSE;
1616 1 hiro
        }
1617 1 hiro
1618 1 hiro
        if (retval == FALSE) {
1619 1 hiro
                gchar *msg;
1620 1 hiro
                AlertValue aval;
1621 1 hiro
1622 1 hiro
                msg = g_strdup_printf(_("The real URL (%s) is different from\n"
1623 1 hiro
                                        "the apparent URL (%s).\n"
1624 1 hiro
                                        "Open it anyway?"),
1625 1 hiro
                                      uri->uri, visible_str);
1626 1 hiro
                aval = alertpanel(_("Warning"), msg, _("Yes"), _("No"), NULL);
1627 1 hiro
                g_free(msg);
1628 1 hiro
                if (aval == G_ALERTDEFAULT)
1629 1 hiro
                        retval = TRUE;
1630 1 hiro
        }
1631 1 hiro
1632 1 hiro
        g_free(visible_str);
1633 1 hiro
1634 1 hiro
        return retval;
1635 1 hiro
}
1636 1 hiro
1637 1 hiro
static void textview_uri_list_remove_all(GSList *uri_list)
1638 1 hiro
{
1639 1 hiro
        GSList *cur;
1640 1 hiro
1641 1 hiro
        for (cur = uri_list; cur != NULL; cur = cur->next) {
1642 1 hiro
                if (cur->data) {
1643 1 hiro
                        g_free(((RemoteURI *)cur->data)->uri);
1644 1 hiro
                        g_free(cur->data);
1645 1 hiro
                }
1646 1 hiro
        }
1647 1 hiro
1648 1 hiro
        g_slist_free(uri_list);
1649 1 hiro
}