Statistics
| Revision:

root / src / textview.c @ 180

History | View | Annotate | Download (51.5 kB)

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