///
/// 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;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using JetBrains.Omea.OpenAPI;
namespace JetBrains.Omea.SamplePlugins.SccPlugin
{
///
/// The main plugin class.
///
public class SccPlugin: IPlugin, IResourceDisplayer, IResourceIconProvider
{
private static IStatusWriter _statusWriter;
private Icon _changeSetIcon;
private Icon _openFolderIcon;
private Icon _closedFolderIcon;
private Icon _repositoryIcon;
private static List _repositoryTypes;
private static IResourceTreePane _folderTreePane;
public static IStatusWriter StatusWriter
{
get { return _statusWriter; }
}
public static IEnumerable RepositoryTypes
{
get { return _repositoryTypes; }
}
public static IResourceTreePane FolderTreePane
{
get { return _folderTreePane; }
}
public void Register()
{
_repositoryTypes = new List {new P4RepositoryType(), new SvnRepositoryType()};
Props.Register( this );
// delete remnants of old version of the plugin
if ( Core.ResourceStore.ResourceTypes.Exist( "jetbrains.p4.ChangeSet" ) )
{
Core.ResourceStore.GetAllResources( "jetbrains.p4.ChangeSet" ).DeleteAll();
Core.ResourceStore.ResourceTypes.Delete( "jetbrains.p4.ChangeSet" );
}
if ( Core.ResourceStore.ResourceTypes.Exist( "jetbrains.p4.Folder" ) )
{
Core.ResourceStore.GetAllResources( "jetbrains.p4.Folder" ).DeleteAll();
Core.ResourceStore.ResourceTypes.Delete( "jetbrains.p4.Folder" );
}
if ( Core.ResourceStore.ResourceTypes.Exist( "jetbrains.p4.FileChange" ) )
{
Core.ResourceStore.GetAllResources( "jetbrains.p4.FileChange" ).DeleteAll();
Core.ResourceStore.ResourceTypes.Delete( "jetbrains.p4.FileChange" );
}
Core.TabManager.RegisterResourceTypeTab( "SCC", "SCC", Props.ChangeSetResource, 80 );
Core.DisplayColumnManager.RegisterDisplayColumn( Props.ChangeSetResource, 0,
new ColumnDescriptor( "From", 150 ) );
Core.DisplayColumnManager.RegisterDisplayColumn( Props.ChangeSetResource, 1,
new ColumnDescriptor( "Subject", 300 ) );
Core.DisplayColumnManager.RegisterDisplayColumn( Props.ChangeSetResource, 2,
new ColumnDescriptor( "Date", 120 ) );
// Register a standard tree pane for showing the folder structure of the Perforce
// repository and the changesets in each folder.
_folderTreePane = Core.LeftSidebar.RegisterResourceStructureTreePane( "SccFolders",
"SCC", "Folders", null,
Props.RepositoryResource );
_folderTreePane.ToolTipCallback = GetRepositoryToolTip;
// Set sorting by name for the folder structure
IResource folderRoot = Core.ResourceTreeManager.GetRootForType( Props.RepositoryResource );
Core.ResourceTreeManager.SetResourceNodeSort( folderRoot, "Name" );
// Register a custom sidebar pane for showing the list of Perforce developers and
// the changesets done by each developer.
Core.LeftSidebar.RegisterViewPane( "SccDevelopers", "SCC", "Developers", null,
new SccDeveloperPane() );
Core.PluginLoader.RegisterResourceUIHandler( Props.FolderResource, new SccFolderUIHandler() );
Core.PluginLoader.RegisterResourceDisplayer( Props.ChangeSetResource, this );
Core.PluginLoader.RegisterResourceTextProvider( Props.ChangeSetResource,
new ChangeSetTextProvider() );
Core.UIManager.RegisterOptionsGroup( "Development",
"The Development options pane controls different development-related plugins" );
Core.UIManager.RegisterOptionsPane( "Development", "Source Control", new OptionsPaneCreator( CreateSccOptionsPane ),
"The Source Control options pane specifies the source control repositories monitored by the SCC plugin" );
Core.UIManager.RegisterWizardPane( "Source Control", new OptionsPaneCreator( CreateSccOptionsPane ), 15 );
_statusWriter = Core.UIManager.GetStatusWriter( this, StatusPane.Network );
Assembly iconAssembly = Assembly.GetExecutingAssembly();
_changeSetIcon = new Icon( iconAssembly.GetManifestResourceStream( "SccPlugin.changeset.ico" ) );
_openFolderIcon = new Icon( iconAssembly.GetManifestResourceStream( "SccPlugin.OpenFolder.ico" ) );
_closedFolderIcon = new Icon( iconAssembly.GetManifestResourceStream( "SccPlugin.ClosedFolder.ico" ) );
_repositoryIcon = new Icon( iconAssembly.GetManifestResourceStream( "SccPlugin.repository.ico" ) );
Core.ResourceIconManager.RegisterResourceIconProvider(
new string[] { Props.ChangeSetResource, Props.FolderResource, Props.RepositoryResource }, this );
// Allow creating rules which affect resources of type ChangeSet
Core.FilterEngine.RegisterRuleApplicableResourceType( Props.ChangeSetResource );
// Register display columns for multiline view
Core.DisplayColumnManager.RegisterMultiLineColumn( Props.ChangeSetResource,
Core.ContactManager.Props.LinkFrom,
0, 0, 0, 120,
MultiLineColumnFlags.AnchorLeft | MultiLineColumnFlags.AnchorRight,
SystemColors.WindowText, HorizontalAlignment.Left );
Core.DisplayColumnManager.RegisterMultiLineColumn( Props.ChangeSetResource,
Core.Props.Date,
0, 0, 120, 80,
MultiLineColumnFlags.AnchorRight,
SystemColors.WindowText, HorizontalAlignment.Right );
Core.DisplayColumnManager.RegisterMultiLineColumn( Props.ChangeSetResource,
Core.Props.Subject,
1, 1, 0, 144,
MultiLineColumnFlags.AnchorLeft | MultiLineColumnFlags.AnchorRight,
Color.FromArgb( 112, 112, 112 ), HorizontalAlignment.Left );
Core.ActionManager.RegisterMainMenuActionGroup( "SendReceiveActions", "Tools", ListAnchor.First );
Core.ActionManager.RegisterMainMenuAction( new SynchronizeRepositoriesAction(), "SendReceiveActions",
ListAnchor.Last, "Synchronize Repositories", null, null, null );
Core.ActionManager.RegisterActionComponent( new DeleteRepositoryAction(), "Delete",
Props.RepositoryResource, null );
Core.ActionManager.RegisterActionComponent( new SynchronizeRepositoryAction(), "Refresh",
Props.RepositoryResource, null );
Core.ActionManager.RegisterContextMenuActionGroup( "PropertiesActions", ListAnchor.Last );
Core.ActionManager.RegisterContextMenuAction( new ToggleShowSubfolderContentsAction(),
"PropertiesActions", ListAnchor.First,
"Show subfolder contents", null, Props.FolderResource, null );
Core.ActionManager.RegisterContextMenuAction( new RepositoryPropertiesAction(), "PropertiesActions", ListAnchor.First,
"Properties...", null, Props.RepositoryResource, null );
}
public void Startup()
{
QueueSynchronizeRepositories();
}
public void Shutdown()
{
}
///
/// Performs initial or timed poll of the Perforce repository to get information about
/// new changesets.
///
private static void TimedSynchronizeRepositories()
{
SynchronizeRepositories();
QueueSynchronizeRepositories();
}
internal static void SynchronizeRepositories()
{
foreach( IResource res in Core.ResourceStore.GetAllResources( Props.RepositoryResource ) )
{
_statusWriter.ShowStatus( "Updating repository " + res.DisplayName + "..." );
if ( Core.ProgressWindow != null )
{
Core.ProgressWindow.UpdateProgress( 0, "Updating repository " + res.DisplayName + "...", null );
}
RepositoryType repType = GetRepositoryType( res );
if ( repType != null )
{
repType.UpdateRepository( res );
}
}
_statusWriter.ClearStatus();
}
private static void QueueSynchronizeRepositories()
{
if ( Settings.PollInterval != 0 )
{
Core.NetworkAP.QueueJobAt( DateTime.Now.AddMinutes( Settings.PollInterval ),
"Updating Perforce repositories",
new MethodInvoker( TimedSynchronizeRepositories ) );
}
}
public IDisplayPane CreateDisplayPane( string resourceType )
{
if ( resourceType == Props.ChangeSetResource )
{
return new ChangeSetDisplayPane();
}
return null;
}
public Icon GetResourceIcon( IResource resource )
{
if ( resource.Type == Props.ChangeSetResource )
{
return _changeSetIcon;
}
if ( resource.Type == Props.FolderResource )
{
if ( resource.GetIntProp( Core.Props.Open ) == 1 )
{
return _openFolderIcon;
}
return _closedFolderIcon;
}
if ( resource.Type == Props.RepositoryResource )
{
return _repositoryIcon;
}
return null;
}
public Icon GetDefaultIcon( string resType )
{
if ( resType == Props.ChangeSetResource )
{
return _changeSetIcon;
}
if ( resType == Props.FolderResource )
{
return _closedFolderIcon;
}
return null;
}
private static AbstractOptionsPane CreateSccOptionsPane()
{
return new SccOptionsPane();
}
public static RepositoryType GetRepositoryType( string id )
{
return _repositoryTypes.Find(type => type.Id == id);
}
public static RepositoryType GetRepositoryType( IResource repository )
{
return GetRepositoryType(repository.GetProp(Props.RepositoryType));
}
private static string GetRepositoryToolTip( IResource res )
{
return res.GetProp( Props.LastError );
}
}
///
/// Accessors for the plugin settings.
///
internal class Settings
{
internal static int ChangeSetsToIndex
{
get { return Core.SettingStore.ReadInt( "SccPlugin", "ChangeSetsToIndex", 100 ); }
set { Core.SettingStore.WriteInt( "SccPlugin", "ChangeSetsToIndex", value ); }
}
internal static int PollInterval
{
get { return Core.SettingStore.ReadInt( "SccPlugin", "PollInterval", 5 ); }
set { Core.SettingStore.WriteInt( "SccPlugin", "PollInterval", value ); }
}
internal static bool HideUnchangedFiles
{
get { return Core.SettingStore.ReadBool( "SccPlugin", "HideUnchangedFiles", false ); }
set { Core.SettingStore.WriteBool( "SccPlugin", "HideUnchangedFiles", value ); }
}
}
///
/// The user interface handler for the SccFolder resource type.
///
internal class SccFolderUIHandler: IResourceUIHandler
{
public void ResourceNodeSelected( IResource res )
{
ResourceListDisplayOptions options = new ResourceListDisplayOptions();
IResource repository = res;
while( repository != null && repository.Type != Props.RepositoryResource )
{
repository = repository.GetLinkProp( Core.Props.Parent );
}
if ( repository != null && repository.HasProp( Props.LastError ) )
{
options.StatusLine = repository.GetProp( Props.LastError );
}
options.CaptionTemplate = "Changes in %OWNER%";
IResourceList resourceList;
if ( !res.HasProp( Props.ShowSubfolderContents ) )
{
resourceList = res.GetLinksOfType( Props.ChangeSetResource, Props.AffectsFolder );
}
else
{
resourceList = Core.ResourceStore.EmptyResourceList;
resourceList = GatherContentsOfSubfolders( res, resourceList );
}
Core.ResourceBrowser.DisplayResourceList( res, resourceList, options );
}
private static IResourceList GatherContentsOfSubfolders( IResource folder, IResourceList changesets )
{
changesets = changesets.Union( folder.GetLinksOfType( Props.ChangeSetResource, Props.AffectsFolder ), true );
foreach( IResource subfolder in folder.GetLinksTo( Props.FolderResource, Core.Props.Parent ) )
{
changesets = GatherContentsOfSubfolders( subfolder, changesets );
}
return changesets;
}
public bool CanRenameResource( IResource res )
{
return false;
}
public bool ResourceRenamed( IResource res, string newName )
{
return false;
}
public bool CanDropResources( IResource targetResource, IResourceList dragResources )
{
return false;
}
public void ResourcesDropped( IResource targetResource, IResourceList droppedResources )
{
}
}
///
/// Provides the indexable content for changeset resources.
///
internal class ChangeSetTextProvider: IResourceTextProvider
{
public bool ProcessResourceText( IResource res, IResourceTextConsumer consumer )
{
consumer.AddDocumentFragment( res.Id, res.GetPropText( Core.Props.LongBody ) );
consumer.AddDocumentFragment( res.Id, res.GetPropText( Core.ContactManager.Props.LinkFrom ),
DocumentSection.SourceSection );
return true;
}
}
}