Statistics
| Revision:

root / src / textview.c @ 1

History | View | Annotate | Download (42.3 KB)

1
/*
2
 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3
 * Copyright (C) 1999-2004 Hiroyuki Yamamoto
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
 */
19

    
20
#ifdef HAVE_CONFIG_H
21
#  include "config.h"
22
#endif
23

    
24
#include "defs.h"
25

    
26
#include <glib.h>
27
#include <gdk/gdk.h>
28
#include <gdk/gdkkeysyms.h>
29
#include <gtk/gtkvbox.h>
30
#include <gtk/gtkscrolledwindow.h>
31
#include <gtk/gtksignal.h>
32
#include <stdio.h>
33
#include <ctype.h>
34
#include <string.h>
35
#include <stdlib.h>
36

    
37
#include "intl.h"
38
#include "main.h"
39
#include "summaryview.h"
40
#include "procheader.h"
41
#include "prefs_common.h"
42
#include "codeconv.h"
43
#include "statusbar.h"
44
#include "utils.h"
45
#include "gtkutils.h"
46
#include "procmime.h"
47
#include "account.h"
48
#include "html.h"
49
#include "compose.h"
50
#include "displayheader.h"
51
#include "alertpanel.h"
52

    
53
typedef struct _RemoteURI        RemoteURI;
54

    
55
struct _RemoteURI
56
{
57
        gchar *uri;
58

    
59
        guint start;
60
        guint end;
61
};
62

    
63
static GdkColor quote_colors[3] = {
64
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0},
65
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0},
66
        {(gulong)0, (gushort)0, (gushort)0, (gushort)0}
67
};
68

    
69
static GdkColor uri_color = {
70
        (gulong)0,
71
        (gushort)0,
72
        (gushort)0,
73
        (gushort)0
74
};
75

    
76
static GdkColor emphasis_color = {
77
        (gulong)0,
78
        (gushort)0,
79
        (gushort)0,
80
        (gushort)0xcfff
81
};
82

    
83
#if 0
84
static GdkColor error_color = {
85
        (gulong)0,
86
        (gushort)0xefff,
87
        (gushort)0,
88
        (gushort)0
89
};
90
#endif
91

    
92
#if USE_GPGME
93
static GdkColor good_sig_color = {
94
        (gulong)0,
95
        (gushort)0,
96
        (gushort)0xbfff,
97
        (gushort)0
98
};
99

    
100
static GdkColor nocheck_sig_color = {
101
        (gulong)0,
102
        (gushort)0,
103
        (gushort)0,
104
        (gushort)0xcfff
105
};
106

    
107
static GdkColor bad_sig_color = {
108
        (gulong)0,
109
        (gushort)0xefff,
110
        (gushort)0,
111
        (gushort)0
112
};
113
#endif
114

    
115
#define STATUSBAR_PUSH(textview, str)                                            \
116
{                                                                            \
117
        gtk_statusbar_push(GTK_STATUSBAR(textview->messageview->statusbar), \
118
                           textview->messageview->statusbar_cid, str);            \
119
}
120

    
121
#define STATUSBAR_POP(textview)                                                   \
122
{                                                                           \
123
        gtk_statusbar_pop(GTK_STATUSBAR(textview->messageview->statusbar), \
124
                          textview->messageview->statusbar_cid);           \
125
}
126

    
127
static void textview_add_part                (TextView        *textview,
128
                                         MimeInfo        *mimeinfo,
129
                                         FILE                *fp);
130
static void textview_add_parts                (TextView        *textview,
131
                                         MimeInfo        *mimeinfo,
132
                                         FILE                *fp);
133
static void textview_write_body                (TextView        *textview,
134
                                         MimeInfo        *mimeinfo,
135
                                         FILE                *fp,
136
                                         const gchar        *charset);
137
static void textview_show_html                (TextView        *textview,
138
                                         FILE                *fp,
139
                                         CodeConverter        *conv);
140

    
141
static void textview_write_line                (TextView        *textview,
142
                                         const gchar        *str,
143
                                         CodeConverter        *conv);
144
static void textview_write_link                (TextView        *textview,
145
                                         const gchar        *str,
146
                                         const gchar        *uri,
147
                                         CodeConverter        *conv);
148

    
149
static GPtrArray *textview_scan_header        (TextView        *textview,
150
                                         FILE                *fp);
151
static void textview_show_header        (TextView        *textview,
152
                                         GPtrArray        *headers);
153

    
154
static gboolean textview_key_pressed                (GtkWidget        *widget,
155
                                                 GdkEventKey        *event,
156
                                                 TextView        *textview);
157
static gboolean textview_uri_button_pressed        (GtkTextTag        *tag,
158
                                                 GObject        *obj,
159
                                                 GdkEvent        *event,
160
                                                 GtkTextIter        *iter,
161
                                                 TextView        *textview);
162

    
163
static void textview_smooth_scroll_do                (TextView        *textview,
164
                                                 gfloat                 old_value,
165
                                                 gfloat                 last_value,
166
                                                 gint                 step);
167
static void textview_smooth_scroll_one_line        (TextView        *textview,
168
                                                 gboolean         up);
169
static gboolean textview_smooth_scroll_page        (TextView        *textview,
170
                                                 gboolean         up);
171

    
172
static gboolean textview_uri_security_check        (TextView        *textview,
173
                                                 RemoteURI        *uri);
174
static void textview_uri_list_remove_all        (GSList                *uri_list);
175

    
176

    
177
TextView *textview_create(void)
178
{
179
        TextView *textview;
180
        GtkWidget *vbox;
181
        GtkWidget *scrolledwin;
182
        GtkWidget *text;
183
        GtkTextBuffer *buffer;
184
        GtkClipboard *clipboard;
185
        //PangoFontDescription *font_desc = NULL;
186

    
187
        debug_print(_("Creating text view...\n"));
188
        textview = g_new0(TextView, 1);
189

    
190
        scrolledwin = gtk_scrolled_window_new(NULL, NULL);
191
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
192
                                       GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
193
        gtk_widget_set_size_request
194
                (scrolledwin, prefs_common.mainview_width, -1);
195

    
196
        text = gtk_text_view_new();
197
        gtk_widget_show(text);
198
        gtk_text_view_set_editable(GTK_TEXT_VIEW(text), FALSE);
199
        gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
200
        gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), 4);
201
        gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), 4);
202

    
203
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
204
        clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
205
        gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
206

    
207
#if 0
208
        if (prefs_common.normalfont)
209
                font_desc = pango_font_description_from_string
210
                        (prefs_common.normalfont);
211
        if (font_desc)
212
                gtk_widget_modify_font(text, font_desc);
213
        pango_font_description_free(font_desc);
214
#endif
215

    
216
        gtk_widget_ref(scrolledwin);
217

    
218
        gtk_container_add(GTK_CONTAINER(scrolledwin), text);
219

    
220
        g_signal_connect(G_OBJECT(text), "key_press_event",
221
                         G_CALLBACK(textview_key_pressed), textview);
222

    
223
        gtk_widget_show(scrolledwin);
224

    
225
        vbox = gtk_vbox_new(FALSE, 0);
226
        gtk_box_pack_start(GTK_BOX(vbox), scrolledwin, TRUE, TRUE, 0);
227

    
228
        gtk_widget_show(vbox);
229

    
230
        textview->vbox             = vbox;
231
        textview->scrolledwin      = scrolledwin;
232
        textview->text             = text;
233
        textview->uri_list         = NULL;
234
        textview->body_pos         = 0;
235
        //textview->cur_pos          = 0;
236
        textview->show_all_headers = FALSE;
237

    
238
        return textview;
239
}
240

    
241
static void textview_create_tags(GtkTextView *text, TextView *textview)
242
{
243
        GtkTextBuffer *buffer;
244
        GtkTextTag *tag;
245

    
246
        buffer = gtk_text_view_get_buffer(text);
247

    
248
        gtk_text_buffer_create_tag(buffer, "header",
249
                                   "pixels-above-lines", 0,
250
                                   "pixels-above-lines-set", TRUE,
251
                                   "pixels-below-lines", 0,
252
                                   "pixels-below-lines-set", TRUE,
253
                                   //"left-margin", 0,
254
                                   //"left-margin-set", TRUE,
255
                                   NULL);
256
        gtk_text_buffer_create_tag(buffer, "header_title",
257
                                   "font", prefs_common.boldfont,
258
                                   NULL);
259
        gtk_text_buffer_create_tag(buffer, "quote0",
260
                                   "foreground-gdk", &quote_colors[0],
261
                                   NULL);
262
        gtk_text_buffer_create_tag(buffer, "quote1",
263
                                   "foreground-gdk", &quote_colors[1],
264
                                   NULL);
265
        gtk_text_buffer_create_tag(buffer, "quote2",
266
                                   "foreground-gdk", &quote_colors[2],
267
                                   NULL);
268
        gtk_text_buffer_create_tag(buffer, "emphasis",
269
                                   "foreground-gdk", &emphasis_color,
270
                                   NULL);
271
        tag = gtk_text_buffer_create_tag(buffer, "link",
272
                                         "foreground-gdk", &uri_color,
273
                                         NULL);
274
#if USE_GPGME
275
        gtk_text_buffer_create_tag(buffer, "good-signature",
276
                                   "foreground-gdk", &good_sig_color,
277
                                   NULL);
278
        gtk_text_buffer_create_tag(buffer, "bad-signature",
279
                                   "foreground-gdk", &bad_sig_color,
280
                                   NULL);
281
        gtk_text_buffer_create_tag(buffer, "nocheck-signature",
282
                                   "foreground-gdk", &nocheck_sig_color,
283
                                   NULL);
284
#endif /* USE_GPGME */
285

    
286
        g_signal_connect(G_OBJECT(tag), "event",
287
                         G_CALLBACK(textview_uri_button_pressed), textview);
288
}
289

    
290
void textview_init(TextView *textview)
291
{
292
        //gtkut_widget_disable_theme_engine(textview->text);
293
        textview_update_message_colors();
294
        textview_set_all_headers(textview, FALSE);
295
        textview_set_font(textview, NULL);
296
        textview_create_tags(GTK_TEXT_VIEW(textview->text), textview);
297
}
298

    
299
void textview_update_message_colors(void)
300
{
301
        GdkColor black = {0, 0, 0, 0};
302

    
303
        if (prefs_common.enable_color) {
304
                /* grab the quote colors, converting from an int to a GdkColor */
305
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
306
                                               &quote_colors[0]);
307
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
308
                                               &quote_colors[1]);
309
                gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
310
                                               &quote_colors[2]);
311
                gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
312
                                               &uri_color);
313
        } else {
314
                quote_colors[0] = quote_colors[1] = quote_colors[2] = 
315
                        uri_color = emphasis_color = black;
316
        }
317
}
318

    
319
void textview_show_message(TextView *textview, MimeInfo *mimeinfo,
320
                           const gchar *file)
321
{
322
        FILE *fp;
323
        const gchar *charset = NULL;
324
        GPtrArray *headers = NULL;
325

    
326
        if ((fp = fopen(file, "rb")) == NULL) {
327
                FILE_OP_ERROR(file, "fopen");
328
                return;
329
        }
330

    
331
        if (textview->messageview->forced_charset)
332
                charset = textview->messageview->forced_charset;
333
        else if (prefs_common.force_charset)
334
                charset = prefs_common.force_charset;
335
        else if (mimeinfo->charset)
336
                charset = mimeinfo->charset;
337

    
338
        textview_set_font(textview, charset);
339
        textview_clear(textview);
340

    
341
        if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) perror("fseek");
342
        headers = textview_scan_header(textview, fp);
343
        if (headers) {
344
                GtkTextView *text = GTK_TEXT_VIEW(textview->text);
345
                GtkTextBuffer *buffer;
346
                GtkTextIter iter;
347

    
348
                textview_show_header(textview, headers);
349
                procheader_header_array_destroy(headers);
350

    
351
                buffer = gtk_text_view_get_buffer(text);
352
                gtk_text_buffer_get_end_iter(buffer, &iter);
353
                textview->body_pos = gtk_text_iter_get_offset(&iter);
354
        }
355

    
356
        textview_add_parts(textview, mimeinfo, fp);
357

    
358
        fclose(fp);
359

    
360
        textview_set_position(textview, 0);
361
}
362

    
363
void textview_show_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
364
{
365
        gchar buf[BUFFSIZE];
366
        const gchar *boundary = NULL;
367
        gint boundary_len = 0;
368
        const gchar *charset = NULL;
369
        GPtrArray *headers = NULL;
370
        gboolean is_rfc822_part = FALSE;
371

    
372
        g_return_if_fail(mimeinfo != NULL);
373
        g_return_if_fail(fp != NULL);
374

    
375
        if (mimeinfo->mime_type == MIME_MULTIPART) {
376
                textview_clear(textview);
377
                textview_add_parts(textview, mimeinfo, fp);
378
                return;
379
        }
380

    
381
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
382
                boundary = mimeinfo->parent->boundary;
383
                boundary_len = strlen(boundary);
384
        }
385

    
386
        if (!boundary && mimeinfo->mime_type == MIME_TEXT) {
387
                if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0)
388
                        perror("fseek");
389
                headers = textview_scan_header(textview, fp);
390
        } else {
391
                if (mimeinfo->mime_type == MIME_TEXT && mimeinfo->parent) {
392
                        glong fpos;
393
                        MimeInfo *parent = mimeinfo->parent;
394

    
395
                        while (parent->parent) {
396
                                if (parent->main &&
397
                                    parent->main->mime_type ==
398
                                        MIME_MESSAGE_RFC822)
399
                                        break;
400
                                parent = parent->parent;
401
                        }
402

    
403
                        if ((fpos = ftell(fp)) < 0)
404
                                perror("ftell");
405
                        else if (fseek(fp, parent->fpos, SEEK_SET) < 0)
406
                                perror("fseek");
407
                        else {
408
                                headers = textview_scan_header(textview, fp);
409
                                if (fseek(fp, fpos, SEEK_SET) < 0)
410
                                        perror("fseek");
411
                        }
412
                }
413
                /* skip MIME part headers */
414
                while (fgets(buf, sizeof(buf), fp) != NULL)
415
                        if (buf[0] == '\r' || buf[0] == '\n') break;
416
        }
417

    
418
        /* display attached RFC822 single text message */
419
        if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
420
                if (headers) procheader_header_array_destroy(headers);
421
                if (!mimeinfo->sub) {
422
                        textview_clear(textview);
423
                        return;
424
                }
425
                headers = textview_scan_header(textview, fp);
426
                mimeinfo = mimeinfo->sub;
427
                is_rfc822_part = TRUE;
428
        }
429

    
430
        if (textview->messageview->forced_charset)
431
                charset = textview->messageview->forced_charset;
432
        else if (prefs_common.force_charset)
433
                charset = prefs_common.force_charset;
434
        else if (mimeinfo->charset)
435
                charset = mimeinfo->charset;
436

    
437
        textview_set_font(textview, charset);
438

    
439
        textview_clear(textview);
440

    
441
        if (headers) {
442
                GtkTextView *text = GTK_TEXT_VIEW(textview->text);
443
                GtkTextBuffer *buffer;
444
                GtkTextIter iter;
445

    
446
                textview_show_header(textview, headers);
447
                procheader_header_array_destroy(headers);
448

    
449
                buffer = gtk_text_view_get_buffer(text);
450
                gtk_text_buffer_get_end_iter(buffer, &iter);
451
                textview->body_pos = gtk_text_iter_get_offset(&iter);
452
                if (!mimeinfo->main)
453
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
454
        }
455

    
456
        if (mimeinfo->mime_type == MIME_MULTIPART || is_rfc822_part)
457
                textview_add_parts(textview, mimeinfo, fp);
458
        else
459
                textview_write_body(textview, mimeinfo, fp, charset);
460
}
461

    
462
static void textview_add_part(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
463
{
464
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
465
        GtkTextBuffer *buffer;
466
        GtkTextIter iter;
467
        gchar buf[BUFFSIZE];
468
        const gchar *boundary = NULL;
469
        gint boundary_len = 0;
470
        const gchar *charset = NULL;
471
        GPtrArray *headers = NULL;
472

    
473
        g_return_if_fail(mimeinfo != NULL);
474
        g_return_if_fail(fp != NULL);
475

    
476
        buffer = gtk_text_view_get_buffer(text);
477
        gtk_text_buffer_get_end_iter(buffer, &iter);
478

    
479
        if (mimeinfo->mime_type == MIME_MULTIPART) return;
480

    
481
        if (fseek(fp, mimeinfo->fpos, SEEK_SET) < 0) {
482
                perror("fseek");
483
                return;
484
        }
485

    
486
        if (mimeinfo->parent && mimeinfo->parent->boundary) {
487
                boundary = mimeinfo->parent->boundary;
488
                boundary_len = strlen(boundary);
489
        }
490

    
491
        while (fgets(buf, sizeof(buf), fp) != NULL)
492
                if (buf[0] == '\r' || buf[0] == '\n') break;
493

    
494
        if (mimeinfo->mime_type == MIME_MESSAGE_RFC822) {
495
                headers = textview_scan_header(textview, fp);
496
                if (headers) {
497
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
498
                        textview_show_header(textview, headers);
499
                        procheader_header_array_destroy(headers);
500
                }
501
                return;
502
        }
503

    
504
#if USE_GPGME
505
        if (mimeinfo->sigstatus)
506
                g_snprintf(buf, sizeof(buf), "\n[%s (%s)]\n",
507
                           mimeinfo->content_type, mimeinfo->sigstatus);
508
        else
509
#endif
510
        if (mimeinfo->filename || mimeinfo->name)
511
                g_snprintf(buf, sizeof(buf), "\n[%s  %s (%d bytes)]\n",
512
                           mimeinfo->filename ? mimeinfo->filename :
513
                           mimeinfo->name,
514
                           mimeinfo->content_type, mimeinfo->size);
515
        else
516
                g_snprintf(buf, sizeof(buf), "\n[%s (%d bytes)]\n",
517
                           mimeinfo->content_type, mimeinfo->size);
518

    
519
#if USE_GPGME
520
        if (mimeinfo->sigstatus) {
521
                const gchar *color;
522
                if (!strcmp(mimeinfo->sigstatus, _("Good signature")))
523
                        color = "good-signature";
524
                else if (!strcmp(mimeinfo->sigstatus, _("BAD signature")))
525
                        color = "bad-signature";
526
                else
527
                        color = "nocheck-signature";
528
                gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, buf, -1,
529
                                                         color, NULL);
530
        } else
531
#endif
532
        if (mimeinfo->mime_type != MIME_TEXT &&
533
            mimeinfo->mime_type != MIME_TEXT_HTML) {
534
                gtk_text_buffer_insert(buffer, &iter, buf, -1);
535
        } else {
536
                if (!mimeinfo->main &&
537
                    mimeinfo->parent &&
538
                    mimeinfo->parent->children != mimeinfo)
539
                        gtk_text_buffer_insert(buffer, &iter, buf, -1);
540
                else
541
                        gtk_text_buffer_insert(buffer, &iter, "\n", 1);
542
                if (textview->messageview->forced_charset)
543
                        charset = textview->messageview->forced_charset;
544
                else if (prefs_common.force_charset)
545
                        charset = prefs_common.force_charset;
546
                else if (mimeinfo->charset)
547
                        charset = mimeinfo->charset;
548
                textview_write_body(textview, mimeinfo, fp, charset);
549
        }
550
}
551

    
552
static void textview_add_parts(TextView *textview, MimeInfo *mimeinfo, FILE *fp)
553
{
554
        gint level;
555

    
556
        g_return_if_fail(mimeinfo != NULL);
557
        g_return_if_fail(fp != NULL);
558

    
559
        level = mimeinfo->level;
560

    
561
        for (;;) {
562
                textview_add_part(textview, mimeinfo, fp);
563
                if (mimeinfo->parent && mimeinfo->parent->content_type &&
564
                    !strcasecmp(mimeinfo->parent->content_type,
565
                                "multipart/alternative"))
566
                        mimeinfo = mimeinfo->parent->next;
567
                else
568
                        mimeinfo = procmime_mimeinfo_next(mimeinfo);
569
                if (!mimeinfo || mimeinfo->level <= level)
570
                        break;
571
        }
572
}
573

    
574
#define TEXT_INSERT(str) \
575
        gtk_text_buffer_insert(buffer, &iter, str, -1)
576

    
577
void textview_show_error(TextView *textview)
578
{
579
        GtkTextBuffer *buffer;
580
        GtkTextIter iter;
581

    
582
        textview_set_font(textview, NULL);
583
        textview_clear(textview);
584

    
585
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
586
        gtk_text_buffer_get_start_iter(buffer, &iter);
587
        TEXT_INSERT(_("This message can't be displayed.\n"));
588
}
589

    
590
void textview_show_mime_part(TextView *textview, MimeInfo *partinfo)
591
{
592
        GtkTextBuffer *buffer;
593
        GtkTextIter iter;
594

    
595
        if (!partinfo) return;
596

    
597
        textview_set_font(textview, NULL);
598
        textview_clear(textview);
599

    
600
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
601
        gtk_text_buffer_get_start_iter(buffer, &iter);
602

    
603
        TEXT_INSERT(_("To save this part, pop up the context menu with "));
604
        TEXT_INSERT(_("right click and select `Save as...', "));
605
        TEXT_INSERT(_("or press `y' key.\n\n"));
606

    
607
        TEXT_INSERT(_("To display this part as a text message, select "));
608
        TEXT_INSERT(_("`Display as text', or press `t' key.\n\n"));
609

    
610
        TEXT_INSERT(_("To open this part with external program, select "));
611
        TEXT_INSERT(_("`Open' or `Open with...', "));
612
        TEXT_INSERT(_("or double-click, or click the center button, "));
613
        TEXT_INSERT(_("or press `l' key."));
614
}
615

    
616
#if USE_GPGME
617
void textview_show_signature_part(TextView *textview, MimeInfo *partinfo)
618
{
619
        GtkTextBuffer *buffer;
620
        GtkTextIter iter;
621

    
622
        if (!partinfo) return;
623

    
624
        textview_set_font(textview, NULL);
625
        textview_clear(textview);
626

    
627
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
628
        gtk_text_buffer_get_start_iter(buffer, &iter);
629

    
630
        if (partinfo->sigstatus_full == NULL) {
631
                TEXT_INSERT(_("This signature has not been checked yet.\n"));
632
                TEXT_INSERT(_("To check it, pop up the context menu with\n"));
633
                TEXT_INSERT(_("right click and select `Check signature'.\n"));
634
        } else {
635
                TEXT_INSERT(partinfo->sigstatus_full);
636
        }
637
}
638
#endif /* USE_GPGME */
639

    
640
#undef TEXT_INSERT
641

    
642
static void textview_write_body(TextView *textview, MimeInfo *mimeinfo,
643
                                FILE *fp, const gchar *charset)
644
{
645
        FILE *tmpfp;
646
        gchar buf[BUFFSIZE];
647
        CodeConverter *conv;
648

    
649
        conv = conv_code_converter_new(charset);
650

    
651
        tmpfp = procmime_decode_content(NULL, fp, mimeinfo);
652
        if (tmpfp) {
653
                if (mimeinfo->mime_type == MIME_TEXT_HTML)
654
                        textview_show_html(textview, tmpfp, conv);
655
                else
656
                        while (fgets(buf, sizeof(buf), tmpfp) != NULL)
657
                                textview_write_line(textview, buf, conv);
658
                fclose(tmpfp);
659
        }
660

    
661
        conv_code_converter_destroy(conv);
662
}
663

    
664
static void textview_show_html(TextView *textview, FILE *fp,
665
                               CodeConverter *conv)
666
{
667
        HTMLParser *parser;
668
        gchar *str;
669

    
670
        parser = html_parser_new(fp, conv);
671
        g_return_if_fail(parser != NULL);
672

    
673
        while ((str = html_parse(parser)) != NULL) {
674
                if (parser->href != NULL)
675
                        textview_write_link(textview, str, parser->href, NULL);
676
                else
677
                        textview_write_line(textview, str, NULL);
678
        }
679
        html_parser_destroy(parser);
680
}
681

    
682
/* get_uri_part() - retrieves a URI starting from scanpos.
683
                    Returns TRUE if succesful */
684
static gboolean get_uri_part(const gchar *start, const gchar *scanpos,
685
                             const gchar **bp, const gchar **ep)
686
{
687
        const gchar *ep_;
688

    
689
        g_return_val_if_fail(start != NULL, FALSE);
690
        g_return_val_if_fail(scanpos != NULL, FALSE);
691
        g_return_val_if_fail(bp != NULL, FALSE);
692
        g_return_val_if_fail(ep != NULL, FALSE);
693

    
694
        *bp = scanpos;
695

    
696
        /* find end point of URI */
697
        for (ep_ = scanpos; *ep_ != '\0'; ep_++) {
698
                if (!isgraph(*(const guchar *)ep_) ||
699
                    !isascii(*(const guchar *)ep_) ||
700
                    strchr("()<>\"", *ep_))
701
                        break;
702
        }
703

    
704
        /* no punctuation at end of string */
705

    
706
        /* FIXME: this stripping of trailing punctuations may bite with other URIs.
707
         * should pass some URI type to this function and decide on that whether
708
         * to perform punctuation stripping */
709

    
710
#define IS_REAL_PUNCT(ch)        (ispunct(ch) && ((ch) != '/')) 
711

    
712
        for (; ep_ - 1 > scanpos + 1 &&
713
               IS_REAL_PUNCT(*(const guchar *)(ep_ - 1));
714
             ep_--)
715
                ;
716

    
717
#undef IS_REAL_PUNCT
718

    
719
        *ep = ep_;
720

    
721
        return TRUE;                
722
}
723

    
724
static gchar *make_uri_string(const gchar *bp, const gchar *ep)
725
{
726
        return g_strndup(bp, ep - bp);
727
}
728

    
729
/* valid mail address characters */
730
#define IS_RFC822_CHAR(ch) \
731
        (isascii(ch) && \
732
         (ch) > 32   && \
733
         (ch) != 127 && \
734
         !isspace(ch) && \
735
         !strchr("(),;<>\"", (ch)))
736

    
737
/* alphabet and number within 7bit ASCII */
738
#define IS_ASCII_ALNUM(ch)        (isascii(ch) && isalnum(ch))
739

    
740
/* get_email_part() - retrieves an email address. Returns TRUE if succesful */
741
static gboolean get_email_part(const gchar *start, const gchar *scanpos,
742
                               const gchar **bp, const gchar **ep)
743
{
744
        /* more complex than the uri part because we need to scan back and forward starting from
745
         * the scan position. */
746
        gboolean result = FALSE;
747
        const gchar *bp_;
748
        const gchar *ep_;
749

    
750
        g_return_val_if_fail(start != NULL, FALSE);
751
        g_return_val_if_fail(scanpos != NULL, FALSE);
752
        g_return_val_if_fail(bp != NULL, FALSE);
753
        g_return_val_if_fail(ep != NULL, FALSE);
754

    
755
        /* scan start of address */
756
        for (bp_ = scanpos - 1;
757
             bp_ >= start && IS_RFC822_CHAR(*(const guchar *)bp_); bp_--)
758
                ;
759

    
760
        /* TODO: should start with an alnum? */
761
        bp_++;
762
        for (; bp_ < scanpos && !IS_ASCII_ALNUM(*(const guchar *)bp_); bp_++)
763
                ;
764

    
765
        if (bp_ != scanpos) {
766
                /* scan end of address */
767
                for (ep_ = scanpos + 1;
768
                     *ep_ && IS_RFC822_CHAR(*(const guchar *)ep_); ep_++)
769
                        ;
770

    
771
                /* TODO: really should terminate with an alnum? */
772
                for (; ep_ > scanpos && !IS_ASCII_ALNUM(*(const guchar *)ep_);
773
                     --ep_)
774
                        ;
775
                ep_++;
776

    
777
                if (ep_ > scanpos + 1) {
778
                        *ep = ep_;
779
                        *bp = bp_;
780
                        result = TRUE;
781
                }
782
        }
783

    
784
        return result;
785
}
786

    
787
#undef IS_ASCII_ALNUM
788
#undef IS_RFC822_CHAR
789

    
790
static gchar *make_email_string(const gchar *bp, const gchar *ep)
791
{
792
        /* returns a mailto: URI; mailto: is also used to detect the
793
         * uri type later on in the button_pressed signal handler */
794
        gchar *tmp;
795
        gchar *result;
796

    
797
        tmp = g_strndup(bp, ep - bp);
798
        result = g_strconcat("mailto:", tmp, NULL);
799
        g_free(tmp);
800

    
801
        return result;
802
}
803

    
804
#define ADD_TXT_POS(bp_, ep_, pti_) \
805
        if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
806
                last = last->next; \
807
                last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
808
                last->next = NULL; \
809
        } else { \
810
                g_warning("alloc error scanning URIs\n"); \
811
                gtk_text_buffer_insert_with_tags_by_name \
812
                        (buffer, &iter, linebuf, -1, fg_tag, NULL); \
813
                return; \
814
        }
815

    
816
/* textview_make_clickable_parts() - colorizes clickable parts */
817
static void textview_make_clickable_parts(TextView *textview,
818
                                          const gchar *fg_tag,
819
                                          const gchar *uri_tag,
820
                                          const gchar *linebuf)
821
{
822
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
823
        GtkTextBuffer *buffer;
824
        GtkTextIter iter;
825

    
826
        /* parse table - in order of priority */
827
        struct table {
828
                const gchar *needle; /* token */
829

    
830
                /* token search function */
831
                gchar    *(*search)        (const gchar *haystack,
832
                                         const gchar *needle);
833
                /* part parsing function */
834
                gboolean  (*parse)        (const gchar *start,
835
                                         const gchar *scanpos,
836
                                         const gchar **bp_,
837
                                         const gchar **ep_);
838
                /* part to URI function */
839
                gchar    *(*build_uri)        (const gchar *bp,
840
                                         const gchar *ep);
841
        };
842

    
843
        static struct table parser[] = {
844
                {"http://",  strcasestr, get_uri_part,   make_uri_string},
845
                {"https://", strcasestr, get_uri_part,   make_uri_string},
846
                {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
847
                {"www.",     strcasestr, get_uri_part,   make_uri_string},
848
                {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
849
                {"@",        strcasestr, get_email_part, make_email_string}
850
        };
851
        const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
852

    
853
        gint  n;
854
        const gchar *walk, *bp, *ep;
855

    
856
        struct txtpos {
857
                const gchar        *bp, *ep;        /* text position */
858
                gint                 pti;                /* index in parse table */
859
                struct txtpos        *next;                /* next */
860
        } head = {NULL, NULL, 0,  NULL}, *last = &head;
861

    
862
        buffer = gtk_text_view_get_buffer(text);
863
        gtk_text_buffer_get_end_iter(buffer, &iter);
864

    
865
        /* parse for clickable parts, and build a list of begin and
866
           end positions  */
867
        for (walk = linebuf, n = 0;;) {
868
                gint last_index = PARSE_ELEMS;
869
                gchar *scanpos = NULL;
870

    
871
                /* FIXME: this looks phony. scanning for anything in the
872
                   parse table */
873
                for (n = 0; n < PARSE_ELEMS; n++) {
874
                        gchar *tmp;
875

    
876
                        tmp = parser[n].search(walk, parser[n].needle);
877
                        if (tmp) {
878
                                if (scanpos == NULL || tmp < scanpos) {
879
                                        scanpos = tmp;
880
                                        last_index = n;
881
                                }
882
                        }                                        
883
                }
884

    
885
                if (scanpos) {
886
                        /* check if URI can be parsed */
887
                        if (parser[last_index].parse(walk, scanpos, &bp, &ep)
888
                            && (ep - bp - 1) > strlen(parser[last_index].needle)) {
889
                                        ADD_TXT_POS(bp, ep, last_index);
890
                                        walk = ep;
891
                        } else
892
                                walk = scanpos +
893
                                        strlen(parser[last_index].needle);
894
                } else
895
                        break;
896
        }
897

    
898
        /* colorize this line */
899
        if (head.next) {
900
                const gchar *normal_text = linebuf;
901

    
902
                /* insert URIs */
903
                for (last = head.next; last != NULL;
904
                     normal_text = last->ep, last = last->next) {
905
                        RemoteURI *uri;
906

    
907
                        uri = g_new(RemoteURI, 1);
908
                        if (last->bp - normal_text > 0)
909
                                gtk_text_buffer_insert_with_tags_by_name
910
                                        (buffer, &iter,
911
                                         normal_text,
912
                                         last->bp - normal_text,
913
                                         fg_tag, NULL);
914
                        uri->uri = parser[last->pti].build_uri(last->bp,
915
                                                               last->ep);
916
                        uri->start = gtk_text_iter_get_offset(&iter);
917
                        gtk_text_buffer_insert_with_tags_by_name
918
                                (buffer, &iter, last->bp, last->ep - last->bp,
919
                                 uri_tag, NULL);
920
                        uri->end = gtk_text_iter_get_offset(&iter);
921
                        textview->uri_list =
922
                                g_slist_append(textview->uri_list, uri);
923
                }
924

    
925
                if (*normal_text)
926
                        gtk_text_buffer_insert_with_tags_by_name
927
                                (buffer, &iter, normal_text, -1, fg_tag, NULL);
928
        } else {
929
                gtk_text_buffer_insert_with_tags_by_name
930
                        (buffer, &iter, linebuf, -1, fg_tag, NULL);
931
        }
932
}
933

    
934
#undef ADD_TXT_POS
935

    
936
static void textview_write_line(TextView *textview, const gchar *str,
937
                                CodeConverter *conv)
938
{
939
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
940
        GtkTextBuffer *buffer;
941
        GtkTextIter iter;
942
        gchar buf[BUFFSIZE];
943
        gchar *fg_color;
944
        gint quotelevel = -1;
945
        gchar quote_tag_str[10];
946

    
947
        buffer = gtk_text_view_get_buffer(text);
948
        gtk_text_buffer_get_end_iter(buffer, &iter);
949

    
950
        if (!conv)
951
                strncpy2(buf, str, sizeof(buf));
952
        else if (conv_convert(conv, buf, sizeof(buf), str) < 0)
953
                conv_localetodisp(buf, sizeof(buf), str);
954

    
955
        strcrchomp(buf);
956
        //if (prefs_common.conv_mb_alnum) conv_mb_alnum(buf);
957
        fg_color = NULL;
958

    
959
        /* change color of quotation
960
           >, foo>, _> ... ok, <foo>, foo bar>, foo-> ... ng
961
           Up to 3 levels of quotations are detected, and each
962
           level is colored using a different color. */
963
        if (prefs_common.enable_color && strchr(buf, '>')) {
964
                quotelevel = get_quote_level(buf);
965

    
966
                /* set up the correct foreground color */
967
                if (quotelevel > 2) {
968
                        /* recycle colors */
969
                        if (prefs_common.recycle_quote_colors)
970
                                quotelevel %= 3;
971
                        else
972
                                quotelevel = 2;
973
                }
974
        }
975

    
976
        if (quotelevel == -1)
977
                fg_color = NULL;
978
        else {
979
                g_snprintf(quote_tag_str, sizeof(quote_tag_str),
980
                           "quote%d", quotelevel);
981
                fg_color = quote_tag_str;
982
        }
983

    
984
        if (prefs_common.enable_color)
985
                textview_make_clickable_parts(textview, fg_color, "link", buf);
986
        else
987
                textview_make_clickable_parts(textview, fg_color, NULL, buf);
988
}
989

    
990
void textview_write_link(TextView *textview, const gchar *str,
991
                         const gchar *uri, CodeConverter *conv)
992
{
993
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
994
        GtkTextBuffer *buffer;
995
        GtkTextIter iter;
996
        gchar buf[BUFFSIZE];
997
        gchar *bufp;
998
        RemoteURI *r_uri;
999

    
1000
        if (*str == '\0')
1001
                return;
1002

    
1003
        buffer = gtk_text_view_get_buffer(text);
1004
        gtk_text_buffer_get_end_iter(buffer, &iter);
1005

    
1006
        if (!conv)
1007
                strncpy2(buf, str, sizeof(buf));
1008
        else if (conv_convert(conv, buf, sizeof(buf), str) < 0)
1009
                conv_localetodisp(buf, sizeof(buf), str);
1010

    
1011
        strcrchomp(buf);
1012

    
1013
        for (bufp = buf; isspace(*(guchar *)bufp); bufp++)
1014
                gtk_text_buffer_insert(buffer, &iter, bufp, 1);
1015

    
1016
        r_uri = g_new(RemoteURI, 1);
1017
        r_uri->uri = g_strdup(uri);
1018
        r_uri->start = gtk_text_iter_get_offset(&iter);
1019
        gtk_text_buffer_insert_with_tags_by_name
1020
                (buffer, &iter, bufp, -1, "link", NULL);
1021
        r_uri->end = gtk_text_iter_get_offset(&iter);
1022
        textview->uri_list = g_slist_append(textview->uri_list, r_uri);
1023
}
1024

    
1025
void textview_clear(TextView *textview)
1026
{
1027
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1028
        GtkTextBuffer *buffer;
1029

    
1030
        buffer = gtk_text_view_get_buffer(text);
1031
        gtk_text_buffer_set_text(buffer, "", -1);
1032

    
1033
        STATUSBAR_POP(textview);
1034
        textview_uri_list_remove_all(textview->uri_list);
1035
        textview->uri_list = NULL;
1036

    
1037
        textview->body_pos = 0;
1038
        //textview->cur_pos  = 0;
1039
}
1040

    
1041
void textview_destroy(TextView *textview)
1042
{
1043
        textview_uri_list_remove_all(textview->uri_list);
1044
        textview->uri_list = NULL;
1045
        g_free(textview);
1046
}
1047

    
1048
void textview_set_all_headers(TextView *textview, gboolean all_headers)
1049
{
1050
        textview->show_all_headers = all_headers;
1051
}
1052

    
1053
void textview_set_font(TextView *textview, const gchar *codeset)
1054
{
1055
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1056

    
1057
        if (prefs_common.textfont) {
1058
                PangoFontDescription *font_desc;
1059
                font_desc = pango_font_description_from_string
1060
                        (prefs_common.textfont);
1061
                if (font_desc) {
1062
                        gtk_widget_modify_font(textview->text, font_desc);
1063
                        pango_font_description_free(font_desc);
1064
                }
1065
        }
1066

    
1067
        gtk_text_view_set_pixels_above_lines(text, prefs_common.line_space / 2);
1068
        gtk_text_view_set_pixels_below_lines(text, prefs_common.line_space / 2);
1069
#if 0
1070
        if (prefs_common.head_space)
1071
                gtk_text_view_set_left_margin(text, 6);
1072
        else
1073
                gtk_text_view_set_left_margin(text, 0);
1074
#endif
1075
}
1076

    
1077
void textview_set_position(TextView *textview, gint pos)
1078
{
1079
        GtkTextBuffer *buffer;
1080
        GtkTextIter iter;
1081

    
1082
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
1083
        gtk_text_buffer_get_iter_at_offset(buffer, &iter, pos);
1084
        gtk_text_buffer_place_cursor(buffer, &iter);
1085
}
1086

    
1087
static GPtrArray *textview_scan_header(TextView *textview, FILE *fp)
1088
{
1089
        gchar buf[BUFFSIZE];
1090
        GPtrArray *headers, *sorted_headers;
1091
        GSList *disphdr_list;
1092
        Header *header;
1093
        gint i;
1094

    
1095
        g_return_val_if_fail(fp != NULL, NULL);
1096

    
1097
        if (textview->show_all_headers)
1098
                return procheader_get_header_array_asis(fp);
1099

    
1100
        if (!prefs_common.display_header) {
1101
                while (fgets(buf, sizeof(buf), fp) != NULL)
1102
                        if (buf[0] == '\r' || buf[0] == '\n') break;
1103
                return NULL;
1104
        }
1105

    
1106
        headers = procheader_get_header_array_asis(fp);
1107

    
1108
        sorted_headers = g_ptr_array_new();
1109

    
1110
        for (disphdr_list = prefs_common.disphdr_list; disphdr_list != NULL;
1111
             disphdr_list = disphdr_list->next) {
1112
                DisplayHeaderProp *dp =
1113
                        (DisplayHeaderProp *)disphdr_list->data;
1114

    
1115
                for (i = 0; i < headers->len; i++) {
1116
                        header = g_ptr_array_index(headers, i);
1117

    
1118
                        if (!g_strcasecmp(header->name, dp->name)) {
1119
                                if (dp->hidden)
1120
                                        procheader_header_free(header);
1121
                                else
1122
                                        g_ptr_array_add(sorted_headers, header);
1123

    
1124
                                g_ptr_array_remove_index(headers, i);
1125
                                i--;
1126
                        }
1127
                }
1128
        }
1129

    
1130
        if (prefs_common.show_other_header) {
1131
                for (i = 0; i < headers->len; i++) {
1132
                        header = g_ptr_array_index(headers, i);
1133
                        g_ptr_array_add(sorted_headers, header);
1134
                }
1135
                g_ptr_array_free(headers, TRUE);
1136
        } else
1137
                procheader_header_array_destroy(headers);
1138

    
1139

    
1140
        return sorted_headers;
1141
}
1142

    
1143
static void textview_show_header(TextView *textview, GPtrArray *headers)
1144
{
1145
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1146
        GtkTextBuffer *buffer;
1147
        GtkTextIter iter;
1148
        Header *header;
1149
        gint i;
1150

    
1151
        g_return_if_fail(headers != NULL);
1152

    
1153
        buffer = gtk_text_view_get_buffer(text);
1154

    
1155
        for (i = 0; i < headers->len; i++) {
1156
                header = g_ptr_array_index(headers, i);
1157
                g_return_if_fail(header->name != NULL);
1158

    
1159
                gtk_text_buffer_get_end_iter(buffer, &iter);
1160
                gtk_text_buffer_insert_with_tags_by_name
1161
                        (buffer, &iter, header->name, -1,
1162
                         "header_title", "header", NULL);
1163
                gtk_text_buffer_insert_with_tags_by_name
1164
                        (buffer, &iter, ":", 1,
1165
                         "header_title", "header", NULL);
1166

    
1167
                if (!g_strcasecmp(header->name, "Subject") ||
1168
                    !g_strcasecmp(header->name, "From")    ||
1169
                    !g_strcasecmp(header->name, "To")      ||
1170
                    !g_strcasecmp(header->name, "Cc"))
1171
                        unfold_line(header->body);
1172

    
1173
#if 0
1174
                if (textview->text_is_mb == TRUE)
1175
                        conv_unreadable_locale(header->body);
1176
#endif
1177

    
1178
                if (prefs_common.enable_color &&
1179
                    (!strncmp(header->name, "X-Mailer", 8) ||
1180
                     !strncmp(header->name, "X-Newsreader", 12)) &&
1181
                    strstr(header->body, "Sylpheed") != NULL) {
1182
                        gtk_text_buffer_get_end_iter(buffer, &iter);
1183
                        gtk_text_buffer_insert_with_tags_by_name
1184
                                (buffer, &iter, header->body, -1,
1185
                                 "header", "emphasis", NULL);
1186
                } else if (prefs_common.enable_color) {
1187
                        textview_make_clickable_parts
1188
                                (textview, "header", "link", header->body);
1189
                } else {
1190
                        textview_make_clickable_parts
1191
                                (textview, "header", NULL, header->body);
1192
                }
1193
                gtk_text_buffer_get_end_iter(buffer, &iter); //
1194
                gtk_text_buffer_insert_with_tags_by_name
1195
                        (buffer, &iter, "\n", 1, "header", NULL);
1196
        }
1197
}
1198

    
1199
gboolean textview_search_string(TextView *textview, const gchar *str,
1200
                                gboolean case_sens)
1201
{
1202
#warning FIXME_GTK2
1203
#if 0
1204
        GtkSText *text = GTK_STEXT(textview->text);
1205
        gint pos;
1206
        gint len;
1207

1208
        g_return_val_if_fail(str != NULL, FALSE);
1209

1210
        len = get_mbs_len(str);
1211
        g_return_val_if_fail(len >= 0, FALSE);
1212

1213
        pos = textview->cur_pos;
1214
        if (pos < textview->body_pos)
1215
                pos = textview->body_pos;
1216

1217
        if ((pos = gtkut_stext_find(text, pos, str, case_sens)) != -1) {
1218
                gtk_editable_set_position(GTK_EDITABLE(text), pos + len);
1219
                gtk_editable_select_region(GTK_EDITABLE(text), pos, pos + len);
1220
                textview_set_position(textview, pos + len);
1221
                return TRUE;
1222
        }
1223

1224
#endif
1225
        return FALSE;
1226
}
1227

    
1228
gboolean textview_search_string_backward(TextView *textview, const gchar *str,
1229
                                         gboolean case_sens)
1230
{
1231
#warning FIXME_GTK2
1232
#if 0
1233
        GtkSText *text = GTK_STEXT(textview->text);
1234
        gint pos;
1235
        wchar_t *wcs;
1236
        gint len;
1237
        gint text_len;
1238
        gboolean found = FALSE;
1239

1240
        g_return_val_if_fail(str != NULL, FALSE);
1241

1242
        wcs = strdup_mbstowcs(str);
1243
        g_return_val_if_fail(wcs != NULL, FALSE);
1244
        len = wcslen(wcs);
1245
        pos = textview->cur_pos;
1246
        text_len = gtk_stext_get_length(text);
1247
        if (text_len - textview->body_pos < len) {
1248
                g_free(wcs);
1249
                return FALSE;
1250
        }
1251
        if (pos <= textview->body_pos || text_len - pos < len)
1252
                pos = text_len - len;
1253

1254
        for (; pos >= textview->body_pos; pos--) {
1255
                if (gtkut_stext_match_string(text, pos, wcs, len, case_sens)
1256
                    == TRUE) {
1257
                        gtk_editable_set_position(GTK_EDITABLE(text), pos);
1258
                        gtk_editable_select_region(GTK_EDITABLE(text),
1259
                                                   pos, pos + len);
1260
                        textview_set_position(textview, pos - 1);
1261
                        found = TRUE;
1262
                        break;
1263
                }
1264
                if (pos == textview->body_pos) break;
1265
        }
1266

1267
        g_free(wcs);
1268
        return found;
1269
#endif
1270
        return FALSE;
1271
}
1272

    
1273
void textview_scroll_one_line(TextView *textview, gboolean up)
1274
{
1275
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1276
        GtkAdjustment *vadj = text->vadjustment;
1277
        gfloat upper;
1278

    
1279
        if (prefs_common.enable_smooth_scroll) {
1280
                textview_smooth_scroll_one_line(textview, up);
1281
                return;
1282
        }
1283

    
1284
        if (!up) {
1285
                upper = vadj->upper - vadj->page_size;
1286
                if (vadj->value < upper) {
1287
                        vadj->value += vadj->step_increment * 4;
1288
                        vadj->value = MIN(vadj->value, upper);
1289
                        g_signal_emit_by_name(G_OBJECT(vadj),
1290
                                              "value_changed", 0);
1291
                }
1292
        } else {
1293
                if (vadj->value > 0.0) {
1294
                        vadj->value -= vadj->step_increment * 4;
1295
                        vadj->value = MAX(vadj->value, 0.0);
1296
                        g_signal_emit_by_name(G_OBJECT(vadj),
1297
                                              "value_changed", 0);
1298
                }
1299
        }
1300
}
1301

    
1302
gboolean textview_scroll_page(TextView *textview, gboolean up)
1303
{
1304
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1305
        GtkAdjustment *vadj = text->vadjustment;
1306
        gfloat upper;
1307
        gfloat page_incr;
1308

    
1309
        if (prefs_common.enable_smooth_scroll)
1310
                return textview_smooth_scroll_page(textview, up);
1311

    
1312
        if (prefs_common.scroll_halfpage)
1313
                page_incr = vadj->page_increment / 2;
1314
        else
1315
                page_incr = vadj->page_increment;
1316

    
1317
        if (!up) {
1318
                upper = vadj->upper - vadj->page_size;
1319
                if (vadj->value < upper) {
1320
                        vadj->value += page_incr;
1321
                        vadj->value = MIN(vadj->value, upper);
1322
                        g_signal_emit_by_name(G_OBJECT(vadj),
1323
                                              "value_changed", 0);
1324
                } else
1325
                        return FALSE;
1326
        } else {
1327
                if (vadj->value > 0.0) {
1328
                        vadj->value -= page_incr;
1329
                        vadj->value = MAX(vadj->value, 0.0);
1330
                        g_signal_emit_by_name(G_OBJECT(vadj),
1331
                                              "value_changed", 0);
1332
                } else
1333
                        return FALSE;
1334
        }
1335

    
1336
        return TRUE;
1337
}
1338

    
1339
static void textview_smooth_scroll_do(TextView *textview,
1340
                                      gfloat old_value, gfloat last_value,
1341
                                      gint step)
1342
{
1343
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1344
        GtkAdjustment *vadj = text->vadjustment;
1345
        gint change_value;
1346
        gboolean up;
1347
        gint i;
1348

    
1349
        if (old_value < last_value) {
1350
                change_value = last_value - old_value;
1351
                up = FALSE;
1352
        } else {
1353
                change_value = old_value - last_value;
1354
                up = TRUE;
1355
        }
1356

    
1357
#warning FIXME_GTK2
1358
        /* gdk_key_repeat_disable(); */
1359

    
1360
        for (i = step; i <= change_value; i += step) {
1361
                vadj->value = old_value + (up ? -i : i);
1362
                g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0);
1363
        }
1364

    
1365
        vadj->value = last_value;
1366
        g_signal_emit_by_name(G_OBJECT(vadj), "value_changed", 0);
1367

    
1368
#warning FIXME_GTK2
1369
        /* gdk_key_repeat_restore(); */
1370
}
1371

    
1372
static void textview_smooth_scroll_one_line(TextView *textview, gboolean up)
1373
{
1374
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1375
        GtkAdjustment *vadj = text->vadjustment;
1376
        gfloat upper;
1377
        gfloat old_value;
1378
        gfloat last_value;
1379

    
1380
        if (!up) {
1381
                upper = vadj->upper - vadj->page_size;
1382
                if (vadj->value < upper) {
1383
                        old_value = vadj->value;
1384
                        last_value = vadj->value + vadj->step_increment * 4;
1385
                        last_value = MIN(last_value, upper);
1386

    
1387
                        textview_smooth_scroll_do(textview, old_value,
1388
                                                  last_value,
1389
                                                  prefs_common.scroll_step);
1390
                }
1391
        } else {
1392
                if (vadj->value > 0.0) {
1393
                        old_value = vadj->value;
1394
                        last_value = vadj->value - vadj->step_increment * 4;
1395
                        last_value = MAX(last_value, 0.0);
1396

    
1397
                        textview_smooth_scroll_do(textview, old_value,
1398
                                                  last_value,
1399
                                                  prefs_common.scroll_step);
1400
                }
1401
        }
1402
}
1403

    
1404
static gboolean textview_smooth_scroll_page(TextView *textview, gboolean up)
1405
{
1406
        GtkTextView *text = GTK_TEXT_VIEW(textview->text);
1407
        GtkAdjustment *vadj = text->vadjustment;
1408
        gfloat upper;
1409
        gfloat page_incr;
1410
        gfloat old_value;
1411
        gfloat last_value;
1412

    
1413
        if (prefs_common.scroll_halfpage)
1414
                page_incr = vadj->page_increment / 2;
1415
        else
1416
                page_incr = vadj->page_increment;
1417

    
1418
        if (!up) {
1419
                upper = vadj->upper - vadj->page_size;
1420
                if (vadj->value < upper) {
1421
                        old_value = vadj->value;
1422
                        last_value = vadj->value + page_incr;
1423
                        last_value = MIN(last_value, upper);
1424

    
1425
                        textview_smooth_scroll_do(textview, old_value,
1426
                                                  last_value,
1427
                                                  prefs_common.scroll_step);
1428
                } else
1429
                        return FALSE;
1430
        } else {
1431
                if (vadj->value > 0.0) {
1432
                        old_value = vadj->value;
1433
                        last_value = vadj->value - page_incr;
1434
                        last_value = MAX(last_value, 0.0);
1435

    
1436
                        textview_smooth_scroll_do(textview, old_value,
1437
                                                  last_value,
1438
                                                  prefs_common.scroll_step);
1439
                } else
1440
                        return FALSE;
1441
        }
1442

    
1443
        return TRUE;
1444
}
1445

    
1446
#warning FIXME_GTK2
1447
#if 0
1448
#define KEY_PRESS_EVENT_STOP() \
1449
        if (gtk_signal_n_emissions_by_name \
1450
                (GTK_OBJECT(widget), "key_press_event") > 0) { \
1451
                gtk_signal_emit_stop_by_name(GTK_OBJECT(widget), \
1452
                                             "key_press_event"); \
1453
        }
1454
#else
1455
#define KEY_PRESS_EVENT_STOP() \
1456
        g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event");
1457
#endif
1458

    
1459
static gboolean textview_key_pressed(GtkWidget *widget, GdkEventKey *event,
1460
                                     TextView *textview)
1461
{
1462
        SummaryView *summaryview = NULL;
1463
        MessageView *messageview = textview->messageview;
1464

    
1465
        if (!event) return FALSE;
1466
        if (messageview->mainwin)
1467
                summaryview = messageview->mainwin->summaryview;
1468

    
1469
        switch (event->keyval) {
1470
        case GDK_Tab:
1471
        case GDK_Home:
1472
        case GDK_Left:
1473
        case GDK_Up:
1474
        case GDK_Right:
1475
        case GDK_Down:
1476
        case GDK_Page_Up:
1477
        case GDK_Page_Down:
1478
        case GDK_End:
1479
        case GDK_Control_L:
1480
        case GDK_Control_R:
1481
                break;
1482
        case GDK_space:
1483
                if (summaryview)
1484
                        summary_pass_key_press_event(summaryview, event);
1485
                else
1486
                        textview_scroll_page
1487
                                (textview,
1488
                                 (event->state &
1489
                                  (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
1490
                break;
1491
        case GDK_BackSpace:
1492
                textview_scroll_page(textview, TRUE);
1493
                break;
1494
        case GDK_Return:
1495
                textview_scroll_one_line
1496
                        (textview, (event->state &
1497
                                    (GDK_SHIFT_MASK|GDK_MOD1_MASK)) != 0);
1498
                break;
1499
        case GDK_Delete:
1500
                if (summaryview)
1501
                        summary_pass_key_press_event(summaryview, event);
1502
                break;
1503
        case GDK_n:
1504
        case GDK_N:
1505
        case GDK_p:
1506
        case GDK_P:
1507
        case GDK_y:
1508
        case GDK_t:
1509
        case GDK_l:
1510
                if (messageview->type == MVIEW_MIME &&
1511
                    textview == messageview->mimeview->textview) {
1512
                        KEY_PRESS_EVENT_STOP();
1513
                        mimeview_pass_key_press_event(messageview->mimeview,
1514
                                                      event);
1515
                        break;
1516
                }
1517
                /* fall through */
1518
        default:
1519
                if (summaryview &&
1520
                    event->window != messageview->mainwin->window->window) {
1521
                        GdkEventKey tmpev = *event;
1522

    
1523
                        tmpev.window = messageview->mainwin->window->window;
1524
                        KEY_PRESS_EVENT_STOP();
1525
                        gtk_widget_event(messageview->mainwin->window,
1526
                                         (GdkEvent *)&tmpev);
1527
                }
1528
                break;
1529
        }
1530

    
1531
        return FALSE;
1532
}
1533

    
1534
static gboolean textview_uri_button_pressed(GtkTextTag *tag, GObject *obj,
1535
                                            GdkEvent *event, GtkTextIter *iter,
1536
                                            TextView *textview)
1537
{
1538
        GtkTextIter start_iter, end_iter;
1539
        gint start_pos, end_pos;
1540
        GdkEventButton *bevent;
1541
        RemoteURI *uri = NULL;
1542
        GSList *cur;
1543
        gchar *trimmed_uri;
1544

    
1545
        if (!event)
1546
                return FALSE;
1547

    
1548
        if (event->type != GDK_BUTTON_PRESS && event->type != GDK_2BUTTON_PRESS)
1549
                return FALSE;
1550

    
1551
        start_iter = *iter;
1552

    
1553
        if (!gtk_text_iter_backward_to_tag_toggle(&start_iter, tag)) {
1554
                debug_print("Can't find start point.");
1555
                return FALSE;
1556
        }
1557
        start_pos = gtk_text_iter_get_offset(&start_iter);
1558

    
1559
        end_iter = *iter;
1560
        if (!gtk_text_iter_forward_to_tag_toggle(&end_iter, tag)) {
1561
                debug_print("Can't find end");
1562
                return FALSE;
1563
        }
1564
        end_pos = gtk_text_iter_get_offset(&end_iter);
1565

    
1566
        for (cur = textview->uri_list; cur != NULL; cur = cur->next) {
1567
                RemoteURI *uri_ = (RemoteURI *)cur->data;
1568

    
1569
                if (start_pos == uri_->start && end_pos == uri_->end) {
1570
                        uri = uri_;
1571
                        break;
1572
                }
1573
        }
1574

    
1575
        STATUSBAR_POP(textview);
1576

    
1577
        if (!uri)
1578
                return FALSE;
1579

    
1580
        trimmed_uri = trim_string(uri->uri, 60);
1581
        STATUSBAR_PUSH(textview, trimmed_uri);
1582
        g_free(trimmed_uri);
1583

    
1584
        bevent = (GdkEventButton *)event;
1585
        if ((event->type == GDK_2BUTTON_PRESS && bevent->button == 1) ||
1586
             bevent->button == 2) {
1587
                if (!g_strncasecmp(uri->uri, "mailto:", 7)) {
1588
                        PrefsAccount *ac = NULL;
1589
                        MsgInfo *msginfo = textview->messageview->msginfo;
1590

    
1591
                        if (msginfo && msginfo->folder)
1592
                                ac = account_find_from_item(msginfo->folder);
1593
                        if (ac && ac->protocol == A_NNTP)
1594
                                ac = NULL;
1595
                        compose_new(ac, msginfo->folder, uri->uri + 7, NULL);
1596
                } else if (textview_uri_security_check(textview, uri) == TRUE) {
1597
                        open_uri(uri->uri, prefs_common.uri_cmd);
1598
                        return TRUE; //
1599
                }
1600
        }
1601

    
1602
        return FALSE;
1603
}
1604

    
1605
static gboolean textview_uri_security_check(TextView *textview, RemoteURI *uri)
1606
{
1607
        GtkTextBuffer *buffer;
1608
        GtkTextIter start_iter, end_iter;
1609
        gchar *visible_str;
1610
        gboolean retval = TRUE;
1611

    
1612
        if (is_uri_string(uri->uri) == FALSE)
1613
                return TRUE;
1614

    
1615
        buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview->text));
1616
        gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, uri->start);
1617
        gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, uri->end);
1618
        visible_str = gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
1619
                                               FALSE);
1620
        if (visible_str == NULL)
1621
                return TRUE;
1622

    
1623
        if (strcmp(visible_str, uri->uri) != 0 && is_uri_string(visible_str)) {
1624
                gchar *uri_path;
1625
                gchar *visible_uri_path;
1626

    
1627
                uri_path = get_uri_path(uri->uri);
1628
                visible_uri_path = get_uri_path(visible_str);
1629
                if (strcmp(uri_path, visible_uri_path) != 0)
1630
                        retval = FALSE;
1631
        }
1632

    
1633
        if (retval == FALSE) {
1634
                gchar *msg;
1635
                AlertValue aval;
1636

    
1637
                msg = g_strdup_printf(_("The real URL (%s) is different from\n"
1638
                                        "the apparent URL (%s).\n"
1639
                                        "Open it anyway?"),
1640
                                      uri->uri, visible_str);
1641
                aval = alertpanel(_("Warning"), msg, _("Yes"), _("No"), NULL);
1642
                g_free(msg);
1643
                if (aval == G_ALERTDEFAULT)
1644
                        retval = TRUE;
1645
        }
1646

    
1647
        g_free(visible_str);
1648

    
1649
        return retval;
1650
}
1651

    
1652
static void textview_uri_list_remove_all(GSList *uri_list)
1653
{
1654
        GSList *cur;
1655

    
1656
        for (cur = uri_list; cur != NULL; cur = cur->next) {
1657
                if (cur->data) {
1658
                        g_free(((RemoteURI *)cur->data)->uri);
1659
                        g_free(cur->data);
1660
                }
1661
        }
1662

    
1663
        g_slist_free(uri_list);
1664
}