///
/// 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.Drawing;
using JetBrains.Omea.Base;
using JetBrains.Omea.GUIControls;
using JetBrains.Omea.OpenAPI;
using JetBrains.Omea.ResourceTools;
namespace JetBrains.Omea.Nntp
{
internal class ArticlePreviewPane: MessageDisplayPane
{
private const string _cPluginIniSection = "NNTP";
private const string _cFontOverride = "NewsArticleFontOverride";
private const string _cFont = "NewsArticleFont";
private const string _cFontSize = "NewsArticleFontSize";
private IResourceList _articleListener;
private System.ComponentModel.Container components = null;
private readonly IResourceBrowser _rbrowser = Core.ResourceBrowser;
private JetLinkLabel _downloadLabel;
private bool _articleIsRedisplayed;
private static string _font;
private static int _fontSize;
///
/// The Web Security Context that displays the News Article preview by default, in the restricted environment.
///
private readonly WebSecurityContext _ctxRestricted;
public ArticlePreviewPane()
{
InitializeComponent();
// Initialize the security context
_ctxRestricted = WebSecurityContext.Restricted;
_ctxRestricted.WorkOffline = false; // Enable downloading of the referenced content
ReadNewsarticleFontAttributes();
Core.UIManager.AddOptionsChangesListener( "Internet", "Newsgroups", ReadNewsarticleFontAttributesHandler);
Core.UIManager.AddOptionsChangesListener( "Omea", "General", ReadNewsarticleFontAttributesHandler);
}
private static void ReadNewsarticleFontAttributesHandler( object sender, EventArgs args )
{
ReadNewsarticleFontAttributes();
Core.ResourceBrowser.RedisplaySelectedResource();
}
private static void ReadNewsarticleFontAttributes()
{
// Initialize font from local settings or from the Core.UIManager.
_font = Core.UIManager.DefaultFontFace;
_fontSize = (int)Core.UIManager.DefaultFontSize;
bool overriden = Core.SettingStore.ReadBool( _cPluginIniSection, _cFontOverride, false );
if( overriden )
{
_font = Core.SettingStore.ReadString( _cPluginIniSection, _cFont, Core.UIManager.DefaultFontFace );
_fontSize = Core.SettingStore.ReadInt( _cPluginIniSection, _cFontSize, (int)Core.UIManager.DefaultFontSize );
}
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Component Designer generated code
private void InitializeComponent()
{
this.SuspendLayout();
//
// ArticlePreviewPane
//
this.Name = "ArticlePreviewPane";
this.Size = new System.Drawing.Size(608, 280);
this.ResumeLayout(false);
}
#endregion
public override void DisplayResource( IResource article, WordPtr[] wordsToHighlight )
{
if( _downloadLabel == null )
{
_downloadLabel = new JetLinkLabel();
_downloadLabel.Text = "Click here or press F5 to download the article";
_downloadLabel.BackColor = Color.Transparent;
_downloadLabel.ClickableLink = true;
_downloadLabel.Click += _downloadLabel_Click;
Controls.Add( _downloadLabel );
}
_downloadLabel.Visible = false;
_ieBrowser.Visible = true;
bool redisplayed = _articleIsRedisplayed;
_articleIsRedisplayed = false;
DisposeArticleListener();
// Set the subject, highlight if needed
ShowSubject( article.GetPropText( Core.Props.Subject ), wordsToHighlight );
try
{
// The content being indexed (plaintext) is not the same that is
// displayed (autogenerated HTML), so the offsets are updated by
// this method to correspond to the new formatting.
string sFormattedArticle = ArticleBody2Html( article, ref wordsToHighlight );
ShowHtml( sFormattedArticle, _ctxRestricted, wordsToHighlight );
}
catch( Exception e )
{
Utils.DisplayException( e, "Error" );
return;
}
/**
* set last selected article for article's owner
*/
IResource owner = _rbrowser.OwnerResource;
if( owner != null && ( owner.Type == NntpPlugin._newsGroup ||
owner.Type == NntpPlugin._newsFolder || owner.Type == NntpPlugin._newsServer ) )
{
ResourceProxy proxy = new ResourceProxy( owner );
proxy.AsyncPriority = JobPriority.AboveNormal;
proxy.SetPropAsync( NntpPlugin._propLastSelectedArticle, article.Id );
}
_articleListener = article.ToResourceListLive();
_articleListener.ResourceChanged += _articleListener_ResourceChanged;
bool hasNoBody = article.HasProp( NntpPlugin._propHasNoBody );
if( !hasNoBody )
{
Core.UIManager.GetStatusWriter( this, StatusPane.Network ).ClearStatus();
}
else
{
if( !Utils.IsNetworkConnectedLight() )
{
ShowHtml( "
" + NntpPlugin._networkUnavailable + ".
", WebSecurityContext.Internet, null );
return;
}
IResourceList groups = article.GetLinksOfType( NntpPlugin._newsGroup, NntpPlugin._propTo );
if( groups.Count > 0 )
{
foreach( IResource groupRes in groups.ValidResources )
{
IResource server = new NewsgroupResource( groupRes ).Server;
if( server != null )
{
ServerResource serverRes = new ServerResource( server );
if( redisplayed || serverRes.DownloadBodiesOnDeliver || serverRes.DownloadBodyOnSelection )
{
Core.UIManager.GetStatusWriter( this, StatusPane.Network ).ShowStatus( "Downloading article..." );
NntpConnection articlesConnection = NntpConnectionPool.GetConnection( server, "foreground" );
NntpDownloadArticleUnit downloadUnit =
new NntpDownloadArticleUnit( article, groupRes, JobPriority.Immediate, true );
downloadUnit.OnProgress += downloadUnit_OnProgress;
ShowHtml( "Downloading article: 0%
", WebSecurityContext.Internet, null );
articlesConnection.StartUnit( Int32.MaxValue - 1, downloadUnit );
return;
}
}
}
if( !redisplayed )
{
Point location = _ieBrowser.Location;
_downloadLabel.Location = new Point( location.X + 8, location.Y + 8 );
_downloadLabel.Visible = true;
_ieBrowser.Visible = false;
}
}
}
}
public override void EndDisplayResource( IResource res )
{
_downloadLabel.Visible = false;
_ieBrowser.Visible = true;
DisposeArticleListener();
}
public override void DisposePane()
{
_downloadLabel.Visible = false;
DisposeArticleListener();
}
///
/// Extracts the article body.
/// If plaintext, optionally converts it to formatted HTML and maintains the offsets in the latter case.
///
internal static string ArticleBody2Html( IResource article, ref WordPtr[] toHighlight )
{
string formattedText;
if( article.HasProp( NntpPlugin._propHtmlContent ) ) // HTML, don't update offsets
{
formattedText = article.GetPropText( NntpPlugin._propHtmlContent );
formattedText = Core.MessageFormatter.GetFormattedHtmlBody( article, formattedText, ref toHighlight );
}
else // Plaintext, possibly formatted, update offsets
{
formattedText = Core.MessageFormatter.GetFormattedBody( article, Core.Props.LongBody, NntpPlugin._propReply,
ref toHighlight, _font, _fontSize );
}
/**
* Look for X-Newsreader header if formating is enabled
*/
if( !article.HasProp( "NoFormat" ) )
{
string[] headers = article.GetPropText( NntpPlugin._propArticleHeaders ).Split( '\n' );
foreach( string headerLine in headers )
{
if( headerLine.StartsWith( "X-Newsreader: " ) && headerLine.IndexOf( Core.ProductName ) > 0 )
{
formattedText += "Posted by ";
formattedText += headerLine.Substring( "X-Newsreader: ".Length );
formattedText += "
";
break;
}
}
}
/**
* display inline attachments and subtitute embedded ones if any
*/
if( Settings.DisplayAttachmentsInline )
{
IResourceList attachments = article.GetLinksOfType( null, NntpPlugin._propAttachment );
foreach( IResource attachment in attachments )
{
if( attachment.Type == "Picture" )
{
string filename = Core.FileResourceManager.GetSourceFile( attachment );
formattedText += "
";
}
}
}
IResourceList embeddedList = article.GetLinksOfType( null, NntpPlugin._propEmbeddedContent );
foreach( IResource embeddedContent in embeddedList )
{
string contentId = embeddedContent.GetPropText( CommonProps.ContentId );
if( contentId.Length > 0 )
{
string filename = Core.FileResourceManager.GetSourceFile( embeddedContent );
formattedText = formattedText.Replace( "cid:" + contentId, filename );
}
}
return formattedText;
}
private void DisposeArticleListener()
{
if( _articleListener != null )
{
_articleListener.Dispose();
_articleListener = null;
}
}
private void _articleListener_ResourceChanged( object sender, ResourcePropIndexEventArgs e )
{
IResource article = e.Resource;
bool need2Redisplay = false;
foreach( int prop in e.ChangeSet.GetChangedProperties() )
{
if( prop == Core.Props.IsUnread )
{
if( article.HasProp( NntpPlugin._propHasNoBody ) && !article.HasProp( prop ) &&
////////////////////////////////////////////////////////////////////////////////////////////////////////
/// that is the dirtiest hack i ever wrote:
/// we try to recognize by name of current resource job that unread status is changed
/// by ResourceBrowser's timer handler
////////////////////////////////////////////////////////////////////////////////////////////////////////
Core.ResourceAP.CurrentJobName == "Marking resource read by timer" )
{
Core.ResourceAP.QueueJob(
JobPriority.Immediate, new ResourceDelegate( SetResourceUnread ), article );
}
}
if( prop == Core.Props.LongBody ||
prop == NntpPlugin._propHtmlContent ||
prop == Core.Props.Subject ||
prop == NntpPlugin._propHasNoBody ||
prop == Core.ContactManager.Props.LinkFrom ||
prop == Core.FileResourceManager.PropCharset )
{
need2Redisplay = true;
}
}
if( need2Redisplay )
{
RedisplayArticle( article );
}
}
private static void SetResourceUnread( IResource res )
{
res.SetProp( Core.Props.IsUnread, true );
}
internal void RedisplayArticle( IResource res )
{
if( !Core.UserInterfaceAP.IsOwnerThread )
{
Core.UserInterfaceAP.QueueJob( new ResourceDelegate( RedisplayArticle ), res );
}
else
{
if( IsArticleDisplayed( res ) )
{
_articleIsRedisplayed = true;
_rbrowser.RedisplaySelectedResource();
}
}
}
internal bool IsArticleDisplayed( IResource res )
{
try
{
if( _articleListener != null && !res.IsDeleted && _articleListener[ 0 ] == res )
{
return true;
}
}
catch( InvalidResourceIdException ) {}
return false;
}
private void downloadUnit_OnProgress( NntpDownloadArticleUnit sender, string progress )
{
if( !Core.UserInterfaceAP.IsOwnerThread )
{
Core.UserInterfaceAP.QueueJob( new NntpDownloadArticleUnit.ProgressDelegate( downloadUnit_OnProgress ), sender, progress );
}
else
{
IResource article = null;
try
{
if( _articleListener != null )
{
article = _articleListener[ 0 ];
}
if( article != null && article == sender.Article && article.HasProp( NntpPlugin._propHasNoBody ) )
{
ShowHtml( "Downloading article: " + progress + "%
", _ctxRestricted, null );
}
}
catch( InvalidResourceIdException ) {}
}
}
private void _downloadLabel_Click(object sender, EventArgs e)
{
if( _articleListener != null )
{
IResource article = null;
lock( _articleListener )
{
if( _articleListener.Count > 0 )
{
article = _articleListener[ 0 ];
}
}
if( article != null && !article.IsDeleted )
{
RefreshArticleAction.RefreshArticleImpl( article.ToResourceList() );
}
}
}
}
}