///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
using JetBrains.Omea.AsyncProcessing;
using JetBrains.Omea.Containers;
using JetBrains.Omea.Diagnostics;
using JetBrains.Omea.FiltersManagement;
using JetBrains.Omea.OpenAPI;
using JetBrains.Omea.Contacts;
using JetBrains.Omea.ResourceTools;
namespace JetBrains.Omea.ContactsPlugin
{
/**
* "New Contact..." action.
*/
public class NewContactAction: IAction
{
private class NewContactLocation
{
internal IResource AddressBook;
internal IResource Category;
}
public void Execute( IActionContext context )
{
ContactView cv = new ContactView();
IResource contact = Core.ResourceStore.NewResourceTransient( "Contact" );
NewContactLocation location = new NewContactLocation();
IResource owner = Core.ResourceBrowser.OwnerResource;
if ( context.SelectedResources.Count == 1 && context.SelectedResources [0].Type == "AddressBook" )
{
location.AddressBook = context.SelectedResources [0];
}
else if ( owner != null && owner.Type == "AddressBook" )
{
location.AddressBook = owner;
}
if ( owner != null && owner.Type == "Category" )
{
location.Category = owner;
}
Core.UIManager.OpenResourceEditWindow( cv, contact, true, OnNewContactSaved, location );
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
if ( ContactsPlugin.IsReader )
{
presentation.Visible = false;
return;
}
if ( context.Kind == ActionContextKind.ContextMenu )
{
presentation.Visible = (context.SelectedResources.Count == 1) &&
context.SelectedResources.AllResourcesOfType( "AddressBook");
}
else if ( context.Kind == ActionContextKind.Keyboard )
{
presentation.Visible = (Core.TabManager.CurrentTabId == "Contacts" );
}
}
private static void OnNewContactSaved( IResource res, object tag )
{
Trace.WriteLine( "OnNewContactSaved is called for " + res.DisplayName );
// NB: SetPropAsync does not work properly under Windows2000
// new ResourceProxy( res ).SetPropAsync( ContactHelper._propUserCreated, true );
new ResourceProxy( res ).SetProp( ContactManager._propUserCreated, true );
if ( tag != null )
{
NewContactLocation location = (NewContactLocation) tag;
if ( location.AddressBook != null )
{
AddressBook ab = new AddressBook( location.AddressBook );
ab.AddContact( res );
}
if ( location.Category != null )
{
Core.CategoryManager.AddResourceCategory( res, location.Category );
}
}
Core.WorkspaceManager.AddToActiveWorkspace( res );
}
}
public class EditContactAction: ActionOnSingleResource
{
public override void Execute( IActionContext context )
{
// Edit Contact can be issued on resources of both "Contact" and
// "ContactName" types.
IResource contact = context.SelectedResources[ 0 ];
if( contact.Type == "ContactName" )
contact = contact.GetLinkProp( Core.ContactManager.Props.LinkBaseContact );
ContactView cv = new ContactView();
Core.UIManager.OpenResourceEditWindow( cv, contact, false );
}
}
#region Merge/Split
public class MergeContactAction: IAction
{
public void Execute( IActionContext context )
{
IResource resultContact;
if( context.SelectedResources.Count == 1 )
{
IResourceList candidates = ContactManager.GetContactsForMerging( context.SelectedResources[ 0 ] );
if( candidates.Count == 0 )
candidates = null;
resultContact = ShowMergeDialog( candidates, context.SelectedResources );
}
else
{
resultContact = ShowMergeDialog( context.SelectedResources, null );
}
if ( resultContact != null )
{
if( Core.TabManager.CurrentTab.Name == "Contacts" )
{
Core.ResourceBrowser.SelectResource( resultContact );
}
else
{
AbstractViewPane pane = Core.LeftSidebar.GetPane( StandardViewPanes.Correspondents );
if( pane != null )
pane.SelectResource( resultContact, false );
}
}
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
int count = context.SelectedResources.Count;
presentation.Visible = (count > 0);
if( count == 1 )
presentation.Text = "Merge Contact With...";
else
if( count > 1 )
presentation.Text = "Merge Selected Contacts...";
}
#region Impl
private delegate IResource DelegateMerge( string fullName, IResourceList contacts, bool showOrigNames );
///
/// Shows the contact merge dialog and performs the merge if accepted by the user.
///
/// The contacts which are shown in the suggestions list.
/// The contacts which are initially shown in
/// the contacts to merge list.
/// The merge result contact, or null if the user cancelled the merge operation.
public static IResource ShowMergeDialog( IResourceList contacts, IResourceList defaultContactsToMerge )
{
var dlg = new MergeContactsForm(contacts, defaultContactsToMerge);
using(dlg)
{
if(dlg.ShowDialog() == DialogResult.OK)
{
// a list of contacts can be changed in the dialog,
// e.g. new contacts may appeared.
Cursor.Current = Cursors.WaitCursor;
IResource result = null;
Core.UIManager.RunWithProgressWindow("Merging Contacts…", delegate { result = DoMerge(dlg.FullName, dlg.ResultContacts, dlg.ShowOriginalNames); });
Cursor.Current = Cursors.Default;
return result;
}
return null;
}
}
private static IResource DoMerge( string fullName, IResourceList contacts, bool showOrigNames )
{
Core.ProgressWindow.UpdateProgress( 0, "Merging...", "" );
IResource resultContact = (IResource) Core.ResourceAP.RunJob( new DelegateMerge( Merge ),
fullName, contacts, showOrigNames );
return resultContact;
}
private static IResource Merge( string fullName, IResourceList contacts, bool showOrigNames )
{
IResource resultContact = Core.ContactManager.Merge( fullName, contacts );
if( showOrigNames )
resultContact.SetProp( Core.ContactManager.Props.ShowOriginalNames, true );
return resultContact;
}
#endregion Impl
}
public class SplitContactAction: IAction
{
IResourceList resultContacts;
private delegate void DelegateSplit( IResource contact, IResourceList contacts2Split );
private void DoSplit( IResource contact, IResourceList contacts2Split )
{
Core.ProgressWindow.UpdateProgress( 0, "Splitting...", "" );
Core.ResourceAP.RunJob( new DelegateSplit( Split ), contact, contacts2Split );
}
private void Split( IResource contact, IResourceList contacts2Split )
{
resultContacts = Core.ContactManager.Split( contact, contacts2Split );
}
public void Execute(IActionContext context)
{
Debug.Assert(context.SelectedResources.Count == 1, "Contact splitting action is called with illegal number of parameters (only one contact is expected)");
IResource contact = context.SelectedResources[0];
if(contact.Type == "ContactName")
contact = contact.GetLinkProp(Core.ContactManager.Props.LinkBaseContact);
var form = new SplitContactForm(contact);
if(form.ShowDialog() == DialogResult.OK)
{
Cursor.Current = Cursors.WaitCursor;
Core.UIManager.RunWithProgressWindow("Merging Contacts…", delegate { DoSplit(contact, form.Contacts2Split); });
Cursor.Current = Cursors.Default;
UpdateResourcePanes();
}
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
presentation.Visible = (context.SelectedResources.Count == 1);
// SplitContact can be issued on resources of both "Contact"
// and "ContactName" types.
if( presentation.Visible )
{
IResource contact = context.SelectedResources[ 0 ];
if( contact.Type == "ContactName" )
contact = contact.GetLinkProp( Core.ContactManager.Props.LinkBaseContact );
if ( context.Kind == ActionContextKind.ContextMenu )
{
presentation.Visible = presentation.Visible &&
contact.HasProp( ContactManager._propSerializationBlobLink );
}
else
{
presentation.Enabled = presentation.Visible &&
contact.HasProp( ContactManager._propSerializationBlobLink );
}
}
}
private void UpdateResourcePanes()
{
if( Core.TabManager.CurrentTab.Name == "Contacts" )
Core.ResourceBrowser.SelectResource( resultContacts[ 0 ] );
else
{
AbstractViewPane pane = Core.LeftSidebar.GetPane( StandardViewPanes.Correspondents );
if( pane != null && resultContacts.Count > 0 )
pane.SelectResource( resultContacts[ 0 ], false );
}
}
}
#endregion Merge/Split
#region AdressBooks
/**
* Action to create an address book.
*/
public class CreateABAction: IAction
{
public void Execute( IActionContext context )
{
string name = "New Address Book";
int uniqueNumber = 0;
while ( Core.ResourceStore.FindResources( "AddressBook", "DeepName", name ).Count > 0 )
{
uniqueNumber++;
name = "New Address Book " + uniqueNumber;
}
Core.UIManager.BeginUpdateSidebar();
Core.TabManager.CurrentTabId = "Contacts";
Core.LeftSidebar.ActivateViewPane( "AddressBooks" );
Core.UIManager.EndUpdateSidebar();
AddressBook ab = new AddressBook( name );
Core.WorkspaceManager.AddToActiveWorkspace( ab.Resource );
ContactsPlugin.AddressBookPane.EditResourceLabel( ab.Resource );
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
if ( ContactsPlugin.IsReader )
{
presentation.Visible = false;
}
}
}
/**
* Action to delete an address book.
*/
public class DeleteABAction: ActionOnResource
{
public override void Execute( IActionContext context )
{
string prompt = "Do you wish to delete ";
if ( context.SelectedResources.Count == 1 )
{
prompt += "the address book '" + context.SelectedResources [0].GetStringProp( "Name" ) + "'?";
}
else
{
prompt += context.SelectedResources.Count + " selected address books?";
}
if ( MessageBox.Show( ICore.Instance.MainWindow, prompt, "Delete Address Book",
MessageBoxButtons.YesNo ) == DialogResult.Yes )
{
foreach( IResource res in context.SelectedResources )
{
new ResourceProxy( res ).DeleteAsync();
}
}
}
}
/**
* Action to remove a contact from an address book.
*/
public class RemoveFromABAction: IAction
{
public void Execute( IActionContext context )
{
ContactManager.RemoveContactFromAddressBook( context.LinkTargetResource, context.SelectedResources[ 0 ] );
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
presentation.Visible = (context.SelectedResources.Count == 1) &&
(context.LinkTargetResource != null) &&
(context.LinkPropId == AddressBook.PropInAddressBook);
presentation.Enabled = presentation.Visible && !context.SelectedResources[ 0 ].HasProp("IsNonExportable");
}
}
public class RemoveContactFromAddressBookAction: IAction
{
public void Execute( IActionContext context )
{
ContactManager.RemoveContactFromAddressBook( context.SelectedResources[ 0 ], context.ListOwnerResource );
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
presentation.Visible = (context.ListOwnerResource != null) &&
(context.ListOwnerResource.Type == "AddressBook");
presentation.Enabled = presentation.Visible && !context.ListOwnerResource.HasProp( "IsNonExportable" ) &&
(context.SelectedResources.Count == 1);
}
}
#endregion AdressBooks
public class DisplayMailsForEmailAccount : IAction
{
public void Execute( IActionContext context )
{
if( context.SelectedResources.Count == 0 ) return;
Core.UIManager.BeginUpdateSidebar();
Core.TabManager.CurrentTabId = Core.TabManager.Tabs [0].Id;
Core.UIManager.EndUpdateSidebar();
IResource mailAccount = context.SelectedResources[0];
IResourceList resourceList = mailAccount.GetLinksOfTypeLive( null, Core.ContactManager.Props.LinkEmailAcctFrom );
IResourceList resourceListTo = mailAccount.GetLinksOfTypeLive( null, Core.ContactManager.Props.LinkEmailAcctTo );
IResourceList resourceListCC = mailAccount.GetLinksOfTypeLive( null, Core.ContactManager.Props.LinkEmailAcctCC );
resourceList = resourceList.Union( resourceListTo );
resourceList = resourceList.Union( resourceListCC );
ResourceTypeHelper.ExcludeUnloadedPluginResources( resourceList );
ResourceListDisplayOptions options = new ResourceListDisplayOptions();
options.Caption = "Messages for " + mailAccount.DisplayName;
options.SetTransientContainer( Core.ResourceTreeManager.ResourceTreeRoot,
StandardViewPanes.ViewsCategories );
Core.ResourceBrowser.DisplayResourceList( null, resourceList, options );
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
presentation.Visible = (context.SelectedResources.Count == 1);
}
}
public class CopyEmailAccountAction: ActionOnSingleResource
{
public override void Execute( IActionContext context )
{
try
{
Clipboard.SetDataObject( context.SelectedResources [0].GetStringProp( "EmailAddress" ) );
}
catch( ExternalException ex )
{
MessageBox.Show( Core.MainWindow, "Failed to copy email account to clipboard: " + ex.Message,
Core.ProductFullName, MessageBoxButtons.OK, MessageBoxIcon.Error );
}
}
}
public class MailToContactAction : IAction
{
static private IEmailService _emailService;
static private bool _init;
static private IEmailService GetEmailService()
{
if ( !_init )
{
_init = true;
_emailService = (IEmailService) Core.PluginLoader.GetPluginService( typeof( IEmailService ) );
}
return _emailService;
}
public void Execute( IActionContext context )
{
Tracer._Trace( "Execute action: MailToContactAction" );
IResource contact = context.SelectedResources[ 0 ];
if ( contact.Type == "Contact" || contact.Type == "ContactName" )
{
string body = null;
bool greeting = Core.SettingStore.ReadBool( "MailFormat", "GreetingInReplies", false );
if ( context.SelectedResources.Count == 1 && greeting )
{
body += "Hello " + contact.DisplayName + ",\r\n\r\n";
}
GetEmailService().CreateEmail( null, body, EmailBodyFormat.PlainText, context.SelectedResources, null, true );
}
else if ( contact.Type.ToLower().Equals( "emailaccount" ) )
{
GetEmailService().CreateEmail( null, null, EmailBodyFormat.PlainText, context.SelectedResources, null, true );
}
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
presentation.Visible = (GetEmailService() != null) && (context.SelectedResources.Count > 0);
presentation.Enabled = presentation.Visible && context.SelectedResources[ 0 ].HasProp( "EmailAcct" );
}
}
public class CleanUnusedContactsAction : IAction
{
public void Execute(IActionContext context)
{
IResource resource = context.SelectedResources[0];
Core.UIManager.RunWithProgressWindow("Deleting Contacts…", delegate { ExecuteMarshaller(resource); });
}
public void Update( IActionContext context, ref ActionPresentation presentation )
{
presentation.Visible = IsContactsTab() &&
(context.Instance == Core.LeftSidebar.DefaultViewPane) &&
(context.SelectedResources.Count == 1 ) &&
(context.SelectedResources[ 0 ].Type == FilterManagerProps.ViewResName);
}
private static bool IsContactsTab()
{
IResourceTypeTab tab = Core.TabManager.CurrentTab;
return (tab.Name == "Contacts");
}
private static void ExecuteMarshaller( IResource res )
{
Core.ResourceBrowser.BeginUpdate();
Core.ProgressWindow.UpdateProgress( 0, "Deleting Contacts...", "" );
Core.ResourceAP.RunJob( new ReenteringContactsDeleter( res ) );
Core.ResourceBrowser.EndUpdate();
}
}
internal class ReenteringContactsDeleter : ReenteringEnumeratorJob
{
readonly IResource SavedView;
IntArrayList ResourceIds;
int Index, Percent;
internal ReenteringContactsDeleter( IResource view )
{
SavedView = view;
}
public override string Name
{
get { return "Performing cleaning of contacts"; }
}
public override void EnumerationStarting()
{
IResourceList inView = Core.FilterEngine.ExecView( SavedView );
inView = inView.Intersect( Core.ResourceBrowser.FilterResourceList, true );
ResourceIds = new IntArrayList( inView.ResourceIds );
Percent = Index = 0;
}
public override void EnumerationFinished() {}
public override AbstractJob GetNextJob()
{
// test anchor.
if( Index >= ResourceIds.Count )
return null;
int newPercent = Index * 100 / ResourceIds.Count;
if( newPercent != Percent )
{
if( Core.ProgressWindow != null )
Core.ProgressWindow.UpdateProgress( newPercent, "Deleting Contacts...", "" );
Percent = newPercent;
}
IResource res = Core.ResourceStore.TryLoadResource( ResourceIds[ Index++ ] );
if( IsContactUseless( res ) )
{
return new DelegateJob( new ResourceDelegate( ContactManager.DeleteContactImpl ), new object[] { res } );
}
return GetNextJob();
}
///
/// Contact is "useless" if it is not linked to any correspondence
/// resource via "From", "To" or "CC" links and does not belong to any
/// non-exportable (that is not a user's or non-modifiable directly)
/// address book.
///
private static bool IsContactUseless( IResource res )
{
if ( res != null )
{
int count = ContactManager.LinkedCorrespondence( res ).Count;
if( count == 0 )
{
IResource addrBook = res.GetLinkProp( "InAddressBook" );
if( addrBook == null || !addrBook.HasProp( "IsNonExportable" ) )
return true;
}
}
return false;
}
}
}