// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (c) 2004 - 2006 Novell, Inc.
//
// Authors:
// Peter Bartok pbartok@novell.com
//
// COMPLETE
#undef DebugRunLoop
using Microsoft.Win32;
using System;
using System.Drawing;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
#if NET_2_0
using System.Windows.Forms.VisualStyles;
#endif
namespace System.Windows.Forms {
public sealed class Application {
internal class MWFThread {
#region Fields
private ApplicationContext context;
private bool messageloop_started;
private int thread_id;
private static Hashtable threads = new Hashtable();
#endregion // Fields
#region Constructors
private MWFThread() {
}
#endregion // Constructors
#region Properties
public ApplicationContext Context {
get {
return context;
}
set {
context = value;
}
}
public bool MessageLoop {
get {
return messageloop_started;
}
set {
messageloop_started = value;
}
}
public static int LoopCount {
get {
IEnumerator e;
int loops;
MWFThread thread;
e = threads.Values.GetEnumerator();
loops = 0;
while (e.MoveNext()) {
thread = (MWFThread)e.Current;
if (thread != null && thread.messageloop_started) {
loops++;
}
}
return loops;
}
}
public static MWFThread Current {
get {
MWFThread thread;
thread = null;
lock (threads) {
thread = (MWFThread)threads[Thread.CurrentThread.GetHashCode()];
if (thread == null) {
thread = new MWFThread();
thread.thread_id = Thread.CurrentThread.GetHashCode();
threads[thread.thread_id] = thread;
}
}
return thread;
}
}
#endregion // Properties
#region Methods
public void Exit() {
if (context != null) {
context.ExitThread();
}
context = null;
if (Application.ThreadExit != null) {
Application.ThreadExit(null, EventArgs.Empty);
}
if (LoopCount == 0) {
if (Application.ApplicationExit != null) {
Application.ApplicationExit(null, EventArgs.Empty);
}
}
lock (threads) {
threads[thread_id] = null;
}
}
#endregion // Methods
}
private static bool browser_embedded = false;
private static InputLanguage input_language = InputLanguage.CurrentInputLanguage;
private static string safe_caption_format = "{1} - {0} - {2}";
private static ArrayList message_filters = new ArrayList();
#if NET_2_0
private static VisualStyleState visual_style_state = VisualStyleState.ClientAndNonClientAreasEnabled;
#endif
private Application () {
}
#region Private Methods
private static void CloseForms(Thread thread) {
Control c;
IEnumerator control;
bool all;
#if DebugRunLoop
Console.WriteLine(" CloseForms({0}) called", thread);
#endif
if (thread == null) {
all = true;
} else {
all = false;
}
control = Control.controls.GetEnumerator();
while (control.MoveNext()) {
c = (Control)control.Current;
if (c is Form) {
if (all || (thread == c.creator_thread)) {
if (c.IsHandleCreated) {
XplatUI.PostMessage(c.Handle, Msg.WM_CLOSE_INTERNAL, IntPtr.Zero, IntPtr.Zero);
}
#if DebugRunLoop
Console.WriteLine(" Closing form {0}", c);
#endif
}
}
}
}
#endregion // Private methods
#region Public Static Properties
public static bool AllowQuit {
get {
return browser_embedded;
}
}
public static string CommonAppDataPath {
get {
return Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
}
}
public static RegistryKey CommonAppDataRegistry {
get {
RegistryKey key;
key = Registry.LocalMachine.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
return key;
}
}
public static string CompanyName {
get {
AssemblyCompanyAttribute[] attrs = (AssemblyCompanyAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyCompanyAttribute), true);
if ((attrs != null) && attrs.Length>0) {
return attrs[0].Company;
}
return Assembly.GetEntryAssembly().GetName().Name;
}
}
public static CultureInfo CurrentCulture {
get {
return Thread.CurrentThread.CurrentUICulture;
}
set {
Thread.CurrentThread.CurrentUICulture=value;
}
}
public static InputLanguage CurrentInputLanguage {
get {
return input_language;
}
set {
input_language=value;
}
}
public static string ExecutablePath {
get {
return Assembly.GetEntryAssembly().Location;
}
}
public static string LocalUserAppDataPath {
get {
return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), CompanyName), ProductName), ProductVersion);
}
}
public static bool MessageLoop {
get {
return MWFThread.Current.MessageLoop;
}
}
public static string ProductName {
get {
AssemblyProductAttribute[] attrs = (AssemblyProductAttribute[]) Assembly.GetEntryAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true);
if ((attrs != null) && attrs.Length>0) {
return attrs[0].Product;
}
return Assembly.GetEntryAssembly().GetName().Name;
}
}
public static string ProductVersion {
get {
String version;
version = Assembly.GetEntryAssembly().GetName().Version.ToString();
return version;
}
}
public static string SafeTopLevelCaptionFormat {
get {
return safe_caption_format;
}
set {
safe_caption_format=value;
}
}
public static string StartupPath {
get {
return Path.GetDirectoryName(Application.ExecutablePath);
}
}
public static string UserAppDataPath {
get {
return Path.Combine(Path.Combine(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), CompanyName), ProductName), ProductVersion);
}
}
public static RegistryKey UserAppDataRegistry {
get {
RegistryKey key;
key = Registry.CurrentUser.OpenSubKey("Software\\" + Application.CompanyName + "\\" + Application.ProductName + "\\" + Application.ProductVersion, true);
return key;
}
}
#if NET_2_0
public static bool RenderWithVisualStyles {
get {
if (VisualStyleInformation.IsSupportedByOS)
{
if (!VisualStyleInformation.IsEnabledByUser)
return false;
if (!XplatUI.ThemesEnabled)
return false;
if (Application.VisualStyleState == VisualStyleState.ClientAndNonClientAreasEnabled)
return true;
if (Application.VisualStyleState == VisualStyleState.ClientAreaEnabled)
return true;
}
return false;
}
}
public static VisualStyleState VisualStyleState {
get { return Application.visual_style_state; }
set { Application.visual_style_state = value; }
}
#endif
#endregion
#region Public Static Methods
public static void AddMessageFilter(IMessageFilter value) {
message_filters.Add(value);
}
public static void DoEvents() {
XplatUI.DoEvents();
}
public static void EnableVisualStyles() {
XplatUI.EnableThemes();
}
#if NET_2_0
//
// If true, it uses GDI+, performance reasons were quoted
//
static internal bool use_compatible_text_rendering = true;
public static void SetCompatibleTextRenderingDefault (bool defaultValue)
{
use_compatible_text_rendering = defaultValue;
}
#endif
public static void Exit() {
CloseForms(null);
// FIXME - this needs to be fired when they're all closed
// But CloseForms uses PostMessage, so it gets fired before
// We need to wait on something...
if (ApplicationExit != null) {
ApplicationExit(null, EventArgs.Empty);
}
}
public static void ExitThread() {
CloseForms(Thread.CurrentThread);
MWFThread.Current.Exit();
}
public static ApartmentState OleRequired() {
//throw new NotImplementedException("OLE Not supported by this System.Windows.Forms implementation");
return ApartmentState.Unknown;
}
public static void OnThreadException(Exception t) {
if (Application.ThreadException != null) {
Application.ThreadException(null, new ThreadExceptionEventArgs(t));
return;
}
if (SystemInformation.UserInteractive) {
Form form = new ThreadExceptionDialog (t);
form.ShowDialog ();
} else {
Console.WriteLine (t.ToString ());
}
}
public static void RemoveMessageFilter(IMessageFilter filter) {
message_filters.Remove(filter);
}
public static void Run() {
RunLoop(false, new ApplicationContext());
}
public static void Run(Form mainForm) {
RunLoop(false, new ApplicationContext(mainForm));
}
public static void Run(ApplicationContext context) {
RunLoop(false, context);
}
internal static void RunLoop(bool Modal, ApplicationContext context) {
Queue toplevels;
IEnumerator control;
MSG msg;
Object queue_id;
MWFThread thread;
thread = MWFThread.Current;
msg = new MSG();
if (context == null) {
context = new ApplicationContext();
}
thread.Context = context;
if (context.MainForm != null) {
context.MainForm.is_modal = Modal;
context.MainForm.context = context;
context.MainForm.closing = false;
context.MainForm.Visible = true; // Cannot use Show() or scaling gets confused by menus
// FIXME - do we need this?
//context.MainForm.PerformLayout();
context.MainForm.Activate();
}
#if DebugRunLoop
Console.WriteLine("Entering RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
#endif
if (Modal) {
Control c;
toplevels = new Queue();
control = Control.controls.GetEnumerator();
while (control.MoveNext()) {
c = (Control)control.Current;
if (c is Form && (c != context.MainForm)) {
if (c.IsHandleCreated && XplatUI.IsEnabled(c.Handle)) {
#if DebugRunLoop
Console.WriteLine(" Disabling form {0}", c);
#endif
XplatUI.EnableWindow(c.Handle, false);
toplevels.Enqueue(c);
}
}
}
// FIXME - need activate?
XplatUI.SetModal(context.MainForm.Handle, true);
} else {
toplevels = null;
}
queue_id = XplatUI.StartLoop(Thread.CurrentThread);
thread.MessageLoop = true;
while (XplatUI.GetMessage(queue_id, ref msg, IntPtr.Zero, 0, 0)) {
if ((message_filters != null) && (message_filters.Count > 0)) {
Message m;
bool drop;
drop = false;
m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
for (int i = 0; i < message_filters.Count; i++) {
if (((IMessageFilter)message_filters[i]).PreFilterMessage(ref m)) {
// we're dropping the message
drop = true;
break;
}
}
if (drop) {
continue;
}
}
switch((Msg)msg.message) {
case Msg.WM_KEYDOWN:
case Msg.WM_SYSKEYDOWN:
case Msg.WM_CHAR:
case Msg.WM_SYSCHAR:
case Msg.WM_KEYUP:
case Msg.WM_SYSKEYUP: {
Message m;
Control c;
m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam);
c = Control.FromHandle(msg.hwnd);
if ((c != null) && !c.PreProcessMessage(ref m)) {
goto default;
}
break;
}
default: {
XplatUI.TranslateMessage(ref msg);
XplatUI.DispatchMessage(ref msg);
break;
}
}
// Handle exit, Form might have received WM_CLOSE and set 'closing' in response
if ((context.MainForm != null) && (context.MainForm.closing || (Modal && !context.MainForm.Visible))) {
if (!Modal) {
XplatUI.PostQuitMessage(0);
} else {
break;
}
}
}
#if DebugRunLoop
Console.WriteLine(" RunLoop loop left");
#endif
thread.MessageLoop = false;
XplatUI.EndLoop(Thread.CurrentThread);
if (Modal) {
Control c;
context.MainForm.Hide();
context.MainForm.is_modal = false;
while (toplevels.Count>0) {
#if DebugRunLoop
Console.WriteLine(" Re-Enabling form form {0}", toplevels.Peek());
#endif
c = (Control)toplevels.Dequeue();
if (c.IsHandleCreated) {
XplatUI.EnableWindow(c.window.Handle, true);
}
}
#if DebugRunLoop
Console.WriteLine(" Done with the re-enable");
#endif
if (context.MainForm.IsHandleCreated) {
XplatUI.SetModal(context.MainForm.Handle, false);
}
#if DebugRunLoop
Console.WriteLine(" Done with the SetModal");
#endif
}
#if DebugRunLoop
Console.WriteLine("Leaving RunLoop(Modal={0}, Form={1})", Modal, context.MainForm != null ? context.MainForm.ToString() : "NULL");
#endif
if (context.MainForm != null) {
context.MainForm.context = null;
}
if (!Modal) {
thread.Exit();
}
}
#endregion // Public Static Methods
#region Events
public static event EventHandler ApplicationExit;
public static event EventHandler Idle {
add {
XplatUI.Idle += value;
}
remove {
XplatUI.Idle -= value;
}
}
public static event EventHandler ThreadExit;
public static event ThreadExceptionEventHandler ThreadException;
#endregion // Events
}
}