mirror of
https://github.com/Kopano-dev/kopano-ol-extension.git
synced 2023-10-10 13:37:40 +02:00
Reimplements folders and items events
This commit is contained in:
parent
cbf9207ff7
commit
dc12b73bbc
@ -286,7 +286,9 @@
|
|||||||
<Compile Include="Stubs\ICommandBars.cs" />
|
<Compile Include="Stubs\ICommandBars.cs" />
|
||||||
<Compile Include="Stubs\IComWrapper.cs" />
|
<Compile Include="Stubs\IComWrapper.cs" />
|
||||||
<Compile Include="Stubs\IExplorer.cs" />
|
<Compile Include="Stubs\IExplorer.cs" />
|
||||||
|
<Compile Include="Stubs\IFolders.cs" />
|
||||||
<Compile Include="Stubs\IItemEvents.cs" />
|
<Compile Include="Stubs\IItemEvents.cs" />
|
||||||
|
<Compile Include="Stubs\IItems.cs" />
|
||||||
<Compile Include="Stubs\IOutlookWindow.cs" />
|
<Compile Include="Stubs\IOutlookWindow.cs" />
|
||||||
<Compile Include="Stubs\IRecipient.cs" />
|
<Compile Include="Stubs\IRecipient.cs" />
|
||||||
<Compile Include="Stubs\IStores.cs" />
|
<Compile Include="Stubs\IStores.cs" />
|
||||||
@ -296,7 +298,9 @@
|
|||||||
<Compile Include="Stubs\OutlookWrappers\AddressEntryWrapper.cs" />
|
<Compile Include="Stubs\OutlookWrappers\AddressEntryWrapper.cs" />
|
||||||
<Compile Include="Stubs\OutlookWrappers\CommandBarsWrapper.cs" />
|
<Compile Include="Stubs\OutlookWrappers\CommandBarsWrapper.cs" />
|
||||||
<Compile Include="Stubs\OutlookWrappers\ExplorerWrapper.cs" />
|
<Compile Include="Stubs\OutlookWrappers\ExplorerWrapper.cs" />
|
||||||
|
<Compile Include="Stubs\OutlookWrappers\FoldersWrapper.cs" />
|
||||||
<Compile Include="Stubs\OutlookWrappers\ItemEventsWrapper.cs" />
|
<Compile Include="Stubs\OutlookWrappers\ItemEventsWrapper.cs" />
|
||||||
|
<Compile Include="Stubs\OutlookWrappers\ItemsWrapper.cs" />
|
||||||
<Compile Include="Stubs\OutlookWrappers\OutlookItemWrapper.cs" />
|
<Compile Include="Stubs\OutlookWrappers\OutlookItemWrapper.cs" />
|
||||||
<Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
|
<Compile Include="Stubs\OutlookWrappers\RecipientWrapper.cs" />
|
||||||
<Compile Include="Stubs\OutlookWrappers\StoresWrapper.cs" />
|
<Compile Include="Stubs\OutlookWrappers\StoresWrapper.cs" />
|
||||||
|
@ -336,7 +336,7 @@ namespace Acacia.Features.GAB
|
|||||||
// Scan a few of the newest items, in case there is some junk in the ZPush folder
|
// Scan a few of the newest items, in case there is some junk in the ZPush folder
|
||||||
// TODO: this shouldn't happen in production.
|
// TODO: this shouldn't happen in production.
|
||||||
int i = 0;
|
int i = 0;
|
||||||
foreach(IItem item in Folder.ItemsSorted("LastModificationTime", true))
|
foreach(IItem item in Folder.Items.Sort("LastModificationTime", true))
|
||||||
{
|
{
|
||||||
using (item)
|
using (item)
|
||||||
{
|
{
|
||||||
|
@ -35,9 +35,7 @@ namespace Acacia.Stubs
|
|||||||
|
|
||||||
bool ShowAsOutlookAB { get; set; }
|
bool ShowAsOutlookAB { get; set; }
|
||||||
|
|
||||||
IEnumerable<IItem> Items { get; }
|
IItems Items { get; }
|
||||||
|
|
||||||
IEnumerable<IItem> ItemsSorted(string field, bool descending);
|
|
||||||
|
|
||||||
IItem GetItemById(string id);
|
IItem GetItemById(string id);
|
||||||
|
|
||||||
@ -58,7 +56,11 @@ namespace Acacia.Stubs
|
|||||||
|
|
||||||
IEnumerable<FolderType> GetSubFolders<FolderType>()
|
IEnumerable<FolderType> GetSubFolders<FolderType>()
|
||||||
where FolderType : IFolder;
|
where FolderType : IFolder;
|
||||||
IEnumerable<IFolder> GetSubFolders();
|
|
||||||
|
IFolders SubFolders
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
}
|
||||||
|
|
||||||
FolderType GetSubFolder<FolderType>(string name)
|
FolderType GetSubFolder<FolderType>(string name)
|
||||||
where FolderType : IFolder;
|
where FolderType : IFolder;
|
||||||
|
32
src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs
Normal file
32
src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IFolders.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Acacia.Stubs
|
||||||
|
{
|
||||||
|
public delegate void IFolders_FolderEventHandler(IFolder folder);
|
||||||
|
public delegate void IFolders_EventHandler();
|
||||||
|
|
||||||
|
public interface IFolders_Events : IDisposable
|
||||||
|
{
|
||||||
|
event IFolders_FolderEventHandler FolderAdd;
|
||||||
|
event IFolders_FolderEventHandler FolderChange;
|
||||||
|
event IFolders_EventHandler FolderRemove;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IFolders : IEnumerable<IFolder>
|
||||||
|
{
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an events subscribption object.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The events. The caller is responsible for disposing</returns>
|
||||||
|
IFolders_Events GetEvents();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
34
src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs
Normal file
34
src/AcaciaZPushPlugin/AcaciaZPushPlugin/Stubs/IItems.cs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Acacia.Stubs
|
||||||
|
{
|
||||||
|
public delegate void IItems_ItemEventHandler(IItem item);
|
||||||
|
|
||||||
|
|
||||||
|
public interface IItems_Events : IDisposable
|
||||||
|
{
|
||||||
|
event IItems_ItemEventHandler ItemAdd;
|
||||||
|
event IItems_ItemEventHandler ItemChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IItems : IEnumerable<IItem>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sorts the items.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="field"></param>
|
||||||
|
/// <param name="descending"></param>
|
||||||
|
/// <returns>The current collection, which will be sorted</returns>
|
||||||
|
IItems Sort(string field, bool descending);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns an events subscribption object.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The events. The caller is responsible for disposing</returns>
|
||||||
|
IItems_Events GetEvents();
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
{
|
{
|
||||||
abstract class ComWrapper<ItemType> : DisposableWrapper, IComWrapper
|
abstract class ComWrapper<ItemType> : DisposableWrapper, IComWrapper
|
||||||
{
|
{
|
||||||
protected ItemType _item { get; private set; }
|
protected readonly ItemType _item;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a wrapper.
|
/// Creates a wrapper.
|
||||||
@ -49,7 +49,6 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
if (MustRelease)
|
if (MustRelease)
|
||||||
{
|
{
|
||||||
ComRelease.Release(_item);
|
ComRelease.Release(_item);
|
||||||
_item = default(ItemType);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,13 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void DoRelease()
|
||||||
|
{
|
||||||
|
base.DoRelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal NSOutlook.Folder RawItem { get { return _item; } }
|
||||||
|
|
||||||
protected override NSOutlook.PropertyAccessor GetPropertyAccessor()
|
protected override NSOutlook.PropertyAccessor GetPropertyAccessor()
|
||||||
{
|
{
|
||||||
return _item.PropertyAccessor;
|
return _item.PropertyAccessor;
|
||||||
@ -125,118 +132,15 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
|
|
||||||
public ItemType ItemType { get { return (ItemType)(int)_item.DefaultItemType; } }
|
public ItemType ItemType { get { return (ItemType)(int)_item.DefaultItemType; } }
|
||||||
|
|
||||||
#region Enumeration
|
|
||||||
|
|
||||||
public class ItemsEnumerator<ItemType> : ComWrapper<NSOutlook.Items>, IEnumerator<ItemType>
|
public IItems Items
|
||||||
where ItemType : IItem
|
|
||||||
{
|
|
||||||
private IEnumerator _enum;
|
|
||||||
private ItemType _last;
|
|
||||||
|
|
||||||
public ItemsEnumerator(NSOutlook.Folder folder, string field, bool descending) : base(folder.Items)
|
|
||||||
{
|
|
||||||
// TODO: can _items be released here already?
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
this._item.Sort("[" + field + "]", descending);
|
|
||||||
}
|
|
||||||
this._enum = _item.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void DoRelease()
|
|
||||||
{
|
|
||||||
CleanLast();
|
|
||||||
if (_enum != null)
|
|
||||||
{
|
|
||||||
if (_enum is IDisposable)
|
|
||||||
((IDisposable)_enum).Dispose();
|
|
||||||
ComRelease.Release(_enum);
|
|
||||||
_enum = null;
|
|
||||||
}
|
|
||||||
base.DoRelease();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ItemType Current
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
CleanLast();
|
|
||||||
_last = Mapping.Wrap<ItemType>(_enum.Current);
|
|
||||||
return _last;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object IEnumerator.Current
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CleanLast()
|
|
||||||
{
|
|
||||||
if (_last != null)
|
|
||||||
{
|
|
||||||
_last.Dispose();
|
|
||||||
_last = default(ItemType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
CleanLast();
|
|
||||||
return _enum.MoveNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
CleanLast();
|
|
||||||
_enum.Reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ItemsEnumerable<ItemType> : IEnumerable<ItemType>
|
|
||||||
where ItemType : IItem
|
|
||||||
{
|
|
||||||
// Managed by the caller, not released here
|
|
||||||
private readonly NSOutlook.Folder _folder;
|
|
||||||
private readonly string _field;
|
|
||||||
private readonly bool _descending;
|
|
||||||
|
|
||||||
public ItemsEnumerable(NSOutlook.Folder folder, string field, bool descending)
|
|
||||||
{
|
|
||||||
this._folder = folder;
|
|
||||||
this._field = field;
|
|
||||||
this._descending = descending;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerator<ItemType> GetEnumerator()
|
|
||||||
{
|
|
||||||
return new ItemsEnumerator<ItemType>(_folder, _field, _descending);
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
|
||||||
{
|
|
||||||
return GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<IItem> Items
|
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new ItemsEnumerable<IItem>(_item, null, false);
|
return new ItemsWrapper(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IItem> ItemsSorted(string field, bool descending)
|
|
||||||
{
|
|
||||||
return new ItemsEnumerable<IItem>(_item, field, descending);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public IItem GetItemById(string entryId)
|
public IItem GetItemById(string entryId)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@ -289,13 +193,16 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
// Don't release the items, the wrapper manages them
|
// Don't release the items, the wrapper manages them
|
||||||
foreach (NSOutlook.Folder folder in _item.Folders.RawEnum(false))
|
foreach (NSOutlook.Folder folder in _item.Folders.RawEnum(false))
|
||||||
{
|
{
|
||||||
yield return WrapFolder<FolderType>(folder);
|
yield return folder.Wrap<FolderType>();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<IFolder> GetSubFolders()
|
public IFolders SubFolders
|
||||||
{
|
{
|
||||||
return GetSubFolders<IFolder>();
|
get
|
||||||
|
{
|
||||||
|
return new FoldersWrapper(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public FolderType GetSubFolder<FolderType>(string name)
|
public FolderType GetSubFolder<FolderType>(string name)
|
||||||
@ -319,7 +226,7 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
}
|
}
|
||||||
if (sub == null)
|
if (sub == null)
|
||||||
return default(FolderType);
|
return default(FolderType);
|
||||||
return WrapFolder<FolderType>(sub);
|
return sub.Wrap<FolderType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FolderType CreateFolder<FolderType>(string name)
|
public FolderType CreateFolder<FolderType>(string name)
|
||||||
@ -330,37 +237,19 @@ namespace Acacia.Stubs.OutlookWrappers
|
|||||||
NSOutlook.Folders folders = com.Add(_item.Folders);
|
NSOutlook.Folders folders = com.Add(_item.Folders);
|
||||||
if (typeof(FolderType) == typeof(IFolder))
|
if (typeof(FolderType) == typeof(IFolder))
|
||||||
{
|
{
|
||||||
return WrapFolder<FolderType>(folders.Add(name));
|
return folders.Add(name).Wrap<FolderType>();
|
||||||
}
|
}
|
||||||
else if (typeof(FolderType) == typeof(IAddressBook))
|
else if (typeof(FolderType) == typeof(IAddressBook))
|
||||||
{
|
{
|
||||||
NSOutlook.MAPIFolder newFolder = folders.Add(name, NSOutlook.OlDefaultFolders.olFolderContacts);
|
NSOutlook.MAPIFolder newFolder = folders.Add(name, NSOutlook.OlDefaultFolders.olFolderContacts);
|
||||||
newFolder.ShowAsOutlookAB = true;
|
newFolder.ShowAsOutlookAB = true;
|
||||||
return WrapFolder<FolderType>(newFolder);
|
return newFolder.Wrap<FolderType>();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FolderType WrapFolder<FolderType>(NSOutlook.MAPIFolder folder)
|
|
||||||
where FolderType : IFolder
|
|
||||||
{
|
|
||||||
if (typeof(FolderType) == typeof(IFolder))
|
|
||||||
{
|
|
||||||
return (FolderType)(IFolder)new FolderWrapper(folder);
|
|
||||||
}
|
|
||||||
else if (typeof(FolderType) == typeof(IAddressBook))
|
|
||||||
{
|
|
||||||
return (FolderType)(IFolder)new AddressBookWrapper(folder);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ComRelease.Release(folder);
|
|
||||||
throw new NotSupportedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public IStorageItem GetStorageItem(string name)
|
public IStorageItem GetStorageItem(string name)
|
||||||
|
@ -0,0 +1,199 @@
|
|||||||
|
using Acacia.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NSOutlook = Microsoft.Office.Interop.Outlook;
|
||||||
|
|
||||||
|
namespace Acacia.Stubs.OutlookWrappers
|
||||||
|
{
|
||||||
|
class FoldersWrapper : IFolders
|
||||||
|
{
|
||||||
|
// Managed by the caller, not released here
|
||||||
|
private readonly FolderWrapper _folder;
|
||||||
|
|
||||||
|
public FoldersWrapper(FolderWrapper folder)
|
||||||
|
{
|
||||||
|
this._folder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<IFolder> GetEnumerator()
|
||||||
|
{
|
||||||
|
// Don't release the items, the wrapper manages them
|
||||||
|
foreach (NSOutlook.Folder folder in _folder.RawItem.Folders.RawEnum(false))
|
||||||
|
{
|
||||||
|
yield return folder.Wrap<IFolder>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
// Don't release the items, the wrapper manages them
|
||||||
|
foreach (NSOutlook.Folder folder in _folder.RawItem.Folders.RawEnum(false))
|
||||||
|
{
|
||||||
|
yield return folder.Wrap<IFolder>();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
private class EventsWrapper : ComWrapper<NSOutlook.Folders>, IFolders_Events
|
||||||
|
{
|
||||||
|
public EventsWrapper(NSOutlook.Folders item) : base(item)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region FolderAdd
|
||||||
|
|
||||||
|
private IFolders_FolderEventHandler _folderAdd;
|
||||||
|
public event IFolders_FolderEventHandler FolderAdd
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
if (_folderAdd == null)
|
||||||
|
HookFolderAdd(true);
|
||||||
|
_folderAdd += value;
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
_folderAdd -= value;
|
||||||
|
if (_folderAdd == null)
|
||||||
|
HookFolderAdd(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HookFolderAdd(bool hook)
|
||||||
|
{
|
||||||
|
if (hook)
|
||||||
|
_item.FolderAdd += HandleFolderAdd;
|
||||||
|
else
|
||||||
|
_item.FolderAdd -= HandleFolderAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleFolderAdd(NSOutlook.MAPIFolder folder)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_folderAdd != null)
|
||||||
|
{
|
||||||
|
using (IFolder folderWrapped = Mapping.Wrap<IFolder>(folder, false))
|
||||||
|
{
|
||||||
|
if (folderWrapped != null)
|
||||||
|
{
|
||||||
|
_folderAdd(folderWrapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Error(this, "Exception in HandleFolderAdd: {0}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region FolderChange
|
||||||
|
|
||||||
|
private IFolders_FolderEventHandler _folderChange;
|
||||||
|
public event IFolders_FolderEventHandler FolderChange
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
if (_folderChange == null)
|
||||||
|
HookFolderChange(true);
|
||||||
|
_folderChange += value;
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
_folderChange -= value;
|
||||||
|
if (_folderChange == null)
|
||||||
|
HookFolderChange(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HookFolderChange(bool hook)
|
||||||
|
{
|
||||||
|
if (hook)
|
||||||
|
_item.FolderChange += HandleFolderChange;
|
||||||
|
else
|
||||||
|
_item.FolderChange -= HandleFolderChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleFolderChange(NSOutlook.MAPIFolder folder)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_folderChange != null)
|
||||||
|
{
|
||||||
|
using (IFolder folderWrapped = Mapping.Wrap<IFolder>(folder, false))
|
||||||
|
{
|
||||||
|
if (folderWrapped != null)
|
||||||
|
{
|
||||||
|
_folderChange(folderWrapped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Error(this, "Exception in HandleFolderChange: {0}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region FolderRemove
|
||||||
|
|
||||||
|
private IFolders_EventHandler _folderRemove;
|
||||||
|
public event IFolders_EventHandler FolderRemove
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
if (_folderRemove == null)
|
||||||
|
HookFolderRemove(true);
|
||||||
|
_folderRemove += value;
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
_folderRemove -= value;
|
||||||
|
if (_folderRemove == null)
|
||||||
|
HookFolderRemove(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HookFolderRemove(bool hook)
|
||||||
|
{
|
||||||
|
if (hook)
|
||||||
|
_item.FolderRemove += HandleFolderRemove;
|
||||||
|
else
|
||||||
|
_item.FolderRemove -= HandleFolderRemove;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleFolderRemove()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_folderRemove != null)
|
||||||
|
{
|
||||||
|
_folderRemove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Error(this, "Exception in HandleFolderRemove: {0}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFolders_Events GetEvents()
|
||||||
|
{
|
||||||
|
return new EventsWrapper(_folder.RawItem.Folders);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,235 @@
|
|||||||
|
using Acacia.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using NSOutlook = Microsoft.Office.Interop.Outlook;
|
||||||
|
|
||||||
|
namespace Acacia.Stubs.OutlookWrappers
|
||||||
|
{
|
||||||
|
class ItemsWrapper : IItems
|
||||||
|
{
|
||||||
|
// Managed by the caller, not released here
|
||||||
|
private readonly FolderWrapper _folder;
|
||||||
|
private string _field;
|
||||||
|
private bool _descending;
|
||||||
|
|
||||||
|
public ItemsWrapper(FolderWrapper folder)
|
||||||
|
{
|
||||||
|
this._folder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IItems Sort(string field, bool descending)
|
||||||
|
{
|
||||||
|
this._field = field;
|
||||||
|
this._descending = descending;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private NSOutlook.Items GetItems()
|
||||||
|
{
|
||||||
|
return _folder.RawItem.Items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<IItem> GetEnumerator()
|
||||||
|
{
|
||||||
|
return new ItemsEnumerator<IItem>(this, _field, _descending);
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Enumeration
|
||||||
|
|
||||||
|
public class ItemsEnumerator<ItemType> : ComWrapper<NSOutlook.Items>, IEnumerator<ItemType>
|
||||||
|
where ItemType : IItem
|
||||||
|
{
|
||||||
|
private IEnumerator _enum;
|
||||||
|
private ItemType _last;
|
||||||
|
|
||||||
|
public ItemsEnumerator(ItemsWrapper items, string field, bool descending) : base(items.GetItems())
|
||||||
|
{
|
||||||
|
// TODO: can _items be released here already?
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
this._item.Sort("[" + field + "]", descending);
|
||||||
|
}
|
||||||
|
this._enum = _item.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void DoRelease()
|
||||||
|
{
|
||||||
|
CleanLast();
|
||||||
|
if (_enum != null)
|
||||||
|
{
|
||||||
|
if (_enum is IDisposable)
|
||||||
|
((IDisposable)_enum).Dispose();
|
||||||
|
ComRelease.Release(_enum);
|
||||||
|
_enum = null;
|
||||||
|
}
|
||||||
|
base.DoRelease();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemType Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
CleanLast();
|
||||||
|
_last = Mapping.Wrap<ItemType>(_enum.Current);
|
||||||
|
return _last;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CleanLast()
|
||||||
|
{
|
||||||
|
if (_last != null)
|
||||||
|
{
|
||||||
|
_last.Dispose();
|
||||||
|
_last = default(ItemType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
CleanLast();
|
||||||
|
return _enum.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
CleanLast();
|
||||||
|
_enum.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
|
||||||
|
private class EventsWrapper : ComWrapper<NSOutlook.Items>, IItems_Events
|
||||||
|
{
|
||||||
|
public EventsWrapper(NSOutlook.Items item) : base(item)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#region ItemAdd
|
||||||
|
|
||||||
|
private IItems_ItemEventHandler _itemAdd;
|
||||||
|
public event IItems_ItemEventHandler ItemAdd
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
if (_itemAdd == null)
|
||||||
|
HookItemAdd(true);
|
||||||
|
_itemAdd += value;
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
_itemAdd -= value;
|
||||||
|
if (_itemAdd == null)
|
||||||
|
HookItemAdd(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HookItemAdd(bool hook)
|
||||||
|
{
|
||||||
|
if (hook)
|
||||||
|
_item.ItemAdd += HandleItemAdd;
|
||||||
|
else
|
||||||
|
_item.ItemAdd -= HandleItemAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleItemAdd(object objItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_itemAdd != null)
|
||||||
|
{
|
||||||
|
using (IItem item = Mapping.Wrap<IItem>(objItem, false))
|
||||||
|
{
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
_itemAdd(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Error(this, "Exception in HandleItemAdd: {0}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region ItemChange
|
||||||
|
|
||||||
|
private IItems_ItemEventHandler _itemChange;
|
||||||
|
public event IItems_ItemEventHandler ItemChange
|
||||||
|
{
|
||||||
|
add
|
||||||
|
{
|
||||||
|
if (_itemChange == null)
|
||||||
|
HookItemChange(true);
|
||||||
|
_itemChange += value;
|
||||||
|
}
|
||||||
|
remove
|
||||||
|
{
|
||||||
|
_itemChange -= value;
|
||||||
|
if (_itemChange == null)
|
||||||
|
HookItemChange(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HookItemChange(bool hook)
|
||||||
|
{
|
||||||
|
if (hook)
|
||||||
|
_item.ItemChange += HandleItemChange;
|
||||||
|
else
|
||||||
|
_item.ItemChange -= HandleItemChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleItemChange(object objItem)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_itemChange != null)
|
||||||
|
{
|
||||||
|
using (IItem item = Mapping.Wrap<IItem>(objItem, false))
|
||||||
|
{
|
||||||
|
if (item != null)
|
||||||
|
{
|
||||||
|
_itemChange(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Error(this, "Exception in HandleItemChange: {0}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
public IItems_Events GetEvents()
|
||||||
|
{
|
||||||
|
return new EventsWrapper(GetItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using Acacia.Stubs.OutlookWrappers;
|
using Acacia.Stubs.OutlookWrappers;
|
||||||
|
using Acacia.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -15,6 +16,25 @@ namespace Acacia.Stubs
|
|||||||
return Mapping.WrapOrDefault<IFolder>(obj);
|
return Mapping.WrapOrDefault<IFolder>(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static FolderType Wrap<FolderType>(this NSOutlook.MAPIFolder folder)
|
||||||
|
where FolderType : IFolder
|
||||||
|
{
|
||||||
|
if (typeof(FolderType) == typeof(IFolder))
|
||||||
|
{
|
||||||
|
return (FolderType)(IFolder)new FolderWrapper(folder);
|
||||||
|
}
|
||||||
|
else if (typeof(FolderType) == typeof(IAddressBook))
|
||||||
|
{
|
||||||
|
return (FolderType)(IFolder)new AddressBookWrapper(folder);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ComRelease.Release(folder);
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static WrapType Wrap<WrapType>(this object o, bool mustRelease = true)
|
public static WrapType Wrap<WrapType>(this object o, bool mustRelease = true)
|
||||||
where WrapType : IBase
|
where WrapType : IBase
|
||||||
{
|
{
|
||||||
|
@ -62,7 +62,6 @@ namespace Acacia.Utils
|
|||||||
ComRelease.Release(source);
|
ComRelease.Release(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check this
|
|
||||||
public static IEnumerable RawEnum(this IEnumerable source, bool releaseItems = true)
|
public static IEnumerable RawEnum(this IEnumerable source, bool releaseItems = true)
|
||||||
{
|
{
|
||||||
foreach (object item in source)
|
foreach (object item in source)
|
||||||
|
@ -27,10 +27,12 @@ namespace Acacia.ZPush
|
|||||||
{
|
{
|
||||||
public class ZPushFolder : DisposableWrapper
|
public class ZPushFolder : DisposableWrapper
|
||||||
{
|
{
|
||||||
private IFolder _folder;
|
private readonly IFolder _folder;
|
||||||
private ZPushFolder _parent;
|
private readonly IItems_Events _items;
|
||||||
|
private readonly IFolders_Events _subFolders;
|
||||||
|
private readonly ZPushFolder _parent;
|
||||||
private readonly ZPushWatcher _watcher;
|
private readonly ZPushWatcher _watcher;
|
||||||
private List<ItemsWatcher> _itemsWatchers = new List<ItemsWatcher>();
|
private readonly List<ItemsWatcher> _itemsWatchers = new List<ItemsWatcher>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Children folders indexed by EntryID
|
/// Children folders indexed by EntryID
|
||||||
@ -50,6 +52,9 @@ namespace Acacia.ZPush
|
|||||||
this._parent = parent;
|
this._parent = parent;
|
||||||
this._watcher = watcher;
|
this._watcher = watcher;
|
||||||
this._folder = folder;
|
this._folder = folder;
|
||||||
|
// We need to keep links to these objects to keep getting events.
|
||||||
|
this._items = folder.Items.GetEvents();
|
||||||
|
this._subFolders = folder.SubFolders.GetEvents();
|
||||||
folder.ZPush = this;
|
folder.ZPush = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +62,13 @@ namespace Acacia.ZPush
|
|||||||
{
|
{
|
||||||
Cleanup();
|
Cleanup();
|
||||||
_folder.Dispose();
|
_folder.Dispose();
|
||||||
|
_items.Dispose();
|
||||||
|
_subFolders.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFolder Folder { get { return _folder; } }
|
public IFolder Folder { get { return _folder; } }
|
||||||
@ -71,7 +83,7 @@ namespace Acacia.ZPush
|
|||||||
_watcher.OnFolderDiscovered(this);
|
_watcher.OnFolderDiscovered(this);
|
||||||
|
|
||||||
// Recurse the children
|
// Recurse the children
|
||||||
foreach (IFolder subfolder in _folder.GetSubFolders())
|
foreach (IFolder subfolder in _folder.SubFolders)
|
||||||
{
|
{
|
||||||
Tasks.Task(null, "WatchChild", () => WatchChild(subfolder));
|
Tasks.Task(null, "WatchChild", () => WatchChild(subfolder));
|
||||||
}
|
}
|
||||||
@ -94,10 +106,10 @@ namespace Acacia.ZPush
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
private void HookEvents(bool register)
|
private void HookEvents(bool register)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
/*
|
|
||||||
if (register)
|
if (register)
|
||||||
{
|
{
|
||||||
// Item events
|
// Item events
|
||||||
@ -119,9 +131,119 @@ namespace Acacia.ZPush
|
|||||||
_subFolders.FolderAdd -= SubFolders_FolderAdd;
|
_subFolders.FolderAdd -= SubFolders_FolderAdd;
|
||||||
_subFolders.FolderRemove -= SubFolders_FolderRemove;
|
_subFolders.FolderRemove -= SubFolders_FolderRemove;
|
||||||
_subFolders.FolderChange -= SubFolders_FolderChange;
|
_subFolders.FolderChange -= SubFolders_FolderChange;
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void SubFolders_FolderAdd(IFolder folder)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Instance.Debug(this, "Folder added in {0}: {1}", Name, folder.Name);
|
||||||
|
WatchChild(folder);
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderAdd: {0}: {1}", Name, e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SubFolders_FolderRemove()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Instance.Debug(this, "Folder removed from {0}", Name);
|
||||||
|
|
||||||
|
// Helpfully, Outlook doesn't tell us which folder was removed. Could use the BeforeFolderMove event instead,
|
||||||
|
// but that doesn't fire if a folder was removed on the server.
|
||||||
|
// Hence, fetch all the remaining folder ids, and remove any folder that no longer exists.
|
||||||
|
// TODO: move this logic into IFolders?
|
||||||
|
HashSet<string> remaining = new HashSet<string>();
|
||||||
|
foreach (IFolder child in _folder.SubFolders)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
remaining.Add(child.EntryID);
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { Logger.Instance.Warning(this, "Ignoring failed child: {0}", e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the folders that need to be removed. There should be only one, but with Outlook we can never be sure,
|
||||||
|
// so compare all. We cannot modify the dictionary during iteration, so store entries to be removed in a
|
||||||
|
// temporary list
|
||||||
|
List<KeyValuePair<string, ZPushFolder>> remove = new List<KeyValuePair<string, ZPushFolder>>();
|
||||||
|
foreach (var entry in _children)
|
||||||
|
{
|
||||||
|
if (!remaining.Contains(entry.Key))
|
||||||
|
{
|
||||||
|
remove.Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually remove the folders
|
||||||
|
foreach (var entry in remove)
|
||||||
|
{
|
||||||
|
Logger.Instance.Debug(this, "Removing subfolder {0}, {1}", Name, entry.Key);
|
||||||
|
_children.Remove(entry.Key);
|
||||||
|
entry.Value.Cleanup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderRemove: {0}: {1}", Name, e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SubFolders_FolderChange(IFolder folder)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Instance.Debug(this, "Folder changed in {0}: {1}", Name, folder.Name);
|
||||||
|
ZPushFolder child;
|
||||||
|
if (_children.TryGetValue(folder.EntryID, out child))
|
||||||
|
{
|
||||||
|
_watcher.OnFolderChanged(child);
|
||||||
|
// TODO: release folder?
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// On a clean profile, we sometimes get a change notification, but not an add notification
|
||||||
|
// Create it now
|
||||||
|
// This will send a discover notification if required, which is just as good as a change notification
|
||||||
|
Logger.Instance.Debug(this, "Folder change on unreported folder in {0}: {1}, {2}, {3}", Name, folder.Name, folder.EntryID, folder.StoreDisplayName);
|
||||||
|
WatchChild(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception e) { Logger.Instance.Error(this, "Exception in SubFolders_FolderChange: {0}: {1}", Name, e); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Items_ItemAdd(IItem item)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Instance.Trace(this, "New item {0}: {1}", Name, item.EntryID);
|
||||||
|
foreach (ItemsWatcher watcher in _itemsWatchers)
|
||||||
|
watcher.OnItemAdd(this, item);
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Trace(this, "ItemAdd exception: {0}: {1}", Name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Items_ItemChange(IItem item)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Instance.Trace(this, "Changed item {0}", Name);
|
||||||
|
foreach (ItemsWatcher watcher in _itemsWatchers)
|
||||||
|
watcher.OnItemChange(this, item);
|
||||||
|
}
|
||||||
|
catch (System.Exception e)
|
||||||
|
{
|
||||||
|
Logger.Instance.Trace(this, "ItemChange exception: {0}: {1}", Name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
private void Cleanup()
|
private void Cleanup()
|
||||||
{
|
{
|
||||||
Logger.Instance.Trace(this, "Unwatching folder: {0}", _folder.Name);
|
Logger.Instance.Trace(this, "Unwatching folder: {0}", _folder.Name);
|
||||||
|
@ -135,7 +135,7 @@ namespace Acacia.ZPush
|
|||||||
// Hide the folders that are not custom folders
|
// Hide the folders that are not custom folders
|
||||||
using (IFolder root = store.GetRootFolder())
|
using (IFolder root = store.GetRootFolder())
|
||||||
{
|
{
|
||||||
foreach(IFolder sub in root.GetSubFolders())
|
foreach(IFolder sub in root.SubFolders)
|
||||||
{
|
{
|
||||||
using (sub)
|
using (sub)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user