diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51fadb6b..51e28ccf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -235,6 +235,7 @@ client/composer/email-entry.vala client/composer/webview-edit-fixer.vala client/dialogs/alert-dialog.vala +client/dialogs/attachment-dialog.vala client/dialogs/password-dialog.vala client/dialogs/preferences-dialog.vala diff --git a/src/client/composer/composer-window.vala b/src/client/composer/composer-window.vala index f41e31bb..1830bd36 100644 --- a/src/client/composer/composer-window.vala +++ b/src/client/composer/composer-window.vala @@ -164,7 +164,6 @@ public class ComposerWindow : Gtk.Window { private bool action_flag = false; private bool is_attachment_overlay_visible = false; private Gee.List? pending_attachments = null; - private string? current_folder = null; private WebKit.WebView editor; // We need to keep a reference to the edit-fixer in composer-window, so it doesn't get @@ -687,32 +686,16 @@ public class ComposerWindow : Gtk.Window { } private void on_add_attachment_button_clicked() { - bool finished = true; + AttachmentDialog dialog = null; do { - Gtk.FileChooserDialog dialog = new Gtk.FileChooserDialog( - _("Choose a file"), this, Gtk.FileChooserAction.OPEN, - Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL, - _("_Attach"), Gtk.ResponseType.ACCEPT); - if (!Geary.String.is_empty(current_folder)) - dialog.set_current_folder(current_folder); - dialog.set_local_only(false); - dialog.set_select_multiple(true); - - if (dialog.run() == Gtk.ResponseType.ACCEPT) { - current_folder = dialog.get_current_folder(); - - foreach (File file in dialog.get_files()) { - if (!add_attachment(file)) { - finished = false; - break; - } - } - } else { - finished = true; - } - - dialog.destroy(); - } while (!finished); + // Transient parent of AttachmentDialog is this ComposerWindow + // But this generates the following warning: + // Attempting to add a widget with type AttachmentDialog to a + // ComposerWindow, but as a GtkBin subclass a ComposerWindow can + // only contain one widget at a time; + // it already contains a widget of type GtkBox + dialog = new AttachmentDialog(this); + } while (!dialog.is_finished(add_attachment)); } private void on_pending_attachments_button_clicked() { diff --git a/src/client/dialogs/attachment-dialog.vala b/src/client/dialogs/attachment-dialog.vala new file mode 100644 index 00000000..bac473eb --- /dev/null +++ b/src/client/dialogs/attachment-dialog.vala @@ -0,0 +1,104 @@ +/* Copyright 2011-2013 Yorba Foundation + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +public class AttachmentDialog : Gtk.FileChooserDialog { + private static const int PREVIEW_SIZE = 180; + private static const int PREVIEW_PADDING = 3; + + private static string? current_folder = null; + + private Gtk.Image preview_image; + + public delegate bool Attacher(File attachment_file, bool alert_errors = true); + + public AttachmentDialog(Gtk.Window? parent) { + Object(title: _("Choose a file"), transient_for: parent, action: Gtk.FileChooserAction.OPEN); + } + + construct { + add_button(Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL); + add_button(_("_Attach"), Gtk.ResponseType.ACCEPT); + + if (!Geary.String.is_empty(current_folder)) { + set_current_folder(current_folder); + } + set_local_only(false); + set_select_multiple(true); + + preview_image = new Gtk.Image(); + set_preview_widget(preview_image); + use_preview_label = false; + + update_preview.connect(on_update_preview); + } + + public bool is_finished(Attacher add_attachment) { + if (run() != Gtk.ResponseType.ACCEPT) { + destroy(); + return true; + } + current_folder = get_current_folder(); + foreach (File file in get_files()) { + if (!add_attachment(file)) { + destroy(); + return false; + } + } + destroy(); + return true; + } + + private void on_update_preview() { + string? filename = get_preview_filename(); + if (filename == null) { + set_preview_widget_active(false); + return; + } + + // read the image format data first + int width = 0; + int height = 0; + Gdk.PixbufFormat? format = Gdk.Pixbuf.get_file_info(filename, out width, out height); + + if (format == null) { + set_preview_widget_active(false); + return; + } + + // if the image is too big, resize it + Gdk.Pixbuf pixbuf; + try { + if (width > PREVIEW_SIZE || height > PREVIEW_SIZE) { + pixbuf = new Gdk.Pixbuf.from_file_at_size(filename, PREVIEW_SIZE, PREVIEW_SIZE); + } else { + pixbuf = new Gdk.Pixbuf.from_file(filename); + } + } catch (Error e) { + set_preview_widget_active(false); + return; + } + + if (pixbuf == null) { + set_preview_widget_active(false); + return; + } + + pixbuf.apply_embedded_orientation(); + + // distribute the extra space around the image + int extra_space = PREVIEW_SIZE - pixbuf.width; + int smaller_half = extra_space/2; + int larger_half = extra_space - smaller_half; + + // pad the image manually (avoids rounding errors) + preview_image.set_margin_left(PREVIEW_PADDING + smaller_half); + preview_image.set_margin_right(PREVIEW_PADDING + larger_half); + + // show the preview + preview_image.set_from_pixbuf(pixbuf); + set_preview_widget_active(true); + } +}