[Xfce4-commits] <postler:master> Rework attachments as class for composer and content

Christian Dywan noreply at xfce.org
Sun Jan 30 09:26:01 CET 2011


Updating branch refs/heads/master
         to 46381c45ecd83b6552f0da151db2da7d33f02c60 (commit)
       from 258acf281b04dc1125d72fe8d9a0402f3b340e70 (commit)

commit 46381c45ecd83b6552f0da151db2da7d33f02c60
Author: Christian Dywan <christian at twotoasts.de>
Date:   Sun Jan 30 08:23:53 2011 +0100

    Rework attachments as class for composer and content
    
    The model contains message parts.
    
    The widget can optionally be editable or not.
    
    Either filename or description is shown next to icons.
    
    Exactly one item can and should be selected at any time.
    
    Text wraps at 20 characters if needed.
    
    This fully replaces the message part buttons.

 postler/postler-attachments.vala |  157 ++++++++++++++++++++++++++++++++++++++
 postler/postler-composer.vala    |  130 ++++++-------------------------
 postler/postler-content.vala     |    2 +-
 postler/postler-viewer.vala      |   85 +++++----------------
 4 files changed, 203 insertions(+), 171 deletions(-)

diff --git a/postler/postler-attachments.vala b/postler/postler-attachments.vala
new file mode 100644
index 0000000..bde2c96
--- /dev/null
+++ b/postler/postler-attachments.vala
@@ -0,0 +1,157 @@
+/*
+ Copyright (C) 2011 Christian Dywan <christian at twotoasts.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ See the file COPYING for the full license text.
+*/
+
+public class Postler.Attachments : Gtk.IconView {
+    public bool editable { get; set; default = false; }
+
+    public Attachments () {
+        model = new Gtk.ListStore (1, typeof (MessagePart));
+
+        selection_mode = Gtk.SelectionMode.BROWSE;
+        columns = 3;
+        set ("item-orientation", Gtk.Orientation.HORIZONTAL);
+        set_no_show_all (true);
+
+        var renderer_pixbuf = new Gtk.CellRendererPixbuf ();
+        pack_start (renderer_pixbuf, true);
+        set_cell_data_func (renderer_pixbuf, render_pixbuf);
+        var renderer_filename = new Gtk.CellRendererText ();
+        pack_start (renderer_filename, false);
+        set_cell_data_func (renderer_filename, render_filename);
+        var renderer_delete = new Postler.CellRendererToggle ();
+        pack_end (renderer_delete, false);
+        set_cell_data_func (renderer_delete, render_delete);
+        renderer_delete.toggled.connect ((renderer, path) => {
+            remove_attachment (path);
+        });
+
+        key_press_event.connect ((event) => {
+            if (!editable)
+                return false;
+            if (event.keyval == Gdk.keyval_from_name ("Delete")) {
+                var path = get_selected_items ().nth_data (0);
+                if (path != null)
+                    remove_attachment (path.to_string ());
+                return true;
+            }
+            return false;
+        });
+    }
+
+    public void add_part (MessagePart part) {
+        (model as Gtk.ListStore).insert_with_values (null, -1, 0, part);
+        show ();
+    }
+
+    public void add_filename (string filename) {
+        uchar[] data = {};
+        bool uncertain;
+        string mime_type = g_content_type_guess (filename, data, out uncertain);
+        var part = new MessagePart (mime_type);
+        part.filename = filename;
+        add_part (part);
+    }
+
+    public void remove_attachment (string tree_path) {
+        Gtk.TreeIter iter = Gtk.TreeIter ();
+        if (!model.get_iter_from_string (out iter, tree_path))
+            return;
+
+        (model as Gtk.ListStore).remove (iter);
+        if (model.iter_n_children (null) == 0)
+            hide ();
+    }
+
+    public MessagePart? get_selected_part () {
+        var paths = get_selected_items ();
+        var path = paths != null ? paths.nth_data (0) : null;
+        if (path != null) {
+            Gtk.TreeIter iter;
+            if (model.get_iter (out iter, path)) {
+                MessagePart part;
+                model.get (iter, 0, out part);
+                return part;
+            }
+        }
+
+        return null;
+    }
+
+    public void select (MessagePart part) {
+        Gtk.TreeIter iter;
+        if (!model.iter_children (out iter, null))
+            return;
+        do {
+            MessagePart existing_part;
+            model.get (iter, 0, out existing_part);
+            if (existing_part == part)
+                select_path (model.get_path (iter));
+        } while (model.iter_next (ref iter));
+    }
+
+    string icon_name_for_mime_type (string mime_type) {
+        var icon_theme = Gtk.IconTheme.get_for_screen (get_screen ());
+        var parts = mime_type.split ("/", 2);
+        string icon_name = parts[0] + "-" + parts[1];
+        if (icon_theme.has_icon (icon_name))
+            return icon_name;
+        icon_name = "gnome-mime-" + parts[0] + "-" + parts[1];
+        if (icon_theme.has_icon (icon_name))
+            return icon_name;
+        icon_name = parts[0] + "-x-generic";
+        if (icon_theme.has_icon (icon_name))
+            return icon_name;
+        icon_name = "gnome-mime-" + parts[0] + "-x-generic";
+        if (icon_theme.has_icon (icon_name))
+            return icon_name;
+        return "application-x-executable";
+    }
+
+    static int attachment_icon_size = 0;
+    void render_pixbuf (Gtk.CellLayout layout, Gtk.CellRenderer cell,
+        Gtk.TreeModel model, Gtk.TreeIter iter) {
+
+        if (attachment_icon_size == 0)
+            attachment_icon_size = Gtk.icon_size_register ("attachment", 32, 32);
+
+        MessagePart part;
+        model.get (iter, 0, out part);
+
+        string icon_name = icon_name_for_mime_type (part.mime_type);
+        cell.set ("icon-name", icon_name,
+                  "stock-size", attachment_icon_size);
+    }
+
+    void render_filename (Gtk.CellLayout layout, Gtk.CellRenderer cell,
+        Gtk.TreeModel model, Gtk.TreeIter iter) {
+
+        MessagePart part;
+        model.get (iter, 0, out part);
+
+        string text = part.filename != null
+            ? Filename.display_basename (part.filename)
+            : g_content_type_get_description (part.mime_type);
+        cell.set ("text", text,
+                  "wrap-mode", Pango.WrapMode.WORD,
+                  "wrap-width", 20);
+    }
+
+    void render_delete (Gtk.CellLayout layout, Gtk.CellRenderer cell,
+        Gtk.TreeModel model, Gtk.TreeIter iter) {
+
+        if (!editable)
+            return;
+
+        cell.set ("stock-id", Gtk.STOCK_REMOVE,
+                  "width", 16, "height", 16);
+    }
+}
+
diff --git a/postler/postler-composer.vala b/postler/postler-composer.vala
index e957fff..650b332 100644
--- a/postler/postler-composer.vala
+++ b/postler/postler-composer.vala
@@ -20,8 +20,7 @@ public class Postler.Composer : Gtk.Window {
     Gtk.VBox shelf;
     Gtk.Toolbar toolbar;
     Postler.Content content;
-    Gtk.IconView att_view;
-    Gtk.ListStore attachments;
+    Postler.Attachments attachments;
     Gtk.ComboBox combo_from;
     Gtk.Entry entry_to;
     Gtk.Entry entry_copy;
@@ -120,8 +119,8 @@ public class Postler.Composer : Gtk.Window {
                 var now = new Soup.Date.from_now (0);
                 string copy = entry_copy.text;
                 string header = "";
-                Gtk.TreeIter att_iter;
-                if (!(attachments.get_iter_first (out att_iter))) {
+                Gtk.TreeIter iter;
+                if (!(attachments.model.get_iter_first (out iter))) {
                     header = ("From: %s\nTo: %s\n%s%s"
                         + "MIME-Version: 1.0\nContent-Transfer-Encoding: 8bit\n"
                         + "Content-Type: text/plain; charset=UTF-8\n"
@@ -144,7 +143,7 @@ public class Postler.Composer : Gtk.Window {
                 string body = clipboard.wait_for_text ();
                 clear_selection ();
 
-                if (attachments.iter_n_children (null) == 0) {
+                if (attachments.model.iter_n_children (null) == 0) {
                     /* i18n: A warning is displayed when writing a new message
                              and there are no attachments but the text mentions
                              attachments. Words are separated by |. */
@@ -170,7 +169,7 @@ public class Postler.Composer : Gtk.Window {
                          }
                 }
 
-                if (attachments.get_iter_first (out att_iter)) {
+                if (attachments.model.get_iter_first (out iter)) {
                     string boundary = GLib.Checksum.compute_for_string (
                         GLib.ChecksumType.MD5,
                         body + now.to_string (Soup.DateFormat.RFC2822));
@@ -193,29 +192,29 @@ public class Postler.Composer : Gtk.Window {
                     body_builder.prepend ("Content-Type: text/plain; charset=UTF-8\n\n");
                     body_builder.prepend ("--" + boundary + "\n");
                     body_builder.append_c ('\n');
-                    string fname;
-                    attachments.get (att_iter, 0, out fname);
+                    MessagePart part;
+                    attachments.model.get (iter, 0, out part);
                     try {
                         body_builder.append ("--" + boundary + "\n");
                         uchar[] data = {};
-                        FileUtils.get_data (fname, out data);
+                        FileUtils.get_data (part.filename, out data);
                         bool uncertain;
-                        string mime_type = g_content_type_guess (fname, data,
+                        string mime_type = g_content_type_guess (part.filename, data,
                                                                  out uncertain);
-                        var fname_parts = fname.split ("/");
+                        var fname_parts = part.filename.split ("/");
                         var filename = fname_parts[fname_parts.length-1];
                         body_builder.append ("Content-Type: " + mime_type + "\n"
                             + "Content-Disposition: attachment; "
                             + "filename=\"" + filename + "\"\n"
                             + "Content-Transfer-Encoding: base64\n\n"
                             + GLib.Base64.encode (data));
-                        while (attachments.iter_next (ref att_iter)) {
-                            attachments.get (att_iter, 0, out fname);
+                        while (attachments.model.iter_next (ref iter)) {
+                            attachments.model.get (iter, 0, out part);
                             body_builder.append ("\n--" + boundary + "\n");
-                            FileUtils.get_data (fname, out data);
-                            mime_type = g_content_type_guess (fname, data,
+                            FileUtils.get_data (part.filename, out data);
+                            mime_type = g_content_type_guess (part.filename, data,
                                                               out uncertain);
-                            fname_parts = fname.split ("/");
+                            fname_parts = part.filename.split ("/");
                             filename = fname_parts[fname_parts.length - 1];
                             body_builder.append ("Content-Type: " + mime_type + "\n"
                                 + "Content-Disposition: attachment; "
@@ -226,7 +225,7 @@ public class Postler.Composer : Gtk.Window {
                         body_builder.append ("\n--" + boundary + "--");
                         body = body_builder.str;
                     } catch (Error e) {
-                        GLib.message (_("Failed to add attachment: %s"), fname);
+                        GLib.message (_("Failed to add attachment: %s"), part.filename);
                     }
                 }
 
@@ -303,60 +302,7 @@ public class Postler.Composer : Gtk.Window {
         filechooser.destroy ();
         if (filename == null)
             return;
-        insert_attachment (filename);
-    }
-
-    void insert_attachment (string path_to_file) {
-        Gtk.TreeIter att_iter;
-        attachments.append (out att_iter);
-        attachments.set (att_iter,0,path_to_file);
-        att_view.show ();
-        attachments.get_iter_first (out att_iter);
-    }
-
-    void remove_attachment (string path) {
-        Gtk.TreeIter att_iter = Gtk.TreeIter ();
-        if (!attachments.get_iter_from_string (out att_iter, path))
-            return;
-
-        attachments.remove (att_iter);
-        if (attachments.length == 0)
-            att_view.visible = false;
-    }
-
-    static int attachment_icon_size = 0;
-    void render_pixbuf (Gtk.CellLayout layout, Gtk.CellRenderer cell,
-        Gtk.TreeModel model, Gtk.TreeIter iter) {
-
-        if (attachment_icon_size == 0)
-            attachment_icon_size = Gtk.icon_size_register ("attachment", 32, 32);
-
-       string filename;
-       model.get (iter, 0, out filename);
-
-        uchar[] data = {};
-        bool uncertain;
-        string mime_type = g_content_type_guess (filename, data, out uncertain);
-        string icon_name = Postler.Viewer.icon_name_for_mime_type (mime_type, this);
-        cell.set ("icon-name", icon_name,
-                  "stock-size", attachment_icon_size);
-    }
-
-    void render_filename (Gtk.CellLayout layout, Gtk.CellRenderer cell,
-        Gtk.TreeModel model, Gtk.TreeIter iter) {
-
-        string filename;
-        model.get (iter, 0, out filename);
-        cell.set ("text", Filename.display_basename (filename),
-                  "width-chars", 16,
-                  "ellipsize", Pango.EllipsizeMode.MIDDLE);
-    }
-
-    void render_delete (Gtk.CellLayout layout, Gtk.CellRenderer cell,
-        Gtk.TreeModel model, Gtk.TreeIter iter) {
-
-        cell.set ("stock-id", Gtk.STOCK_REMOVE,
-                  "width", 16, "height", 16);
+        attachments.add_filename (filename);
     }
 
     void drag_received (Gdk.DragContext context, int x, int y,
@@ -367,7 +313,7 @@ public class Postler.Composer : Gtk.Window {
             File file = File.new_for_uri (uri);
             if (file.query_file_type (FileQueryInfoFlags.NOFOLLOW_SYMLINKS)
                 == FileType.REGULAR && file.is_native ()) {
-                insert_attachment (file.get_path ());
+                attachments.add_filename (file.get_path ());
                 success = true;
             }
         }
@@ -600,43 +546,17 @@ public class Postler.Composer : Gtk.Window {
         scrolled.shadow_type = Gtk.ShadowType.IN;
         shelf.pack_start (scrolled, true, true, 0);
 
-        att_view = new Gtk.IconView ();
-        att_view.columns = 3;
-        att_view.set ("item-orientation", Gtk.Orientation.HORIZONTAL);
-        var renderer_pixbuf = new Gtk.CellRendererPixbuf ();
-        att_view.pack_start (renderer_pixbuf, true);
-        att_view.set_cell_data_func (renderer_pixbuf, render_pixbuf);
-        var renderer_filename = new Gtk.CellRendererText ();
-        att_view.pack_start (renderer_filename, false);
-        att_view.set_cell_data_func (renderer_filename, render_filename);
-        var renderer_delete = new Postler.CellRendererToggle ();
-        att_view.pack_end (renderer_delete, false);
-        att_view.set_cell_data_func (renderer_delete, render_delete);
-        renderer_delete.toggled.connect ((renderer, path) => {
-            remove_attachment (path);
-        });
-        attachments = new Gtk.ListStore (1, typeof (string));
-        att_view.set_model (attachments);
-        att_view.set_no_show_all (true);
-        att_view.key_press_event.connect ((event) => {
-            if (event.keyval == Gdk.keyval_from_name ("Delete")) {
-                var paths = att_view.get_selected_items ();
-                var path = paths.nth_data (0);
-                if (path != null)
-                    remove_attachment (path.to_string ());
-                return true;
-            }
-            return false;
-        });
-        shelf.pack_start (att_view, false, false, 0);
+        attachments = new Postler.Attachments ();
+        attachments.editable = true;
+        shelf.pack_start (attachments, false, false, 0);
         shelf.show_all ();
 
         Gtk.drag_dest_set (content, Gtk.DestDefaults.ALL, {}, Gdk.DragAction.COPY);
         Gtk.drag_dest_add_uri_targets (content);
         content.drag_data_received.connect (drag_received);
-        Gtk.drag_dest_set (att_view, Gtk.DestDefaults.ALL, {}, Gdk.DragAction.COPY);
-        Gtk.drag_dest_add_uri_targets (att_view);
-        att_view.drag_data_received.connect (drag_received);
+        Gtk.drag_dest_set (attachments, Gtk.DestDefaults.ALL, {}, Gdk.DragAction.COPY);
+        Gtk.drag_dest_add_uri_targets (attachments);
+        attachments.drag_data_received.connect (drag_received);
 
         actions.get_action ("MessageSend").sensitive = false;
         entry_to.notify["text"].connect ((object, pspec) => {
@@ -735,7 +655,7 @@ public class Postler.Composer : Gtk.Window {
                 ? data.substring (7, -1) : data;
             if (Path.is_absolute (filename)
              && FileUtils.test (filename, FileTest.EXISTS)) {
-                insert_attachment (filename);
+                attachments.add_filename (filename);
                 return true;
             }
             GLib.warning (_("File %s doesn't exist"), data);
diff --git a/postler/postler-content.vala b/postler/postler-content.vala
index 860620e..75e4bab 100644
--- a/postler/postler-content.vala
+++ b/postler/postler-content.vala
@@ -9,7 +9,7 @@
  See the file COPYING for the full license text.
 */
 
-public class Postler.MessagePart {
+public class Postler.MessagePart : Object {
     public StringBuilder? body;
     public string? mime_type;
     public string? charset;
diff --git a/postler/postler-viewer.vala b/postler/postler-viewer.vala
index 37bf7a5..6dab01f 100644
--- a/postler/postler-viewer.vala
+++ b/postler/postler-viewer.vala
@@ -1,5 +1,5 @@
 /*
- Copyright (C) 2010 Christian Dywan <christian at twotoasts.de>
+ Copyright (C) 2011 Christian Dywan <christian at twotoasts.de>
 
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
@@ -12,8 +12,7 @@
 public class Postler.Viewer : Gtk.VBox {
     Gtk.InfoBar infobar;
     Postler.Content content;
-    Gtk.VBox message_parts;
-    Gtk.ToggleButton? last_button;
+    Postler.Attachments attachments;
     Gtk.Toolbar findbar;
     Gtk.Entry find_entry;
 
@@ -37,17 +36,16 @@ public class Postler.Viewer : Gtk.VBox {
         infobar.set_no_show_all (true);
         content.resource_request_starting.connect (resource_request);
 
-        var content_box = new Gtk.HBox (false, 0);
-        pack_start (content_box, true, true, 0);
         var scrolled = new Postler.ScrolledWindow (content);
-        content_box.pack_start (scrolled, true, true, 0);
-        message_parts = new Gtk.VBox (false, 0);
-        scrolled = new Postler.ScrolledWindow (message_parts);
-        content_box.pack_start (scrolled, false, false, 0);
-        message_parts.parent.parent.set_no_show_all (true);
+        pack_start (scrolled, true, true, 0);
+
+        attachments = new Postler.Attachments ();
+        pack_start (attachments, false, false, 0);
+
         notify_message_parts (content, null);
         content.notify["message-parts"].connect (notify_message_parts);
         content.notify["current-part"].connect (notify_current_part);
+        attachments.selection_changed.connect (part_selected);
 
         findbar = new Gtk.Toolbar ();
         pack_start (findbar, false, false, 0);
@@ -78,77 +76,34 @@ public class Postler.Viewer : Gtk.VBox {
         content.display_part (content.current_part);
     }
 
-    internal static string icon_name_for_mime_type (string mime_type, Gtk.Widget widget) {
-        var icon_theme = Gtk.IconTheme.get_for_screen (widget.get_screen ());
-        var parts = mime_type.split ("/", 2);
-        string icon_name = parts[0] + "-" + parts[1];
-        if (icon_theme.has_icon (icon_name))
-            return icon_name;
-        icon_name = "gnome-mime-" + parts[0] + "-" + parts[1];
-        if (icon_theme.has_icon (icon_name))
-            return icon_name;
-        icon_name = parts[0] + "-x-generic";
-        if (icon_theme.has_icon (icon_name))
-            return icon_name;
-        icon_name = "gnome-mime-" + parts[0] + "-x-generic";
-        if (icon_theme.has_icon (icon_name))
-            return icon_name;
-        return "application-x-executable";
-    }
-
     void notify_message_parts (GLib.Object object, GLib.ParamSpec? pspec) {
         infobar.hide ();
         allow_external_images = false;
 
-        var scrollable = message_parts.parent.parent;
         if (content.message_parts.length () < 2) {
-            scrollable.hide ();
+            attachments.hide ();
             return;
         }
 
-        var children = message_parts.get_children ();
-        foreach (var child in children)
-            child.destroy ();
+        (attachments.model as Gtk.ListStore).clear ();
         foreach (var part in content.message_parts) {
             if (part.mime_type.has_prefix ("multipart/"))
                 continue;
 
-            string icon_name = icon_name_for_mime_type (part.mime_type, this);
-            var icon = new Gtk.Image.from_icon_name (icon_name,
-                                                     Gtk.IconSize.BUTTON);
-            var button = new Gtk.ToggleButton ();
-            button.relief = Gtk.ReliefStyle.NONE;
-            button.add (icon);
-            button.set_tooltip_text (part.filename ??
-                g_content_type_get_description (part.mime_type));
-            button.set_data ("part", part);
-            if (part == content.current_part) {
-                last_button = button;
-                button.active = true;
-            }
-            button.clicked.connect ((button) => {
-                if (last_button != null)
-                    last_button.active = false;
-                last_button = button as Gtk.ToggleButton;
-                MessagePart current_part = button.get_data ("part");
-                content.display_part (current_part);
-            });
-            message_parts.pack_start (button, false, false, 0);
+            attachments.add_part (part);
+            if (part == content.current_part)
+                attachments.select (content.current_part);
         }
-        message_parts.parent.parent.set_no_show_all (false);
-        scrollable.show_all ();
     }
 
     void notify_current_part () {
-        foreach (var child in message_parts.get_children ()) {
-            MessagePart part = child.get_data ("part");
-            if (part == content.current_part) {
-                var button = child as Gtk.ToggleButton;
-                if (!button.active)
-                    button.active = true;
-                break;
-            }
-        }
+        attachments.select (content.current_part);
+    }
+
+    void part_selected () {
+        MessagePart? part = attachments.get_selected_part ();
+        if (part != null)
+            content.display_part (part);
     }
 
     bool key_pressed (Gdk.EventKey event) {



More information about the Xfce4-commits mailing list