/// /// 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; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using JetBrains.Omea.Contacts; using JetBrains.Omea.OpenAPI; using JetBrains.DataStructures; namespace JetBrains.Omea.ContactsPlugin { public class ContactView: AbstractEditPane, IContactTabBlockContainer { private System.ComponentModel.Container components = null; private TabControl _contentPages; private readonly Dictionary _pages = new Dictionary(); private readonly ArrayList _contactBlocks = new ArrayList(); private readonly HashMap _blockValidationErrors = new HashMap(); private ContactBO _contact; public ContactView() { InitializeComponent(); ContactService.GetInstance().CreateContactBlocks( this ); } /// /// Clean up any resources being used. /// protected override void Dispose( bool disposing ) { if( disposing ) { foreach( AbstractContactViewBlock block in _contactBlocks ) { block.ValidStateChanged -= block_ValidStateChanged; block.SizeChanged -= OnBlockSizeChanged; } if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Component Designer generated code /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { _contentPages = new TabControl(); this.SuspendLayout(); // // _contentPages // _contentPages.Dock = DockStyle.Fill; _contentPages.Name = "_contentPages"; // // ContactView // this.Controls.Add(this._contentPages); this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(204))); this.Name = "ContactView"; this.Size = new System.Drawing.Size(600, 336); this.ResumeLayout(false); } #endregion void IContactTabBlockContainer.AddContactBlock( string tabName, string caption, AbstractContactViewBlock block ) { TabPage page; if( _pages.ContainsKey( tabName )) page = _pages[ tabName ]; else { page = new TabPage( tabName ); page.AutoScroll = true; _contentPages.TabPages.Add( page ); _pages[ tabName ] = page; } block.Location = new Point(4, 12); block.Dock = DockStyle.Fill; // Decorative panes delimit groupboxes from each other so that // the bottom of the upper box is not visually merged with the // top of the lower one. Panel decorativeShift = new Panel(); decorativeShift.Dock = DockStyle.Top; decorativeShift.Size = new Size( block.Width + 8, 10 ); page.Controls.Add( decorativeShift ); page.Controls.SetChildIndex( decorativeShift, 0 ); GroupBox blockGroupBox = new GroupBox(); blockGroupBox.Dock = DockStyle.Top; blockGroupBox.Text = caption; blockGroupBox.Size = new Size(block.Width + 8, block.Height + (int)(20 * Core.ScaleFactor.Height)); blockGroupBox.FlatStyle = FlatStyle.System; blockGroupBox.Controls.Add(block); page.Controls.Add( blockGroupBox ); page.Controls.SetChildIndex( blockGroupBox, 0 ); _contactBlocks.Add( block ); block.ValidStateChanged += block_ValidStateChanged; block.SizeChanged += OnBlockSizeChanged; } /** * Propagates a valid state change notification from one of the contact blocks * to the entire pane. */ private void block_ValidStateChanged( object sender, ValidStateEventArgs e ) { if ( e.IsValid ) { _blockValidationErrors.Remove( sender ); if ( _blockValidationErrors.Count > 0 ) { IEnumerator errEnumerator = _blockValidationErrors.GetEnumerator(); errEnumerator.MoveNext(); HashMap.Entry entry = ( HashMap.Entry ) errEnumerator.Current; OnValidStateChanged( (ValidStateEventArgs)entry.Value ); } else { OnValidStateChanged( new ValidStateEventArgs( true ) ); } } else { _blockValidationErrors[ sender ] = e; OnValidStateChanged( e ); } } /** * When the size of a block changes, shifts blocks below it up or down. */ private void OnBlockSizeChanged( object sender, EventArgs e ) { if( Width != 0 && Height != 0 ) { AbstractContactViewBlock senderBlock = (AbstractContactViewBlock) sender; GroupBox blockGroupBox = (GroupBox) senderBlock.Parent; blockGroupBox.Height = senderBlock.Height + (int)(20 * Core.ScaleFactor.Height); } } public override void EditResource( IResource res ) { _contact = new ContactBO( res ); foreach( AbstractContactViewBlock block in _contactBlocks ) { block.EditResource( _contact.Resource ); } } public override void Save() { // _contact.Changed ALWAYS returns false in the current // implementation because although we create a ContacBO for // a resource here, we pass its resource object to all blocks, // thus all changes are missed from change-control mechanism of // ContactBO (IContact) interface. Thus we have to rely on // IsChanged method of each block and artificially call text // indexing. bool contentChanged = _contact.Changed; foreach( AbstractContactViewBlock block in _contactBlocks ) contentChanged = contentChanged || block.IsChanged(); Core.ResourceAP.RunJob( new MethodInvoker( SaveContact ) ); } public override void Cancel() { Core.ResourceAP.RunJob( new MethodInvoker( CancelContactBlocks ) ); } private void SaveContact() { // Special attention is payed to the asynchronous nature // of this Form per se. In some contexts, saving of the // resource can be activated (not queued) after the actual // resource is deleted. We consider that simple check whether // the resource to be saved is ALIVE is enough if( !_contact.Resource.IsDeleted && !_contact.Resource.IsDeleting ) { _contact.BeginUpdate(); foreach( AbstractContactViewBlock block in _contactBlocks ) { block.Save(); } _contact.EndUpdate(); // NB: IContact.EndUpdate checks whether the body has // changed and automatically requests TI. // Core.TextIndexManager.QueryIndexing( _contact.ID ); } } private void CancelContactBlocks() { if( !_contact.Resource.IsDeleted && !_contact.Resource.IsDeleting ) { _contact.Resource.BeginUpdate(); foreach( AbstractContactViewBlock block in _contactBlocks ) { block.Cancel(); } _contact.Resource.EndUpdate(); } } } }