///
/// 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.ComponentModel;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using JetBrains.Interop.WinApi;
using JetBrains.Interop.WinApi.Wrappers;
using JetBrains.Omea.GUIControls;
using JetBrains.Omea.OpenAPI;
using Timer=System.Windows.Forms.Timer;
namespace JetBrains.Omea
{
///
/// Summary description for SplashScreen.
///
public class SplashScreen : Form, IProgressWindow
{
///
/// A handle to the unmanaged pre-splash screen window, if one was shown by the unmanaged Launcher stage. Should be discarded by the managed splash.
///
private IntPtr _hwndUnmanagedPreSplash;
private const int _BaseHeight = 300;
private PictureBox _pictureBox1;
private ProgressBar _progressBar;
private JetLinkLabel _timeLabel;
private JetLinkLabel _messageLabel;
private Panel _loadingErrors;
private int _startTickCount;
private string _lastTimeMessage;
///
/// Required designer variable.
///
private Container components = null;
private Timer _timerRemoveUnmanagedPreSplash;
internal event EventHandler OnFirstShow;
///
/// Creates the splash-and-progress window.
///
///
/// A handle to the unmanaged pre-splash screen window, if one was shown by the unmanaged Launcher stage. Should be discarded by the managed splash.
///
public SplashScreen(IntPtr hwndUnmanagedPreSplash)
{
_hwndUnmanagedPreSplash = hwndUnmanagedPreSplash;
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Positioning
SetStartWindowPos();
Text = "Starting " + Core.ProductFullName;
#if READER
_pictureBox1.Image = Image.FromStream(
Assembly.GetExecutingAssembly().GetManifestResourceStream( "OmniaMea.Icons.SplashScreenReader.png" ) );
#else
_pictureBox1.Image = Image.FromStream(
Assembly.GetExecutingAssembly().GetManifestResourceStream( "OmniaMea.Icons.splash_pro.png" ) );
#endif
CreateHandle();
Refresh();
}
///
/// By default, the window is positioned in the “center-screen” fashion.
/// If the unmanaged pre-splash was available, use its location as the initial one.
///
private unsafe void SetStartWindowPos()
{
try
{
if(_hwndUnmanagedPreSplash == IntPtr.Zero)
return;
if(User32Dll.IsWindow((void*)_hwndUnmanagedPreSplash) == 0)
return; // Dead
Location = User32Dll.Helpers.GetWindowRect(_hwndUnmanagedPreSplash).Location;
StartPosition = FormStartPosition.Manual;
}
catch(Exception ex)
{
MessageBox.Show(new Win32Window(_hwndUnmanagedPreSplash), string.Format("Could not get the window coordinates. {0}", ex.Message), "JetBrains Omea", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}
protected override unsafe void WndProc(ref Message m)
{
switch((WindowsMessages)m.Msg)
{
case WindowsMessages.WM_ERASEBKGND:
var hdc = (void*)m.WParam;
RECT rcClient = ClientRectangle;
User32Dll.FillRect(hdc, &rcClient, Gdi32Dll.GetStockObject((int)StockLogicalObjects.WHITE_BRUSH));
m.Result = (IntPtr)1; // Did it
return;
}
base.WndProc(ref m);
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(SplashScreen));
this._pictureBox1 = new System.Windows.Forms.PictureBox();
this._messageLabel = new JetBrains.Omea.GUIControls.JetLinkLabel();
this._progressBar = new System.Windows.Forms.ProgressBar();
this._timeLabel = new JetBrains.Omea.GUIControls.JetLinkLabel();
this._timerRemoveUnmanagedPreSplash = new Timer();
components = new Container();
components.Add(_timerRemoveUnmanagedPreSplash);
this.SuspendLayout();
//
// _pictureBox1
//
this._pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill;
this._pictureBox1.Location = new System.Drawing.Point(0, 0);
this._pictureBox1.Name = "_pictureBox1";
this._pictureBox1.Size = new System.Drawing.Size(400, 300);
this._pictureBox1.TabIndex = 0;
this._pictureBox1.TabStop = false;
this._pictureBox1.Paint += new PaintEventHandler(SplashScreen_Paint);
//
// _progressBar
//
this._progressBar.Location = new System.Drawing.Point(24, 240);
this._progressBar.Name = "_progressBar";
this._progressBar.Size = new System.Drawing.Size(348, 16);
this._progressBar.TabIndex = 2;
//
// _messageLabel
//
this._messageLabel.AutoSize = false;
this._messageLabel.BackColor = System.Drawing.SystemColors.Window;
this._messageLabel.ClickableLink = false;
this._messageLabel.Cursor = System.Windows.Forms.Cursors.Default;
this._messageLabel.EndEllipsis = true;
this._messageLabel.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(204)));
this._messageLabel.ForeColor = System.Drawing.Color.Black;
this._messageLabel.Location = new System.Drawing.Point(26, 222);
this._messageLabel.Name = "_messageLabel";
this._messageLabel.Size = new System.Drawing.Size(344, 20);
this._messageLabel.TabIndex = 1;
//
// _timeLabel
//
this._timeLabel.AutoSize = false;
this._timeLabel.BackColor = System.Drawing.SystemColors.Window;
this._timeLabel.ClickableLink = false;
this._timeLabel.Cursor = System.Windows.Forms.Cursors.Default;
this._timeLabel.EndEllipsis = true;
this._timeLabel.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(204)));
this._timeLabel.ForeColor = System.Drawing.Color.Black;
this._timeLabel.Location = new System.Drawing.Point(26, 260);
this._timeLabel.Name = "_timeLabel";
this._timeLabel.Size = new System.Drawing.Size(344, 20);
this._timeLabel.TabIndex = 3;
// Timer
_timerRemoveUnmanagedPreSplash.Interval = (int)TimeSpan.FromSeconds(.3).TotalMilliseconds;
_timerRemoveUnmanagedPreSplash.Enabled = true;
_timerRemoveUnmanagedPreSplash.Tick += OnTimerRemoveUnmanagedPreSplash;
//
// SplashScreen
//
this.AutoScaleMode = AutoScaleMode.None;
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(400, 300);
this.Name = "SplashScreen";
this.BackColor = Color.White;
this.Controls.Add(this._timeLabel);
this.Controls.Add(this._progressBar);
this.Controls.Add(this._messageLabel);
this.Controls.Add(this._pictureBox1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Omea Splash Screen";
this.VisibleChanged += new System.EventHandler(this.ProgressWindow_VisibleChanged);
this.Icon = LoadIcon();
this.ResumeLayout(false);
}
///
/// Time to remove the unmanaged splash.
/// Note: we won't do it on , as the window is not responding for some time after that (loading netfx, jitting, so on), so let's wait for messages to pump and then remove the pre-splash.
///
private void OnTimerRemoveUnmanagedPreSplash(object sender, EventArgs e)
{
if((!IsHandleCreated) || (!Visible))
return; // Not ready yet
_timerRemoveUnmanagedPreSplash.Stop(); // Needed no more; reentrancy OK
// Cause the managed splash to appear onscreen
Refresh();
User32Dll.Helpers.SetLayeredWindowAttributes(this, Color.Empty, 0.9, SetLayeredWindowAttributesFlags.LWA_ALPHA);
// Remove the unmanaged pre-splash
if(_hwndUnmanagedPreSplash != IntPtr.Zero)
{
User32Dll.Helpers.DestroyWindow(_hwndUnmanagedPreSplash);
_hwndUnmanagedPreSplash = IntPtr.Zero;
}
}
private static Icon LoadIcon()
{
#if !READER
return MainFrame.LoadIconFromAssembly("App.ico");
#else
return MainFrame.LoadIconFromAssembly( "AppReader.ico" );
#endif
}
#endregion
private void SplashScreen_Paint(object sender, PaintEventArgs e)
{
base.OnPaint( e );
if (_loadingErrors != null)
{
Graphics g = e.Graphics;
var track = new Point[4];
track[0].X = 0; track[0].Y = _BaseHeight;
track[1].X = 0; track[1].Y = _pictureBox1.Height - 1;
track[2].X = _pictureBox1.Width - 1; track[2].Y = _pictureBox1.Height - 1;
track[3].X = _pictureBox1.Width - 1; track[3].Y = _BaseHeight;
g.DrawLines(new Pen(Color.Gray), track);
g.FillRectangle(new SolidBrush(Color.White), 1, _BaseHeight, _pictureBox1.Width - 2, _pictureBox1.Height - _BaseHeight - 2);
}
}
/**
* When the form is first shown, invokes a delegate to start the indexing
* process after the form becomes visible.
*/
private void ProgressWindow_VisibleChanged(object sender, EventArgs e)
{
VisibleChanged -= ProgressWindow_VisibleChanged;
BeginInvoke( new MethodInvoker( FirstShow ) );
}
/**
* After the form is shown, starts the index building process.
*/
private void FirstShow()
{
ResetElapsedTime();
if ( OnFirstShow != null )
{
OnFirstShow( this, EventArgs.Empty );
}
// Normally return only if there were no erros during the startup.
// Otherwise, display little link label which allows to continue the
// application.
if (_loadingErrors == null)
{
DialogResult = DialogResult.OK;
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ClassStyle |= (int)WindowClassStyles.CS_DROPSHADOW;
cp.ExStyle |= (int)WindowExStyles.WS_EX_LAYERED;
return cp;
}
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
// Make the window translucent
// That's the easiest way of making the system repaint our window for us
// We don't use the Opacity property, as it'd throw from time to time
// Initially, the translucent window appears black onscreen, that's why we make it 100% transparent
// When the painting is ready, we'd make it more opaque, see ontimer
User32Dll.Helpers.SetLayeredWindowAttributes(this, Color.Empty, 0, SetLayeredWindowAttributesFlags.LWA_ALPHA);
}
#region UpdateProgress
private delegate void UpdateProgressDelegate( int percentage, string message, string timeMessage );
public void ResetElapsedTime()
{
_startTickCount = Environment.TickCount;
}
public void UpdateProgress(int percentage, string message, string timeMessage)
{
#region Preconditions
if ( percentage < 0 )
throw new ArgumentException( "Percent must be non-negative" );
if ( percentage > 100 )
throw new ArgumentException( "Percent must be > 100" );
if ( message == null )
throw new ArgumentNullException( "message", "Progress message must be valid string (not null)" );
#endregion Preconditions
if ( !Core.UserInterfaceAP.IsOwnerThread )
{
Core.UserInterfaceAP.QueueJob( JobPriority.Immediate, "Update Progress",
new UpdateProgressDelegate( UpdateProgress ), percentage, message, timeMessage );
return;
}
_messageLabel.Text = message;
_progressBar.Value = percentage;
_lastTimeMessage = timeMessage;
UpdateElapsedTime();
Application.DoEvents();
}
private void UpdateElapsedTime()
{
int secs = (Environment.TickCount - _startTickCount) / 1000;
string elapsedTime = "Elapsed time: " + String.Format("{0}:{1:d2}", secs / 60, secs % 60);
if( !string.IsNullOrEmpty( _lastTimeMessage ) )
{
elapsedTime += ", " + _lastTimeMessage;
}
_timeLabel.Text = elapsedTime;
}
#endregion UpdateProgress
}
}