Merge with default Redesign_Ribbon
authorThomas
Wed, 12 Jul 2017 15:09:25 +0200
branchRedesign_Ribbon
changeset 174003c04f05dc77
parent 1735 c8046a89552f
parent 1739 32612407d28c
child 1741 aad40b3e7ede
Merge with default
Extensions/MailItemExtensions.cs
MsgProcessor.cs
UI/FormRegionPrivacyStatus.cs
     1.1 --- a/Extensions/MailItemExtensions.cs	Tue Jul 11 15:41:05 2017 +0200
     1.2 +++ b/Extensions/MailItemExtensions.cs	Wed Jul 12 15:09:25 2017 +0200
     1.3 @@ -556,7 +556,84 @@
     1.4          /// <returns>True if the mail item is secure, otherwise false.</returns>
     1.5          public static bool GetIsSecure(this Outlook.MailItem omi)
     1.6          {
     1.7 -            return (PEPMessage.GetIsSecure(omi));
     1.8 +            if (omi != null)
     1.9 +            {
    1.10 +                // PGP/MIME format
    1.11 +                if (omi.GetIsPGPMIMEEncrypted())
    1.12 +                {
    1.13 +                    return (true);
    1.14 +                }
    1.15 +
    1.16 +                // Partitioned or inline PGP format
    1.17 +                if (omi.Body != null && PEPMessage.IsPGPText(omi.Body))
    1.18 +                {
    1.19 +                    return (true);
    1.20 +                }
    1.21 +            }
    1.22 +
    1.23 +            return false;
    1.24 +        }
    1.25 +
    1.26 +        /// <summary>
    1.27 +        /// Determines if the given mail item is encrypted in PGP/MIME format.
    1.28 +        /// </summary>
    1.29 +        /// <param name="omi">The mail item to check encryption for.</param>
    1.30 +        /// <returns>True if the given mail item is PGP/MIME encrypted, otherwise false.</returns>
    1.31 +        public static bool GetIsPGPMIMEEncrypted(this Outlook.MailItem omi)
    1.32 +        {
    1.33 +            bool result = false;
    1.34 +            bool versionInfoFound = false;
    1.35 +            bool contentFound = false;
    1.36 +            PEPAttachment attach;
    1.37 +            Outlook.Attachment attachment = null;
    1.38 +            Outlook.Attachments attachments = null;
    1.39 +
    1.40 +            try
    1.41 +            {
    1.42 +                if (omi != null)
    1.43 +                {
    1.44 +                    attachments = omi.Attachments;
    1.45 +
    1.46 +                    // Require only two attachments (version identification & encrypted content)
    1.47 +                    // However, allow the attachments to be in any order
    1.48 +                    if (attachments.Count == 2)
    1.49 +                    {
    1.50 +                        // Note: attachment index starts at 1
    1.51 +                        for (int i = 1; i <= attachments.Count; i++)
    1.52 +                        {
    1.53 +                            attachment = attachments[i];
    1.54 +                            attach = new PEPAttachment(attachment);
    1.55 +
    1.56 +                            if (attach.IsPGPMIMEVersionInfoFormat)
    1.57 +                            {
    1.58 +                                versionInfoFound = true;
    1.59 +                            }
    1.60 +                            else if (attach.IsPGPMIMEContentFormat)
    1.61 +                            {
    1.62 +                                contentFound = true;
    1.63 +                            }
    1.64 +
    1.65 +                            attachment = null;
    1.66 +                        }
    1.67 +
    1.68 +                        if (versionInfoFound && contentFound)
    1.69 +                        {
    1.70 +                            result = true;
    1.71 +                        }
    1.72 +                    }
    1.73 +                }
    1.74 +            }
    1.75 +            catch (Exception ex)
    1.76 +            {
    1.77 +                Log.Error("GetIsPGPMIMEEncrypted: Error. " + ex.ToString());
    1.78 +            }
    1.79 +            finally
    1.80 +            {
    1.81 +                attachment = null;
    1.82 +                attachments = null;
    1.83 +            }
    1.84 +
    1.85 +            return (result);
    1.86          }
    1.87  
    1.88          /// <summary>
     2.1 --- a/MsgProcessor.cs	Tue Jul 11 15:41:05 2017 +0200
     2.2 +++ b/MsgProcessor.cs	Wed Jul 12 15:09:25 2017 +0200
     2.3 @@ -750,9 +750,8 @@
     2.4              //  This updates workingMessage
     2.5              ///////////////////////////////////////////////////////////
     2.6  
     2.7 -            Log.Verbose("ProcessSentMessage: Processing CIDs and flattening recipients.");
     2.8 +            Log.Verbose("ProcessSentMessage: Flattening recipients.");
     2.9  
    2.10 -            workingMessage.NormalizeContentIDs();
    2.11              workingMessage.FlattenAllRecipientIdentities();
    2.12  
    2.13              ///////////////////////////////////////////////////////////
     3.1 --- a/PEPAttachment.cs	Tue Jul 11 15:41:05 2017 +0200
     3.2 +++ b/PEPAttachment.cs	Wed Jul 12 15:09:25 2017 +0200
     3.3 @@ -7,6 +7,7 @@
     3.4  using System.Drawing;
     3.5  using System.IO;
     3.6  using System.Runtime.InteropServices;
     3.7 +using System.Text;
     3.8  using Outlook = Microsoft.Office.Interop.Outlook;
     3.9  
    3.10  namespace pEp
    3.11 @@ -32,6 +33,9 @@
    3.12          /// </summary>
    3.13          public event PropertyChangedEventHandler PropertyChanged;
    3.14  
    3.15 +        public const string                 CID_PREFIX      = @"cid://";
    3.16 +        public const string                 FILE_PREFIX     = @"file://";
    3.17 +
    3.18          private string _ContentId;
    3.19          private byte[] _Data;
    3.20          private string _FileName;
    3.21 @@ -80,9 +84,27 @@
    3.22          /// <param name="b">The blob to build from.</param>
    3.23          public PEPAttachment(Blob b)
    3.24          {
    3.25 -            this._ContentId = null;
    3.26 +            this._ContentId = string.Empty;
    3.27 +            this._FileName = string.Empty;
    3.28 +
    3.29 +            /* If the blob's file name has the content id prefix "cid://", this attachment
    3.30 +             * is embedded and the Filename property has to be assigned to the attachment's
    3.31 +             * ContentId property (we need to trim the prefix as it is referenced from the
    3.32 +             * mail's HTML without it.)
    3.33 +             */ 
    3.34 +            if (b.Filename?.StartsWith(PEPAttachment.CID_PREFIX) ?? false)
    3.35 +            {
    3.36 +                this._ContentId = b.Filename.Substring(PEPAttachment.CID_PREFIX.Length);
    3.37 +            }
    3.38 +            else if (b.Filename?.StartsWith(PEPAttachment.FILE_PREFIX) ?? false)
    3.39 +            {
    3.40 +                this._FileName = b.Filename.Substring(PEPAttachment.FILE_PREFIX.Length);
    3.41 +            }
    3.42 +            else
    3.43 +            {
    3.44 +                this._FileName = b.Filename;
    3.45 +            }
    3.46              this._Data = b.Value;
    3.47 -            this._FileName = b.Filename;
    3.48              this._MimeType = b.MimeType;
    3.49              this._Tag = null;
    3.50          }
    3.51 @@ -344,6 +366,62 @@
    3.52          }
    3.53  
    3.54          /// <summary>
    3.55 +        /// Gets whether this attachment is formatted like PGP/MIME content.
    3.56 +        /// </summary>
    3.57 +        /// <returns>True if the attachment is PGP/MIME content formatted, otherwise false.</returns>
    3.58 +        public bool IsPGPMIMEContentFormat
    3.59 +        {
    3.60 +            get
    3.61 +            {
    3.62 +                bool result = false;
    3.63 +
    3.64 +                if ((this != null) &&
    3.65 +                    (string.IsNullOrEmpty(this.MimeType) == false) &&
    3.66 +                    (string.Equals(this.MimeType.Trim(), "application/octet-stream", StringComparison.OrdinalIgnoreCase)))
    3.67 +                {
    3.68 +                    // Require data to start with PGP text and be at least 100 bytes
    3.69 +                    if ((this.Data != null) &&
    3.70 +                        (this.Data.Length > 100))
    3.71 +                    {
    3.72 +                        // Check for PGP text after converting to string
    3.73 +                        if (PEPMessage.IsPGPText(Encoding.ASCII.GetString(this.Data, 0, 100)))
    3.74 +                        {
    3.75 +                            result = true;
    3.76 +                        }
    3.77 +                    }
    3.78 +                }
    3.79 +
    3.80 +                return (result);
    3.81 +            }
    3.82 +        }
    3.83 +
    3.84 +        /// <summary>
    3.85 +        /// Gets whether this attachment is formatted like a PGP/MIME version identification.
    3.86 +        /// </summary>
    3.87 +        /// <returns>True if the attachment is PGP/MIME version identifcation formatted, otherwise false.</returns>
    3.88 +        public bool IsPGPMIMEVersionInfoFormat
    3.89 +        {
    3.90 +            get
    3.91 +            {
    3.92 +                bool result = false;
    3.93 +
    3.94 +                if ((this != null) &&
    3.95 +                    (string.IsNullOrEmpty(this.MimeType) == false) &&
    3.96 +                    (string.Equals(this.MimeType.Trim(), "application/pgp-encrypted", StringComparison.OrdinalIgnoreCase)))
    3.97 +                {
    3.98 +                    // Allow any data
    3.99 +                    if ((this.Data != null) &&
   3.100 +                        (this.Data.Length > 0))
   3.101 +                    {
   3.102 +                        result = true;
   3.103 +                    }
   3.104 +                }
   3.105 +
   3.106 +                return (result);
   3.107 +            }
   3.108 +        }
   3.109 +
   3.110 +        /// <summary>
   3.111          /// Gets whether this attachment is a key.
   3.112          /// The logic follows the one applied in the pEpEngine's message_api.c:is_key() function.
   3.113          /// </summary>
   3.114 @@ -372,17 +450,17 @@
   3.115              }
   3.116          }
   3.117  
   3.118 -    /**************************************************************
   3.119 -     * 
   3.120 -     * Methods
   3.121 -     * 
   3.122 -     *************************************************************/
   3.123 +        /**************************************************************
   3.124 +         * 
   3.125 +         * Methods
   3.126 +         * 
   3.127 +         *************************************************************/
   3.128  
   3.129 -    /// <summary>
   3.130 -    /// Raises the property changed event, if possible, with the given arguments.
   3.131 -    /// </summary>
   3.132 -    /// <param name="propertyName">The name of the property that changed.</param>
   3.133 -    private void RaisePropertyChangedEvent(string propertyName)
   3.134 +        /// <summary>
   3.135 +        /// Raises the property changed event, if possible, with the given arguments.
   3.136 +        /// </summary>
   3.137 +        /// <param name="propertyName">The name of the property that changed.</param>
   3.138 +        private void RaisePropertyChangedEvent(string propertyName)
   3.139          {
   3.140              this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
   3.141              return;
   3.142 @@ -397,7 +475,38 @@
   3.143          {
   3.144              Blob b = new Blob();
   3.145              b.Value = (this._Data != null ? this._Data : new byte[0]);
   3.146 -            b.Filename = this._FileName;
   3.147 +
   3.148 +            /* If attachment has content id (= is embedded), assign this content id
   3.149 +             * to the blob's Filename property and add the content id prefix so that 
   3.150 +             * it will be preserved during the engine processing.
   3.151 +             * Else, use file name or empty string if not available.
   3.152 +             */ 
   3.153 +            if (string.IsNullOrEmpty(this._ContentId) == false)
   3.154 +            {
   3.155 +                if (this._ContentId.StartsWith(PEPAttachment.CID_PREFIX))
   3.156 +                {
   3.157 +                    b.Filename = this._ContentId;
   3.158 +                }
   3.159 +                else
   3.160 +                {
   3.161 +                    b.Filename = PEPAttachment.CID_PREFIX + this._ContentId;
   3.162 +                }
   3.163 +            }
   3.164 +            else if (string.IsNullOrEmpty(this._FileName) == false)
   3.165 +            {
   3.166 +                if (this._FileName.StartsWith(PEPAttachment.FILE_PREFIX))
   3.167 +                {
   3.168 +                    b.Filename = this._FileName;
   3.169 +                }
   3.170 +                else
   3.171 +                {
   3.172 +                    b.Filename = PEPAttachment.FILE_PREFIX + this._FileName;
   3.173 +                }
   3.174 +            }
   3.175 +            else
   3.176 +            {
   3.177 +                b.Filename = string.Empty;
   3.178 +            }
   3.179              b.MimeType = this._MimeType;
   3.180  
   3.181              return (b);
     4.1 --- a/PEPMessage.cs	Tue Jul 11 15:41:05 2017 +0200
     4.2 +++ b/PEPMessage.cs	Wed Jul 12 15:09:25 2017 +0200
     4.3 @@ -1581,7 +1581,7 @@
     4.4  
     4.5                  foreach (PEPAttachment attach in this._Attachments)
     4.6                  {
     4.7 -                    if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
     4.8 +                    if (attach.IsPGPMIMEVersionInfoFormat)
     4.9                      {
    4.10                          data.AddRange(attach.Data);
    4.11                          versionInfoFound = true;
    4.12 @@ -1600,7 +1600,7 @@
    4.13  
    4.14                  foreach (PEPAttachment attach in this._Attachments)
    4.15                  {
    4.16 -                    if (PEPMessage.IsPGPMIMEContentFormat(attach))
    4.17 +                    if (attach.IsPGPMIMEContentFormat)
    4.18                      {
    4.19                          data.AddRange(attach.Data);
    4.20                          contentFound = true;
    4.21 @@ -2327,100 +2327,6 @@
    4.22  
    4.23          /// <summary>
    4.24          /// Determines if the given message is considered secure.
    4.25 -        /// </summary>
    4.26 -        /// <param name="omi">The outlook mail item to check security for.</param>
    4.27 -        /// <returns>True if the given message is encrypted, otherwise false.</returns>
    4.28 -        public static bool GetIsSecure(Outlook.MailItem omi)
    4.29 -        {
    4.30 -            if (omi != null)
    4.31 -            {
    4.32 -                // PGP/MIME format
    4.33 -                if (PEPMessage.GetIsPGPMIMEEncrypted(omi))
    4.34 -                {
    4.35 -                    return (true);
    4.36 -                }
    4.37 -
    4.38 -                // Partitioned or inline PGP format
    4.39 -                if (omi.Body != null && PEPMessage.IsPGPText(omi.Body))
    4.40 -                {
    4.41 -                    return (true);
    4.42 -                }                
    4.43 -            }
    4.44 -
    4.45 -            return (false);
    4.46 -        }
    4.47 -
    4.48 -        /// <summary>
    4.49 -        /// Determines if the given message is encrypted in PGP/MIME format.
    4.50 -        /// </summary>
    4.51 -        /// <param name="msg">The message to check encryption for.</param>
    4.52 -        /// <returns>True if the given message is PGP/MIME encrypted, otherwise false.</returns>
    4.53 -        public static bool GetIsPGPMIMEEncrypted(Outlook.MailItem omi)
    4.54 -        {
    4.55 -            bool result = false;
    4.56 -            bool versionInfoFound = false;
    4.57 -            bool contentFound = false;
    4.58 -            PEPAttachment attach;
    4.59 -            Outlook.Attachment attachment = null;
    4.60 -            Outlook.Attachments attachments = null;
    4.61 -
    4.62 -            try
    4.63 -            {
    4.64 -                if (omi != null)
    4.65 -                {
    4.66 -                    attachments = omi.Attachments;
    4.67 -
    4.68 -                    // Require only two attachments (version identification & encrypted content)
    4.69 -                    // However, allow the attachments to be in any order
    4.70 -                    if (attachments.Count == 2)
    4.71 -                    {
    4.72 -                        // Note: attachment index starts at 1
    4.73 -                        for (int i = 1; i <= attachments.Count; i++)
    4.74 -                        {
    4.75 -                            attachment = attachments[i];
    4.76 -                            attach = new PEPAttachment(attachment);
    4.77 -
    4.78 -                            if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
    4.79 -                            {
    4.80 -                                versionInfoFound = true;
    4.81 -                            }
    4.82 -                            else if (PEPMessage.IsPGPMIMEContentFormat(attach))
    4.83 -                            {
    4.84 -                                contentFound = true;
    4.85 -                            }
    4.86 -
    4.87 -                            // Marshal.ReleaseComObject(attachment);
    4.88 -                            attachment = null;
    4.89 -                        }
    4.90 -
    4.91 -                        if (versionInfoFound && contentFound)
    4.92 -                        {
    4.93 -                            result = true;
    4.94 -                        }
    4.95 -                    }
    4.96 -                }
    4.97 -            }
    4.98 -            catch { }
    4.99 -            finally
   4.100 -            {
   4.101 -                if (attachment != null)
   4.102 -                {
   4.103 -                    // Marshal.ReleaseComObject(attachment);
   4.104 -                    attachment = null;
   4.105 -                }
   4.106 -
   4.107 -                if (attachments != null)
   4.108 -                {
   4.109 -                    // Marshal.ReleaseComObject(attachments);
   4.110 -                    attachments = null;
   4.111 -                }
   4.112 -            }
   4.113 -
   4.114 -            return (result);
   4.115 -        }
   4.116 -
   4.117 -        /// <summary>
   4.118 -        /// Determines if the given message is considered secure.
   4.119          /// Currently, only PGP encrypted messages will be detected.
   4.120          /// </summary>
   4.121          /// <param name="msg">The message to check security for.</param>
   4.122 @@ -2464,11 +2370,11 @@
   4.123                  {
   4.124                      foreach (PEPAttachment attach in msg.Attachments)
   4.125                      {
   4.126 -                        if (PEPMessage.IsPGPMIMEVersionInfoFormat(attach))
   4.127 +                        if (attach.IsPGPMIMEVersionInfoFormat)
   4.128                          {
   4.129                              versionInfoFound = true;
   4.130                          }
   4.131 -                        else if (PEPMessage.IsPGPMIMEContentFormat(attach))
   4.132 +                        else if (attach.IsPGPMIMEContentFormat)
   4.133                          {
   4.134                              contentFound = true;
   4.135                          }
   4.136 @@ -2483,58 +2389,6 @@
   4.137  
   4.138              return (result);
   4.139          }
   4.140 -
   4.141 -        /// <summary>
   4.142 -        /// Determines if the given attachment is formatted like a PGP/MIME version identification.
   4.143 -        /// </summary>
   4.144 -        /// <param name="attachment">The attachment to check.</param>
   4.145 -        /// <returns>True if the attachment is PGP/MIME version identifcation formatted, otherwise false.</returns>
   4.146 -        private static bool IsPGPMIMEVersionInfoFormat(PEPAttachment attachment)
   4.147 -        {
   4.148 -            bool result = false;
   4.149 -
   4.150 -            if ((attachment != null) &&
   4.151 -                (string.IsNullOrEmpty(attachment.MimeType) == false) &&
   4.152 -                (string.Equals(attachment.MimeType.Trim(), "application/pgp-encrypted", StringComparison.OrdinalIgnoreCase)))
   4.153 -            {
   4.154 -                // Allow any data
   4.155 -                if ((attachment.Data != null) &&
   4.156 -                    (attachment.Data.Length > 0))
   4.157 -                {
   4.158 -                    result = true;
   4.159 -                }
   4.160 -            }
   4.161 -
   4.162 -            return (result);
   4.163 -        }
   4.164 -
   4.165 -        /// <summary>
   4.166 -        /// Determines if the given attachment is formatted like PGP/MIME content.
   4.167 -        /// </summary>
   4.168 -        /// <param name="attachment">The attachment to check.</param>
   4.169 -        /// <returns>True if the attachment is PGP/MIME content formatted, otherwise false.</returns>
   4.170 -        private static bool IsPGPMIMEContentFormat(PEPAttachment attachment)
   4.171 -        {
   4.172 -            bool result = false;
   4.173 -
   4.174 -            if ((attachment != null) &&
   4.175 -                (string.IsNullOrEmpty(attachment.MimeType) == false) &&
   4.176 -                (string.Equals(attachment.MimeType.Trim(), "application/octet-stream", StringComparison.OrdinalIgnoreCase)))
   4.177 -            {
   4.178 -                // Require data to start with PGP text and be at least 100 bytes
   4.179 -                if ((attachment.Data != null) &&
   4.180 -                    (attachment.Data.Length > 100))
   4.181 -                {
   4.182 -                    // Check for PGP text after converting to string
   4.183 -                    if (PEPMessage.IsPGPText(Encoding.ASCII.GetString(attachment.Data, 0, 100)))
   4.184 -                    {
   4.185 -                        result = true;
   4.186 -                    }
   4.187 -                }
   4.188 -            }
   4.189 -
   4.190 -            return (result);
   4.191 -        }
   4.192      }
   4.193  
   4.194      /// <summary>
     5.1 --- a/UI/FormControlPreviewMessage.xaml	Tue Jul 11 15:41:05 2017 +0200
     5.2 +++ b/UI/FormControlPreviewMessage.xaml	Wed Jul 12 15:09:25 2017 +0200
     5.3 @@ -15,6 +15,7 @@
     5.4              <!-- Converters -->
     5.5              <local:InvertBoolConverter x:Key="InvertBool" />
     5.6              <local:IconToImageSourceConverter x:Key="IconToImageSource" />
     5.7 +            <local:HasNonInlineAttachmentsConverter x:Key="HasNonInlineAttachments" />
     5.8              <local:MultiBooleanToVisibilityConverter x:Key="MultiBooleanToVisibility" />
     5.9              <local:ValueConverterGroup x:Key="InvertBoolToVisibility">
    5.10                  <local:InvertBoolConverter />
    5.11 @@ -24,6 +25,10 @@
    5.12                  <local:IsListEmptyConverter />
    5.13                  <local:InvertBoolConverter />
    5.14              </local:ValueConverterGroup>
    5.15 +            <local:ValueConverterGroup x:Key="IsStringEmptyToVisibility">
    5.16 +                <local:IsStringEmptyConverter />
    5.17 +                <BooleanToVisibilityConverter />
    5.18 +            </local:ValueConverterGroup>
    5.19  
    5.20              <!-- Dictionary -->
    5.21              <ResourceDictionary.MergedDictionaries>
    5.22 @@ -232,6 +237,9 @@
    5.23                          <Binding Path="Message.Attachments"
    5.24                                   Mode="OneWay"
    5.25                                   Converter="{StaticResource IsListEmptyToInvertBool}" />
    5.26 +                        <Binding Path="Message.Attachments"
    5.27 +                                 Mode="OneWay"
    5.28 +                                 Converter="{StaticResource HasNonInlineAttachments}" />
    5.29                          <Binding Path="IsNoteModeEnabled"
    5.30                                   Mode="OneWay"
    5.31                                   Converter="{StaticResource InvertBool}" />
    5.32 @@ -239,94 +247,98 @@
    5.33                  </ItemsControl.Visibility>
    5.34                  <ItemsControl.ItemTemplate>
    5.35                      <DataTemplate>
    5.36 -                        <Border BorderBrush="DarkGray"
    5.37 -                                BorderThickness="1"
    5.38 -                                Height="35"
    5.39 -                                Width="180"
    5.40 -                                Margin="2">
    5.41 -                            <Grid>
    5.42 -                                <Grid.ColumnDefinitions>
    5.43 -                                    <ColumnDefinition Width="*" />
    5.44 -                                    <ColumnDefinition Width="20" />
    5.45 -                                </Grid.ColumnDefinitions>
    5.46 -                                <Grid.RowDefinitions>
    5.47 -                                    <RowDefinition Height="*" />
    5.48 -                                </Grid.RowDefinitions>
    5.49 -                                <ContentControl MouseDoubleClick="ButtonAttachment_DoubleClick">
    5.50 -                                    <Grid Grid.Column="0" Grid.Row="0">
    5.51 -                                        <Grid.ColumnDefinitions>
    5.52 -                                            <ColumnDefinition Width="30" />
    5.53 -                                            <ColumnDefinition Width="*" />
    5.54 -                                        </Grid.ColumnDefinitions>
    5.55 -                                        <Grid.RowDefinitions>
    5.56 -                                            <RowDefinition Height="*" />
    5.57 -                                            <RowDefinition Height="*" />
    5.58 -                                        </Grid.RowDefinitions>
    5.59 +                        <StackPanel Visibility="{Binding Path=ContentId, Converter={StaticResource IsStringEmptyToVisibility}}">
    5.60 +                            <Border BorderBrush="DarkGray"
    5.61 +                                    BorderThickness="1"
    5.62 +                                    Height="35"
    5.63 +                                    Width="180"
    5.64 +                                    Margin="2">
    5.65 +                                <Grid>
    5.66 +                                    <Grid.ColumnDefinitions>
    5.67 +                                        <ColumnDefinition Width="*" />
    5.68 +                                        <ColumnDefinition Width="20" />
    5.69 +                                    </Grid.ColumnDefinitions>
    5.70 +                                    <Grid.RowDefinitions>
    5.71 +                                        <RowDefinition Height="*" />
    5.72 +                                    </Grid.RowDefinitions>
    5.73 +                                    <ContentControl MouseDoubleClick="ButtonAttachment_DoubleClick">
    5.74 +                                        <Grid Grid.Column="0"
    5.75 +                                              Grid.Row="0">
    5.76 +                                            <Grid.ColumnDefinitions>
    5.77 +                                                <ColumnDefinition Width="30" />
    5.78 +                                                <ColumnDefinition Width="*" />
    5.79 +                                            </Grid.ColumnDefinitions>
    5.80 +                                            <Grid.RowDefinitions>
    5.81 +                                                <RowDefinition Height="*" />
    5.82 +                                                <RowDefinition Height="*" />
    5.83 +                                            </Grid.RowDefinitions>
    5.84  
    5.85 -                                        <!-- File icon -->
    5.86 -                                        <Image Grid.Column="0"
    5.87 -                                       Grid.Row="0"
    5.88 -                                       Grid.RowSpan="2"
    5.89 -                                       Stretch="Uniform"
    5.90 -                                       Source="{Binding Path='FileIcon', Mode=OneWay, Converter={StaticResource IconToImageSource}}"
    5.91 -                                       HorizontalAlignment="Stretch"
    5.92 -                                       VerticalAlignment="Stretch"
    5.93 -                                       Margin="5" />
    5.94 +                                            <!-- File icon -->
    5.95 +                                            <Image Grid.Column="0"
    5.96 +                                                   Grid.Row="0"
    5.97 +                                                   Grid.RowSpan="2"
    5.98 +                                                   Stretch="Uniform"
    5.99 +                                                   Source="{Binding Path='FileIcon', Mode=OneWay, Converter={StaticResource IconToImageSource}}"
   5.100 +                                                   HorizontalAlignment="Stretch"
   5.101 +                                                   VerticalAlignment="Stretch"
   5.102 +                                                   Margin="5" />
   5.103  
   5.104 -                                        <!-- File name and size -->
   5.105 -                                        <TextBlock Grid.Column="1"
   5.106 -                                           Grid.Row="0"
   5.107 -                                           HorizontalAlignment="Stretch"
   5.108 -                                           VerticalAlignment="Bottom"
   5.109 -                                           Text="{Binding Path='FileName', Mode=OneWay}" />
   5.110 -                                        <TextBlock Grid.Column="1"
   5.111 -                                           Grid.Row="1"
   5.112 -                                           HorizontalAlignment="Stretch"
   5.113 -                                           VerticalAlignment="Center"
   5.114 -                                           Text="{Binding Path='FileSizeString', Mode=OneWay}" />
   5.115 -                                    </Grid>
   5.116 -                                </ContentControl>
   5.117 -                                <Grid Grid.Column="1" Grid.Row="0">
   5.118 -                                    <!-- Attachment actions -->
   5.119 -                                    <!-- The button needs to pass both the PEPAttachment and DataContext to the ContextMenu
   5.120 +                                            <!-- File name and size -->
   5.121 +                                            <TextBlock Grid.Column="1"
   5.122 +                                                       Grid.Row="0"
   5.123 +                                                       HorizontalAlignment="Stretch"
   5.124 +                                                       VerticalAlignment="Bottom"
   5.125 +                                                       Text="{Binding Path='FileName', Mode=OneWay}" />
   5.126 +                                            <TextBlock Grid.Column="1"
   5.127 +                                                       Grid.Row="1"
   5.128 +                                                       HorizontalAlignment="Stretch"
   5.129 +                                                       VerticalAlignment="Center"
   5.130 +                                                       Text="{Binding Path='FileSizeString', Mode=OneWay}" />
   5.131 +                                        </Grid>
   5.132 +                                    </ContentControl>
   5.133 +                                    <Grid Grid.Column="1"
   5.134 +                                          Grid.Row="0">
   5.135 +                                        <!-- Attachment actions -->
   5.136 +                                        <!-- The button needs to pass both the PEPAttachment and DataContext to the ContextMenu
   5.137                                       This is a little tricky because the ContextMenu is outside of the visual tree. 
   5.138                                       In order to do this, the Button Tag is set to the DataContext and the Tag is the UserControl DataContext.
   5.139                                       You cannot Set the Button DataContext to the UserControl DataContext or the PEPAttachment is lost. -->
   5.140 -                                    <Button Name="ButtonAttachment"
   5.141 -                                        Grid.Column="0"
   5.142 -                                        Grid.Row="0"
   5.143 -                                        Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
   5.144 -                                        BorderThickness="0"
   5.145 -                                        HorizontalContentAlignment="Center"
   5.146 -                                        VerticalContentAlignment="Center"
   5.147 -                                        HorizontalAlignment="Stretch"
   5.148 -                                        VerticalAlignment="Stretch"
   5.149 -                                        ContextMenuService.Placement="Bottom"
   5.150 -                                        Tag="{Binding Path='DataContext', RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
   5.151 -                                        DataContext="{Binding Path=.}"
   5.152 -                                        Click="ButtonAttachment_Click">
   5.153 -                                        <Button.ContextMenu>
   5.154 -                                            <ContextMenu Name="ContextMenuAttachment"
   5.155 -                                                     Placement="Bottom"
   5.156 -                                                     DataContext="{Binding Path='PlacementTarget', RelativeSource={RelativeSource Self}}">
   5.157 -                                                <MenuItem Header="{Binding Path='Tag.OpenText'}"
   5.158 -                                                      Tag="{Binding Path='DataContext'}"
   5.159 -                                                      Click="MenuItemOpen_Click" />
   5.160 -                                                <Separator />
   5.161 -                                                <MenuItem Header="{Binding Path='Tag.SaveAsText'}"
   5.162 -                                                      Tag="{Binding Path='DataContext'}"
   5.163 -                                                      Click="MenuItemSaveAs_Click" />
   5.164 -                                            </ContextMenu>
   5.165 -                                        </Button.ContextMenu>
   5.166 -                                        <TextBlock Text="▼"
   5.167 -                                               TextAlignment="Center"
   5.168 -                                               FontFamily="Arial"
   5.169 -                                               FontSize="9"
   5.170 -                                               Foreground="DarkGray" />
   5.171 -                                    </Button>
   5.172 +                                        <Button Name="ButtonAttachment"
   5.173 +                                                Grid.Column="0"
   5.174 +                                                Grid.Row="0"
   5.175 +                                                Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
   5.176 +                                                BorderThickness="0"
   5.177 +                                                HorizontalContentAlignment="Center"
   5.178 +                                                VerticalContentAlignment="Center"
   5.179 +                                                HorizontalAlignment="Stretch"
   5.180 +                                                VerticalAlignment="Stretch"
   5.181 +                                                ContextMenuService.Placement="Bottom"
   5.182 +                                                Tag="{Binding Path='DataContext', RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"
   5.183 +                                                DataContext="{Binding Path=.}"
   5.184 +                                                Click="ButtonAttachment_Click">
   5.185 +                                            <Button.ContextMenu>
   5.186 +                                                <ContextMenu Name="ContextMenuAttachment"
   5.187 +                                                             Placement="Bottom"
   5.188 +                                                             DataContext="{Binding Path='PlacementTarget', RelativeSource={RelativeSource Self}}">
   5.189 +                                                    <MenuItem Header="{Binding Path='Tag.OpenText'}"
   5.190 +                                                              Tag="{Binding Path='DataContext'}"
   5.191 +                                                              Click="MenuItemOpen_Click" />
   5.192 +                                                    <Separator />
   5.193 +                                                    <MenuItem Header="{Binding Path='Tag.SaveAsText'}"
   5.194 +                                                              Tag="{Binding Path='DataContext'}"
   5.195 +                                                              Click="MenuItemSaveAs_Click" />
   5.196 +                                                </ContextMenu>
   5.197 +                                            </Button.ContextMenu>
   5.198 +                                            <TextBlock Text="▼"
   5.199 +                                                       TextAlignment="Center"
   5.200 +                                                       FontFamily="Arial"
   5.201 +                                                       FontSize="9"
   5.202 +                                                       Foreground="DarkGray" />
   5.203 +                                        </Button>
   5.204 +                                    </Grid>
   5.205                                  </Grid>
   5.206 -                            </Grid>
   5.207 -                        </Border>
   5.208 +                            </Border>
   5.209 +                        </StackPanel>
   5.210                      </DataTemplate>
   5.211                  </ItemsControl.ItemTemplate>
   5.212                  <ItemsControl.ItemsPanel>
   5.213 @@ -343,6 +355,9 @@
   5.214                          <Binding Path="Message.Attachments"
   5.215                                   Mode="OneWay"
   5.216                                   Converter="{StaticResource IsListEmptyToInvertBool}" />
   5.217 +                        <Binding Path="Message.Attachments"
   5.218 +                                 Mode="OneWay"
   5.219 +                                 Converter="{StaticResource HasNonInlineAttachments}" />
   5.220                          <Binding Path="IsNoteModeEnabled"
   5.221                                   Mode="OneWay"
   5.222                                   Converter="{StaticResource InvertBool}" />
     6.1 --- a/UI/FormControlPreviewMessage.xaml.cs	Tue Jul 11 15:41:05 2017 +0200
     6.2 +++ b/UI/FormControlPreviewMessage.xaml.cs	Wed Jul 12 15:09:25 2017 +0200
     6.3 @@ -293,7 +293,7 @@
     6.4                  {
     6.5                      foreach (var imageName in imagesNames)
     6.6                      {
     6.7 -                        var attachmentImage = this.displayState?.Message?.Attachments?.SingleOrDefault(i => i.FileName == imageName);
     6.8 +                        var attachmentImage = this.displayState?.Message?.Attachments?.SingleOrDefault(i => i.ContentId == imageName);
     6.9                          Bitmap img = null;
    6.10  
    6.11                          try
     7.1 --- a/UI/FormRegionPrivacyStatus.cs	Tue Jul 11 15:41:05 2017 +0200
     7.2 +++ b/UI/FormRegionPrivacyStatus.cs	Wed Jul 12 15:09:25 2017 +0200
     7.3 @@ -616,7 +616,6 @@
     7.4              //        this.cryptableMailItem.ProcessingCompleted += MailItem_ProcessingCompleted;
     7.5              //        this.cryptableMailItem.GetMirrorCompleted += MailItem_GetMirrorCompleted;
     7.6              //        this.cryptableMailItem.Send += MailItem_Send;
     7.7 -
     7.8              //        if (this.cryptableMailItem.IsSecurelyStored)
     7.9              //        {
    7.10              //            this.cryptableMailItem.Open += MailItem_Open;
     8.1 --- a/UI/ValueConverters.cs	Tue Jul 11 15:41:05 2017 +0200
     8.2 +++ b/UI/ValueConverters.cs	Wed Jul 12 15:09:25 2017 +0200
     8.3 @@ -10,6 +10,116 @@
     8.4  namespace pEp.UI
     8.5  {
     8.6      /// <summary>
     8.7 +    /// Returns transparent if false or white if true or error.
     8.8 +    /// </summary>
     8.9 +    public class BooleanToBackgroundConverter : IValueConverter
    8.10 +    {
    8.11 +        public object Convert(object value,
    8.12 +                              Type targetType,
    8.13 +                              object parameter,
    8.14 +                              CultureInfo culture)
    8.15 +        {
    8.16 +            System.Windows.Media.Brush color = System.Windows.Media.Brushes.Transparent;
    8.17 +
    8.18 +            if ((value is bool) &&
    8.19 +                ((bool)value))
    8.20 +            {
    8.21 +                color = System.Windows.Media.Brushes.White;
    8.22 +            }
    8.23 +
    8.24 +            return color;
    8.25 +        }
    8.26 +
    8.27 +        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    8.28 +        {
    8.29 +            throw new NotImplementedException();
    8.30 +        }
    8.31 +    }
    8.32 +
    8.33 +    /// <summary>
    8.34 +    /// Converter to check whether a PEPMessage has attachments that are not
    8.35 +    /// inline attachments.
    8.36 +    /// </summary>
    8.37 +    public class HasNonInlineAttachmentsConverter : IValueConverter
    8.38 +    {
    8.39 +        public object Convert(object value,
    8.40 +                              Type targetType,
    8.41 +                              object parameter,
    8.42 +                              CultureInfo culture)
    8.43 +        {
    8.44 +            if (value is List<PEPAttachment>)
    8.45 +            {
    8.46 +                var attachments = value as List<PEPAttachment>;
    8.47 +
    8.48 +                foreach (var attachment in attachments)
    8.49 +                {
    8.50 +                    if (string.IsNullOrEmpty(attachment.ContentId))
    8.51 +                    {
    8.52 +                        return true;
    8.53 +                    }
    8.54 +                }
    8.55 +
    8.56 +                return false;
    8.57 +            }
    8.58 +            else
    8.59 +            {
    8.60 +                throw new ArgumentException("Input has to be List<PEPAttachment>.");
    8.61 +            }
    8.62 +        }
    8.63 +
    8.64 +        public object ConvertBack(object value,
    8.65 +                                  Type targetType,
    8.66 +                                  object parameter,
    8.67 +                                  CultureInfo culture)
    8.68 +        {
    8.69 +            throw new NotImplementedException();
    8.70 +        }
    8.71 +    }
    8.72 +
    8.73 +    /// <summary>
    8.74 +    /// Converter to change an Icon into an ImageSource.
    8.75 +    /// </summary>
    8.76 +    public class IconToImageSourceConverter : IValueConverter
    8.77 +    {
    8.78 +        public object Convert(object value,
    8.79 +                              Type targetType,
    8.80 +                              object parameter,
    8.81 +                              CultureInfo culture)
    8.82 +        {
    8.83 +            Icon icon;
    8.84 +            ImageSource source = null;
    8.85 +
    8.86 +            if (value == null)
    8.87 +            {
    8.88 +                return new System.Windows.Media.Imaging.BitmapImage();
    8.89 +                //                throw new ArgumentNullException();
    8.90 +            }
    8.91 +
    8.92 +            if (value is Icon)
    8.93 +            {
    8.94 +                icon = value as Icon;
    8.95 +                source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
    8.96 +                                                                                    new Int32Rect(0, 0, icon.Width, icon.Height),
    8.97 +                                                                                    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
    8.98 +            }
    8.99 +            else
   8.100 +            {
   8.101 +                throw new ArgumentException();
   8.102 +            }
   8.103 +
   8.104 +            return (source);
   8.105 +        }
   8.106 +
   8.107 +        public object ConvertBack(object value,
   8.108 +                                  Type targetType,
   8.109 +                                  object parameter,
   8.110 +                                  CultureInfo culture)
   8.111 +        {
   8.112 +            throw new NotImplementedException();
   8.113 +        }
   8.114 +    }
   8.115 +
   8.116 +    /// <summary>
   8.117      /// Inverts a bool value.
   8.118      /// </summary>
   8.119      public class InvertBoolConverter : IValueConverter
   8.120 @@ -46,77 +156,6 @@
   8.121      }
   8.122  
   8.123      /// <summary>
   8.124 -    /// Returns gray if false or black if true or error.
   8.125 -    /// </summary>
   8.126 -    public class IsEnabledToColorConverter : IValueConverter
   8.127 -    {
   8.128 -        public object Convert(object value,
   8.129 -                              Type targetType,
   8.130 -                              object parameter,
   8.131 -                              CultureInfo culture)
   8.132 -        {
   8.133 -            var color = System.Windows.SystemColors.ControlTextBrush;
   8.134 -
   8.135 -            if ((value is bool) &&
   8.136 -                ((bool)value == false))
   8.137 -            {
   8.138 -                color = System.Windows.SystemColors.GrayTextBrush;
   8.139 -            }
   8.140 -
   8.141 -            return color;
   8.142 -        }
   8.143 -
   8.144 -        public object ConvertBack(object value,
   8.145 -                                  Type targetType,
   8.146 -                                  object parameter,
   8.147 -                                  CultureInfo culture)
   8.148 -        {
   8.149 -            throw new NotImplementedException();
   8.150 -        }
   8.151 -    }
   8.152 -
   8.153 -    /// <summary>
   8.154 -    /// Returns true if given parameter is equal to HandshakeMode.Standard. Otherwise false
   8.155 -    /// </summary>
   8.156 -    public class IsStandardModeToBoolConverter : IValueConverter
   8.157 -    {
   8.158 -        public object Convert(object value,
   8.159 -                              Type targetType,
   8.160 -                              object parameter,
   8.161 -                              CultureInfo culture)
   8.162 -        {
   8.163 -            bool result = false;
   8.164 -            string param = null;
   8.165 -            string val = null;
   8.166 -
   8.167 -            param = parameter as string;
   8.168 -
   8.169 -            try
   8.170 -            {
   8.171 -                val = Enum.GetName(typeof(HandshakeDialog.HandshakeMode), value);
   8.172 -            }
   8.173 -            catch
   8.174 -            {
   8.175 -                val = null;
   8.176 -            }
   8.177 -
   8.178 -            if ((param != null) &&
   8.179 -                (val != null) &&
   8.180 -                (val == param))
   8.181 -            {
   8.182 -                result = true;
   8.183 -            }
   8.184 -
   8.185 -            return result;
   8.186 -        }
   8.187 -
   8.188 -        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   8.189 -        {
   8.190 -            throw new NotImplementedException();
   8.191 -        }
   8.192 -    }
   8.193 -
   8.194 -    /// <summary>
   8.195      /// Returns true if given parameter is same as an active tab. Otherwise false
   8.196      /// </summary>
   8.197      public class IsActiveTabToBoolConverter : IValueConverter
   8.198 @@ -158,72 +197,26 @@
   8.199      }
   8.200  
   8.201      /// <summary>
   8.202 -    /// Returns transparent if false or white if true or error.
   8.203 +    /// Returns gray if false or black if true or error.
   8.204      /// </summary>
   8.205 -    public class BooleanToBackgroundConverter : IValueConverter
   8.206 +    public class IsEnabledToColorConverter : IValueConverter
   8.207      {
   8.208          public object Convert(object value,
   8.209                                Type targetType,
   8.210                                object parameter,
   8.211                                CultureInfo culture)
   8.212          {
   8.213 -            System.Windows.Media.Brush color = System.Windows.Media.Brushes.Transparent;
   8.214 +            var color = System.Windows.SystemColors.ControlTextBrush;
   8.215  
   8.216              if ((value is bool) &&
   8.217 -                ((bool)value))
   8.218 +                ((bool)value == false))
   8.219              {
   8.220 -                color = System.Windows.Media.Brushes.White;
   8.221 +                color = System.Windows.SystemColors.GrayTextBrush;
   8.222              }
   8.223  
   8.224              return color;
   8.225          }
   8.226  
   8.227 -        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   8.228 -        {
   8.229 -            throw new NotImplementedException();
   8.230 -        }
   8.231 -    }
   8.232 -
   8.233 -    /// <summary>
   8.234 -    /// Returns a bool value indicating if the release mode matches the given parameter.
   8.235 -    /// </summary>
   8.236 -    public class IsReleaseModeConverter : IValueConverter
   8.237 -    {
   8.238 -        public object Convert(object value,
   8.239 -                              Type targetType,
   8.240 -                              object parameter,
   8.241 -                              CultureInfo culture)
   8.242 -        {
   8.243 -            bool success;
   8.244 -            Globals.ReleaseMode param;
   8.245 -
   8.246 -            if ((value is Globals.ReleaseMode) &&
   8.247 -                (parameter is string))
   8.248 -            {
   8.249 -                success = Enum.TryParse((parameter as string), out param);
   8.250 -
   8.251 -                if (success)
   8.252 -                {
   8.253 -                    if (((Globals.ReleaseMode)value) == param)
   8.254 -                    {
   8.255 -                        return (true);
   8.256 -                    }
   8.257 -                    else
   8.258 -                    {
   8.259 -                        return (false);
   8.260 -                    }
   8.261 -                }
   8.262 -                else
   8.263 -                {
   8.264 -                    throw new ArgumentException();
   8.265 -                }
   8.266 -            }
   8.267 -            else
   8.268 -            {
   8.269 -                throw new ArgumentException();
   8.270 -            }
   8.271 -        }
   8.272 -
   8.273          public object ConvertBack(object value,
   8.274                                    Type targetType,
   8.275                                    object parameter,
   8.276 @@ -283,78 +276,6 @@
   8.277      }
   8.278  
   8.279      /// <summary>
   8.280 -    /// Converter to change an Icon into an ImageSource.
   8.281 -    /// </summary>
   8.282 -    public class IconToImageSourceConverter : IValueConverter
   8.283 -    {
   8.284 -        public object Convert(object value,
   8.285 -                              Type targetType,
   8.286 -                              object parameter,
   8.287 -                              CultureInfo culture)
   8.288 -        {
   8.289 -            Icon icon;
   8.290 -            ImageSource source = null;
   8.291 -
   8.292 -            if (value == null)
   8.293 -            {
   8.294 -                return new System.Windows.Media.Imaging.BitmapImage();
   8.295 -                //                throw new ArgumentNullException();
   8.296 -            }
   8.297 -
   8.298 -            if (value is Icon)
   8.299 -            {
   8.300 -                icon = value as Icon;
   8.301 -                source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHIcon(icon.Handle,
   8.302 -                                                                                    new Int32Rect(0, 0, icon.Width, icon.Height),
   8.303 -                                                                                    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
   8.304 -            }
   8.305 -            else
   8.306 -            {
   8.307 -                throw new ArgumentException();
   8.308 -            }
   8.309 -
   8.310 -            return (source);
   8.311 -        }
   8.312 -
   8.313 -        public object ConvertBack(object value,
   8.314 -                                  Type targetType,
   8.315 -                                  object parameter,
   8.316 -                                  CultureInfo culture)
   8.317 -        {
   8.318 -            throw new NotImplementedException();
   8.319 -        }
   8.320 -    }
   8.321 -
   8.322 -    /// <summary>
   8.323 -    /// Converter to check if a string is null or empty.
   8.324 -    /// </summary>
   8.325 -    public class IsStringEmptyConverter : IValueConverter
   8.326 -    {
   8.327 -        public object Convert(object value,
   8.328 -                              Type targetType,
   8.329 -                              object parameter,
   8.330 -                              CultureInfo culture)
   8.331 -        {
   8.332 -            if (value is string)
   8.333 -            {
   8.334 -                return (string.IsNullOrEmpty(value.ToString()));
   8.335 -            }
   8.336 -            else
   8.337 -            {
   8.338 -                throw new ArgumentException();
   8.339 -            }
   8.340 -        }
   8.341 -
   8.342 -        public object ConvertBack(object value,
   8.343 -                                  Type targetType,
   8.344 -                                  object parameter,
   8.345 -                                  CultureInfo culture)
   8.346 -        {
   8.347 -            throw new NotImplementedException();
   8.348 -        }
   8.349 -    }
   8.350 -
   8.351 -    /// <summary>
   8.352      /// Converter to check if a list is empty.
   8.353      /// </summary>
   8.354      public class IsListEmptyConverter : IValueConverter
   8.355 @@ -395,24 +316,43 @@
   8.356      }
   8.357  
   8.358      /// <summary>
   8.359 -    /// Converter to chain together multiple converters.
   8.360 +    /// Returns a bool value indicating if the release mode matches the given parameter.
   8.361      /// </summary>
   8.362 -    public class ValueConverterGroup : List<IValueConverter>, IValueConverter
   8.363 +    public class IsReleaseModeConverter : IValueConverter
   8.364      {
   8.365          public object Convert(object value,
   8.366                                Type targetType,
   8.367                                object parameter,
   8.368                                CultureInfo culture)
   8.369          {
   8.370 -            object curValue;
   8.371 +            bool success;
   8.372 +            Globals.ReleaseMode param;
   8.373  
   8.374 -            curValue = value;
   8.375 -            for (int i = 0; i < this.Count; i++)
   8.376 +            if ((value is Globals.ReleaseMode) &&
   8.377 +                (parameter is string))
   8.378              {
   8.379 -                curValue = this[i].Convert(curValue, targetType, parameter, culture);
   8.380 +                success = Enum.TryParse((parameter as string), out param);
   8.381 +
   8.382 +                if (success)
   8.383 +                {
   8.384 +                    if (((Globals.ReleaseMode)value) == param)
   8.385 +                    {
   8.386 +                        return (true);
   8.387 +                    }
   8.388 +                    else
   8.389 +                    {
   8.390 +                        return (false);
   8.391 +                    }
   8.392 +                }
   8.393 +                else
   8.394 +                {
   8.395 +                    throw new ArgumentException();
   8.396 +                }
   8.397              }
   8.398 -
   8.399 -            return (curValue);
   8.400 +            else
   8.401 +            {
   8.402 +                throw new ArgumentException();
   8.403 +            }
   8.404          }
   8.405  
   8.406          public object ConvertBack(object value,
   8.407 @@ -420,15 +360,77 @@
   8.408                                    object parameter,
   8.409                                    CultureInfo culture)
   8.410          {
   8.411 -            object curValue;
   8.412 +            throw new NotImplementedException();
   8.413 +        }
   8.414 +    }
   8.415  
   8.416 -            curValue = value;
   8.417 -            for (int i = (this.Count - 1); i >= 0; i--)
   8.418 +    /// <summary>
   8.419 +    /// Returns true if given parameter is equal to HandshakeMode.Standard. Otherwise false
   8.420 +    /// </summary>
   8.421 +    public class IsStandardModeToBoolConverter : IValueConverter
   8.422 +    {
   8.423 +        public object Convert(object value,
   8.424 +                              Type targetType,
   8.425 +                              object parameter,
   8.426 +                              CultureInfo culture)
   8.427 +        {
   8.428 +            bool result = false;
   8.429 +            string param = null;
   8.430 +            string val = null;
   8.431 +
   8.432 +            param = parameter as string;
   8.433 +
   8.434 +            try
   8.435              {
   8.436 -                curValue = this[i].ConvertBack(curValue, targetType, parameter, culture);
   8.437 +                val = Enum.GetName(typeof(HandshakeDialog.HandshakeMode), value);
   8.438 +            }
   8.439 +            catch
   8.440 +            {
   8.441 +                val = null;
   8.442              }
   8.443  
   8.444 -            return (curValue);
   8.445 +            if ((param != null) &&
   8.446 +                (val != null) &&
   8.447 +                (val == param))
   8.448 +            {
   8.449 +                result = true;
   8.450 +            }
   8.451 +
   8.452 +            return result;
   8.453 +        }
   8.454 +
   8.455 +        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   8.456 +        {
   8.457 +            throw new NotImplementedException();
   8.458 +        }
   8.459 +    }
   8.460 +
   8.461 +    /// <summary>
   8.462 +    /// Converter to check if a string is null or empty.
   8.463 +    /// </summary>
   8.464 +    public class IsStringEmptyConverter : IValueConverter
   8.465 +    {
   8.466 +        public object Convert(object value,
   8.467 +                              Type targetType,
   8.468 +                              object parameter,
   8.469 +                              CultureInfo culture)
   8.470 +        {
   8.471 +            if (value is string)
   8.472 +            {
   8.473 +                return (string.IsNullOrEmpty(value.ToString()));
   8.474 +            }
   8.475 +            else
   8.476 +            {
   8.477 +                throw new ArgumentException();
   8.478 +            }
   8.479 +        }
   8.480 +
   8.481 +        public object ConvertBack(object value,
   8.482 +                                  Type targetType,
   8.483 +                                  object parameter,
   8.484 +                                  CultureInfo culture)
   8.485 +        {
   8.486 +            throw new NotImplementedException();
   8.487          }
   8.488      }
   8.489  
   8.490 @@ -474,4 +476,42 @@
   8.491              throw new NotImplementedException();
   8.492          }
   8.493      }
   8.494 +
   8.495 +    /// <summary>
   8.496 +    /// Converter to chain together multiple converters.
   8.497 +    /// </summary>
   8.498 +    public class ValueConverterGroup : List<IValueConverter>, IValueConverter
   8.499 +    {
   8.500 +        public object Convert(object value,
   8.501 +                              Type targetType,
   8.502 +                              object parameter,
   8.503 +                              CultureInfo culture)
   8.504 +        {
   8.505 +            object curValue;
   8.506 +
   8.507 +            curValue = value;
   8.508 +            for (int i = 0; i < this.Count; i++)
   8.509 +            {
   8.510 +                curValue = this[i].Convert(curValue, targetType, parameter, culture);
   8.511 +            }
   8.512 +
   8.513 +            return (curValue);
   8.514 +        }
   8.515 +
   8.516 +        public object ConvertBack(object value,
   8.517 +                                  Type targetType,
   8.518 +                                  object parameter,
   8.519 +                                  CultureInfo culture)
   8.520 +        {
   8.521 +            object curValue;
   8.522 +
   8.523 +            curValue = value;
   8.524 +            for (int i = (this.Count - 1); i >= 0; i--)
   8.525 +            {
   8.526 +                curValue = this[i].ConvertBack(curValue, targetType, parameter, culture);
   8.527 +            }
   8.528 +
   8.529 +            return (curValue);
   8.530 +        }
   8.531 +    }
   8.532  }