diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj index f989cbf..186931c 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/AcaciaZPushPlugin.csproj @@ -287,6 +287,7 @@ SignaturesSettings.cs + @@ -337,6 +338,7 @@ + @@ -618,6 +620,11 @@ + + + + + diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Features.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Features.cs index a3166de..7f85a19 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Features.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/Features.cs @@ -38,7 +38,8 @@ namespace Acacia.Features typeof(SendAs.FeatureSendAs), typeof(Signatures.FeatureSignatures), typeof(MeetingRequest.FeatureMeetingRequest), - typeof(DebugSupport.FeatureDebugSupport) + typeof(DebugSupport.FeatureDebugSupport), + typeof(SyncState.FeatureSyncState), }; } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SyncState/FeatureSyncState.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SyncState/FeatureSyncState.cs new file mode 100644 index 0000000..0d840d2 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Features/SyncState/FeatureSyncState.cs @@ -0,0 +1,165 @@ +/// Copyright 2017 Kopano b.v. +/// +/// This program is free software: you can redistribute it and/or modify +/// it under the terms of the GNU Affero General Public License, version 3, +/// as published by the Free Software Foundation. +/// +/// This program is distributed in the hope that it will be useful, +/// but WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the +/// GNU Affero General Public License for more details. +/// +/// You should have received a copy of the GNU Affero General Public License +/// along with this program.If not, see. +/// +/// Consult LICENSE file for details + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Acacia.Stubs; +using Acacia.Utils; +using Acacia.ZPush; +using Acacia.Features.SharedFolders; +using Acacia.ZPush.API.SharedFolders; +using static Acacia.DebugOptions; +using Acacia.UI.Outlook; +using System.Drawing; + +namespace Acacia.Features.SyncState +{ + public class FeatureSyncState : FeatureDisabled, FeatureWithRibbon + { + private class SyncStateData : DataProvider + { + private FeatureSyncState _feature; + + /// + /// Number in range [0,1] + /// + private double _syncProgress; + public double SyncProgress + { + get { return _syncProgress; } + set + { + int old = SyncProgressPercent; + + _syncProgress = value; + + if (SyncProgressPercent != old) + { + // Percentage has changed, update required + _feature._button.Invalidate(); + } + } + } + + public int SyncProgressPercent + { + get + { + return AlignProgress(100); + } + } + + private int AlignProgress(int steps, double round = 0.5) + { + int val = (int)Math.Floor(_syncProgress * steps + round); + return Math.Min(steps, val); + } + + public SyncStateData(FeatureSyncState feature) + { + this._feature = feature; + } + + private static readonly Bitmap[] PROGRESS = CreateProgressImages(); + + private const int PROGRESS_STEPS = 20; + private static Bitmap[] CreateProgressImages() + { + Bitmap[] images = new Bitmap[PROGRESS_STEPS + 1]; + + Bitmap img0 = Properties.Resources.Progress0; + Bitmap img1 = Properties.Resources.Progress1; + images[0] = img0; + images[PROGRESS_STEPS] = img1; + + // Create a series of images starting with img0, overlayed with part of img1. This shows the progress bar filling up. + for (int i = 1; i < PROGRESS_STEPS; ++i) + { + Bitmap img = new Bitmap(img0); + using (var canvas = Graphics.FromImage(img)) + { + int w = img1.Width * i / PROGRESS_STEPS; + Rectangle r = new Rectangle(0, 0, w, img0.Height); + canvas.DrawImage(img1, r, r, GraphicsUnit.Pixel); + canvas.Save(); + } + + images[i] = img; + } + + return images; + } + + public Bitmap GetImage(string elementId, bool large) + { + int index = AlignProgress(PROGRESS_STEPS, 0.05); + + // extra safety check, just in case + return (index >= 0 && index <= PROGRESS_STEPS) ? PROGRESS[index] : PROGRESS[0]; + } + + public string GetLabel(string elementId) + { + if (SyncProgressPercent == 100) + return Properties.Resources.Ribbon_SyncState_Label_Done; + return string.Format(Properties.Resources.Ribbon_SyncState_Label, SyncProgressPercent); + } + + public string GetScreenTip(string elementId) + { + return Properties.Resources.Ribbon_SyncState_Screentip; + } + + public string GetSuperTip(string elementId) + { + return Properties.Resources.Ribbon_SyncState_Supertip; + } + } + + private RibbonButton _button; + + public FeatureSyncState() + { + } + + public override void Startup() + { + _button = RegisterButton(this, "Progress", true, ShowSyncState, ZPushBehaviour.None); + _button.DataProvider = new SyncStateData(this); + + // Debug timer to increase progress + var timer = new System.Windows.Forms.Timer(); + timer.Interval = 1000; + timer.Tick += (o, args) => + { + SyncStateData data = (SyncStateData)_button.DataProvider; + double val = (data.SyncProgress + 0.05); + if (val > 1.01) + val = 0; + data.SyncProgress = val; + }; + timer.Start(); + + } + + private void ShowSyncState() + { + } + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs index 76429bb..961bee0 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.Designer.cs @@ -324,6 +324,26 @@ namespace Acacia.Properties { } } + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Progress0 { + get { + object obj = ResourceManager.GetObject("Progress0", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap Progress1 { + get { + object obj = ResourceManager.GetObject("Progress1", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// @@ -745,6 +765,42 @@ namespace Acacia.Properties { } } + /// + /// Looks up a localized string similar to Syncing: {0}%. + /// + internal static string Ribbon_SyncState_Label { + get { + return ResourceManager.GetString("Ribbon_SyncState_Label", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Up-to-date. + /// + internal static string Ribbon_SyncState_Label_Done { + get { + return ResourceManager.GetString("Ribbon_SyncState_Label_Done", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Synchronisation state. + /// + internal static string Ribbon_SyncState_Screentip { + get { + return ResourceManager.GetString("Ribbon_SyncState_Screentip", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Open the "Synchronisation" dialog, in which the synchronisation state can be viewed and managed.. + /// + internal static string Ribbon_SyncState_Supertip { + get { + return ResourceManager.GetString("Ribbon_SyncState_Supertip", resourceCulture); + } + } + /// /// Looks up a localized string similar to Kopano. /// diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx index 93f2739..cf507ad 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Properties/Resources.resx @@ -457,4 +457,23 @@ There are unsaved changes. Do you really want to to discard these? + + ..\Resources\Icons\Progress0.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\Icons\Progress1.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Syncing: {0}% + {0} will be replaced with progress in percent + + + Up-to-date + + + Synchronisation state + + + Open the "Synchronisation" dialog, in which the synchronisation state can be viewed and managed. + \ No newline at end of file diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Resources/Icons/Progress0.png b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Resources/Icons/Progress0.png new file mode 100644 index 0000000..132939b Binary files /dev/null and b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Resources/Icons/Progress0.png differ diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Resources/Icons/Progress1.png b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Resources/Icons/Progress1.png new file mode 100644 index 0000000..ce01430 Binary files /dev/null and b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/Resources/Icons/Progress1.png differ diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/CommandElement.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/CommandElement.cs index eb53650..85748f2 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/CommandElement.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/CommandElement.cs @@ -35,6 +35,8 @@ namespace Acacia.UI.Outlook public CheckCommandHandler CheckEnabled; public CheckCommandHandler CheckVisible; + public DataProvider DataProvider { get; set; } + public CommandElement(FeatureWithUI feature, string id, System.Action callback, ZPushBehaviour zpushBehaviour) { @@ -65,6 +67,11 @@ namespace Acacia.UI.Outlook Logger.Instance.Trace(Owner, "Command {0}: Handled", Id); } + public void Invalidate() + { + UI?.InvalidateCommand(this); + } + private bool _isEnabled = true; public bool IsEnabled { @@ -78,7 +85,7 @@ namespace Acacia.UI.Outlook if (_isEnabled != value) { _isEnabled = value; - UI?.InvalidateCommand(this); + Invalidate(); } } } @@ -96,7 +103,7 @@ namespace Acacia.UI.Outlook if (_isVisible != value) { _isVisible = value; - UI?.InvalidateCommand(this); + Invalidate(); } } } diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/DataProvider.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/DataProvider.cs new file mode 100644 index 0000000..80bd763 --- /dev/null +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/DataProvider.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Acacia.UI.Outlook +{ + public interface DataProvider + { + string GetLabel(string elementId); + string GetScreenTip(string elementId); + string GetSuperTip(string elementId); + Bitmap GetImage(string elementId, bool large); + } +} diff --git a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/OutlookUI.cs b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/OutlookUI.cs index 1f88a94..6fb19e8 100644 --- a/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/OutlookUI.cs +++ b/src/AcaciaZPushPlugin/AcaciaZPushPlugin/UI/Outlook/OutlookUI.cs @@ -242,47 +242,84 @@ namespace Acacia.UI.Outlook this._officeUI = ribbonUI; } + private class ResourceDataProvider : DataProvider + { + public Bitmap GetImage(string elementId, bool large) + { + string id = "Ribbon_" + elementId + (large ? "" : "_Small"); + object o = Properties.Resources.ResourceManager.GetObject(id); + if (o == null) + throw new InvalidDataException("Missing image resource " + id); + return o as Bitmap; + } + + public string GetLabel(string elementId) + { + return GetString(elementId, "Label"); + } + + public string GetScreenTip(string elementId) + { + return GetString(elementId, "Screentip"); + } + + public string GetSuperTip(string elementId) + { + return GetString(elementId, "Supertip"); + } + + private string GetString(string elementId, string suffix) + { + string id = "Ribbon_" + elementId + "_" + suffix; + string s = Properties.Resources.ResourceManager.GetString(id); + if (s == null) + throw new InvalidDataException("Missing string resource " + id); + return s; + } + } + + private static readonly DataProvider RESOURCE_DATA_PROVIDER = new ResourceDataProvider(); + + private CommandElement GetCommand(Office.IRibbonControl control) + { + CommandElement command = null; + _commandIds.TryGetValue(control.Id, out command); + return command; + } + + private DataProvider GetDataProvider(CommandElement cmd) + { + return cmd?.DataProvider ?? RESOURCE_DATA_PROVIDER; + } + public Bitmap getControlImage_large(Office.IRibbonControl control) { - return GetControlImage(control, ""); + CommandElement cmd = GetCommand(control); + return GetDataProvider(cmd).GetImage(control.Id, true); } public Bitmap getControlImage_normal(Office.IRibbonControl control) { - return GetControlImage(control, "_Small"); - } - - private Bitmap GetControlImage(Office.IRibbonControl control, string suffix) - { - string id = "Ribbon_" + control.Id + suffix; - object o = Properties.Resources.ResourceManager.GetObject(id); - if (o == null) - throw new InvalidDataException("Missing image resource " + id); - return o as Bitmap; + CommandElement cmd = GetCommand(control); + return GetDataProvider(cmd).GetImage(control.Id, false); } public string getControlLabel(Office.IRibbonControl control) { - return GetString(control, "Label"); + CommandElement cmd = GetCommand(control); + return GetDataProvider(cmd).GetLabel(control.Id); } public string getControlScreentip(Office.IRibbonControl control) { - return GetString(control, "Screentip"); + CommandElement cmd = GetCommand(control); + return GetDataProvider(cmd).GetScreenTip(control.Id); } public string getControlSupertip(Office.IRibbonControl control) { - return GetString(control, "Supertip"); - } - - private string GetString(Office.IRibbonControl control, string suffix) - { - string id = "Ribbon_" + control.Id + "_" + suffix; - string s = Properties.Resources.ResourceManager.GetString(id); - if (s == null) - throw new InvalidDataException("Missing string resource " + id); - return s; + CommandElement cmd = GetCommand(control); + return GetDataProvider(cmd).GetSuperTip(control.Id); } #endregion