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