1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /* Balsa E-Mail Client
3 * Copyright (C) 1997-2013 Stuart Parmenter and others,
4 * See the file AUTHORS for a list.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
23 # include "config.h"
24 #endif /* HAVE_CONFIG_H */
25 #include "balsa-mime-widget-callbacks.h"
26
27 #include <string.h>
28 #include "balsa-app.h"
29 #include <glib/gi18n.h>
30 #include "libbalsa-vfs.h"
31 #include "balsa-message.h"
32 #include "balsa-mime-widget.h"
33
34 #include <gdk/gdkkeysyms.h>
35
36 #if HAVE_MACOSX_DESKTOP
37 # include "macosx-helpers.h"
38 #endif
39
40
41 void
balsa_mime_widget_ctx_menu_cb(GtkWidget * menu_item,LibBalsaMessageBody * mime_body)42 balsa_mime_widget_ctx_menu_cb(GtkWidget * menu_item,
43 LibBalsaMessageBody * mime_body)
44 {
45 GError *err = NULL;
46 gboolean result;
47
48 g_return_if_fail(mime_body != NULL);
49 result = libbalsa_vfs_launch_app_for_body(mime_body,
50 G_OBJECT(menu_item),
51 &err);
52 if (!result)
53 balsa_information(LIBBALSA_INFORMATION_WARNING,
54 _("Could not launch application: %s"),
55 err ? err->message : "Unknown error");
56 g_clear_error(&err);
57 }
58
59
60 /** Pops up a "save part" dialog for a message part.
61
62 @param parent_widget the widget located in the window that is to
63 became a parent for the dialog box.
64
65 @param mime_body message part to be saved.
66 */
67 void
balsa_mime_widget_ctx_menu_save(GtkWidget * parent_widget,LibBalsaMessageBody * mime_body)68 balsa_mime_widget_ctx_menu_save(GtkWidget * parent_widget,
69 LibBalsaMessageBody * mime_body)
70 {
71 gchar *cont_type, *title;
72 GtkWidget *save_dialog;
73 gchar *file_uri;
74 LibbalsaVfs *save_file;
75 gboolean do_save;
76 GError *err = NULL;
77
78 g_return_if_fail(mime_body != NULL);
79
80 cont_type = libbalsa_message_body_get_mime_type(mime_body);
81 title = g_strdup_printf(_("Save %s MIME Part"), cont_type);
82
83 save_dialog =
84 gtk_file_chooser_dialog_new(title,
85 balsa_get_parent_window(parent_widget),
86 GTK_FILE_CHOOSER_ACTION_SAVE,
87 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
88 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
89 #if HAVE_MACOSX_DESKTOP
90 libbalsa_macosx_menu_for_parent(save_dialog, balsa_get_parent_window(parent_widget));
91 #endif
92 gtk_dialog_set_default_response(GTK_DIALOG(save_dialog),
93 GTK_RESPONSE_OK);
94 g_free(title);
95 g_free(cont_type);
96
97 gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(save_dialog),
98 libbalsa_vfs_local_only());
99 if (balsa_app.save_dir)
100 gtk_file_chooser_set_current_folder_uri(GTK_FILE_CHOOSER(save_dialog),
101 balsa_app.save_dir);
102
103 if (mime_body->filename) {
104 gchar * filename = g_strdup(mime_body->filename);
105 libbalsa_utf8_sanitize(&filename, balsa_app.convert_unknown_8bit,
106 NULL);
107 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(save_dialog),
108 filename);
109 g_free(filename);
110 }
111
112 gtk_window_set_modal(GTK_WINDOW(save_dialog), TRUE);
113 if (gtk_dialog_run(GTK_DIALOG(save_dialog)) != GTK_RESPONSE_OK) {
114 gtk_widget_destroy(save_dialog);
115 return;
116 }
117
118 /* get the file name */
119 file_uri = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(save_dialog));
120 gtk_widget_destroy(save_dialog);
121 if (!(save_file = libbalsa_vfs_new_from_uri(file_uri))) {
122 balsa_information(LIBBALSA_INFORMATION_ERROR,
123 _("Could not construct URI from %s"),
124 file_uri);
125 g_free(file_uri);
126 return;
127 }
128
129 /* remember the folder uri */
130 g_free(balsa_app.save_dir);
131 balsa_app.save_dir = g_strdup(libbalsa_vfs_get_folder(save_file));
132
133 /* get a confirmation to overwrite if the file exists */
134 if (libbalsa_vfs_file_exists(save_file)) {
135 GtkWidget *confirm;
136
137 /* File exists. check if they really want to overwrite */
138 confirm = gtk_message_dialog_new(GTK_WINDOW(balsa_app.main_window),
139 GTK_DIALOG_MODAL,
140 GTK_MESSAGE_QUESTION,
141 GTK_BUTTONS_YES_NO,
142 _("File already exists. Overwrite?"));
143 #if HAVE_MACOSX_DESKTOP
144 libbalsa_macosx_menu_for_parent(confirm, GTK_WINDOW(balsa_app.main_window));
145 #endif
146 do_save =
147 (gtk_dialog_run(GTK_DIALOG(confirm)) == GTK_RESPONSE_YES);
148 gtk_widget_destroy(confirm);
149 if (do_save)
150 if (libbalsa_vfs_file_unlink(save_file, &err) != 0)
151 balsa_information(LIBBALSA_INFORMATION_ERROR,
152 _("Unlink %s: %s"),
153 file_uri, err ? err->message : "Unknown error");
154 } else
155 do_save = TRUE;
156
157 /* save the file */
158 if (do_save) {
159 if (!libbalsa_message_body_save_vfs(mime_body, save_file,
160 LIBBALSA_MESSAGE_BODY_UNSAFE,
161 mime_body->body_type ==
162 LIBBALSA_MESSAGE_BODY_TYPE_TEXT,
163 &err)) {
164 balsa_information(LIBBALSA_INFORMATION_ERROR,
165 _("Could not save %s: %s"),
166 file_uri, err ? err->message : "Unknown error");
167 g_clear_error(&err);
168 }
169 }
170
171 g_object_unref(save_file);
172 g_free(file_uri);
173 }
174
175 static void
scroll_change(GtkAdjustment * adj,gint diff,BalsaMessage * bm)176 scroll_change(GtkAdjustment * adj, gint diff, BalsaMessage * bm)
177 {
178 gdouble upper =
179 gtk_adjustment_get_upper(adj) - gtk_adjustment_get_page_size(adj);
180 gdouble value;
181
182 if (bm && gtk_adjustment_get_value(adj) >= upper && diff > 0) {
183 if (balsa_window_next_unread(balsa_app.main_window))
184 /* We're changing mailboxes, and GtkNotebook will grab the
185 * focus, so we want to grab it back the next time we lose
186 * it. */
187 bm->focus_state = BALSA_MESSAGE_FOCUS_STATE_HOLD;
188 return;
189 }
190
191 value = gtk_adjustment_get_value(adj);
192 value += (gdouble) diff;
193
194 gtk_adjustment_set_value(adj, MIN(value, upper));
195 }
196
197 gint
balsa_mime_widget_key_press_event(GtkWidget * widget,GdkEventKey * event,BalsaMessage * bm)198 balsa_mime_widget_key_press_event(GtkWidget * widget, GdkEventKey * event,
199 BalsaMessage * bm)
200 {
201 GtkAdjustment *adj;
202 int page_adjust;
203
204 adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW
205 (bm->scroll));
206
207 page_adjust = balsa_app.pgdownmod ?
208 (gtk_adjustment_get_page_size(adj) * balsa_app.pgdown_percent) /
209 100 : gtk_adjustment_get_page_increment(adj);
210
211 switch (event->keyval) {
212 case GDK_KEY_Up:
213 scroll_change(adj, -gtk_adjustment_get_step_increment(adj), NULL);
214 break;
215 case GDK_KEY_Down:
216 scroll_change(adj, gtk_adjustment_get_step_increment(adj), NULL);
217 break;
218 case GDK_KEY_Page_Up:
219 scroll_change(adj, -page_adjust, NULL);
220 break;
221 case GDK_KEY_Page_Down:
222 scroll_change(adj, page_adjust, NULL);
223 break;
224 case GDK_KEY_Home:
225 if (event->state & GDK_CONTROL_MASK)
226 scroll_change(adj, -gtk_adjustment_get_value(adj), NULL);
227 else
228 return FALSE;
229 break;
230 case GDK_KEY_End:
231 if (event->state & GDK_CONTROL_MASK)
232 scroll_change(adj, gtk_adjustment_get_upper(adj), NULL);
233 else
234 return FALSE;
235 break;
236 case GDK_KEY_F10:
237 if (event->state & GDK_SHIFT_MASK) {
238 GtkWidget *current_widget = balsa_message_current_part_widget(bm);
239
240 if (current_widget) {
241 gboolean retval;
242
243 g_signal_emit_by_name(current_widget, "popup-menu", &retval);
244
245 return retval;
246 } else
247 return FALSE;
248 } else
249 return FALSE;
250 break;
251 case GDK_KEY_space:
252 scroll_change(adj, page_adjust, bm);
253 break;
254
255 default:
256 return FALSE;
257 }
258 return TRUE;
259 }
260
261
262 gint
balsa_mime_widget_limit_focus(GtkWidget * widget,GdkEventFocus * event,BalsaMessage * bm)263 balsa_mime_widget_limit_focus(GtkWidget * widget, GdkEventFocus * event, BalsaMessage * bm)
264 {
265 /* Disable can_focus on other message parts so that TAB does not
266 * attempt to move the focus on them. */
267 GList *list = g_list_append(NULL, widget);
268
269 gtk_container_set_focus_chain(GTK_CONTAINER(bm->bm_widget->container), list);
270 g_list_free(list);
271 if (bm->focus_state == BALSA_MESSAGE_FOCUS_STATE_NO)
272 bm->focus_state = BALSA_MESSAGE_FOCUS_STATE_YES;
273 return FALSE;
274 }
275
276
277 gint
balsa_mime_widget_unlimit_focus(GtkWidget * widget,GdkEventFocus * event,BalsaMessage * bm)278 balsa_mime_widget_unlimit_focus(GtkWidget * widget, GdkEventFocus * event, BalsaMessage * bm)
279 {
280 gtk_container_unset_focus_chain(GTK_CONTAINER(bm->bm_widget->container));
281 if (bm->message) {
282 BalsaMessageFocusState focus_state = bm->focus_state;
283 if (focus_state == BALSA_MESSAGE_FOCUS_STATE_HOLD) {
284 balsa_message_grab_focus(bm);
285 bm->focus_state = BALSA_MESSAGE_FOCUS_STATE_YES;
286 } else
287 bm->focus_state = BALSA_MESSAGE_FOCUS_STATE_NO;
288 }
289 return FALSE;
290 }
291