1 /*
2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2014 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/gdkkeysyms.h>
29 #include <gtk/gtkwidget.h>
30 #include <gtk/gtkwindow.h>
31 #include <gtk/gtkvbox.h>
32 #include <gtk/gtktable.h>
33 #include <gtk/gtklabel.h>
34 #include <gtk/gtkentry.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkcheckbutton.h>
37 #include <gtk/gtkhbbox.h>
38 #include <gtk/gtkbutton.h>
39 #include <gtk/gtkstock.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "main.h"
45 #include "message_search.h"
46 #include "messageview.h"
47 #include "utils.h"
48 #include "gtkutils.h"
49 #include "manage_window.h"
50 #include "alertpanel.h"
51
52 static struct MessageSearchWindow {
53 GtkWidget *window;
54 GtkWidget *body_entry;
55 GtkWidget *case_checkbtn;
56 GtkWidget *next_btn;
57 GtkWidget *prev_btn;
58 GtkWidget *close_btn;
59
60 MessageView *messageview;
61 } search_window;
62
63 static void message_search_create (void);
64 static void message_search_execute (gboolean backward);
65 static void message_search_close (void);
66
67 static void message_search_prev_clicked (GtkButton *button,
68 gpointer data);
69 static void message_search_next_clicked (GtkButton *button,
70 gpointer data);
71 static void close_clicked (GtkButton *button,
72 gpointer data);
73 static void body_activated (void);
74 static gboolean window_deleted (GtkWidget *widget,
75 GdkEventAny *event,
76 gpointer data);
77 static gboolean key_pressed (GtkWidget *widget,
78 GdkEventKey *event,
79 gpointer data);
80
81 static void view_destroyed (GtkWidget *widget,
82 gpointer data);
83
message_search(MessageView * messageview)84 void message_search(MessageView *messageview)
85 {
86 if (!search_window.window)
87 message_search_create();
88
89 search_window.messageview = messageview;
90 g_signal_handlers_disconnect_by_func(GTK_WIDGET_PTR(messageview),
91 view_destroyed, messageview);
92 g_signal_connect(G_OBJECT(GTK_WIDGET_PTR(messageview)), "destroy",
93 G_CALLBACK(view_destroyed), messageview);
94
95 gtk_widget_grab_focus(search_window.next_btn);
96 gtk_widget_grab_focus(search_window.body_entry);
97 gtk_widget_show(search_window.window);
98 gtk_window_present(GTK_WINDOW(search_window.window));
99 }
100
message_search_create(void)101 static void message_search_create(void)
102 {
103 GtkWidget *window;
104
105 GtkWidget *vbox1;
106 GtkWidget *hbox1;
107 GtkWidget *body_label;
108 GtkWidget *body_entry;
109
110 GtkWidget *checkbtn_hbox;
111 GtkWidget *case_checkbtn;
112
113 GtkWidget *confirm_area;
114 GtkWidget *next_btn;
115 GtkWidget *prev_btn;
116 GtkWidget *close_btn;
117
118 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
119 gtk_window_set_title (GTK_WINDOW (window),
120 _("Find in current message"));
121 gtk_widget_set_size_request (window, 450 * gtkut_get_dpi_multiplier(), -1);
122 gtk_window_set_policy(GTK_WINDOW(window), FALSE, TRUE, TRUE);
123 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
124 g_signal_connect(G_OBJECT(window), "delete_event",
125 G_CALLBACK(window_deleted), NULL);
126 g_signal_connect(G_OBJECT(window), "key_press_event",
127 G_CALLBACK(key_pressed), NULL);
128 MANAGE_WINDOW_SIGNALS_CONNECT(window);
129
130 vbox1 = gtk_vbox_new (FALSE, 0);
131 gtk_widget_show (vbox1);
132 gtk_container_add (GTK_CONTAINER (window), vbox1);
133
134 hbox1 = gtk_hbox_new (FALSE, 8);
135 gtk_widget_show (hbox1);
136 gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);
137
138 body_label = gtk_label_new (_("Find text:"));
139 gtk_widget_show (body_label);
140 gtk_box_pack_start (GTK_BOX (hbox1), body_label, FALSE, FALSE, 0);
141
142 body_entry = gtk_entry_new ();
143 gtk_widget_show (body_entry);
144 gtk_box_pack_start (GTK_BOX (hbox1), body_entry, TRUE, TRUE, 0);
145 g_signal_connect(G_OBJECT(body_entry), "activate",
146 G_CALLBACK(body_activated), NULL);
147
148 checkbtn_hbox = gtk_hbox_new (FALSE, 8);
149 gtk_widget_show (checkbtn_hbox);
150 gtk_box_pack_start (GTK_BOX (vbox1), checkbtn_hbox, TRUE, TRUE, 0);
151 gtk_container_set_border_width (GTK_CONTAINER (checkbtn_hbox), 8);
152
153 case_checkbtn = gtk_check_button_new_with_label (_("Case sensitive"));
154 gtk_widget_show (case_checkbtn);
155 gtk_box_pack_start (GTK_BOX (checkbtn_hbox), case_checkbtn,
156 FALSE, FALSE, 0);
157
158 gtkut_stock_button_set_create(&confirm_area,
159 &next_btn, GTK_STOCK_GO_FORWARD,
160 &prev_btn, GTK_STOCK_GO_BACK,
161 &close_btn, GTK_STOCK_CLOSE);
162 gtkut_box_set_reverse_order(GTK_BOX(confirm_area), FALSE);
163 gtk_widget_show (confirm_area);
164 gtk_box_pack_start (GTK_BOX (vbox1), confirm_area, FALSE, FALSE, 0);
165 gtk_widget_grab_default(next_btn);
166
167 g_signal_connect(G_OBJECT(prev_btn), "clicked",
168 G_CALLBACK(message_search_prev_clicked), NULL);
169 g_signal_connect(G_OBJECT(next_btn), "clicked",
170 G_CALLBACK(message_search_next_clicked), NULL);
171 g_signal_connect(G_OBJECT(close_btn), "clicked",
172 G_CALLBACK(close_clicked), NULL);
173
174 search_window.window = window;
175 search_window.body_entry = body_entry;
176 search_window.case_checkbtn = case_checkbtn;
177 search_window.next_btn = next_btn;
178 search_window.prev_btn = prev_btn;
179 search_window.close_btn = close_btn;
180 }
181
message_search_execute(gboolean backward)182 static void message_search_execute(gboolean backward)
183 {
184 MessageView *messageview = search_window.messageview;
185 gboolean case_sens;
186 gboolean all_searched = FALSE;
187 const gchar *body_str;
188
189 body_str = gtk_entry_get_text(GTK_ENTRY(search_window.body_entry));
190 if (*body_str == '\0') return;
191
192 case_sens = gtk_toggle_button_get_active
193 (GTK_TOGGLE_BUTTON(search_window.case_checkbtn));
194
195 for (;;) {
196 gchar *str;
197 AlertValue val;
198
199 if (backward) {
200 if (messageview_search_string_backward
201 (messageview, body_str, case_sens) == TRUE)
202 break;
203 } else {
204 if (messageview_search_string
205 (messageview, body_str, case_sens) == TRUE)
206 break;
207 }
208
209 if (all_searched) {
210 alertpanel_message
211 (_("Search failed"),
212 _("Search string not found."),
213 ALERT_WARNING);
214 break;
215 }
216
217 all_searched = TRUE;
218
219 if (backward)
220 str = _("Beginning of message reached; "
221 "continue from end?");
222 else
223 str = _("End of message reached; "
224 "continue from beginning?");
225
226 val = alertpanel(_("Search finished"), str,
227 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
228 if (G_ALERTDEFAULT == val) {
229 manage_window_focus_in(search_window.window,
230 NULL, NULL);
231 messageview_set_position(messageview,
232 backward ? -1 : 0);
233 } else
234 break;
235 }
236 }
237
message_search_close(void)238 static void message_search_close(void)
239 {
240 search_window.messageview = NULL;
241 gtk_widget_hide(search_window.window);
242 }
243
message_search_prev_clicked(GtkButton * button,gpointer data)244 static void message_search_prev_clicked(GtkButton *button, gpointer data)
245 {
246 message_search_execute(TRUE);
247 }
248
message_search_next_clicked(GtkButton * button,gpointer data)249 static void message_search_next_clicked(GtkButton *button, gpointer data)
250 {
251 message_search_execute(FALSE);
252 }
253
close_clicked(GtkButton * button,gpointer data)254 static void close_clicked(GtkButton *button, gpointer data)
255 {
256 message_search_close();
257 }
258
body_activated(void)259 static void body_activated(void)
260 {
261 gtk_button_clicked(GTK_BUTTON(search_window.next_btn));
262 }
263
window_deleted(GtkWidget * widget,GdkEventAny * event,gpointer data)264 static gboolean window_deleted(GtkWidget *widget, GdkEventAny *event,
265 gpointer data)
266 {
267 message_search_close();
268 return TRUE;
269 }
270
key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)271 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event,
272 gpointer data)
273 {
274 if (event && event->keyval == GDK_Escape)
275 message_search_close();
276 return FALSE;
277 }
278
view_destroyed(GtkWidget * widget,gpointer data)279 static void view_destroyed(GtkWidget *widget, gpointer data)
280 {
281 if ((MessageView *)data == search_window.messageview)
282 message_search_close();
283 }
284