mirror of
https://github.com/Kopano-dev/kopano-ol-extension.git
synced 2023-10-10 13:37:40 +02:00
[KOE-24] Fixed sync dialog to refresh more often, and also updates the ribbon while in progress. Now also records the sync session, to deal with folders that are synced and removed from the list.
This commit is contained in:
parent
ef92888586
commit
4e4046ce43
@ -333,6 +333,7 @@
|
||||
<Compile Include="Stubs\ISignatures.cs" />
|
||||
<Compile Include="Stubs\IStores.cs" />
|
||||
<Compile Include="Stubs\ISyncObject.cs" />
|
||||
<Compile Include="Stubs\ISystemWindow.cs" />
|
||||
<Compile Include="Stubs\OutlookWrappers\AccountWrapper.cs" />
|
||||
<Compile Include="Stubs\OutlookWrappers\AddInWrapper.cs" />
|
||||
<Compile Include="Stubs\OutlookWrappers\AddressEntryWrapper.cs" />
|
||||
|
@ -206,15 +206,23 @@ namespace Acacia.Controls
|
||||
|
||||
private void buttonCancel_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (Cancellation != null)
|
||||
Cancellation.Cancel();
|
||||
DoClose();
|
||||
}
|
||||
|
||||
private void buttonClose_Click(object sender, EventArgs e)
|
||||
{
|
||||
DoClose();
|
||||
}
|
||||
|
||||
private void DoClose()
|
||||
{
|
||||
if (Cancellation != null)
|
||||
Cancellation.Cancel();
|
||||
|
||||
// If we're not on a modal form, close the form manually
|
||||
Form form = FindForm();
|
||||
if (form?.Modal == false)
|
||||
form.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
@ -32,6 +33,24 @@ namespace Acacia.Controls
|
||||
Icon = Properties.Resources.Kopano;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show(IWin32Window) doesn't properly center on the parent, this fixes that
|
||||
/// </summary>
|
||||
/// <param name="owner">The owner, used for the bounds</param>
|
||||
public void ShowCentered(Stubs.ISystemWindow owner)
|
||||
{
|
||||
if (owner == null)
|
||||
{
|
||||
Show();
|
||||
return;
|
||||
}
|
||||
Rectangle parent = owner.Bounds;
|
||||
Rectangle centered = KUIUtil.Center(parent, Size);
|
||||
StartPosition = FormStartPosition.Manual;
|
||||
Location = new Point(centered.X - parent.X, centered.Y - parent.Y);
|
||||
Show(owner);
|
||||
}
|
||||
|
||||
#region Control links
|
||||
|
||||
[Category("Kopano")]
|
||||
|
@ -32,6 +32,8 @@ using Acacia.ZPush.Connect.Soap;
|
||||
using Acacia.Features.GAB;
|
||||
using Acacia.Features.Signatures;
|
||||
using Acacia.UI;
|
||||
using System.Windows.Forms;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
// Prevent field assignment warnings
|
||||
#pragma warning disable 0649
|
||||
@ -45,32 +47,35 @@ namespace Acacia.Features.SyncState
|
||||
#region Sync configuration
|
||||
|
||||
[AcaciaOption("Sets the period to check synchronisation state if a sync is in progress.")]
|
||||
public TimeSpan CheckPeriod
|
||||
public TimeSpan CheckPeriodSync
|
||||
{
|
||||
get { return GetOption(OPTION_CHECK_PERIOD); }
|
||||
set { SetOption(OPTION_CHECK_PERIOD, value); }
|
||||
get { return GetOption(OPTION_CHECK_PERIOD_SYNC); }
|
||||
set { SetOption(OPTION_CHECK_PERIOD_SYNC, value); }
|
||||
}
|
||||
private static readonly TimeSpanOption OPTION_CHECK_PERIOD = new TimeSpanOption("CheckPeriod", new TimeSpan(0, 5, 0));
|
||||
private static readonly TimeSpanOption OPTION_CHECK_PERIOD_SYNC = new TimeSpanOption("CheckPeriodSync", new TimeSpan(0, 1, 0));
|
||||
|
||||
[AcaciaOption("Sets the period to check synchronisation state if a sync is in progress and the dialog is open.")]
|
||||
public TimeSpan CheckPeriodDialog
|
||||
public TimeSpan CheckPeriodDialogSync
|
||||
{
|
||||
get { return GetOption(OPTION_CHECK_PERIOD_DIALOG); }
|
||||
set { SetOption(OPTION_CHECK_PERIOD_DIALOG, value); }
|
||||
get { return GetOption(OPTION_CHECK_PERIOD_DIALOG_SYNC); }
|
||||
set { SetOption(OPTION_CHECK_PERIOD_DIALOG_SYNC, value); }
|
||||
}
|
||||
private static readonly TimeSpanOption OPTION_CHECK_PERIOD_DIALOG = new TimeSpanOption("CheckPeriodDialog", new TimeSpan(0, 1, 0));
|
||||
private static readonly TimeSpanOption OPTION_CHECK_PERIOD_DIALOG_SYNC = new TimeSpanOption("CheckPeriodDialogSync", new TimeSpan(0, 0, 30));
|
||||
|
||||
private TimeSpan DelayTime
|
||||
[AcaciaOption("Sets the period to check synchronisation state if a sync is NOT in progress and the dialog is open.")]
|
||||
public TimeSpan CheckPeriodDialogNoSync
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_dialogOpen)
|
||||
return CheckPeriodDialog;
|
||||
else
|
||||
return CheckPeriod;
|
||||
}
|
||||
get { return GetOption(OPTION_CHECK_PERIOD_DIALOG_NO_SYNC); }
|
||||
set { SetOption(OPTION_CHECK_PERIOD_DIALOG_NO_SYNC, value); }
|
||||
}
|
||||
private bool _dialogOpen;
|
||||
private static readonly TimeSpanOption OPTION_CHECK_PERIOD_DIALOG_NO_SYNC = new TimeSpanOption("CheckPeriodDialogNoSync", new TimeSpan(0, 5, 0));
|
||||
|
||||
private bool _isSyncing;
|
||||
public TimeSpan CheckPeriodDialogEffective
|
||||
{
|
||||
get { return _isSyncing ? CheckPeriodDialogSync : CheckPeriodDialogNoSync; }
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
@ -79,39 +84,21 @@ namespace Acacia.Features.SyncState
|
||||
{
|
||||
private FeatureSyncState _feature;
|
||||
|
||||
/// <summary>
|
||||
/// Number in range [0,1]
|
||||
/// </summary>
|
||||
private double _syncProgress = 1;
|
||||
public double SyncProgress
|
||||
{
|
||||
get { return _syncProgress; }
|
||||
set
|
||||
{
|
||||
int old = SyncProgressPercent;
|
||||
|
||||
_syncProgress = value;
|
||||
|
||||
if (SyncProgressPercent != old)
|
||||
{
|
||||
// Percentage has changed, update required
|
||||
_feature._button.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int _syncProgressPercent = 100;
|
||||
public int SyncProgressPercent
|
||||
{
|
||||
get
|
||||
get { return _syncProgressPercent; }
|
||||
set
|
||||
{
|
||||
return AlignProgress(100);
|
||||
}
|
||||
}
|
||||
if (value != _syncProgressPercent)
|
||||
{
|
||||
_syncProgressPercent = Math.Max(0, Math.Min(100, value));
|
||||
|
||||
private int AlignProgress(int steps, double round = 0.5)
|
||||
{
|
||||
int val = (int)Math.Floor(_syncProgress * steps + round);
|
||||
return Math.Min(steps, val);
|
||||
// Percentage has changed, update required
|
||||
// If the dialog is open, force the update, otherwise it'll trigger only when the main window is active
|
||||
_feature._button.Invalidate(_feature._dialog != null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SyncStateData(FeatureSyncState feature)
|
||||
@ -122,6 +109,7 @@ namespace Acacia.Features.SyncState
|
||||
private static readonly Bitmap[] PROGRESS = CreateProgressImages();
|
||||
|
||||
private const int PROGRESS_STEPS = 20;
|
||||
|
||||
private static Bitmap[] CreateProgressImages()
|
||||
{
|
||||
Bitmap[] images = new Bitmap[PROGRESS_STEPS + 1];
|
||||
@ -151,7 +139,9 @@ namespace Acacia.Features.SyncState
|
||||
|
||||
public Bitmap GetImage(string elementId, bool large)
|
||||
{
|
||||
int index = AlignProgress(PROGRESS_STEPS, 0.05);
|
||||
double round = (double)PROGRESS_STEPS / 100;
|
||||
int val = (int)Math.Floor((_syncProgressPercent / (double)100) * PROGRESS_STEPS + round);
|
||||
int index = Math.Max(0, Math.Min(PROGRESS_STEPS, val));
|
||||
|
||||
// extra safety check, just in case
|
||||
return (index >= 0 && index <= PROGRESS_STEPS) ? PROGRESS[index] : PROGRESS[0];
|
||||
@ -177,6 +167,7 @@ namespace Acacia.Features.SyncState
|
||||
|
||||
private RibbonButton _button;
|
||||
private SyncStateData _state;
|
||||
private ZPushSync.SyncTask _task;
|
||||
|
||||
public FeatureSyncState()
|
||||
{
|
||||
@ -188,7 +179,7 @@ namespace Acacia.Features.SyncState
|
||||
_button = RegisterButton(this, "Progress", true, ShowSyncState, ZPushBehaviour.None);
|
||||
_button.DataProvider = _state;
|
||||
// Add a sync task to start checking. If this finds it's not fully synchronised, it will check more often
|
||||
Watcher.Sync.AddTask(this, Name, CheckSyncState);
|
||||
_task = Watcher.Sync.AddTask(this, Name, CheckSyncState);
|
||||
}
|
||||
|
||||
private class DeviceDetails : ISoapSerializable<DeviceDetails.Data>
|
||||
@ -204,6 +195,12 @@ namespace Acacia.Features.SyncState
|
||||
{
|
||||
return string.Format("{0}: {1}/{2}={3}", status, total, done, todo);
|
||||
}
|
||||
|
||||
internal void MakeDone()
|
||||
{
|
||||
done = total;
|
||||
todo = 0;
|
||||
}
|
||||
}
|
||||
|
||||
[SoapField]
|
||||
@ -221,7 +218,7 @@ namespace Acacia.Features.SyncState
|
||||
[SoapField(4)]
|
||||
public SyncData Sync;
|
||||
|
||||
public bool IsSyncing { get { return Sync != null; } }
|
||||
public bool IsSyncing { get { return Sync != null && Sync.todo > 0; } }
|
||||
|
||||
[SoapField(5)]
|
||||
public string id; // TODO: backend folder id
|
||||
@ -279,56 +276,97 @@ namespace Acacia.Features.SyncState
|
||||
{
|
||||
get { return _data.data.contentdata; }
|
||||
}
|
||||
|
||||
#region Totals
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the totals for the data
|
||||
/// </summary>
|
||||
internal void Calculate()
|
||||
{
|
||||
long total = 0;
|
||||
long done = 0;
|
||||
IsSyncing = false;
|
||||
foreach (ContentData content in Content.Values)
|
||||
{
|
||||
if (content.IsSyncing)
|
||||
{
|
||||
total += content.Sync.total;
|
||||
done += content.Sync.done;
|
||||
IsSyncing = true;
|
||||
}
|
||||
}
|
||||
|
||||
this.Total = total;
|
||||
this.Done = done;
|
||||
}
|
||||
|
||||
public long Total
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public long Done
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public bool IsSyncing
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private class GetDeviceDetailsRequest : SoapRequest<DeviceDetails>
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Information stored per account on a synchronisation session.
|
||||
/// </summary>
|
||||
private class SyncSession
|
||||
{
|
||||
public long Total { get; private set; }
|
||||
public long Done { get; private set; }
|
||||
public bool IsSyncing { get; private set; }
|
||||
|
||||
private readonly FeatureSyncState _feature;
|
||||
private readonly ZPushAccount _account;
|
||||
private readonly Dictionary<string, DeviceDetails.ContentData> _syncContent = new Dictionary<string, DeviceDetails.ContentData>();
|
||||
|
||||
public SyncSession(FeatureSyncState feature, ZPushAccount account)
|
||||
{
|
||||
this._feature = feature;
|
||||
this._account = account;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the details to the current session
|
||||
/// </summary>
|
||||
public void Add(DeviceDetails details)
|
||||
{
|
||||
StringBuilder debug = new StringBuilder();
|
||||
|
||||
// If a folder is no longer reported as it's already synced, we don't necessarily get the
|
||||
// last step where (done == total). This causes some folders to keep lingering. To clean these
|
||||
// up, keep a list of folders syncing in this iteration.
|
||||
// Any folders in the session but not this list are done
|
||||
HashSet<string> syncingNow = new HashSet<string>();
|
||||
|
||||
// Check all syncing data
|
||||
foreach (DeviceDetails.ContentData content in details.Content.Values)
|
||||
{
|
||||
if (content.IsSyncing)
|
||||
{
|
||||
// If the current session is not syncing, this is a restart. Clear stat
|
||||
if (!IsSyncing)
|
||||
{
|
||||
_syncContent.Clear();
|
||||
debug.AppendLine("Starting new SyncSession");
|
||||
}
|
||||
|
||||
// Add to the syncing content
|
||||
IsSyncing = true;
|
||||
_syncContent[content.synckey] = content;
|
||||
syncingNow.Add(content.synckey);
|
||||
|
||||
debug.AppendLine(string.Format("\tFolder: {0} \tSync: {1} \tStatus: {2} / {3}",
|
||||
content.synckey, content.IsSyncing, content.Sync.done, content.Sync.total));
|
||||
}
|
||||
}
|
||||
debug.AppendLine(string.Format("Calculating totals: ({0})", IsSyncing));
|
||||
|
||||
// Clean up any done items
|
||||
bool _syncingNow = false;
|
||||
foreach (DeviceDetails.ContentData content in _syncContent.Values)
|
||||
{
|
||||
if (!syncingNow.Contains(content.synckey))
|
||||
{
|
||||
content.Sync.MakeDone();
|
||||
}
|
||||
else
|
||||
{
|
||||
_syncingNow = true;
|
||||
}
|
||||
}
|
||||
IsSyncing = _syncingNow;
|
||||
|
||||
// Update totals
|
||||
Total = 0;
|
||||
Done = 0;
|
||||
foreach(DeviceDetails.ContentData content in _syncContent.Values)
|
||||
{
|
||||
Total += content.Sync.total;
|
||||
Done += content.Sync.done;
|
||||
debug.AppendLine(string.Format("\tFolder: {0} \tSync: {1} \tStatus: {2} / {3}",
|
||||
content.synckey, content.IsSyncing, content.Sync.done, content.Sync.total));
|
||||
}
|
||||
debug.AppendLine(string.Format("Total: {0} / {1} ({2}%)", Done, Total, CalculatePercentage(Done, Total)));
|
||||
Logger.Instance.Trace(_feature, "Syncing account {0}:\n{1}", _account, debug);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckSyncState(ZPushAccount account)
|
||||
{
|
||||
// TODO: we probably want one invocation for all accounts
|
||||
@ -337,16 +375,28 @@ namespace Acacia.Features.SyncState
|
||||
{
|
||||
// Fetch
|
||||
DeviceDetails details = deviceService.Execute(new GetDeviceDetailsRequest());
|
||||
if (details != null)
|
||||
{
|
||||
bool wasSyncing = false;
|
||||
|
||||
// Determine the totals
|
||||
details?.Calculate();
|
||||
// Create or update session
|
||||
SyncSession session = account.GetFeatureData<SyncSession>(this, null);
|
||||
if (session == null)
|
||||
session = new SyncSession(this, account);
|
||||
else
|
||||
wasSyncing = session.IsSyncing;
|
||||
|
||||
// And store with the account
|
||||
account.SetFeatureData(this, null, details);
|
||||
session.Add(details);
|
||||
|
||||
// If syncing, check again soon.
|
||||
if (details?.IsSyncing == true)
|
||||
Util.Delayed(this, (int)DelayTime.TotalMilliseconds, () => CheckSyncState(account));
|
||||
// Store with the account
|
||||
account.SetFeatureData(this, null, session);
|
||||
|
||||
if (wasSyncing != session.IsSyncing)
|
||||
{
|
||||
// Sync state has changed, update the schedule
|
||||
Watcher.Sync.SetTaskSchedule(_task, account, session.IsSyncing ? CheckPeriodSync : (TimeSpan?)null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the total for all accounts
|
||||
@ -357,35 +407,66 @@ namespace Acacia.Features.SyncState
|
||||
{
|
||||
long total = 0;
|
||||
long done = 0;
|
||||
bool isSyncing = false;
|
||||
|
||||
foreach(ZPushAccount account in Watcher.Accounts.GetAccounts())
|
||||
{
|
||||
DeviceDetails details = account.GetFeatureData<DeviceDetails>(this, null);
|
||||
if (details != null)
|
||||
SyncSession sync = account.GetFeatureData<SyncSession>(this, null);
|
||||
if (sync != null)
|
||||
{
|
||||
total += details.Total;
|
||||
done += details.Done;
|
||||
total += sync.Total;
|
||||
done += sync.Done;
|
||||
if (sync.IsSyncing)
|
||||
isSyncing = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate progress and update
|
||||
if (done == 0)
|
||||
_state.SyncProgress = total == 0 ? 1 : 0;
|
||||
else
|
||||
_state.SyncProgress = (double)done / total;
|
||||
// Update UI
|
||||
_state.SyncProgressPercent = CalculatePercentage(done, total);
|
||||
if (_dialog != null)
|
||||
_dialog.RefreshData();
|
||||
|
||||
if (_isSyncing != isSyncing)
|
||||
{
|
||||
_isSyncing = isSyncing;
|
||||
if(_dialog != null)
|
||||
{
|
||||
// Update the task schedule
|
||||
Watcher.Sync.SetTaskSchedule(_task, null, CheckPeriodDialogEffective, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int CalculatePercentage(long done, long total)
|
||||
{
|
||||
if (total == 0 || done == total)
|
||||
return 100;
|
||||
|
||||
return Math.Max(0, Math.Min(100, (int)(done * 100 / total)));
|
||||
}
|
||||
|
||||
private SyncStateDialog _dialog;
|
||||
|
||||
private void ShowSyncState()
|
||||
{
|
||||
_dialogOpen = true;
|
||||
try
|
||||
// Only show the dialog once
|
||||
if (_dialog != null)
|
||||
return;
|
||||
|
||||
// Ramp up the checking schedule while the dialog is open
|
||||
// The other check sets per-account schedules, we use the global one, so they should't interfere.
|
||||
TimeSpan? old = Watcher.Sync.SetTaskSchedule(_task, null, CheckPeriodDialogEffective, true);
|
||||
SyncStateDialog dlg = new SyncStateDialog(this);
|
||||
dlg.FormClosed += (s, e) =>
|
||||
{
|
||||
new SyncStateDialog(this).ShowDialog();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_dialogOpen = false;
|
||||
}
|
||||
// Restore the schedule
|
||||
Watcher.Sync.SetTaskSchedule(_task, null, old);
|
||||
_dialog = null;
|
||||
};
|
||||
|
||||
// Show the dialog as a non-modal, otherwise the ribbon doesn't get updated
|
||||
_dialog = dlg;
|
||||
dlg.ShowCentered(ThisAddIn.Instance.Window);
|
||||
}
|
||||
|
||||
private class SyncStateImpl : SyncState
|
||||
@ -433,6 +514,11 @@ namespace Acacia.Features.SyncState
|
||||
private set;
|
||||
}
|
||||
|
||||
public int Percentage
|
||||
{
|
||||
get { return CalculatePercentage(Done, Total); }
|
||||
}
|
||||
|
||||
public bool CanResync(ResyncOption option)
|
||||
{
|
||||
// TODO: check if outlook is not offline?
|
||||
@ -500,12 +586,12 @@ namespace Acacia.Features.SyncState
|
||||
|
||||
foreach (ZPushAccount account in _accounts)
|
||||
{
|
||||
DeviceDetails details = account.GetFeatureData<DeviceDetails>(_feature, null);
|
||||
if (details != null)
|
||||
SyncSession sync = account.GetFeatureData<SyncSession>(_feature, null);
|
||||
if (sync != null)
|
||||
{
|
||||
Total += details.Total;
|
||||
Done += details.Done;
|
||||
if (details.IsSyncing)
|
||||
Total += sync.Total;
|
||||
Done += sync.Done;
|
||||
if (sync.IsSyncing)
|
||||
IsSyncing = true;
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,15 @@ namespace Acacia.Features.SyncState
|
||||
long Remaining { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of items already synced;
|
||||
/// Returns the number of items already synced.
|
||||
/// </summary>
|
||||
long Done { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the percentage of syncing that is done.
|
||||
/// </summary>
|
||||
int Percentage { get; }
|
||||
|
||||
bool IsSyncing { get; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -50,15 +50,6 @@ namespace Acacia.Features.SyncState
|
||||
// Add the accounts
|
||||
foreach (ZPushAccount account in ThisAddIn.Instance.Watcher.Accounts.GetAccounts())
|
||||
comboAccounts.Items.Add(account);
|
||||
|
||||
// Add a timer to update the UI
|
||||
Timer timer = new Timer();
|
||||
timer.Interval = 2500;
|
||||
timer.Tick += (o, args) =>
|
||||
{
|
||||
UpdateUI();
|
||||
};
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void ShowHint(object sender, KHintButton.HintEventArgs e)
|
||||
@ -136,9 +127,21 @@ namespace Acacia.Features.SyncState
|
||||
_syncButtons[(int)option].Enabled = _syncState.CanResync(option);
|
||||
}
|
||||
|
||||
RefreshDisplay();
|
||||
}
|
||||
|
||||
public void RefreshData()
|
||||
{
|
||||
_syncState.Update();
|
||||
ThisAddIn.Instance.InUI(RefreshDisplay);
|
||||
}
|
||||
|
||||
private void RefreshDisplay()
|
||||
{
|
||||
if (_syncState.IsSyncing)
|
||||
{
|
||||
textRemaining.Text = _syncState.Remaining.ToString() + " / " + _syncState.Total.ToString();
|
||||
textRemaining.Text = string.Format("{0} / {1} ({2}%)", _syncState.Done, _syncState.Total,
|
||||
_syncState.Percentage);
|
||||
progress.Value = (int)(_syncState.Done * 100.0 / _syncState.Total);
|
||||
}
|
||||
else
|
||||
|
@ -139,13 +139,13 @@
|
||||
</data>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="_buttons.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>2, 286</value>
|
||||
<value>5, 711</value>
|
||||
</data>
|
||||
<data name="_buttons.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>2, 2, 2, 2</value>
|
||||
<value>5, 5, 5, 5</value>
|
||||
</data>
|
||||
<data name="_buttons.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>333, 35</value>
|
||||
<value>889, 54</value>
|
||||
</data>
|
||||
<data name="_buttons.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>0</value>
|
||||
@ -181,10 +181,13 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="_labelRemaining.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>3, 54</value>
|
||||
<value>8, 129</value>
|
||||
</data>
|
||||
<data name="_labelRemaining.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 0, 8, 0</value>
|
||||
</data>
|
||||
<data name="_labelRemaining.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>77, 25</value>
|
||||
<value>203, 60</value>
|
||||
</data>
|
||||
<data name="_labelRemaining.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>5</value>
|
||||
@ -214,10 +217,13 @@
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="_labelAccount.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>3, 0</value>
|
||||
<value>8, 0</value>
|
||||
</data>
|
||||
<data name="_labelAccount.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 0, 8, 0</value>
|
||||
</data>
|
||||
<data name="_labelAccount.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>77, 27</value>
|
||||
<value>203, 53</value>
|
||||
</data>
|
||||
<data name="_labelAccount.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>0</value>
|
||||
@ -250,10 +256,13 @@
|
||||
<value>All Z-Push accounts</value>
|
||||
</data>
|
||||
<data name="comboAccounts.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 3</value>
|
||||
<value>227, 7</value>
|
||||
</data>
|
||||
<data name="comboAccounts.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="comboAccounts.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 21</value>
|
||||
<value>648, 39</value>
|
||||
</data>
|
||||
<data name="comboAccounts.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>1</value>
|
||||
@ -277,10 +286,13 @@
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="_labelProgress.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>3, 27</value>
|
||||
<value>8, 53</value>
|
||||
</data>
|
||||
<data name="_labelProgress.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 0, 8, 0</value>
|
||||
</data>
|
||||
<data name="_labelProgress.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>77, 27</value>
|
||||
<value>203, 76</value>
|
||||
</data>
|
||||
<data name="_labelProgress.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>2</value>
|
||||
@ -307,10 +319,13 @@
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="progress.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 30</value>
|
||||
<value>227, 60</value>
|
||||
</data>
|
||||
<data name="progress.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="progress.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 21</value>
|
||||
<value>648, 62</value>
|
||||
</data>
|
||||
<data name="progress.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>3</value>
|
||||
@ -334,16 +349,16 @@
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="textRemaining.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 57</value>
|
||||
<value>227, 136</value>
|
||||
</data>
|
||||
<data name="textRemaining.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>3, 3, 3, 3</value>
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="textRemaining.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>3, 3, 3, 3</value>
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="textRemaining.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 19</value>
|
||||
<value>648, 46</value>
|
||||
</data>
|
||||
<data name="textRemaining.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>7</value>
|
||||
@ -367,10 +382,13 @@
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="_labelResync.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>3, 79</value>
|
||||
<value>8, 189</value>
|
||||
</data>
|
||||
<data name="_labelResync.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 0, 8, 0</value>
|
||||
</data>
|
||||
<data name="_labelResync.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>77, 35</value>
|
||||
<value>203, 70</value>
|
||||
</data>
|
||||
<data name="_labelResync.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>8</value>
|
||||
@ -409,13 +427,16 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="buttonGAB.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 82</value>
|
||||
<value>227, 196</value>
|
||||
</data>
|
||||
<data name="buttonGAB.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonGAB.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>3, 3, 3, 3</value>
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonGAB.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 29</value>
|
||||
<value>648, 56</value>
|
||||
</data>
|
||||
<data name="buttonGAB.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>0</value>
|
||||
@ -451,13 +472,16 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="buttonSignatures.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 117</value>
|
||||
<value>227, 266</value>
|
||||
</data>
|
||||
<data name="buttonSignatures.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonSignatures.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>3, 3, 3, 3</value>
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonSignatures.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 29</value>
|
||||
<value>648, 56</value>
|
||||
</data>
|
||||
<data name="buttonSignatures.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>1</value>
|
||||
@ -493,13 +517,16 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="buttonServerData.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 152</value>
|
||||
<value>227, 336</value>
|
||||
</data>
|
||||
<data name="buttonServerData.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonServerData.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>3, 3, 3, 3</value>
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonServerData.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 29</value>
|
||||
<value>648, 56</value>
|
||||
</data>
|
||||
<data name="buttonServerData.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>2</value>
|
||||
@ -535,13 +562,16 @@
|
||||
<value>NoControl</value>
|
||||
</data>
|
||||
<data name="buttonFullResync.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 187</value>
|
||||
<value>227, 406</value>
|
||||
</data>
|
||||
<data name="buttonFullResync.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonFullResync.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>3, 3, 3, 3</value>
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="buttonFullResync.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 29</value>
|
||||
<value>648, 56</value>
|
||||
</data>
|
||||
<data name="buttonFullResync.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>3</value>
|
||||
@ -565,13 +595,16 @@
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="_labelResyncOption.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>86, 219</value>
|
||||
<value>227, 469</value>
|
||||
</data>
|
||||
<data name="_labelResyncOption.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 0, 8, 0</value>
|
||||
</data>
|
||||
<data name="_labelResyncOption.Padding" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>0, 6, 0, 0</value>
|
||||
<value>0, 14, 0, 0</value>
|
||||
</data>
|
||||
<data name="_labelResyncOption.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>242, 59</value>
|
||||
<value>648, 223</value>
|
||||
</data>
|
||||
<data name="_labelResyncOption.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>4</value>
|
||||
@ -592,13 +625,16 @@
|
||||
<value>Fill</value>
|
||||
</data>
|
||||
<data name="_layoutMain.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>3, 3</value>
|
||||
<value>8, 7</value>
|
||||
</data>
|
||||
<data name="_layoutMain.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="_layoutMain.RowCount" type="System.Int32, mscorlib">
|
||||
<value>8</value>
|
||||
</data>
|
||||
<data name="_layoutMain.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>331, 278</value>
|
||||
<value>883, 692</value>
|
||||
</data>
|
||||
<data name="_layoutMain.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>1</value>
|
||||
@ -624,11 +660,14 @@
|
||||
<data name="_layout.Location" type="System.Drawing.Point, System.Drawing">
|
||||
<value>0, 0</value>
|
||||
</data>
|
||||
<data name="_layout.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="_layout.RowCount" type="System.Int32, mscorlib">
|
||||
<value>2</value>
|
||||
</data>
|
||||
<data name="_layout.Size" type="System.Drawing.Size, System.Drawing">
|
||||
<value>337, 323</value>
|
||||
<value>899, 770</value>
|
||||
</data>
|
||||
<data name="_layout.TabIndex" type="System.Int32, mscorlib">
|
||||
<value>0</value>
|
||||
@ -652,13 +691,16 @@
|
||||
<value>True</value>
|
||||
</metadata>
|
||||
<data name="$this.AutoScaleDimensions" type="System.Drawing.SizeF, System.Drawing">
|
||||
<value>6, 13</value>
|
||||
<value>16, 31</value>
|
||||
</data>
|
||||
<data name="$this.AutoSize" type="System.Boolean, mscorlib">
|
||||
<value>True</value>
|
||||
</data>
|
||||
<data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing">
|
||||
<value>337, 323</value>
|
||||
<value>899, 770</value>
|
||||
</data>
|
||||
<data name="$this.Margin" type="System.Windows.Forms.Padding, System.Windows.Forms">
|
||||
<value>8, 7, 8, 7</value>
|
||||
</data>
|
||||
<data name="$this.StartPosition" type="System.Windows.Forms.FormStartPosition, System.Windows.Forms">
|
||||
<value>CenterParent</value>
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
@ -47,6 +48,31 @@ namespace Acacia.Native
|
||||
[DllImport("user32.dll")]
|
||||
public static extern int GetWindowLong(IntPtr hWnd, GWL gwl);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RECT
|
||||
{
|
||||
public int Left;
|
||||
public int Top;
|
||||
public int Right;
|
||||
public int Bottom;
|
||||
|
||||
public Rectangle ToRectangle()
|
||||
{
|
||||
return new Rectangle(Left, Top, Right - Left, Bottom - Top);
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||
|
||||
public static Rectangle GetWindowRect(IntPtr hWnd)
|
||||
{
|
||||
RECT rect;
|
||||
GetWindowRect(hWnd, out rect);
|
||||
return rect.ToRectangle();
|
||||
}
|
||||
|
||||
#region Messages
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
|
@ -116,6 +116,11 @@ namespace Acacia
|
||||
public const string PR_ORIGINATOR_NON_DELIVERY_REPORT_REQUESTED = PROP + "0C08" + PT_BOOLEAN;
|
||||
public const string PR_READ_RECEIPT_REQUESTED = PROP + "0029" + PT_BOOLEAN;
|
||||
|
||||
public const string PR_READ_RECEIPT_ADDR_TYPE = PROP + "4029" + PT_UNICODE;
|
||||
public const string PR_READ_RECEIPT_DISPLAY_NAME = PROP + "402B" + PT_UNICODE;
|
||||
public const string PR_READ_RECEIPT_EMAIL_ADDR = PROP + "402A" + PT_UNICODE;
|
||||
public const string PR_READ_RECEIPT_SIMPLE_DISP_NAME = PROP + "4036" + PT_UNICODE;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Meeting requests
|
||||
|
@ -40,7 +40,7 @@ namespace Acacia.Stubs
|
||||
#region UI
|
||||
|
||||
OutlookUI OutlookUI { get; }
|
||||
IWin32Window Window { get; }
|
||||
ISystemWindow Window { get; }
|
||||
IExplorer GetActiveExplorer();
|
||||
|
||||
#endregion
|
||||
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Acacia.Stubs
|
||||
{
|
||||
public interface ISystemWindow : IWin32Window
|
||||
{
|
||||
Rectangle Bounds { get; }
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using NSOutlook = Microsoft.Office.Interop.Outlook;
|
||||
using System.Drawing;
|
||||
|
||||
namespace Acacia.Stubs.OutlookWrappers
|
||||
{
|
||||
@ -259,7 +260,7 @@ namespace Acacia.Stubs.OutlookWrappers
|
||||
/// Simple IWin32Window wrapper for a native handle. NativeWindow sometimes refuses to handle
|
||||
/// these (FromHandle returns null), so use a simple wrapper.
|
||||
/// </summary>
|
||||
private class WindowHandle : IWin32Window
|
||||
private class WindowHandle : ISystemWindow
|
||||
{
|
||||
private IntPtr hWnd;
|
||||
|
||||
@ -268,6 +269,14 @@ namespace Acacia.Stubs.OutlookWrappers
|
||||
this.hWnd = hWnd;
|
||||
}
|
||||
|
||||
public Rectangle Bounds
|
||||
{
|
||||
get
|
||||
{
|
||||
return User32.GetWindowRect(hWnd);
|
||||
}
|
||||
}
|
||||
|
||||
public IntPtr Handle
|
||||
{
|
||||
get
|
||||
@ -277,7 +286,7 @@ namespace Acacia.Stubs.OutlookWrappers
|
||||
}
|
||||
}
|
||||
|
||||
public IWin32Window Window
|
||||
public ISystemWindow Window
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -67,9 +67,13 @@ namespace Acacia.UI.Outlook
|
||||
Logger.Instance.Trace(Owner, "Command {0}: Handled", Id);
|
||||
}
|
||||
|
||||
public void Invalidate()
|
||||
/// <summary>
|
||||
/// Invalidates the command, triggering a reload of labels and images.
|
||||
/// </summary>
|
||||
/// <param name="forceUpdate">If true, the Outlook UI will be updated straight away.</param>
|
||||
public void Invalidate(bool forceUpdate = false)
|
||||
{
|
||||
UI?.InvalidateCommand(this);
|
||||
UI?.InvalidateCommand(this, forceUpdate);
|
||||
}
|
||||
|
||||
private bool _isEnabled = true;
|
||||
|
@ -327,9 +327,14 @@ namespace Acacia.UI.Outlook
|
||||
|
||||
#region Command state
|
||||
|
||||
internal void InvalidateCommand(CommandElement command)
|
||||
internal void InvalidateCommand(CommandElement command, bool forceUpdate)
|
||||
{
|
||||
_officeUI?.InvalidateControl(command.Id);
|
||||
if (forceUpdate)
|
||||
{
|
||||
_officeUI?.Invalidate();
|
||||
// TODO: sometimes if the focus is on another window, it is not updated.
|
||||
}
|
||||
}
|
||||
|
||||
public bool getControlEnabled(Office.IRibbonControl control)
|
||||
|
@ -46,7 +46,7 @@ namespace Acacia.UI.Outlook
|
||||
if (_isPressed != value)
|
||||
{
|
||||
_isPressed = value;
|
||||
UI?.InvalidateCommand(this);
|
||||
UI?.InvalidateCommand(this, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Acacia.ZPush
|
||||
{
|
||||
@ -34,18 +34,89 @@ namespace Acacia.ZPush
|
||||
{
|
||||
#region SyncTask
|
||||
|
||||
public interface SyncTask
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private class Schedule
|
||||
{
|
||||
public delegate void TickHandler(Schedule schedule);
|
||||
public readonly SyncTaskImpl Task;
|
||||
public readonly ZPushAccount Account;
|
||||
private readonly TickHandler _tick;
|
||||
private TimeSpan _period;
|
||||
|
||||
/// <summary>
|
||||
/// We use a Threading.Timer here, as the schedule may be modified from any thread
|
||||
/// </summary>
|
||||
private Timer _timer;
|
||||
|
||||
public TimeSpan Period
|
||||
{
|
||||
get { return _period; }
|
||||
}
|
||||
|
||||
public Schedule(SyncTaskImpl task, ZPushAccount account, TickHandler scheduleTick, TimeSpan period)
|
||||
{
|
||||
this.Task = task;
|
||||
this.Account = account;
|
||||
this._tick = scheduleTick;
|
||||
this._period = period;
|
||||
}
|
||||
|
||||
private void _timer_Tick(object state)
|
||||
{
|
||||
_tick(this);
|
||||
}
|
||||
|
||||
public void Cancel()
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPeriod(TimeSpan value, bool executeNow)
|
||||
{
|
||||
_period = value;
|
||||
|
||||
// Start, this will destroy the old and start the new
|
||||
Start(executeNow);
|
||||
}
|
||||
|
||||
public void Start(bool executeNow)
|
||||
{
|
||||
// Cancel any existing timer
|
||||
Cancel();
|
||||
_timer = new Timer(_timer_Tick, null, executeNow ? TimeSpan.Zero : _period, _period);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a SyncTask. This is not specific for an account. When tasks are executed, GetInstance is
|
||||
/// used to create a task instance for each account, which will be executed by the system task manager.
|
||||
/// </summary>
|
||||
private class SyncTask
|
||||
private class SyncTaskImpl : SyncTask
|
||||
{
|
||||
private readonly Feature _owner;
|
||||
private readonly string _name;
|
||||
private readonly SyncAction _action;
|
||||
private readonly SyncActionConnection _actionConnection;
|
||||
|
||||
public SyncTask(Feature owner, string name, SyncAction action, SyncActionConnection actionConnection)
|
||||
/// <summary>
|
||||
/// Optional schedule for all accounts.
|
||||
/// </summary>
|
||||
private Schedule _scheduleAll;
|
||||
|
||||
/// <summary>
|
||||
/// Optional schedules per account.
|
||||
/// </summary>
|
||||
private readonly Dictionary<ZPushAccount, Schedule> _schedulesAccount = new Dictionary<ZPushAccount, Schedule>();
|
||||
|
||||
public SyncTaskImpl(Feature owner, string name, SyncAction action, SyncActionConnection actionConnection)
|
||||
{
|
||||
this._owner = owner;
|
||||
this._name = name;
|
||||
@ -53,8 +124,25 @@ namespace Acacia.ZPush
|
||||
this._actionConnection = actionConnection;
|
||||
}
|
||||
|
||||
public AcaciaTask GetInstance(ZPushAccount account)
|
||||
public override string ToString()
|
||||
{
|
||||
string s = _owner.Name;
|
||||
if (_name != null)
|
||||
s += ":" + _name;
|
||||
return s;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the task for execution.
|
||||
/// </summary>
|
||||
/// <param name="schedule">The schedule that triggered the task execution, or null if this is a sync.</param>
|
||||
/// <param name="account">The account for which the tasks are requested.</param>
|
||||
/// <returns>The task instance, or null if there is no task to execute in this schedule.</returns>
|
||||
public AcaciaTask GetInstance(Schedule schedule, ZPushAccount account)
|
||||
{
|
||||
if (!IsTaskScheduleApplicable(schedule, account))
|
||||
return null;
|
||||
|
||||
if (_actionConnection != null)
|
||||
{
|
||||
return new AcaciaTask(null, _owner, _name, () =>
|
||||
@ -71,6 +159,68 @@ namespace Acacia.ZPush
|
||||
return new AcaciaTask(null, _owner, _name, () => _action(account));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets a custom schedule for the task. This method only records the schedule, execution is done by ZPushSync.
|
||||
/// </summary>
|
||||
/// <param name="account">The specific account to set the schedule for, or null to set it for all accounts</param>
|
||||
/// <param name="schedule">The schedule. Specify null to clear the schedule and resume the original.</param>
|
||||
public void SetTaskSchedule(ZPushAccount account, Schedule schedule)
|
||||
{
|
||||
if (account == null)
|
||||
_scheduleAll = schedule;
|
||||
else
|
||||
{
|
||||
if (schedule == null)
|
||||
_schedulesAccount.Remove(account);
|
||||
else
|
||||
_schedulesAccount[account] = schedule;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns any custom task scheddule registered for the account, or all accounts.
|
||||
/// </summary>
|
||||
/// <param name="account">The account, or null to retrieve the schedule for all accounts.</param>
|
||||
/// <returns>The schedule, or null if no schedule is registered</returns>
|
||||
public Schedule GetTaskSchedule(ZPushAccount account)
|
||||
{
|
||||
if (account == null)
|
||||
{
|
||||
return _scheduleAll;
|
||||
}
|
||||
else
|
||||
{
|
||||
Schedule schedule;
|
||||
_schedulesAccount.TryGetValue(account, out schedule);
|
||||
return schedule;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsTaskScheduleApplicable(Schedule schedule, ZPushAccount account)
|
||||
{
|
||||
// The fastest schedule is applicable, so determine that
|
||||
Schedule perAccount = GetTaskSchedule(account);
|
||||
Schedule all = _scheduleAll;
|
||||
|
||||
// If there is no custom schedule, only applicable if the task schedule is the standard sync
|
||||
if (perAccount == null && all == null)
|
||||
return schedule == null;
|
||||
// Otherwise the standard sync is not applicable
|
||||
else if (schedule == null)
|
||||
return false;
|
||||
|
||||
// Determine the quickest
|
||||
Schedule quickest = perAccount;
|
||||
if (quickest == null)
|
||||
quickest = all;
|
||||
else if (all != null && all.Period.TotalMilliseconds < quickest.Period.TotalMilliseconds)
|
||||
quickest = all;
|
||||
|
||||
// Applicable if that is the effective schedule
|
||||
return quickest == schedule;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -78,10 +228,10 @@ namespace Acacia.ZPush
|
||||
#region Setup
|
||||
|
||||
private readonly ISyncObject _syncObject;
|
||||
private readonly Timer _timer;
|
||||
private readonly System.Windows.Forms.Timer _timer;
|
||||
private ZPushWatcher _watcher;
|
||||
private bool _started;
|
||||
private readonly List<SyncTask> _tasks = new List<SyncTask>();
|
||||
private readonly List<SyncTaskImpl> _tasks = new List<SyncTaskImpl>();
|
||||
|
||||
public readonly bool Enabled;
|
||||
public readonly TimeSpan Period;
|
||||
@ -103,7 +253,7 @@ namespace Acacia.ZPush
|
||||
if (Enabled)
|
||||
{
|
||||
_watcher = watcher;
|
||||
_timer = new Timer();
|
||||
_timer = new System.Windows.Forms.Timer();
|
||||
_timer.Interval = (int)Period.TotalMilliseconds;
|
||||
_timer.Tick += _timer_Tick;
|
||||
_timer.Start();
|
||||
@ -153,11 +303,13 @@ namespace Acacia.ZPush
|
||||
/// <param name="owner">The feature owning the task.</param>
|
||||
/// <param name="name">The task's name, for logging.</param>
|
||||
/// <param name="action">The action to execute.</param>
|
||||
public void AddTask(Feature owner, string name, SyncAction action)
|
||||
public SyncTask AddTask(Feature owner, string name, SyncAction action)
|
||||
{
|
||||
if (_started)
|
||||
throw new Exception("Already started, cannot add task");
|
||||
_tasks.Add(new SyncTask(owner, name, action, null));
|
||||
SyncTaskImpl task = new SyncTaskImpl(owner, name, action, null);
|
||||
_tasks.Add(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
@ -167,11 +319,87 @@ namespace Acacia.ZPush
|
||||
/// <param name="owner">The feature owning the task.</param>
|
||||
/// <param name="name">The task's name, for logging.</param>
|
||||
/// <param name="action">The action to execute.</param>
|
||||
public void AddTask(Feature owner, string name, SyncActionConnection action)
|
||||
public SyncTask AddTask(Feature owner, string name, SyncActionConnection action)
|
||||
{
|
||||
if (_started)
|
||||
throw new Exception("Already started, cannot add task");
|
||||
_tasks.Add(new SyncTask(owner, name, null, action));
|
||||
|
||||
SyncTaskImpl task = new SyncTaskImpl(owner, name, null, action);
|
||||
_tasks.Add(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a custom schedule for the specified task.
|
||||
/// </summary>
|
||||
/// <param name="task">The task</param>
|
||||
/// <param name="account">The specific account to set the schedule for, or null to set it for all accounts.
|
||||
/// If a per-account schedule is set, and an all-account schedule, the quickest of both applies.</param>
|
||||
/// <param name="period">The schedule. Specify null to clear the schedule and resume the original.</param>
|
||||
/// <returns>The old schedule, or null if there was none.</returns>
|
||||
public TimeSpan? SetTaskSchedule(SyncTask task, ZPushAccount account, TimeSpan? period, bool executeNow = false)
|
||||
{
|
||||
SyncTaskImpl impl = (SyncTaskImpl)task;
|
||||
|
||||
Logger.Instance.Trace(this, "Setting task schedule for {0}{1} to {2}",
|
||||
impl.ToString(),
|
||||
account == null ? "" : (" for account " + account),
|
||||
period == null ? "default" : period.Value.ToString());
|
||||
|
||||
// Check if there's an existing schedule to modify
|
||||
Schedule schedule = impl.GetTaskSchedule(account);
|
||||
TimeSpan? old = schedule?.Period;
|
||||
|
||||
if (period == null)
|
||||
{
|
||||
// Clearing the schedule
|
||||
if (schedule != null)
|
||||
{
|
||||
// Clear the existing schedule
|
||||
schedule.Cancel();
|
||||
impl.SetTaskSchedule(account, null);
|
||||
}
|
||||
// If there was no schedule, we're already done
|
||||
}
|
||||
else
|
||||
{
|
||||
// Setting the schedule
|
||||
if (schedule == null)
|
||||
{
|
||||
// Create a new schedule
|
||||
schedule = new Schedule(impl, account, ScheduleTick, period.Value);
|
||||
impl.SetTaskSchedule(account, schedule);
|
||||
schedule.Start(executeNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update the existing schedule
|
||||
schedule.SetPeriod(period.Value, executeNow);
|
||||
}
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
private void ScheduleTick(Schedule schedule)
|
||||
{
|
||||
// Don't contact the network if Outlook is offline
|
||||
if (ThisAddIn.Instance.IsOffline)
|
||||
return;
|
||||
|
||||
if (schedule.Account == null)
|
||||
{
|
||||
// Execute tasks for all accounts
|
||||
foreach (ZPushAccount account in _watcher.Accounts.GetAccounts())
|
||||
{
|
||||
ExecuteTask(schedule.Task.GetInstance(schedule, account), false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Execute tasks for the specific account
|
||||
ExecuteTask(schedule.Task.GetInstance(schedule, schedule.Account), false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -218,12 +446,22 @@ namespace Acacia.ZPush
|
||||
return;
|
||||
|
||||
// Execute the tasks for the account
|
||||
foreach (SyncTask task in _tasks)
|
||||
foreach (SyncTaskImpl task in _tasks)
|
||||
{
|
||||
Tasks.Task(task.GetInstance(account), synchronous);
|
||||
ExecuteTask(task.GetInstance(null, account), synchronous);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to execute a task. Silently ignores null tasks.
|
||||
/// </summary>
|
||||
private void ExecuteTask(AcaciaTask task, bool synchronous)
|
||||
{
|
||||
if (task == null)
|
||||
return;
|
||||
Tasks.Task(task, synchronous);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timer callback, executes any tasks.
|
||||
/// </summary>
|
||||
|
Loading…
Reference in New Issue
Block a user