diff options
| author | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
|---|---|---|
| committer | MichaelTheShifter <[email protected]> | 2016-07-20 09:40:36 -0400 |
| commit | d40fed5ce2bc806a91245adb18039634eac13ed0 (patch) | |
| tree | f1d7168aee6db109ac2c738ad18c9db667a6ba69 /source/ShiftUI/Internal | |
| parent | f1856e8ed30ed882229fd3fa2a4038122a5fb441 (diff) | |
| download | shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.gz shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.tar.bz2 shiftos-c--d40fed5ce2bc806a91245adb18039634eac13ed0.zip | |
Move ShiftUI source code to ShiftOS
This'll be a lot easier to work on.
Diffstat (limited to 'source/ShiftUI/Internal')
180 files changed, 71265 insertions, 0 deletions
diff --git a/source/ShiftUI/Internal/ApplicationHandler.cs b/source/ShiftUI/Internal/ApplicationHandler.cs new file mode 100644 index 0000000..df7d3d6 --- /dev/null +++ b/source/ShiftUI/Internal/ApplicationHandler.cs @@ -0,0 +1,78 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; + +namespace ShiftUI.CarbonInternal { + internal class ApplicationHandler : EventHandlerBase, IEventHandler { + internal const uint kEventAppActivated = 1; + internal const uint kEventAppDeactivated = 2; + internal const uint kEventAppQuit = 3; + internal const uint kEventAppLaunchNotification = 4; + internal const uint kEventAppLaunched = 5; + internal const uint kEventAppTerminated = 6; + internal const uint kEventAppFrontSwitched = 7; + internal const uint kEventAppFocusMenuBar = 8; + internal const uint kEventAppFocusNextDocumentWindow = 9; + internal const uint kEventAppFocusNextFloatingWindow = 10; + internal const uint kEventAppFocusToolbar = 11; + internal const uint kEventAppFocusDrawer = 12; + internal const uint kEventAppGetDockTileMenu = 20; + internal const uint kEventAppIsEventInInstantMouser = 104; + internal const uint kEventAppHidden = 107; + internal const uint kEventAppShown = 108; + internal const uint kEventAppSystemUIModeChanged = 109; + internal const uint kEventAppAvailableWindowBoundsChanged = 110; + internal const uint kEventAppActiveWindowChanged = 111; + + internal ApplicationHandler (XplatUICarbon driver) : base (driver) {} + + public bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) { + switch (kind) { + case kEventAppActivated: { + foreach (IntPtr utility_window in XplatUICarbon.UtilityWindows) + if (!XplatUICarbon.IsWindowVisible (utility_window)) + XplatUICarbon.ShowWindow (utility_window); + break; + } + case kEventAppDeactivated: { + if (XplatUICarbon.FocusWindow != IntPtr.Zero) { + Driver.SendMessage (XplatUICarbon.FocusWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero); + } + if (XplatUICarbon.Grab.Hwnd != IntPtr.Zero) { + Driver.SendMessage (Hwnd.ObjectFromHandle (XplatUICarbon.Grab.Hwnd).Handle, Msg.WM_LBUTTONDOWN, (IntPtr)MsgButtons.MK_LBUTTON, (IntPtr) (Driver.MousePosition.X << 16 | Driver.MousePosition.Y)); + } + foreach (IntPtr utility_window in XplatUICarbon.UtilityWindows) + if (XplatUICarbon.IsWindowVisible (utility_window)) + XplatUICarbon.HideWindow (utility_window); + break; + } + } + + return true; + } + } +} diff --git a/source/ShiftUI/Internal/ArrangedElementCollection.cs b/source/ShiftUI/Internal/ArrangedElementCollection.cs new file mode 100644 index 0000000..61f5e52 --- /dev/null +++ b/source/ShiftUI/Internal/ArrangedElementCollection.cs @@ -0,0 +1,173 @@ +// +// ArrangedElementCollection.cs +// +// 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) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System.Collections; +using System; + +namespace ShiftUI.Layout +{ + public class ArrangedElementCollection : IList, ICollection, IEnumerable + { + internal ArrayList list; + + internal ArrangedElementCollection () + { + this.list = new ArrayList (); + } + + #region Public Properties + public virtual int Count { get { return list.Count; } } + public virtual bool IsReadOnly { get { return list.IsReadOnly; } } + #endregion + + #region Public Methods + public void CopyTo (Array array, int index) + { + list.CopyTo (array, index); + } + + public override bool Equals (object obj) + { + if (obj is ArrangedElementCollection && this == obj) + return (true); + else + return (false); + } + + public virtual IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + #endregion + + #region IList Members + int IList.Add (object value) + { + return Add (value); + } + + internal int Add (object value) + { + return list.Add (value); + } + + void IList.Clear () + { + Clear (); + } + + internal void Clear () + { + list.Clear (); + } + + bool IList.Contains (object value) + { + return Contains (value); + } + + internal bool Contains (object value) + { + return list.Contains (value); + } + + int IList.IndexOf (object value) + { + return IndexOf (value); + } + + internal int IndexOf (object value) + { + return list.IndexOf (value); + } + + void IList.Insert (int index, object value) + { + throw new NotSupportedException (); + } + + internal void Insert (int index, object value) + { + list.Insert (index, value); + } + + bool IList.IsFixedSize { + get { return this.IsFixedSize; } + } + + internal bool IsFixedSize { + get { return list.IsFixedSize; } + } + + void IList.Remove (object value) + { + Remove (value); + } + + internal void Remove (object value) + { + list.Remove (value); + } + + void IList.RemoveAt (int index) + { + list.RemoveAt (index); + } + + internal void InternalRemoveAt (int index) + { + list.RemoveAt (index); + } + + object IList.this[int index] { + get { return this[index]; } + set { this[index] = value; } + } + + internal object this[int index] { + get { return list[index]; } + set { list[index] = value; } + } + #endregion + + #region ICollection Members + bool ICollection.IsSynchronized { + get { return list.IsSynchronized; } + } + + object ICollection.SyncRoot { + get { return list.IsSynchronized; } + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/AsyncMethodData.cs b/source/ShiftUI/Internal/AsyncMethodData.cs new file mode 100644 index 0000000..707ce62 --- /dev/null +++ b/source/ShiftUI/Internal/AsyncMethodData.cs @@ -0,0 +1,41 @@ +// 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 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + + +using System; +using System.Threading; + + +namespace ShiftUI { + + internal class AsyncMethodData { + public IntPtr Handle; + public Delegate Method; + public object [] Args; + public AsyncMethodResult Result; + public ExecutionContext Context; + public SynchronizationContext SyncContext; + } + +} diff --git a/source/ShiftUI/Internal/AsyncMethodResult.cs b/source/ShiftUI/Internal/AsyncMethodResult.cs new file mode 100644 index 0000000..48c480d --- /dev/null +++ b/source/ShiftUI/Internal/AsyncMethodResult.cs @@ -0,0 +1,108 @@ +// 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 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + + +using System; +using System.Threading; + +namespace ShiftUI { + + internal class AsyncMethodResult : IAsyncResult { + + private ManualResetEvent handle; + private object state; + private bool completed; + private object return_value; + private Exception exception; + + public AsyncMethodResult () + { + handle = new ManualResetEvent (false); + } + + public virtual WaitHandle AsyncWaitHandle { + get { + lock (this) { + return handle; + } + } + } + + public object AsyncState { + get { return state; } + set { state = value; } + } + + public bool CompletedSynchronously { + get { return false; } + } + + public bool IsCompleted { + get { + lock (this) { + return completed; + } + } + } + + public object EndInvoke () + { + lock (this) { + if (completed) { + if (exception == null) + return return_value; + else + throw exception; + } + } + handle.WaitOne (); + + if (exception != null) + throw exception; + + return return_value; + } + + public void Complete (object result) + { + lock (this) { + completed = true; + return_value = result; + handle.Set (); + } + } + + public void CompleteWithException (Exception ex) + { + lock (this) { + completed = true; + exception = ex; + handle.Set (); + } + } + } + +} + + diff --git a/source/ShiftUI/Internal/AutoCompleteSource.cs b/source/ShiftUI/Internal/AutoCompleteSource.cs new file mode 100644 index 0000000..ab2fc6a --- /dev/null +++ b/source/ShiftUI/Internal/AutoCompleteSource.cs @@ -0,0 +1,44 @@ +// +// AutoCompleteSource.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +namespace ShiftUI +{ + public enum AutoCompleteSource + { + FileSystem = 1, + HistoryList = 2, + RecentlyUsedList = 4, + AllUrl = 6, + AllSystemSources = 7, + FileSystemDirectories = 32, + CustomSource = 64, + None = 128, + ListItems = 256 + } +} diff --git a/source/ShiftUI/Internal/AutoCompleteStringCollection.cs b/source/ShiftUI/Internal/AutoCompleteStringCollection.cs new file mode 100644 index 0000000..2142d47 --- /dev/null +++ b/source/ShiftUI/Internal/AutoCompleteStringCollection.cs @@ -0,0 +1,203 @@ +// +// AutoCompleteStringCollection.cs +// +// 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) 2006 Daniel Nauck +// +// Author: +// Daniel Nauck (dna(at)mono-project(dot)de) + + +using System; +using System.Collections; +using System.ComponentModel; +using System.Reflection; + +namespace ShiftUI +{ + public class AutoCompleteStringCollection : IList, ICollection, IEnumerable + { + private ArrayList list = null; + + public AutoCompleteStringCollection () + { + list = new ArrayList (); + } + + public event CollectionChangeEventHandler CollectionChanged; + + protected void OnCollectionChanged (CollectionChangeEventArgs e) + { + if(CollectionChanged == null) + return; + + CollectionChanged (this, e); + } + + #region IEnumerable Members + + public IEnumerator GetEnumerator () + { + return list.GetEnumerator (); + } + + #endregion + + #region ICollection Members + + void ICollection.CopyTo (Array array, int index) + { + list.CopyTo (array, index); + } + + public void CopyTo (string[] array, int index) + { + list.CopyTo (array, index); + } + + public int Count + { + get { return list.Count; } + } + + public bool IsSynchronized + { + get { return false; } + } + + public object SyncRoot + { + get { return this; } + } + + #endregion + + #region IList Members + + int IList.Add (object value) + { + return Add ((string)value); + } + + public int Add (string value) + { + int index = list.Add (value); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, value)); + return index; + } + + public void AddRange (string[] value) + { + if (value == null) + throw new ArgumentNullException ("value", "Argument cannot be null!"); + + list.AddRange (value); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null)); + } + + public void Clear () + { + list.Clear (); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null)); + } + + bool IList.Contains (object value) + { + return Contains ((string)value); + } + + public bool Contains (string value) + { + return list.Contains (value); + } + + int IList.IndexOf (object value) + { + return IndexOf ((string)value); + } + + public int IndexOf (string value) + { + return list.IndexOf (value); + } + + void IList.Insert (int index, object value) + { + Insert (index, (string)value); + } + + public void Insert (int index, string value) + { + list.Insert (index, value); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, value)); + } + + bool IList.IsFixedSize + { + get { return false; } + } + + bool IList.IsReadOnly + { + get { return false; } + } + + public bool IsReadOnly + { + get { return false; } + } + + void IList.Remove (object value) + { + Remove((string)value); + } + + public void Remove (string value) + { + list.Remove (value); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, value)); + } + + public void RemoveAt (int index) + { + string value = this[index]; + list.RemoveAt (index); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, value)); + } + + object IList.this[int index] + { + get { return this[index]; } + set { this[index] = (string)value; } + } + + public string this[int index] + { + get { return (string)list[index]; } + set { + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, list[index])); + list[index] = value; + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, value)); + } + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/BaseCollection.cs b/source/ShiftUI/Internal/BaseCollection.cs new file mode 100644 index 0000000..91b3661 --- /dev/null +++ b/source/ShiftUI/Internal/BaseCollection.cs @@ -0,0 +1,99 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + + +// COMPLETE + +using System; +using System.Collections; +using System.ComponentModel; + +namespace ShiftUI { + public class BaseCollection : MarshalByRefObject, ICollection, IEnumerable { + internal ArrayList list; + + #region Public Constructors + public BaseCollection () + { + } + #endregion // Public Constructors + + #region Public Instance Properties + [Browsable(false)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + public virtual int Count { + get { + return List.Count; + } + } + + [Browsable(false)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + public bool IsReadOnly { + get { + return false; + } + } + + [Browsable(false)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + public bool IsSynchronized { + get { + return false; + } + } + + [Browsable(false)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + public object SyncRoot { + get { + return this; + } + } + #endregion // Public Instance Properties + + #region Protected Instance Properties + protected virtual ArrayList List { + get { + if (list == null) + list = new ArrayList (); + return list; + } + } + #endregion // Protected Instance Properties + + #region Public Instance Methods + public void CopyTo (Array ar, int index) + { + List.CopyTo (ar, index); + } + + public IEnumerator GetEnumerator () + { + return List.GetEnumerator (); + } + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/Binding.cs b/source/ShiftUI/Internal/Binding.cs new file mode 100644 index 0000000..7b6ccc7 --- /dev/null +++ b/source/ShiftUI/Internal/Binding.cs @@ -0,0 +1,578 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// Jackson Harper [email protected] +// + + +using System.ComponentModel; +using System; + +namespace ShiftUI { + + [TypeConverter (typeof (ListBindingConverter))] + public class Binding { + + private string property_name; + private object data_source; + private string data_member; + + private bool is_binding; + private bool checked_isnull; + + private BindingMemberInfo binding_member_info; + private IBindableComponent control; + + private BindingManagerBase manager; + private PropertyDescriptor control_property; + private PropertyDescriptor is_null_desc; + + private object data; + private Type data_type; + + private DataSourceUpdateMode datasource_update_mode; + private ControlUpdateMode control_update_mode; + private object datasource_null_value = Convert.DBNull; + private object null_value; + private IFormatProvider format_info; + private string format_string; + private bool formatting_enabled; + #region Public Constructors + public Binding (string propertyName, object dataSource, string dataMember) + : this (propertyName, dataSource, dataMember, false, DataSourceUpdateMode.OnValidation, null, string.Empty, null) + { + } + + public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled) + : this (propertyName, dataSource, dataMember, formattingEnabled, DataSourceUpdateMode.OnValidation, null, string.Empty, null) + { + } + + public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode) + : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, null, string.Empty, null) + { + } + + public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue) + : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, string.Empty, null) + { + } + + public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString) + : this (propertyName, dataSource, dataMember, formattingEnabled, dataSourceUpdateMode, nullValue, formatString, null) + { + } + + public Binding (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode dataSourceUpdateMode, object nullValue, string formatString, IFormatProvider formatInfo) + { + property_name = propertyName; + data_source = dataSource; + data_member = dataMember; + binding_member_info = new BindingMemberInfo (dataMember); + datasource_update_mode = dataSourceUpdateMode; + null_value = nullValue; + format_string = formatString; + format_info = formatInfo; + } + #endregion // Public Constructors + + #region Public Instance Properties + [DefaultValue (null)] + public IBindableComponent BindableComponent { + get { + return control; + } + } + + public BindingManagerBase BindingManagerBase { + get { + return manager; + } + } + + public BindingMemberInfo BindingMemberInfo { + get { + return binding_member_info; + } + } + + [DefaultValue (null)] + public Widget Widget { + get { + return control as Widget; + } + } + + [DefaultValue (ControlUpdateMode.OnPropertyChanged)] + public ControlUpdateMode ControlUpdateMode { + get { + return control_update_mode; + } + set { + control_update_mode = value; + } + } + + public object DataSource { + get { + return data_source; + } + } + + [DefaultValue (DataSourceUpdateMode.OnValidation)] + public DataSourceUpdateMode DataSourceUpdateMode { + get { + return datasource_update_mode; + } + set { + datasource_update_mode = value; + } + } + + public object DataSourceNullValue { + get { + return datasource_null_value; + } + set { + datasource_null_value = value; + } + } + + [DefaultValue (false)] + public bool FormattingEnabled { + get { + return formatting_enabled; + } + set { + if (formatting_enabled == value) + return; + + formatting_enabled = value; + PushData (); + } + } + + [DefaultValue (null)] + public IFormatProvider FormatInfo { + get { + return format_info; + } + set { + if (value == format_info) + return; + + format_info = value; + if (formatting_enabled) + PushData (); + } + } + + public string FormatString { + get { + return format_string; + } + set { + if (value == null) + value = String.Empty; + if (value == format_string) + return; + + format_string = value; + if (formatting_enabled) + PushData (); + } + } + + public bool IsBinding { + get { + if (manager == null || manager.IsSuspended) + return false; + + return is_binding; + } + } + + public object NullValue { + get { + return null_value; + } + set { + if (value == null_value) + return; + + null_value = value; + if (formatting_enabled) + PushData (); + } + } + + [DefaultValue ("")] + public string PropertyName { + get { + return property_name; + } + } + #endregion // Public Instance Properties + + public void ReadValue () + { + PushData (true); + } + + public void WriteValue () + { + PullData (true); + } + + #region Protected Instance Methods + protected virtual void OnBindingComplete (BindingCompleteEventArgs e) + { + if (BindingComplete != null) + BindingComplete (this, e); + } + + protected virtual void OnFormat (ConvertEventArgs cevent) + { + if (Format!=null) + Format (this, cevent); + } + + protected virtual void OnParse (ConvertEventArgs cevent) + { + if (Parse!=null) + Parse (this, cevent); + } + #endregion // Protected Instance Methods + + internal string DataMember { + get { return data_member; } + } + + internal void SetWidget (IBindableComponent control) + { + if (control == this.control) + return; + + control_property = TypeDescriptor.GetProperties (control).Find (property_name, true); + + if (control_property == null) + throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' on target control.")); + if (control_property.IsReadOnly) + throw new ArgumentException (String.Concat ("Cannot bind to property '", property_name, "' because it is read only.")); + + data_type = control_property.PropertyType; // Getting the PropertyType is kinda slow and it should never change, so it is cached + + Widget ctrl = control as Widget; + if (ctrl != null) { + ctrl.Validating += new CancelEventHandler (ControlValidatingHandler); + if (!ctrl.IsHandleCreated) + ctrl.HandleCreated += new EventHandler (ControlCreatedHandler); + } + + EventDescriptor prop_changed_event = GetPropertyChangedEvent (control, property_name); + if (prop_changed_event != null) + prop_changed_event.AddEventHandler (control, new EventHandler (ControlPropertyChangedHandler)); + this.control = control; + UpdateIsBinding (); + } + + internal void Check () + { + if (control == null || control.BindingContext == null) + return; + + if (manager == null) { + manager = control.BindingContext [data_source, binding_member_info.BindingPath]; + + if (manager.Position > -1 && binding_member_info.BindingField != String.Empty && + TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true) == null) + throw new ArgumentException ("Cannot bind to property '" + binding_member_info.BindingField + "' on DataSource.", + "dataMember"); + + manager.AddBinding (this); + manager.PositionChanged += new EventHandler (PositionChangedHandler); + + if (manager is PropertyManager) { // Match .net, which only watchs simple objects + EventDescriptor prop_changed_event = GetPropertyChangedEvent (manager.Current, binding_member_info.BindingField); + if (prop_changed_event != null) + prop_changed_event.AddEventHandler (manager.Current, new EventHandler (SourcePropertyChangedHandler)); + } + } + + if (manager.Position == -1) + return; + + if (!checked_isnull) { + is_null_desc = TypeDescriptor.GetProperties (manager.Current).Find (property_name + "IsNull", false); + checked_isnull = true; + } + + PushData (); + } + + internal bool PullData () + { + return PullData (false); + } + + // Return false ONLY in case of error - and return true even in cases + // where no update was possible + bool PullData (bool force) + { + if (IsBinding == false || manager.Current == null) + return true; + if (!force && datasource_update_mode == DataSourceUpdateMode.Never) + return true; + + data = control_property.GetValue (control); + if (data == null) + data = datasource_null_value; + + try { + SetPropertyValue (data); + } catch (Exception e) { + if (formatting_enabled) { + FireBindingComplete (BindingCompleteContext.DataSourceUpdate, e, e.Message); + return false; + } + throw e; + } + + if (formatting_enabled) + FireBindingComplete (BindingCompleteContext.DataSourceUpdate, null, null); + return true; + } + + internal void PushData () + { + PushData (false); + } + + void PushData (bool force) + { + if (manager == null || manager.IsSuspended || manager.Count == 0 || manager.Position == -1) + return; + if (!force && control_update_mode == ControlUpdateMode.Never) + return; + + if (is_null_desc != null) { + bool is_null = (bool) is_null_desc.GetValue (manager.Current); + if (is_null) { + data = Convert.DBNull; + return; + } + } + + PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true); + if (pd == null) { + data = manager.Current; + } else { + data = pd.GetValue (manager.Current); + } + + if ((data == null || data == DBNull.Value) && null_value != null) + data = null_value; + + try { + data = FormatData (data); + SetControlValue (data); + } catch (Exception e) { + if (formatting_enabled) { + FireBindingComplete (BindingCompleteContext.ControlUpdate, e, e.Message); + return; + } + throw e; + } + + if (formatting_enabled) + FireBindingComplete (BindingCompleteContext.ControlUpdate, null, null); + } + + internal void UpdateIsBinding () + { + is_binding = false; + if (control == null || (control is Widget && !((Widget)control).IsHandleCreated)) + return; + + is_binding = true; + PushData (); + } + + private void SetControlValue (object data) + { + control_property.SetValue (control, data); + } + + private void SetPropertyValue (object data) + { + PropertyDescriptor pd = TypeDescriptor.GetProperties (manager.Current).Find (binding_member_info.BindingField, true); + if (pd.IsReadOnly) + return; + data = ParseData (data, pd.PropertyType); + pd.SetValue (manager.Current, data); + } + + private void ControlValidatingHandler (object sender, CancelEventArgs e) + { + if (datasource_update_mode != DataSourceUpdateMode.OnValidation) + return; + + bool ok = true; + // If the data doesn't seem to be valid (it can't be converted, + // is the wrong type, etc, we reset to the old data value. + // If Formatting is enabled, no exception is fired, but we get a false value + try { + ok = PullData (); + } catch { + ok = false; + } + + e.Cancel = !ok; + } + + private void ControlCreatedHandler (object o, EventArgs args) + { + UpdateIsBinding (); + } + + private void PositionChangedHandler (object sender, EventArgs e) + { + Check (); + PushData (); + } + + EventDescriptor GetPropertyChangedEvent (object o, string property_name) + { + if (o == null || property_name == null || property_name.Length == 0) + return null; + + string event_name = property_name + "Changed"; + Type event_handler_type = typeof (EventHandler); + + EventDescriptor prop_changed_event = null; + foreach (EventDescriptor event_desc in TypeDescriptor.GetEvents (o)) { + if (event_desc.Name == event_name && event_desc.EventType == event_handler_type) { + prop_changed_event = event_desc; + break; + } + } + + return prop_changed_event; + } + + void SourcePropertyChangedHandler (object o, EventArgs args) + { + PushData (); + } + + void ControlPropertyChangedHandler (object o, EventArgs args) + { + if (datasource_update_mode != DataSourceUpdateMode.OnPropertyChanged) + return; + + PullData (); + } + + private object ParseData (object data, Type data_type) + { + ConvertEventArgs e = new ConvertEventArgs (data, data_type); + + OnParse (e); + if (data_type.IsInstanceOfType (e.Value)) + return e.Value; + if (e.Value == Convert.DBNull) + return e.Value; + if (e.Value == null) { + bool nullable = data_type.IsGenericType && !data_type.ContainsGenericParameters && + data_type.GetGenericTypeDefinition () == typeof (Nullable<>); + return data_type.IsValueType && !nullable ? Convert.DBNull : null; + } + + return ConvertData (e.Value, data_type); + } + + private object FormatData (object data) + { + ConvertEventArgs e = new ConvertEventArgs (data, data_type); + + OnFormat (e); + if (data_type.IsInstanceOfType (e.Value)) + return e.Value; + + if (formatting_enabled) { + if ((e.Value == null || e.Value == Convert.DBNull) && null_value != null) + return null_value; + + if (e.Value is IFormattable && data_type == typeof (string)) { + IFormattable formattable = (IFormattable) e.Value; + return formattable.ToString (format_string, format_info); + } + } + + if (e.Value == null && data_type == typeof (object)) + return Convert.DBNull; + + return ConvertData (data, data_type); + } + + private object ConvertData (object data, Type data_type) + { + if (data == null) + return null; + + TypeConverter converter = TypeDescriptor.GetConverter (data.GetType ()); + if (converter != null && converter.CanConvertTo (data_type)) + return converter.ConvertTo (data, data_type); + + converter = TypeDescriptor.GetConverter (data_type); + if (converter != null && converter.CanConvertFrom (data.GetType())) + return converter.ConvertFrom (data); + + if (data is IConvertible) { + object res = Convert.ChangeType (data, data_type); + if (data_type.IsInstanceOfType (res)) + return res; + } + + return null; + } + void FireBindingComplete (BindingCompleteContext context, Exception exc, string error_message) + { + BindingCompleteEventArgs args = new BindingCompleteEventArgs (this, + exc == null ? BindingCompleteState.Success : BindingCompleteState.Exception, + context); + if (exc != null) { + args.SetException (exc); + args.SetErrorText (error_message); + } + + OnBindingComplete (args); + } + + #region Events + public event ConvertEventHandler Format; + public event ConvertEventHandler Parse; + public event BindingCompleteEventHandler BindingComplete; + #endregion // Events + } +} diff --git a/source/ShiftUI/Internal/BindingCompleteContext.cs b/source/ShiftUI/Internal/BindingCompleteContext.cs new file mode 100644 index 0000000..44d7347 --- /dev/null +++ b/source/ShiftUI/Internal/BindingCompleteContext.cs @@ -0,0 +1,37 @@ +// +// BindingCompleteContext.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +namespace ShiftUI +{ + public enum BindingCompleteContext + { + ControlUpdate = 0, + DataSourceUpdate = 1 + } +} diff --git a/source/ShiftUI/Internal/BindingContext.cs b/source/ShiftUI/Internal/BindingContext.cs new file mode 100644 index 0000000..fbf461a --- /dev/null +++ b/source/ShiftUI/Internal/BindingContext.cs @@ -0,0 +1,257 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// Jackson Harper [email protected] + + +using System.Data; +using System.Collections; +using System.Globalization; +using System.ComponentModel; +using System; + +namespace ShiftUI { + + [DefaultEvent("CollectionChanged")] + public class BindingContext : ICollection, IEnumerable { + + private Hashtable managers; + private EventHandler onCollectionChangedHandler; + + private class HashKey { + public object source; + public string member; + + public HashKey (object source, string member) + { + this.source = source; + this.member = member; + } + + public override int GetHashCode () + { + return source.GetHashCode() ^ member.GetHashCode (); + } + + public override bool Equals (object o) + { + HashKey hk = o as HashKey; + if (hk == null) + return false; + return hk.source == source && hk.member == member; + } + } + + public BindingContext () + { + managers = new Hashtable (); + onCollectionChangedHandler = null; + } + + public bool IsReadOnly { + get { return false; } + } + + public BindingManagerBase this [object dataSource] { + get { return this [dataSource, String.Empty]; } + } + + public BindingManagerBase this [object dataSource, string dataMember] { + get { + if (dataSource == null) + throw new ArgumentNullException ("dataSource"); + if (dataMember == null) + dataMember = String.Empty; + + ICurrencyManagerProvider cm_provider = dataSource as ICurrencyManagerProvider; + if (cm_provider != null) { + if (dataMember.Length == 0) + return cm_provider.CurrencyManager; + + return cm_provider.GetRelatedCurrencyManager (dataMember); + } + + HashKey key = new HashKey (dataSource, dataMember); + BindingManagerBase res = managers [key] as BindingManagerBase; + + if (res != null) + return res; + + res = CreateBindingManager (dataSource, dataMember); + if (res == null) + return null; + managers [key] = res; + return res; + } + } + + private BindingManagerBase CreateBindingManager (object data_source, string data_member) + { + if (data_member == "") { + if (IsListType (data_source.GetType ())) + return new CurrencyManager (data_source); + else + return new PropertyManager (data_source); + } + else { + BindingMemberInfo info = new BindingMemberInfo (data_member); + + BindingManagerBase parent_manager = this[data_source, info.BindingPath]; + + PropertyDescriptor pd = parent_manager == null ? null : parent_manager.GetItemProperties ().Find (info.BindingField, true); + + if (pd == null) + throw new ArgumentException (String.Format ("Cannot create a child list for field {0}.", info.BindingField)); + + if (IsListType (pd.PropertyType)) + return new RelatedCurrencyManager (parent_manager, pd); + else + return new RelatedPropertyManager (parent_manager, info.BindingField); + } + } + + bool IsListType (Type t) + { + return (typeof (IList).IsAssignableFrom (t) + || typeof (IListSource).IsAssignableFrom (t)); + } + + #region Public Instance Methods + public bool Contains(object dataSource) + { + return Contains (dataSource, String.Empty); + } + + public bool Contains (object dataSource, string dataMember) + { + if (dataSource == null) + throw new ArgumentNullException ("dataSource"); + if (dataMember == null) + dataMember = String.Empty; + + HashKey key = new HashKey (dataSource, dataMember); + return managers [key] != null; + } + #endregion // Public Instance Methods + + #region Protected Instance Methods + + protected internal void Add (object dataSource, BindingManagerBase listManager) + { + AddCore (dataSource, listManager); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, dataSource)); + } + + protected virtual void AddCore (object dataSource, BindingManagerBase listManager) + { + if (dataSource == null) + throw new ArgumentNullException ("dataSource"); + if (listManager == null) + throw new ArgumentNullException ("listManager"); + + HashKey key = new HashKey (dataSource, String.Empty); + managers [key] = listManager; + } + + protected internal void Clear () + { + ClearCore(); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null)); + } + + protected virtual void ClearCore () + { + managers.Clear (); + } + + protected virtual void OnCollectionChanged (CollectionChangeEventArgs ccevent) + { + if (onCollectionChangedHandler != null) { + onCollectionChangedHandler (this, ccevent); + } + } + + protected internal void Remove (object dataSource) + { + if (dataSource == null) + throw new ArgumentNullException ("dataSource"); + + RemoveCore (dataSource); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Remove, dataSource)); + } + + protected virtual void RemoveCore (object dataSource) + { + HashKey[] keys = new HashKey [managers.Keys.Count]; + managers.Keys.CopyTo (keys, 0); + + for (int i = 0; i < keys.Length; i ++) { + if (keys[i].source == dataSource) + managers.Remove (keys[i]); + } + } + + [MonoTODO ("Stub, does nothing")] + public static void UpdateBinding (BindingContext newBindingContext, Binding binding) + { + } + + #endregion // Protected Instance Methods + + #region Events + [Browsable (false)] + //[EditorBrowsable (EditorBrowsableState.Never)] + public event CollectionChangeEventHandler CollectionChanged { + add { throw new NotImplementedException (); } + remove { /* nothing to do here.. */ } + } + #endregion // Events + + #region ICollection Interfaces + void ICollection.CopyTo (Array ar, int index) + { + managers.CopyTo (ar, index); + } + + int ICollection.Count { + get { return managers.Count; } + } + + bool ICollection.IsSynchronized { + get { return false; } + } + + object ICollection.SyncRoot { + get { return null; } + } + + #endregion // ICollection Interfaces + + #region IEnumerable Interfaces + [MonoInternalNote ("our enumerator is slightly different. in MS's implementation the Values are WeakReferences to the managers.")] + IEnumerator IEnumerable.GetEnumerator() { + return managers.GetEnumerator (); + } + #endregion // IEnumerable Interfaces + } +} diff --git a/source/ShiftUI/Internal/BindingManagerBase.cs b/source/ShiftUI/Internal/BindingManagerBase.cs new file mode 100644 index 0000000..c2b0392 --- /dev/null +++ b/source/ShiftUI/Internal/BindingManagerBase.cs @@ -0,0 +1,204 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// Jackson Harper [email protected] +// + + +// NOT COMPLETE + +using System.ComponentModel; +using System.Collections; +using System; + +namespace ShiftUI +{ + public abstract class BindingManagerBase + { + private BindingsCollection bindings; + internal bool transfering_data; /* true if we're pushing or pulling data */ + + #region Public Constructors + public BindingManagerBase() + { + } + #endregion // Public Constructors + + #region Protected Instance Fields + protected EventHandler onCurrentChangedHandler; + protected EventHandler onPositionChangedHandler; + internal EventHandler onCurrentItemChangedHandler; + #endregion // Protected Instance Fields + + #region Public Instance Properties + public BindingsCollection Bindings { + get { + if (bindings == null) { + bindings = new BindingsCollection (); + } + return bindings; + } + } + + public abstract int Count { + get; + } + + public abstract object Current { + get; + } + + public bool IsBindingSuspended { + get { + return IsSuspended; + } + } + + public abstract int Position { + get; set; + } + #endregion // Public Instance Properties + + #region Public Instance Methods + public abstract void AddNew(); + + public abstract void CancelCurrentEdit(); + + public abstract void EndCurrentEdit(); + + public virtual PropertyDescriptorCollection GetItemProperties() + { + return GetItemPropertiesInternal (); + } + + internal virtual PropertyDescriptorCollection GetItemPropertiesInternal () + { + throw new NotImplementedException (); + } + + public abstract void RemoveAt(int index); + + public abstract void ResumeBinding(); + + public abstract void SuspendBinding(); + #endregion // Public Instance Methods + + internal virtual bool IsSuspended { + get { + return false; + } + } + + #region Protected Instance Methods + [MonoTODO ("Not implemented, will throw NotImplementedException")] + protected internal virtual PropertyDescriptorCollection GetItemProperties (ArrayList dataSources, ArrayList listAccessors) + { + throw new NotImplementedException(); + } + + [MonoTODO ("Not implemented, will throw NotImplementedException")] + protected virtual PropertyDescriptorCollection GetItemProperties (Type listType, int offset, ArrayList dataSources, ArrayList listAccessors) + { + throw new NotImplementedException(); + } + + protected internal abstract string GetListName (ArrayList listAccessors); + + protected internal abstract void OnCurrentChanged (EventArgs e); + + protected void PullData() + { + try { + if (!transfering_data) { + transfering_data = true; + UpdateIsBinding (); + } + foreach (Binding binding in Bindings) { + binding.PullData (); + } + } finally { + transfering_data = false; + } + } + + protected void PushData() + { + try { + if (!transfering_data) { + transfering_data = true; + UpdateIsBinding (); + } + foreach (Binding binding in Bindings) { + binding.PushData (); + } + } finally { + transfering_data = false; + } + } + + + protected void OnBindingComplete (BindingCompleteEventArgs args) + { + if (BindingComplete != null) + BindingComplete (this, args); + } + + protected abstract void OnCurrentItemChanged (EventArgs e); + + protected void OnDataError (Exception e) + { + if (DataError != null) + DataError (this, new BindingManagerDataErrorEventArgs (e)); + } + + protected abstract void UpdateIsBinding(); + #endregion // Protected Instance Methods + + internal void AddBinding (Binding binding) + { + if (Bindings.Contains (binding)) + return; + Bindings.Add (binding); + } + + #region Events + public event EventHandler CurrentChanged { + add { onCurrentChangedHandler += value; } + remove { onCurrentChangedHandler -= value; } + } + + public event EventHandler PositionChanged { + add { onPositionChangedHandler += value; } + remove { onPositionChangedHandler -= value; } + } + + public event EventHandler CurrentItemChanged { + add { onCurrentItemChangedHandler += value; } + remove { onCurrentItemChangedHandler -= value; } + } + + public event BindingCompleteEventHandler BindingComplete; + public event BindingManagerDataErrorEventHandler DataError; + #endregion // Events + } +} diff --git a/source/ShiftUI/Internal/BindingMemberInfo.cs b/source/ShiftUI/Internal/BindingMemberInfo.cs new file mode 100644 index 0000000..3f7ae11 --- /dev/null +++ b/source/ShiftUI/Internal/BindingMemberInfo.cs @@ -0,0 +1,107 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + + +// COMPLETE +using System; + +namespace ShiftUI { + public struct BindingMemberInfo { + private string data_member; + private string data_field; + private string data_path; + + #region Public Constructors + public BindingMemberInfo(string dataMember) { + int i; + + if (dataMember!=null) { + this.data_member=dataMember; + } else { + this.data_member=String.Empty; + } + + // Break out our components + i=data_member.LastIndexOf('.'); + if (i!=-1) { + data_field=data_member.Substring(i+1); + data_path=data_member.Substring(0, i); + } else { + data_field=data_member; + data_path=String.Empty; + } + } + #endregion // Public Constructors + + #region Public Instance Properties + public string BindingField { + get { + return this.data_field; + } + } + + public string BindingMember { + get { + return this.data_member; + } + } + + public string BindingPath { + get { + return this.data_path; + } + } + #endregion // Public Instance Properties + + #region Public Instance Methods + public override bool Equals(object otherObject) { + if (otherObject is BindingMemberInfo) { + return ((this.data_field == ((BindingMemberInfo)otherObject).data_field) && + (this.data_path == ((BindingMemberInfo)otherObject).data_path) && + (this.data_member == ((BindingMemberInfo)otherObject).data_member)); + } else { + return false; + } + } + + public override int GetHashCode() { + return this.data_member.GetHashCode(); + } + #endregion // Public Instance Methods + + #region Public Static Methods + public static bool operator == (BindingMemberInfo a, BindingMemberInfo b) + { + return (a.Equals (b)); + } + + public static bool operator != (BindingMemberInfo a, BindingMemberInfo b) + { + return !(a.Equals (b)); + } + #endregion // Public Static Methods + + } +} diff --git a/source/ShiftUI/Internal/BindingsCollection.cs b/source/ShiftUI/Internal/BindingsCollection.cs new file mode 100644 index 0000000..11f4fe9 --- /dev/null +++ b/source/ShiftUI/Internal/BindingsCollection.cs @@ -0,0 +1,136 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// Jackson Harper [email protected] +// + + +// COMPLETE + +using System.Collections; +using System.ComponentModel; + +namespace ShiftUI { + [DefaultEvent("CollectionChanged")] + public class BindingsCollection : BaseCollection { + +#region Public Constructors + internal BindingsCollection () + { + } + #endregion // Public Constructors + + #region Public Instance Properties + public override int Count { + get { + return base.Count; + } + } + + public Binding this[int index] { + get { + return (Binding)(base.List[index]); + } + } + #endregion // Public Instance Properties + + #region Protected Instance Properties + protected override ArrayList List { + get { + return base.List; + } + } + #endregion // Protected Instance Properties + + #region Protected Instance Methods + protected internal void Add(Binding binding) { + AddCore(binding); + } + + protected virtual void AddCore (Binding dataBinding) + { + CollectionChangeEventArgs args = new CollectionChangeEventArgs (CollectionChangeAction.Add, dataBinding); + OnCollectionChanging (args); + base.List.Add(dataBinding); + OnCollectionChanged (args); + } + + protected internal void Clear() { + ClearCore(); + } + + protected virtual void ClearCore() + { + CollectionChangeEventArgs args = new CollectionChangeEventArgs(CollectionChangeAction.Refresh, null); + OnCollectionChanging (args); + base.List.Clear(); + OnCollectionChanged (args); + } + + protected virtual void OnCollectionChanged(System.ComponentModel.CollectionChangeEventArgs ccevent) { + if (CollectionChanged!=null) CollectionChanged(this, ccevent); + } + + protected virtual void OnCollectionChanging (CollectionChangeEventArgs e) + { + if (CollectionChanging != null) + CollectionChanging (this, e); + } + + protected internal void Remove(Binding binding) { + RemoveCore(binding); + } + + protected internal void RemoveAt(int index) { + base.List.RemoveAt(index); + OnCollectionChanged(new CollectionChangeEventArgs(CollectionChangeAction.Remove, base.List)); + } + + protected virtual void RemoveCore(Binding dataBinding) + { + CollectionChangeEventArgs args = new CollectionChangeEventArgs(CollectionChangeAction.Remove, dataBinding); + OnCollectionChanging (args); + base.List.Remove(dataBinding); + OnCollectionChanged (args); + } + + protected internal bool ShouldSerializeMyAll() { + if (this.Count>0) { + return(true); + } else { + return(false); + } + } + #endregion // Public Instance Methods + + internal bool Contains (Binding binding) + { + return List.Contains (binding); + } + + #region Events + public event CollectionChangeEventHandler CollectionChanged; + public event CollectionChangeEventHandler CollectionChanging; + #endregion // Events + } +} diff --git a/source/ShiftUI/Internal/BootMode.cs b/source/ShiftUI/Internal/BootMode.cs new file mode 100644 index 0000000..495d678 --- /dev/null +++ b/source/ShiftUI/Internal/BootMode.cs @@ -0,0 +1,39 @@ +// 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 Novell, Inc. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// + +//COMPLETE + + +namespace ShiftUI +{ + + public enum BootMode + { + Normal = 0, + FailSafe = 1, + FailSafeWithNetwork = 2 + } +} + diff --git a/source/ShiftUI/Internal/BoundsSpecified.cs b/source/ShiftUI/Internal/BoundsSpecified.cs new file mode 100644 index 0000000..7acc040 --- /dev/null +++ b/source/ShiftUI/Internal/BoundsSpecified.cs @@ -0,0 +1,43 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + +// COMPLETE +using System; + +namespace ShiftUI +{ + [Flags] + public enum BoundsSpecified + { + None = 0x00000000, + X = 0x00000001, + Y = 0x00000002, + Location = 0x00000003, + Width = 0x00000004, + Height = 0x00000008, + Size = 0x0000000c, + All = 0x0000000f + } +} diff --git a/source/ShiftUI/Internal/ButtonBase.cs b/source/ShiftUI/Internal/ButtonBase.cs new file mode 100644 index 0000000..7f8c894 --- /dev/null +++ b/source/ShiftUI/Internal/ButtonBase.cs @@ -0,0 +1,747 @@ +// 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 [email protected] +// + +using System.ComponentModel; +using System.ComponentModel.Design; +using System.Drawing; +using System.Drawing.Text; +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI { + [ClassInterface (ClassInterfaceType.AutoDispatch)] + [ComVisible (true)] + //[Designer ("ShiftUI.Design.ButtonBaseDesigner, " + Consts.AssemblySystem_Design, + //"System.ComponentModel.Design.IDesigner")] + public abstract class ButtonBase : Widget + { + #region Local Variables + private FlatStyle flat_style; + private int image_index; + internal Image image; + internal ImageList image_list; + private ContentAlignment image_alignment; + internal ContentAlignment text_alignment; + private bool is_default; + internal bool is_pressed; +// private bool enter_state; + internal StringFormat text_format; + internal bool paint_as_acceptbutton; + + // Properties are 2.0, but variables used in 1.1 for common drawing code + private bool auto_ellipsis; + private FlatButtonAppearance flat_button_appearance; + private string image_key; + private TextImageRelation text_image_relation; + private TextFormatFlags text_format_flags; + private bool use_mnemonic; + private bool use_visual_style_back_color; + #endregion // Local Variables + + #region Public Constructors + protected ButtonBase() : base() + { + flat_style = FlatStyle.Standard; + flat_button_appearance = new FlatButtonAppearance (this); + this.image_key = string.Empty; + this.text_image_relation = TextImageRelation.Overlay; + this.use_mnemonic = true; + use_visual_style_back_color = true; + image_index = -1; + image = null; + image_list = null; + image_alignment = ContentAlignment.MiddleCenter; + ImeMode = ImeMode.Disable; + text_alignment = ContentAlignment.MiddleCenter; + is_default = false; + is_pressed = false; + text_format = new StringFormat(); + text_format.Alignment = StringAlignment.Center; + text_format.LineAlignment = StringAlignment.Center; + text_format.HotkeyPrefix = HotkeyPrefix.Show; + text_format.FormatFlags |= StringFormatFlags.LineLimit; + + text_format_flags = TextFormatFlags.HorizontalCenter; + text_format_flags |= TextFormatFlags.VerticalCenter; + text_format_flags |= TextFormatFlags.TextBoxControl; + + SetStyle (Widgetstyles.ResizeRedraw | + Widgetstyles.Opaque | + Widgetstyles.UserMouse | + Widgetstyles.SupportsTransparentBackColor | + Widgetstyles.CacheText | + Widgetstyles.OptimizedDoubleBuffer, true); + SetStyle (Widgetstyles.StandardClick, false); + } + #endregion // Public Constructors + + #region Public Properties + [Browsable (true)] + [DefaultValue (false)] + //[EditorBrowsable (EditorBrowsableState.Always)] + [MWFCategory("Behavior")] + public bool AutoEllipsis { + get { return this.auto_ellipsis; } + set + { + if (this.auto_ellipsis != value) { + this.auto_ellipsis = value; + + if (this.auto_ellipsis) { + text_format_flags |= TextFormatFlags.EndEllipsis; + text_format_flags &= ~TextFormatFlags.WordBreak; + } else { + text_format_flags &= ~TextFormatFlags.EndEllipsis; + text_format_flags |= TextFormatFlags.WordBreak; + } + + if (Parent != null) + Parent.PerformLayout (this, "AutoEllipsis"); + this.Invalidate (); + } + } + } + + [Browsable (true)] + //[EditorBrowsable (EditorBrowsableState.Always)] + //[DesignerSerializationVisibility (DesignerSerializationVisibility.Visible)] + [MWFCategory("Layout")] + public override bool AutoSize { + get { return base.AutoSize; } + set { base.AutoSize = value; } + } + + public override Color BackColor { + get { return base.BackColor; } + set { base.BackColor = value; } + } + + //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] + [Browsable (true)] + [MWFCategory("Appearance")] + public FlatButtonAppearance FlatAppearance { + get { return flat_button_appearance; } + } + + [Localizable(true)] + [DefaultValue(FlatStyle.Standard)] + [MWFDescription("Determines look of button"), MWFCategory("Appearance")] + public FlatStyle FlatStyle { + get { return flat_style; } + set { + if (flat_style != value) { + flat_style = value; + + if (Parent != null) + Parent.PerformLayout (this, "FlatStyle"); + Invalidate(); + } + } + } + + [Localizable(true)] + [MWFDescription("Sets image to be displayed on button face"), MWFCategory("Appearance")] + public Image Image { + get { + if (this.image != null) + return this.image; + + if (this.image_index >= 0) + if (this.image_list != null) + return this.image_list.Images[this.image_index]; + + if (!string.IsNullOrEmpty (this.image_key)) + if (this.image_list != null) + return this.image_list.Images[this.image_key]; + return null; + } + set { + if (this.image != value) { + this.image = value; + this.image_index = -1; + this.image_key = string.Empty; + this.image_list = null; + + if (this.AutoSize && this.Parent != null) + this.Parent.PerformLayout (this, "Image"); + + Invalidate (); + } + } + } + + internal bool ShouldSerializeImage () + { + return this.Image != null; + } + + [Localizable(true)] + [DefaultValue(ContentAlignment.MiddleCenter)] + [MWFDescription("Sets the alignment of the image to be displayed on button face"), MWFCategory("Appearance")] + public ContentAlignment ImageAlign { + get { return image_alignment; } + set { + if (image_alignment != value) { + image_alignment = value; + Invalidate (); + } + } + } + + [Localizable(true)] + [DefaultValue(-1)] + //[Editor("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))] + [TypeConverter(typeof(ImageIndexConverter))] + [MWFDescription("Index of image to display, if ImageList is used for button face images"), MWFCategory("Appearance")] + [RefreshProperties (RefreshProperties.Repaint)] + public int ImageIndex { + get { + if (image_list == null) + return -1; + + return image_index; + } + set { + if (this.image_index != value) { + this.image_index = value; + this.image = null; + this.image_key = string.Empty; + Invalidate (); + } + } + } + + [Localizable (true)] + [DefaultValue ("")] + //[Editor ("ShiftUI.Design.ImageIndexEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))] + [RefreshProperties (RefreshProperties.Repaint)] + [TypeConverter (typeof (ImageKeyConverter))] + [MWFCategory("Appearance")] + public string ImageKey { + get { return this.image_key; } + set { + if (this.image_key != value) { + this.image = null; + this.image_index = -1; + this.image_key = value; + this.Invalidate (); + } + } + } + + [DefaultValue(null)] + [MWFDescription("ImageList used for ImageIndex"), MWFCategory("Appearance")] + [RefreshProperties (RefreshProperties.Repaint)] + public ImageList ImageList { + get { return image_list; } + set { + if (image_list != value) { + image_list = value; + + if (value != null && image != null) + image = null; + + Invalidate (); + } + } + } + + [Browsable(false)] + //[EditorBrowsable (EditorBrowsableState.Never)] + public new ImeMode ImeMode { + get { return base.ImeMode; } + set { base.ImeMode = value; } + } + + [SettingsBindable (true)] + //[Editor ("System.ComponentModel.Design.MultilineStringEditor, " + Consts.AssemblySystem_Design, + //"System.Drawing.Design.UITypeEditor, " + Consts.AssemblySystem_Drawing)] + public override string Text { + get { return base.Text; } + set { base.Text = value; } + } + + [Localizable(true)] + [DefaultValue(ContentAlignment.MiddleCenter)] + [MWFDescription("Alignment for button text"), MWFCategory("Appearance")] + public virtual ContentAlignment TextAlign { + get { return text_alignment; } + set { + if (text_alignment != value) { + text_alignment = value; + + text_format_flags &= ~TextFormatFlags.Bottom; + text_format_flags &= ~TextFormatFlags.Top; + text_format_flags &= ~TextFormatFlags.Left; + text_format_flags &= ~TextFormatFlags.Right; + text_format_flags &= ~TextFormatFlags.HorizontalCenter; + text_format_flags &= ~TextFormatFlags.VerticalCenter; + + switch (text_alignment) { + case ContentAlignment.TopLeft: + text_format.Alignment=StringAlignment.Near; + text_format.LineAlignment=StringAlignment.Near; + break; + + case ContentAlignment.TopCenter: + text_format.Alignment=StringAlignment.Center; + text_format.LineAlignment=StringAlignment.Near; + text_format_flags |= TextFormatFlags.HorizontalCenter; + break; + + case ContentAlignment.TopRight: + text_format.Alignment=StringAlignment.Far; + text_format.LineAlignment=StringAlignment.Near; + text_format_flags |= TextFormatFlags.Right; + break; + + case ContentAlignment.MiddleLeft: + text_format.Alignment=StringAlignment.Near; + text_format.LineAlignment=StringAlignment.Center; + text_format_flags |= TextFormatFlags.VerticalCenter; + break; + + case ContentAlignment.MiddleCenter: + text_format.Alignment=StringAlignment.Center; + text_format.LineAlignment=StringAlignment.Center; + text_format_flags |= TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter; + break; + + case ContentAlignment.MiddleRight: + text_format.Alignment=StringAlignment.Far; + text_format.LineAlignment=StringAlignment.Center; + text_format_flags |= TextFormatFlags.VerticalCenter | TextFormatFlags.Right; + break; + + case ContentAlignment.BottomLeft: + text_format.Alignment=StringAlignment.Near; + text_format.LineAlignment=StringAlignment.Far; + text_format_flags |= TextFormatFlags.Bottom; + break; + + case ContentAlignment.BottomCenter: + text_format.Alignment=StringAlignment.Center; + text_format.LineAlignment=StringAlignment.Far; + text_format_flags |= TextFormatFlags.HorizontalCenter | TextFormatFlags.Bottom; + break; + + case ContentAlignment.BottomRight: + text_format.Alignment=StringAlignment.Far; + text_format.LineAlignment=StringAlignment.Far; + text_format_flags |= TextFormatFlags.Bottom | TextFormatFlags.Right; + break; + } + + Invalidate(); + } + } + } + + [Localizable (true)] + [DefaultValue (TextImageRelation.Overlay)] + [MWFCategory("Appearance")] + public TextImageRelation TextImageRelation { + get { return this.text_image_relation; } + set { + if (!Enum.IsDefined (typeof (TextImageRelation), value)) + throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for TextImageRelation", value)); + + if (this.text_image_relation != value) { + this.text_image_relation = value; + + if (this.AutoSize && this.Parent != null) + this.Parent.PerformLayout (this, "TextImageRelation"); + + this.Invalidate (); + } + } + } + + [DefaultValue (false)] + [MWFCategory("Behavior")] + public bool UseCompatibleTextRendering { + get { return use_compatible_text_rendering; } + set { + if (use_compatible_text_rendering != value) { + use_compatible_text_rendering = value; + if (Parent != null) + Parent.PerformLayout (this, "UseCompatibleTextRendering"); + Invalidate (); + } + } + } + + [DefaultValue (true)] + [MWFCategory("Appearance")] + public bool UseMnemonic { + get { return this.use_mnemonic; } + set { + if (this.use_mnemonic != value) { + this.use_mnemonic = value; + + if (this.use_mnemonic) + text_format_flags &= ~TextFormatFlags.NoPrefix; + else + text_format_flags |= TextFormatFlags.NoPrefix; + + this.Invalidate (); + } + } + } + + [MWFCategory("Appearance")] + public bool UseVisualStyleBackColor { + get { return use_visual_style_back_color; } + set { + if (use_visual_style_back_color != value) { + use_visual_style_back_color = value; + Invalidate (); + } + } + } + #endregion // Public Instance Properties + + #region Protected Properties + protected override CreateParams CreateParams { + get { return base.CreateParams; } + } + + protected override ImeMode DefaultImeMode { + get { return ImeMode.Disable; } + } + + protected override Size DefaultSize { + get { return ThemeEngine.Current.ButtonBaseDefaultSize; } + } + + protected internal bool IsDefault { + get { return is_default; } + set { + if (is_default != value) { + is_default = value; + Invalidate (); + } + } + } + #endregion // Public Instance Properties + + #region Public Methods + // The base calls into GetPreferredSizeCore, which we will override in our subclasses + public override Size GetPreferredSize (Size proposedSize) + { + return base.GetPreferredSize (proposedSize); + } + #endregion + + #region Protected Methods + protected override AccessibleObject CreateAccessibilityInstance () + { + return new ButtonBaseAccessibleObject (this); + } + + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + } + + protected override void OnEnabledChanged (EventArgs e) + { + base.OnEnabledChanged (e); + } + + protected override void OnGotFocus (EventArgs e) + { + Invalidate (); + base.OnGotFocus (e); + } + + protected override void OnKeyDown (KeyEventArgs kevent) + { + if (kevent.KeyData == Keys.Space) { + is_pressed = true; + Invalidate (); + kevent.Handled = true; + } + + base.OnKeyDown (kevent); + } + + protected override void OnKeyUp (KeyEventArgs kevent) + { + if (kevent.KeyData == Keys.Space) { + is_pressed = false; + Invalidate (); + OnClick (EventArgs.Empty); + kevent.Handled = true; + } + + base.OnKeyUp (kevent); + } + + protected override void OnLostFocus (EventArgs e) + { + Invalidate (); + base.OnLostFocus (e); + } + + protected override void OnMouseDown (MouseEventArgs mevent) + { + if ((mevent.Button & MouseButtons.Left) != 0) { + is_pressed = true; + Invalidate (); + } + + base.OnMouseDown (mevent); + } + + protected override void OnMouseEnter (EventArgs eventargs) + { + is_entered = true; + Invalidate (); + base.OnMouseEnter (eventargs); + } + + protected override void OnMouseLeave (EventArgs eventargs) + { + is_entered = false; + Invalidate (); + base.OnMouseLeave (eventargs); + } + + protected override void OnMouseMove (MouseEventArgs mevent) { + bool inside = false; + bool redraw = false; + + if (ClientRectangle.Contains (mevent.Location)) + inside = true; + + // If the button was pressed and we leave, release the button press and vice versa + if ((mevent.Button & MouseButtons.Left) != 0) { + if (this.Capture && (inside != is_pressed)) { + is_pressed = inside; + redraw = true; + } + } + + if (is_entered != inside) { + is_entered = inside; + redraw = true; + } + + if (redraw) + Invalidate (); + + base.OnMouseMove (mevent); + } + + protected override void OnMouseUp (MouseEventArgs mevent) + { + if (this.Capture && ((mevent.Button & MouseButtons.Left) != 0)) { + this.Capture = false; + + if (is_pressed) { + is_pressed = false; + Invalidate (); + } else if ((this.flat_style == FlatStyle.Flat) || (this.flat_style == FlatStyle.Popup)) { + Invalidate (); + } + + if (ClientRectangle.Contains (mevent.Location)) + if (!ValidationFailed) { + OnClick (EventArgs.Empty); + OnMouseClick (mevent); + } + } + + base.OnMouseUp (mevent); + } + + protected override void OnPaint (PaintEventArgs pevent) + { + Draw (pevent); + base.OnPaint (pevent); + } + + protected override void OnParentChanged (EventArgs e) + { + base.OnParentChanged (e); + } + + protected override void OnTextChanged (EventArgs e) + { + Invalidate (); + base.OnTextChanged (e); + } + + protected override void OnVisibleChanged (EventArgs e) + { + if (!Visible) { + is_pressed = false; + is_entered = false; + } + + base.OnVisibleChanged (e); + } + + protected void ResetFlagsandPaint () + { + // Nothing to do; MS internal + // Should we do Invalidate (); ? + } + + protected override void WndProc (ref Message m) + { + switch ((Msg)m.Msg) { + case Msg.WM_LBUTTONDBLCLK: { + HaveDoubleClick (); + break; + } + + case Msg.WM_MBUTTONDBLCLK: { + HaveDoubleClick (); + break; + } + + case Msg.WM_RBUTTONDBLCLK: { + HaveDoubleClick (); + break; + } + } + + base.WndProc (ref m); + } + #endregion // Public Instance Properties + + #region Public Events + [Browsable (true)] + //[EditorBrowsable (EditorBrowsableState.Always)] + public new event EventHandler AutoSizeChanged { + add { base.AutoSizeChanged += value; } + remove { base.AutoSizeChanged -= value; } + } + + [Browsable (false)] + //[EditorBrowsable (EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { base.ImeModeChanged += value; } + remove { base.ImeModeChanged -= value; } + } + #endregion // Events + + #region Internal Properties + internal ButtonState ButtonState { + get { + ButtonState ret = ButtonState.Normal; + + if (Enabled) { + // Popup style is only followed as long as the mouse isn't "in" the control + if (is_entered) { + if (flat_style == FlatStyle.Flat) { + ret |= ButtonState.Flat; + } + } else { + if (flat_style == FlatStyle.Flat || flat_style == FlatStyle.Popup) { + ret |= ButtonState.Flat; + } + } + + if (is_entered && is_pressed) { + ret |= ButtonState.Pushed; + } + } else { + ret |= ButtonState.Inactive; + if ((flat_style == FlatStyle.Flat) || (flat_style == FlatStyle.Popup)) { + ret |= ButtonState.Flat; + } + } + return ret; + } + } + + internal bool Pressed { + get { return this.is_pressed; } + } + + // The flags to be used for MeasureText and DrawText + internal TextFormatFlags TextFormatFlags { + get { return this.text_format_flags; } + } + #endregion + + #region Internal Methods + // Derived classes should override Draw method and we dont want + // to break the control signature, hence this approach. + internal virtual void Draw (PaintEventArgs pevent) + { + ThemeEngine.Current.DrawButtonBase (pevent.Graphics, pevent.ClipRectangle, this); + } + + internal virtual void HaveDoubleClick () + { + // override me + } + + internal override void OnPaintBackgroundInternal (PaintEventArgs e) + { + base.OnPaintBackground (e); + } + #endregion // Internal Methods + + #region ButtonBaseAccessibleObject sub-class + [ComVisible (true)] + public class ButtonBaseAccessibleObject : WidgetAccessibleObject + { + #region ButtonBaseAccessibleObject Local Variables + private new Widget owner; + #endregion // ButtonBaseAccessibleObject Local Variables + + #region ButtonBaseAccessibleObject Constructors + public ButtonBaseAccessibleObject (Widget owner) : base (owner) + { + if (owner == null) + throw new ArgumentNullException ("owner"); + + this.owner = owner; + default_action = "Press"; + role = AccessibleRole.PushButton; + } + #endregion // ButtonBaseAccessibleObject Constructors + + #region Public Properties + public override AccessibleStates State { + get { return base.State; } + } + #endregion + + #region ButtonBaseAccessibleObject Methods + public override void DoDefaultAction () + { + ((ButtonBase)owner).OnClick (EventArgs.Empty); + } + #endregion // ButtonBaseAccessibleObject Methods + } + #endregion // ButtonBaseAccessibleObject sub-class + } +} diff --git a/source/ShiftUI/Internal/ButtonRenderer.cs b/source/ShiftUI/Internal/ButtonRenderer.cs new file mode 100644 index 0000000..a2e4237 --- /dev/null +++ b/source/ShiftUI/Internal/ButtonRenderer.cs @@ -0,0 +1,153 @@ +// +// ButtonRenderer.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System.Drawing; +using ShiftUI.VisualStyles; +using System; + +namespace ShiftUI +{ + public sealed class ButtonRenderer + { + private static bool always_use_visual_styles = false; + + #region Private Constructor + private ButtonRenderer () { } + #endregion + + #region Public Static Methods + public static void DrawButton (Graphics g, Rectangle bounds, PushButtonState state) + { + DrawButton (g, bounds, String.Empty, null, TextFormatFlags.Default, null, Rectangle.Empty, false, state); + } + + public static void DrawButton (Graphics g, Rectangle bounds, bool focused, PushButtonState state) + { + DrawButton (g, bounds, String.Empty, null, TextFormatFlags.Default, null, Rectangle.Empty, focused, state); + } + + public static void DrawButton (Graphics g, Rectangle bounds, Image image, Rectangle imageBounds, bool focused, PushButtonState state) + { + DrawButton (g, bounds, String.Empty, null, TextFormatFlags.Default, image, imageBounds, focused, state); + } + + public static void DrawButton (Graphics g, Rectangle bounds, string buttonText, Font font, bool focused, PushButtonState state) + { + DrawButton (g, bounds, buttonText, font, TextFormatFlags.HorizontalCenter, null, Rectangle.Empty, focused, state); + } + + public static void DrawButton (Graphics g, Rectangle bounds, string buttonText, Font font, TextFormatFlags flags, bool focused, PushButtonState state) + { + DrawButton (g, bounds, buttonText, font, flags, null, Rectangle.Empty, focused, state); + } + + public static void DrawButton (Graphics g, Rectangle bounds, string buttonText, Font font, Image image, Rectangle imageBounds, bool focused, PushButtonState state) + { + DrawButton (g, bounds, buttonText, font, TextFormatFlags.HorizontalCenter, image, imageBounds, focused, state); + } + + public static void DrawButton (Graphics g, Rectangle bounds, string buttonText, Font font, TextFormatFlags flags, Image image, Rectangle imageBounds, bool focused, PushButtonState state) + { + if (Application.RenderWithVisualStyles || always_use_visual_styles == true) { + VisualStyleRenderer vsr = GetPushButtonRenderer (state); + + vsr.DrawBackground (g, bounds); + + if (image != null) + vsr.DrawImage (g, imageBounds, image); + } else { + if (state == PushButtonState.Pressed) + WidgetPaint.DrawButton (g, bounds, ButtonState.Pushed); + else + WidgetPaint.DrawButton (g, bounds, ButtonState.Normal); + + if (image != null) + g.DrawImage (image, imageBounds); + } + + Rectangle focus_rect = bounds; + focus_rect.Inflate (-3, -3); + + if (focused) + WidgetPaint.DrawFocusRectangle (g, focus_rect); + + if (buttonText != String.Empty) + if (state == PushButtonState.Disabled) + TextRenderer.DrawText (g, buttonText, font, focus_rect, SystemColors.GrayText, flags); + else + TextRenderer.DrawText (g, buttonText, font, focus_rect, SystemColors.ControlText, flags); + } + + public static bool IsBackgroundPartiallyTransparent (PushButtonState state) + { + if (!VisualStyleRenderer.IsSupported) + return false; + + VisualStyleRenderer vsr = GetPushButtonRenderer (state); + + return vsr.IsBackgroundPartiallyTransparent (); + } + + public static void DrawParentBackground (Graphics g, Rectangle bounds, Widget childControl) + { + if (!VisualStyleRenderer.IsSupported) + return; + + VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.Button.PushButton.Default); + + vsr.DrawParentBackground (g, bounds, childControl); + } + #endregion + + #region Private Static Methods + internal static VisualStyleRenderer GetPushButtonRenderer (PushButtonState state) + { + switch (state) { + case PushButtonState.Normal: + return new VisualStyleRenderer (VisualStyleElement.Button.PushButton.Normal); + case PushButtonState.Hot: + return new VisualStyleRenderer (VisualStyleElement.Button.PushButton.Hot); + case PushButtonState.Pressed: + return new VisualStyleRenderer (VisualStyleElement.Button.PushButton.Pressed); + case PushButtonState.Disabled: + return new VisualStyleRenderer (VisualStyleElement.Button.PushButton.Disabled); + case PushButtonState.Default: + default: + return new VisualStyleRenderer (VisualStyleElement.Button.PushButton.Default); + } + } + #endregion + + #region Public Static Properties + public static bool RenderMatchingApplicationState { + get { return !always_use_visual_styles; } + set { always_use_visual_styles = !value; } + } + #endregion + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/CheckBoxRenderer.cs b/source/ShiftUI/Internal/CheckBoxRenderer.cs new file mode 100644 index 0000000..8800aa0 --- /dev/null +++ b/source/ShiftUI/Internal/CheckBoxRenderer.cs @@ -0,0 +1,194 @@ +// +// CheckBoxRenderer.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System.Drawing; +using ShiftUI.VisualStyles; +using System; + +namespace ShiftUI +{ + public sealed class CheckBoxRenderer + { + private static bool always_use_visual_styles = false; + + #region Private Constructor + private CheckBoxRenderer () {} + #endregion + + #region Public Static Methods + public static void DrawCheckBox (Graphics g, Point glyphLocation, CheckBoxState state) + { + DrawCheckBox (g, glyphLocation, Rectangle.Empty, String.Empty, null, TextFormatFlags.HorizontalCenter, null, Rectangle.Empty, false, state); + } + + public static void DrawCheckBox (Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, bool focused, CheckBoxState state) + { + DrawCheckBox (g, glyphLocation, textBounds, checkBoxText, font, TextFormatFlags.HorizontalCenter, null, Rectangle.Empty, focused, state); + } + + public static void DrawCheckBox (Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, TextFormatFlags flags, bool focused, CheckBoxState state) + { + DrawCheckBox (g, glyphLocation, textBounds, checkBoxText, font, flags, null, Rectangle.Empty, focused, state); + } + + public static void DrawCheckBox (Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, Image image, Rectangle imageBounds, bool focused, CheckBoxState state) + { + DrawCheckBox (g, glyphLocation, textBounds, checkBoxText, font, TextFormatFlags.HorizontalCenter, image, imageBounds, focused, state); + } + + public static void DrawCheckBox (Graphics g, Point glyphLocation, Rectangle textBounds, string checkBoxText, Font font, TextFormatFlags flags, Image image, Rectangle imageBounds, bool focused, CheckBoxState state) + { + Rectangle bounds = new Rectangle (glyphLocation, GetGlyphSize (g, state)); + + if (Application.RenderWithVisualStyles || always_use_visual_styles == true) { + VisualStyleRenderer vsr = GetCheckBoxRenderer (state); + + vsr.DrawBackground (g, bounds); + + if (image != null) + vsr.DrawImage (g, imageBounds, image); + + if (focused) + WidgetPaint.DrawFocusRectangle (g, textBounds); + + if (checkBoxText != String.Empty) + if (state == CheckBoxState.CheckedDisabled || state == CheckBoxState.MixedDisabled || state == CheckBoxState.UncheckedDisabled) + TextRenderer.DrawText (g, checkBoxText, font, textBounds, SystemColors.GrayText, flags); + else + TextRenderer.DrawText (g, checkBoxText, font, textBounds, SystemColors.ControlText, flags); + } else { + switch (state) { + case CheckBoxState.CheckedDisabled: + case CheckBoxState.MixedDisabled: + case CheckBoxState.MixedPressed: + WidgetPaint.DrawCheckBox (g, bounds, ButtonState.Inactive | ButtonState.Checked); + break; + case CheckBoxState.CheckedHot: + case CheckBoxState.CheckedNormal: + WidgetPaint.DrawCheckBox (g, bounds, ButtonState.Checked); + break; + case CheckBoxState.CheckedPressed: + WidgetPaint.DrawCheckBox (g, bounds, ButtonState.Pushed | ButtonState.Checked); + break; + case CheckBoxState.MixedHot: + case CheckBoxState.MixedNormal: + WidgetPaint.DrawMixedCheckBox (g, bounds, ButtonState.Checked); + break; + case CheckBoxState.UncheckedDisabled: + case CheckBoxState.UncheckedPressed: + WidgetPaint.DrawCheckBox (g, bounds, ButtonState.Inactive); + break; + case CheckBoxState.UncheckedHot: + case CheckBoxState.UncheckedNormal: + WidgetPaint.DrawCheckBox (g, bounds, ButtonState.Normal); + break; + } + + if (image != null) + g.DrawImage (image, imageBounds); + + if (focused) + WidgetPaint.DrawFocusRectangle (g, textBounds); + + if (checkBoxText != String.Empty) + TextRenderer.DrawText (g, checkBoxText, font, textBounds, SystemColors.ControlText, flags); + } + } + + public static bool IsBackgroundPartiallyTransparent (CheckBoxState state) + { + if (!VisualStyleRenderer.IsSupported) + return false; + + VisualStyleRenderer vsr = GetCheckBoxRenderer (state); + + return vsr.IsBackgroundPartiallyTransparent (); + } + + public static void DrawParentBackground (Graphics g, Rectangle bounds, Widget childControl) + { + if (!VisualStyleRenderer.IsSupported) + return; + + VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.UncheckedNormal); + + vsr.DrawParentBackground (g, bounds, childControl); + } + + public static Size GetGlyphSize (Graphics g, CheckBoxState state) + { + if (!VisualStyleRenderer.IsSupported) + return new Size (13, 13); + + VisualStyleRenderer vsr = GetCheckBoxRenderer (state); + + return vsr.GetPartSize (g, ThemeSizeType.Draw); + } + #endregion + + #region Private Static Methods + private static VisualStyleRenderer GetCheckBoxRenderer (CheckBoxState state) + { + switch (state) { + case CheckBoxState.CheckedDisabled: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.CheckedDisabled); + case CheckBoxState.CheckedHot: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.CheckedHot); + case CheckBoxState.CheckedNormal: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.CheckedNormal); + case CheckBoxState.CheckedPressed: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.CheckedPressed); + case CheckBoxState.MixedDisabled: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.MixedDisabled); + case CheckBoxState.MixedHot: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.MixedHot); + case CheckBoxState.MixedNormal: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.MixedNormal); + case CheckBoxState.MixedPressed: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.MixedPressed); + case CheckBoxState.UncheckedDisabled: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.UncheckedDisabled); + case CheckBoxState.UncheckedHot: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.UncheckedHot); + case CheckBoxState.UncheckedNormal: + default: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.UncheckedNormal); + case CheckBoxState.UncheckedPressed: + return new VisualStyleRenderer (VisualStyleElement.Button.CheckBox.UncheckedPressed); + } + } + #endregion + + #region Public Static Properties + public static bool RenderMatchingApplicationState { + get { return !always_use_visual_styles; } + set { always_use_visual_styles = !value; } + } + #endregion + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/Clipboard.cs b/source/ShiftUI/Internal/Clipboard.cs new file mode 100644 index 0000000..9c832a8 --- /dev/null +++ b/source/ShiftUI/Internal/Clipboard.cs @@ -0,0 +1,433 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System; +using System.Drawing; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Threading; + +namespace ShiftUI { + public sealed class Clipboard { + #region Local Variables + #endregion // Local Variables + + #region Constructors + private Clipboard() { + } + #endregion // Constructors + + #region Private Methods + private static bool ConvertToClipboardData(ref int type, object obj, out byte[] data) { + data = null; + return false; + } + + private static bool ConvertFromClipboardData(int type, IntPtr data, out object obj) { + obj = null; + if (data == IntPtr.Zero) { + return false; + } + return false; + } + #endregion // Private Methods + + #region Public Static Methods + public static void Clear () + { + IntPtr clipboard_handle; + + clipboard_handle = XplatUI.ClipboardOpen (false); + XplatUI.ClipboardStore (clipboard_handle, null, 0, null, false); + } + + public static bool ContainsAudio () + { + return ClipboardContainsFormat (DataFormats.WaveAudio); + } + + public static bool ContainsData (string format) + { + return ClipboardContainsFormat (format); + } + + public static bool ContainsFileDropList () + { + return ClipboardContainsFormat (DataFormats.FileDrop); + } + + public static bool ContainsImage () + { + return ClipboardContainsFormat (DataFormats.Bitmap); + } + + public static bool ContainsText () + { + return ClipboardContainsFormat (DataFormats.Text, DataFormats.UnicodeText); + } + + public static bool ContainsText (TextDataFormat format) + { + switch (format) { + case TextDataFormat.Text: + return ClipboardContainsFormat (DataFormats.Text); + case TextDataFormat.UnicodeText: + return ClipboardContainsFormat (DataFormats.UnicodeText); + case TextDataFormat.Rtf: + return ClipboardContainsFormat (DataFormats.Rtf); + case TextDataFormat.Html: + return ClipboardContainsFormat (DataFormats.Html); + case TextDataFormat.CommaSeparatedValue: + return ClipboardContainsFormat (DataFormats.CommaSeparatedValue); + } + + return false; + } + + public static Stream GetAudioStream () + { + IDataObject data = GetDataObject (); + + if (data == null) + return null; + + return (Stream)data.GetData (DataFormats.WaveAudio, true); + } + + public static Object GetData (string format) + { + IDataObject data = GetDataObject (); + + if (data == null) + return null; + + return data.GetData (format, true); + } + + public static IDataObject GetDataObject () + { + return GetDataObject (false); + } + + public static StringCollection GetFileDropList () + { + IDataObject data = GetDataObject (); + + if (data == null) + return null; + + return (StringCollection)data.GetData (DataFormats.FileDrop, true); + } + + public static Image GetImage () + { + IDataObject data = GetDataObject (); + + if (data == null) + return null; + + return (Image)data.GetData (DataFormats.Bitmap, true); + } + + public static string GetText () + { + return GetText (TextDataFormat.UnicodeText); + } + + public static string GetText (TextDataFormat format) + { + if (!Enum.IsDefined (typeof (TextDataFormat), format)) + throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for TextDataFormat", format)); + + IDataObject data = GetDataObject (); + + if (data == null) + return string.Empty; + + string retval; + + switch (format) { + case TextDataFormat.Text: + default: + retval = (string)data.GetData (DataFormats.Text, true); + break; + case TextDataFormat.UnicodeText: + retval = (string)data.GetData (DataFormats.UnicodeText, true); + break; + case TextDataFormat.Rtf: + retval = (string)data.GetData (DataFormats.Rtf, true); + break; + case TextDataFormat.Html: + retval = (string)data.GetData (DataFormats.Html, true); + break; + case TextDataFormat.CommaSeparatedValue: + retval = (string)data.GetData (DataFormats.CommaSeparatedValue, true); + break; + } + + return retval == null ? string.Empty : retval; + } + + public static void SetAudio (byte[] audioBytes) + { + if (audioBytes == null) + throw new ArgumentNullException ("audioBytes"); + + MemoryStream ms = new MemoryStream (audioBytes); + + SetAudio (ms); + } + + public static void SetAudio (Stream audioStream) + { + if (audioStream == null) + throw new ArgumentNullException ("audioStream"); + + SetData (DataFormats.WaveAudio, audioStream); + } + + public static void SetData (string format, Object data) + { + if (data == null) + throw new ArgumentNullException ("data"); + + DataObject data_object = new DataObject (format, data); + SetDataObject (data_object); + } + + public static void SetDataObject(object data) { + SetDataObject(data, false); // MSDN says default behavior is to place non-persistent data to clipboard + } + + public static void SetDataObject(object data, bool copy) { + SetDataObject(data, copy, 10, 100); // MSDN says default behavior is to try 10 times with 100 ms delay + } + + internal static void SetDataObjectImpl(object data, bool copy) { + IntPtr clipboard_handle; + XplatUI.ObjectToClipboard converter; + int native_format; + DataFormats.Format item_format; + + converter = new XplatUI.ObjectToClipboard(ConvertToClipboardData); + + clipboard_handle = XplatUI.ClipboardOpen(false); + XplatUI.ClipboardStore(clipboard_handle, null, 0, null, copy); // Empty clipboard + + native_format = -1; + + if (data is IDataObject) { + string[] formats; + + IDataObject data_object = data as IDataObject; + formats = data_object.GetFormats(); + for (int i = 0; i < formats.Length; i++) { + item_format = DataFormats.GetFormat(formats[i]); + if ((item_format != null) && (item_format.Name != DataFormats.StringFormat)) { + native_format = item_format.Id; + } + + object obj = data_object.GetData (formats [i]); + + // this is used only by custom formats + if (IsDataSerializable (obj)) + item_format.is_serializable = true; + + XplatUI.ClipboardStore(clipboard_handle, obj, native_format, converter, copy); + } + } else { + item_format = DataFormats.Format.Find(data.GetType().FullName); + if ((item_format != null) && (item_format.Name != DataFormats.StringFormat)) { + native_format = item_format.Id; + } + + XplatUI.ClipboardStore(clipboard_handle, data, native_format, converter, copy); + } + XplatUI.ClipboardClose(clipboard_handle); + } + + static bool IsDataSerializable (object obj) + { + if (obj is ISerializable) + return true; + + AttributeCollection attrs = TypeDescriptor.GetAttributes (obj); + return attrs [typeof (SerializableAttribute)] != null; + } + + public static void SetDataObject(object data, bool copy, int retryTimes, int retryDelay) + { + if (data == null) + throw new ArgumentNullException("data"); + if (retryTimes < 0) + throw new ArgumentOutOfRangeException("retryTimes"); + if (retryDelay < 0) + throw new ArgumentOutOfRangeException("retryDelay"); + + // MS implementation actually puts data to clipboard even when retryTimes == 0 + bool retry = true; + do + { + retry = false; + --retryTimes; + try + { + SetDataObjectImpl(data, copy); + } catch (ExternalException) { + if (retryTimes <= 0) + throw; + retry = true; + Thread.Sleep(retryDelay); + } + } while (retry && retryTimes > 0); + } + + [MonoInternalNote ("Needs additional checks for valid paths, see MSDN")] + public static void SetFileDropList (StringCollection filePaths) + { + if (filePaths == null) + throw new ArgumentNullException ("filePaths"); + + SetData (DataFormats.FileDrop, filePaths); + } + + public static void SetImage (Image image) + { + if (image == null) + throw new ArgumentNullException ("image"); + + SetData (DataFormats.Bitmap, image); + } + + public static void SetText (string text) + { + if (string.IsNullOrEmpty (text)) + throw new ArgumentNullException ("text"); + + SetData (DataFormats.UnicodeText, text); + } + + public static void SetText (string text, TextDataFormat format) + { + if (string.IsNullOrEmpty (text)) + throw new ArgumentNullException ("text"); + if (!Enum.IsDefined (typeof (TextDataFormat), format)) + throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for TextDataFormat", format)); + + switch (format) { + case TextDataFormat.Text: + SetData (DataFormats.Text, text); + break; + case TextDataFormat.UnicodeText: + SetData (DataFormats.UnicodeText, text); + break; + case TextDataFormat.Rtf: + SetData (DataFormats.Rtf, text); + break; + case TextDataFormat.Html: + SetData (DataFormats.Html, text); + break; + case TextDataFormat.CommaSeparatedValue: + SetData (DataFormats.CommaSeparatedValue, text); + break; + } + } + #endregion // Public Static Methods + + #region Internal Static Methods + internal static IDataObject GetDataObject (bool primary_selection) + { + DataObject clipboard; + IntPtr clipboard_handle; + int[] native_formats; + DataFormats.Format item_format; + object managed_clipboard_item; + XplatUI.ClipboardToObject converter; + + converter = new XplatUI.ClipboardToObject (ConvertFromClipboardData); + + clipboard_handle = XplatUI.ClipboardOpen (primary_selection); + native_formats = XplatUI.ClipboardAvailableFormats (clipboard_handle); + if (native_formats == null) { + return null; // Clipboard empty + } + + // Build the IDataObject + clipboard = new DataObject (); + for (int i = 0; i < native_formats.Length; i++) { + // We might get a format we don't understand or know + item_format = DataFormats.GetFormat (native_formats[i]); + + if (item_format != null) { + managed_clipboard_item = XplatUI.ClipboardRetrieve (clipboard_handle, native_formats[i], converter); + + if (managed_clipboard_item != null) { + clipboard.SetData (item_format.Name, managed_clipboard_item); + // We don't handle 'bitmap' since it involves handles, so we'll equate it to dib + if (item_format.Name == DataFormats.Dib) { + clipboard.SetData (DataFormats.Bitmap, managed_clipboard_item); + } + } + } + } + + XplatUI.ClipboardClose (clipboard_handle); + + return clipboard; + } + + internal static bool ClipboardContainsFormat (params string[] formats) + { + IntPtr clipboard_handle; + int[] native_formats; + DataFormats.Format item_format; + + clipboard_handle = XplatUI.ClipboardOpen (false); + native_formats = XplatUI.ClipboardAvailableFormats (clipboard_handle); + + if (native_formats == null) + return false; + + foreach (int i in native_formats) { + // We might get a format we don't understand or know + item_format = DataFormats.GetFormat (i); + + if (item_format != null) + if (((IList)formats).Contains (item_format.Name)) + return true; + } + + return false; + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/Consts.cs b/source/ShiftUI/Internal/Consts.cs new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/source/ShiftUI/Internal/Consts.cs diff --git a/source/ShiftUI/Internal/ContextMenuStrip.cs b/source/ShiftUI/Internal/ContextMenuStrip.cs new file mode 100644 index 0000000..052bb04 --- /dev/null +++ b/source/ShiftUI/Internal/ContextMenuStrip.cs @@ -0,0 +1,80 @@ +// +// ContextMenuStrip.cs +// +// 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) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace ShiftUI +{ + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDispatch)] + [DefaultEvent ("Opening")] + public class ContextMenuStrip : ToolStripDropDownMenu + { + Widget source_Widget; + internal Widget container; + + #region Public Construtors + public ContextMenuStrip () : base () + { + source_Widget = null; + } + + public ContextMenuStrip (IContainer container) : base () + { + source_Widget = null; + } + #endregion + + #region Public Properties + [Browsable (false)] + //[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] + public Widget SourceWidget { + get { return this.source_Widget; } + } + #endregion + + #region Protected Methods + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + } + + protected override void SetVisibleCore (bool visible) + { + base.SetVisibleCore (visible); + if (visible) + XplatUI.SetTopmost (this.Handle, true); + } + #endregion + + internal void SetSourceWidget (Widget source_Widget) + { + container = this.source_Widget = source_Widget; + } + } +} diff --git a/source/ShiftUI/Internal/ControlBindingsCollection.cs b/source/ShiftUI/Internal/ControlBindingsCollection.cs new file mode 100644 index 0000000..345efee --- /dev/null +++ b/source/ShiftUI/Internal/ControlBindingsCollection.cs @@ -0,0 +1,204 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + + +using System; +using System.Collections; +using System.ComponentModel; +using System.Reflection; + + +namespace ShiftUI { + [DefaultEvent("CollectionChanged")] + public class WidgetBindingsCollection : BindingsCollection { + #region Fields + private Widget control; + private IBindableComponent bindable_component; + private DataSourceUpdateMode default_datasource_update_mode; + #endregion // Fields + + #region Constructors + internal WidgetBindingsCollection (Widget control) { + this.control = control; + bindable_component = this.control as IBindableComponent; + default_datasource_update_mode = DataSourceUpdateMode.OnValidation; + } + + public WidgetBindingsCollection (IBindableComponent control) + { + bindable_component = control; + control = control as Widget; + default_datasource_update_mode = DataSourceUpdateMode.OnValidation; + } + #endregion // Constructors + + #region Public Instance Properties + public Widget Widget { + get { + return control; + } + } + + public Binding this[string propertyName] { + get { + foreach (Binding b in base.List) { + if (b.PropertyName == propertyName) { + return b; + } + } + return null; + } + } + + public IBindableComponent BindableComponent { + get { + return bindable_component; + } + } + + public DataSourceUpdateMode DefaultDataSourceUpdateMode { + get { + return default_datasource_update_mode; + } + set { + default_datasource_update_mode = value; + } + } + #endregion // Public Instance Properties + + #region Public Instance Methods + public new void Add (Binding binding) + { + AddCore (binding); + OnCollectionChanged (new CollectionChangeEventArgs (CollectionChangeAction.Add, binding)); + } + + public Binding Add (string propertyName, object dataSource, string dataMember) + { + if (dataSource == null) + throw new ArgumentNullException ("dataSource"); + + Binding res = new Binding (propertyName, dataSource, dataMember); + res.DataSourceUpdateMode = default_datasource_update_mode; + Add (res); + return res; + } + + public Binding Add (string propertyName, object dataSource, string dataMember, bool formattingEnabled) + { + return Add (propertyName, dataSource, dataMember, formattingEnabled, default_datasource_update_mode, null, String.Empty, null); + } + + public Binding Add (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode) + { + return Add (propertyName, dataSource, dataMember, formattingEnabled, updateMode, null, String.Empty, null); + } + + public Binding Add (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode, + object nullValue) + { + return Add (propertyName, dataSource, dataMember, formattingEnabled, updateMode, nullValue, String.Empty, null); + } + + public Binding Add (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode, + object nullValue, string formatString) + { + return Add (propertyName, dataSource, dataMember, formattingEnabled, updateMode, nullValue, formatString, null); + } + + public Binding Add (string propertyName, object dataSource, string dataMember, bool formattingEnabled, DataSourceUpdateMode updateMode, + object nullValue, string formatString, IFormatProvider formatInfo) + { + if (dataSource == null) + throw new ArgumentNullException ("dataSource"); + + Binding res = new Binding (propertyName, dataSource, dataMember); + res.FormattingEnabled = formattingEnabled; + res.DataSourceUpdateMode = updateMode; + res.NullValue = nullValue; + res.FormatString = formatString; + res.FormatInfo = formatInfo; + + Add (res); + return res; + } + + public new void Clear() { + base.Clear(); + } + + public new void Remove (Binding binding) { + if (binding == null) + throw new NullReferenceException("The binding is null"); + + base.Remove(binding); + } + + public new void RemoveAt(int index) { + if (index < 0 || index >= base.List.Count) + throw new ArgumentOutOfRangeException("index"); + + base.RemoveAt(index); + } + #endregion // Public Instance Methods + + #region Protected Instance Methods + protected override void AddCore (Binding dataBinding) + { + if (dataBinding == null) + throw new ArgumentNullException ("dataBinding"); + + if (dataBinding.Widget != null && dataBinding.BindableComponent != bindable_component) + throw new ArgumentException ("dataBinding belongs to another BindingsCollection"); + + for (int i = 0; i < Count; i++) { + Binding bnd = this [i]; + if (bnd == null || bnd.PropertyName.Length == 0 || dataBinding.PropertyName.Length == 0) { + continue; + } + + if (String.Compare (bnd.PropertyName, dataBinding.PropertyName, true) == 0) { + throw new ArgumentException ("The binding is already in the collection"); + } + } + + dataBinding.SetWidget (bindable_component); + dataBinding.Check (); + base.AddCore (dataBinding); + } + + protected override void ClearCore() { + base.ClearCore (); + } + + protected override void RemoveCore (Binding dataBinding) { + if (dataBinding == null) + throw new ArgumentNullException ("dataBinding"); + + base.RemoveCore (dataBinding); + } + #endregion // Protected Instance Methods + } +} + diff --git a/source/ShiftUI/Internal/ControlEventArgs.cs b/source/ShiftUI/Internal/ControlEventArgs.cs new file mode 100644 index 0000000..17eb992 --- /dev/null +++ b/source/ShiftUI/Internal/ControlEventArgs.cs @@ -0,0 +1,49 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// +// + + +// COMPLETE +using System; + +namespace ShiftUI { + public class WidgetEventArgs : EventArgs { + private Widget control; + + #region Public Constructors + public WidgetEventArgs(Widget control) { + this.control=control; + } + #endregion // Public Constructors + + #region Public Instance Properties + public Widget Widget { + get { + return this.control; + } + } + #endregion // Public Instance Properties + } +} diff --git a/source/ShiftUI/Internal/ControlEventHandler.cs b/source/ShiftUI/Internal/ControlEventHandler.cs new file mode 100644 index 0000000..faed044 --- /dev/null +++ b/source/ShiftUI/Internal/ControlEventHandler.cs @@ -0,0 +1,32 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// +// + + +// COMPLETE + +namespace ShiftUI { + public delegate void WidgetEventHandler (object sender, WidgetEventArgs e); +} diff --git a/source/ShiftUI/Internal/ControlHandler.cs b/source/ShiftUI/Internal/ControlHandler.cs new file mode 100644 index 0000000..08be9bd --- /dev/null +++ b/source/ShiftUI/Internal/ControlHandler.cs @@ -0,0 +1,229 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace ShiftUI.CarbonInternal { + internal class WidgetHandler : EventHandlerBase, IEventHandler { + internal const uint kEventWidgetInitialize = 1000; + internal const uint kEventWidgetDispose = 1001; + internal const uint kEventWidgetGetOptimalBounds = 1003; + internal const uint kEventWidgetDefInitialize = kEventWidgetInitialize; + internal const uint kEventWidgetDefDispose = kEventWidgetDispose; + internal const uint kEventWidgetHit = 1; + internal const uint kEventWidgetsimulateHit = 2; + internal const uint kEventWidgetHitTest = 3; + internal const uint kEventWidgetDraw = 4; + internal const uint kEventWidgetApplyBackground = 5; + internal const uint kEventWidgetApplyTextColor = 6; + internal const uint kEventWidgetsetFocusPart = 7; + internal const uint kEventWidgetGetFocusPart = 8; + internal const uint kEventWidgetActivate = 9; + internal const uint kEventWidgetDeactivate = 10; + internal const uint kEventWidgetsetCursor = 11; + internal const uint kEventWidgetContextualMenuClick = 12; + internal const uint kEventWidgetClick = 13; + internal const uint kEventWidgetGetNextFocusCandidate = 14; + internal const uint kEventWidgetGetAutoToggleValue = 15; + internal const uint kEventWidgetInterceptSubviewClick = 16; + internal const uint kEventWidgetGetClickActivation = 17; + internal const uint kEventWidgetDragEnter = 18; + internal const uint kEventWidgetDragWithin = 19; + internal const uint kEventWidgetDragLeave = 20; + internal const uint kEventWidgetDragReceive = 21; + internal const uint kEventWidgetInvalidateForSizeChange = 22; + internal const uint kEventWidgetTrackingAreaEntered = 23; + internal const uint kEventWidgetTrackingAreaExited = 24; + internal const uint kEventWidgetTrack = 51; + internal const uint kEventWidgetGetScrollToHereStartPoint = 52; + internal const uint kEventWidgetGetIndicatorDragConstraint = 53; + internal const uint kEventWidgetIndicatorMoved = 54; + internal const uint kEventWidgetGhostingFinished = 55; + internal const uint kEventWidgetGetActionProcPart = 56; + internal const uint kEventWidgetGetPartRegion = 101; + internal const uint kEventWidgetGetPartBounds = 102; + internal const uint kEventWidgetsetData = 103; + internal const uint kEventWidgetGetData = 104; + internal const uint kEventWidgetGetSizeConstraints= 105; + internal const uint kEventWidgetGetFrameMetrics = 106; + internal const uint kEventWidgetValueFieldChanged = 151; + internal const uint kEventWidgetAddedSubWidget = 152; + internal const uint kEventWidgetRemovingSubWidget = 153; + internal const uint kEventWidgetBoundsChanged = 154; + internal const uint kEventWidgetVisibilityChanged = 157; + internal const uint kEventWidgetTitleChanged = 158; + internal const uint kEventWidgetOwningWindowChanged = 159; + internal const uint kEventWidgetHiliteChanged = 160; + internal const uint kEventWidgetEnabledStateChanged = 161; + internal const uint kEventWidgetLayoutInfoChanged = 162; + internal const uint kEventWidgetArbitraryMessage = 201; + + internal const uint kEventParamCGContextRef = 1668183160; + internal const uint kEventParamDirectObject = 757935405; + internal const uint kEventParamWidgetPart = 1668313716; + internal const uint kEventParamWidgetLikesDrag = 1668047975; + internal const uint kEventParamRgnHandle = 1919381096; + internal const uint typeWidgetRef = 1668575852; + internal const uint typeCGContextRef = 1668183160; + internal const uint typeQDPoint = 1363439732; + internal const uint typeQDRgnHandle = 1919381096; + internal const uint typeWidgetPartCode = 1668313716; + internal const uint typeBoolean = 1651470188; + + internal WidgetHandler (XplatUICarbon driver) : base (driver) {} + + public bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) { + Hwnd hwnd; + bool client; + + GetEventParameter (eventref, kEventParamDirectObject, typeWidgetRef, IntPtr.Zero, (uint) Marshal.SizeOf (typeof (IntPtr)), IntPtr.Zero, ref handle); + hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) + return false; + + msg.hwnd = hwnd.Handle; + client = (hwnd.ClientWindow == handle ? true : false); + + switch (kind) { + case kEventWidgetDraw: { + IntPtr rgn = IntPtr.Zero; + HIRect bounds = new HIRect (); + + GetEventParameter (eventref, kEventParamRgnHandle, typeQDRgnHandle, IntPtr.Zero, (uint) Marshal.SizeOf (typeof (IntPtr)), IntPtr.Zero, ref rgn); + + if (rgn != IntPtr.Zero) { + Rect rbounds = new Rect (); + + GetRegionBounds (rgn, ref rbounds); + + bounds.origin.x = rbounds.left; + bounds.origin.y = rbounds.top; + bounds.size.width = rbounds.right - rbounds.left; + bounds.size.height = rbounds.bottom - rbounds.top; + } else { + HIViewGetBounds (handle, ref bounds); + } + + if (!hwnd.visible) { + if (client) { + hwnd.expose_pending = false; + } else { + hwnd.nc_expose_pending = false; + } + return false; + } + + if (!client) { + DrawBorders (hwnd); + } + + Driver.AddExpose (hwnd, client, bounds); + + return true; + } + case kEventWidgetVisibilityChanged: { + if (client) { + msg.message = Msg.WM_SHOWWINDOW; + msg.lParam = (IntPtr) 0; + msg.wParam = (HIViewIsVisible (handle) ? (IntPtr)1 : (IntPtr)0); + return true; + } + return false; + } + case kEventWidgetBoundsChanged: { + HIRect view_frame = new HIRect (); + + HIViewGetFrame (handle, ref view_frame); + if (!client) { + hwnd.X = (int) view_frame.origin.x; + hwnd.Y = (int) view_frame.origin.y; + hwnd.Width = (int) view_frame.size.width; + hwnd.Height = (int) view_frame.size.height; + Driver.PerformNCCalc (hwnd); + } + + msg.message = Msg.WM_WINDOWPOSCHANGED; + msg.hwnd = hwnd.Handle; + + return true; + } + case kEventWidgetGetFocusPart: { + short pcode = 0; + SetEventParameter (eventref, kEventParamWidgetPart, typeWidgetPartCode, (uint)Marshal.SizeOf (typeof (short)), ref pcode); + return false; + } + case kEventWidgetDragEnter: + case kEventWidgetDragWithin: + case kEventWidgetDragLeave: + case kEventWidgetDragReceive: + return Dnd.HandleEvent (callref, eventref, handle, kind, ref msg); + } + return false; + } + + private void DrawBorders (Hwnd hwnd) { + switch (hwnd.border_style) { + case FormBorderStyle.Fixed3D: { + Graphics g; + + g = Graphics.FromHwnd(hwnd.whole_window); + if (hwnd.border_static) + WidgetPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), Border3DStyle.SunkenOuter); + else + WidgetPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), Border3DStyle.Sunken); + g.Dispose(); + break; + } + + case FormBorderStyle.FixedSingle: { + Graphics g; + + g = Graphics.FromHwnd(hwnd.whole_window); + WidgetPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), Color.Black, ButtonBorderStyle.Solid); + g.Dispose(); + break; + } + } + } + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetRegionBounds (IntPtr rgnhandle, ref Rect region); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref IntPtr data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int SetEventParameter (IntPtr eventref, uint name, uint type, uint size, ref short data); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int HIViewGetBounds (IntPtr handle, ref HIRect rect); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int HIViewGetFrame (IntPtr handle, ref HIRect rect); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static bool HIViewIsVisible (IntPtr vHnd); + } +} diff --git a/source/ShiftUI/Internal/ControlPaint.cs b/source/ShiftUI/Internal/ControlPaint.cs new file mode 100644 index 0000000..d469f51 --- /dev/null +++ b/source/ShiftUI/Internal/ControlPaint.cs @@ -0,0 +1,495 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + + +// NOT COMPLETE + +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System; + +namespace ShiftUI { + public sealed class WidgetPaint { + #region Local Variables + static int RGBMax=255; + static int HLSMax=255; + #endregion // Local Variables + + #region Private Enumerations + + + #region Constructor + // Prevent a public constructor from being created + private WidgetPaint() { + } + #endregion // Constructor + + + #endregion // Private Enumerations + + #region Helpers + internal static void Color2HBS(Color color, out int h, out int l, out int s) { + int r; + int g; + int b; + int cMax; + int cMin; + int rDelta; + int gDelta; + int bDelta; + + r=color.R; + g=color.G; + b=color.B; + + cMax = Math.Max(Math.Max(r, g), b); + cMin = Math.Min(Math.Min(r, g), b); + + l = (((cMax+cMin)*HLSMax)+RGBMax)/(2*RGBMax); + + if (cMax==cMin) { // Achromatic + h=0; // h undefined + s=0; + return; + } + + /* saturation */ + if (l<=(HLSMax/2)) { + s=(((cMax-cMin)*HLSMax)+((cMax+cMin)/2))/(cMax+cMin); + } else { + s=(((cMax-cMin)*HLSMax)+((2*RGBMax-cMax-cMin)/2))/(2*RGBMax-cMax-cMin); + } + + /* hue */ + rDelta=(((cMax-r)*(HLSMax/6))+((cMax-cMin)/2))/(cMax-cMin); + gDelta=(((cMax-g)*(HLSMax/6))+((cMax-cMin)/2))/(cMax-cMin); + bDelta=(((cMax-b)*(HLSMax/6))+((cMax-cMin)/2))/(cMax-cMin); + + if (r == cMax) { + h=bDelta - gDelta; + } else if (g == cMax) { + h=(HLSMax/3) + rDelta - bDelta; + } else { /* B == cMax */ + h=((2*HLSMax)/3) + gDelta - rDelta; + } + + if (h<0) { + h+=HLSMax; + } + + if (h>HLSMax) { + h-=HLSMax; + } + } + + private static int HueToRGB(int n1, int n2, int hue) { + if (hue<0) { + hue+=HLSMax; + } + + if (hue>HLSMax) { + hue -= HLSMax; + } + + /* return r,g, or b value from this tridrant */ + if (hue<(HLSMax/6)) { + return(n1+(((n2-n1)*hue+(HLSMax/12))/(HLSMax/6))); + } + + if (hue<(HLSMax/2)) { + return(n2); + } + + if (hue<((HLSMax*2)/3)) { + return(n1+(((n2-n1)*(((HLSMax*2)/3)-hue)+(HLSMax/12))/(HLSMax/6))); + } else { + return(n1); + } + } + + internal static Color HBS2Color(int hue, int lum, int sat) { + int R; + int G; + int B; + int Magic1; + int Magic2; + + if (sat == 0) { /* Achromatic */ + R=G=B=(lum*RGBMax)/HLSMax; + // FIXME : Should throw exception if hue!=0 + } else { + if (lum<=(HLSMax/2)) { + Magic2=(lum*(HLSMax+sat)+(HLSMax/2))/HLSMax; + } else { + Magic2=sat+lum-((sat*lum)+(HLSMax/2))/HLSMax; + } + Magic1=2*lum-Magic2; + + R = Math.Min(255, (HueToRGB(Magic1,Magic2,hue+(HLSMax/3))*RGBMax+(HLSMax/2))/HLSMax); + G = Math.Min(255, (HueToRGB(Magic1,Magic2,hue)*RGBMax+(HLSMax/2))/HLSMax); + B = Math.Min(255, (HueToRGB(Magic1,Magic2,hue-(HLSMax/3))*RGBMax+(HLSMax/2))/HLSMax); + } + return (Color.FromArgb(R, G, B)); + } + #endregion // Helpers + + #region Public Static Properties + public static Color ContrastControlDark { + get { return(SystemColors.ControlDark); } + } + #endregion // Public Static Properties + + #region Public Static Methods + [MonoTODO ("Not implemented, will throw NotImplementedException")] + public static IntPtr CreateHBitmap16Bit (Bitmap bitmap, Color background) + { + throw new NotImplementedException (); + } + + [MonoTODO ("Not implemented, will throw NotImplementedException")] + public static IntPtr CreateHBitmapColorMask (Bitmap bitmap, IntPtr monochromeMask) + { + throw new NotImplementedException (); + } + + [MonoTODO ("Not implemented, will throw NotImplementedException")] + public static IntPtr CreateHBitmapTransparencyMask (Bitmap bitmap) + { + throw new NotImplementedException (); + } + + public static Color Light(Color baseColor) { + return Light(baseColor, 0.5f); + } + + public static Color Light (Color baseColor, float percOfLightLight) + { + if (baseColor.ToArgb () == ThemeEngine.Current.ColorControl.ToArgb ()) { + int r_sub, g_sub, b_sub; + Color color; + + if (percOfLightLight <= 0f) + return ThemeEngine.Current.ColorControlLight; + + if (percOfLightLight == 1.0f) + return ThemeEngine.Current.ColorControlLightLight; + + r_sub = ThemeEngine.Current.ColorControlLightLight.R - ThemeEngine.Current.ColorControlLight.R; + g_sub = ThemeEngine.Current.ColorControlLightLight.G - ThemeEngine.Current.ColorControlLight.G; + b_sub = ThemeEngine.Current.ColorControlLightLight.B - ThemeEngine.Current.ColorControlLight.B; + + color = Color.FromArgb (ThemeEngine.Current.ColorControlLight.A, + (int) (ThemeEngine.Current.ColorControlLight.R + (r_sub * percOfLightLight)), + (int) (ThemeEngine.Current.ColorControlLight.G + (g_sub * percOfLightLight)), + (int) (ThemeEngine.Current.ColorControlLight.B + (b_sub * percOfLightLight))); + return color; + } + + int H, I, S; + + WidgetPaint.Color2HBS (baseColor, out H, out I, out S); + int NewIntensity = Math.Min (255, I + (int) ((255 - I) * 0.5f * percOfLightLight)); + + return WidgetPaint.HBS2Color (H, NewIntensity, S); + } + + public static Color LightLight (Color baseColor) + { + return Light(baseColor, 1.0f); + } + + public static Color Dark (Color baseColor) + { + return Dark(baseColor, 0.5f); + } + + public static Color Dark (Color baseColor, float percOfDarkDark) + { + if (baseColor.ToArgb () == ThemeEngine.Current.ColorControl.ToArgb ()) { + + int r_sub, g_sub, b_sub; + Color color; + + if (percOfDarkDark <= 0f) + return ThemeEngine.Current.ColorControlDark; + + if (percOfDarkDark == 1.0f) + return ThemeEngine.Current.ColorControlDarkDark; + + r_sub = ThemeEngine.Current.ColorControlDarkDark.R - ThemeEngine.Current.ColorControlDark.R; + g_sub = ThemeEngine.Current.ColorControlDarkDark.G - ThemeEngine.Current.ColorControlDark.G; + b_sub = ThemeEngine.Current.ColorControlDarkDark.B - ThemeEngine.Current.ColorControlDark.B; + + color = Color.FromArgb (ThemeEngine.Current.ColorControlDark.A, + (int) (ThemeEngine.Current.ColorControlDark.R + (r_sub * percOfDarkDark)), + (int) (ThemeEngine.Current.ColorControlDark.G + (g_sub * percOfDarkDark)), + (int) (ThemeEngine.Current.ColorControlDark.B + (b_sub * percOfDarkDark))); + return color; + } + + int H, I, S; + + WidgetPaint.Color2HBS(baseColor, out H, out I, out S); + int PreIntensity = Math.Max (0, I - (int) (I * 0.333f)); + int NewIntensity = Math.Max (0, PreIntensity - (int) (PreIntensity * percOfDarkDark)); + return WidgetPaint.HBS2Color(H, NewIntensity, S); + } + + public static Color DarkDark (Color baseColor) + { + return Dark(baseColor, 1.0f); + } + + public static void DrawBorder (Graphics graphics, Rectangle bounds, Color color, ButtonBorderStyle style) + { + int line_width_top_left = 1; + int line_width_bottom_right = 1; + + if (style == ButtonBorderStyle.Inset) + line_width_top_left = 2; + if (style == ButtonBorderStyle.Outset) { + line_width_bottom_right = 2; + line_width_top_left = 2; + } + + DrawBorder(graphics, bounds, color, line_width_top_left, style, color, line_width_top_left, style, color, line_width_bottom_right, style, color, line_width_bottom_right, style); + } + + internal static void DrawBorder (Graphics graphics, RectangleF bounds, Color color, ButtonBorderStyle style) + { + int line_width_top_left = 1; + int line_width_bottom_right = 1; + + if (style == ButtonBorderStyle.Inset) + line_width_top_left = 2; + if (style == ButtonBorderStyle.Outset) { + line_width_bottom_right = 2; + line_width_top_left = 2; + } + + ThemeEngine.Current.CPDrawBorder (graphics, bounds, color, line_width_top_left, style, color, line_width_top_left, style, color, line_width_bottom_right, style, color, line_width_bottom_right, style); + } + + public static void DrawBorder( Graphics graphics, Rectangle bounds, Color leftColor, int leftWidth, + ButtonBorderStyle leftStyle, Color topColor, int topWidth, ButtonBorderStyle topStyle, + Color rightColor, int rightWidth, ButtonBorderStyle rightStyle, Color bottomColor, int bottomWidth, + ButtonBorderStyle bottomStyle) { + + ThemeEngine.Current.CPDrawBorder (graphics, bounds, leftColor, leftWidth, + leftStyle, topColor, topWidth, topStyle, rightColor, rightWidth, rightStyle, + bottomColor, bottomWidth, bottomStyle); + } + + + public static void DrawBorder3D(Graphics graphics, Rectangle rectangle) { + DrawBorder3D(graphics, rectangle, Border3DStyle.Etched, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom); + } + + public static void DrawBorder3D(Graphics graphics, Rectangle rectangle, Border3DStyle style) { + DrawBorder3D(graphics, rectangle, style, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom); + } + + public static void DrawBorder3D(Graphics graphics, int x, int y, int width, int height) { + DrawBorder3D(graphics, new Rectangle(x, y, width, height), Border3DStyle.Etched, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom); + } + + public static void DrawBorder3D(Graphics graphics, int x, int y, int width, int height, Border3DStyle style) { + DrawBorder3D(graphics, new Rectangle(x, y, width, height), style, Border3DSide.Left | Border3DSide.Right | Border3DSide.Top | Border3DSide.Bottom); + } + + public static void DrawBorder3D( Graphics graphics, int x, int y, int width, int height, Border3DStyle style,Border3DSide sides) { + DrawBorder3D( graphics, new Rectangle(x, y, width, height), style, sides); + } + + public static void DrawBorder3D( Graphics graphics, Rectangle rectangle, Border3DStyle style, Border3DSide sides) { + + ThemeEngine.Current.CPDrawBorder3D (graphics, rectangle, style, sides); + } + + public static void DrawButton( Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawButton(graphics, new Rectangle(x, y, width, height), state); + } + + public static void DrawButton( Graphics graphics, Rectangle rectangle, ButtonState state) { + + ThemeEngine.Current.CPDrawButton (graphics, rectangle, state); + } + + + public static void DrawCaptionButton(Graphics graphics, int x, int y, int width, int height, CaptionButton button, ButtonState state) { + DrawCaptionButton(graphics, new Rectangle(x, y, width, height), button, state); + } + + public static void DrawCaptionButton(Graphics graphics, Rectangle rectangle, CaptionButton button, ButtonState state) { + + ThemeEngine.Current.CPDrawCaptionButton (graphics, rectangle, button, state); + } + + public static void DrawCheckBox(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawCheckBox(graphics, new Rectangle(x, y, width, height), state); + } + + public static void DrawCheckBox(Graphics graphics, Rectangle rectangle, ButtonState state) { + + ThemeEngine.Current.CPDrawCheckBox (graphics, rectangle, state); + } + + public static void DrawComboButton(Graphics graphics, Rectangle rectangle, ButtonState state) { + + ThemeEngine.Current.CPDrawComboButton (graphics, rectangle, state); + } + + public static void DrawComboButton(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawComboButton(graphics, new Rectangle(x, y, width, height), state); + } + + public static void DrawContainerGrabHandle(Graphics graphics, Rectangle bounds) { + + ThemeEngine.Current.CPDrawContainerGrabHandle (graphics, bounds); + } + + public static void DrawFocusRectangle( Graphics graphics, Rectangle rectangle) { + DrawFocusRectangle(graphics, rectangle, SystemColors.Control, SystemColors.ControlText); + } + + public static void DrawFocusRectangle( Graphics graphics, Rectangle rectangle, Color foreColor, Color backColor) { + + ThemeEngine.Current.CPDrawFocusRectangle (graphics, rectangle, foreColor, backColor); + } + + public static void DrawGrabHandle(Graphics graphics, Rectangle rectangle, bool primary, bool enabled) { + + ThemeEngine.Current.CPDrawGrabHandle (graphics, rectangle, primary, enabled); + } + + public static void DrawGrid(Graphics graphics, Rectangle area, Size pixelsBetweenDots, Color backColor) { + + ThemeEngine.Current.CPDrawGrid (graphics, area, pixelsBetweenDots, backColor); + } + + public static void DrawImageDisabled(Graphics graphics, Image image, int x, int y, Color background) { + + ThemeEngine.Current.CPDrawImageDisabled (graphics, image, x, y, background); + } + + public static void DrawLockedFrame(Graphics graphics, Rectangle rectangle, bool primary) { + + ThemeEngine.Current.CPDrawLockedFrame (graphics, rectangle, primary); + } + + public static void DrawMenuGlyph(Graphics graphics, Rectangle rectangle, MenuGlyph glyph) { + + ThemeEngine.Current.CPDrawMenuGlyph (graphics, rectangle, glyph, ThemeEngine.Current.ColorMenuText, Color.Empty); + } + + public static void DrawMenuGlyph (Graphics graphics, Rectangle rectangle, MenuGlyph glyph, Color foreColor, Color backColor) + { + ThemeEngine.Current.CPDrawMenuGlyph (graphics, rectangle, glyph, foreColor, backColor); + } + + public static void DrawMenuGlyph(Graphics graphics, int x, int y, int width, int height, MenuGlyph glyph) { + DrawMenuGlyph(graphics, new Rectangle(x, y, width, height), glyph); + } + + public static void DrawMenuGlyph (Graphics graphics, int x, int y, int width, int height, MenuGlyph glyph, Color foreColor, Color backColor) + { + DrawMenuGlyph (graphics, new Rectangle (x, y, width, height), glyph, foreColor, backColor); + } + + public static void DrawMixedCheckBox(Graphics graphics, Rectangle rectangle, ButtonState state) { + ThemeEngine.Current.CPDrawMixedCheckBox (graphics, rectangle, state); + } + + public static void DrawMixedCheckBox(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawMixedCheckBox(graphics, new Rectangle(x, y, width, height), state); + } + + + public static void DrawRadioButton(Graphics graphics, int x, int y, int width, int height, ButtonState state) { + DrawRadioButton(graphics, new Rectangle(x, y, width, height), state); + } + + public static void DrawRadioButton(Graphics graphics, Rectangle rectangle, ButtonState state) { + + ThemeEngine.Current.CPDrawRadioButton (graphics, rectangle, state); + } + + public static void DrawReversibleFrame(Rectangle rectangle, Color backColor, FrameStyle style) { + XplatUI.DrawReversibleFrame (rectangle, backColor, style); + } + + public static void DrawReversibleLine(Point start, Point end, Color backColor) { + XplatUI.DrawReversibleLine (start, end, backColor); + } + + public static void FillReversibleRectangle(Rectangle rectangle, Color backColor) { + XplatUI.FillReversibleRectangle (rectangle, backColor); + } + + public static void DrawScrollButton (Graphics graphics, int x, int y, int width, int height, ScrollButton button, ButtonState state) { + ThemeEngine.Current.CPDrawScrollButton (graphics, new Rectangle(x, y, width, height), button, state); + } + + public static void DrawScrollButton (Graphics graphics, Rectangle rectangle, ScrollButton button, ButtonState state) { + ThemeEngine.Current.CPDrawScrollButton (graphics, rectangle, button, state); + } + + [MonoTODO ("Stub, does nothing")] + private static bool DSFNotImpl = false; + public static void DrawSelectionFrame(Graphics graphics, bool active, Rectangle outsideRect, Rectangle insideRect, Color backColor) { + if (!DSFNotImpl) { + DSFNotImpl = true; + Console.WriteLine("NOT IMPLEMENTED: DrawSelectionFrame(Graphics graphics, bool active, Rectangle outsideRect, Rectangle insideRect, Color backColor)"); + } + //throw new NotImplementedException(); + } + + public static void DrawSizeGrip (Graphics graphics, Color backColor, Rectangle bounds) + { + ThemeEngine.Current.CPDrawSizeGrip (graphics, backColor, bounds); + } + + public static void DrawSizeGrip(Graphics graphics, Color backColor, int x, int y, int width, int height) { + DrawSizeGrip(graphics, backColor, new Rectangle(x, y, width, height)); + } + + public static void DrawStringDisabled(Graphics graphics, string s, Font font, Color color, RectangleF layoutRectangle, StringFormat format) { + + ThemeEngine.Current.CPDrawStringDisabled (graphics, s, font, color, layoutRectangle, format); + } + + public static void DrawStringDisabled (IDeviceContext dc, string s, Font font, Color color, Rectangle layoutRectangle, TextFormatFlags format) + { + ThemeEngine.Current.CPDrawStringDisabled (dc, s, font, color, layoutRectangle, format); + } + + public static void DrawVisualStyleBorder (Graphics graphics, Rectangle bounds) + { + ThemeEngine.Current.CPDrawVisualStyleBorder (graphics, bounds); + } + #endregion // Public Static Methods + } +} diff --git a/source/ShiftUI/Internal/CurrencyManager.cs b/source/ShiftUI/Internal/CurrencyManager.cs new file mode 100644 index 0000000..9f68fd4 --- /dev/null +++ b/source/ShiftUI/Internal/CurrencyManager.cs @@ -0,0 +1,453 @@ +// 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) 2005 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) +// Ivan N. Zlatev ([email protected]) +// + +using System; +using System.Data; +using System.Reflection; +using System.Collections; +using System.ComponentModel; + +namespace ShiftUI { + public class CurrencyManager : BindingManagerBase { + + protected int listposition; + protected Type finalType; + + private IList list; + private bool binding_suspended; + + private object data_source; + + bool editing; + + internal CurrencyManager () + { + } + + internal CurrencyManager (object data_source) + { + SetDataSource (data_source); + } + + public IList List { + get { return list; } + } + + public override object Current { + get { + if (listposition == -1 || listposition >= list.Count) { + // Console.WriteLine ("throwing exception from here"); + // Console.WriteLine (Environment.StackTrace); + throw new IndexOutOfRangeException ("list position"); + } + return list [listposition]; + } + } + + public override int Count { + get { return list.Count; } + } + + public override int Position { + get { + return listposition; + } + set { + if (value < 0) + value = 0; + if (value >= list.Count) + value = list.Count - 1; + if (listposition == value) + return; + + if (listposition != -1) + EndCurrentEdit (); + + listposition = value; + OnCurrentChanged (EventArgs.Empty); + OnPositionChanged (EventArgs.Empty); + } + } + + internal void SetDataSource (object data_source) + { + if (this.data_source is IBindingList) + ((IBindingList)this.data_source).ListChanged -= new ListChangedEventHandler (ListChangedHandler); + + if (data_source is IListSource) + data_source = ((IListSource)data_source).GetList(); + + this.data_source = data_source; + if (data_source != null) + this.finalType = data_source.GetType(); + + listposition = -1; + if (this.data_source is IBindingList) + ((IBindingList)this.data_source).ListChanged += new ListChangedEventHandler (ListChangedHandler); + + list = (IList)data_source; + + // XXX this is wrong. MS invokes OnItemChanged directly, which seems to call PushData. + ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1)); + } + + public override PropertyDescriptorCollection GetItemProperties () + { + return ListBindingHelper.GetListItemProperties (list); + } + + public override void RemoveAt (int index) + { + list.RemoveAt (index); + } + + public override void SuspendBinding () + { + binding_suspended = true; + } + + public override void ResumeBinding () + { + binding_suspended = false; + } + + internal override bool IsSuspended { + get { + // Always return true if we don't have items + if (Count == 0) + return true; + + return binding_suspended; + } + } + + internal bool AllowNew { + get { + if (list is IBindingList) + return ((IBindingList)list).AllowNew; + + if (list.IsReadOnly) + return false; + + return false; + } + } + + internal bool AllowRemove { + get { + if (list.IsReadOnly) + return false; + + if (list is IBindingList) + return ((IBindingList)list).AllowRemove; + + return false; + } + } + + internal bool AllowEdit { + get { + if (list is IBindingList) + return ((IBindingList)list).AllowEdit; + + return false; + } + } + + public override void AddNew () + { + IBindingList ibl = list as IBindingList; + + if (ibl == null) + throw new NotSupportedException (); + + ibl.AddNew (); + + bool validate = (Position != (list.Count - 1)); + ChangeRecordState (list.Count - 1, validate, validate, true, true); + } + + + void BeginEdit () + { + IEditableObject editable = Current as IEditableObject; + + if (editable != null) { + try { + editable.BeginEdit (); + editing = true; + } + catch { + /* swallow exceptions in IEditableObject.BeginEdit () */ + } + } + } + + public override void CancelCurrentEdit () + { + if (listposition == -1) + return; + + IEditableObject editable = Current as IEditableObject; + + if (editable != null) { + editing = false; + editable.CancelEdit (); + OnItemChanged (new ItemChangedEventArgs (Position)); + } + if (list is ICancelAddNew) + ((ICancelAddNew)list).CancelNew (listposition); + } + + public override void EndCurrentEdit () + { + if (listposition == -1) + return; + + IEditableObject editable = Current as IEditableObject; + + if (editable != null) { + editing = false; + editable.EndEdit (); + } + + if (list is ICancelAddNew) + ((ICancelAddNew)list).EndNew (listposition); + } + + public void Refresh () + { + ListChangedHandler (null, new ListChangedEventArgs (ListChangedType.Reset, -1)); + } + + protected void CheckEmpty () + { + if (list == null || list.Count < 1) + throw new Exception ("List is empty."); + + } + + protected internal override void OnCurrentChanged (EventArgs e) + { + if (onCurrentChangedHandler != null) { + onCurrentChangedHandler (this, e); + } + + // don't call OnCurrentItemChanged here, as it can be overridden + if (onCurrentItemChangedHandler != null) { + onCurrentItemChangedHandler (this, e); + } + + } + + protected override void OnCurrentItemChanged (EventArgs e) + { + if (onCurrentItemChangedHandler != null) { + onCurrentItemChangedHandler (this, e); + } + } + + protected virtual void OnItemChanged (ItemChangedEventArgs e) + { + if (ItemChanged != null) + ItemChanged (this, e); + + transfering_data = true; + PushData (); + transfering_data = false; + } + + void OnListChanged (ListChangedEventArgs args) + { + if (ListChanged != null) + ListChanged (this, args); + } + + protected virtual void OnPositionChanged (EventArgs e) + { + if (onPositionChangedHandler != null) + onPositionChangedHandler (this, e); + } + + protected internal override string GetListName (ArrayList listAccessors) + { + if (list is ITypedList) { + PropertyDescriptor [] pds = null; + if (listAccessors != null) { + pds = new PropertyDescriptor [listAccessors.Count]; + listAccessors.CopyTo (pds, 0); + } + return ((ITypedList) list).GetListName (pds); + } + else if (finalType != null) { + return finalType.Name; + } + return String.Empty; + } + + protected override void UpdateIsBinding () + { + UpdateItem (); + + foreach (Binding binding in Bindings) + binding.UpdateIsBinding (); + + ChangeRecordState (listposition, false, false, true, false); + + OnItemChanged (new ItemChangedEventArgs (-1)); + } + + private void ChangeRecordState (int newPosition, + bool validating, + bool endCurrentEdit, + bool firePositionChanged, + bool pullData) + { + if (endCurrentEdit) + EndCurrentEdit (); + + int old_index = listposition; + + listposition = newPosition; + + if (listposition >= list.Count) + listposition = list.Count - 1; + + if (old_index != -1 && listposition != -1) + OnCurrentChanged (EventArgs.Empty); + + if (firePositionChanged) + OnPositionChanged (EventArgs.Empty); + } + + private void UpdateItem () + { + // Probably should be validating or something here + if (!transfering_data && listposition == -1 && list.Count > 0) { + listposition = 0; + BeginEdit (); + } + } + + internal object this [int index] { + get { return list [index]; } + } + + private PropertyDescriptorCollection GetBrowsableProperties (Type t) + { + Attribute [] att = new System.Attribute [1]; + att [0] = new BrowsableAttribute (true); + return TypeDescriptor.GetProperties (t, att); + } + + protected void OnMetaDataChanged (EventArgs e) + { + if (MetaDataChanged != null) + MetaDataChanged (this, e); + } + + private void ListChangedHandler (object sender, ListChangedEventArgs e) + { + switch (e.ListChangedType) { + case ListChangedType.PropertyDescriptorAdded: + case ListChangedType.PropertyDescriptorDeleted: + case ListChangedType.PropertyDescriptorChanged: + OnMetaDataChanged (EventArgs.Empty); + OnListChanged (e); + break; + case ListChangedType.ItemDeleted: + if (list.Count == 0) { + /* the last row was deleted */ + listposition = -1; + UpdateIsBinding (); + + OnPositionChanged (EventArgs.Empty); + OnCurrentChanged (EventArgs.Empty); + } + else if (e.NewIndex <= listposition) { + /* the deleted row was either the current one, or one earlier in the list. + Update the index and emit PositionChanged, CurrentChanged, and ItemChanged. */ + // FIXME: this looks wrong, shouldn't it be (listposition - 1) instead of e.NewIndex ? + ChangeRecordState (e.NewIndex, + false, false, e.NewIndex != listposition, false); + } + else { + /* the deleted row was after the current one, so we don't + need to update bound Widgets for Position/Current changed */ + } + + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + break; + case ListChangedType.ItemAdded: + if (list.Count == 1) { + /* it's the first one we've added */ + ChangeRecordState (e.NewIndex, + false, false, true, false); + + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + } + else { + if (e.NewIndex <= listposition) { + ChangeRecordState (listposition + 1, + false, false, false, false); + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + OnPositionChanged (EventArgs.Empty); + } + else { + OnItemChanged (new ItemChangedEventArgs (-1)); + OnListChanged (e); + } + } + + break; + case ListChangedType.ItemChanged: + if (editing) { + if (e.NewIndex == listposition) + OnCurrentItemChanged (EventArgs.Empty); + OnItemChanged (new ItemChangedEventArgs (e.NewIndex)); + } + OnListChanged (e); + break; + case ListChangedType.Reset: + PushData(); + UpdateIsBinding(); + OnListChanged (e); + break; + default: + OnListChanged (e); + break; + } + } + + public event ListChangedEventHandler ListChanged; + public event ItemChangedEventHandler ItemChanged; + public event EventHandler MetaDataChanged; + } +} + diff --git a/source/ShiftUI/Internal/Cursor.cs b/source/ShiftUI/Internal/Cursor.cs new file mode 100644 index 0000000..1b3e0f6 --- /dev/null +++ b/source/ShiftUI/Internal/Cursor.cs @@ -0,0 +1,245 @@ +// 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) 2008 Novell, Inc. +// +// Authors: +// Geoff Norton ([email protected]) +// +// + + +using System; +using System.Drawing; +using ShiftUI; +using System.Runtime.InteropServices; + +namespace ShiftUI.CarbonInternal { + internal class Cursor { + internal static CarbonCursor defcur = new CarbonCursor (StdCursor.Default); + + internal static Bitmap DefineStdCursorBitmap (StdCursor id) { + // FIXME + return new Bitmap (16, 16); + } + internal static IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) { + CarbonCursor cc = new CarbonCursor (bitmap, mask, cursor_pixel, mask_pixel, xHotSpot, yHotSpot); + + return (IntPtr) GCHandle.Alloc (cc); + } + internal static IntPtr DefineStdCursor (StdCursor id) { + CarbonCursor cc = new CarbonCursor (id); + + return (IntPtr) GCHandle.Alloc (cc); + } + internal static void SetCursor (IntPtr cursor) { + if (cursor == IntPtr.Zero) { + defcur.SetCursor (); + return; + } + + CarbonCursor cc = (CarbonCursor) ((GCHandle) cursor).Target; + + cc.SetCursor (); + } + } + + internal struct CarbonCursor { + private Bitmap bmp; + private Bitmap mask; + private Color cursor_color; + private Color mask_color; + private int hot_x; + private int hot_y; + private StdCursor id; + private bool standard; + + public CarbonCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) { + this.id = StdCursor.Default; + this.bmp = bitmap; + this.mask = mask; + this.cursor_color = cursor_pixel; + this.mask_color = mask_pixel; + this.hot_x = xHotSpot; + this.hot_y = yHotSpot; + standard = true; + } + + public CarbonCursor (StdCursor id) { + this.id = id; + this.bmp = null; + this.mask = null; + this.cursor_color = Color.Black; + this.mask_color = Color.Black; + this.hot_x = 0; + this.hot_y = 0; + standard = true; + } + + public StdCursor StdCursor { + get { + return id; + } + } + + public Bitmap Bitmap { + get { + return bmp; + } + } + + public Bitmap Mask { + get { + return mask; + } + } + + public Color CursorColor { + get { + return cursor_color; + } + } + + public Color MaskColor { + get { + return mask_color; + } + } + + public int HotSpotX { + get { + return hot_x; + } + } + + public int HotSpotY { + get { + return hot_y; + } + } + + public void SetCursor () { + if (standard) + SetStandardCursor (); + else + SetCustomCursor (); + } + + public void SetCustomCursor () { + throw new NotImplementedException ("We dont support custom cursors yet"); + } + + public void SetStandardCursor () { + switch (id) { + case StdCursor.AppStarting: + SetThemeCursor (ThemeCursor.kThemeSpinningCursor); + break; + case StdCursor.Arrow: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.Cross: + SetThemeCursor (ThemeCursor.kThemeCrossCursor); + break; + case StdCursor.Default: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.Hand: + SetThemeCursor (ThemeCursor.kThemeOpenHandCursor); + break; + case StdCursor.Help: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.HSplit: + SetThemeCursor (ThemeCursor.kThemeResizeLeftRightCursor); + break; + case StdCursor.IBeam: + SetThemeCursor (ThemeCursor.kThemeIBeamCursor); + break; + case StdCursor.No: + SetThemeCursor (ThemeCursor.kThemeNotAllowedCursor); + break; + case StdCursor.NoMove2D: + SetThemeCursor (ThemeCursor.kThemeNotAllowedCursor); + break; + case StdCursor.NoMoveHoriz: + SetThemeCursor (ThemeCursor.kThemeNotAllowedCursor); + break; + case StdCursor.NoMoveVert: + SetThemeCursor (ThemeCursor.kThemeNotAllowedCursor); + break; + case StdCursor.PanEast: + SetThemeCursor (ThemeCursor.kThemeResizeRightCursor); + break; + case StdCursor.PanNE: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.PanNorth: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.PanNW: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.PanSE: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.PanSouth: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.PanSW: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.PanWest: + SetThemeCursor (ThemeCursor.kThemeResizeLeftCursor); + break; + case StdCursor.SizeAll: + SetThemeCursor (ThemeCursor.kThemeResizeLeftRightCursor); + break; + case StdCursor.SizeNESW: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.SizeNS: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.SizeNWSE: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.SizeWE: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.UpArrow: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.VSplit: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + case StdCursor.WaitCursor: + SetThemeCursor (ThemeCursor.kThemeSpinningCursor); + break; + default: + SetThemeCursor (ThemeCursor.kThemeArrowCursor); + break; + } + return; + } + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int SetThemeCursor (ThemeCursor cursor); + + } +} diff --git a/source/ShiftUI/Internal/CursorConverter.cs b/source/ShiftUI/Internal/CursorConverter.cs new file mode 100644 index 0000000..d84b3d2 --- /dev/null +++ b/source/ShiftUI/Internal/CursorConverter.cs @@ -0,0 +1,126 @@ +// 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) 2005 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + + +using System; +using System.IO; +using System.Drawing; +using System.Drawing.Imaging; +using System.Collections; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; + +namespace ShiftUI +{ + public class CursorConverter : TypeConverter + { + public CursorConverter () + { + } + + public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (byte [])) + return true; + return base.CanConvertFrom (context, sourceType); + } + + public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof (byte []) || destinationType == typeof (InstanceDescriptor)) + return true; + return base.CanConvertTo (context, destinationType); + } + + public override object ConvertFrom (ITypeDescriptorContext context, + CultureInfo culture, object value) + { + byte [] val = value as byte []; + if (val == null) + return base.ConvertFrom (context, culture, value); + + using (MemoryStream s = new MemoryStream (val)) { + return new Cursor (s); + } + } + + public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, + object value, Type destinationType) + { + if (destinationType == null) + throw new ArgumentNullException ("destinationType"); + + if (value == null && destinationType == typeof (string)) + return "(none)"; + + if ( !(value is Cursor)) + throw new ArgumentException("object must be of class Cursor", "value"); + + if (destinationType == typeof (byte [])) { + Cursor c; + SerializationInfo si; + + if (value == null) { + return new byte [0]; + } + + c = (Cursor)value; + + si = new SerializationInfo(typeof(Cursor), new FormatterConverter()); + ((ISerializable)c).GetObjectData(si, new StreamingContext(StreamingContextStates.Remoting)); + + return (byte[])si.GetValue("CursorData", typeof(byte[])); + } else if (destinationType == typeof (InstanceDescriptor)) { + PropertyInfo[] properties = typeof (Cursors).GetProperties (); + foreach (PropertyInfo propInfo in properties) { + if (propInfo.GetValue (null, null) == value) { + return new InstanceDescriptor (propInfo, null); + } + } + } + return base.ConvertTo (context, culture, value, destinationType); + } + + public override StandardValuesCollection GetStandardValues (ITypeDescriptorContext context) + { + PropertyInfo[] props = typeof (Cursors).GetProperties(); + + ArrayList vals = new ArrayList (); + + for (int i = 0; i < props.Length; i++) { + vals.Add (props [i].GetValue (null, null)); + } + return new StandardValuesCollection (vals); + } + + public override bool GetStandardValuesSupported (ITypeDescriptorContext context) + { + return true; + } + } +} diff --git a/source/ShiftUI/Internal/Cursors.cs b/source/ShiftUI/Internal/Cursors.cs new file mode 100644 index 0000000..2d43345 --- /dev/null +++ b/source/ShiftUI/Internal/Cursors.cs @@ -0,0 +1,365 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok [email protected] +// +// + +// COMPLETE + +using ShiftUI; + +namespace ShiftUI { + public sealed class Cursors { + #region Local Variables + internal static Cursor app_starting; + internal static Cursor arrow; + internal static Cursor cross; + internal static Cursor def; + internal static Cursor hand; + internal static Cursor help; + internal static Cursor hsplit; + internal static Cursor ibeam; + internal static Cursor no; + internal static Cursor no_move_2d; + internal static Cursor no_move_horiz; + internal static Cursor no_move_vert; + internal static Cursor pan_east; + internal static Cursor pan_ne; + internal static Cursor pan_north; + internal static Cursor pan_nw; + internal static Cursor pan_se; + internal static Cursor pan_south; + internal static Cursor pan_sw; + internal static Cursor pan_west; + internal static Cursor size_all; + internal static Cursor size_nesw; + internal static Cursor size_ns; + internal static Cursor size_nwse; + internal static Cursor size_we; + internal static Cursor up_arrow; + internal static Cursor vsplit; + internal static Cursor wait_cursor; + #endregion // Local Variables + + #region Constructors + private Cursors() { + } + #endregion // Constructors + + #region Public Static Properties + public static Cursor AppStarting { + get { + if (app_starting == null) { + app_starting = new Cursor(StdCursor.AppStarting); + app_starting.name = "AppStarting"; + } + return app_starting; + } + } + + public static Cursor Arrow { + get { + if (arrow == null) { + arrow = new Cursor(StdCursor.Arrow); + arrow.name = "Arrow"; + } + return arrow; + } + } + + public static Cursor Cross { + get { + if (cross == null) { + cross = new Cursor(StdCursor.Cross); + cross.name = "Cross"; + } + return cross; + } + } + + public static Cursor Default { + get { + if (def == null) { + def = new Cursor(StdCursor.Default); + def.name = "Default"; + } + return def; + } + } + + public static Cursor Hand { + get { + if (hand == null) { + hand = new Cursor(StdCursor.Hand); + hand.name = "Hand"; + } + return hand; + } + } + + public static Cursor Help { + get { + if (help == null) { + help = new Cursor(StdCursor.Help); + help.name = "Help"; + } + return help; + } + } + + public static Cursor HSplit { + get { + if (hsplit == null) { + hsplit = new Cursor(global::ShiftUI.Properties.Resources.SplitterWE); + hsplit.name = "HSplit"; + } + return hsplit; + } + } + + public static Cursor IBeam { + get { + if (ibeam == null) { + ibeam = new Cursor(StdCursor.IBeam); + ibeam.name = "IBeam"; + } + return ibeam; + } + } + + public static Cursor No { + get { + if (no == null) { + no = new Cursor(StdCursor.No); + no.name = "No"; + } + return no; + } + } + + public static Cursor NoMove2D { + get { + if (no_move_2d == null) { + no_move_2d = new Cursor(StdCursor.NoMove2D); + no_move_2d.name = "NoMove2D"; + } + return no_move_2d; + } + } + + public static Cursor NoMoveHoriz { + get { + if (no_move_horiz == null) { + no_move_horiz = new Cursor(StdCursor.NoMoveHoriz); + no_move_horiz.name = "NoMoveHoriz"; + } + return no_move_horiz; + } + } + + public static Cursor NoMoveVert { + get { + if (no_move_vert == null) { + no_move_vert = new Cursor(StdCursor.NoMoveVert); + no_move_vert.name = "NoMoveVert"; + } + return no_move_vert; + } + } + + public static Cursor PanEast { + get { + if (pan_east == null) { + pan_east = new Cursor(StdCursor.PanEast); + pan_east.name = "PanEast"; + } + return pan_east; + } + } + + + + + public static Cursor PanNE { + get { + if (pan_ne == null) { + pan_ne = new Cursor(StdCursor.PanNE); + pan_ne.name = "PanNE"; + } + return pan_ne; + } + } + + + public static Cursor PanNorth { + get { + if (pan_north == null) { + pan_north = new Cursor(StdCursor.PanNorth); + pan_north.name = "PanNorth"; + } + return pan_north; + } + } + + public static Cursor PanNW { + get { + if (pan_nw == null) { + pan_nw = new Cursor(StdCursor.PanNW); + pan_nw.name = "PanNW"; + } + return pan_nw; + } + } + + public static Cursor PanSE { + get { + if (pan_se == null) { + pan_se = new Cursor(StdCursor.PanSE); + pan_se.name = "PanSE"; + } + return pan_se; + } + } + + public static Cursor PanSouth { + get { + if (pan_south == null) { + pan_south = new Cursor(StdCursor.PanSouth); + pan_south.name = "PanSouth"; + } + return pan_south; + } + } + + public static Cursor PanSW { + get { + if (pan_sw == null) { + pan_sw = new Cursor(StdCursor.PanSW); + pan_sw.name = "PanSW"; + } + return pan_sw; + } + } + + public static Cursor PanWest { + get { + if (pan_west == null) { + pan_west = new Cursor(StdCursor.PanWest); + pan_west.name = "PanWest"; + } + return pan_west; + } + } + + public static Cursor SizeAll { + get { + if (size_all == null) { + size_all = new Cursor(StdCursor.SizeAll); + size_all.name = "SizeAll"; + } + return size_all; + } + } + + public static Cursor SizeNESW { + get { + if (size_nesw == null) { + if (XplatUI.RunningOnUnix) { + size_nesw = new Cursor(Properties.Resources.NESW); + size_nesw.name = "SizeNESW"; + } else { + size_nesw = new Cursor(StdCursor.SizeNESW); + size_nesw.name = "SizeNESW"; + } + } + return size_nesw; + } + } + + public static Cursor SizeNS { + get { + if (size_ns == null) { + size_ns = new Cursor(StdCursor.SizeNS); + size_ns.name = "SizeNS"; + } + return size_ns; + } + } + + public static Cursor SizeNWSE { + get { + if (size_nwse == null) { + if (XplatUI.RunningOnUnix) { + size_nwse = new Cursor(Properties.Resources.NWSE); + size_nwse.name = "SizeNWSE"; + } else { + size_nwse = new Cursor(StdCursor.SizeNWSE); + size_nwse.name = "SizeNWSE"; + } + } + return size_nwse; + } + } + + public static Cursor SizeWE { + get { + if (size_we == null) { + size_we = new Cursor(StdCursor.SizeWE); + size_we.name = "SizeWE"; + } + return size_we; + } + } + + public static Cursor UpArrow { + get { + if (up_arrow == null) { + up_arrow = new Cursor(StdCursor.UpArrow); + up_arrow.name = "UpArrow"; + } + return up_arrow; + } + } + + public static Cursor VSplit { + get { + if (vsplit == null) { + vsplit = new Cursor(Properties.Resources.SplitterWE); + vsplit.name = "VSplit"; + } + return vsplit; + } + } + + public static Cursor WaitCursor { + get { + if (wait_cursor == null) { + wait_cursor = new Cursor(StdCursor.WaitCursor); + wait_cursor.name = "WaitCursor"; + } + return wait_cursor; + } + } + #endregion // Public Static Properties + } +} diff --git a/source/ShiftUI/Internal/DataObject.cs b/source/ShiftUI/Internal/DataObject.cs new file mode 100644 index 0000000..cfe74d5 --- /dev/null +++ b/source/ShiftUI/Internal/DataObject.cs @@ -0,0 +1,485 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System; +using System.IO; +using System.Drawing; +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace ShiftUI { + [ClassInterface(ClassInterfaceType.None)] + public class DataObject : IDataObject, System.Runtime.InteropServices.ComTypes.IDataObject + { + #region DataObject.Entry Class + private class Entry { + #region Local Variables + private string type; + private object data; + private bool autoconvert; + internal Entry next; + #endregion // Local Variables + + #region Constructors + internal Entry(string type, object data, bool autoconvert) { + this.type = type; + this.data = data; + this.autoconvert = autoconvert; + } + #endregion // Constructors + + #region Properties + public object Data { + get { + return data; + } + + set { + data = value; + } + } + public bool AutoConvert { + get { + return autoconvert; + } + set { + autoconvert = value; + } + } + #endregion // Properties + + #region Methods + public static int Count(Entry entries) { + int result; + + result = 0; + + while (entries != null) { + result++; + entries = entries.next; + } + + return result; + } + + public static Entry Find (Entry entries, string type) { + return Find (entries, type, false); + } + + public static Entry Find(Entry entries, string type, bool only_convertible) { + while (entries != null) { + bool available = true; + if (only_convertible && !entries.autoconvert) + available = false; + if (available && String.Compare (entries.type, type, true) == 0) { + return entries; + } + entries = entries.next; + } + + return null; + } + + public static Entry FindConvertible(Entry entries, string type) { + Entry e; + + e = Find(entries, type); + if (e != null) { + return e; + } + + // map to *any* other text format if needed + if (type == DataFormats.StringFormat || type == DataFormats.Text || type == DataFormats.UnicodeText) { + e = entries; + while (e != null) { + if (e.type == DataFormats.StringFormat || e.type == DataFormats.Text || e.type == DataFormats.UnicodeText) + return e; + + e = e.next; + } + } + + return null; + } + + public static string[] Entries(Entry entries, bool convertible) { + Entry e; + ArrayList list; + string[] result; + + // Initially store into something that we can grow easily + list = new ArrayList(Entry.Count(entries)); + e = entries; + + if (convertible) { + // Add the convertibles + Entry text_entry = Entry.Find (entries, DataFormats.Text); + Entry utext_entry = Entry.Find (entries, DataFormats.UnicodeText); + Entry string_entry = Entry.Find (entries, DataFormats.StringFormat); + bool text_convertible = text_entry != null && text_entry.AutoConvert; + bool utext_convertible = utext_entry != null && utext_entry.AutoConvert; + bool string_convertible = string_entry != null && string_entry.AutoConvert; + + if (text_convertible || utext_convertible || string_convertible) { + list.Add (DataFormats.StringFormat); + list.Add (DataFormats.UnicodeText); + list.Add (DataFormats.Text); + } + } + + while (e != null) { + if (!list.Contains (e.type)) + list.Add (e.type); + e = e.next; + } + + // Copy the results into a string array + result = new string[list.Count]; + for (int i = 0; i < list.Count; i++) { + result[i] = (string)list[i]; + } + + return result; + } + #endregion // Methods + } + #endregion // DataObject.Entry class + + #region Local Variables + private Entry entries; + #endregion // Local Variables + + #region Public Constructors + public DataObject() { + entries = null; + } + + public DataObject(object data) { + SetData(data); + } + + public DataObject(string format, object data) { + SetData(format, data); + } + #endregion // Public Constructors + + #region Public Instance Methods + public virtual bool ContainsAudio () + { + return GetDataPresent (DataFormats.WaveAudio, true); + } + + public virtual bool ContainsFileDropList () + { + return GetDataPresent (DataFormats.FileDrop, true); + } + + public virtual bool ContainsImage () + { + return GetDataPresent (DataFormats.Bitmap, true); + } + + public virtual bool ContainsText () + { + return GetDataPresent (DataFormats.UnicodeText, true); + } + + public virtual bool ContainsText (TextDataFormat format) + { + if (!Enum.IsDefined (typeof (TextDataFormat), format)) + throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for TextDataFormat", format)); + + return GetDataPresent (TextFormatToDataFormat (format), true); + } + + public virtual Stream GetAudioStream () + { + return (Stream)GetData (DataFormats.WaveAudio, true); + } + + public virtual object GetData(string format) { + return GetData(format, true); + } + + public virtual object GetData(string format, bool autoConvert) { + Entry e; + if (autoConvert) { + e = Entry.FindConvertible(entries, format); + } else { + e = Entry.Find(entries, format); + } + if (e == null) + return null; + return e.Data; + } + + public virtual object GetData(Type format) { + return GetData(format.FullName, true); + } + + public virtual bool GetDataPresent(string format) { + return GetDataPresent(format, true); + } + + public virtual bool GetDataPresent(string format, bool autoConvert) { + if (autoConvert) { + return Entry.FindConvertible(entries, format) != null; + } else { + return Entry.Find(entries, format) != null; + } + } + + public virtual bool GetDataPresent(Type format) { + return GetDataPresent(format.FullName, true); + } + + public virtual StringCollection GetFileDropList () + { + return (StringCollection)GetData (DataFormats.FileDrop, true); + } + public virtual string[] GetFormats() { + return GetFormats(true); + } + + public virtual string[] GetFormats(bool autoConvert) { + return Entry.Entries(entries, autoConvert); + } + + public virtual Image GetImage () + { + return (Image)GetData (DataFormats.Bitmap, true); + } + + public virtual string GetText () + { + return (string)GetData (DataFormats.UnicodeText, true); + } + + public virtual string GetText (TextDataFormat format) + { + if (!Enum.IsDefined (typeof (TextDataFormat), format)) + throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for TextDataFormat", format)); + + return (string)GetData (TextFormatToDataFormat (format), false); + } + + public virtual void SetAudio (byte[] audioBytes) + { + if (audioBytes == null) + throw new ArgumentNullException ("audioBytes"); + + MemoryStream ms = new MemoryStream (audioBytes); + + SetAudio (ms); + } + + public virtual void SetAudio (Stream audioStream) + { + if (audioStream == null) + throw new ArgumentNullException ("audioStream"); + + SetData (DataFormats.WaveAudio, audioStream); + } + + public virtual void SetData(object data) { + SetData(data.GetType(), data); + } + + public virtual void SetData(string format, bool autoConvert, object data) { + Entry entry; + Entry e; + + entry = Entry.Find(entries, format); + + if (entry == null) { + entry = new DataObject.Entry(format, data, autoConvert); + } else { + entry.Data = data; + return; + } + + lock (this) { + if (entries == null) { + entries = entry; + } else { + // Insert into the list of known/defined formats + e = entries; + + while (e.next != null) { + e = e.next; + } + e.next = entry; + } + } + } + + public virtual void SetData(string format, object data) { + SetData(format, true, data); + } + + public virtual void SetData(Type format, object data) { + SetData(EnsureFormat(format), true, data); + } + + [MonoInternalNote ("Needs additional checks for valid paths, see MSDN")] + public virtual void SetFileDropList (StringCollection filePaths) + { + if (filePaths == null) + throw new ArgumentNullException ("filePaths"); + + SetData (DataFormats.FileDrop, filePaths); + } + + public virtual void SetImage (Image image) + { + if (image == null) + throw new ArgumentNullException ("image"); + + SetData (DataFormats.Bitmap, image); + } + + public virtual void SetText (string textData) + { + if (string.IsNullOrEmpty (textData)) + throw new ArgumentNullException ("text"); + + SetData (DataFormats.UnicodeText, textData); + } + + public virtual void SetText (string textData, TextDataFormat format) + { + if (string.IsNullOrEmpty (textData)) + throw new ArgumentNullException ("text"); + if (!Enum.IsDefined (typeof (TextDataFormat), format)) + throw new InvalidEnumArgumentException (string.Format ("Enum argument value '{0}' is not valid for TextDataFormat", format)); + + switch (format) { + case TextDataFormat.Text: + SetData (DataFormats.Text, textData); + break; + case TextDataFormat.UnicodeText: + SetData (DataFormats.UnicodeText, textData); + break; + case TextDataFormat.Rtf: + SetData (DataFormats.Rtf, textData); + break; + case TextDataFormat.Html: + SetData (DataFormats.Html, textData); + break; + case TextDataFormat.CommaSeparatedValue: + SetData (DataFormats.CommaSeparatedValue, textData); + break; + } + } + #endregion // Public Instance Methods + + #region Private Methods + internal string EnsureFormat(string name) { + DataFormats.Format f; + + f = DataFormats.Format.Find(name); + if (f == null) { + // Register the format + f = DataFormats.Format.Add(name); + } + + return f.Name; + } + + internal string EnsureFormat(Type type) { + return EnsureFormat(type.FullName); + } + + private string TextFormatToDataFormat (TextDataFormat format) + { + switch (format) { + case TextDataFormat.Text: + default: + return DataFormats.Text; + case TextDataFormat.UnicodeText: + return DataFormats.UnicodeText; + case TextDataFormat.Rtf: + return DataFormats.Rtf; + case TextDataFormat.Html: + return DataFormats.Html; + case TextDataFormat.CommaSeparatedValue: + return DataFormats.CommaSeparatedValue; + } + } + #endregion // Private Methods + + #region IDataObject Members + int System.Runtime.InteropServices.ComTypes.IDataObject.DAdvise (ref System.Runtime.InteropServices.ComTypes.FORMATETC pFormatetc, System.Runtime.InteropServices.ComTypes.ADVF advf, System.Runtime.InteropServices.ComTypes.IAdviseSink adviseSink, out int connection) + { + throw new NotImplementedException (); + } + + void System.Runtime.InteropServices.ComTypes.IDataObject.DUnadvise (int connection) + { + throw new NotImplementedException (); + } + + int System.Runtime.InteropServices.ComTypes.IDataObject.EnumDAdvise (out System.Runtime.InteropServices.ComTypes.IEnumSTATDATA enumAdvise) + { + throw new NotImplementedException (); + } + + System.Runtime.InteropServices.ComTypes.IEnumFORMATETC System.Runtime.InteropServices.ComTypes.IDataObject.EnumFormatEtc (System.Runtime.InteropServices.ComTypes.DATADIR direction) + { + throw new NotImplementedException (); + } + + int System.Runtime.InteropServices.ComTypes.IDataObject.GetCanonicalFormatEtc (ref System.Runtime.InteropServices.ComTypes.FORMATETC formatIn, out System.Runtime.InteropServices.ComTypes.FORMATETC formatOut) + { + throw new NotImplementedException (); + } + + void System.Runtime.InteropServices.ComTypes.IDataObject.GetData (ref System.Runtime.InteropServices.ComTypes.FORMATETC format, out System.Runtime.InteropServices.ComTypes.STGMEDIUM medium) + { + throw new NotImplementedException (); + } + + void System.Runtime.InteropServices.ComTypes.IDataObject.GetDataHere (ref System.Runtime.InteropServices.ComTypes.FORMATETC format, ref System.Runtime.InteropServices.ComTypes.STGMEDIUM medium) + { + throw new NotImplementedException (); + } + + int System.Runtime.InteropServices.ComTypes.IDataObject.QueryGetData (ref System.Runtime.InteropServices.ComTypes.FORMATETC format) + { + throw new NotImplementedException (); + } + + void System.Runtime.InteropServices.ComTypes.IDataObject.SetData (ref System.Runtime.InteropServices.ComTypes.FORMATETC formatIn, ref System.Runtime.InteropServices.ComTypes.STGMEDIUM medium, bool release) + { + throw new NotImplementedException (); + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/Day.cs b/source/ShiftUI/Internal/Day.cs new file mode 100644 index 0000000..0c797a7 --- /dev/null +++ b/source/ShiftUI/Internal/Day.cs @@ -0,0 +1,39 @@ +// +// 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 Novell, Inc. +// +// Authors: +// John BouAntoun [email protected] +// + +namespace ShiftUI { + // used mainly by monthcalendar + public enum Day { + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday, + Default + } +} diff --git a/source/ShiftUI/Internal/DebugHelper.cs b/source/ShiftUI/Internal/DebugHelper.cs new file mode 100644 index 0000000..3c76307 --- /dev/null +++ b/source/ShiftUI/Internal/DebugHelper.cs @@ -0,0 +1,257 @@ +// 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) 2008 Novell, Inc. +// +// Authors: +// Andreia Gaita ([email protected]) +// + +//#define DEBUG +//#define TRACE + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.IO; +using System.Diagnostics; + +namespace ShiftUI +{ + internal class DebugHelper + { + static DebugHelper () { + Debug.AutoFlush = true; + } + + struct Data { + public MethodBase method; + public object[] args; + public Data (MethodBase m, object[] a) { + this.method = m; + this.args = a; + } + } + + static Stack<Data> methods = new Stack<Data>(); + + [Conditional("DEBUG")] + internal static void DumpCallers () { + StackTrace trace = new StackTrace(true); + int count = trace.FrameCount; + Debug.Indent (); + for (int i = 1; i < count; i++) { + StackFrame parentFrame = trace.GetFrame(i); + MethodBase parentMethod = parentFrame.GetMethod(); + string file = parentFrame.GetFileName(); + if (file != null && file.Length > 1) + file = file.Substring (file.LastIndexOf (Path.DirectorySeparatorChar) + 1); + Debug.WriteLine(parentMethod.DeclaringType.Name + "." + parentMethod.Name + + " at " + file + ":" + parentFrame.GetFileLineNumber() + ); + } + + Debug.Unindent (); + } + + [Conditional("DEBUG")] + internal static void DumpCallers (int count) { + StackTrace trace = new StackTrace(true); + int c = (count > trace.FrameCount ? trace.FrameCount : count); + Debug.Indent (); + for (int i = 1; i < c; i++) { + StackFrame parentFrame = trace.GetFrame(i); + MethodBase parentMethod = parentFrame.GetMethod(); + string file = parentFrame.GetFileName(); + if (file != null && file.Length > 1) + file = file.Substring (file.LastIndexOf (Path.DirectorySeparatorChar) + 1); + Debug.WriteLine(parentMethod.DeclaringType.Name + "." + parentMethod.Name + + " at " + file + ":" + parentFrame.GetFileLineNumber() + ); + } + + Debug.Unindent (); + } + + [Conditional("DEBUG")] + internal static void Enter () + { + StackTrace trace = new StackTrace(); + methods.Push (new Data (trace.GetFrame(1).GetMethod(), null)); + Print (); + Debug.Indent (); + } + + [Conditional("DEBUG")] + internal static void Enter (object[] args) + { + StackTrace trace = new StackTrace(); + methods.Push (new Data (trace.GetFrame(1).GetMethod(), args)); + Print (); + Debug.Indent (); + } + + [Conditional("DEBUG")] + internal static void Leave () + { + if (methods.Count > 0) { + methods.Pop (); + Debug.Unindent (); + } + } + + [Conditional("DEBUG")] + internal static void Print () + { + if (methods.Count == 0) + return; + + Data data = methods.Peek (); + Debug.WriteLine (data.method.DeclaringType.Name + "." + data.method.Name); + } + + [Conditional("DEBUG")] + internal static void Print (int index) + { + if (methods.Count == 0 || methods.Count <= index || index < 0) + return; + + Stack<Data> temp = new Stack<Data>(index-1); + + for (int i = 0; i < index; i++) + temp.Push (methods.Pop ()); + + Data data = methods.Peek (); + for (int i = 0; i < temp.Count; i++) + methods.Push (temp.Pop()); + temp = null; + + Debug.WriteLine (data.method.DeclaringType.Name + "." + data.method.Name); + } + + [Conditional("DEBUG")] + internal static void Print (string methodName, string parameterName) + { + if (methods.Count == 0) + return; + + Stack<Data> temp = new Stack<Data>(); + + Data data = methods.Peek (); + bool foundit = false; + for (int i = 0; i < methods.Count; i++) + { + data = methods.Peek (); + if (data.method.Name.Equals (methodName)) { + foundit = true; + break; + } + temp.Push (methods.Pop ()); + } + + for (int i = 0; i < temp.Count; i++) + methods.Push (temp.Pop()); + temp = null; + + if (!foundit) + return; + + Debug.WriteLine (data.method.DeclaringType.Name + "." + data.method.Name); + ParameterInfo[] pi = data.method.GetParameters (); + + for (int i = 0; i < pi.Length; i++) { + if (pi[i].Name == parameterName) { + Debug.Indent (); + Debug.Write (parameterName + "="); + if (pi[i].ParameterType == typeof(IntPtr)) + Debug.WriteLine (String.Format ("0x{0:x}", ((IntPtr)data.args[i]).ToInt32())); + else + Debug.WriteLine (data.args[i]); + Debug.Unindent (); + } + } + } + + [Conditional("DEBUG")] + internal static void Print (string parameterName) + { + if (methods.Count == 0) + return; + Data data = methods.Peek (); + + ParameterInfo[] pi = data.method.GetParameters (); + + for (int i = 0; i < pi.Length; i++) { + if (pi[i].Name == parameterName) { + Debug.Indent (); + Debug.Write (parameterName + "="); + if (pi[i].ParameterType == typeof(IntPtr)) + Debug.WriteLine (String.Format ("0x{0:x}", data.args[i])); + else + Debug.WriteLine (data.args[i]); + Debug.Unindent (); + } + } + } + + [Conditional("DEBUG")] + internal static void WriteLine (object arg) + { + Debug.WriteLine (arg); + } + + [Conditional("DEBUG")] + internal static void WriteLine (string format, params object[] arg) + { + Debug.WriteLine (String.Format (format, arg)); + } + + [Conditional("DEBUG")] + internal static void WriteLine (string message) + { + Debug.WriteLine (message); + } + + [Conditional("DEBUG")] + internal static void Indent () + { + Debug.Indent (); + } + + [Conditional("DEBUG")] + internal static void Unindent () + { + Debug.Unindent (); + } + + [Conditional("TRACE")] + internal static void TraceWriteLine (string format, params object[] arg) + { + Debug.WriteLine (String.Format (format, arg)); + } + + [Conditional("TRACE")] + internal static void TraceWriteLine (string message) + { + Debug.WriteLine (message); + } + + } +} diff --git a/source/ShiftUI/Internal/DefaultLayout.cs b/source/ShiftUI/Internal/DefaultLayout.cs new file mode 100644 index 0000000..4fbf647 --- /dev/null +++ b/source/ShiftUI/Internal/DefaultLayout.cs @@ -0,0 +1,290 @@ +// +// DefaultLayout.cs +// +// 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) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// Stefan Noack ([email protected]) +// + +using System; +using System.Drawing; + +namespace ShiftUI.Layout +{ + class DefaultLayout : LayoutEngine + { + void LayoutDockedChildren (Widget parent, Widget[] Widgets) + { + Rectangle space = parent.DisplayRectangle; + MdiClient mdi = null; + + // Deal with docking; go through in reverse, MS docs say that lowest Z-order is closest to edge + for (int i = Widgets.Length - 1; i >= 0; i--) { + Widget child = Widgets[i]; + Size child_size = child.Size; + + if (child.AutoSize) + child_size = GetPreferredWidgetSize (child); + + if (!child.VisibleInternal + || child.WidgetLayoutType == Widget.LayoutType.Anchor) + continue; + + // MdiClient never fills the whole area like other Widgets, have to do it later + if (child is MdiClient) { + mdi = (MdiClient)child; + continue; + } + + switch (child.Dock) { + case DockStyle.None: + // Do nothing + break; + + case DockStyle.Left: + child.SetBoundsInternal (space.Left, space.Y, child_size.Width, space.Height, BoundsSpecified.None); + space.X += child.Width; + space.Width -= child.Width; + break; + + case DockStyle.Top: + child.SetBoundsInternal (space.Left, space.Y, space.Width, child_size.Height, BoundsSpecified.None); + space.Y += child.Height; + space.Height -= child.Height; + break; + + case DockStyle.Right: + child.SetBoundsInternal (space.Right - child_size.Width, space.Y, child_size.Width, space.Height, BoundsSpecified.None); + space.Width -= child.Width; + break; + + case DockStyle.Bottom: + child.SetBoundsInternal (space.Left, space.Bottom - child_size.Height, space.Width, child_size.Height, BoundsSpecified.None); + space.Height -= child.Height; + break; + + case DockStyle.Fill: + child.SetBoundsInternal (space.Left, space.Top, space.Width, space.Height, BoundsSpecified.None); + break; + } + } + + // MdiClient gets whatever space is left + if (mdi != null) + mdi.SetBoundsInternal (space.Left, space.Top, space.Width, space.Height, BoundsSpecified.None); + } + + void LayoutAnchoredChildren (Widget parent, Widget[] Widgets) + { + Rectangle space = parent.ClientRectangle; + + for (int i = 0; i < Widgets.Length; i++) { + int left; + int top; + int width; + int height; + + Widget child = Widgets[i]; + + if (!child.VisibleInternal + || child.WidgetLayoutType == Widget.LayoutType.Dock) + continue; + + AnchorStyles anchor = child.Anchor; + + left = child.Left; + top = child.Top; + + width = child.Width; + height = child.Height; + + if ((anchor & AnchorStyles.Right) != 0) { + if ((anchor & AnchorStyles.Left) != 0) + width = space.Width - child.dist_right - left; + else + left = space.Width - child.dist_right - width; + } + else if ((anchor & AnchorStyles.Left) == 0) { + // left+=diff_width/2 will introduce rounding errors (diff_width removed from svn after r51780) + // This calculates from scratch every time: + left = left + (space.Width - (left + width + child.dist_right)) / 2; + child.dist_right = space.Width - (left + width); + } + + if ((anchor & AnchorStyles.Bottom) != 0) { + if ((anchor & AnchorStyles.Top) != 0) + height = space.Height - child.dist_bottom - top; + else + top = space.Height - child.dist_bottom - height; + } + else if ((anchor & AnchorStyles.Top) == 0) { + // top += diff_height/2 will introduce rounding errors (diff_height removed from after r51780) + // This calculates from scratch every time: + top = top + (space.Height - (top + height + child.dist_bottom)) / 2; + child.dist_bottom = space.Height - (top + height); + } + + // Sanity + if (width < 0) + width = 0; + + if (height < 0) + height = 0; + + child.SetBoundsInternal (left, top, width, height, BoundsSpecified.None); + } + } + + void LayoutAutoSizedChildren (Widget parent, Widget[] Widgets) + { + for (int i = 0; i < Widgets.Length; i++) { + int left; + int top; + + Widget child = Widgets[i]; + if (!child.VisibleInternal + || child.WidgetLayoutType == Widget.LayoutType.Dock + || !child.AutoSize) + continue; + + AnchorStyles anchor = child.Anchor; + left = child.Left; + top = child.Top; + + Size preferredsize = GetPreferredWidgetSize (child); + + if (((anchor & AnchorStyles.Left) != 0) || ((anchor & AnchorStyles.Right) == 0)) + child.dist_right += child.Width - preferredsize.Width; + if (((anchor & AnchorStyles.Top) != 0) || ((anchor & AnchorStyles.Bottom) == 0)) + child.dist_bottom += child.Height - preferredsize.Height; + + child.SetBoundsInternal (left, top, preferredsize.Width, preferredsize.Height, BoundsSpecified.None); + } + } + + void LayoutAutoSizeContainer (Widget container) + { + int left; + int top; + int width; + int height; + + if (!container.VisibleInternal || container.WidgetLayoutType == Widget.LayoutType.Dock || !container.AutoSize) + return; + + left = container.Left; + top = container.Top; + + Size preferredsize = container.PreferredSize; + + if (container.GetAutoSizeMode () == AutoSizeMode.GrowAndShrink) { + width = preferredsize.Width; + height = preferredsize.Height; + } else { + width = container.ExplicitBounds.Width; + height = container.ExplicitBounds.Height; + if (preferredsize.Width > width) + width = preferredsize.Width; + if (preferredsize.Height > height) + height = preferredsize.Height; + } + + // Sanity + if (width < container.MinimumSize.Width) + width = container.MinimumSize.Width; + + if (height < container.MinimumSize.Height) + height = container.MinimumSize.Height; + + if (container.MaximumSize.Width != 0 && width > container.MaximumSize.Width) + width = container.MaximumSize.Width; + + if (container.MaximumSize.Height != 0 && height > container.MaximumSize.Height) + height = container.MaximumSize.Height; + + container.SetBoundsInternal (left, top, width, height, BoundsSpecified.None); + } + + public override bool Layout (object container, LayoutEventArgs args) + { + Widget parent = container as Widget; + + Widget[] Widgets = parent.Widgets.GetAllWidgets (); + + LayoutDockedChildren (parent, Widgets); + LayoutAnchoredChildren (parent, Widgets); + LayoutAutoSizedChildren (parent, Widgets); + if (parent is Form) LayoutAutoSizeContainer (parent); + + return false; + } + + private Size GetPreferredWidgetSize (Widget child) + { + int width; + int height; + Size preferredsize = child.PreferredSize; + + if (child.GetAutoSizeMode () == AutoSizeMode.GrowAndShrink || (child.Dock != DockStyle.None && !(child is Button) && !(child is FlowLayoutPanel))) { + width = preferredsize.Width; + height = preferredsize.Height; + } else { + width = child.ExplicitBounds.Width; + height = child.ExplicitBounds.Height; + if (preferredsize.Width > width) + width = preferredsize.Width; + if (preferredsize.Height > height) + height = preferredsize.Height; + } + if (child.AutoSize && child is FlowLayoutPanel && child.Dock != DockStyle.None) { + switch (child.Dock) { + case DockStyle.Left: + case DockStyle.Right: + if (preferredsize.Width < child.ExplicitBounds.Width && preferredsize.Height < child.Parent.PaddingClientRectangle.Height) + width = preferredsize.Width; + break; + case DockStyle.Top: + case DockStyle.Bottom: + if (preferredsize.Height < child.ExplicitBounds.Height && preferredsize.Width < child.Parent.PaddingClientRectangle.Width) + height = preferredsize.Height; + break; + } + } + // Sanity + if (width < child.MinimumSize.Width) + width = child.MinimumSize.Width; + + if (height < child.MinimumSize.Height) + height = child.MinimumSize.Height; + + if (child.MaximumSize.Width != 0 && width > child.MaximumSize.Width) + width = child.MaximumSize.Width; + + if (child.MaximumSize.Height != 0 && height > child.MaximumSize.Height) + height = child.MaximumSize.Height; + + return new Size (width, height); + } + } +} diff --git a/source/ShiftUI/Internal/Dnd.cs b/source/ShiftUI/Internal/Dnd.cs new file mode 100644 index 0000000..b08b528 --- /dev/null +++ b/source/ShiftUI/Internal/Dnd.cs @@ -0,0 +1,371 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton ([email protected]) +// +// + + +using System; +using System.IO; +using System.Text; +using System.Drawing; +using System.Threading; +using System.Collections; +using ShiftUI; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization.Formatters.Binary; + +namespace ShiftUI.CarbonInternal { + internal delegate void DragTrackingDelegate (short message, IntPtr window, IntPtr data, IntPtr dragref); + + internal class Dnd { + internal const uint kEventParamDragRef = 1685217639; + internal const uint typeDragRef = 1685217639; + + internal const uint typeMono = 1836019311; + internal const uint typeMonoSerializedObject = 1836279154; + + private static DragDropEffects effects = DragDropEffects.None; + private static DragTrackingDelegate DragTrackingHandler = new DragTrackingDelegate (TrackingCallback); + + static Dnd () { + try { + InstallTrackingHandler (DragTrackingHandler, IntPtr.Zero, IntPtr.Zero); + } catch (EntryPointNotFoundException) { + // it is deprecated in 10.6 and does not exist anymore. + } + } + + internal Dnd () { + } + + internal static void TrackingCallback (short message, IntPtr window, IntPtr data, IntPtr dragref) { + XplatUICarbon.GetInstance ().FlushQueue (); + } + + internal static DragDropEffects DragActionsToEffects (UInt32 actions) { + DragDropEffects effects = DragDropEffects.None; + if ((actions & 1) != 0) + effects |= DragDropEffects.Copy; + if ((actions & (1 << 4)) != 0) + effects |= DragDropEffects.Move; + if ((actions & 0xFFFFFFFF) != 0) + effects |= DragDropEffects.All; + + return effects; + } + + internal static DataObject DragToDataObject (IntPtr dragref) { + UInt32 items = 0; + ArrayList flavorlist = new ArrayList (); + + CountDragItems (dragref, ref items); + + for (uint item_counter = 1; item_counter <= items; item_counter++) { + IntPtr itemref = IntPtr.Zero; + UInt32 flavors = 0; + + GetDragItemReferenceNumber (dragref, item_counter, ref itemref); + CountDragItemFlavors (dragref, itemref, ref flavors); + for (uint flavor_counter = 1; flavor_counter <= flavors; flavor_counter++) { + FlavorHandler flavor = new FlavorHandler (dragref, itemref, flavor_counter); + if (flavor.Supported) + flavorlist.Add (flavor); + } + } + + if (flavorlist.Count > 0) { + return ((FlavorHandler) flavorlist [0]).Convert (flavorlist); + } + + return new DataObject (); + } + + internal static bool HandleEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) { + Widget Widget; + DataObject data; + DragEventArgs drag_event; + DragDropEffects allowed; + QDPoint point = new QDPoint (); + UInt32 actions = 0; + IntPtr dragref = IntPtr.Zero; + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null || hwnd.Handle != handle) + return false; + + GetEventParameter (eventref, kEventParamDragRef, typeDragRef, IntPtr.Zero, (uint) Marshal.SizeOf (typeof (IntPtr)), IntPtr.Zero, ref dragref); + XplatUICarbon.GetGlobalMouse (ref point); + GetDragAllowableActions (dragref, ref actions); + Widget = Widget.FromHandle (hwnd.Handle); + allowed = DragActionsToEffects (actions); + data = DragToDataObject (dragref); + drag_event = new DragEventArgs (data, 0, point.x, point.y, allowed, DragDropEffects.None); + + switch (kind) { + case WidgetHandler.kEventWidgetDragEnter: { + bool accept = Widget.AllowDrop; + SetEventParameter (eventref, WidgetHandler.kEventParamWidgetLikesDrag, WidgetHandler.typeBoolean, (uint)Marshal.SizeOf (typeof (bool)), ref accept); + + Widget.DndEnter (drag_event); + effects = drag_event.Effect; + return false; + } + case WidgetHandler.kEventWidgetDragWithin: + Widget.DndOver (drag_event); + effects = drag_event.Effect; + break; + case WidgetHandler.kEventWidgetDragLeave: + Widget.DndLeave (drag_event); + break; + case WidgetHandler.kEventWidgetDragReceive: + Widget.DndDrop (drag_event); + break; + } + return true; + } + + public void SetAllowDrop (Hwnd hwnd, bool allow) { + if (hwnd.allow_drop == allow) + return; + + hwnd.allow_drop = allow; + SetWidgetDragTrackingEnabled (hwnd.whole_window, true); + SetWidgetDragTrackingEnabled (hwnd.client_window, true); + } + + public void SendDrop (IntPtr handle, IntPtr from, IntPtr time) { + } + + public DragDropEffects StartDrag (IntPtr handle, object data, DragDropEffects allowed_effects) { + IntPtr dragref = IntPtr.Zero; + EventRecord eventrecord = new EventRecord (); + + effects = DragDropEffects.None; + + NewDrag (ref dragref); + XplatUICarbon.GetGlobalMouse (ref eventrecord.mouse); + StoreObjectInDrag (handle, dragref, data); + + TrackDrag (dragref, ref eventrecord, IntPtr.Zero); + + DisposeDrag (dragref); + + return effects; + } + + public void StoreObjectInDrag (IntPtr handle, IntPtr dragref, object data) { + IntPtr type = IntPtr.Zero; + IntPtr dataptr = IntPtr.Zero; + Int32 size = 0; + + if (data is string) { + // implement me + throw new NotSupportedException ("Implement me."); + } else if (data is ISerializable) { + MemoryStream stream = new MemoryStream (); + BinaryFormatter bf = new BinaryFormatter (); + + bf.Serialize (stream, data); + + dataptr = Marshal.AllocHGlobal ((int) stream.Length); + stream.Seek (0, 0); + + for (int i = 0; i < stream.Length; i++) { + Marshal.WriteByte (dataptr, i, (byte) stream.ReadByte ()); + } + + type = (IntPtr) typeMonoSerializedObject; + size = (int) stream.Length; + } else { + dataptr = (IntPtr) GCHandle.Alloc (data); + + type = (IntPtr) typeMono; + size = Marshal.SizeOf (typeof (IntPtr)); + } + + AddDragItemFlavor (dragref, handle, type, ref dataptr, size, 1 << 0); + } + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int InstallTrackingHandler (DragTrackingDelegate callback, IntPtr window, IntPtr data); + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref IntPtr data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int SetEventParameter (IntPtr eventref, uint name, uint type, uint size, ref bool data); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int SetWidgetDragTrackingEnabled (IntPtr view, bool enabled); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int AddDragItemFlavor (IntPtr dragref, IntPtr itemref, IntPtr flavortype, ref IntPtr data, Int32 size, UInt32 flags); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int CountDragItems (IntPtr dragref, ref UInt32 count); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int CountDragItemFlavors (IntPtr dragref, IntPtr itemref, ref UInt32 count); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetDragItemReferenceNumber (IntPtr dragref, UInt32 index, ref IntPtr itemref); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int NewDrag (ref IntPtr dragref); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int TrackDrag (IntPtr dragref, ref EventRecord eventrecord, IntPtr region); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int DisposeDrag (IntPtr dragref); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetDragAllowableActions (IntPtr dragref, ref UInt32 actions); + } + + internal struct EventRecord { + internal UInt16 what; + internal UInt32 message; + internal UInt32 when; + internal QDPoint mouse; + internal UInt16 modifiers; + } + + internal class FlavorHandler { + internal IntPtr flavorref; + internal IntPtr dragref; + internal IntPtr itemref; + internal Int32 size; + internal UInt32 flags; + internal byte [] data; + internal string fourcc; + + internal FlavorHandler (IntPtr dragref, IntPtr itemref, uint counter) { + GetFlavorType (dragref, itemref, counter, ref flavorref); + GetFlavorFlags (dragref, itemref, flavorref, ref flags); + byte [] fourcc_b = BitConverter.GetBytes ((Int32)flavorref); + this.fourcc = String.Format ("{0}{1}{2}{3}", (char)fourcc_b [3], (char)fourcc_b [2], (char)fourcc_b [1], (char)fourcc_b [0]); + this.dragref = dragref; + this.itemref = itemref; + + this.GetData (); + } + + internal void GetData () { + GetFlavorDataSize (dragref, itemref, flavorref, ref size); + data = new byte [size]; + GetFlavorData (dragref, itemref, flavorref, data, ref size, 0); + } + + internal string DataString { + get { return Encoding.Default.GetString (this.data); } + } + + internal byte[] DataArray { + get { return this.data; } + } + + internal IntPtr DataPtr { + get { return (IntPtr) BitConverter.ToInt32 (this.data, 0); } + } + + internal bool Supported { + get { + switch (fourcc) { + case "furl": + return true; + case "mono": + return true; + case "mser": + return true; + } + return false; + } + } + + internal DataObject Convert (ArrayList flavorlist) { + switch (fourcc) { + case "furl": + return ConvertToFileDrop (flavorlist); + case "mono": + return ConvertToObject (flavorlist); + case "mser": + return DeserializeObject (flavorlist); + } + + return new DataObject (); + } + + internal DataObject DeserializeObject (ArrayList flavorlist) { + DataObject data = new DataObject (); + MemoryStream stream = new MemoryStream (this.DataArray); + BinaryFormatter bf = new BinaryFormatter (); + + if (stream.Length == 0) + return data; + + stream.Seek (0, 0); + data.SetData (bf.Deserialize (stream)); + + return data; + } + + internal DataObject ConvertToObject (ArrayList flavorlist) { + DataObject data = new DataObject (); + + foreach (FlavorHandler flavor in flavorlist) { + GCHandle handle = (GCHandle) flavor.DataPtr; + + data.SetData (handle.Target); + } + + return data; + } + + internal DataObject ConvertToFileDrop (ArrayList flavorlist) { + DataObject data = new DataObject (); + ArrayList uri_list = new ArrayList (); + + foreach (FlavorHandler flavor in flavorlist) { + try { + uri_list.Add (new Uri (flavor.DataString).LocalPath); + } catch { } + } + + string [] l = (string []) uri_list.ToArray (typeof (string)); + if (l.Length < 1) + return data; + data.SetData (DataFormats.FileDrop, l); + data.SetData ("FileName", l [0]); + data.SetData ("FileNameW", l [0]); + + return data; + } + + public override string ToString () { + return fourcc; + } + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetFlavorDataSize (IntPtr dragref, IntPtr itemref, IntPtr flavorref, ref Int32 size); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetFlavorData (IntPtr dragref, IntPtr itemref, IntPtr flavorref, [In, Out] byte[] data, ref Int32 size, UInt32 offset); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetFlavorFlags (IntPtr dragref, IntPtr itemref, IntPtr flavorref, ref UInt32 flags); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetFlavorType (IntPtr dragref, IntPtr itemref, UInt32 index, ref IntPtr flavor); + } +} diff --git a/source/ShiftUI/Internal/DockingAttribute.cs b/source/ShiftUI/Internal/DockingAttribute.cs new file mode 100644 index 0000000..3aba6bf --- /dev/null +++ b/source/ShiftUI/Internal/DockingAttribute.cs @@ -0,0 +1,74 @@ +// +// DockingAttribute.cs +// +// 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) 2006 Daniel Nauck +// +// Author: +// Daniel Nauck (dna(at)mono-project(dot)de) + + +using System; +using ShiftUI; + +namespace ShiftUI +{ + [AttributeUsageAttribute(AttributeTargets.Class)] + public sealed class DockingAttribute : Attribute + { + private DockingBehavior dockingBehavior; + + public DockingAttribute() + { + dockingBehavior = DockingBehavior.Never; + } + + public DockingAttribute(DockingBehavior dockingBehavior) + { + this.dockingBehavior = dockingBehavior; + } + + public static readonly DockingAttribute Default = new DockingAttribute(); + + public DockingBehavior DockingBehavior + { + get { return dockingBehavior; } + } + + public override bool Equals(object obj) + { + if (obj is DockingAttribute) + return (dockingBehavior == ((DockingAttribute)obj).DockingBehavior); + else + return false; + } + + public override int GetHashCode() + { + return dockingBehavior.GetHashCode(); + } + + public override bool IsDefaultAttribute() + { + return Default.Equals(this); + } + } +} diff --git a/source/ShiftUI/Internal/DockingBehavior.cs b/source/ShiftUI/Internal/DockingBehavior.cs new file mode 100644 index 0000000..b5dcdc0 --- /dev/null +++ b/source/ShiftUI/Internal/DockingBehavior.cs @@ -0,0 +1,38 @@ +// +// DockingBehavior.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +namespace ShiftUI +{ + public enum DockingBehavior + { + Never = 0, + Ask = 1, + AutoDock = 2 + } +} diff --git a/source/ShiftUI/Internal/DrawToolTipEventArgs.cs b/source/ShiftUI/Internal/DrawToolTipEventArgs.cs new file mode 100644 index 0000000..8d66f5f --- /dev/null +++ b/source/ShiftUI/Internal/DrawToolTipEventArgs.cs @@ -0,0 +1,113 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Rolf Bjarne Kvinge <[email protected]> +// +// +// COMPLETE + + +using System.Drawing; +using System; + +namespace ShiftUI +{ + public class DrawToolTipEventArgs : EventArgs + { + private Widget associated_control; + private IWin32Window associated_window; + private Color back_color; + private Font font; + private Rectangle bounds; + private Color fore_color; + private Graphics graphics; + private string tooltip_text; + + public DrawToolTipEventArgs (Graphics graphics, IWin32Window associatedWindow, Widget associatedControl, Rectangle bounds, string toolTipText, Color backColor, Color foreColor, Font font) + { + this.graphics = graphics; + this.associated_window = associatedWindow; + this.associated_control = associatedControl; + this.bounds = bounds; + this.tooltip_text = toolTipText; + this.back_color = backColor; + this.fore_color = foreColor; + this.font = font; + } + + public void DrawBackground () + { + graphics.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (back_color), bounds); + } + + public void DrawBorder () + { + WidgetPaint.DrawBorder (graphics, bounds, SystemColors.WindowFrame, ButtonBorderStyle.Solid); + } + + public void DrawText () + { + DrawText (TextFormatFlags.HidePrefix | TextFormatFlags.SingleLine | TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter); + } + + public void DrawText (TextFormatFlags flags) + { + TextRenderer.DrawTextInternal (graphics, tooltip_text, font, bounds, fore_color, flags, false); + } + + public Widget AssociatedWidget { + get { + return associated_control; + } + } + + public IWin32Window AssociatedWindow { + get { + return associated_window; + } + } + + public Rectangle Bounds { + get { + return bounds; + } + } + + public Font Font { + get { + return font; + } + } + + public Graphics Graphics { + get { + return graphics; + } + } + + public string ToolTipText { + get { + return tooltip_text; + } + } + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/DrawToolTipEventHandler.cs b/source/ShiftUI/Internal/DrawToolTipEventHandler.cs new file mode 100644 index 0000000..e647ae8 --- /dev/null +++ b/source/ShiftUI/Internal/DrawToolTipEventHandler.cs @@ -0,0 +1,31 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Rolf Bjarne Kvinge <[email protected]> +// +// +// COMPLETE + +namespace ShiftUI +{ + public delegate void DrawToolTipEventHandler (object sender, DrawToolTipEventArgs e); +} diff --git a/source/ShiftUI/Internal/Enums.cs b/source/ShiftUI/Internal/Enums.cs new file mode 100644 index 0000000..50d705e --- /dev/null +++ b/source/ShiftUI/Internal/Enums.cs @@ -0,0 +1,108 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// + +using System; + +namespace ShiftUI.CarbonInternal { + internal enum WindowClass : uint { + kAlertWindowClass = 1, + kMovableAlertWindowClass = 2, + kModalWindowClass = 3, + kMovableModalWindowClass = 4, + kFloatingWindowClass = 5, + kDocumentWindowClass = 6, + kUtilityWindowClass = 8, + kHelpWindowClass = 10, + kSheetWindowClass = 11, + kToolbarWindowClass = 12, + kPlainWindowClass = 13, + kOverlayWindowClass = 14, + kSheetAlertWindowClass = 15, + kAltPlainWindowClass = 16, + kDrawerWindowClass = 20, + kAllWindowClasses = 0xFFFFFFFF + } + + internal enum WindowAttributes : uint { + kWindowNoAttributes = 0, + kWindowCloseBoxAttribute = (1u << 0), + kWindowHorizontalZoomAttribute = (1u << 1), + kWindowVerticalZoomAttribute = (1u << 2), + kWindowFullZoomAttribute = (kWindowVerticalZoomAttribute | kWindowHorizontalZoomAttribute), + kWindowCollapseBoxAttribute = (1u << 3), + kWindowResizableAttribute = (1u << 4), + kWindowSideTitlebarAttribute = (1u << 5), + kWindowToolbarButtonAttribute = (1u << 6), + kWindowMetalAttribute = (1u << 8), + kWindowNoUpdatesAttribute = (1u << 16), + kWindowNoActivatesAttribute = (1u << 17), + kWindowOpaqueForEventsAttribute = (1u << 18), + kWindowCompositingAttribute = (1u << 19), + kWindowNoShadowAttribute = (1u << 21), + kWindowHideOnSuspendAttribute = (1u << 24), + kWindowStandardHandlerAttribute = (1u << 25), + kWindowHideOnFullScreenAttribute = (1u << 26), + kWindowInWindowMenuAttribute = (1u << 27), + kWindowLiveResizeAttribute = (1u << 28), + kWindowIgnoreClicksAttribute = (1u << 29), + kWindowNoConstrainAttribute = (1u << 31), + kWindowStandardDocumentAttributes = (kWindowCloseBoxAttribute | kWindowFullZoomAttribute | kWindowCollapseBoxAttribute | kWindowResizableAttribute), + kWindowStandardFloatingAttributes = (kWindowCloseBoxAttribute | kWindowCollapseBoxAttribute) + } + + internal enum ThemeCursor : uint { + kThemeArrowCursor = 0, + kThemeCopyArrowCursor = 1, + kThemeAliasArrowCursor = 2, + kThemeContextualMenuArrowCursor = 3, + kThemeIBeamCursor = 4, + kThemeCrossCursor = 5, + kThemePlusCursor = 6, + kThemeWatchCursor = 7, + kThemeClosedHandCursor = 8, + kThemeOpenHandCursor = 9, + kThemePointingHandCursor = 10, + kThemeCountingUpHandCursor = 11, + kThemeCountingDownHandCursor = 12, + kThemeCountingUpAndDownHandCursor = 13, + kThemeSpinningCursor = 14, + kThemeResizeLeftCursor = 15, + kThemeResizeRightCursor = 16, + kThemeResizeLeftRightCursor = 17, + kThemeNotAllowedCursor = 18 + } + + internal enum MouseTrackingResult : ushort { + kMouseTrackingMouseDown = 1, + kMouseTrackingMouseUp = 2, + kMouseTrackingMouseExited = 3, + kMouseTrackingMouseEntered = 4, + kMouseTrackingMouseDragged = 5, + kMouseTrackingKeyModifiersChanged = 6, + kMouseTrackingUserCancelled = 7, + kMouseTrackingTimedOut = 8, + kMouseTrackingMouseMoved = 9 + } +} diff --git a/source/ShiftUI/Internal/EventHandler.cs b/source/ShiftUI/Internal/EventHandler.cs new file mode 100644 index 0000000..673e3d0 --- /dev/null +++ b/source/ShiftUI/Internal/EventHandler.cs @@ -0,0 +1,184 @@ +// 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 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; +using System.Runtime.InteropServices; + +namespace ShiftUI.CarbonInternal { + internal delegate int EventDelegate (IntPtr callref, IntPtr eventref, IntPtr user_data); + + internal class EventHandler { + internal static EventDelegate EventHandlerDelegate = new EventDelegate (EventCallback); + internal static XplatUICarbon Driver; + + internal const int EVENT_NOT_HANDLED = 0; + internal const int EVENT_HANDLED = -9874; + + internal const uint kEventClassMouse = 1836021107; + internal const uint kEventClassKeyboard = 1801812322; + internal const uint kEventClassTextInput = 1952807028; + internal const uint kEventClassApplication = 1634758764; + internal const uint kEventClassAppleEvent = 1701867619; + internal const uint kEventClassMenu = 1835363957; + internal const uint kEventClassWindow = 2003398244; + internal const uint kEventClassWidget = 1668183148; + internal const uint kEventClassCommand = 1668113523; + internal const uint kEventClassTablet = 1952607348; + internal const uint kEventClassVolume = 1987013664; + internal const uint kEventClassAppearance = 1634758765; + internal const uint kEventClassService = 1936028278; + internal const uint kEventClassToolbar = 1952604530; + internal const uint kEventClassToolbarItem = 1952606580; + internal const uint kEventClassAccessibility = 1633903461; + internal const uint kEventClassHIObject = 1751740258; + + internal static EventTypeSpec [] HIObjectEvents = new EventTypeSpec [] { + new EventTypeSpec (kEventClassHIObject, HIObjectHandler.kEventHIObjectConstruct), + new EventTypeSpec (kEventClassHIObject, HIObjectHandler.kEventHIObjectInitialize), + new EventTypeSpec (kEventClassHIObject, HIObjectHandler.kEventHIObjectDestruct) + }; + internal static EventTypeSpec [] WidgetEvents = new EventTypeSpec [] { + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetBoundsChanged), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetDraw), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetDragEnter), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetDragWithin), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetDragLeave), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetDragReceive), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetGetFocusPart), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetInitialize), + new EventTypeSpec (kEventClassWidget, WidgetHandler.kEventWidgetVisibilityChanged) + }; + + internal static EventTypeSpec [] ApplicationEvents = new EventTypeSpec[] { + new EventTypeSpec (kEventClassApplication, ApplicationHandler.kEventAppActivated), + new EventTypeSpec (kEventClassApplication, ApplicationHandler.kEventAppDeactivated) + }; + + private static EventTypeSpec [] WindowEvents = new EventTypeSpec[] { + new EventTypeSpec (kEventClassMouse, MouseHandler.kEventMouseMoved), + new EventTypeSpec (kEventClassMouse, MouseHandler.kEventMouseDragged), + new EventTypeSpec (kEventClassMouse, MouseHandler.kEventMouseDown), + new EventTypeSpec (kEventClassMouse, MouseHandler.kEventMouseUp), + new EventTypeSpec (kEventClassMouse, MouseHandler.kEventMouseWheelMoved), + new EventTypeSpec (kEventClassMouse, MouseHandler.kEventMouseScroll), + + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowDeactivated), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowActivated), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowDeactivated), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowCollapsed), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowCollapsing), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowExpanded), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowExpanding), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowBoundsChanged), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowResizeStarted), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowResizeCompleted), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowClose), + new EventTypeSpec (kEventClassWindow, WindowHandler.kEventWindowShown), + + new EventTypeSpec (kEventClassKeyboard, KeyboardHandler.kEventRawKeyModifiersChanged), + new EventTypeSpec (kEventClassKeyboard, KeyboardHandler.kEventRawKeyDown), + new EventTypeSpec (kEventClassKeyboard, KeyboardHandler.kEventRawKeyRepeat), + new EventTypeSpec (kEventClassKeyboard, KeyboardHandler.kEventRawKeyUp), + new EventTypeSpec (kEventClassTextInput, KeyboardHandler.kEventTextInputUnicodeForKeyEvent) + }; + + internal static int EventCallback (IntPtr callref, IntPtr eventref, IntPtr handle) { + uint klass = GetEventClass (eventref); + uint kind = GetEventKind (eventref); + MSG msg = new MSG (); + IEventHandler handler = null; + + switch (klass) { + case kEventClassHIObject: { + handler = (IEventHandler) Driver.HIObjectHandler; + break; + } + case kEventClassKeyboard: + case kEventClassTextInput: + handler = (IEventHandler) Driver.KeyboardHandler; + break; + case kEventClassWindow: + handler = (IEventHandler) Driver.WindowHandler; + break; + case kEventClassMouse: + handler = (IEventHandler) Driver.MouseHandler; + break; + case kEventClassWidget: + handler = (IEventHandler) Driver.WidgetHandler; + break; + case kEventClassApplication: + handler = (IEventHandler) Driver.ApplicationHandler; + break; + default: + return EVENT_NOT_HANDLED; + } + + if (handler.ProcessEvent (callref, eventref, handle, kind, ref msg)) { + Driver.EnqueueMessage (msg); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; + } + + internal static bool TranslateMessage (ref MSG msg) { + bool result = false; + + if (!result) + result = Driver.KeyboardHandler.TranslateMessage (ref msg); + if (!result) + result = Driver.MouseHandler.TranslateMessage (ref msg); + + return result; + } + + internal static void InstallApplicationHandler () { + InstallEventHandler (GetApplicationEventTarget (), EventHandlerDelegate, (uint)ApplicationEvents.Length, ApplicationEvents, IntPtr.Zero, IntPtr.Zero); + } + + internal static void InstallWidgetHandler (IntPtr Widget) { + InstallEventHandler (GetWidgetEventTarget (Widget), EventHandlerDelegate, (uint)WidgetEvents.Length, WidgetEvents, Widget, IntPtr.Zero); + } + + internal static void InstallWindowHandler (IntPtr window) { + InstallEventHandler (GetWindowEventTarget (window), EventHandlerDelegate, (uint)WindowEvents.Length, WindowEvents, window, IntPtr.Zero); + } + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern IntPtr GetApplicationEventTarget (); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern IntPtr GetWidgetEventTarget (IntPtr Widget); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern IntPtr GetWindowEventTarget (IntPtr window); + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern uint GetEventClass (IntPtr eventref); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern uint GetEventKind (IntPtr eventref); + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int InstallEventHandler (IntPtr window, EventDelegate event_handler, uint count, EventTypeSpec [] types, IntPtr user_data, IntPtr handlerref); + } +} diff --git a/source/ShiftUI/Internal/EventHandlerBase.cs b/source/ShiftUI/Internal/EventHandlerBase.cs new file mode 100644 index 0000000..39bada4 --- /dev/null +++ b/source/ShiftUI/Internal/EventHandlerBase.cs @@ -0,0 +1,40 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; + +namespace ShiftUI.CarbonInternal { + internal abstract class EventHandlerBase { + internal XplatUICarbon Driver; + + public EventHandlerBase () { + } + + public EventHandlerBase (XplatUICarbon driver) { + Driver = driver; + } + } +} diff --git a/source/ShiftUI/Internal/FixedSizeTextBox.cs b/source/ShiftUI/Internal/FixedSizeTextBox.cs new file mode 100644 index 0000000..33b5dff --- /dev/null +++ b/source/ShiftUI/Internal/FixedSizeTextBox.cs @@ -0,0 +1,47 @@ +// 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) 2006 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) +// +// + +// This is an internal class that allows us to use a textbox as a child control +// for things such as UpDown Widgets, and the ComboBox that will not have their +// size altered by scaling + +namespace ShiftUI { + + internal class FixedSizeTextBox : TextBox { + + public FixedSizeTextBox () + { + SetStyle (Widgetstyles.FixedWidth, true); + SetStyle (Widgetstyles.FixedHeight, true); + } + + public FixedSizeTextBox (bool fixed_horz, bool fixed_vert) { + SetStyle (Widgetstyles.FixedWidth, fixed_horz); + SetStyle (Widgetstyles.FixedHeight, fixed_vert); + } + } +} + diff --git a/source/ShiftUI/Internal/FlowLayout.cs b/source/ShiftUI/Internal/FlowLayout.cs new file mode 100644 index 0000000..5eb55a5 --- /dev/null +++ b/source/ShiftUI/Internal/FlowLayout.cs @@ -0,0 +1,589 @@ +// +// FlowLayout.cs +// +// 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) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace ShiftUI.Layout +{ + class FlowLayout : LayoutEngine + { + private static FlowLayoutSettings default_settings = new FlowLayoutSettings (); + + public FlowLayout () + { + } + + public override void InitLayout (object child, BoundsSpecified specified) + { + base.InitLayout (child, specified); + } + + public override bool Layout (object container, LayoutEventArgs args) + { + if (container is ToolStripPanel) + return false; + + if (container is ToolStrip) + return LayoutToolStrip ((ToolStrip)container); + + Widget parent = container as Widget; + + FlowLayoutSettings settings; + if (parent is FlowLayoutPanel) + settings = (parent as FlowLayoutPanel).LayoutSettings; + else + settings = default_settings; + + // Nothing to layout, exit method + if (parent.Widgets.Count == 0) return false; + + // Use DisplayRectangle so that parent.Padding is honored. + Rectangle parentDisplayRectangle = parent.DisplayRectangle; + Point currentLocation; + + // Set our starting point based on flow direction + switch (settings.FlowDirection) { + case FlowDirection.BottomUp: + currentLocation = new Point (parentDisplayRectangle.Left, parentDisplayRectangle.Bottom); + break; + case FlowDirection.LeftToRight: + case FlowDirection.TopDown: + default: + currentLocation = parentDisplayRectangle.Location; + break; + case FlowDirection.RightToLeft: + currentLocation = new Point (parentDisplayRectangle.Right, parentDisplayRectangle.Top); + break; + } + + bool forceFlowBreak = false; + + List<Widget> rowWidgets = new List<Widget> (); + + foreach (Widget c in parent.Widgets) { + // Only apply layout to visible Widgets. + if (!c.Visible) { continue; } + + // Resize any AutoSize Widgets to their preferred size + if (c.AutoSize == true) { + Size new_size = c.GetPreferredSize (c.Size); + c.SetBoundsInternal (c.Left, c.Top, new_size.Width, new_size.Height, BoundsSpecified.None); + } + + switch (settings.FlowDirection) { + case FlowDirection.BottomUp: + // Decide if it's time to start a new column + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents) + if ((currentLocation.Y) < (c.Height + c.Margin.Top + c.Margin.Bottom) || forceFlowBreak) { + + currentLocation.X = FinishColumn (rowWidgets); + currentLocation.Y = parentDisplayRectangle.Bottom; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the right margin and set the Widget to our point + currentLocation.Offset (0, c.Margin.Bottom * -1); + c.SetBoundsInternal (currentLocation.X + c.Margin.Left, currentLocation.Y - c.Height, c.Width, c.Height, BoundsSpecified.None); + + // Update our location pointer + currentLocation.Y -= (c.Height + c.Margin.Top); + break; + case FlowDirection.LeftToRight: + default: + // Decide if it's time to start a new row + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents && !(parent is ToolStripPanel)) + if ((parentDisplayRectangle.Width + parentDisplayRectangle.Left - currentLocation.X) < (c.Width + c.Margin.Left + c.Margin.Right) || forceFlowBreak) { + + currentLocation.Y = FinishRow (rowWidgets); + currentLocation.X = parentDisplayRectangle.Left; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the left margin and set the Widget to our point + currentLocation.Offset (c.Margin.Left, 0); + c.SetBoundsInternal (currentLocation.X, currentLocation.Y + c.Margin.Top, c.Width, c.Height, BoundsSpecified.None); + + // Update our location pointer + currentLocation.X += c.Width + c.Margin.Right; + break; + case FlowDirection.RightToLeft: + // Decide if it's time to start a new row + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents) + if ((currentLocation.X) < (c.Width + c.Margin.Left + c.Margin.Right) || forceFlowBreak) { + + currentLocation.Y = FinishRow (rowWidgets); + currentLocation.X = parentDisplayRectangle.Right; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the right margin and set the Widget to our point + currentLocation.Offset (c.Margin.Right * -1, 0); + c.SetBoundsInternal (currentLocation.X - c.Width, currentLocation.Y + c.Margin.Top, c.Width, c.Height, BoundsSpecified.None); + + // Update our location pointer + currentLocation.X -= (c.Width + c.Margin.Left); + break; + case FlowDirection.TopDown: + // Decide if it's time to start a new column + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents) + if ((parentDisplayRectangle.Height + parentDisplayRectangle.Top - currentLocation.Y) < (c.Height + c.Margin.Top + c.Margin.Bottom) || forceFlowBreak) { + + currentLocation.X = FinishColumn (rowWidgets); + currentLocation.Y = parentDisplayRectangle.Top; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the top margin and set the Widget to our point + currentLocation.Offset (0, c.Margin.Top); + c.SetBoundsInternal (currentLocation.X + c.Margin.Left, currentLocation.Y, c.Width, c.Height, BoundsSpecified.None); + + // Update our location pointer + currentLocation.Y += c.Height + c.Margin.Bottom; + break; + } + // Add it to our list of things to adjust the second dimension of + rowWidgets.Add (c); + + // If user set a flowbreak on this Widget, it will be the last one in this row/column + if (settings.GetFlowBreak (c)) + forceFlowBreak = true; + } + + // Set the Widget heights/widths for the last row/column + if (settings.FlowDirection == FlowDirection.LeftToRight || settings.FlowDirection == FlowDirection.RightToLeft) + FinishRow (rowWidgets); + else + FinishColumn (rowWidgets); + + return false; + } + + // Calculate the heights of the Widgets, returns the y coordinate of the greatest height it uses + private int FinishRow (List<Widget> row) + { + // Nothing to do + if (row.Count == 0) return 0; + + int rowTop = int.MaxValue; + int rowBottom = 0; + bool allDockFill = true; + bool noAuto = true; + + // Special semantics if all Widgets are Dock.Fill/Anchor:Top,Bottom or AutoSize = true + foreach (Widget c in row) { + if (c.Dock != DockStyle.Fill && !((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top && (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)) + allDockFill = false; + if (c.AutoSize == true) + noAuto = false; + } + + // Find the tallest Widget with a concrete height + foreach (Widget c in row) { + if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock != DockStyle.Fill) && ((c.Anchor & AnchorStyles.Top) != AnchorStyles.Top || (c.Anchor & AnchorStyles.Bottom) != AnchorStyles.Bottom || c.AutoSize == true)) + rowBottom = c.Bottom + c.Margin.Bottom; + if (c.Top - c.Margin.Top < rowTop) + rowTop = c.Top - c.Margin.Top; + } + + // Find the tallest Widget that is AutoSize = true + if (rowBottom == 0) + foreach (Widget c in row) + if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock != DockStyle.Fill && c.AutoSize == true)) + rowBottom = c.Bottom + c.Margin.Bottom; + + // Find the tallest Widget that is Dock = Fill + if (rowBottom == 0) + foreach (Widget c in row) + if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock == DockStyle.Fill)) + rowBottom = c.Bottom + c.Margin.Bottom; + + // Set the new heights for each Widget + foreach (Widget c in row) + if (allDockFill && noAuto) + c.SetBoundsInternal (c.Left, c.Top, c.Width, 0, BoundsSpecified.None); + else if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top) && ((c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)) + c.SetBoundsInternal (c.Left, c.Top, c.Width, rowBottom - c.Top - c.Margin.Bottom, BoundsSpecified.None); + else if (c.Dock == DockStyle.Bottom || ((c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)) + c.SetBoundsInternal (c.Left, rowBottom - c.Margin.Bottom - c.Height, c.Width, c.Height, BoundsSpecified.None); + else if (c.Dock == DockStyle.Top || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top)) + continue; + else + c.SetBoundsInternal (c.Left, ((rowBottom - rowTop) / 2) - (c.Height / 2) + (int)Math.Floor (((c.Margin.Top - c.Margin.Bottom) / 2.0)) + rowTop, c.Width, c.Height, BoundsSpecified.None); + + // Return bottom y of this row used + if (rowBottom == 0) + return rowTop; + + return rowBottom; + } + + // Calculate the widths of the Widgets, returns the x coordinate of the greatest width it uses + private int FinishColumn (List<Widget> col) + { + // Nothing to do + if (col.Count == 0) return 0; + + int rowLeft = int.MaxValue; + int rowRight = 0; + bool allDockFill = true; + bool noAuto = true; + + // Special semantics if all Widgets are Dock.Fill/Anchor:Left,Right or AutoSize = true + foreach (Widget c in col) { + if (c.Dock != DockStyle.Fill && !((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left && (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)) + allDockFill = false; + if (c.AutoSize == true) + noAuto = false; + } + + // Find the widest Widget with a concrete width + foreach (Widget c in col) { + if (c.Right + c.Margin.Right > rowRight && (c.Dock != DockStyle.Fill) && ((c.Anchor & AnchorStyles.Left) != AnchorStyles.Left || (c.Anchor & AnchorStyles.Right) != AnchorStyles.Right || c.AutoSize == true)) + rowRight = c.Right + c.Margin.Right; + if (c.Left - c.Margin.Left < rowLeft) + rowLeft = c.Left - c.Margin.Left; + } + + // Find the widest Widget that is AutoSize = true + if (rowRight == 0) + foreach (Widget c in col) + if (c.Right + c.Margin.Right > rowRight && (c.Dock != DockStyle.Fill && c.AutoSize == true)) + rowRight = c.Right + c.Margin.Right; + + // Find the widest Widget that is Dock = Fill + if (rowRight == 0) + foreach (Widget c in col) + if (c.Right + c.Margin.Right > rowRight && (c.Dock == DockStyle.Fill)) + rowRight = c.Right + c.Margin.Right; + + // Set the new widths for each Widget + foreach (Widget c in col) + if (allDockFill && noAuto) + c.SetBoundsInternal (c.Left, c.Top, 0, c.Height, BoundsSpecified.None); + else if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left) && ((c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)) + c.SetBoundsInternal (c.Left, c.Top, rowRight - c.Left - c.Margin.Right, c.Height, BoundsSpecified.None); + else if (c.Dock == DockStyle.Right || ((c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)) + c.SetBoundsInternal (rowRight - c.Margin.Right - c.Width, c.Top, c.Width, c.Height, BoundsSpecified.None); + else if (c.Dock == DockStyle.Left || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left)) + continue; + else + c.SetBoundsInternal (((rowRight - rowLeft) / 2) - (c.Width / 2) + (int)Math.Floor (((c.Margin.Left - c.Margin.Right) / 2.0)) + rowLeft, c.Top, c.Width, c.Height, BoundsSpecified.None); + + // Return rightmost x of this row used + if (rowRight == 0) + return rowLeft; + + return rowRight; + } + + #region Layout for ToolStrip + // ToolStrips use the same FlowLayout, but is made up of ToolStripItems which + // are Components instead of Widgets, so we have to duplicate this login for + // ToolStripItems. + private bool LayoutToolStrip (ToolStrip parent) + { + FlowLayoutSettings settings; + settings = (FlowLayoutSettings)parent.LayoutSettings; + + // Nothing to layout, exit method + if (parent.Items.Count == 0) return false; + + foreach (ToolStripItem tsi in parent.Items) + tsi.SetPlacement (ToolStripItemPlacement.Main); + + // Use DisplayRectangle so that parent.Padding is honored. + Rectangle parentDisplayRectangle = parent.DisplayRectangle; + Point currentLocation; + + // Set our starting point based on flow direction + switch (settings.FlowDirection) { + case FlowDirection.BottomUp: + currentLocation = new Point (parentDisplayRectangle.Left, parentDisplayRectangle.Bottom); + break; + case FlowDirection.LeftToRight: + case FlowDirection.TopDown: + default: + currentLocation = parentDisplayRectangle.Location; + break; + case FlowDirection.RightToLeft: + currentLocation = new Point (parentDisplayRectangle.Right, parentDisplayRectangle.Top); + break; + } + + bool forceFlowBreak = false; + + List<ToolStripItem> rowWidgets = new List<ToolStripItem> (); + + foreach (ToolStripItem c in parent.Items) { + // Only apply layout to visible Widgets. + if (!c.Available) { continue; } + + // Resize any AutoSize Widgets to their preferred size + if (c.AutoSize == true) + c.SetBounds (new Rectangle (c.Location, c.GetPreferredSize (c.Size))); + + switch (settings.FlowDirection) { + case FlowDirection.BottomUp: + // Decide if it's time to start a new column + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents) + if ((currentLocation.Y) < (c.Height + c.Margin.Top + c.Margin.Bottom) || forceFlowBreak) { + + currentLocation.X = FinishColumn (rowWidgets); + currentLocation.Y = parentDisplayRectangle.Bottom; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the right margin and set the Widget to our point + currentLocation.Offset (0, c.Margin.Bottom * -1); + c.Location = new Point (currentLocation.X + c.Margin.Left, currentLocation.Y - c.Height); + + // Update our location pointer + currentLocation.Y -= (c.Height + c.Margin.Top); + break; + case FlowDirection.LeftToRight: + default: + // Decide if it's time to start a new row + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents) + if ((parentDisplayRectangle.Width - currentLocation.X) < (c.Width + c.Margin.Left + c.Margin.Right) || forceFlowBreak) { + + currentLocation.Y = FinishRow (rowWidgets); + currentLocation.X = parentDisplayRectangle.Left; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the left margin and set the Widget to our point + currentLocation.Offset (c.Margin.Left, 0); + c.Location = new Point (currentLocation.X, currentLocation.Y + c.Margin.Top); + + // Update our location pointer + currentLocation.X += c.Width + c.Margin.Right; + break; + case FlowDirection.RightToLeft: + // Decide if it's time to start a new row + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents) + if ((currentLocation.X) < (c.Width + c.Margin.Left + c.Margin.Right) || forceFlowBreak) { + + currentLocation.Y = FinishRow (rowWidgets); + currentLocation.X = parentDisplayRectangle.Right; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the right margin and set the Widget to our point + currentLocation.Offset (c.Margin.Right * -1, 0); + c.Location = new Point (currentLocation.X - c.Width, currentLocation.Y + c.Margin.Top); + + // Update our location pointer + currentLocation.X -= (c.Width + c.Margin.Left); + break; + case FlowDirection.TopDown: + // Decide if it's time to start a new column + // - Our settings must be WrapContents, and we ran out of room or the previous Widget's FlowBreak == true + if (settings.WrapContents) + if ((parentDisplayRectangle.Height - currentLocation.Y) < (c.Height + c.Margin.Top + c.Margin.Bottom) || forceFlowBreak) { + + currentLocation.X = FinishColumn (rowWidgets); + currentLocation.Y = parentDisplayRectangle.Top; + + forceFlowBreak = false; + rowWidgets.Clear (); + } + + // Offset the top margin and set the Widget to our point + currentLocation.Offset (0, c.Margin.Top); + c.Location = new Point (currentLocation.X + c.Margin.Left, currentLocation.Y); + + // Update our location pointer + currentLocation.Y += c.Height + c.Margin.Bottom; + break; + } + // Add it to our list of things to adjust the second dimension of + rowWidgets.Add (c); + + // If user set a flowbreak on this Widget, it will be the last one in this row/column + if (settings.GetFlowBreak (c)) + forceFlowBreak = true; + } + + int final_height = 0; + + // Set the Widget heights/widths for the last row/column + if (settings.FlowDirection == FlowDirection.LeftToRight || settings.FlowDirection == FlowDirection.RightToLeft) + final_height = FinishRow (rowWidgets); + else + FinishColumn (rowWidgets); + + if (final_height > 0) + parent.SetBoundsInternal (parent.Left, parent.Top, parent.Width, final_height + parent.Padding.Bottom, BoundsSpecified.None); + + return false; + + } + + // Calculate the heights of the Widgets, returns the y coordinate of the greatest height it uses + private int FinishRow (List<ToolStripItem> row) + { + // Nothing to do + if (row.Count == 0) return 0; + + int rowTop = int.MaxValue; + int rowBottom = 0; + bool allDockFill = true; + bool noAuto = true; + + // Special semantics if all Widgets are Dock.Fill/Anchor:Top,Bottom or AutoSize = true + foreach (ToolStripItem c in row) { + if (c.Dock != DockStyle.Fill && !((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top && (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)) + allDockFill = false; + if (c.AutoSize == true) + noAuto = false; + } + + // Find the tallest Widget with a concrete height + foreach (ToolStripItem c in row) { + if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock != DockStyle.Fill) && ((c.Anchor & AnchorStyles.Top) != AnchorStyles.Top || (c.Anchor & AnchorStyles.Bottom) != AnchorStyles.Bottom || c.AutoSize == true)) + rowBottom = c.Bottom + c.Margin.Bottom; + if (c.Top - c.Margin.Top < rowTop) + rowTop = c.Top - c.Margin.Top; + } + + // Find the tallest Widget that is AutoSize = true + if (rowBottom == 0) + foreach (ToolStripItem c in row) + if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock != DockStyle.Fill && c.AutoSize == true)) + rowBottom = c.Bottom + c.Margin.Bottom; + + // Find the tallest Widget that is Dock = Fill + if (rowBottom == 0) + foreach (ToolStripItem c in row) + if (c.Bottom + c.Margin.Bottom > rowBottom && (c.Dock == DockStyle.Fill)) + rowBottom = c.Bottom + c.Margin.Bottom; + + // Set the new heights for each Widget + foreach (ToolStripItem c in row) + if (allDockFill && noAuto) + c.Height = 0; + else if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top) && ((c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)) + c.Height = rowBottom - c.Top - c.Margin.Bottom; + else if (c.Dock == DockStyle.Bottom || ((c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)) + c.Top = rowBottom - c.Margin.Bottom - c.Height; + else if (c.Dock == DockStyle.Top || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top)) + continue; + else + c.Top = ((rowBottom - rowTop) / 2) - (c.Height / 2) + (int)Math.Floor (((c.Margin.Top - c.Margin.Bottom) / 2.0)) + rowTop; + + // Return bottom y of this row used + if (rowBottom == 0) + return rowTop; + + return rowBottom; + } + + // Calculate the widths of the Widgets, returns the x coordinate of the greatest width it uses + private int FinishColumn (List<ToolStripItem> col) + { + // Nothing to do + if (col.Count == 0) return 0; + + int rowLeft = int.MaxValue; + int rowRight = 0; + bool allDockFill = true; + bool noAuto = true; + + // Special semantics if all Widgets are Dock.Fill/Anchor:Left,Right or AutoSize = true + foreach (ToolStripItem c in col) { + if (c.Dock != DockStyle.Fill && !((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left && (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)) + allDockFill = false; + if (c.AutoSize == true) + noAuto = false; + } + + // Find the widest Widget with a concrete width + foreach (ToolStripItem c in col) { + if (c.Right + c.Margin.Right > rowRight && (c.Dock != DockStyle.Fill) && ((c.Anchor & AnchorStyles.Left) != AnchorStyles.Left || (c.Anchor & AnchorStyles.Right) != AnchorStyles.Right || c.AutoSize == true)) + rowRight = c.Right + c.Margin.Right; + if (c.Left - c.Margin.Left < rowLeft) + rowLeft = c.Left - c.Margin.Left; + } + + // Find the widest Widget that is AutoSize = true + if (rowRight == 0) + foreach (ToolStripItem c in col) + if (c.Right + c.Margin.Right > rowRight && (c.Dock != DockStyle.Fill && c.AutoSize == true)) + rowRight = c.Right + c.Margin.Right; + + // Find the widest Widget that is Dock = Fill + if (rowRight == 0) + foreach (ToolStripItem c in col) + if (c.Right + c.Margin.Right > rowRight && (c.Dock == DockStyle.Fill)) + rowRight = c.Right + c.Margin.Right; + + // Set the new widths for each Widget + foreach (ToolStripItem c in col) + if (allDockFill && noAuto) + c.Width = 0; + else if (c.Dock == DockStyle.Fill || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left) && ((c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)) + c.Width = rowRight - c.Left - c.Margin.Right; + else if (c.Dock == DockStyle.Right || ((c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)) + c.Left = rowRight - c.Margin.Right - c.Width; + else if (c.Dock == DockStyle.Left || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left)) + continue; + else + c.Left = ((rowRight - rowLeft) / 2) - (c.Width / 2) + (int)Math.Floor (((c.Margin.Left - c.Margin.Right) / 2.0)) + rowLeft; + + // Return rightmost x of this row used + if (rowRight == 0) + return rowLeft; + + return rowRight; + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/FlowLayoutSettings.cs b/source/ShiftUI/Internal/FlowLayoutSettings.cs new file mode 100644 index 0000000..3480165 --- /dev/null +++ b/source/ShiftUI/Internal/FlowLayoutSettings.cs @@ -0,0 +1,111 @@ +// +// FlowLayoutSettings.cs +// +// 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) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using ShiftUI.Layout; +using System.ComponentModel; +using System.Collections.Generic; +using System; + +namespace ShiftUI +{ + [DefaultProperty ("FlowDirection")] + public class FlowLayoutSettings : LayoutSettings + { + private FlowDirection flow_direction; + private bool wrap_contents; + private LayoutEngine layout_engine; + private Dictionary<object, bool> flow_breaks; + private Widget owner; + + internal FlowLayoutSettings () : this (null) + { + } + + internal FlowLayoutSettings (Widget owner) + { + flow_breaks = new Dictionary<object, bool> (); + wrap_contents = true; + flow_direction = FlowDirection.LeftToRight; + this.owner = owner; + } + + #region Public Properties + [DefaultValue (FlowDirection.LeftToRight)] + public FlowDirection FlowDirection { + get { return this.flow_direction; } + set { + if (this.flow_direction != value) { + this.flow_direction = value; + if (owner != null) + owner.PerformLayout (owner, "FlowDirection"); + } + } + } + + public override LayoutEngine LayoutEngine { + get { + if (this.layout_engine == null) + this.layout_engine = new FlowLayout (); + + return this.layout_engine; + } + } + + [DefaultValue (true)] + public bool WrapContents { + get { return this.wrap_contents; } + set { + if (this.wrap_contents != value) { + this.wrap_contents = value; + if (owner != null) + owner.PerformLayout (owner, "WrapContents"); + } + } + } + #endregion + + #region Public Methods + public bool GetFlowBreak (Object child) + { + bool retval; + + if (flow_breaks.TryGetValue (child, out retval)) + return retval; + + return false; + } + + public void SetFlowBreak (Object child, bool value) + { + flow_breaks[child] = value; + if (owner != null) + owner.PerformLayout ((Widget)child, "FlowBreak"); + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/GetChildAtPointSkip.cs b/source/ShiftUI/Internal/GetChildAtPointSkip.cs new file mode 100644 index 0000000..963406c --- /dev/null +++ b/source/ShiftUI/Internal/GetChildAtPointSkip.cs @@ -0,0 +1,41 @@ +// +// GetChildAtPointSkip.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// +using System; + + +namespace ShiftUI +{ + [Flags] + public enum GetChildAtPointSkip + { + None = 0, + Invisible = 1, + Disabled = 2, + Transparent = 4 + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/GridItem.cs b/source/ShiftUI/Internal/GridItem.cs new file mode 100644 index 0000000..3efac21 --- /dev/null +++ b/source/ShiftUI/Internal/GridItem.cs @@ -0,0 +1,114 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// NOT COMPLETE + +using System; +using System.Drawing; +using System.ComponentModel; + +namespace ShiftUI +{ + public abstract class GridItem + { + #region Fields + private bool expanded; + private object tag; + #endregion Fields + + #region Constructors + protected GridItem() + { + expanded = false; + } + #endregion // Constructors + + #region Public Instance Properties + public virtual bool Expandable + { + get { + return GridItems.Count > 1; + } + } + + public virtual bool Expanded + { + get { + return expanded; + } + + set { + expanded = value; + } + } + + public abstract GridItemCollection GridItems + { + get; + } + + public abstract GridItemType GridItemType + { + get; + } + + public abstract string Label + { + get; + } + + + public abstract GridItem Parent + { + get; + } + + + public abstract PropertyDescriptor PropertyDescriptor + { + get; + } + + [Localizable (false)] + [Bindable (true)] + [DefaultValue (null)] + [TypeConverter (typeof (StringConverter))] + public Object Tag { + get { return this.tag; } + set { this.tag = value; } + } + + public abstract object Value + { + get; + set; + } + #endregion + + #region Public Instance Methods + public abstract bool Select (); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/GridItemType.cs b/source/ShiftUI/Internal/GridItemType.cs new file mode 100644 index 0000000..821626a --- /dev/null +++ b/source/ShiftUI/Internal/GridItemType.cs @@ -0,0 +1,38 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +namespace ShiftUI +{ + public enum GridItemType + { + Property = 0, + Category = 1, + ArrayValue = 2, + Root = 3 + } +} + diff --git a/source/ShiftUI/Internal/GroupBoxRenderer.cs b/source/ShiftUI/Internal/GroupBoxRenderer.cs new file mode 100644 index 0000000..c0c9699 --- /dev/null +++ b/source/ShiftUI/Internal/GroupBoxRenderer.cs @@ -0,0 +1,157 @@ +// +// GroupBoxRenderer.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System.Drawing; +using ShiftUI.VisualStyles; +using System; + +namespace ShiftUI +{ + public sealed class GroupBoxRenderer + { + private static bool always_use_visual_styles = false; + + #region Private Constructor + private GroupBoxRenderer () { } + #endregion + + #region Public Static Methods + public static void DrawGroupBox (Graphics g, Rectangle bounds, GroupBoxState state) + { + DrawGroupBox (g, bounds, String.Empty, null, Color.Empty, TextFormatFlags.Default, state); + } + + public static void DrawGroupBox (Graphics g, Rectangle bounds, string groupBoxText, Font font, GroupBoxState state) + { + DrawGroupBox (g, bounds, groupBoxText, font, Color.Empty, TextFormatFlags.Default, state); + } + + public static void DrawGroupBox (Graphics g, Rectangle bounds, string groupBoxText, Font font, Color textColor, GroupBoxState state) + { + DrawGroupBox (g, bounds, groupBoxText, font, textColor, TextFormatFlags.Default, state); + } + + public static void DrawGroupBox (Graphics g, Rectangle bounds, string groupBoxText, Font font, TextFormatFlags flags, GroupBoxState state) + { + DrawGroupBox (g, bounds, groupBoxText, font, Color.Empty, flags, state); + } + + public static void DrawGroupBox (Graphics g, Rectangle bounds, string groupBoxText, Font font, Color textColor, TextFormatFlags flags, GroupBoxState state) + { + Size font_size = TextRenderer.MeasureText (groupBoxText, font); + + if (Application.RenderWithVisualStyles || always_use_visual_styles == true) { + VisualStyleRenderer vsr; + Rectangle new_bounds; + + switch (state) { + case GroupBoxState.Normal: + default: + vsr = new VisualStyleRenderer (VisualStyleElement.Button.GroupBox.Normal); + new_bounds = new Rectangle (bounds.Left, bounds.Top + (int)(font_size.Height / 2) - 1, bounds.Width, bounds.Height - (int)(font_size.Height / 2) + 1); + break; + case GroupBoxState.Disabled: + vsr = new VisualStyleRenderer (VisualStyleElement.Button.GroupBox.Disabled); + new_bounds = new Rectangle (bounds.Left, bounds.Top + (int)(font_size.Height / 2) - 2, bounds.Width, bounds.Height - (int)(font_size.Height / 2) + 2); + break; + } + + if (groupBoxText == String.Empty) + vsr.DrawBackground (g, bounds); + else + vsr.DrawBackgroundExcludingArea (g, new_bounds, new Rectangle (bounds.Left + 9, bounds.Top, font_size.Width - 3, font_size.Height)); + + if (textColor == Color.Empty) + textColor = vsr.GetColor (ColorProperty.TextColor); + + if (groupBoxText != String.Empty) + TextRenderer.DrawText (g, groupBoxText, font, new Point (bounds.Left + 8, bounds.Top), textColor, flags); + } + else { + // MS has a pretty big bug when rendering the non-visual styles group box. Instead of using the height + // part of the bounds as height, they use it as the bottom, so the boxes are drawn in completely different + // places. Rather than emulate this bug, we do it correctly. After googling for a while, I don't think + // anyone has ever actually used this class for anything, so it should be fine. :) + Rectangle new_bounds = new Rectangle (bounds.Left, bounds.Top + (int)(font_size.Height / 2), bounds.Width, bounds.Height - (int)(font_size.Height / 2)); + + // Don't paint over the background where we are going to put the text + Region old_clip = g.Clip; + g.SetClip (new Rectangle (bounds.Left + 9, bounds.Top, font_size.Width - 3, font_size.Height), System.Drawing.Drawing2D.CombineMode.Exclude); + + WidgetPaint.DrawBorder3D (g, new_bounds, Border3DStyle.Etched); + + g.Clip = old_clip; + + if (groupBoxText != String.Empty) { + if (textColor == Color.Empty) + textColor = state == GroupBoxState.Normal ? SystemColors.ControlText : + SystemColors.GrayText; + TextRenderer.DrawText (g, groupBoxText, font, new Point (bounds.Left + 8, bounds.Top), textColor, flags); + } + } + } + + public static bool IsBackgroundPartiallyTransparent (GroupBoxState state) + { + if (!VisualStyleRenderer.IsSupported) + return false; + + VisualStyleRenderer vsr; + + switch (state) { + case GroupBoxState.Normal: + default: + vsr = new VisualStyleRenderer (VisualStyleElement.Button.GroupBox.Normal); + break; + case GroupBoxState.Disabled: + vsr = new VisualStyleRenderer (VisualStyleElement.Button.GroupBox.Disabled); + break; + } + + return vsr.IsBackgroundPartiallyTransparent (); + } + + public static void DrawParentBackground (Graphics g, Rectangle bounds, Widget childControl) + { + if (!VisualStyleRenderer.IsSupported) + return; + + VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.Button.GroupBox.Normal); + + vsr.DrawParentBackground (g, bounds, childControl); + } + #endregion + + #region Public Static Properties + public static bool RenderMatchingApplicationState { + get { return !always_use_visual_styles; } + set { always_use_visual_styles = !value; } + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/HIObjectHandler.cs b/source/ShiftUI/Internal/HIObjectHandler.cs new file mode 100644 index 0000000..ca7adc0 --- /dev/null +++ b/source/ShiftUI/Internal/HIObjectHandler.cs @@ -0,0 +1,57 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; +using System.Runtime.InteropServices; + +namespace ShiftUI.CarbonInternal { + internal class HIObjectHandler : EventHandlerBase, IEventHandler { + internal const uint kEventHIObjectConstruct = 1; + internal const uint kEventHIObjectInitialize = 2; + internal const uint kEventHIObjectDestruct = 3; + + internal HIObjectHandler (XplatUICarbon driver) : base (driver) {} + + public bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) { + switch (kind) { + case kEventHIObjectConstruct: + IntPtr v = IntPtr.Zero; + GetEventParameter (eventref, (uint)1751740265, (uint)1751740258, IntPtr.Zero, 4, IntPtr.Zero, ref v); + return false; + case kEventHIObjectInitialize: + CallNextEventHandler (callref, eventref); + return false; + case kEventHIObjectDestruct: + return false; + } + return false; + } + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int CallNextEventHandler (IntPtr callref, IntPtr eventref); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref IntPtr data); + } +} diff --git a/source/ShiftUI/Internal/HScrollProperties.cs b/source/ShiftUI/Internal/HScrollProperties.cs new file mode 100644 index 0000000..4eed2da --- /dev/null +++ b/source/ShiftUI/Internal/HScrollProperties.cs @@ -0,0 +1,33 @@ +// 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. +// +// Authors: +// Olivier Dufour [email protected] +// + +namespace ShiftUI +{ + public class HScrollProperties : ScrollProperties + { + public HScrollProperties (ScrollableWidget container) : base (container) + { + scroll_bar = container.hscrollbar; + } + } +} diff --git a/source/ShiftUI/Internal/Hwnd.cs b/source/ShiftUI/Internal/Hwnd.cs new file mode 100644 index 0000000..fcdc17d --- /dev/null +++ b/source/ShiftUI/Internal/Hwnd.cs @@ -0,0 +1,910 @@ +// 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) 2005-2006 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// NOT COMPLETE + +using System; +using System.Collections; +using System.Drawing; +using System.Runtime.InteropServices; + +// NOTE: Possible optimization: +// Several properties calculate dimensions on the fly; instead; they can +// be stored in a field and only be recalculated when a style is changed (DefaultClientRect, for example) + +namespace ShiftUI { + internal class Hwnd : IDisposable { + #region Local Variables + private static Hashtable windows = new Hashtable(100, 0.5f); + //private const int menu_height = 14; // FIXME - Read this value from somewhere + + private IntPtr handle; + internal IntPtr client_window; + internal IntPtr whole_window; + internal IntPtr cursor; + internal Menu menu; + internal TitleStyle title_style; + internal FormBorderStyle border_style; + internal bool border_static; + internal int x; + internal int y; + internal int width; + internal int height; + internal bool allow_drop; + internal Hwnd parent; + internal bool visible; + internal bool mapped; + internal uint opacity; + internal bool enabled; + internal bool zero_sized; + internal ArrayList invalid_list; + internal Rectangle nc_invalid; + internal bool expose_pending; + internal bool nc_expose_pending; + internal bool configure_pending; + internal bool resizing_or_moving; // Used by the X11 backend to track form resize/move + internal bool reparented; + internal Stack drawing_stack; + internal object user_data; + internal Rectangle client_rectangle; + internal ArrayList marshal_free_list; + internal int caption_height; + internal int tool_caption_height; + internal bool whacky_wm; + internal bool fixed_size; + internal bool zombie; /* X11 only flag. true if the X windows have been destroyed but we haven't been Disposed */ + internal bool topmost; /* X11 only. */ + internal Region user_clip; + internal XEventQueue queue; + internal WindowExStyles initial_ex_style; + internal WindowStyles initial_style; + internal FormWindowState cached_window_state = (FormWindowState)(-1); /* X11 only field */ + internal Point previous_child_startup_location = new Point (int.MinValue, int.MinValue); + static internal Point previous_main_startup_location = new Point (int.MinValue, int.MinValue); + internal ArrayList children; + + [ThreadStatic] + private static Bitmap bmp; + [ThreadStatic] + private static Graphics bmp_g; + #endregion // Local Variables + + // locks for some operations (used in XplatUIX11.cs) + internal object configure_lock = new object (); + internal object expose_lock = new object (); + + #region Constructors and destructors + public Hwnd() { + x = 0; + y = 0; + width = 0; + height = 0; + visible = false; + menu = null; + border_style = FormBorderStyle.None; + client_window = IntPtr.Zero; + whole_window = IntPtr.Zero; + cursor = IntPtr.Zero; + handle = IntPtr.Zero; + parent = null; + invalid_list = new ArrayList(); + expose_pending = false; + nc_expose_pending = false; + enabled = true; + reparented = false; + client_rectangle = Rectangle.Empty; + marshal_free_list = new ArrayList(2); + opacity = 0xffffffff; + fixed_size = false; + drawing_stack = new Stack (); + children = new ArrayList (); + resizing_or_moving = false; + whacky_wm = false; + topmost = false; + } + + public void Dispose() { + expose_pending = false; + nc_expose_pending = false; + Parent = null; + lock (windows) { + windows.Remove(client_window); + windows.Remove(whole_window); + } + client_window = IntPtr.Zero; + whole_window = IntPtr.Zero; + zombie = false; + for (int i = 0; i < marshal_free_list.Count; i++) { + Marshal.FreeHGlobal((IntPtr)marshal_free_list[i]); + } + marshal_free_list.Clear(); + } + #endregion + + #region Static Methods + public static Hwnd ObjectFromWindow(IntPtr window) { + Hwnd rv; + lock (windows) { + rv = (Hwnd)windows[window]; + } + return rv; + } + + public static Hwnd ObjectFromHandle(IntPtr handle) { + //return (Hwnd)(((GCHandle)handle).Target); + Hwnd rv; + lock (windows) { + rv = (Hwnd)windows[handle]; + } + return rv; + } + + public static IntPtr HandleFromObject(Hwnd obj) { + return obj.handle; + } + + public static Hwnd GetObjectFromWindow(IntPtr window) { + Hwnd rv; + lock (windows) { + rv = (Hwnd)windows[window]; + } + return rv; + } + + public static IntPtr GetHandleFromWindow(IntPtr window) { + Hwnd hwnd; + + lock (windows) { + hwnd = (Hwnd)windows[window]; + } + if (hwnd != null) { + return hwnd.handle; + } else { + return IntPtr.Zero; + } + } + + public static Borders GetBorderWidth (CreateParams cp) + { + Borders border_size = new Borders (); + + Size windowborder = ThemeEngine.Current.BorderSize; /*new Size (1, 1);*/ // This is the only one that can be changed from the display properties in windows. + Size border = ThemeEngine.Current.BorderStaticSize; /*new Size (1, 1);*/ + Size clientedge = ThemeEngine.Current.Border3DSize; /*new Size (2, 2);*/ + Size thickframe = new Size (2 + windowborder.Width, 2 + windowborder.Height); + Size dialogframe = ThemeEngine.Current.BorderSizableSize; /* new Size (3, 3);*/ + + if (cp.IsSet (WindowStyles.WS_CAPTION)) { + border_size.Inflate (dialogframe); + } else if (cp.IsSet (WindowStyles.WS_BORDER)) { + if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME)) { + if (cp.IsSet (WindowStyles.WS_THICKFRAME) && (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE) || cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE))) { + border_size.Inflate (border); + } + } else { + border_size.Inflate (border); + } + } else if (cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (dialogframe); + } + + if (cp.IsSet (WindowStyles.WS_THICKFRAME)) { + if (cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (border); + } else { + border_size.Inflate (thickframe); + } + } + + bool only_small_border; + Size small_border = Size.Empty; + + only_small_border = cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME); + if (only_small_border && cp.IsSet (WindowStyles.WS_THICKFRAME) && !cp.IsSet (WindowStyles.WS_BORDER) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) { + small_border = border; + } + + if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) { + border_size.Inflate (clientedge + (only_small_border ? small_border : dialogframe)); + } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_DLGMODALFRAME)) { + border_size.Inflate (only_small_border ? small_border : dialogframe); + } else if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE | WindowExStyles.WS_EX_CLIENTEDGE)) { + border_size.Inflate (border + (only_small_border ? Size.Empty : clientedge)); + } else { + if (cp.IsSet (WindowExStyles.WS_EX_CLIENTEDGE)) { + border_size.Inflate (clientedge); + } + if (cp.IsSet (WindowExStyles.WS_EX_DLGMODALFRAME) && !cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (cp.IsSet (WindowStyles.WS_THICKFRAME) ? border : dialogframe); + } + if (cp.IsSet (WindowExStyles.WS_EX_STATICEDGE)) { + if (cp.IsSet (WindowStyles.WS_THICKFRAME) || cp.IsSet (WindowStyles.WS_DLGFRAME)) { + border_size.Inflate (new Size (-border.Width, -border.Height)); + } else { + border_size.Inflate (border); + } + } + } + + return border_size; + } + + public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu) + { + return GetWindowRectangle (cp, menu, Rectangle.Empty); + } + + public static Rectangle GetWindowRectangle (CreateParams cp, Menu menu, Rectangle client_rect) + { + Rectangle rect; + Borders borders; + + borders = GetBorders (cp, menu); + + rect = new Rectangle (Point.Empty, client_rect.Size); + rect.Y -= borders.top; + rect.Height += borders.top + borders.bottom; + rect.X -= borders.left; + rect.Width += borders.left + borders.right; + + #if debug + Console.WriteLine ("GetWindowRectangle ({0}, {1}, {2}): {3}", cp, menu != null, client_rect, rect); + #endif + return rect; + } + + public Rectangle GetClientRectangle (int width, int height) + { + CreateParams cp = new CreateParams (); + cp.WindowStyle = initial_style; + cp.WindowExStyle = initial_ex_style; + return GetClientRectangle (cp, menu, width, height); + } + + // This could be greatly optimized by caching the outputs and only updating when something is moved + // in the parent planar space. To do that we need to track z-order in the parent space as well + public ArrayList GetClippingRectangles () + { + ArrayList masks = new ArrayList (); + + if (x < 0) { + masks.Add (new Rectangle (0, 0, x*-1, Height)); + if (y < 0) { + masks.Add (new Rectangle (x*-1, 0, Width, y*-1)); + } + } else if (y < 0) { + masks.Add (new Rectangle (0, 0, Width, y*-1)); + } + + foreach (Hwnd child in children) { + if (child.visible) + masks.Add (new Rectangle (child.X, child.Y, child.Width, child.Height)); + } + + if (parent == null) { + return masks; + } + + ArrayList siblings = parent.children; + + foreach (Hwnd sibling in siblings) { + IntPtr sibling_handle = whole_window; + + if (sibling == this) + continue; + + // This entire method should be cached to find all higher views at the time of query + do { + sibling_handle = XplatUI.GetPreviousWindow (sibling_handle); + + if (sibling_handle == sibling.WholeWindow && sibling.visible) { + + Rectangle intersect = Rectangle.Intersect (new Rectangle (X, Y, Width, Height), new Rectangle (sibling.X, sibling.Y, sibling.Width, sibling.Height)); + + if (intersect == Rectangle.Empty) + continue; + + intersect.X -= X; + intersect.Y -= Y; + + masks.Add (intersect); + } + } while (sibling_handle != IntPtr.Zero); + } + + return masks; + } + + public static Borders GetBorders (CreateParams cp, Menu menu) + { + + Borders borders = new Borders (); + + if (menu != null) { + int menu_height = menu.Rect.Height; + if (menu_height == 0) + menu_height = ThemeEngine.Current.CalcMenuBarSize (GraphicsContext, menu, cp.Width); + borders.top += menu_height; + } + + if (cp.IsSet (WindowStyles.WS_CAPTION)) { + int caption_height; + if (cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) { + caption_height = ThemeEngine.Current.ToolWindowCaptionHeight; + } else { + caption_height = ThemeEngine.Current.CaptionHeight; + } + borders.top += caption_height; + } + + Borders border_width = GetBorderWidth (cp); + + borders.left += border_width.left; + borders.right += border_width.right; + borders.top += border_width.top; + borders.bottom += border_width.bottom; + + return borders; + } + + public static Rectangle GetClientRectangle(CreateParams cp, Menu menu, int width, int height) { + Rectangle rect; + Borders borders; + + borders = GetBorders (cp, menu); + + rect = new Rectangle(0, 0, width, height); + rect.Y += borders.top; + rect.Height -= borders.top + borders.bottom; + rect.X += borders.left; + rect.Width -= borders.left + borders.right; + + #if debug + Console.WriteLine ("GetClientRectangle ({0}, {1}, {2}, {3}): {4}", cp, menu != null, width, height, rect); + #endif + + return rect; + } + + public static Graphics GraphicsContext { + get { + if (bmp_g == null) { + bmp = new Bitmap (1, 1, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + bmp_g = Graphics.FromImage (bmp); + } + + return bmp_g; + } + } + #endregion // Static Methods + + #region Instance Properties + public FormBorderStyle BorderStyle { + get { + return border_style; + } + + set { + border_style = value; + } + } + + public Rectangle ClientRect { + get { + if (client_rectangle == Rectangle.Empty) { + return DefaultClientRect; + } + return client_rectangle; + } + + set { + client_rectangle = value; + } + } + + public IntPtr Cursor { + get { + return cursor; + } + + set { + cursor = value; + } + } + + public IntPtr ClientWindow { + get { + return client_window; + } + + set { + client_window = value; + handle = value; + + zombie = false; + + if (client_window != IntPtr.Zero) { + lock (windows) { + if (windows[client_window] == null) { + windows[client_window] = this; + } + } + } + } + } + + public Region UserClip { + get { + return user_clip; + } + + set { + user_clip = value; + } + } + + public Rectangle DefaultClientRect { + get { + // We pass a Zero for the menu handle so the menu size is + // not computed this is done via an WM_NCCALC + CreateParams cp = new CreateParams (); + Rectangle rect; + + cp.WindowStyle = initial_style; + cp.WindowExStyle = initial_ex_style; + + rect = GetClientRectangle (cp, null, width, height); + + return rect; + } + } + + public bool ExposePending { + get { + return expose_pending; + } + } + + public IntPtr Handle { + get { + if (handle == IntPtr.Zero) { + throw new ArgumentNullException("Handle", "Handle is not yet assigned, need a ClientWindow"); + } + return handle; + } + } + + public int Height { + get { + return height; + } + + set { + height = value; + } + } + + public Menu Menu { + get { + return menu; + } + + set { + menu = value; + } + } + + public bool Reparented { + get { + return reparented; + } + + set { + reparented = value; + } + } + + public uint Opacity { + get { + return opacity; + } + + set { + opacity = value; + } + } + + public XEventQueue Queue { + get { + return queue; + } + + set { + queue = value; + } + } + + public bool Enabled { + get { + if (!enabled) { + return false; + } + + if (parent != null) { + return parent.Enabled; + } + + return true; + } + + set { + enabled = value; + } + } + + public IntPtr EnabledHwnd { + get { + if (Enabled || parent == null) { + return Handle; + } + + return parent.EnabledHwnd; + } + } + + public Point MenuOrigin { + get { + Form frm = Widget.FromHandle (handle) as Form; + if (frm != null && frm.window_manager != null) + return frm.window_manager.GetMenuOrigin (); + + Point pt; + Size border_3D_size = ThemeEngine.Current.Border3DSize; + + pt = new Point(0, 0); + + if (border_style == FormBorderStyle.Fixed3D) { + pt.X += border_3D_size.Width; + pt.Y += border_3D_size.Height; + } else if (border_style == FormBorderStyle.FixedSingle) { + pt.X += 1; + pt.Y += 1; + } + + if (this.title_style == TitleStyle.Normal) { + pt.Y += caption_height; + } else if (this.title_style == TitleStyle.Normal) { + pt.Y += tool_caption_height; + } + + return pt; + } + } + + public Rectangle Invalid { + get { + if (invalid_list.Count == 0) + return Rectangle.Empty; + + Rectangle result = (Rectangle)invalid_list[0]; + for (int i = 1; i < invalid_list.Count; i ++) { + result = Rectangle.Union (result, (Rectangle)invalid_list[i]); + } + return result; + } + } + + public Rectangle[] ClipRectangles { + get { + return (Rectangle[]) invalid_list.ToArray (typeof (Rectangle)); + } + } + + public Rectangle NCInvalid { + get { return nc_invalid; } + set { nc_invalid = value; } + + } + + public bool NCExposePending { + get { + return nc_expose_pending; + } + } + + public Hwnd Parent { + get { + return parent; + } + + set { + if (parent != null) + parent.children.Remove (this); + parent = value; + if (parent != null) + parent.children.Add (this); + } + } + + public bool Mapped { + get { + if (!mapped) { + return false; + } + + if (parent != null) { + return parent.Mapped; + } + + return true; + } + + set { + mapped = value; + } + } + + public int CaptionHeight { + get { return caption_height; } + set { caption_height = value; } + } + + public int ToolCaptionHeight { + get { return tool_caption_height; } + set { tool_caption_height = value; } + } + + public TitleStyle TitleStyle { + get { + return title_style; + } + + set { + title_style = value; + } + } + + public object UserData { + get { + return user_data; + } + + set { + user_data = value; + } + } + + public IntPtr WholeWindow { + get { + return whole_window; + } + + set { + whole_window = value; + + zombie = false; + + if (whole_window != IntPtr.Zero) { + lock (windows) { + if (windows[whole_window] == null) { + windows[whole_window] = this; + } + } + } + } + } + + public int Width { + get { + return width; + } + + set { + width = value; + } + } + + public bool Visible { + get { + return visible; + } + + set { + visible = value; + } + } + + public int X { + get { + return x; + } + + set { + x = value; + } + } + + public int Y { + get { + return y; + } + + set { + y = value; + } + } + + #endregion // Instance properties + + #region Methods + public void AddInvalidArea(int x, int y, int width, int height) { + AddInvalidArea(new Rectangle(x, y, width, height)); + } + + public void AddInvalidArea(Rectangle rect) { + ArrayList tmp = new ArrayList (); + foreach (Rectangle r in invalid_list) { + if (!rect.Contains (r)) { + tmp.Add (r); + } + } + tmp.Add (rect); + invalid_list = tmp; + } + + public void ClearInvalidArea() { + invalid_list.Clear(); + expose_pending = false; + } + + public void AddNcInvalidArea(int x, int y, int width, int height) { + if (nc_invalid == Rectangle.Empty) { + nc_invalid = new Rectangle (x, y, width, height); + return; + } + + int right, bottom; + right = Math.Max (nc_invalid.Right, x + width); + bottom = Math.Max (nc_invalid.Bottom, y + height); + nc_invalid.X = Math.Min (nc_invalid.X, x); + nc_invalid.Y = Math.Min (nc_invalid.Y, y); + + nc_invalid.Width = right - nc_invalid.X; + nc_invalid.Height = bottom - nc_invalid.Y; + } + + public void AddNcInvalidArea(Rectangle rect) { + if (nc_invalid == Rectangle.Empty) { + nc_invalid = rect; + return; + } + nc_invalid = Rectangle.Union (nc_invalid, rect); + } + + public void ClearNcInvalidArea() { + nc_invalid = Rectangle.Empty; + nc_expose_pending = false; + } + + public override string ToString() { + return String.Format("Hwnd, Mapped:{3} ClientWindow:0x{0:X}, WholeWindow:0x{1:X}, Zombie={4}, Parent:[{2:X}]", client_window.ToInt32(), whole_window.ToInt32(), parent != null ? parent.ToString() : "<null>", Mapped, zombie); + } + + public static Point GetNextStackedFormLocation (CreateParams cp, Hwnd parent_hwnd) + { + if (cp.control == null) + return Point.Empty; + + int X = cp.X; + int Y = cp.Y; + Point previous, next; + Rectangle within; + + if (parent_hwnd != null) { + Widget parent = cp.control.Parent; + previous = parent_hwnd.previous_child_startup_location; + if (parent_hwnd.client_rectangle == Rectangle.Empty && parent != null) { + within = parent.ClientRectangle; + } else { + within = parent_hwnd.client_rectangle; + } + } else { + previous = Hwnd.previous_main_startup_location; + within = ShiftUI.Screen.PrimaryScreen.WorkingArea; + } + + if (previous.X == int.MinValue || previous.Y == int.MinValue) { + next = Point.Empty; + } else { + next = new Point (previous.X + 22, previous.Y + 22); + } + + if (!within.Contains (next.X * 3, next.Y * 3)) { + next = Point.Empty; + } + + if (next == Point.Empty && cp.Parent == IntPtr.Zero) { + next = new Point (22, 22); + } + + if (parent_hwnd != null) { + parent_hwnd.previous_child_startup_location = next; + } else { + Hwnd.previous_main_startup_location = next; + } + + if (X == int.MinValue && Y == int.MinValue) { + X = next.X; + Y = next.Y; + } + + return new Point (X, Y); + } + + #endregion // Methods + + internal struct Borders + { + public int top; + public int bottom; + public int left; + public int right; + + public void Inflate (Size size) + { + left += size.Width; + right += size.Width; + top += size.Height; + bottom += size.Height; + } + + public override string ToString () + { + return string.Format("{{top={0}, bottom={1}, left={2}, right={3}}}", top, bottom, left, right); + } + + public static bool operator == (Borders a, Borders b) + { + return (a.left == b.left && a.right == b.right && a.top == b.top && a.bottom == b.bottom); + } + + public static bool operator != (Borders a, Borders b) + { + return !(a == b); + } + + public override bool Equals (object obj) + { + return base.Equals (obj); + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + } + } +} diff --git a/source/ShiftUI/Internal/IBindableComponent.cs b/source/ShiftUI/Internal/IBindableComponent.cs new file mode 100644 index 0000000..8c7edaa --- /dev/null +++ b/source/ShiftUI/Internal/IBindableComponent.cs @@ -0,0 +1,34 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// + + +using System; +using System.ComponentModel; + +namespace ShiftUI { + + public interface IBindableComponent : IComponent, IDisposable + { + BindingContext BindingContext { get; set; } + WidgetBindingsCollection DataBindings { get; } + } +} diff --git a/source/ShiftUI/Internal/IBounds.cs b/source/ShiftUI/Internal/IBounds.cs new file mode 100644 index 0000000..923a029 --- /dev/null +++ b/source/ShiftUI/Internal/IBounds.cs @@ -0,0 +1,40 @@ +// +// IBounds.cs +// +// 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) 2008 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace ShiftUI +{ + interface IBounds + { + Rectangle Bounds { get; } + } +} diff --git a/source/ShiftUI/Internal/IButtonControl.cs b/source/ShiftUI/Internal/IButtonControl.cs new file mode 100644 index 0000000..33d9fc8 --- /dev/null +++ b/source/ShiftUI/Internal/IButtonControl.cs @@ -0,0 +1,40 @@ +// +// ShiftUI.IButtonWidget.cs +// +// 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. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// +// + + +// COMPLETE + +namespace ShiftUI +{ + + public interface IButtonWidget + { + DialogResult DialogResult {get; set;} + void NotifyDefault (bool value); + void PerformClick (); + } +} diff --git a/source/ShiftUI/Internal/ICommandExecutor.cs b/source/ShiftUI/Internal/ICommandExecutor.cs new file mode 100644 index 0000000..dfe1573 --- /dev/null +++ b/source/ShiftUI/Internal/ICommandExecutor.cs @@ -0,0 +1,34 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// + +// COMPLETE + +namespace ShiftUI { + public interface ICommandExecutor { + #region Public Instance Methods + void Execute(); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/IComponentEditorPageSite.cs b/source/ShiftUI/Internal/IComponentEditorPageSite.cs new file mode 100644 index 0000000..fd9eaa9 --- /dev/null +++ b/source/ShiftUI/Internal/IComponentEditorPageSite.cs @@ -0,0 +1,34 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok ([email protected]); +// + +// COMPLETE + +namespace ShiftUI { + + public interface IComponentEditorPageSite { + Widget GetWidget(); + void SetDirty(); + } +} diff --git a/source/ShiftUI/Internal/IContainerControl.cs b/source/ShiftUI/Internal/IContainerControl.cs new file mode 100644 index 0000000..da5cc19 --- /dev/null +++ b/source/ShiftUI/Internal/IContainerControl.cs @@ -0,0 +1,39 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + + +// COMPLETE + +namespace ShiftUI { + public interface IContainerWidget { + #region Public Instance Properties + Widget ActiveWidget {get; set;} + #endregion // Public Instance Properties + + #region Public Instance Methods + bool ActivateWidget(Widget active); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/ICurrencyManagerProvider.cs b/source/ShiftUI/Internal/ICurrencyManagerProvider.cs new file mode 100644 index 0000000..a3993cc --- /dev/null +++ b/source/ShiftUI/Internal/ICurrencyManagerProvider.cs @@ -0,0 +1,32 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// + + +namespace ShiftUI { + + public interface ICurrencyManagerProvider { + CurrencyManager CurrencyManager { get; } + + CurrencyManager GetRelatedCurrencyManager (string dataMember); + } + +} diff --git a/source/ShiftUI/Internal/IDataGridColumnStyleEditingNotificationService.cs b/source/ShiftUI/Internal/IDataGridColumnStyleEditingNotificationService.cs new file mode 100644 index 0000000..f55a6c6 --- /dev/null +++ b/source/ShiftUI/Internal/IDataGridColumnStyleEditingNotificationService.cs @@ -0,0 +1,35 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +namespace ShiftUI { + public interface IDataGridColumnStyleEditingNotificationService { + #region Public Instance Methods + void ColumnStartedEditing(Widget editingWidget); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/IDataGridEditingService.cs b/source/ShiftUI/Internal/IDataGridEditingService.cs new file mode 100644 index 0000000..41f9c9a --- /dev/null +++ b/source/ShiftUI/Internal/IDataGridEditingService.cs @@ -0,0 +1,36 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// +// Peter Bartok [email protected] +// + +// COMPLETE + +namespace ShiftUI { + public interface IDataGridEditingService { + #region Public Instance Methods + bool BeginEdit(DataGridColumnStyle gridColumn, int rowNumber); + bool EndEdit(DataGridColumnStyle gridColumn, int rowNumber, bool shouldAbort); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/IDataGridViewEditingCell.cs b/source/ShiftUI/Internal/IDataGridViewEditingCell.cs new file mode 100644 index 0000000..30fc6e4 --- /dev/null +++ b/source/ShiftUI/Internal/IDataGridViewEditingCell.cs @@ -0,0 +1,41 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Author: +// Pedro MartÃnez Juliá <[email protected]> +// + + +namespace ShiftUI { + + public interface IDataGridViewEditingCell { + + object EditingCellFormattedValue { get; set; } + + bool EditingCellValueChanged { get; set; } + + object GetEditingCellFormattedValue (DataGridViewDataErrorContexts context); + + void PrepareEditingCellForEdit (bool selectAll); + + } + +} diff --git a/source/ShiftUI/Internal/IDataGridViewEditingControl.cs b/source/ShiftUI/Internal/IDataGridViewEditingControl.cs new file mode 100644 index 0000000..8b102d0 --- /dev/null +++ b/source/ShiftUI/Internal/IDataGridViewEditingControl.cs @@ -0,0 +1,53 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Author: +// Pedro MartÃnez Juliá <[email protected]> +// + + +namespace ShiftUI { + + public interface IDataGridViewEditingWidget { + + DataGridView EditingWidgetDataGridView {get; set;} + + object EditingWidgetFormattedValue {get; set;} + + int EditingWidgetRowIndex {get; set;} + + bool EditingWidgetValueChanged {get; set;} + + Cursor EditingPanelCursor {get;} + + bool RepositionEditingWidgetOnValueChange {get;} + + void ApplyCellStyleToEditingWidget (DataGridViewCellStyle dataGridViewCellStyle); + + bool EditingWidgetWantsInputKey (Keys keyData, bool dataGridViewWantsInputKey); + + object GetEditingWidgetFormattedValue (DataGridViewDataErrorContexts context); + + void PrepareEditingWidgetForEdit (bool selectAll); + + } + +} diff --git a/source/ShiftUI/Internal/IDataObject.cs b/source/ShiftUI/Internal/IDataObject.cs new file mode 100644 index 0000000..7ce0d80 --- /dev/null +++ b/source/ShiftUI/Internal/IDataObject.cs @@ -0,0 +1,54 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// +// + + +// COMPLETE + +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI { + [ComVisible(true)] + public interface IDataObject { + #region Public Instance Methods + object GetData(string format); + object GetData(string format, bool autoConvert); + object GetData(Type format); + + bool GetDataPresent(string format); + bool GetDataPresent(string format, bool autoConvert); + bool GetDataPresent(Type format); + + string[] GetFormats(); + string[] GetFormats(bool autoConvert); + + void SetData(object data); + void SetData(string format, bool autoConvert, object data); + void SetData(string format, object data); + void SetData(Type format, object data); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/IDropTarget.cs b/source/ShiftUI/Internal/IDropTarget.cs new file mode 100644 index 0000000..2589634 --- /dev/null +++ b/source/ShiftUI/Internal/IDropTarget.cs @@ -0,0 +1,36 @@ +// 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) 2006 Novell, Inc. +// + +using System; +using System.Drawing; +using System.ComponentModel; + +namespace ShiftUI { + + public interface IDropTarget { + void OnDragDrop (DragEventArgs e); + void OnDragEnter (DragEventArgs e); + void OnDragLeave (EventArgs e); + void OnDragOver (DragEventArgs e); + } + +} diff --git a/source/ShiftUI/Internal/IEventHandler.cs b/source/ShiftUI/Internal/IEventHandler.cs new file mode 100644 index 0000000..e46175e --- /dev/null +++ b/source/ShiftUI/Internal/IEventHandler.cs @@ -0,0 +1,33 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; + +namespace ShiftUI.CarbonInternal { + internal interface IEventHandler { + bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg); + } +} diff --git a/source/ShiftUI/Internal/IFeatureSupport.cs b/source/ShiftUI/Internal/IFeatureSupport.cs new file mode 100644 index 0000000..748b510 --- /dev/null +++ b/source/ShiftUI/Internal/IFeatureSupport.cs @@ -0,0 +1,38 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE +using System; + +namespace ShiftUI { + public interface IFeatureSupport { + #region Public Instance Methods + Version GetVersionPresent(object feature); + bool IsPresent(object feature); + bool IsPresent(object feature, Version minimumVersion); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/IFileReaderService.cs b/source/ShiftUI/Internal/IFileReaderService.cs new file mode 100644 index 0000000..abee90e --- /dev/null +++ b/source/ShiftUI/Internal/IFileReaderService.cs @@ -0,0 +1,37 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System.IO; + +namespace ShiftUI { + public interface IFileReaderService { + #region Public Instance Methods + Stream OpenFileFromSource(string relativePath); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/IKeyFilter.cs b/source/ShiftUI/Internal/IKeyFilter.cs new file mode 100644 index 0000000..ecc2396 --- /dev/null +++ b/source/ShiftUI/Internal/IKeyFilter.cs @@ -0,0 +1,32 @@ +// 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) 2008 Novell, Inc. +// +// Authors: +// Mike Gorse [email protected] +// +// + + +namespace ShiftUI { + internal interface IKeyFilter { + bool PreFilterKey(KeyFilterData data); + } +} diff --git a/source/ShiftUI/Internal/IMessageFilter.cs b/source/ShiftUI/Internal/IMessageFilter.cs new file mode 100644 index 0000000..d55394f --- /dev/null +++ b/source/ShiftUI/Internal/IMessageFilter.cs @@ -0,0 +1,34 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// +// + + +// COMPLETE + +namespace ShiftUI { + public interface IMessageFilter { + bool PreFilterMessage(ref Message m); + } +} diff --git a/source/ShiftUI/Internal/IToolStripData.cs b/source/ShiftUI/Internal/IToolStripData.cs new file mode 100644 index 0000000..c01afd3 --- /dev/null +++ b/source/ShiftUI/Internal/IToolStripData.cs @@ -0,0 +1,41 @@ +// +// IToolStripData.cs +// +// 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) 2008 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System; +using System.Collections.Generic; +using System.Text; +using System.Drawing; + +namespace ShiftUI +{ + interface IToolStripData + { + bool IsCurrentlyDragging { get; } + bool Stretch { get; set; } + } +} diff --git a/source/ShiftUI/Internal/IWin32Window.cs b/source/ShiftUI/Internal/IWin32Window.cs new file mode 100644 index 0000000..73bfc31 --- /dev/null +++ b/source/ShiftUI/Internal/IWin32Window.cs @@ -0,0 +1,44 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// +// + + +// COMPLETE + +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI { + [ComVisible(true)] + [Guid("458AB8A2-A1EA-4d7b-8EBE-DEE5D3D9442C")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IWin32Window { + #region Public Instance Properties + IntPtr Handle { + get; + } + #endregion // Public Instance Properties + } +} diff --git a/source/ShiftUI/Internal/IWindowTarget.cs b/source/ShiftUI/Internal/IWindowTarget.cs new file mode 100644 index 0000000..0157773 --- /dev/null +++ b/source/ShiftUI/Internal/IWindowTarget.cs @@ -0,0 +1,34 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// +using System; + +namespace ShiftUI { + public interface IWindowTarget { + #region Public Instance Methods + void OnHandleChange(IntPtr newHandle); + void OnMessage(ref Message m); + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/ImageIndexConverter.cs b/source/ShiftUI/Internal/ImageIndexConverter.cs new file mode 100644 index 0000000..b2783e9 --- /dev/null +++ b/source/ShiftUI/Internal/ImageIndexConverter.cs @@ -0,0 +1,102 @@ +// 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 Novell, Inc. +// +// Authors: +// Ravindra [email protected] +// + + +// COMPLETE + +using System; +using System.Drawing; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace ShiftUI +{ + public class ImageIndexConverter : Int32Converter + { + #region Constructors + + public ImageIndexConverter () { } + + #endregion Constructors + + #region Protected Properties + + protected virtual bool IncludeNoneAsStandardValue { + get { return true; } + } + + #endregion Protected Properties + + #region Public Methods + + public override object ConvertFrom (ITypeDescriptorContext context, CultureInfo culture, object value) + { + string indexStr; + if (value != null && value is string) { + indexStr = (string) value; + if (indexStr == "(none)") + return -1; + else + return Int32.Parse (indexStr); + } + else + return base.ConvertFrom (context, culture, value); + } + + public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, + object value, Type destinationType) + { + if (value != null && destinationType == typeof (string)) { + if (value is int && (int) value == -1) + return "(none)"; + else + return value.ToString (); + } + else + return base.ConvertTo (context, culture, value, destinationType); + } + + public override StandardValuesCollection GetStandardValues (ITypeDescriptorContext context) + { + int [] stdVal = new int [] {-1}; + return new TypeConverter.StandardValuesCollection (stdVal); + } + + public override bool GetStandardValuesExclusive (ITypeDescriptorContext context) + { + return false; + } + + public override bool GetStandardValuesSupported (ITypeDescriptorContext context) + { + return true; + } + + #endregion Public Methods + } +} diff --git a/source/ShiftUI/Internal/ImageKeyConverter.cs b/source/ShiftUI/Internal/ImageKeyConverter.cs new file mode 100644 index 0000000..e70b5e1 --- /dev/null +++ b/source/ShiftUI/Internal/ImageKeyConverter.cs @@ -0,0 +1,97 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Jonathan Pobst [email protected] +// + +using System.Drawing; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI +{ + public class ImageKeyConverter : StringConverter + { + #region Constructors + public ImageKeyConverter () { } + #endregion Constructors + + #region Protected Properties + protected virtual bool IncludeNoneAsStandardValue { + get { return true; } + } + #endregion Protected Properties + + #region Public Methods + public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + return true; + + return false; + } + + public override object ConvertFrom (ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value != null && value is string) + return (string) value; + else + return base.ConvertFrom (context, culture, value); + } + + public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, + object value, Type destinationType) + { + if (value == null) + return "(none)"; + else if (destinationType == typeof (string)) { + if (value is string && (string) value == string.Empty) + return "(none)"; + else + return value.ToString (); + } + else + return base.ConvertTo (context, culture, value, destinationType); + } + + public override StandardValuesCollection GetStandardValues (ITypeDescriptorContext context) + { + string[] stdVal = new string[] { string.Empty }; + return new TypeConverter.StandardValuesCollection (stdVal); + } + + public override bool GetStandardValuesExclusive (ITypeDescriptorContext context) + { + return true; + } + + public override bool GetStandardValuesSupported (ITypeDescriptorContext context) + { + return true; + } + #endregion Public Methods + } +} diff --git a/source/ShiftUI/Internal/ImageList.cs b/source/ShiftUI/Internal/ImageList.cs new file mode 100644 index 0000000..b983455 --- /dev/null +++ b/source/ShiftUI/Internal/ImageList.cs @@ -0,0 +1,1073 @@ +// +// ShiftUI.ImageList.cs +// +// Authors: +// Peter Bartok <[email protected]> +// Kornél Pál <http://www.kornelpal.hu/> +// +// Copyright (C) 2004-2005 Novell, Inc. +// Copyright (C) 2005 Kornél Pál +// + +// +// 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. +// + +// COMPLETE + +// +// Differences between MS.NET ImageList and this implementation: +// +// This is a fully managed image list implementation. +// +// Images are stored as Format32bppArgb internally but ColorDepth is applied +// to the colors of images. Images[index] returns a Format32bppArgb copy of +// the image so this difference is only internal. +// +// MS.NET has no alpha channel support (except for icons in 32-bit mode with +// comctl32.dll version 6.0) but this implementation has full alpha channel +// support in 32-bit mode. +// +// Handle should be an HIMAGELIST returned by ImageList_Create. This +// implementation uses (IntPtr)(-1) that is a non-zero but invalid handle. +// +// MS.NET destroys handles using the garbage collector this implementation +// does the same with Image objects stored in an ArrayList. +// +// MS.NET 1.x shares the same HIMAGELIST between ImageLists that were +// initialized from the same ImageListStreamer and doesn't update ImageSize +// and ColorDepth that are treated as bugs and MS.NET 2.0 behavior is +// implemented. +// +// MS.NET 2.0 does not clear keys when handle is destroyed that is treated as +// a bug. +// + +using System.Collections; +using System.Collections.Specialized; +using System.ComponentModel; +using System.ComponentModel.Design.Serialization; +using System.Drawing; +using System.Drawing.Design; +using System.Drawing.Imaging; +using System.Globalization; +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI +{ + [DefaultProperty("Images")] + //[Designer("ShiftUI.Design.ImageListDesigner, " + Consts.AssemblySystem_Design)] + //[DesignerSerializer("ShiftUI.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)] + [ToolboxItemFilter("ShiftUI")] + [TypeConverter(typeof(ImageListConverter))] + public sealed class ImageList : System.ComponentModel.Component + { + #region Private Fields + private const ColorDepth DefaultColorDepth = ColorDepth.Depth8Bit; + private static readonly Size DefaultImageSize = new Size(16, 16); + private static readonly Color DefaultTransparentColor = Color.Transparent; + private object tag; + private readonly ImageCollection images; + #endregion // Private Fields + + #region Sub-classes + //[Editor("ShiftUI.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))] + public sealed class ImageCollection : IList, ICollection, IEnumerable + { + private const int AlphaMask = unchecked((int)0xFF000000); + + private static class IndexedColorDepths + { + internal static readonly ColorPalette Palette4Bit; + internal static readonly ColorPalette Palette8Bit; + private static readonly int[] squares; + + static IndexedColorDepths() + { + Bitmap bitmap; + int index; + + bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed); + Palette4Bit = bitmap.Palette; + bitmap.Dispose(); + + bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed); + Palette8Bit = bitmap.Palette; + bitmap.Dispose(); + + squares = new int[511]; + for (index = 0; index < 256; index++) + squares[255 + index] = squares[255 - index] = index * index; + } + + internal static int GetNearestColor(Color[] palette, int color) + { + int index; + int count; + int red; + int green; + int blue; + int nearestColor; + int minDistance; + int distance; + + count = palette.Length; + for (index = 0; index < count; index++) + if (palette[index].ToArgb() == color) + return color; + + red = unchecked((int)(unchecked((uint)color) >> 16) & 0xFF); + green = unchecked((int)(unchecked((uint)color) >> 8) & 0xFF); + blue = color & 0xFF; + nearestColor = AlphaMask; + minDistance = int.MaxValue; + + for (index = 0; index < count; index++) + if ((distance = squares[255 + palette[index].R - red] + squares[255 + palette[index].G - green] + squares[255 + palette[index].B - blue]) < minDistance) { + nearestColor = palette[index].ToArgb(); + minDistance = distance; + } + + return nearestColor; + } + } + + [Flags()] + private enum ItemFlags + { + None = 0, + UseTransparentColor = 1, + ImageStrip = 2 + } + + private sealed class ImageListItem + { + internal readonly object Image; + internal readonly ItemFlags Flags; + internal readonly Color TransparentColor; + internal readonly int ImageCount = 1; + + internal ImageListItem(Icon value) + { + if (value == null) + throw new ArgumentNullException("value"); + + // Icons are cloned. + this.Image = (Icon)value.Clone(); + } + + internal ImageListItem(Image value) + { + if (value == null) + throw new ArgumentNullException("value"); + + if (!(value is Bitmap)) + throw new ArgumentException("Image must be a Bitmap."); + + // Images are not cloned. + this.Image = value; + } + + internal ImageListItem(Image value, Color transparentColor) : this(value) + { + this.Flags = ItemFlags.UseTransparentColor; + this.TransparentColor = transparentColor; + } + + internal ImageListItem(Image value, int imageCount) : this(value) + { + this.Flags = ItemFlags.ImageStrip; + this.ImageCount = imageCount; + } + } + + #region ImageCollection Private Fields + private ColorDepth colorDepth = DefaultColorDepth; + private Size imageSize = DefaultImageSize; + private Color transparentColor = DefaultTransparentColor; + private ArrayList list = new ArrayList(); + private ArrayList keys = new ArrayList(); + private int count; + private bool handleCreated; + private int lastKeyIndex = -1; + private readonly ImageList owner; + #endregion // ImageCollection Private Fields + + #region ImageCollection Internal Constructors + // For use in ImageList + internal ImageCollection(ImageList owner) + { + this.owner = owner; + } + #endregion // ImageCollection Internal Constructor + + #region ImageCollection Internal Instance Properties + // For use in ImageList + internal ColorDepth ColorDepth { + get { + return this.colorDepth; + } + + set { + if (!Enum.IsDefined(typeof(ColorDepth), value)) + throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth)); + + if (this.colorDepth != value) { + this.colorDepth = value; + RecreateHandle(); + } + } + } + + // For use in ImageList + internal IntPtr Handle { + get { + CreateHandle(); + return (IntPtr)(-1); + } + } + + // For use in ImageList + internal bool HandleCreated { + get { + return this.handleCreated; + } + } + + // For use in ImageList + internal Size ImageSize { + get { + return this.imageSize; + } + + set { + if (value.Width < 1 || value.Width > 256 || value.Height < 1 || value.Height > 256) + throw new ArgumentException("ImageSize.Width and Height must be between 1 and 256", "value"); + + if (this.imageSize != value) { + this.imageSize = value; + RecreateHandle(); + } + } + } + + // For use in ImageList + internal ImageListStreamer ImageStream { + get { + return this.Empty ? null : new ImageListStreamer(this); + } + + set { + int index; + Image[] streamImages; + + if (value == null) { + if (this.handleCreated) + DestroyHandle(); + else + this.Clear(); + } + // Only deserialized ImageListStreamers are used. + else if ((streamImages = value.Images) != null) { + this.list = new ArrayList(streamImages.Length); + this.count = 0; + this.handleCreated = true; + this.keys = new ArrayList(streamImages.Length); + + for (index = 0; index < streamImages.Length; index++) { + list.Add((Image)streamImages[index].Clone()); + keys.Add(null); + } + + // Invalid ColorDepth values are ignored. + if (Enum.IsDefined(typeof(ColorDepth), value.ColorDepth)) + this.colorDepth = (ColorDepth)value.ColorDepth; + this.imageSize = value.ImageSize; + + // Event is raised even when handle was not created yet. + owner.OnRecreateHandle(); + } + } + } + + // For use in ImageList + internal Color TransparentColor { + get { + return this.transparentColor; + } + + set { + this.transparentColor = value; + } + } + #endregion // ImageCollection Internal Instance Properties + + #region ImageCollection Public Instance Properties + [Browsable(false)] + public int Count { + get { + return this.handleCreated ? list.Count : this.count; + } + } + + public bool Empty { + get { + return this.Count == 0; + } + } + + public bool IsReadOnly { + get { + return false; + } + } + + [Browsable(false)] + //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public Image this[int index] { + get { + return (Image)GetImage(index).Clone(); + } + + set { + Image image; + + if (index < 0 || index >= this.Count) + throw new ArgumentOutOfRangeException("index"); + + if (value == null) + throw new ArgumentNullException("value"); + + if (!(value is Bitmap)) + throw new ArgumentException("Image must be a Bitmap."); + + image = CreateImage(value, this.transparentColor); + CreateHandle(); + list[index] = image; + } + } + + public Image this[string key] { + get { + int index; + + return (index = IndexOfKey(key)) == -1 ? null : this[index]; + } + } + + public StringCollection Keys { + get { + int index; + string key; + StringCollection keyCollection; + + // Returns all keys even when there are more keys than + // images. Null keys are returned as empty strings. + + keyCollection = new StringCollection(); + for (index = 0; index < keys.Count; index++) + keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key); + + return keyCollection; + } + } + #endregion // ImageCollection Public Instance Properties + + #region ImageCollection Private Static Methods + private static bool CompareKeys(string key1, string key2) + { + // Keys are case-insensitive and keys with different length + // are not equal even when string.Compare treats them equal. + + if (key1 == null || key2 == null || key1.Length != key2.Length) + return false; + + return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0; + } + #endregion // ImageCollection Private Static Methods + + #region ImageCollection Private Instance Methods + private int AddItem(string key, ImageListItem item) + { + int itemIndex; + int index; + + if (this.handleCreated) + itemIndex = AddItemInternal(item); + else { + // Image strips are counted as a single item in the return + // value of Add and AddStrip until handle is created. + + itemIndex = list.Add(item); + this.count += item.ImageCount; + } + + if ((item.Flags & ItemFlags.ImageStrip) == 0) + keys.Add(key); + else + for (index = 0; index < item.ImageCount; index++) + keys.Add(null); + + return itemIndex; + } + + internal event EventHandler Changed; + + private int AddItemInternal(ImageListItem item) + { + if (Changed != null) + Changed (this, EventArgs.Empty); + + if (item.Image is Icon) { + int imageWidth; + int imageHeight; + Bitmap bitmap; + Graphics graphics; + + bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb); + graphics = Graphics.FromImage(bitmap); + graphics.DrawIcon((Icon)item.Image, new Rectangle(0, 0, imageWidth, imageHeight)); + graphics.Dispose(); + + ReduceColorDepth(bitmap); + return list.Add(bitmap); + } + else if ((item.Flags & ItemFlags.ImageStrip) == 0) + return list.Add(CreateImage((Image)item.Image, (item.Flags & ItemFlags.UseTransparentColor) == 0 ? this.transparentColor : item.TransparentColor)); + else { + int imageX; + int width; + int imageWidth; + int imageHeight; + int index; + Image image; + Bitmap bitmap; + Graphics graphics; + Rectangle imageRect; + ImageAttributes imageAttributes; + + // When ImageSize was changed after adding image strips + // Count will return invalid values based on old ImageSize + // but when creating handle either ArgumentException will + // be thrown or image strip will be added according to the + // new ImageSize. This can result in image count + // difference that can result in exceptions in methods + // that use Count before creating handle. In addition this + // can result in the loss of sync with keys. When doing + // the same after handle was created there are no problems + // as handle will be recreated after changing ImageSize + // that results in the loss of images added previously. + + if ((width = (image = (Image)item.Image).Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0) + throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value"); + + if (image.Height != (imageHeight = this.imageSize.Height)) + throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value"); + + imageRect = new Rectangle(0, 0, imageWidth, imageHeight); + if (this.transparentColor.A == 0) + imageAttributes = null; + else { + imageAttributes = new ImageAttributes(); + imageAttributes.SetColorKey(this.transparentColor, this.transparentColor); + } + + index = list.Count; + for (imageX = 0; imageX < width; imageX += imageWidth) { + bitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format32bppArgb); + graphics = Graphics.FromImage(bitmap); + graphics.DrawImage(image, imageRect, imageX, 0, imageWidth, imageHeight, GraphicsUnit.Pixel, imageAttributes); + graphics.Dispose(); + + ReduceColorDepth(bitmap); + list.Add(bitmap); + } + + if (imageAttributes != null) + imageAttributes.Dispose(); + + return index; + } + } + + private void CreateHandle() + { + int index; + ArrayList items; + + if (!this.handleCreated) { + items = this.list; + this.list = new ArrayList(this.count); + this.count = 0; + this.handleCreated = true; + + for (index = 0; index < items.Count; index++) + AddItemInternal((ImageListItem)items[index]); + } + } + + private Image CreateImage(Image value, Color transparentColor) + { + int imageWidth; + int imageHeight; + ImageAttributes imageAttributes; + + if (transparentColor.A == 0) + imageAttributes = null; + else { + imageAttributes = new ImageAttributes(); + imageAttributes.SetColorKey (transparentColor, transparentColor); + } + + var bitmap = new Bitmap (imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb); + using (var graphics = Graphics.FromImage (bitmap)) + graphics.DrawImage (value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes); + + if (imageAttributes != null) + imageAttributes.Dispose (); + + ReduceColorDepth (bitmap); + return bitmap; + } + + private void RecreateHandle() + { + if (this.handleCreated) { + DestroyHandle(); + this.handleCreated = true; + owner.OnRecreateHandle(); + } + } + + private unsafe void ReduceColorDepth(Bitmap bitmap) + { + byte* pixelPtr; + byte* lineEndPtr; + byte* linePtr; + int line; + int pixel; + int height; + int widthBytes; + int stride; + BitmapData bitmapData; + Color[] palette; + + if (this.colorDepth < ColorDepth.Depth32Bit) { + bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb); + try { + linePtr = (byte*)bitmapData.Scan0; + height = bitmapData.Height; + widthBytes = bitmapData.Width << 2; + stride = bitmapData.Stride; + + if (this.colorDepth < ColorDepth.Depth16Bit) { + palette = (this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit).Entries; + + for (line = 0; line < height; line++) { + lineEndPtr = linePtr + widthBytes; + for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4) + *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask); + linePtr += stride; + } + } + else if (this.colorDepth < ColorDepth.Depth24Bit) { + for (line = 0; line < height; line++) { + lineEndPtr = linePtr + widthBytes; + for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4) + *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask; + linePtr += stride; + } + } + else { + for (line = 0; line < height; line++) { + lineEndPtr = linePtr + widthBytes; + for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4) + *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask; + linePtr += stride; + } + } + } + finally { + bitmap.UnlockBits(bitmapData); + } + } + } + #endregion // ImageCollection Private Instance Methods + + #region ImageCollection Internal Instance Methods + // For use in ImageList + internal void DestroyHandle() + { + if (this.handleCreated) { + this.list = new ArrayList(); + this.count = 0; + this.handleCreated = false; + keys = new ArrayList(); + } + } + + // For use in ImageList + internal Image GetImage(int index) + { + if (index < 0 || index >= this.Count) + throw new ArgumentOutOfRangeException("index"); + + CreateHandle(); + return (Image)list[index]; + } + + // For use in ImageListStreamer + internal Image[] ToArray() + { + Image[] images; + + // Handle is created even when the list is empty. + CreateHandle(); + images = new Image[list.Count]; + list.CopyTo(images); + return images; + } + #endregion // ImageCollection Internal Instance Methods + + #region ImageCollection Public Instance Methods + public void Add(Icon value) + { + Add(null, value); + } + + public void Add(Image value) + { + Add(null, value); + } + + public int Add(Image value, Color transparentColor) + { + return AddItem(null, new ImageListItem(value, transparentColor)); + } + + public void Add(string key, Icon icon) + { + // Argument has name icon but exceptions use name value. + AddItem(key, new ImageListItem(icon)); + } + + public void Add(string key, Image image) + { + // Argument has name image but exceptions use name value. + AddItem(key, new ImageListItem(image)); + } + + public void AddRange(Image[] images) + { + int index; + + if (images == null) + throw new ArgumentNullException("images"); + + for (index = 0; index < images.Length; index++) + Add(images[index]); + } + + public int AddStrip(Image value) + { + int width; + int imageWidth; + + if (value == null) + throw new ArgumentNullException("value"); + + if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0) + throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value"); + + if (value.Height != this.imageSize.Height) + throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value"); + + return AddItem(null, new ImageListItem(value, width / imageWidth)); + } + + public void Clear() + { + list.Clear(); + if (!this.handleCreated) + this.count = 0; + keys.Clear(); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public bool Contains(Image image) + { + throw new NotSupportedException(); + } + + public bool ContainsKey(string key) + { + return IndexOfKey(key) != -1; + } + + public IEnumerator GetEnumerator() + { + Image[] images = new Image[this.Count]; + int index; + + if (images.Length != 0) { + // Handle is created only when there are images. + CreateHandle(); + + for (index = 0; index < images.Length; index++) + images[index] = (Image)((Image)list[index]).Clone(); + } + + return images.GetEnumerator(); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public int IndexOf(Image image) + { + throw new NotSupportedException(); + } + + public int IndexOfKey(string key) + { + int index; + + if (key != null && key.Length != 0) { + // When last IndexOfKey was successful and the same key was + // assigned to an image with a lower index than the last + // result and the key of the last result equals to key + // argument the last result is returned. + + if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key)) + return this.lastKeyIndex; + + // Duplicate keys are allowed and first match is returned. + for (index = 0; index < this.Count; index++) + if (CompareKeys((string)keys[index], key)) + return this.lastKeyIndex = index; + } + + return this.lastKeyIndex = -1; + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public void Remove(Image image) + { + throw new NotSupportedException(); + } + + public void RemoveAt(int index) + { + if (index < 0 || index >= this.Count) + throw new ArgumentOutOfRangeException("index"); + + CreateHandle(); + list.RemoveAt(index); + keys.RemoveAt(index); + if (Changed != null) + Changed (this, EventArgs.Empty); + } + + public void RemoveByKey(string key) + { + int index; + + if ((index = IndexOfKey(key)) != -1) + RemoveAt(index); + } + + public void SetKeyName(int index, string name) + { + // Only SetKeyName throws IndexOutOfRangeException. + if (index < 0 || index >= this.Count) + throw new IndexOutOfRangeException(); + + keys[index] = name; + } + #endregion // ImageCollection Public Instance Methods + + #region ImageCollection Interface Properties + object IList.this[int index] { + get { + return this[index]; + } + + set { + if (!(value is Image)) + throw new ArgumentException("value"); + + this[index] = (Image)value; + } + } + + bool IList.IsFixedSize { + get { + return false; + } + } + + bool ICollection.IsSynchronized { + get { + return false; + } + } + + object ICollection.SyncRoot { + get { + return this; + } + } + #endregion // ImageCollection Interface Properties + + #region ImageCollection Interface Methods + int IList.Add(object value) + { + int index; + + if (!(value is Image)) + throw new ArgumentException("value"); + + index = this.Count; + this.Add((Image)value); + return index; + } + + bool IList.Contains(object image) + { + return image is Image ? this.Contains ((Image) image) : false; + } + + int IList.IndexOf (object image) + { + return image is Image ? this.IndexOf ((Image) image) : -1; + } + + void IList.Insert(int index, object value) + { + throw new NotSupportedException(); + } + + void IList.Remove (object image) + { + if (image is Image) + this.Remove ((Image) image); + } + + void ICollection.CopyTo(Array dest, int index) + { + for (int imageIndex = 0; imageIndex < this.Count; imageIndex++) + dest.SetValue (this[imageIndex], index++); + } + #endregion // ImageCollection Interface Methods + } + #endregion // Sub-classes + + #region Public Constructors + public ImageList() + { + images = new ImageCollection(this); + } + + public ImageList(System.ComponentModel.IContainer container) : this() + { + container.Add(this); + } + #endregion // Public Constructors + + #region Private Instance Methods + private void OnRecreateHandle() + { + EventHandler eh = (EventHandler)(Events [RecreateHandleEvent]); + if (eh != null) + eh (this, EventArgs.Empty); + } + + // MS's TypeDescriptor stuff apparently uses + // non-public ShouldSerialize* methods, because it + // picks up this behavior even though the methods + // aren't public. we can't make them private, though, + // without adding compiler warnings. so, make then + // internal instead. + + internal bool ShouldSerializeTransparentColor () + { + return this.TransparentColor != Color.LightGray; + } + + internal bool ShouldSerializeColorDepth() + { + // ColorDepth is serialized in ImageStream when non-empty. + // It is serialized even if it has its default value when empty. + return images.Empty; + } + + internal bool ShouldSerializeImageSize() + { + // ImageSize is serialized in ImageStream when non-empty. + // It is serialized even if it has its default value when empty. + return images.Empty; + } + + internal void ResetColorDepth () + { + this.ColorDepth = DefaultColorDepth; + } + + internal void ResetImageSize () + { + this.ImageSize = DefaultImageSize; + } + + internal void ResetTransparentColor () + { + this.TransparentColor = Color.LightGray; + } + #endregion // Private Instance Methods + + #region Public Instance Properties + public ColorDepth ColorDepth { + get { + return images.ColorDepth; + } + + set { + images.ColorDepth = value; + } + } + + [Browsable(false)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public IntPtr Handle { + get { + return images.Handle; + } + } + + [Browsable(false)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public bool HandleCreated { + get { + return images.HandleCreated; + } + } + + [DefaultValue(null)] + [MergableProperty(false)] + //[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + public ImageCollection Images { + get { + return this.images; + } + } + + [Localizable(true)] + public Size ImageSize { + get { + return images.ImageSize; + } + + set { + images.ImageSize = value; + } + } + + [Browsable(false)] + [DefaultValue(null)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + public ImageListStreamer ImageStream { + get { + return images.ImageStream; + } + + set { + images.ImageStream = value; + } + } + + [Bindable(true)] + [DefaultValue(null)] + [Localizable(false)] + [TypeConverter(typeof(StringConverter))] + public object Tag { + get { + return this.tag; + } + + set { + this.tag = value; + } + } + + public Color TransparentColor { + get { + return images.TransparentColor; + } + + set { + images.TransparentColor = value; + } + } + #endregion // Public Instance Properties + + #region Public Instance Methods + public void Draw(Graphics g, Point pt, int index) + { + this.Draw(g, pt.X, pt.Y, index); + } + + public void Draw(Graphics g, int x, int y, int index) + { + g.DrawImage(images.GetImage(index), x, y); + } + + public void Draw(Graphics g, int x, int y, int width, int height, int index) + { + g.DrawImage(images.GetImage(index), x, y, width, height); + } + + public override string ToString() + { + return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString(); + } + #endregion // Public Instance Methods + + #region Protected Instance Methods + protected override void Dispose(bool disposing) + { + if (disposing) + images.DestroyHandle(); + + base.Dispose(disposing); + } + #endregion // Protected Instance Methods + + #region Events + static object RecreateHandleEvent = new object (); + + [Browsable(false)] + //[EditorBrowsable(EditorBrowsableState.Advanced)] + public event EventHandler RecreateHandle { + add { Events.AddHandler (RecreateHandleEvent, value); } + remove { Events.RemoveHandler (RecreateHandleEvent, value); } + } + #endregion // Events + } +} diff --git a/source/ShiftUI/Internal/ImageListConverter.cs b/source/ShiftUI/Internal/ImageListConverter.cs new file mode 100644 index 0000000..8074d03 --- /dev/null +++ b/source/ShiftUI/Internal/ImageListConverter.cs @@ -0,0 +1,44 @@ +// 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) 2005 Novell, Inc. +// +// Authors: +// Matthias Felgner [email protected] +// + +// COMPLETE + +using System.ComponentModel; + + +namespace ShiftUI { + internal class ImageListConverter : ComponentConverter { + #region Constructors + public ImageListConverter() : base(typeof(ImageList)) { + } + #endregion Constructors + + #region Public Methods + public override bool GetPropertiesSupported(ITypeDescriptorContext context){ + return true; + } + #endregion Public Methods + } +} diff --git a/source/ShiftUI/Internal/ImageListStreamer.cs b/source/ShiftUI/Internal/ImageListStreamer.cs new file mode 100644 index 0000000..3186747 --- /dev/null +++ b/source/ShiftUI/Internal/ImageListStreamer.cs @@ -0,0 +1,338 @@ +// 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) 2002-2006 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) +// Gonzalo Paniagua Javier ([email protected]) +// +// Based on work done by: +// Dennis Hayes ([email protected]) +// Aleksey Ryabchuk ([email protected]) + +using System.IO; +using System.Drawing; +using System.Collections; +using System.Drawing.Imaging; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System; + + +namespace ShiftUI { + [Serializable] + public sealed class ImageListStreamer : ISerializable { + readonly ImageList.ImageCollection imageCollection; + Image [] images; + Size image_size; + Color back_color; + + internal ImageListStreamer (ImageList.ImageCollection imageCollection) + { + this.imageCollection = imageCollection; + } + + private ImageListStreamer (SerializationInfo info, StreamingContext context) + { + byte [] data = (byte []) info.GetValue ("Data", typeof (byte [])); + if (data == null || data.Length <= 4) { // 4 is the signature + return; + } + + // check the signature ( 'MSFt' ) + if (data [0] != 77 || data [1] != 83 || data [2] != 70 || data [3] != 116) { + return; + } + + MemoryStream decoded = GetDecodedStream (data, 4, data.Length - 4); + decoded.Position = 4; // jumps over 'magic' and 'version', which are 16-bits each + + BinaryReader reader = new BinaryReader (decoded); + ushort nimages = reader.ReadUInt16 (); + reader.ReadUInt16 (); // cMaxImage + ushort grow = reader.ReadUInt16 (); // cGrow + ushort cx = reader.ReadUInt16 (); + ushort cy = reader.ReadUInt16 (); + uint bkcolor = reader.ReadUInt32 (); + back_color = Color.FromArgb ((int) bkcolor); + reader.ReadUInt16 (); // flags + + short [] ovls = new short [4]; + for (int i = 0; i < 4; i++) { + ovls[i] = reader.ReadInt16 (); + } + + byte [] decoded_buffer = decoded.GetBuffer (); + int bmp_offset = 28; + // FileSize field from the bitmap file header + int filesize = decoded_buffer [bmp_offset + 2] + (decoded_buffer [bmp_offset + 3] << 8) + + (decoded_buffer [bmp_offset + 4] << 16) + (decoded_buffer [bmp_offset + 5] << 24); + // ImageSize field from the info header (can be 0) + int imagesize = decoded_buffer [bmp_offset + 34] + (decoded_buffer [bmp_offset + 35] << 8) + + (decoded_buffer [bmp_offset + 36] << 16) + (decoded_buffer [bmp_offset + 37] << 24); + + int bmp_length = imagesize + filesize; + MemoryStream bmpms = new MemoryStream (decoded_buffer, bmp_offset, bmp_length); + Bitmap bmp = null; + Bitmap mask = null; + bmp = new Bitmap (bmpms); + MemoryStream mask_stream = new MemoryStream (decoded_buffer, + bmp_offset + bmp_length, + (int) (decoded.Length - bmp_offset - bmp_length)); + + if (mask_stream.Length > 0) + mask = new Bitmap (mask_stream); + + if (bkcolor == 0xFFFFFFFF) + back_color = bmp.GetPixel (0, 0); + + if (mask != null) { + int width = bmp.Width; + int height = bmp.Height; + Bitmap newbmp = new Bitmap (bmp); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + Color mcolor = mask.GetPixel (x, y); + if (mcolor.B != 0) { + newbmp.SetPixel (x, y, Color.Transparent); + } + } + } + bmp.Dispose (); + bmp = newbmp; + mask.Dispose (); + mask = null; + } + images = new Image [nimages]; + image_size = new Size (cx, cy); + Rectangle dest_rect = new Rectangle (0, 0, cx, cy); + if (grow * bmp.Width > cx) // Some images store a wrong 'grow' factor + grow = (ushort) (bmp.Width / cx); + + for (int r = 0 ; r < nimages ; r++) { + int col = r % grow; + int row = r / grow; + Rectangle area = new Rectangle (col * cx, row * cy, cx, cy); + Bitmap b = new Bitmap (cx, cy); + using (Graphics g = Graphics.FromImage (b)) { + g.DrawImage (bmp, dest_rect, area, GraphicsUnit.Pixel); + } + + images [r] = b; + } + bmp.Dispose (); + } + + /* + static void WriteToFile (MemoryStream st) + { + st.Position = 0; + FileStream fs = File.OpenWrite (Path.GetTempFileName ()); + Console.WriteLine ("Writing to {0}", fs.Name); + st.WriteTo (fs); + fs.Close (); + } + */ + + static byte [] header = new byte []{ 77, 83, 70, 116, 73, 76, 1, 1 }; + public void GetObjectData (SerializationInfo si, StreamingContext context) + { + MemoryStream stream = new MemoryStream (); + BinaryWriter writer = new BinaryWriter (stream); + writer.Write (header); + + Image [] images = (imageCollection != null) ? imageCollection.ToArray () : this.images; + int cols = 4; + int rows = images.Length / cols; + if (images.Length % cols > 0) + ++rows; + + writer.Write ((ushort) images.Length); + writer.Write ((ushort) images.Length); + writer.Write ((ushort) 0x4); + writer.Write ((ushort) (images [0].Width)); + writer.Write ((ushort) (images [0].Height)); + writer.Write (0xFFFFFFFF); //BackColor.ToArgb ()); //FIXME: should set the right one here. + writer.Write ((ushort) 0x21); + for (int i = 0; i < 4; i++) + writer.Write ((short) -1); + + Bitmap main = new Bitmap (cols * ImageSize.Width, rows * ImageSize.Height); + using (Graphics g = Graphics.FromImage (main)) { + g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (BackColor), 0, 0, + main.Width, main.Height); + for (int i = 0; i < images.Length; i++) { + g.DrawImage (images [i], (i % cols) * ImageSize.Width, + (i / cols) * ImageSize.Height); + } + } + + MemoryStream tmp = new MemoryStream (); + main.Save (tmp, ImageFormat.Bmp); + tmp.WriteTo (stream); + + Bitmap mask = Get1bppMask (main); + main.Dispose (); + main = null; + + tmp = new MemoryStream (); + mask.Save (tmp, ImageFormat.Bmp); + tmp.WriteTo (stream); + mask.Dispose (); + + stream = GetRLEStream (stream, 4); + si.AddValue ("Data", stream.ToArray (), typeof (byte [])); + } + + unsafe Bitmap Get1bppMask (Bitmap main) + { + Rectangle rect = new Rectangle (0, 0, main.Width, main.Height); + Bitmap result = new Bitmap (main.Width, main.Height, PixelFormat.Format1bppIndexed); + BitmapData dresult = result.LockBits (rect, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed); + + int w = images [0].Width; + int h = images [0].Height; + byte *scan = (byte *) dresult.Scan0.ToPointer (); + int stride = dresult.Stride; + Bitmap current = null; + for (int idx = 0; idx < images.Length; idx++) { + current = (Bitmap) images [idx]; + // Hack for newly added images. + // Probably has to be done somewhere else. + Color c1 = current.GetPixel (0, 0); + if (c1.A != 0 && c1 == back_color) + current.MakeTransparent (back_color); + // + } + + int yidx = 0; + int imgidx = 0; + int localy = 0; + int localx = 0; + int factor_y = 0; + int factor_x = 0; + for (int y = 0; y < main.Height; y++) { + if (localy == h) { + localy = 0; + factor_y += 4; + } + factor_x = 0; + localx = 0; + for (int x = 0; x < main.Width; x++) { + if (localx == w) { + localx = 0; + factor_x++; + } + imgidx = factor_y + factor_x; + if (imgidx >= images.Length) + break; + current = (Bitmap) images [imgidx]; + Color color = current.GetPixel (localx, localy); + if (color.A == 0) { + int ptridx = yidx + (x >> 3); + scan [ptridx] |= (byte) (0x80 >> (x & 7)); + } + localx++; + } + if (imgidx >= images.Length) + break; + yidx += stride; + localy++; + } + result.UnlockBits (dresult); + + return result; + } + + static MemoryStream GetDecodedStream (byte [] bytes, int offset, int size) + { + byte [] buffer = new byte [512]; + int position = 0; + int count, data; + MemoryStream result = new MemoryStream (); + while (size > 0) { + count = (int) bytes [offset++]; + data = (int) bytes [offset++]; + if ((512 - count) < position) { + result.Write (buffer, 0, position); + position = 0; + } + + for (int i = 0; i < count; i++) + buffer [position++] = (byte) data; + size -= 2; + } + + if (position > 0) + result.Write (buffer, 0, position); + + result.Position = 0; + return result; + } + + //TODO: OptimizeMe + static MemoryStream GetRLEStream (MemoryStream input, int start) + { + MemoryStream result = new MemoryStream (); + byte [] ibuffer = input.GetBuffer (); + result.Write (ibuffer, 0, start); + input.Position = start; + + int prev = -1; + int count = 0; + int current; + while ((current = input.ReadByte ()) != -1) { + if (prev != current || count == 255) { + if (prev != -1) { + result.WriteByte ((byte) count); + result.WriteByte ((byte) prev); + } + prev = current; + count = 0; + } + count++; + } + + if (count > 0) { + result.WriteByte ((byte) count); + result.WriteByte ((byte) current); + } + + return result; + } + + internal Image [] Images { + get { return images; } + } + + internal Size ImageSize { + get { return image_size; } + } + + internal ColorDepth ColorDepth { + get { return ColorDepth.Depth32Bit; } + } + + internal Color BackColor { + get { return back_color; } + } + } +} + diff --git a/source/ShiftUI/Internal/InternalWindowManager.cs b/source/ShiftUI/Internal/InternalWindowManager.cs new file mode 100644 index 0000000..95fae49 --- /dev/null +++ b/source/ShiftUI/Internal/InternalWindowManager.cs @@ -0,0 +1,1211 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Jackson Harper ([email protected]) +// +// + + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + + +namespace ShiftUI { + + internal abstract class InternalWindowManager { + private TitleButtons title_buttons; + internal Form form; + + // moving windows + internal Point start; + internal State state; + protected Point clicked_point; + private FormPos sizing_edge; + internal Rectangle virtual_position; + + private Rectangle normal_bounds; + private Rectangle iconic_bounds; + + + public enum State { + Idle, + Moving, + Sizing, + } + + [Flags] + public enum FormPos { + None, + + TitleBar = 1, + + Top = 2, + Left = 4, + Right = 8, + Bottom = 16, + + TopLeft = Top | Left, + TopRight = Top | Right, + + BottomLeft = Bottom | Left, + BottomRight = Bottom | Right, + + AnyEdge = Top | Left | Right | Bottom, + } + + public InternalWindowManager (Form form) + { + this.form = form; + + form.SizeChanged += new EventHandler (FormSizeChangedHandler); + + title_buttons = new TitleButtons (form); + ThemeEngine.Current.ManagedWindowSetButtonLocations (this); + } + + public Form Form { + get { return form; } + } + + public int IconWidth { + get { return TitleBarHeight - 5; } + } + + public TitleButtons TitleButtons { + get { + return title_buttons; + } + } + internal Rectangle NormalBounds { + get { + return normal_bounds; + } + set { + normal_bounds = value; + } + } + internal Size IconicSize { + get { + return SystemInformation.MinimizedWindowSize; + } + } + + internal Rectangle IconicBounds { + get { + if (iconic_bounds == Rectangle.Empty) + return Rectangle.Empty; + Rectangle result = iconic_bounds; + result.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y; + return result; + } + set { + iconic_bounds = value; + iconic_bounds.Y = Form.Parent.ClientRectangle.Bottom - iconic_bounds.Y; + } + } + + internal virtual Rectangle MaximizedBounds { + get { + return Form.Parent.ClientRectangle; + } + } + + public virtual void UpdateWindowState (FormWindowState old_window_state, FormWindowState new_window_state, bool force) + { + if (old_window_state == FormWindowState.Normal) { + NormalBounds = form.Bounds; + } else if (old_window_state == FormWindowState.Minimized) { + IconicBounds = form.Bounds; + } + + switch (new_window_state) { + case FormWindowState.Minimized: + if (IconicBounds == Rectangle.Empty) { + Size size = IconicSize; + Point location = new Point (0, Form.Parent.ClientSize.Height - size.Height); + IconicBounds = new Rectangle (location, size); + } + form.Bounds = IconicBounds; + break; + case FormWindowState.Maximized: + form.Bounds = MaximizedBounds; + break; + case FormWindowState.Normal: + form.Bounds = NormalBounds; + break; + } + + UpdateWindowDecorations (new_window_state); + form.ResetCursor (); + } + + public virtual void UpdateWindowDecorations (FormWindowState window_state) + { + ThemeEngine.Current.ManagedWindowSetButtonLocations (this); + if (form.IsHandleCreated) + XplatUI.RequestNCRecalc (form.Handle); + } + + public virtual bool WndProc (ref Message m) + { +#if debug + Console.WriteLine(DateTime.Now.ToLongTimeString () + " " + this.GetType () .Name + " (Handle={0},Text={1}) received message {2}", form.IsHandleCreated ? form.Handle : IntPtr.Zero, form.Text, m.ToString ()); +#endif + + switch ((Msg)m.Msg) { + + + // The mouse handling messages are actually + // not WM_NC* messages except for the first button and NCMOVEs + // down because we capture on the form + + case Msg.WM_MOUSEMOVE: + return HandleMouseMove (form, ref m); + + case Msg.WM_LBUTTONUP: + HandleLButtonUp (ref m); + break; + + case Msg.WM_RBUTTONDOWN: + return HandleRButtonDown (ref m); + + case Msg.WM_LBUTTONDOWN: + return HandleLButtonDown (ref m); + + case Msg.WM_LBUTTONDBLCLK: + return HandleLButtonDblClick (ref m); + + case Msg.WM_PARENTNOTIFY: + if (Widget.LowOrder(m.WParam.ToInt32()) == (int) Msg.WM_LBUTTONDOWN) + Activate (); + break; + + case Msg.WM_NCHITTEST: + return HandleNCHitTest (ref m); + + // Return true from these guys, otherwise win32 will mess up z-order + case Msg.WM_NCLBUTTONUP: + HandleNCLButtonUp (ref m); + return true; + + case Msg.WM_NCLBUTTONDOWN: + HandleNCLButtonDown (ref m); + return true; + + case Msg.WM_NCMOUSEMOVE: + HandleNCMouseMove (ref m); + return true; + + case Msg.WM_NCLBUTTONDBLCLK: + HandleNCLButtonDblClick (ref m); + break; + + case Msg.WM_NCMOUSELEAVE: + HandleNCMouseLeave (ref m); + break; + + case Msg.WM_MOUSELEAVE: + HandleMouseLeave (ref m); + break; + + case Msg.WM_NCCALCSIZE: + return HandleNCCalcSize (ref m); + + case Msg.WM_NCPAINT: + return HandleNCPaint (ref m); + } + + return false; + } + + protected virtual bool HandleNCPaint (ref Message m) + { + PaintEventArgs pe = XplatUI.PaintEventStart (ref m, form.Handle, false); + + Rectangle clip; + + if (form.ActiveMenu != null) { + Point pnt; + + pnt = GetMenuOrigin (); + + // The entire menu has to be in the clip rectangle because the + // control buttons are right-aligned and otherwise they would + // stay painted when the window gets resized. + clip = new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0); + clip = Rectangle.Union (clip, pe.ClipRectangle); + pe.SetClip (clip); + pe.Graphics.SetClip (clip); + + form.ActiveMenu.Draw (pe, new Rectangle (pnt.X, pnt.Y, form.ClientSize.Width, 0)); + } + if (HasBorders || IsMinimized && !(Form.IsMdiChild && IsMaximized)) { + // clip region is not correct on win32. + // use the entire form's area. + clip = new Rectangle (0, 0, form.Width, form.Height); + ThemeEngine.Current.DrawManagedWindowDecorations (pe.Graphics, clip, this); + } + XplatUI.PaintEventEnd (ref m, form.Handle, false); + return true; + } + + protected virtual bool HandleNCCalcSize (ref Message m) + { + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + XplatUIWin32.RECT rect; + + if (m.WParam == (IntPtr)1) { + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (m.LParam, + typeof (XplatUIWin32.NCCALCSIZE_PARAMS)); + + ncp.rgrc1 = NCCalcSize (ncp.rgrc1); + + Marshal.StructureToPtr (ncp, m.LParam, true); + } else { + rect = (XplatUIWin32.RECT) Marshal.PtrToStructure (m.LParam, typeof (XplatUIWin32.RECT)); + + rect = NCCalcSize (rect); + + Marshal.StructureToPtr (rect, m.LParam, true); + } + + return true; + } + + protected virtual XplatUIWin32.RECT NCCalcSize (XplatUIWin32.RECT proposed_window_rect) + { + int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this); + + if (HasBorders) { + proposed_window_rect.top += TitleBarHeight + bw; + proposed_window_rect.bottom -= bw; + proposed_window_rect.left += bw; + proposed_window_rect.right -= bw; + } + + if (XplatUI.RequiresPositiveClientAreaSize) { + // This is necessary for Linux, can't handle <= 0-sized + // client areas correctly. + if (proposed_window_rect.right <= proposed_window_rect.left) { + proposed_window_rect.right += proposed_window_rect.left - proposed_window_rect.right + 1; + } + if (proposed_window_rect.top >= proposed_window_rect.bottom) { + proposed_window_rect.bottom += proposed_window_rect.top - proposed_window_rect.bottom + 1; + } + } + + return proposed_window_rect; + } + + protected virtual bool HandleNCHitTest (ref Message m) + { + + int x = Widget.LowOrder ((int)m.LParam.ToInt32 ()); + int y = Widget.HighOrder ((int)m.LParam.ToInt32 ()); + + NCPointToClient (ref x, ref y); + + FormPos pos = FormPosForCoords (x, y); + + if (pos == FormPos.TitleBar) { + m.Result = new IntPtr ((int)HitTest.HTCAPTION); + return true; + } + + if (!IsSizable) + return false; + + switch (pos) { + case FormPos.Top: + m.Result = new IntPtr ((int)HitTest.HTTOP); + break; + case FormPos.Left: + m.Result = new IntPtr ((int)HitTest.HTLEFT); + break; + case FormPos.Right: + m.Result = new IntPtr ((int)HitTest.HTRIGHT); + break; + case FormPos.Bottom: + m.Result = new IntPtr ((int)HitTest.HTBOTTOM); + break; + case FormPos.TopLeft: + m.Result = new IntPtr ((int)HitTest.HTTOPLEFT); + break; + case FormPos.TopRight: + m.Result = new IntPtr ((int)HitTest.HTTOPRIGHT); + break; + case FormPos.BottomLeft: + m.Result = new IntPtr ((int)HitTest.HTBOTTOMLEFT); + break; + case FormPos.BottomRight: + m.Result = new IntPtr ((int)HitTest.HTBOTTOMRIGHT); + break; + default: + // We return false so that DefWndProc handles things + return false; + } + return true; + } + + public virtual void UpdateBorderStyle (FormBorderStyle border_style) + { + if (form.IsHandleCreated) { + XplatUI.SetBorderStyle (form.Handle, border_style); + } + + if (ShouldRemoveWindowManager (border_style)) { + form.RemoveWindowManager (); + return; + } + + ThemeEngine.Current.ManagedWindowSetButtonLocations (this); + } + + + + public virtual void SetWindowState (FormWindowState old_state, FormWindowState window_state) + { + UpdateWindowState (old_state, window_state, false); + } + + public virtual FormWindowState GetWindowState () + { + return form.window_state; + } + + public virtual void PointToClient (ref int x, ref int y) + { + // toolwindows stay in screencoords we just have to make sure + // they obey the working area + Rectangle working = SystemInformation.WorkingArea; + + if (x > working.Right) + x = working.Right; + if (x < working.Left) + x = working.Left; + + if (y < working.Top) + y = working.Top; + if (y > working.Bottom) + y = working.Bottom; + } + + public virtual void PointToScreen (ref int x, ref int y) + { + XplatUI.ClientToScreen (form.Handle, ref x, ref y); + } + + protected virtual bool ShouldRemoveWindowManager (FormBorderStyle style) + { + return style != FormBorderStyle.FixedToolWindow && style != FormBorderStyle.SizableToolWindow; + } + + public bool IconRectangleContains (int x, int y) + { + if (!ShowIcon) + return false; + + Rectangle icon = ThemeEngine.Current.ManagedWindowGetTitleBarIconArea (this); + return icon.Contains (x, y); + } + + public bool ShowIcon { + get { + if (!Form.ShowIcon) + return false; + if (!HasBorders) + return false; + if (IsMinimized) + return true; + if (IsToolWindow || Form.FormBorderStyle == FormBorderStyle.FixedDialog) + return false; + return true; + } + } + + protected virtual void Activate () + { + form.Invalidate (true); + form.Update (); + } + + public virtual bool IsActive { + get { + return true; + } + } + + + private void FormSizeChangedHandler (object sender, EventArgs e) + { + if (form.IsHandleCreated) { + ThemeEngine.Current.ManagedWindowSetButtonLocations (this); + XplatUI.InvalidateNC (form.Handle); + } + } + + protected virtual bool HandleRButtonDown (ref Message m) + { + Activate (); + return false; + } + + protected virtual bool HandleLButtonDown (ref Message m) + { + Activate (); + return false; + } + + protected virtual bool HandleLButtonDblClick(ref Message m) + { + return false; + } + + protected virtual bool HandleNCMouseLeave (ref Message m) + { + int x = Widget.LowOrder ((int)m.LParam.ToInt32 ()); + int y = Widget.HighOrder ((int)m.LParam.ToInt32 ()); + + NCPointToClient (ref x, ref y); + FormPos pos = FormPosForCoords (x, y); + + if (pos != FormPos.TitleBar) { + HandleTitleBarLeave (x, y); + return true; + } + + return true; + } + + protected virtual bool HandleNCMouseMove (ref Message m) + { + int x = Widget.LowOrder((int)m.LParam.ToInt32( )); + int y = Widget.HighOrder((int)m.LParam.ToInt32( )); + + NCPointToClient (ref x, ref y); + FormPos pos = FormPosForCoords (x, y); + + if (pos == FormPos.TitleBar) { + HandleTitleBarMouseMove (x, y); + return true; + } + + if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) { + MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y, 0); + form.ActiveMenu.OnMouseMove (form, mea); + } + + return true; + + } + + protected virtual bool HandleNCLButtonDown (ref Message m) + { + Activate (); + + start = Cursor.Position; + virtual_position = form.Bounds; + + int x = Widget.LowOrder ((int) m.LParam.ToInt32 ()); + int y = Widget.HighOrder ((int) m.LParam.ToInt32 ()); + + // Need to adjust because we are in NC land + NCPointToClient (ref x, ref y); + FormPos pos = FormPosForCoords (x, y); + + if (form.ActiveMenu != null && XplatUI.IsEnabled (form.Handle)) { + MouseEventArgs mea = new MouseEventArgs (Form.FromParamToMouseButtons (m.WParam.ToInt32 ()), form.mouse_clicks, x, y - TitleBarHeight, 0); + form.ActiveMenu.OnMouseDown (form, mea); + } + + if (pos == FormPos.TitleBar) { + HandleTitleBarDown (x, y); + return true; + } + + if (IsSizable) { + if ((pos & FormPos.AnyEdge) == 0) + return false; + + virtual_position = form.Bounds; + state = State.Sizing; + sizing_edge = pos; + form.Capture = true; + return true; + } + + return false; + } + + protected virtual void HandleNCLButtonDblClick (ref Message m) + { + int x = Widget.LowOrder ((int)m.LParam.ToInt32 ()); + int y = Widget.HighOrder ((int)m.LParam.ToInt32 ()); + + // Need to adjust because we are in NC land + NCPointToClient (ref x, ref y); + + FormPos pos = FormPosForCoords (x, y); + if (pos == FormPos.TitleBar || pos == FormPos.Top) + HandleTitleBarDoubleClick (x, y); + + } + + protected virtual void HandleTitleBarDoubleClick (int x, int y) + { + + } + + protected virtual void HandleTitleBarLeave (int x, int y) + { + title_buttons.MouseLeave (x, y); + } + + protected virtual void HandleTitleBarMouseMove (int x, int y) + { + if (title_buttons.MouseMove (x, y)) + XplatUI.InvalidateNC (form.Handle); + } + + protected virtual void HandleTitleBarUp (int x, int y) + { + title_buttons.MouseUp (x, y); + + return; + } + + protected virtual void HandleTitleBarDown (int x, int y) + { + title_buttons.MouseDown (x, y); + + if (!TitleButtons.AnyPushedTitleButtons && !IsMaximized) { + state = State.Moving; + clicked_point = new Point (x, y); + if (form.Parent != null) { + form.CaptureWithConfine (form.Parent); + } else { + form.Capture = true; + } + } + + XplatUI.InvalidateNC (form.Handle); + } + + private bool HandleMouseMove (Form form, ref Message m) + { + switch (state) { + case State.Moving: + HandleWindowMove (m); + return true; + case State.Sizing: + HandleSizing (m); + return true; + } + + return false; + } + + private void HandleMouseLeave (ref Message m) + { + form.ResetCursor (); + } + + protected virtual void HandleWindowMove (Message m) + { + Point move = MouseMove (Cursor.Position); + + UpdateVP (virtual_position.X + move.X, virtual_position.Y + move.Y, + virtual_position.Width, virtual_position.Height); + } + + private void HandleSizing (Message m) + { + Rectangle pos = virtual_position; + int mw; + int mh; + if (IsToolWindow) { + int border_width = BorderWidth; + mw = 2 * (border_width + Theme.ManagedWindowSpacingAfterLastTitleButton) + ThemeEngine.Current.ManagedWindowButtonSize (this).Width; + mh = 2 * border_width + TitleBarHeight; + } else { + Size minimum_size = SystemInformation.MinWindowTrackSize; + mw = minimum_size.Width; + mh = minimum_size.Height; + } + int x = Cursor.Position.X; + int y = Cursor.Position.Y; + + PointToClient (ref x, ref y); + + if ((sizing_edge & FormPos.Top) != 0) { + if (pos.Bottom - y < mh) + y = pos.Bottom - mh; + pos.Height = pos.Bottom - y; + pos.Y = y; + } else if ((sizing_edge & FormPos.Bottom) != 0) { + int height = y - pos.Top; + if (height <= mh) + height = mh; + pos.Height = height; + } + + if ((sizing_edge & FormPos.Left) != 0) { + if (pos.Right - x < mw) + x = pos.Right - mw; + pos.Width = pos.Right - x; + pos.X = x; + } else if ((sizing_edge & FormPos.Right) != 0) { + int width = x - form.Left; + if (width <= mw) + width = mw; + pos.Width = width; + } + + UpdateVP (pos); + } + + public bool IsMaximized { + get { return GetWindowState () == FormWindowState.Maximized; } + } + + public bool IsMinimized { + get { return GetWindowState () == FormWindowState.Minimized; } + } + + public bool IsSizable { + get { + switch (form.FormBorderStyle) { + case FormBorderStyle.Sizable: + case FormBorderStyle.SizableToolWindow: + return (form.window_state != FormWindowState.Minimized); + default: + return false; + } + } + } + + public bool HasBorders { + get { + return form.FormBorderStyle != FormBorderStyle.None; + } + } + + public bool IsToolWindow { + get { + if (form.FormBorderStyle == FormBorderStyle.SizableToolWindow || + form.FormBorderStyle == FormBorderStyle.FixedToolWindow || + form.GetCreateParams().IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) + return true; + return false; + } + } + + public int TitleBarHeight { + get { + return ThemeEngine.Current.ManagedWindowTitleBarHeight (this); + } + } + + public int BorderWidth { + get { + return ThemeEngine.Current.ManagedWindowBorderWidth (this); + } + } + + public virtual int MenuHeight { + get { + return (form.Menu != null ? ThemeEngine.Current.MenuHeight : 0); + } + } + + protected void UpdateVP (Rectangle r) + { + UpdateVP (r.X, r.Y, r.Width, r.Height); + } + + protected void UpdateVP (Point loc, int w, int h) + { + UpdateVP (loc.X, loc.Y, w, h); + } + + protected void UpdateVP (int x, int y, int w, int h) + { + virtual_position.X = x; + virtual_position.Y = y; + virtual_position.Width = w; + virtual_position.Height = h; + + DrawVirtualPosition (virtual_position); + } + + protected virtual void HandleLButtonUp (ref Message m) + { + if (state == State.Idle) + return; + + ClearVirtualPosition (); + + form.Capture = false; + if (state == State.Moving && form.Location != virtual_position.Location) + form.Location = virtual_position.Location; + else if (state == State.Sizing && form.Bounds != virtual_position) + form.Bounds = virtual_position; + state = State.Idle; + + OnWindowFinishedMoving (); + } + + private bool HandleNCLButtonUp (ref Message m) + { + if (form.Capture) { + ClearVirtualPosition (); + + form.Capture = false; + state = State.Idle; + if (form.MdiContainer != null) + form.MdiContainer.SizeScrollBars(); + } + + int x = Widget.LowOrder ((int) m.LParam.ToInt32 ()); + int y = Widget.HighOrder ((int) m.LParam.ToInt32 ()); + + NCPointToClient (ref x, ref y); + FormPos pos = FormPosForCoords (x, y); + + if (pos == FormPos.TitleBar) { + HandleTitleBarUp (x, y); + return true; + } + + return true; + } + + protected void DrawTitleButton (Graphics dc, TitleButton button, Rectangle clip) + { + if (!button.Rectangle.IntersectsWith (clip)) + return; + + ThemeEngine.Current.ManagedWindowDrawMenuButton (dc, button, clip, this); + } + + public virtual void DrawMaximizedButtons (object sender, PaintEventArgs pe) + { + } + + protected Point MouseMove (Point pos) + { + return new Point (pos.X - start.X, pos.Y - start.Y); + } + + protected virtual void DrawVirtualPosition (Rectangle virtual_position) + { + form.Bounds = virtual_position; + start = Cursor.Position; + } + + protected virtual void ClearVirtualPosition () + { + + } + + protected virtual void OnWindowFinishedMoving () + { + } + + protected virtual void NCPointToClient(ref int x, ref int y) { + form.PointToClient(ref x, ref y); + NCClientToNC (ref x, ref y); + } + + protected virtual void NCClientToNC (ref int x, ref int y) { + y += TitleBarHeight; + y += BorderWidth; + y += MenuHeight; + } + + internal Point GetMenuOrigin () + { + return new Point (BorderWidth, BorderWidth + TitleBarHeight); + } + + protected FormPos FormPosForCoords (int x, int y) + { + int bw = BorderWidth; + if (y < TitleBarHeight + bw) { + // Console.WriteLine ("A"); + if (y > bw && x > bw && + x < form.Width - bw) + return FormPos.TitleBar; + + if (x < bw || (x < 20 && y < bw)) + return FormPos.TopLeft; + + if (x > form.Width - bw || + (x > form.Width - 20 && y < bw)) + return FormPos.TopRight; + + if (y < bw) + return FormPos.Top; + + } else if (y > form.Height - 20) { + // Console.WriteLine ("B"); + if (x < bw || + (x < 20 && y > form.Height - bw)) + return FormPos.BottomLeft; + + if (x > form.Width - (bw * 2) || + (x > form.Width - 20 && + y > form.Height - bw)) + return FormPos.BottomRight; + + if (y > form.Height - (bw * 2)) + return FormPos.Bottom; + + + } else if (x < bw) { + // Console.WriteLine ("C"); + return FormPos.Left; + } else if (x > form.Width - (bw * 2)) { +// Console.WriteLine ("D"); + return FormPos.Right; + } else { + // Console.WriteLine ("E {0}", form.Width - bw); + } + + return FormPos.None; + } + } + internal class TitleButton + { + public Rectangle Rectangle; + public ButtonState State; + public CaptionButton Caption; + private EventHandler Clicked; + public bool Visible; + bool entered; + + public TitleButton (CaptionButton caption, EventHandler clicked) + { + Caption = caption; + Clicked = clicked; + } + + public void OnClick () + { + if (Clicked != null) { + Clicked (this, EventArgs.Empty); + } + } + + public bool Entered { + get { return entered; } + set { entered = value; } + } + } + + internal class TitleButtons : System.Collections.IEnumerable + { + public TitleButton MinimizeButton; + public TitleButton MaximizeButton; + public TitleButton RestoreButton; + public TitleButton CloseButton; + public TitleButton HelpButton; + + public TitleButton [] AllButtons; + public bool Visible; + + private ToolTip.ToolTipWindow tooltip; + private Timer tooltip_timer; + private TitleButton tooltip_hovered_button; + private TitleButton tooltip_hidden_button; + private const int tooltip_hide_interval = 3000; + private const int tooltip_show_interval = 1000; + private Form form; + + public TitleButtons (Form frm) + { + this.form = frm; + this.Visible = true; + + MinimizeButton = new TitleButton (CaptionButton.Minimize, new EventHandler (ClickHandler)); + MaximizeButton = new TitleButton (CaptionButton.Maximize, new EventHandler (ClickHandler)); + RestoreButton = new TitleButton (CaptionButton.Restore, new EventHandler (ClickHandler)); + CloseButton = new TitleButton (CaptionButton.Close, new EventHandler (ClickHandler)); + HelpButton = new TitleButton (CaptionButton.Help, new EventHandler (ClickHandler)); + + AllButtons = new TitleButton [] { MinimizeButton, MaximizeButton, RestoreButton, CloseButton, HelpButton }; + } + + private void ClickHandler (object sender, EventArgs e) + { + if (!Visible) { + return; + } + + TitleButton button = (TitleButton) sender; + + switch (button.Caption) { + case CaptionButton.Close: + form.Close (); + break; + case CaptionButton.Help: + Console.WriteLine ("Help not implemented."); + break; + case CaptionButton.Maximize: + form.WindowState = FormWindowState.Maximized; + break; + case CaptionButton.Minimize: + form.WindowState = FormWindowState.Minimized; + break; + case CaptionButton.Restore: + form.WindowState = FormWindowState.Normal; + break; + } + } + + public TitleButton FindButton (int x, int y) + { + if (!Visible) { + return null; + } + + foreach (TitleButton button in AllButtons) { + if (button.Visible && button.Rectangle.Contains (x, y)) { + return button; + } + } + return null; + } + + public bool AnyPushedTitleButtons { + get { + if (!Visible) { + return false; + } + + foreach (TitleButton button in AllButtons) { + if (button.Visible && button.State == ButtonState.Pushed) { + return true; + } + } + return false; + } + } + + #region IEnumerable Members + + public System.Collections.IEnumerator GetEnumerator () + { + return AllButtons.GetEnumerator (); + } + #endregion + + #region ToolTip helpers + // Called from MouseMove if mouse is over a button + public void ToolTipStart (TitleButton button) + { + tooltip_hovered_button = button; + + if (tooltip_hovered_button == tooltip_hidden_button) + return; + tooltip_hidden_button = null; + + if (tooltip != null && tooltip.Visible) + ToolTipShow (true); + + if (tooltip_timer == null) { + + tooltip_timer = new Timer (); + tooltip_timer.Tick += new EventHandler (ToolTipTimerTick); + } + + tooltip_timer.Interval = tooltip_show_interval; + tooltip_timer.Start (); + tooltip_hovered_button = button; + } + + public void ToolTipTimerTick (object sender, EventArgs e) + { + if (tooltip_timer.Interval == tooltip_hide_interval) { + tooltip_hidden_button = tooltip_hovered_button; + ToolTipHide (false); + } else { + ToolTipShow (false); + } + } + // Called from timer (with only_refresh = false) + // Called from ToolTipStart if tooltip is already shown (with only_refresh = true) + public void ToolTipShow (bool only_refresh) + { + if (!form.Visible) + return; + + string text = String.Format (tooltip_hovered_button.Caption.ToString ()); + + tooltip_timer.Interval = tooltip_hide_interval; + tooltip_timer.Enabled = true; + + if (only_refresh && (tooltip == null || !tooltip.Visible)) { + return; + } + + if (tooltip == null) + tooltip = new ToolTip.ToolTipWindow (); + else if (tooltip.Text == text && tooltip.Visible) + return; + else if (tooltip.Visible) + tooltip.Visible = false; + + if (form.WindowState == FormWindowState.Maximized && form.MdiParent != null) + tooltip.Present (form.MdiParent, text); + else + tooltip.Present (form, text); + + } + + // Called from MouseLeave (with reset_hidden_button = true) + // Called from MouseDown (with reset_hidden_button = false) + // Called from MouseMove if mouse isn't over any button (with reset_hidden_button = false) + // Called from Timer if hiding (with reset_hidden_button = false) + public void ToolTipHide (bool reset_hidden_button) + { + if (tooltip_timer != null) + tooltip_timer.Enabled = false; + if (tooltip != null && tooltip.Visible) + tooltip.Visible = false; + if (reset_hidden_button) + tooltip_hidden_button = null; + } + #endregion + + public bool MouseMove (int x, int y) + { + if (!Visible) { + return false; + } + + bool any_change = false; + bool any_pushed_buttons = AnyPushedTitleButtons; + bool any_tooltip = false; + TitleButton over_button = FindButton (x, y); + + foreach (TitleButton button in this) { + if (button == null) + continue; + + if (button.State == ButtonState.Inactive) + continue; + + if (button == over_button) { + if (any_pushed_buttons) { + any_change |= button.State != ButtonState.Pushed; + button.State = ButtonState.Pushed; + } + ToolTipStart (button); + any_tooltip = true; + if (!button.Entered) { + button.Entered = true; + if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form)) + any_change = true; + } + } else { + if (any_pushed_buttons) { + any_change |= button.State != ButtonState.Normal; + button.State = ButtonState.Normal; + } + if (button.Entered) { + button.Entered = false; + if (ThemeEngine.Current.ManagedWindowTitleButtonHasHotElementStyle (button, form)) + any_change = true; + } + } + } + + if (!any_tooltip) + ToolTipHide (false); + + return any_change; + } + + public void MouseDown (int x, int y) + { + if (!Visible) { + return; + } + + ToolTipHide (false); + + foreach (TitleButton button in this) { + if (button != null && button.State != ButtonState.Inactive) { + button.State = ButtonState.Normal; + } + } + TitleButton clicked_button = FindButton (x, y); + if (clicked_button != null && clicked_button.State != ButtonState.Inactive) { + clicked_button.State = ButtonState.Pushed; + } + } + + public void MouseUp (int x, int y) + { + if (!Visible) { + return; + } + + TitleButton clicked_button = FindButton (x, y); + if (clicked_button != null && clicked_button.State != ButtonState.Inactive) { + clicked_button.OnClick (); + } + + foreach (TitleButton button in this) { + if (button == null || button.State == ButtonState.Inactive) + continue; + + button.State = ButtonState.Normal; + } + + if (clicked_button == CloseButton && !form.closing) + XplatUI.InvalidateNC (form.Handle); + + ToolTipHide (true); + } + + internal void MouseLeave (int x, int y) + { + if (!Visible) { + return; + } + + foreach (TitleButton button in this) { + if (button == null || button.State == ButtonState.Inactive) + continue; + + button.State = ButtonState.Normal; + } + + ToolTipHide (true); + } + } +} + + diff --git a/source/ShiftUI/Internal/KeyboardHandler.cs b/source/ShiftUI/Internal/KeyboardHandler.cs new file mode 100644 index 0000000..5122e7b --- /dev/null +++ b/source/ShiftUI/Internal/KeyboardHandler.cs @@ -0,0 +1,329 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton ([email protected]) +// +// +using System; +using System.Collections; +using System.Text; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace ShiftUI.CarbonInternal { + internal class KeyboardHandler : EventHandlerBase, IEventHandler { + internal const uint kEventRawKeyDown = 1; + internal const uint kEventRawKeyRepeat = 2; + internal const uint kEventRawKeyUp = 3; + internal const uint kEventRawKeyModifiersChanged = 4; + internal const uint kEventHotKeyPressed = 5; + internal const uint kEventHotKeyReleased = 6; + + internal const uint kEventParamKeyMacCharCodes = 1801676914; + internal const uint kEventParamKeyCode = 1801678692; + internal const uint kEventParamKeyModifiers = 1802334052; + internal const uint kEventTextInputUnicodeForKeyEvent = 2; + internal const uint kEventParamTextInputSendText = 1953723512; + + internal const uint typeChar = 1413830740; + internal const uint typeUInt32 = 1835100014; + internal const uint typeUnicodeText = 1970567284; + + internal static byte [] key_filter_table; + internal static byte [] key_modifier_table; + internal static byte [] key_translation_table; + internal static byte [] char_translation_table; + + internal static bool translate_modifier = false; + + internal string ComposedString; + + static KeyboardHandler () { + // our key filter table is a 256 byte array - if the corresponding byte + // is set the key should be filtered from WM_CHAR (apple pushes unicode events + // for some keys which win32 only handles as KEYDOWN + // currently filtered: + // fn+f* == 16 + // left == 28 + // right == 29 + // up == 30 + // down == 31 + // Please update this list as well as the table as more keys are found + key_filter_table = new byte [256] { +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + // our char translation table is a set of translations from mac char codes + // to win32 vkey codes + // most things map directly + char_translation_table = new byte [256] { +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 0x25, 0x27, 0x26, 0x28, +32, 49, 34, 51, 52, 53, 55, 222, 57, 48, 56, 187, 188, 189, 190, 191, +48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 186, 60, 61, 62, 63, +50, 65, 66, 67, 68, 187, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, +80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 219, 220, 221, 54, 189, +192, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, +80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 123, 124, 125, 126, 0x2e, +128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, +144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, +160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, +176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, +192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, +208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, +224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, +240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + }; + key_translation_table = new byte [256] { +0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, +32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, +48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, +64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, +80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, +0x74, 0x75, 0x76, 0x72, 0x77, 0x78, 0x79, 103, 104, 105, 106, 107, 108, 109, 0x7a, 0x7b, +112, 113, 114, 115, 116, 117, 0x73, 119, 0x71, 121, 0x70, 123, 124, 125, 126, 127, +128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, +144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, +160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, +176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, +192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, +208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, +224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, +240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + }; + // the key modifier table is a state table of the possible modifier keys + // apple currently only goes up to 1 << 14 keys, we've extended this to 32 + // bytes as thats the size that apple uses + key_modifier_table = new byte [32]; + } + + internal KeyboardHandler (XplatUICarbon driver) : base (driver) {} + + private void ModifierToVirtualKey (int i, ref MSG msg, bool down) { + msg.hwnd = XplatUICarbon.FocusWindow; + + if (i == 9 || i == 13) { + msg.message = (down ? Msg.WM_KEYDOWN : Msg.WM_KEYUP); + msg.wParam = (IntPtr) VirtualKeys.VK_SHIFT; + msg.lParam = IntPtr.Zero; + return; + } + if (i == 12 || i == 14) { + msg.message = (down ? Msg.WM_KEYDOWN : Msg.WM_KEYUP); + msg.wParam = (IntPtr) VirtualKeys.VK_CONTROL; + msg.lParam = IntPtr.Zero; + return; + } + if (i == 8) { + msg.message = (down ? Msg.WM_SYSKEYDOWN : Msg.WM_SYSKEYUP); + msg.wParam = (IntPtr) VirtualKeys.VK_MENU; + msg.lParam = new IntPtr (0x20000000); + return; + } + + return; + } + + public void ProcessModifiers (IntPtr eventref, ref MSG msg) { + // we get notified when modifiers change, but not specifically what changed + UInt32 modifiers = 0; + + GetEventParameter (eventref, kEventParamKeyModifiers, typeUInt32, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (UInt32)), IntPtr.Zero, ref modifiers); + + for (int i = 0; i < 32; i++) { + if (key_modifier_table [i] == 0x01 && (modifiers & (1 << i)) == 0) { + ModifierToVirtualKey (i, ref msg, false); + key_modifier_table [i] = 0x00; + return; + } else if (key_modifier_table [i] == 0x00 && (modifiers & (1 << i)) == (1 << i)) { + ModifierToVirtualKey (i, ref msg, true); + key_modifier_table [i] = 0x01; + return; + } + } + + return; + } + + public void ProcessText (IntPtr eventref, ref MSG msg) { + UInt32 size = 0; + IntPtr buffer = IntPtr.Zero; + byte [] bdata; + + // get the size of the unicode buffer + GetEventParameter (eventref, kEventParamTextInputSendText, typeUnicodeText, IntPtr.Zero, 0, ref size, IntPtr.Zero); + + buffer = Marshal.AllocHGlobal ((int) size); + bdata = new byte [size]; + + // get the actual text buffer + GetEventParameter (eventref, kEventParamTextInputSendText, typeUnicodeText, IntPtr.Zero, size, IntPtr.Zero, buffer); + + Marshal.Copy (buffer, bdata, 0, (int) size); + Marshal.FreeHGlobal (buffer); + + if (key_filter_table [bdata [0]] == 0x00) { + if (size == 1) { + msg.message = Msg.WM_CHAR; + msg.wParam = BitConverter.IsLittleEndian ? (IntPtr) bdata [0] : (IntPtr) bdata [size-1]; + msg.lParam = IntPtr.Zero; + msg.hwnd = XplatUICarbon.FocusWindow; + } else { + msg.message = Msg.WM_IME_COMPOSITION; + Encoding enc = BitConverter.IsLittleEndian ? Encoding.Unicode : Encoding.BigEndianUnicode; + ComposedString = enc.GetString (bdata); + msg.hwnd = XplatUICarbon.FocusWindow; + } + } + } + + public void ProcessKeyPress (IntPtr eventref, ref MSG msg) { + byte charCode = 0x0; + byte keyCode = 0x0; + + GetEventParameter (eventref, kEventParamKeyMacCharCodes, typeChar, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (byte)), IntPtr.Zero, ref charCode); + GetEventParameter (eventref, kEventParamKeyCode, typeUInt32, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (byte)), IntPtr.Zero, ref keyCode); + + msg.lParam = (IntPtr) charCode; + msg.wParam = charCode == 0x10 ? (IntPtr) key_translation_table [keyCode] : (IntPtr) char_translation_table [charCode]; + msg.hwnd = XplatUICarbon.FocusWindow; + } + + public bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) { + uint klass = EventHandler.GetEventClass (eventref); + bool result = true; + + if (klass == EventHandler.kEventClassTextInput) { + switch (kind) { + case kEventTextInputUnicodeForKeyEvent: + ProcessText (eventref, ref msg); + break; + default: + Console.WriteLine ("WARNING: KeyboardHandler.ProcessEvent default handler for kEventClassTextInput should not be reached"); + break; + } + } else if (klass == EventHandler.kEventClassKeyboard) { + switch (kind) { + case kEventRawKeyDown: + case kEventRawKeyRepeat: + msg.message = Msg.WM_KEYDOWN; + ProcessKeyPress (eventref, ref msg); + break; + case kEventRawKeyUp: + msg.message = Msg.WM_KEYUP; + ProcessKeyPress (eventref, ref msg); + break; + case kEventRawKeyModifiersChanged: + ProcessModifiers (eventref, ref msg); + break; + default: + Console.WriteLine ("WARNING: KeyboardHandler.ProcessEvent default handler for kEventClassKeyboard should not be reached"); + break; + } + } else { + Console.WriteLine ("WARNING: KeyboardHandler.ProcessEvent default handler for kEventClassTextInput should not be reached"); + } + + return result; + } + + public bool TranslateMessage (ref MSG msg) { + bool res = false; + + if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST) + res = true; + + if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN && msg.message != Msg.WM_KEYUP && msg.message != Msg.WM_SYSKEYUP && msg.message != Msg.WM_CHAR && msg.message != Msg.WM_SYSCHAR) + return res; + + if (key_modifier_table [8] == 0x01 && key_modifier_table [12] == 0x00 && key_modifier_table [14] == 0x00) { + if (msg.message == Msg.WM_KEYDOWN) { + msg.message = Msg.WM_SYSKEYDOWN; + } else if (msg.message == Msg.WM_CHAR) { + msg.message = Msg.WM_SYSCHAR; + translate_modifier = true; + } else if (msg.message == Msg.WM_KEYUP) { + msg.message = Msg.WM_SYSKEYUP; + } else { + return res; + } + + msg.lParam = new IntPtr (0x20000000); + } else if (msg.message == Msg.WM_SYSKEYUP && translate_modifier && msg.wParam == (IntPtr) 18) { + msg.message = Msg.WM_KEYUP; + + msg.lParam = IntPtr.Zero; + translate_modifier = false; + } + + return res; + } + + internal Keys ModifierKeys { + get { + Keys keys = Keys.None; + if (key_modifier_table [9] == 0x01 || key_modifier_table [13] == 0x01) { keys |= Keys.Shift; } + if (key_modifier_table [8] == 0x01) { keys |= Keys.Alt; } + if (key_modifier_table [12] == 0x01 || key_modifier_table [14] == 0x01) { keys |= Keys.Widget; } + return keys; + } + } + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, ref UInt32 outsize, IntPtr data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, IntPtr data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref byte data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref UInt32 data); + } + + internal enum KeyboardModifiers : uint { + activeFlag = 1 << 0, + btnState = 1 << 7, + cmdKey = 1 << 8, + shiftKey = 1 << 9, + alphaLock = 1 << 10, + optionKey = 1 << 11, + WidgetKey = 1 << 12, + rightShiftKey = 1 << 13, + rightOptionKey = 1 << 14, + rightWidgetKey = 1 << 14, + } +} diff --git a/source/ShiftUI/Internal/KeysConverter.cs b/source/ShiftUI/Internal/KeysConverter.cs new file mode 100644 index 0000000..6f24eae --- /dev/null +++ b/source/ShiftUI/Internal/KeysConverter.cs @@ -0,0 +1,139 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System.Collections; +using System.ComponentModel; +using System.Text; +using System; + +namespace ShiftUI { + public class KeysConverter : TypeConverter, IComparer { + #region Public Constructors + public KeysConverter() { + } + #endregion // Public Constructors + + #region Public Instance Methods + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return false; + } + + public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof (Enum[])) + return true; + + return base.CanConvertTo (context, destinationType); + } + + public int Compare(object a, object b) { + if (a is string && b is string) { + return String.Compare((string) a, (string)b); + } + return String.Compare(a.ToString(), b.ToString()); + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { + if (value is string) { + string[] keys; + Keys key; + + keys = ((string)value).Split(new char[] {'+'}); + key = Keys.None; + + if (keys.Length > 1) { + for (int i = 0; i < keys.Length - 1; i++) { + if (keys[i].Equals("Ctrl")) { + key |= Keys.Widget; + } else { + key |= (Keys)Enum.Parse(typeof(Keys), keys[i], true); + } + } + } + if (keys [keys.Length - 1].Equals ("Ctrl")) + key |= Keys.Widget; + else + key |= (Keys)Enum.Parse(typeof(Keys), keys[keys.Length - 1], true); + return key; + } + return base.ConvertFrom (context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { + if (destinationType == typeof(string)) { + StringBuilder sb; + Keys key; + + sb = new StringBuilder(); + key = (Keys)value; + + // Modifiers first + if ((key & Keys.Widget) != 0) { + sb.Append("Ctrl+"); + } + + if ((key & Keys.Alt) != 0) { + sb.Append("Alt+"); + } + + if ((key & Keys.Shift) != 0) { + sb.Append("Shift+"); + } + + // Keycode last + sb.Append(Enum.GetName(typeof(Keys), key & Keys.KeyCode)); + + return sb.ToString(); + } + return base.ConvertTo (context, culture, value, destinationType); + } + + public override TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) { + Keys [] stdVal = new Keys [] { Keys.D0, Keys.D1, Keys.D2, Keys.D3, Keys.D4, Keys.D5, Keys.D6, Keys.D7, + Keys.D8, Keys.D9, Keys.Alt, Keys.Back, Keys.Widget, Keys.Delete, Keys.End, Keys.Return, Keys.F1, + Keys.F10, Keys.F11, Keys.F12, Keys.F2, Keys.F3, Keys.F4, Keys.F5, Keys.F6, Keys.F7, Keys.F8, Keys.F9, + Keys.Home, Keys.Insert, Keys.Next, Keys.PageUp, Keys.Shift }; + + return new TypeConverter.StandardValuesCollection (stdVal); + } + + public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) { + + return false; + } + + public override bool GetStandardValuesSupported(ITypeDescriptorContext context) { + return true; + } + + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/LayoutEngine.cs b/source/ShiftUI/Internal/LayoutEngine.cs new file mode 100644 index 0000000..7b2ce71 --- /dev/null +++ b/source/ShiftUI/Internal/LayoutEngine.cs @@ -0,0 +1,43 @@ +// 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. +// +// +// Author: +// Miguel de Icaza ([email protected]) +// +// (C) 2004 Novell, Inc. +// + +using System; +using System.ComponentModel; + +namespace ShiftUI.Layout { + + public abstract class LayoutEngine { + + public virtual void InitLayout (object child, BoundsSpecified specified) + { + } + + public virtual bool Layout (object container, LayoutEventArgs layoutEventArgs) + { + return false; + } + } +} diff --git a/source/ShiftUI/Internal/LayoutSettings.cs b/source/ShiftUI/Internal/LayoutSettings.cs new file mode 100644 index 0000000..a97d744 --- /dev/null +++ b/source/ShiftUI/Internal/LayoutSettings.cs @@ -0,0 +1,40 @@ +// 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. +// +// +// Author: +// Miguel de Icaza ([email protected]) +// +// (C) 2004 Novell, Inc. +// + +using System; +using System.ComponentModel; +using ShiftUI.Layout; + +namespace ShiftUI { + + public abstract class LayoutSettings { + public virtual LayoutEngine LayoutEngine { + get { + return null; + } + } + } +} diff --git a/source/ShiftUI/Internal/Line.cs b/source/ShiftUI/Internal/Line.cs new file mode 100644 index 0000000..a523047 --- /dev/null +++ b/source/ShiftUI/Internal/Line.cs @@ -0,0 +1,811 @@ +// 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. (http://www.novell.com) +// +// Authors: +// Peter Bartok [email protected] +// +// + +using System; +using System.Collections; +using System.Drawing; +using System.Drawing.Text; +using System.Text; + +namespace ShiftUI +{ + internal class Line : ICloneable, IComparable + { + #region Local Variables + + internal Document document; + // Stuff that matters for our line + internal StringBuilder text; // Characters for the line + internal float[] widths; // Width of each character; always one larger than text.Length + internal int space; // Number of elements in text and widths + internal int line_no; // Line number + internal LineTag tags; // Tags describing the text + internal int offset; // Baseline can be on the X or Y axis depending if we are in multiline mode or not + internal int height; // Height of the line (height of tallest tag) + internal int ascent; // Ascent of the line (ascent of the tallest tag) + internal HorizontalAlignment alignment; // Alignment of the line + internal int align_shift; // Pixel shift caused by the alignment + internal int indent; // Left indent for the first line + internal int hanging_indent; // Hanging indent (left indent for all but the first line) + internal int right_indent; // Right indent for all lines + internal LineEnding ending; + + // Stuff that's important for the tree + internal Line parent; // Our parent line + internal Line left; // Line with smaller line number + internal Line right; // Line with higher line number + internal LineColor color; // We're doing a black/red tree. this is the node color + static int DEFAULT_TEXT_LEN = 0; // + internal bool recalc; // Line changed + + private static Hashtable kerning_fonts = new Hashtable (); // record which fonts use kerning + #endregion // Local Variables + + #region Constructors + internal Line (Document document, LineEnding ending) + { + this.document = document; + color = LineColor.Red; + left = null; + right = null; + parent = null; + text = null; + recalc = true; + alignment = document.alignment; + + this.ending = ending; + } + + internal Line (Document document, int LineNo, string Text, Font font, Color color, LineEnding ending) : this (document, ending) + { + space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN; + + text = new StringBuilder (Text, space); + line_no = LineNo; + this.ending = ending; + + widths = new float[space + 1]; + + + tags = new LineTag(this, 1); + tags.Font = font; + tags.Color = color; + } + + internal Line (Document document, int LineNo, string Text, HorizontalAlignment align, Font font, Color color, LineEnding ending) : this(document, ending) + { + space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN; + + text = new StringBuilder (Text, space); + line_no = LineNo; + this.ending = ending; + alignment = align; + + widths = new float[space + 1]; + + + tags = new LineTag(this, 1); + tags.Font = font; + tags.Color = color; + } + + internal Line (Document document, int LineNo, string Text, LineTag tag, LineEnding ending) : this(document, ending) + { + space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN; + + text = new StringBuilder (Text, space); + this.ending = ending; + line_no = LineNo; + + widths = new float[space + 1]; + tags = tag; + } + + #endregion // Constructors + + #region Internal Properties + internal HorizontalAlignment Alignment { + get { return alignment; } + set { + if (alignment != value) { + alignment = value; + recalc = true; + } + } + } + + internal int HangingIndent { + get { return hanging_indent; } + set { + hanging_indent = value; + recalc = true; + } + } + + // UIA: Method used via reflection in TextRangeProvider + internal int Height { + get { return height; } + set { height = value; } + } + + internal int Indent { + get { return indent; } + set { + indent = value; + recalc = true; + } + } + + internal int LineNo { + get { return line_no; } + set { line_no = value; } + } + + internal int RightIndent { + get { return right_indent; } + set { + right_indent = value; + recalc = true; + } + } + + // UIA: Method used via reflection in TextRangeProvider + internal int Width { + get { + int res = (int) widths [text.Length]; + return res; + } + } + + internal string Text { + get { return text.ToString(); } + set { + int prev_length = text.Length; + text = new StringBuilder(value, value.Length > DEFAULT_TEXT_LEN ? value.Length + 1 : DEFAULT_TEXT_LEN); + + if (text.Length > prev_length) + Grow (text.Length - prev_length); + } + } + + // UIA: Method used via reflection in TextRangeProvider + internal int X { + get { + if (document.multiline) + return align_shift; + return offset + align_shift; + } + } + + // UIA: Method used via reflection in TextRangeProvider + internal int Y { + get { + if (!document.multiline) + return document.top_margin; + return document.top_margin + offset; + } + } + #endregion // Internal Properties + + #region Internal Methods + + /// <summary> + /// Builds a simple code to record which tags are links and how many tags + /// used to compare lines before and after to see if the scan for links + /// process has changed anything. + /// </summary> + internal void LinkRecord (StringBuilder linkRecord) + { + LineTag tag = tags; + + while (tag != null) { + if (tag.IsLink) + linkRecord.Append ("L"); + else + linkRecord.Append ("N"); + + tag = tag.Next; + } + } + + /// <summary> + /// Clears all link properties from tags + /// </summary> + internal void ClearLinks () + { + LineTag tag = tags; + + while (tag != null) { + tag.IsLink = false; + tag = tag.Next; + } + } + + public void DeleteCharacters(int pos, int count) + { + LineTag tag; + bool streamline = false; + + // Can't delete more than the line has + if (pos >= text.Length) + return; + + // Find the first tag that we are deleting from + tag = FindTag (pos + 1); + + // Remove the characters from the line + text.Remove (pos, count); + + if (tag == null) + return; + + // Check if we're crossing tag boundaries + if ((pos + count) > (tag.Start + tag.Length - 1)) { + int left; + + // We have to delete cross tag boundaries + streamline = true; + left = count; + + left -= tag.Start + tag.Length - pos - 1; + tag = tag.Next; + + // Update the start of each tag + while ((tag != null) && (left > 0)) { + // Cache tag.Length as is will be indireclty modified + // by changes to tag.Start + int tag_length = tag.Length; + tag.Start -= count - left; + + if (tag_length > left) { + left = 0; + } else { + left -= tag_length; + tag = tag.Next; + } + + } + } else { + // We got off easy, same tag + + if (tag.Length == 0) + streamline = true; + } + + // Delete empty orphaned tags at the end + LineTag walk = tag; + while (walk != null && walk.Next != null && walk.Next.Length == 0) { + LineTag t = walk; + walk.Next = walk.Next.Next; + if (walk.Next != null) + walk.Next.Previous = t; + walk = walk.Next; + } + + // Adjust the start point of any tags following + if (tag != null) { + tag = tag.Next; + while (tag != null) { + tag.Start -= count; + tag = tag.Next; + } + } + + recalc = true; + + if (streamline) + Streamline (document.Lines); + } + + // This doesn't do exactly what you would think, it just pulls off the \n part of the ending + internal void DrawEnding (Graphics dc, float y) + { + if (document.multiline) + return; + LineTag last = tags; + while (last.Next != null) + last = last.Next; + + string end_str = null; + switch (document.LineEndingLength (ending)) { + case 0: + return; + case 1: + end_str = "\u0013"; + break; + case 2: + end_str = "\u0013\u0013"; + break; + case 3: + end_str = "\u0013\u0013\u0013"; + break; + } + + TextBoxTextRenderer.DrawText (dc, end_str, last.Font, last.Color, X + widths [TextLengthWithoutEnding ()] - document.viewport_x + document.OffsetX, y, true); + } + + /// <summary> Find the tag on a line based on the character position, pos is 0-based</summary> + internal LineTag FindTag (int pos) + { + LineTag tag; + + if (pos == 0) + return tags; + + tag = this.tags; + + if (pos >= text.Length) + pos = text.Length - 1; + + while (tag != null) { + if (((tag.Start - 1) <= pos) && (pos <= (tag.Start + tag.Length - 1))) + return LineTag.GetFinalTag (tag); + + tag = tag.Next; + } + + return null; + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + + // Get the tag that contains this x coordinate + public LineTag GetTag (int x) + { + LineTag tag = tags; + + // Coord is to the left of the first character + if (x < tag.X) + return LineTag.GetFinalTag (tag); + + // All we have is a linked-list of tags, so we have + // to do a linear search. But there shouldn't be + // too many tags per line in general. + while (true) { + if (x >= tag.X && x < (tag.X + tag.Width)) + return tag; + + if (tag.Next != null) + tag = tag.Next; + else + return LineTag.GetFinalTag (tag); + } + } + + // Make sure we always have enoughs space in text and widths + internal void Grow (int minimum) + { + int length; + float[] new_widths; + + length = text.Length; + + if ((length + minimum) > space) { + // We need to grow; double the size + + if ((length + minimum) > (space * 2)) { + new_widths = new float[length + minimum * 2 + 1]; + space = length + minimum * 2; + } else { + new_widths = new float[space * 2 + 1]; + space *= 2; + } + widths.CopyTo (new_widths, 0); + + widths = new_widths; + } + } + public void InsertString (int pos, string s) + { + InsertString (pos, s, FindTag (pos)); + } + + // Inserts a string at the given position + public void InsertString (int pos, string s, LineTag tag) + { + int len = s.Length; + + // Insert the text into the StringBuilder + text.Insert (pos, s); + + // Update the start position of every tag after this one + tag = tag.Next; + + while (tag != null) { + tag.Start += len; + tag = tag.Next; + } + + // Make sure we have room in the widths array + Grow (len); + + // This line needs to be recalculated + recalc = true; + } + + /// <summary> + /// Go through all tags on a line and recalculate all size-related values; + /// returns true if lineheight changed + /// </summary> + internal bool RecalculateLine (Graphics g, Document doc) + { + return RecalculateLine (g, doc, kerning_fonts.ContainsKey (tags.Font.GetHashCode ())); + } + + private bool RecalculateLine (Graphics g, Document doc, bool handleKerning) + { + LineTag tag; + int pos; + int len; + SizeF size; + float w; + int prev_offset; + bool retval; + bool wrapped; + Line line; + int wrap_pos; + int prev_height; + int prev_ascent; + + pos = 0; + len = this.text.Length; + tag = this.tags; + prev_offset = this.offset; // For drawing optimization calculations + prev_height = this.height; + prev_ascent = this.ascent; + this.height = 0; // Reset line height + this.ascent = 0; // Reset the ascent for the line + tag.Shift = 0; // Reset shift (which should be stored as pixels, not as points) + + if (ending == LineEnding.Wrap) + widths[0] = document.left_margin + hanging_indent; + else + widths[0] = document.left_margin + indent; + + this.recalc = false; + retval = false; + wrapped = false; + + wrap_pos = 0; + + while (pos < len) { + + while (tag.Length == 0) { // We should always have tags after a tag.length==0 unless len==0 + //tag.Ascent = 0; + tag.Shift = (tag.Line.ascent - tag.Ascent) / 72; + tag = tag.Next; + } + + // kerning is a problem. The original code in this method assumed that the + // width of a string equals the sum of the widths of its characters. This is + // not true when kerning takes place during the display process. Since it's + // impossible to find out easily whether a font does kerning, and with which + // characters, we just detect that kerning must have happened and use a slower + // (but accurate) measurement for those fonts henceforth. Without handling + // kerning, many fonts for English become unreadable during typing for many + // input strings, and text in many other languages is even worse trying to + // type in TextBoxes. + // See https://bugzilla.xamarin.com/show_bug.cgi?id=26478 for details. + float newWidth; + if (handleKerning && !Char.IsWhiteSpace(text[pos])) + { + // MeasureText doesn't measure trailing spaces, so we do the best we can for those + // in the else branch. + size = TextBoxTextRenderer.MeasureText (g, text.ToString (0, pos + 1), tag.Font); + newWidth = widths[0] + size.Width; + } + else + { + size = tag.SizeOfPosition (g, pos); + w = size.Width; + newWidth = widths[pos] + w; + } + + if (Char.IsWhiteSpace (text[pos])) + wrap_pos = pos + 1; + + if (doc.wrap) { + if ((wrap_pos > 0) && (wrap_pos != len) && (newWidth + 5) > (doc.viewport_width - this.right_indent)) { + // Make sure to set the last width of the line before wrapping + widths[pos + 1] = newWidth; + + pos = wrap_pos; + len = text.Length; + doc.Split (this, tag, pos); + ending = LineEnding.Wrap; + len = this.text.Length; + + retval = true; + wrapped = true; + } else if (pos > 1 && newWidth > (doc.viewport_width - this.right_indent)) { + // No suitable wrap position was found so break right in the middle of a word + + // Make sure to set the last width of the line before wrapping + widths[pos + 1] = newWidth; + + doc.Split (this, tag, pos); + ending = LineEnding.Wrap; + len = this.text.Length; + retval = true; + wrapped = true; + } + } + + // Contract all wrapped lines that follow back into our line + if (!wrapped) { + pos++; + + widths[pos] = newWidth; + + if (pos == len) { + line = doc.GetLine (this.line_no + 1); + if ((line != null) && (ending == LineEnding.Wrap || ending == LineEnding.None)) { + // Pull the two lines together + doc.Combine (this.line_no, this.line_no + 1); + len = this.text.Length; + retval = true; + } + } + } + + if (pos == (tag.Start - 1 + tag.Length)) { + // We just found the end of our current tag + tag.Height = tag.MaxHeight (); + + // Check if we're the tallest on the line (so far) + if (tag.Height > this.height) + this.height = tag.Height; // Yep; make sure the line knows + + if (tag.Ascent > this.ascent) { + LineTag t; + + // We have a tag that has a taller ascent than the line; + t = tags; + while (t != null && t != tag) { + t.Shift = (tag.Ascent - t.Ascent) / 72; + t = t.Next; + } + + // Save on our line + this.ascent = tag.Ascent; + } else { + tag.Shift = (this.ascent - tag.Ascent) / 72; + } + + tag = tag.Next; + if (tag != null) { + tag.Shift = 0; + wrap_pos = pos; + } + } + } + + var fullText = text.ToString(); + if (!handleKerning && fullText.Length > 1 && !wrapped) + { + // Check whether kerning takes place for this string and font. + var realSize = TextBoxTextRenderer.MeasureText(g, fullText, tags.Font); + float realWidth = realSize.Width + widths[0]; + // MeasureText ignores trailing whitespace, so we will too at this point. + int length = fullText.TrimEnd().Length; + float sumWidth = widths[length]; + if (realWidth != sumWidth) + { + kerning_fonts.Add(tags.Font.GetHashCode (), true); + // Using a slightly incorrect width this time around isn't that bad. All that happens + // is that the cursor is a pixel or two off until the next character is typed. It's + // the accumulation of pixel after pixel that causes display problems. + } + } + + while (tag != null) { + tag.Shift = (tag.Line.ascent - tag.Ascent) / 72; + tag = tag.Next; + } + + if (this.height == 0) { + this.height = tags.Font.Height; + tags.Height = this.height; + tags.Shift = 0; + } + + if (prev_offset != offset || prev_height != this.height || prev_ascent != this.ascent) + retval = true; + + return retval; + } + + /// <summary> + /// Recalculate a single line using the same char for every character in the line + /// </summary> + internal bool RecalculatePasswordLine (Graphics g, Document doc) + { + LineTag tag; + int pos; + int len; + float w; + bool ret; + + pos = 0; + len = this.text.Length; + tag = this.tags; + ascent = 0; + tag.Shift = 0; + + this.recalc = false; + widths[0] = document.left_margin + indent; + + w = TextBoxTextRenderer.MeasureText (g, doc.password_char, tags.Font).Width; + + if (this.height != (int)tag.Font.Height) + ret = true; + else + ret = false; + + this.height = (int)tag.Font.Height; + tag.Height = this.height; + + this.ascent = tag.Ascent; + + while (pos < len) { + pos++; + widths[pos] = widths[pos - 1] + w; + } + + return ret; + } + + internal void Streamline (int lines) + { + LineTag current; + LineTag next; + + current = this.tags; + next = current.Next; + + // + // Catch what the loop below wont; eliminate 0 length + // tags, but only if there are other tags after us + // We only eliminate text tags if there is another text tag + // after it. Otherwise we wind up trying to type on picture tags + // + while ((current.Length == 0) && (next != null) && (next.IsTextTag)) { + tags = next; + tags.Previous = null; + current = next; + next = current.Next; + } + + + if (next == null) + return; + + while (next != null) { + // Take out 0 length tags unless it's the last tag in the document + if (current.IsTextTag && next.Length == 0 && next.IsTextTag) { + if ((next.Next != null) || (line_no != lines)) { + current.Next = next.Next; + if (current.Next != null) { + current.Next.Previous = current; + } + next = current.Next; + continue; + } + } + + if (current.Combine (next)) { + next = current.Next; + continue; + } + + current = current.Next; + next = current.Next; + } + } + + internal int TextLengthWithoutEnding () + { + return text.Length - document.LineEndingLength (ending); + } + + internal string TextWithoutEnding () + { + return text.ToString (0, text.Length - document.LineEndingLength (ending)); + } + #endregion // Internal Methods + + #region Administrative + public object Clone () + { + Line clone; + + clone = new Line (document, ending); + + clone.text = text; + + if (left != null) + clone.left = (Line)left.Clone(); + + if (left != null) + clone.left = (Line)left.Clone(); + + return clone; + } + + internal object CloneLine () + { + Line clone; + + clone = new Line (document, ending); + + clone.text = text; + + return clone; + } + + public int CompareTo (object obj) + { + if (obj == null) + return 1; + + if (! (obj is Line)) + throw new ArgumentException("Object is not of type Line", "obj"); + + if (line_no < ((Line)obj).line_no) + return -1; + else if (line_no > ((Line)obj).line_no) + return 1; + else + return 0; + } + + public override bool Equals (object obj) + { + if (obj == null) + return false; + + if (!(obj is Line)) + return false; + + if (obj == this) + return true; + + if (line_no == ((Line)obj).line_no) + return true; + + return false; + } + + public override string ToString() + { + return string.Format ("Line {0}", line_no); + } + #endregion // Administrative + } +} diff --git a/source/ShiftUI/Internal/LineTag.cs b/source/ShiftUI/Internal/LineTag.cs new file mode 100644 index 0000000..e34132c --- /dev/null +++ b/source/ShiftUI/Internal/LineTag.cs @@ -0,0 +1,618 @@ +// 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. (http://www.novell.com) +// +// Authors: +// Peter Bartok [email protected] +// +// + +using System; +using System.Collections; +using System.Drawing; +using System.Drawing.Text; +using System.Text; + +namespace ShiftUI +{ + internal class LineTag + { + #region Local Variables + // Formatting + private Font font; // System.Drawing.Font object for this tag + private Color color; // The font color for this tag + private Color back_color; // In 2.0 tags can have background colours. + private Font link_font; // Cached font used for link if IsLink + private bool is_link; // Whether this tag is a link + private string link_text; // The full link text e.g. this might be + // word-wrapped to "w" but this would be + // "www.mono-project.com" + + // Payload; text + private int start; // start, in chars; index into Line.text + // 1 based!! + + // Drawing support + private int height; // Height in pixels of the text this tag describes + private int ascent; // Ascent of the font for this tag + private int descent; // Descent of the font for this tag + private int shift; // Shift down for this tag, to stay on baseline + + // Administrative + private Line line; // The line we're on + private LineTag next; // Next tag on the same line + private LineTag previous; // Previous tag on the same line + #endregion + + #region Constructors + public LineTag (Line line, int start) + { + this.line = line; + Start = start; + link_font = null; + is_link = false; + link_text = null; + } + #endregion // Constructors + + #region Public Properties + public int Ascent { + get { return ascent; } + } + + public Color BackColor { + get { return back_color; } + set { back_color = value; } + } + + public Color ColorToDisplay { + get { + if (IsLink == true) + return Color.Blue; + + return color; + } + } + + public Color Color { + get { return color; } + set { color = value; } + } + + public int Descent { + get { return descent; } + } + + public int End { + get { return start + Length; } + } + + public Font FontToDisplay { + get { + if (IsLink) { + if (link_font == null) + link_font = new Font (font.FontFamily, font.Size, font.Style | FontStyle.Underline); + + return link_font; + } + + return font; + } + } + + public Font Font { + get { return font; } + set { + if (font != value) { + link_font = null; + font = value; + + height = Font.Height; + XplatUI.GetFontMetrics (Hwnd.GraphicsContext, Font, out ascent, out descent); + line.recalc = true; + } + } + } + + public int Height { + get { return height; } + set { height = value; } + } + + public virtual bool IsTextTag { + get { return true; } + } + + public int Length { + get { + int res = 0; + if (next != null) + res = next.start - start; + else + res = line.text.Length - (start - 1); + + return res > 0 ? res : 0; + } + } + + public Line Line { + get { return line; } + set { line = value; } + } + + public LineTag Next { + get { return next; } + set { next = value; } + } + + public LineTag Previous { + get { return previous; } + set { previous = value; } + } + + public int Shift { + get { return shift; } + set { shift = value; } + } + + public int Start { + get { return start; } + set { +#if DEBUG + if (value <= 0) + throw new Exception("Start of tag must be 1 or higher!"); + + if (this.Previous != null) { + if (this.Previous.Start == value) + System.Console.Write("Creating empty tag"); + if (this.Previous.Start > value) + throw new Exception("New tag makes an insane tag"); + } +#endif + start = value; + } + } + + public int TextEnd { + get { return start + TextLength; } + } + + public int TextLength { + get { + int res = 0; + if (next != null) + res = next.start - start; + else + res = line.TextLengthWithoutEnding () - (start - 1); + + return res > 0 ? res : 0; + } + } + + public float Width { + get { + if (Length == 0) + return 0; + return line.widths [start + Length - 1] - (start != 0 ? line.widths [start - 1] : 0); + } + } + + public float X { + get { + if (start == 0) + return line.X; + return line.X + line.widths [start - 1]; + } + } + + public bool IsLink { + get { return is_link; } + set { is_link = value; } + } + + public string LinkText { + get { return link_text; } + set { link_text = value; } + } + #endregion + + #region Public Methods + ///<summary>Break a tag into two with identical attributes; pos is 1-based; returns tag starting at >pos< or null if end-of-line</summary> + public LineTag Break (int pos) + { + LineTag new_tag; + +#if DEBUG + // Sanity + if (pos < this.Start) + throw new Exception ("Breaking at a negative point"); +#endif + +#if DEBUG + if (pos > End) + throw new Exception ("Breaking past the end of a line"); +#endif + + new_tag = new LineTag(line, pos); + new_tag.CopyFormattingFrom (this); + + new_tag.next = this.next; + this.next = new_tag; + new_tag.previous = this; + + if (new_tag.next != null) + new_tag.next.previous = new_tag; + + return new_tag; + } + + /// <summary>Combines 'this' tag with 'other' tag</summary> + public bool Combine (LineTag other) + { + if (!this.Equals (other)) + return false; + + this.next = other.next; + + if (this.next != null) + this.next.previous = this; + + return true; + } + + public void CopyFormattingFrom (LineTag other) + { + Font = other.font; + color = other.color; + back_color = other.back_color; + } + + public void Delete () + { + // If we are the only tag, we can't be deleted + if (previous == null && next == null) + return; + + // If we are the last tag, deletion is easy + if (next == null) { + previous.next = null; + return; + } + + // Easy cases gone, little tougher, delete ourself + // Update links, and start + next.previous = null; + + LineTag loop = next; + + while (loop != null) { + loop.Start -= Length; + loop = loop.next; + } + + return; + } + + public virtual void Draw (Graphics dc, Color color, float x, float y, int start, int end) + { + TextBoxTextRenderer.DrawText (dc, line.text.ToString (start, end).Replace ("\r", string.Empty), FontToDisplay, color, x, y, false); + } + + public virtual void Draw (Graphics dc, Color color, float xoff, float y, int start, int end, string text) + { + Rectangle measured_text; + Draw (dc, color, xoff, y, start, end, text, out measured_text, false); + } + + /// <summary> + /// + /// </summary> + /// <param name="drawStart">0 based start index</param> + public virtual void Draw (Graphics dc, Color color, float xoff, float y, int drawStart, int drawEnd, + string text, out Rectangle measuredText, bool measureText) + { + if (measureText) { + int xstart = (int)line.widths [drawStart] + (int)xoff; + int xend = (int)line.widths [drawEnd] - (int)line.widths [drawStart]; + int ystart = (int)y; + int yend = (int)TextBoxTextRenderer.MeasureText (dc, Text (), FontToDisplay).Height; + + measuredText = new Rectangle (xstart, ystart, xend, yend); + } else { + measuredText = new Rectangle (); + } + + while (drawStart < drawEnd) { + int tab_index = text.IndexOf ("\t", drawStart); + + if (tab_index == -1) + tab_index = drawEnd; + + TextBoxTextRenderer.DrawText (dc, text.Substring (drawStart, tab_index - drawStart).Replace ("\r", string.Empty), FontToDisplay, color, xoff + line.widths [drawStart], y, false); + + // non multilines get the unknown char + if (!line.document.multiline && tab_index != drawEnd) + TextBoxTextRenderer.DrawText (dc, "\u0013", FontToDisplay, color, xoff + line.widths [tab_index], y, true); + + drawStart = tab_index + 1; + } + } + + /// <summary>Checks if 'this' tag describes the same formatting options as 'obj'</summary> + public override bool Equals (object obj) + { + LineTag other; + + if (obj == null) + return false; + + if (!(obj is LineTag)) + return false; + + if (obj == this) + return true; + + other = (LineTag)obj; + + if (other.IsTextTag != IsTextTag) + return false; + + if (this.IsLink != other.IsLink) + return false; + + if (this.LinkText != other.LinkText) + return false; + + if (this.font.Equals (other.font) && this.color.Equals (other.color)) + return true; + + return false; + } + + /// <summary>Finds the tag that describes the character at position 'pos' (0 based) on 'line'</summary> + public static LineTag FindTag (Line line, int pos) + { + LineTag tag = line.tags; + + // Beginning of line is a bit special + if (pos == 0) + return tag; // Not sure if we should get the final tag here + + while (tag != null) { + // [H e][l][l o _ W][o r] Text + // [1 2][3][4 5 6 7][8 9] Start + // 3 4 8 10 End + // 0 1 2 3 4 5 6 7 8 9 Pos + if ((tag.start <= pos) && (pos < tag.End)) + return GetFinalTag (tag); + + tag = tag.next; + } + + return null; + } + + /// <summary>Applies 'font' and 'brush' to characters starting at 'start' for 'length' chars; + /// Removes any previous tags overlapping the same area; + /// returns true if lineheight has changed</summary> + /// <param name="formatStart">1-based character position on line</param> + public static bool FormatText (Line line, int formatStart, int length, Font font, Color color, Color backColor, FormatSpecified specified) + { + LineTag tag; + LineTag start_tag; + LineTag end_tag; + int end; + bool retval = false; // Assume line-height doesn't change + + // Too simple? + if (((FormatSpecified.Font & specified) == FormatSpecified.Font) && font.Height != line.height) + retval = true; + + line.recalc = true; // This forces recalculation of the line in RecalculateDocument + + // A little sanity, not sure if it's needed, might be able to remove for speed + if (length > line.text.Length) + length = line.text.Length; + + tag = line.tags; + end = formatStart + length; + + // Common special case + if ((formatStart == 1) && (length == tag.Length)) { + SetFormat (tag, font, color, backColor, specified); + return retval; + } + + // empty selection style at begining of line means + // we only need one new tag + if (formatStart == 1 && length == 0) { + line.tags.Break (1); + SetFormat (line.tags, font, color, backColor, specified); + return retval; + } + + start_tag = FindTag (line, formatStart - 1); + + // we are at an empty tag already! + // e.g. [Tag 0 - "He"][Tag 1 = 0 length][Tag 2 "llo world"] + // Find Tag will return tag 0 at position 3, but we should just + // use the empty tag after.. + if (start_tag.End == formatStart && length == 0 && start_tag.Next != null && start_tag.Next.Length == 0) { + SetFormat (start_tag.Next, font, color, backColor, specified); + return retval; + } + + // if we are at the end of a tag, we want to move to the next tag + while (start_tag.End == formatStart && start_tag.Next != null) + start_tag = start_tag.Next; + + tag = start_tag.Break (formatStart); + + // empty selection style at end of line - its the only situation + // where the rest of the tag would be empty, since we moved to the + // begining of next non empty tag + if (tag.Length == 0) { + SetFormat (tag, font, color, backColor, specified); + return retval; + } + + // empty - so we just create another tag for + // after our new (now) empty one.. + if (length == 0) { + tag.Break (formatStart); + SetFormat (tag, font, color, backColor, specified); + return retval; + } + + while (tag != null && tag.End <= end) { + SetFormat (tag, font, color, backColor, specified); + tag = tag.next; + } + + // did the last tag conveniently fit? + if (tag != null && tag.End == end) + return retval; + + /// Now do the last tag + end_tag = FindTag (line, end-1); + + if (end_tag != null) { + end_tag.Break (end); + SetFormat (end_tag, font, color, backColor, specified); + } + + return retval; + } + + // Gets the character at the x-coordinate. Index is based from the + // line, not the start of the tag. + // returns 0 based index (0 means before character at 1, 1 means at character 1) + public int GetCharIndex (int x) + { + int low = start; + int high = low + Length; + int length_no_ending = line.TextLengthWithoutEnding (); + + if (Length == 0) + return low-1; + + if (length_no_ending == 0) + return 0; + + if (x < line.widths [low]) { + if (low == 1 && x > (line.widths [1] / 2)) + return low; + return low - 1; + } + + if (x > line.widths[length_no_ending]) + return length_no_ending; + + while (low < high - 1) { + int mid = (high + low) / 2; + float width = line.widths[mid]; + + if (width < x) + low = mid; + else + high = mid; + } + + float char_width = line.widths[high] - line.widths[low]; + + if ((x - line.widths[low]) >= (char_width / 2)) + return high; + else + return low; + } + + // There can be multiple tags at the same position, we want to make + // sure we are using the very last tag at the given position + // Empty tags are necessary if style is set at a position with + // no length. + public static LineTag GetFinalTag (LineTag tag) + { + LineTag res = tag; + + while (res.Length == 0 && res.next != null && res.next.Length == 0) + res = res.next; + + return res; + } + + public override int GetHashCode () + { + return base.GetHashCode (); + } + + internal virtual int MaxHeight () + { + return font.Height; + } + + private static void SetFormat (LineTag tag, Font font, Color color, Color back_color, FormatSpecified specified) + { + if ((FormatSpecified.Font & specified) == FormatSpecified.Font) { + tag.Font = font; + } + if ((FormatSpecified.Color & specified) == FormatSpecified.Color) + tag.color = color; + if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor) { + tag.back_color = back_color; + } + // Console.WriteLine ("setting format: {0} {1} new color {2}", color.Color, specified, tag.color.Color); + } + + public virtual SizeF SizeOfPosition (Graphics dc, int pos) + { + if (pos >= line.TextLengthWithoutEnding () && line.document.multiline) + return SizeF.Empty; + + string text = line.text.ToString (pos, 1); + switch ((int) text [0]) { + case '\t': + if (!line.document.multiline) + goto case 10; + SizeF res = TextBoxTextRenderer.MeasureText (dc, " ", font); + res.Width *= 8.0F; + return res; + case 10: + case 13: + return TextBoxTextRenderer.MeasureText (dc, "\u000D", font); + } + + return TextBoxTextRenderer.MeasureText (dc, text, font); + } + + public virtual string Text () + { + return line.text.ToString (start - 1, Length); + } + + public override string ToString () + { + if (Length > 0) + return string.Format ("{0} Tag starts at index: {1}, length: {2}, text: {3}, font: {4}", GetType (), start, Length, Text (), font.ToString ()); + + return string.Format ("Zero Length tag at index: {0}", start); + } + #endregion // Internal Methods + } +} diff --git a/source/ShiftUI/Internal/ListBindingConverter.cs b/source/ShiftUI/Internal/ListBindingConverter.cs new file mode 100644 index 0000000..46f39e5 --- /dev/null +++ b/source/ShiftUI/Internal/ListBindingConverter.cs @@ -0,0 +1,60 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// NOT COMPLETE + +using System.ComponentModel; +using System; + +namespace ShiftUI { + public class ListBindingConverter : TypeConverter { + #region Public Constructors + public ListBindingConverter() { + } + #endregion // Public Constructors + + #region Public Instance Methods + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(string)) { + return true; + } + return base.CanConvertTo (context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { + return base.ConvertTo (context, culture, value, destinationType); + } + + public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { + return new Binding((string)propertyValues["PropertyName"], (object)propertyValues["DataSource"], (string)propertyValues["DataMember"]); + } + + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/ListBindingHelper.cs b/source/ShiftUI/Internal/ListBindingHelper.cs new file mode 100644 index 0000000..2615a33 --- /dev/null +++ b/source/ShiftUI/Internal/ListBindingHelper.cs @@ -0,0 +1,189 @@ +// 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) 2007 Novell, Inc. +// +// Author: +// Carlos Alberto Cortez <[email protected]> +// Ivan Zlatev <[email protected]> +// + +using System; +using System.Collections; +using System.ComponentModel; +using System.Reflection; +using System.Collections.Generic; + +namespace ShiftUI +{ + + public static class ListBindingHelper + { + public static object GetList (object list) + { + if (list is IListSource) + return ((IListSource) list).GetList (); + return list; + } + + public static object GetList (object dataSource, string dataMember) + { + dataSource = GetList (dataSource); + if (dataSource == null || dataMember == null || dataMember.Length == 0) + return dataSource; + + PropertyDescriptor property = GetListItemProperties (dataSource).Find (dataMember, true); + if (property == null) + throw new ArgumentException ("dataMember"); + + object item = null; + + ICurrencyManagerProvider currencyManagerProvider = dataSource as ICurrencyManagerProvider; + if (currencyManagerProvider != null && currencyManagerProvider.CurrencyManager != null) { + CurrencyManager currencyManager = currencyManagerProvider.CurrencyManager; + if (currencyManager != null && currencyManager.Count > 0 && currencyManager.Current != null) + item = currencyManager.Current; + } + + if (item == null) { + if (dataSource is IEnumerable) { + if (dataSource is IList) { + IList list = (IList) dataSource; + item = list.Count > 0 ? list[0] : null; + } else { + IEnumerator e = ((IEnumerable) dataSource).GetEnumerator (); + if (e != null && e.MoveNext ()) + item = e.Current; + } + } else { + item = dataSource; + } + } + + if (item != null) + return property.GetValue (item); + return null; + } + + public static Type GetListItemType (object list) + { + return GetListItemType (list, String.Empty); + } + + public static Type GetListItemType (object dataSource, string dataMember) + { + if (dataSource == null) + return null; + + if (dataMember != null && dataMember.Length > 0) { + PropertyDescriptor property = GetProperty (dataSource, dataMember); + if (property == null) + return typeof (object); + + return property.PropertyType; + } + + if (dataSource is Array) + return dataSource.GetType ().GetElementType (); + + // IEnumerable seems to have higher precedence over IList + if (dataSource is IEnumerable) { + IEnumerator enumerator = ((IEnumerable) dataSource).GetEnumerator (); + if (enumerator.MoveNext () && enumerator.Current != null) + return enumerator.Current.GetType (); + + if (dataSource is IList || dataSource.GetType () == typeof (IList<>)) { + PropertyInfo property = GetPropertyByReflection (dataSource.GetType (), "Item"); + if (property != null) // `Item' could be interface-explicit, and thus private + return property.PropertyType; + } + + // fallback to object + return typeof (object); + } + + return dataSource.GetType (); + } + + public static PropertyDescriptorCollection GetListItemProperties (object list) + { + return GetListItemProperties (list, null); + } + + public static PropertyDescriptorCollection GetListItemProperties (object list, PropertyDescriptor [] listAccessors) + { + list = GetList (list); + + if (list == null) + return new PropertyDescriptorCollection (null); + + if (list is ITypedList) + return ((ITypedList)list).GetItemProperties (listAccessors); + + if (listAccessors == null || listAccessors.Length == 0) { + Type item_type = GetListItemType (list); + return TypeDescriptor.GetProperties (item_type, + new Attribute [] { new BrowsableAttribute (true) }); + } + + // Take into account only the first property + Type property_type = listAccessors [0].PropertyType; + if (typeof (IList).IsAssignableFrom (property_type) || typeof (IList<>).IsAssignableFrom (property_type)) { + + PropertyInfo property = GetPropertyByReflection (property_type, "Item"); + return TypeDescriptor.GetProperties (property.PropertyType); + } + + return new PropertyDescriptorCollection (new PropertyDescriptor [0]); + } + + public static PropertyDescriptorCollection GetListItemProperties (object dataSource, string dataMember, + PropertyDescriptor [] listAccessors) + { + throw new NotImplementedException (); + } + + public static string GetListName (object list, PropertyDescriptor [] listAccessors) + { + if (list == null) + return String.Empty; + + Type item_type = GetListItemType (list); + return item_type.Name; + } + + static PropertyDescriptor GetProperty (object obj, string property_name) + { + return TypeDescriptor.GetProperties (obj, + new Attribute [] { new BrowsableAttribute (true) })[property_name]; + } + + // + // Need to use reflection as we need to bypass the TypeDescriptor.GetProperties () limitations + // + static PropertyInfo GetPropertyByReflection (Type type, string property_name) + { + foreach (PropertyInfo prop in type.GetProperties (BindingFlags.Public | BindingFlags.Instance)) + if (prop.Name == property_name) + return prop; + + return null; + } + } +} diff --git a/source/ShiftUI/Internal/MONO-NOTES b/source/ShiftUI/Internal/MONO-NOTES new file mode 100644 index 0000000..2775bb9 --- /dev/null +++ b/source/ShiftUI/Internal/MONO-NOTES @@ -0,0 +1,28 @@ +Application.Idle is invoked on the same thread used to add the event + +our hacky double buffering only works on the client area, not the nc +area. need to move to a more XplatUI solution involving server-side +drawing surfaces (pixmaps in the X case). + +right now the expose/configure event aggregating is done in the +XEventThread (in the hwnd code, but in the thread running the x event +loop). There is probably a way to get it to run in the thread doing +GetMessage, probably by making GetMessage know about both the xevent +and paint queue's, and having it consume the x queue until there's +nothing left, doing the AddExpose stuff there. then, once the xqueue +is empty, consume the paint queue. This has the added benefit of +making the paint queue a ThreadQueue local thing - no locking will be +needed on it, since the only thing touching it will be the thread +running the message pump. + +why do we /nowarn:108? we lose tons of possibly important warnings due to that... + +more event work: + +CheckedListBox: nothing done. +DataGridView*: nothing done. +GridColumnStylesCollection: nothing done, no Component inheritance +GridTableStylesCollection: nothing done, no Component inheritance +MenuItem: what do we do about CloneMenu? we can't have assignment of events like that (look for the #if notyet) +MonthCalendar: nothing done. +Panel: nothing done. see whether the events should be "new" or now.. diff --git a/source/ShiftUI/Internal/MONO-TODO b/source/ShiftUI/Internal/MONO-TODO new file mode 100644 index 0000000..76397d0 --- /dev/null +++ b/source/ShiftUI/Internal/MONO-TODO @@ -0,0 +1,112 @@ +plans for XplatUIX11.cs: + + - 1 thread watching X socket, doing nothing but + XNextEvent/hwnd.Queue.Enqueue (with minimal motion + compression/key repeat detection) + + - figure out how to deal with the selection events, since they + busy loop waiting on a response from the X socket. maybe + block that thread with an event and wake it up when the + event thread get enough info? + + - Idle event raised once regardless of how many thread queues + we have (should it come from the X event thread?) + + - X11Hwnd which caches (amongst other things) the properties + for the toplevel window so we can avoid doing all those + XChangeProperty things on every single UpdateStyle call. + + - have RULES for things (like "do not call this function + except from ...", etc) and make sure they're enforced. + + - SendMessage.. can't we just insert the right message on the + hwnd's queue instead of using SendAsyncMethod? + + - what's the difference between PostMessage and SendMessage? + + - clean up the MESS that is: + GetWindowState + SetWindowState + + and especially: + SetHwndStyles + SetWMStyles + DeriveStyles + the Style handling in CreateWindow + + - multiple screens? do we care? xinerama foo? + + - fix the mostly static, somewhat instance nature of + XplatUIX11.cs. Either make it static, or make it an + instance. + + - we need to rethink the expose event handling. right now + expose events are only handled once all the other events in + the queue are handled. So, if you're resizing quickly you + get no redraws until you're done. maybe look at the + _NET_WM_SYNC_REQUEST protocol (for the resize/redraw case + specifically). Also, the current expose handling has us + accessing the invalid list/rectangle from multiple threads. + + - factor out ALL knowledge of the multiple windows + (client/whole) into the X11Hwnd class, so we can try to + factor it out. + + - clean up the systray icon stuff - right now it destroys (!) + the client_window and sets client_window == whole_window. + ugh. + + - hwnds should keep track of the width/height they report to + SWF as well as the x window's width/height. That is, we + should allow hwnds with 0,0 width/height even though X + doesn't allow it. There's some "zero_size" stuff in Hwnd, + but it still appears impossible to have 0,0 sized Widgets. + + - window (subwindow, really) destruction should move to Hwnd + if possible. + + - remove 90% of "lock (XlibLock)" usage. It's not really + needed at all, except perhaps to synchronize the + XEventThread and the PeekMessage/GetMessage methods. + + - the big synchronization problem now is the XEventQueue + class. I think all queue operations need to be done while + holding a lock, as the events are queued from the + XEventThread and dequeued from GetMessage. + +possible classes: + + - XplatUIX11 (creates the first (only?) X11Display class. + Most calls should just get the hwnd/display and make a + method call) + + - X11Display (manages the display handle, creates the event + thread, keeps ref to an X11Atoms and the X11RootHwnd) + + - XEventThread (does this need to be a separate class? can + probably just be a method in X11Display) + + - X11Hwnd (move all the style translation here? handles + property changes and caches them. window title here too) + + - X11RootHwnd (add the special root window property stuff + here) + + - X11Atoms (per display atom list. should these be properties + or fields? fields probably, with the builtins as constants). + + - Xlib (the pinvoke wrapper for Xlib calls) + +window managers we need to test again: + + - metacity on SLED/SUSE + - metacity on ubuntu edgy + - compiz on SLED + - compiz HEAD + - beryl? or should compiz be enough? + +specs: + +Extended Window Manager Hints: http://standards.freedesktop.org/wm-spec/wm-spec-latest.html +XEmbed spec: http://standards.freedesktop.org/xembed-spec/latest/ +Systray spec: http://standards.freedesktop.org/systemtray-spec/systemtray-spec-0.2.html diff --git a/source/ShiftUI/Internal/MWFCategoryAttribute.cs b/source/ShiftUI/Internal/MWFCategoryAttribute.cs new file mode 100644 index 0000000..5e348f1 --- /dev/null +++ b/source/ShiftUI/Internal/MWFCategoryAttribute.cs @@ -0,0 +1,50 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System.ComponentModel; +using ShiftUI; + +namespace System { + [AttributeUsage (AttributeTargets.All, AllowMultiple=false)] + internal sealed class MWFCategoryAttribute : System.ComponentModel.CategoryAttribute { + #region Constructors + public MWFCategoryAttribute() : base() { + } + + public MWFCategoryAttribute(string category) : base(category) { + } + #endregion // Constructors + + #region Methods + protected override string GetLocalizedString(string value) { + return String.Format(value); + } + + #endregion // Methods + } +} diff --git a/source/ShiftUI/Internal/MWFDescriptionAttribute.cs b/source/ShiftUI/Internal/MWFDescriptionAttribute.cs new file mode 100644 index 0000000..38ac348 --- /dev/null +++ b/source/ShiftUI/Internal/MWFDescriptionAttribute.cs @@ -0,0 +1,51 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System.ComponentModel; +using ShiftUI; + +namespace System { + [AttributeUsage (AttributeTargets.All, AllowMultiple=false)] + internal sealed class MWFDescriptionAttribute : System.ComponentModel.DescriptionAttribute { + #region Constructors + public MWFDescriptionAttribute() : base() { + } + + public MWFDescriptionAttribute(string category) : base(category) { + } + #endregion // Constructors + + #region Properties + public override string Description { + get { + return String.Format(base.Description); + } + } + #endregion // Properties + } +} diff --git a/source/ShiftUI/Internal/MainMenu.cs b/source/ShiftUI/Internal/MainMenu.cs new file mode 100644 index 0000000..2bb0f3f --- /dev/null +++ b/source/ShiftUI/Internal/MainMenu.cs @@ -0,0 +1,216 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// +// + +// COMPLETE + +using System.ComponentModel; +using System.Drawing; +using System; + +namespace ShiftUI +{ + [ToolboxItemFilter("ShiftUI.MainMenu", ToolboxItemFilterType.Allow)] + public class MainMenu : Menu + { + private RightToLeft right_to_left = RightToLeft.Inherit; + private Form form = null; + + public MainMenu () : base (null) + { + + } + + public MainMenu (MenuItem[] items) : base (items) + { + + } + + public MainMenu (IContainer container) : this () + { + container.Add (this); + } + + #region Events + + static object CollapseEvent = new object (); + + public event EventHandler Collapse { + add { Events.AddHandler (CollapseEvent, value); } + remove { Events.RemoveHandler (CollapseEvent, value); } + } + + #endregion Events + + #region Public Properties + [Localizable(true)] + [AmbientValue (RightToLeft.Inherit)] + public virtual RightToLeft RightToLeft { + get { return right_to_left;} + set { right_to_left = value; } + } + + #endregion Public Properties + + #region Public Methods + + public virtual MainMenu CloneMenu () + { + MainMenu new_menu = new MainMenu (); + new_menu.CloneMenu (this); + return new_menu; + } + + protected override IntPtr CreateMenuHandle () + { + return IntPtr.Zero; + } + + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + } + + public Form GetForm () + { + return form; + } + + public override string ToString () + { + return base.ToString () + ", GetForm: " + form; + } + + protected internal virtual void OnCollapse (EventArgs e) + { + EventHandler eh = (EventHandler) (Events [CollapseEvent]); + if (eh != null) + eh (this, e); + } + + #endregion Public Methods + + #region Private Methods + + internal void Draw () + { + Message m = Message.Create (Wnd.window.Handle, (int) Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + PaintEventArgs pe = XplatUI.PaintEventStart (ref m, Wnd.window.Handle, false); + Draw (pe, Rect); + } + + internal void Draw (Rectangle rect) + { + if (Wnd.IsHandleCreated) { + Point pt = XplatUI.GetMenuOrigin (Wnd.window.Handle); + Message m = Message.Create (Wnd.window.Handle, (int)Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + PaintEventArgs pevent = XplatUI.PaintEventStart (ref m, Wnd.window.Handle, false); + pevent.Graphics.SetClip (new Rectangle (rect.X + pt.X, rect.Y + pt.Y, rect.Width, rect.Height)); + Draw (pevent, Rect); + XplatUI.PaintEventEnd (ref m, Wnd.window.Handle, false); + } + } + + internal void Draw (PaintEventArgs pe) + { + Draw (pe, Rect); + } + + internal void Draw (PaintEventArgs pe, Rectangle rect) + { + if (!Wnd.IsHandleCreated) + return; + + X = rect.X; + Y = rect.Y; + Height = Rect.Height; + + ThemeEngine.Current.DrawMenuBar (pe.Graphics, this, rect); + + PaintEventHandler eh = (PaintEventHandler)(Events [PaintEvent]); + if (eh != null) + eh (this, pe); + } + + internal override void InvalidateItem (MenuItem item) + { + Draw (item.bounds); + } + + internal void SetForm (Form form) + { + this.form = form; + Wnd = form; + + if (tracker == null) { + tracker = new MenuTracker (this); + tracker.GrabControl = form; + } + } + + internal override void OnMenuChanged (EventArgs e) + { + base.OnMenuChanged (EventArgs.Empty); + if (form == null) + return; + + Rectangle clip = Rect; + Height = 0; /* need this so the theme code will re-layout the menu items + (why is the theme code doing the layout? argh) */ + + if (!Wnd.IsHandleCreated) + return; + + Message m = Message.Create (Wnd.window.Handle, (int) Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + PaintEventArgs pevent = XplatUI.PaintEventStart (ref m, Wnd.window.Handle, false); + pevent.Graphics.SetClip (clip); + + Draw (pevent, clip); + } + + /* Mouse events from the form */ + internal void OnMouseDown (object window, MouseEventArgs args) + { + tracker.OnMouseDown (args); + } + + internal void OnMouseMove (object window, MouseEventArgs e) + { + MouseEventArgs args = new MouseEventArgs (e.Button, e.Clicks, Widget.MousePosition.X, Widget.MousePosition.Y, e.Delta); + tracker.OnMotion (args); + } + + static object PaintEvent = new object (); + + internal event PaintEventHandler Paint { + add { Events.AddHandler (PaintEvent, value); } + remove { Events.RemoveHandler (PaintEvent, value); } + } + + #endregion Private Methods + } +} + + diff --git a/source/ShiftUI/Internal/MdiClient.cs b/source/ShiftUI/Internal/MdiClient.cs new file mode 100644 index 0000000..8c2cf8c --- /dev/null +++ b/source/ShiftUI/Internal/MdiClient.cs @@ -0,0 +1,1003 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok [email protected] +// +// + +// NOT COMPLETE + +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI { + [ComVisible (true)] + [ClassInterface(ClassInterfaceType.AutoDispatch)] + [DesignTimeVisible(false)] + [ToolboxItem(false)] + public sealed class MdiClient : Widget { + #region Local Variables + private int mdi_created; + private ImplicitHScrollBar hbar; + private ImplicitVScrollBar vbar; + private SizeGrip sizegrip; + private int hbar_value; + private int vbar_value; + private bool lock_sizing; + private bool initializing_scrollbars; + private int prev_bottom; + private bool setting_windowstates = false; + internal ArrayList mdi_child_list; + private string form_text; + private bool setting_form_text; + private Form active_child; + + #endregion // Local Variables + + #region Public Classes + [ComVisible (false)] + public new class WidgetCollection : Widget.WidgetCollection { + + private MdiClient owner; + + public WidgetCollection(MdiClient owner) : base(owner) { + this.owner = owner; + } + + public override void Add(Widget value) { + if ((value is Form) == false || !(((Form)value).IsMdiChild)) { + throw new ArgumentException("Form must be MdiChild"); + } + owner.mdi_child_list.Add (value); + base.Add (value); + + // newest member is the active one + Form form = (Form) value; + owner.ActiveMdiChild = form; + } + + public override void Remove(Widget value) + { + Form form = value as Form; + if (form != null) { + MdiWindowManager wm = form.WindowManager as MdiWindowManager; + if (wm != null) { + form.Closed -= wm.form_closed_handler; + } + } + + owner.mdi_child_list.Remove (value); + base.Remove (value); + } + } + #endregion // Public Classes + + #region Public Constructors + public MdiClient() + { + mdi_child_list = new ArrayList (); + BackColor = SystemColors.AppWorkspace; + Dock = DockStyle.Fill; + SetStyle (Widgetstyles.Selectable, false); + } + #endregion // Public Constructors + + internal void SendFocusToActiveChild () + { + Form active = this.ActiveMdiChild; + if (active == null) { + ParentForm.SendWidgetFocus (this); + } else { + active.SendWidgetFocus (active); + ParentForm.ActiveWidget = active; + } + } + + internal bool HorizontalScrollbarVisible { + get { return hbar != null && hbar.Visible; } + } + internal bool VerticalScrollbarVisible { + get { return vbar != null && vbar.Visible; } + } + + internal void SetParentText(bool text_changed) + { + if (setting_form_text) + return; + + setting_form_text = true; + + if (text_changed) + form_text = ParentForm.Text; + + if (ParentForm.ActiveMaximizedMdiChild == null) { + ParentForm.Text = form_text; + } else { + string childText = ParentForm.ActiveMaximizedMdiChild.form.Text; + if (childText.Length > 0) { + ParentForm.Text = form_text + " - [" + ParentForm.ActiveMaximizedMdiChild.form.Text + "]"; + } else { + ParentForm.Text = form_text; + } + } + + setting_form_text = false; + } + + internal override void OnPaintBackgroundInternal (PaintEventArgs pe) + { + if (BackgroundImage != null) + return; + + if (Parent == null || Parent.BackgroundImage == null) + return; + Parent.PaintWidgetBackground (pe); + } + + internal Form ParentForm { + get { return (Form) Parent; } + } + + protected override Widget.WidgetCollection CreateWidgetsInstance () + { + return new MdiClient.WidgetCollection (this); + } + + protected override void WndProc(ref Message m) { + switch ((Msg)m.Msg) { + case Msg.WM_NCPAINT: + PaintEventArgs pe = XplatUI.PaintEventStart (ref m, Handle, false); + + Rectangle clip; + clip = new Rectangle (0, 0, Width, Height); + + WidgetPaint.DrawBorder3D (pe.Graphics, clip, Border3DStyle.Sunken); + XplatUI.PaintEventEnd (ref m, Handle, false); + m.Result = IntPtr.Zero; + return ; + } + + base.WndProc (ref m); + } + + protected override void OnResize (EventArgs e) + { + base.OnResize (e); + + if (Parent != null && Parent.IsHandleCreated) + XplatUI.InvalidateNC (Parent.Handle); + // Should probably make this into one loop + SizeScrollBars (); + ArrangeWindows (); + } + + protected override void ScaleWidget (SizeF factor, BoundsSpecified specified) + { + // Never change the MdiClient's location + specified &= ~BoundsSpecified.Location; + + base.ScaleWidget (factor, specified); + } + + [System.ComponentModel.EditorBrowsable (EditorBrowsableState.Never)] + protected override void ScaleCore (float dx, float dy) + { + base.ScaleCore (dx, dy); + } + + protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified) + { + base.SetBoundsCore (x, y, width, height, specified); + } + + #region Public Instance Properties + [Localizable(true)] + public override System.Drawing.Image BackgroundImage { + get { + return base.BackgroundImage; + } + set { + base.BackgroundImage = value; + } + } + + //[EditorBrowsable (EditorBrowsableState.Never)] + [Browsable (false)] + public override ImageLayout BackgroundImageLayout { + get { + return base.BackgroundImageLayout; + } + set { + base.BackgroundImageLayout = value; + } + } + + public Form [] MdiChildren { + get { + if (mdi_child_list == null) + return new Form [0]; + return (Form []) mdi_child_list.ToArray (typeof (Form)); + } + } + #endregion // Public Instance Properties + +#region Protected Instance Properties + protected override CreateParams CreateParams { + get { + CreateParams result = base.CreateParams; + result.ExStyle |= (int) WindowExStyles.WS_EX_CLIENTEDGE; + return result; + } + } + #endregion // Protected Instance Properties + + #region Public Instance Methods + public void LayoutMdi (MdiLayout value) { + + // Don't forget to always call ArrangeIconicWindows + ArrangeIconicWindows (true); + + switch (value) { + case MdiLayout.Cascade: { + int i = 0; + for (int c = Widgets.Count - 1; c >= 0; c--) { + Form form = (Form) Widgets [c]; + + if (form.WindowState == FormWindowState.Minimized) + continue; + + if (form.WindowState == FormWindowState.Maximized) + form.WindowState = FormWindowState.Normal; + + form.Width = System.Convert.ToInt32 (ClientSize.Width * 0.8); + form.Height = Math.Max ( + System.Convert.ToInt32 (ClientSize.Height * 0.8), + SystemInformation.MinimumWindowSize.Height + 2); + + int l = 22 * i; + int t = 22 * i; + + if (i != 0 && (l + form.Width > ClientSize.Width || t + form.Height > ClientSize.Height)) { + i = 0; + l = 22 * i; + t = 22 * i; + } + + form.Left = l; + form.Top = t; + + i++; + } + break; + } + case MdiLayout.TileHorizontal: + case MdiLayout.TileVertical: { + // First count number of windows to tile + int total = 0; + + // And space used by iconic windows + int clientHeight = ClientSize.Height; + + for (int i = 0; i < Widgets.Count; i++) { + Form form = Widgets [i] as Form; + + if (form == null) + continue; + + if (!form.Visible) + continue; + + if (form.WindowState == FormWindowState.Maximized) + form.WindowState = FormWindowState.Normal; + else if (form.WindowState == FormWindowState.Minimized) { + if (form.Bounds.Top < clientHeight) + clientHeight = form.Bounds.Top; + continue; + } + + total++; + } + if (total <= 0) + return; + + // Calculate desired height and width + Size newSize; + Size offset; + + if (value == MdiLayout.TileHorizontal) { + newSize = new Size(ClientSize.Width, clientHeight / total); + offset = new Size (0, newSize.Height); + } else { + newSize = new Size(ClientSize.Width / total, clientHeight); + offset = new Size (newSize.Width, 0); + } + + // Loop again and set the size and location. + Point nextLocation = Point.Empty; + + for (int i = 0; i < Widgets.Count; i++) { + Form form = Widgets [i] as Form; + + if (form == null) + continue; + + if (!form.Visible) + continue; + + if (form.WindowState == FormWindowState.Minimized) + continue; + + form.Size = newSize; + form.Location = nextLocation; + nextLocation += offset; + } + + break; + } + } + } + #endregion // Public Instance Methods + + #region Protected Instance Methods + #endregion // Protected Instance Methods + + internal void SizeScrollBars () + { + if (lock_sizing) + return; + + if (!IsHandleCreated) + return; + + if (Widgets.Count == 0 || ((Form) Widgets [0]).WindowState == FormWindowState.Maximized) { + if (hbar != null) + hbar.Visible = false; + if (vbar != null) + vbar.Visible = false; + if (sizegrip != null) + sizegrip.Visible = false; + return; + } + + int right = 0; + int left = 0; + int top = 0; + int bottom = 0; + + foreach (Form child in Widgets) { + if (!child.Visible) + continue; + if (child.Right > right) + right = child.Right; + if (child.Left < left) { + left = child.Left; + } + + if (child.Bottom > bottom) + bottom = child.Bottom; + if (child.Top < 0) { + top = child.Top; + } + } + + int available_width = ClientSize.Width; + int available_height = ClientSize.Height; + + bool need_hbar = false; + bool need_vbar = false; + + if (right - left > available_width || left < 0) { + need_hbar = true; + available_height -= SystemInformation.HorizontalScrollBarHeight; + } + if (bottom - top > available_height || top < 0) { + need_vbar = true; + available_width -= SystemInformation.VerticalScrollBarWidth; + + if (!need_hbar && (right - left > available_width || left < 0)) { + need_hbar = true; + available_height -= SystemInformation.HorizontalScrollBarHeight; + } + } + + if (need_hbar) { + if (hbar == null) { + hbar = new ImplicitHScrollBar (); + Widgets.AddImplicit (hbar); + } + hbar.Visible = true; + CalcHBar (left, right, need_vbar); + } else if (hbar != null) + hbar.Visible = false; + + if (need_vbar) { + if (vbar == null) { + vbar = new ImplicitVScrollBar (); + Widgets.AddImplicit (vbar); + } + vbar.Visible = true; + CalcVBar (top, bottom, need_hbar); + } else if (vbar != null) + vbar.Visible = false; + + if (need_hbar && need_vbar) { + if (sizegrip == null) { + sizegrip = new SizeGrip (this.ParentForm); + Widgets.AddImplicit (sizegrip); + } + sizegrip.Location = new Point (hbar.Right, vbar.Bottom); + sizegrip.Visible = true; + XplatUI.SetZOrder (sizegrip.Handle, vbar.Handle, false, false); + } else if (sizegrip != null) { + sizegrip.Visible = false; + } + + XplatUI.InvalidateNC (Handle); + } + + private void CalcHBar (int left, int right, bool vert_vis) + { + initializing_scrollbars = true; + + hbar.Left = 0; + hbar.Top = ClientRectangle.Bottom - hbar.Height; + hbar.Width = ClientRectangle.Width - (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0); + hbar.LargeChange = 50; + hbar.Minimum = Math.Min (left, 0); + hbar.Maximum = Math.Max (right - ClientSize.Width + 51 + (vert_vis ? SystemInformation.VerticalScrollBarWidth : 0), 0); + hbar.Value = 0; + hbar_value = 0; + hbar.ValueChanged += new EventHandler (HBarValueChanged); + XplatUI.SetZOrder (hbar.Handle, IntPtr.Zero, true, false); + + initializing_scrollbars = false; + } + + private void CalcVBar (int top, int bottom, bool horz_vis) + { + initializing_scrollbars = true; + + vbar.Top = 0; + vbar.Left = ClientRectangle.Right - vbar.Width; + vbar.Height = ClientRectangle.Height - (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0); + vbar.LargeChange = 50; + vbar.Minimum = Math.Min (top, 0); + vbar.Maximum = Math.Max (bottom - ClientSize.Height + 51 + (horz_vis ? SystemInformation.HorizontalScrollBarHeight : 0), 0); + vbar.Value = 0; + vbar_value = 0; + vbar.ValueChanged += new EventHandler (VBarValueChanged); + XplatUI.SetZOrder (vbar.Handle, IntPtr.Zero, true, false); + + initializing_scrollbars = false; + } + + private void HBarValueChanged (object sender, EventArgs e) + { + if (initializing_scrollbars) + return; + + if (hbar.Value == hbar_value) + return; + + lock_sizing = true; + + try { + int diff = hbar_value - hbar.Value; + foreach (Form child in Widgets) { + child.Left += diff; + } + } finally { + lock_sizing = false; + } + + hbar_value = hbar.Value; + } + + private void VBarValueChanged (object sender, EventArgs e) + { + if (initializing_scrollbars) + return; + + if (vbar.Value == vbar_value) + return; + + lock_sizing = true; + + try { + int diff = vbar_value - vbar.Value; + foreach (Form child in Widgets) { + child.Top += diff; + } + } finally { + lock_sizing = false; + } + + vbar_value = vbar.Value; + } + + private void ArrangeWindows () + { + if (!IsHandleCreated) + return; + + int change = 0; + if (prev_bottom != -1) + change = Bottom - prev_bottom; + + foreach (Widget c in Widgets) { + Form child = c as Form; + + if (c == null || !child.Visible) + continue; + + MdiWindowManager wm = child.WindowManager as MdiWindowManager; + if (wm.GetWindowState () == FormWindowState.Maximized) + child.Bounds = wm.MaximizedBounds; + + if (wm.GetWindowState () == FormWindowState.Minimized) { + child.Top += change; + } + + } + + prev_bottom = Bottom; + } + + internal void ArrangeIconicWindows (bool rearrange_all) + { + Rectangle rect = Rectangle.Empty; + + lock_sizing = true; + foreach (Form form in Widgets) { + if (form.WindowState != FormWindowState.Minimized) + continue; + + MdiWindowManager wm = (MdiWindowManager) form.WindowManager; + + if (wm.IconicBounds != Rectangle.Empty && !rearrange_all) { + if (form.Bounds != wm.IconicBounds) + form.Bounds = wm.IconicBounds; + continue; + } + + bool success = true; + int startx, starty, currentx, currenty; + + rect.Size = wm.IconicSize; + + startx = 0; + starty = ClientSize.Height - rect.Height; + currentx = startx; + currenty = starty; + + do { + rect.X = currentx; + rect.Y = currenty; + success = true; + foreach (Form form2 in Widgets) { + if (form2 == form || form2.window_state != FormWindowState.Minimized) + continue; + + if (form2.Bounds.IntersectsWith(rect)) { + success = false; + break; + } + } + if (!success) { + currentx += rect.Width; + if (currentx + rect.Width > Right) { + currentx = startx; + currenty -= rect.Height; + } + } + } while (!success); + wm.IconicBounds = rect; + form.Bounds = wm.IconicBounds; + } + lock_sizing = false; + } + + internal void ChildFormClosed (Form form) + { + FormWindowState closed_form_windowstate = form.WindowState; + + form.Visible = false; + Widgets.Remove (form); + + if (Widgets.Count == 0) { + ((MdiWindowManager) form.window_manager).RaiseDeactivate (); + } else if (closed_form_windowstate == FormWindowState.Maximized) { + Form current = (Form) Widgets [0]; + current.WindowState = FormWindowState.Maximized; + ActivateChild(current); + } + + if (Widgets.Count == 0) { + XplatUI.RequestNCRecalc (Parent.Handle); + ParentForm.PerformLayout (); + + // If we closed the last child, unmerge the menus. + // If it's not the last child, the menu will be unmerged + // when another child takes focus. + MenuStrip parent_menu = form.MdiParent.MainMenuStrip; + + if (parent_menu != null) + if (parent_menu.IsCurrentlyMerged) + ToolStripManager.RevertMerge (parent_menu); + } + SizeScrollBars (); + SetParentText (false); + form.Dispose(); + } + + internal void ActivateNextChild () + { + if (Widgets.Count < 1) + return; + if (Widgets.Count == 1 && Widgets[0] == ActiveMdiChild) + return; + + Form front = (Form) Widgets [0]; + Form form = (Form) Widgets [1]; + + ActivateChild (form); + front.SendToBack (); + } + + internal void ActivatePreviousChild () + { + if (Widgets.Count <= 1) + return; + + Form back = (Form) Widgets [Widgets.Count - 1]; + + ActivateChild (back); + } + + internal void ActivateChild (Form form) + { + if (Widgets.Count < 1) + return; + + if (ParentForm.is_changing_visible_state > 0) + return; + + Form current = (Form) Widgets [0]; + bool raise_deactivate = ParentForm.ActiveWidget == current; + + // We want to resize the new active form before it is + // made active to avoid flickering. Can't do it in the + // normal way (form.WindowState = Maximized) since it's not + // active yet and everything would just return to before. + // We also won't suspend layout, this way the layout will + // happen before the form is made active (and in many cases + // before it is visible, which avoids flickering as well). + MdiWindowManager wm = (MdiWindowManager)form.WindowManager; + + if (current.WindowState == FormWindowState.Maximized && form.WindowState != FormWindowState.Maximized && form.Visible) { + FormWindowState old_state = form.window_state; + SetWindowState (form, old_state, FormWindowState.Maximized, true); + wm.was_minimized = form.window_state == FormWindowState.Minimized; + form.window_state = FormWindowState.Maximized; + SetParentText (false); + } + + form.BringToFront (); + form.SendWidgetFocus (form); + SetWindowStates (wm); + if (current != form) { + form.has_focus = false; + if (current.IsHandleCreated) + XplatUI.InvalidateNC (current.Handle); + if (form.IsHandleCreated) + XplatUI.InvalidateNC (form.Handle); + if (raise_deactivate) { + MdiWindowManager current_wm = (MdiWindowManager) current.window_manager; + current_wm.RaiseDeactivate (); + + } + } + active_child = (Form) Widgets [0]; + + if (active_child.Visible) { + bool raise_activated = ParentForm.ActiveWidget != active_child; + ParentForm.ActiveWidget = active_child; + if (raise_activated) { + MdiWindowManager active_wm = (MdiWindowManager) active_child.window_manager; + active_wm.RaiseActivated (); + } + } + } + + internal override IntPtr AfterTopMostWidget () + { + // order of scrollbars: + // top = vertical + // sizegrid + // bottom = horizontal + if (hbar != null && hbar.Visible) + return hbar.Handle; + // no need to check for sizegrip since it will only + // be visible if hbar is visible. + if (vbar != null && vbar.Visible) + return vbar.Handle; + + return base.AfterTopMostWidget (); + } + + internal bool SetWindowStates (MdiWindowManager wm) + { + /* + MDI WindowState behaviour: + - If the active window is maximized, all other maximized windows are normalized. + - If a normal window gets focus and the original active window was maximized, + the normal window gets maximized and the original window gets normalized. + - If a minimized window gets focus and the original window was maximized, + the minimzed window gets maximized and the original window gets normalized. + If the ex-minimized window gets deactivated, it will be normalized. + */ + Form form = wm.form; + + if (setting_windowstates) { + return false; + } + + if (!form.Visible) + return false; + + bool is_active = wm.IsActive; + bool maximize_this = false; + + if (!is_active){ + return false; + } + + ArrayList minimize_these = new ArrayList (); + ArrayList normalize_these = new ArrayList (); + + setting_windowstates = true; + foreach (Form frm in mdi_child_list) { + if (frm == form) { + continue; + } else if (!frm.Visible){ + continue; + } + if (frm.WindowState == FormWindowState.Maximized && is_active) { + maximize_this = true; + if (((MdiWindowManager) frm.window_manager).was_minimized) { + minimize_these.Add (frm); + } else { + normalize_these.Add (frm); + } + } + } + + if (maximize_this && form.WindowState != FormWindowState.Maximized) { + wm.was_minimized = form.window_state == FormWindowState.Minimized; + form.WindowState = FormWindowState.Maximized; + } + + foreach (Form frm in minimize_these) + frm.WindowState = FormWindowState.Minimized; + + foreach (Form frm in normalize_these) + frm.WindowState = FormWindowState.Normal; + + + SetParentText (false); + + XplatUI.RequestNCRecalc (ParentForm.Handle); + XplatUI.RequestNCRecalc (Handle); + + SizeScrollBars (); + + setting_windowstates = false; + + if (form.MdiParent.MainMenuStrip != null) + form.MdiParent.MainMenuStrip.RefreshMdiItems (); + + // Implicit menu strip merging + // - When child is activated + // - Parent form must have a MainMenuStrip + // - Find the first menustrip on the child + // - Merge + MenuStrip parent_menu = form.MdiParent.MainMenuStrip; + + if (parent_menu != null) { + if (parent_menu.IsCurrentlyMerged) + ToolStripManager.RevertMerge (parent_menu); + + MenuStrip child_menu = LookForChildMenu (form); + + if (form.WindowState != FormWindowState.Maximized) + RemoveControlMenuItems (wm); + + if (form.WindowState == FormWindowState.Maximized) { + bool found = false; + + foreach (ToolStripItem tsi in parent_menu.Items) { + if (tsi is MdiWidgetStrip.SystemMenuItem) { + (tsi as MdiWidgetStrip.SystemMenuItem).MdiForm = form; + found = true; + } else if (tsi is MdiWidgetStrip.WidgetBoxMenuItem) { + (tsi as MdiWidgetStrip.WidgetBoxMenuItem).MdiForm = form; + found = true; + } + } + + if (!found) { + parent_menu.SuspendLayout (); + parent_menu.Items.Insert (0, new MdiWidgetStrip.SystemMenuItem (form)); + parent_menu.Items.Add (new MdiWidgetStrip.WidgetBoxMenuItem (form, MdiWidgetStrip.ControlBoxType.Close)); + parent_menu.Items.Add (new MdiWidgetStrip.WidgetBoxMenuItem (form, MdiWidgetStrip.ControlBoxType.Max)); + parent_menu.Items.Add (new MdiWidgetStrip.WidgetBoxMenuItem (form, MdiWidgetStrip.ControlBoxType.Min)); + parent_menu.ResumeLayout (); + } + } + + if (child_menu != null) + ToolStripManager.Merge (child_menu, parent_menu); + } + + return maximize_this; + } + + private MenuStrip LookForChildMenu (Widget parent) + { + foreach (Widget c in parent.Widgets) { + if (c is MenuStrip) + return (MenuStrip)c; + + if (c is ToolStripContainer || c is ToolStripPanel) { + MenuStrip ms = LookForChildMenu (c); + + if (ms != null) + return ms; + } + } + + return null; + } + + internal void RemoveControlMenuItems (MdiWindowManager wm) + { + Form form = wm.form; + MenuStrip parent_menu = form.MdiParent.MainMenuStrip; + + // Only remove the items if the form requesting still owns the menu items + if (parent_menu != null) { + parent_menu.SuspendLayout (); + + for (int i = parent_menu.Items.Count - 1; i >= 0; i--) { + if (parent_menu.Items[i] is MdiWidgetStrip.SystemMenuItem) { + if ((parent_menu.Items[i] as MdiWidgetStrip.SystemMenuItem).MdiForm == form) + parent_menu.Items.RemoveAt (i); + } else if (parent_menu.Items[i] is MdiWidgetStrip.WidgetBoxMenuItem) { + if ((parent_menu.Items[i] as MdiWidgetStrip.WidgetBoxMenuItem).MdiForm == form) + parent_menu.Items.RemoveAt (i); + } + } + + parent_menu.ResumeLayout (); + } + } + + internal void SetWindowState (Form form, FormWindowState old_window_state, FormWindowState new_window_state, bool is_activating_child) + { + bool mdiclient_layout; + + MdiWindowManager wm = (MdiWindowManager) form.window_manager; + + if (!is_activating_child && new_window_state == FormWindowState.Maximized && !wm.IsActive) { + ActivateChild (form); + return; + } + + if (old_window_state == FormWindowState.Normal) + wm.NormalBounds = form.Bounds; + + if (SetWindowStates (wm)) + return; + + if (old_window_state == new_window_state) + return; + + mdiclient_layout = old_window_state == FormWindowState.Maximized || new_window_state == FormWindowState.Maximized; + + switch (new_window_state) { + case FormWindowState.Minimized: + ArrangeIconicWindows (false); + break; + case FormWindowState.Maximized: + form.Bounds = wm.MaximizedBounds; + break; + case FormWindowState.Normal: + form.Bounds = wm.NormalBounds; + break; + } + + wm.UpdateWindowDecorations (new_window_state); + + form.ResetCursor (); + + if (mdiclient_layout) + Parent.PerformLayout (); + + XplatUI.RequestNCRecalc (Parent.Handle); + XplatUI.RequestNCRecalc (form.Handle); + if (!setting_windowstates) + SizeScrollBars (); + } + internal int ChildrenCreated { + get { return mdi_created; } + set { mdi_created = value; } + } + + internal Form ActiveMdiChild { + get { + if (ParentForm != null && !ParentForm.Visible) + return null; + + if (Widgets.Count < 1) + return null; + + if (!ParentForm.IsHandleCreated) + return null; + + if (!ParentForm.has_been_visible) + return null; + + if (!ParentForm.Visible) + return active_child; + + active_child = null; + for (int i = 0; i < Widgets.Count; i++) { + if (Widgets [i].Visible) { + active_child = (Form) Widgets [i]; + break; + } + } + return active_child; + } + set { + ActivateChild (value); + } + } + + internal void ActivateActiveMdiChild () + { + if (ParentForm.is_changing_visible_state > 0) + return; + + for (int i = 0; i < Widgets.Count; i++) { + if (Widgets [i].Visible) { + ActivateChild ((Form) Widgets [i]); + return; + } + } + } + } +} + diff --git a/source/ShiftUI/Internal/MdiControlStrip.cs b/source/ShiftUI/Internal/MdiControlStrip.cs new file mode 100644 index 0000000..a04e02a --- /dev/null +++ b/source/ShiftUI/Internal/MdiControlStrip.cs @@ -0,0 +1,207 @@ +// +// MdiWidgetstrip.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System; +using System.Runtime.InteropServices; +using System.ComponentModel; +using System.Drawing; +using ShiftUI.Layout; +using System.Collections.Generic; +using System.ComponentModel.Design.Serialization; + +namespace ShiftUI +{ + internal class MdiWidgetStrip + { + public class SystemMenuItem : ToolStripMenuItem + { + private Form form; + + public SystemMenuItem (Form ownerForm) + { + form = ownerForm; + + base.AutoSize = false; + base.Size = new Size (20, 20); + base.Image = ownerForm.Icon.ToBitmap (); + base.MergeIndex = int.MinValue; + base.DisplayStyle = ToolStripItemDisplayStyle.Image; + + DropDownItems.Add ("&Restore", null, RestoreItemHandler); + ToolStripMenuItem tsiMove = (ToolStripMenuItem)DropDownItems.Add ("&Move"); + tsiMove.Enabled = false; + ToolStripMenuItem tsiSize = (ToolStripMenuItem)DropDownItems.Add ("&Size"); + tsiSize.Enabled = false; + DropDownItems.Add ("Mi&nimize", null, MinimizeItemHandler); + ToolStripMenuItem tsiMaximize = (ToolStripMenuItem)DropDownItems.Add ("Ma&ximize"); + tsiMaximize.Enabled = false; + DropDownItems.Add ("-"); + ToolStripMenuItem tsiClose = (ToolStripMenuItem)DropDownItems.Add ("&Close", null, CloseItemHandler); + tsiClose.ShortcutKeys = Keys.Widget | Keys.F4; + DropDownItems.Add ("-"); + ToolStripMenuItem tsiNext = (ToolStripMenuItem)DropDownItems.Add ("Nex&t", null, NextItemHandler); + tsiNext.ShortcutKeys = Keys.Widget | Keys.F6; + } + + protected override void OnPaint (PaintEventArgs e) + { + // Can't render without an owner + if (this.Owner == null) + return; + + // If DropDown.ShowImageMargin is false, we don't display the image + Image draw_image = this.Image; + + // Figure out where our text and image go + Rectangle text_layout_rect; + Rectangle image_layout_rect; + + this.CalculateTextAndImageRectangles (out text_layout_rect, out image_layout_rect); + + if (image_layout_rect != Rectangle.Empty) + this.Owner.Renderer.DrawItemImage (new ToolStripItemImageRenderEventArgs (e.Graphics, this, draw_image, image_layout_rect)); + + return; + } + + public Form MdiForm { + get { return form; } + set { form = value; } + } + + private void RestoreItemHandler (object sender, EventArgs e) + { + form.WindowState = FormWindowState.Normal; + } + + private void MinimizeItemHandler (object sender, EventArgs e) + { + form.WindowState = FormWindowState.Minimized; + } + + private void CloseItemHandler (object sender, EventArgs e) + { + form.Close (); + } + + private void NextItemHandler (object sender, EventArgs e) + { + form.MdiParent.MdiContainer.ActivateNextChild (); + } + } + + public class WidgetBoxMenuItem : ToolStripMenuItem + { + private Form form; + private ControlBoxType type; + + public WidgetBoxMenuItem (Form ownerForm, ControlBoxType type) + { + form = ownerForm; + this.type = type; + + base.AutoSize = false; + base.Alignment = ToolStripItemAlignment.Right; + base.Size = new Size (20, 20); + base.MergeIndex = int.MaxValue; + base.DisplayStyle = ToolStripItemDisplayStyle.None; + + switch (type) { + case ControlBoxType.Close: + this.Click += new EventHandler(CloseItemHandler); + break; + case ControlBoxType.Min: + this.Click += new EventHandler (MinimizeItemHandler); + break; + case ControlBoxType.Max: + this.Click += new EventHandler (RestoreItemHandler); + break; + } + } + + protected override void OnPaint (PaintEventArgs e) + { + base.OnPaint (e); + Graphics g = e.Graphics; + + switch (type) { + case ControlBoxType.Close: + g.FillRectangle (Brushes.Black, 8, 8, 4, 4); + g.FillRectangle (Brushes.Black, 6, 6, 2, 2); + g.FillRectangle (Brushes.Black, 6, 12, 2, 2); + g.FillRectangle (Brushes.Black, 12, 6, 2, 2); + g.FillRectangle (Brushes.Black, 12, 12, 2, 2); + g.DrawLine (Pens.Black, 8, 7, 8, 12); + g.DrawLine (Pens.Black, 7, 8, 12, 8); + g.DrawLine (Pens.Black, 11, 7, 11, 12); + g.DrawLine (Pens.Black, 7, 11, 12, 11); + break; + case ControlBoxType.Min: + g.DrawLine (Pens.Black, 6, 12, 11, 12); + g.DrawLine (Pens.Black, 6, 13, 11, 13); + break; + case ControlBoxType.Max: + g.DrawLines (Pens.Black, new Point[] {new Point (7, 8), new Point (7, 5), new Point (13, 5), new Point (13, 10), new Point (11, 10)}); + g.DrawLine (Pens.Black, 7, 6, 12, 6); + + g.DrawRectangle (Pens.Black, new Rectangle (5, 8, 6, 5)); + g.DrawLine (Pens.Black, 5, 9, 11, 9); + + break; + } + } + + public Form MdiForm { + get { return form; } + set { form = value; } + } + + private void RestoreItemHandler (object sender, EventArgs e) + { + form.WindowState = FormWindowState.Normal; + } + + private void MinimizeItemHandler (object sender, EventArgs e) + { + form.WindowState = FormWindowState.Minimized; + } + + private void CloseItemHandler (object sender, EventArgs e) + { + form.Close (); + } + } + + public enum ControlBoxType + { + Close, + Min, + Max + } + } +} diff --git a/source/ShiftUI/Internal/MdiLayout.cs b/source/ShiftUI/Internal/MdiLayout.cs new file mode 100644 index 0000000..8c610a4 --- /dev/null +++ b/source/ShiftUI/Internal/MdiLayout.cs @@ -0,0 +1,36 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +namespace ShiftUI { + public enum MdiLayout { + Cascade = 0, + TileHorizontal = 1, + TileVertical = 2, + ArrangeIcons = 3 + } +} diff --git a/source/ShiftUI/Internal/MdiWindowManager.cs b/source/ShiftUI/Internal/MdiWindowManager.cs new file mode 100644 index 0000000..0344f2c --- /dev/null +++ b/source/ShiftUI/Internal/MdiWindowManager.cs @@ -0,0 +1,624 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Jackson Harper ([email protected]) +// +// + + +using System; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Runtime.InteropServices; + +namespace ShiftUI { + + internal class MdiWindowManager : InternalWindowManager { + + private MainMenu merged_menu; + private MainMenu maximized_menu; + private MenuItem icon_menu; + internal bool was_minimized; + + private PaintEventHandler draw_maximized_buttons; + internal EventHandler form_closed_handler; + + private MdiClient mdi_container; + private Rectangle prev_virtual_position; + + private Point icon_clicked; + private DateTime icon_clicked_time; + private bool icon_dont_show_popup; + + private TitleButtons maximized_title_buttons; + private bool is_visible_pending; + private byte last_activation_event; // 0 = none, 1 = activated, 2 = deactivated. + + public void RaiseActivated () + { + if (last_activation_event == 1) + return; + + last_activation_event = 1; + form.OnActivatedInternal (); + form.SelectActiveWidget (); + } + + public void RaiseDeactivate () + { + if (last_activation_event != 1) + return; + last_activation_event = 2; + form.OnDeactivateInternal (); + } + + public override int MenuHeight { + get { + // Mdi children don't get menus on the form, they're shown on the main form. + return 0; + } + } + + internal bool IsVisiblePending { + get { + return is_visible_pending; + } + set { + is_visible_pending = value; + } + } + + private TitleButtons MaximizedTitleButtons { + get { + if (maximized_title_buttons == null) { + maximized_title_buttons = new TitleButtons (this.Form); + maximized_title_buttons.CloseButton.Visible = true; + maximized_title_buttons.RestoreButton.Visible = true; + maximized_title_buttons.MinimizeButton.Visible = true; + } + return maximized_title_buttons; + } + } + + internal override Rectangle MaximizedBounds { + get { + Rectangle pb = mdi_container.ClientRectangle; + int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this); + int tw = TitleBarHeight; + + Rectangle new_bounds = new Rectangle (pb.Left - bw, + pb.Top - tw - bw, + pb.Width + bw * 2, + pb.Height + tw + bw * 2); + return new_bounds; + } + } + + + + public MdiWindowManager (Form form, MdiClient mdi_container) : base (form) + { + this.mdi_container = mdi_container; + if (form.WindowState == FormWindowState.Normal) { + NormalBounds = form.Bounds; + } + form_closed_handler = new EventHandler (FormClosed); + form.Closed += form_closed_handler; + form.TextChanged += new EventHandler (FormTextChangedHandler); + form.SizeChanged += new EventHandler (FormSizeChangedHandler); + form.LocationChanged += new EventHandler (FormLocationChangedHandler); + form.VisibleChanged += new EventHandler (FormVisibleChangedHandler); + draw_maximized_buttons = new PaintEventHandler (DrawMaximizedButtons); + CreateIconMenus (); + } + + private void FormVisibleChangedHandler (object sender, EventArgs e) + { + if (mdi_container == null) + return; + + if (form.Visible) { + mdi_container.ActivateChild (form); + } else if (mdi_container.Widgets.Count > 1) { + mdi_container.ActivateActiveMdiChild (); + } + } + + private void FormTextChangedHandler (object sender, EventArgs e) + { + mdi_container.SetParentText (false); + + if (form.MdiParent.MainMenuStrip != null) + form.MdiParent.MainMenuStrip.RefreshMdiItems (); + } + + private void FormLocationChangedHandler (object sender, EventArgs e) + { + if (form.window_state == FormWindowState.Minimized) + IconicBounds = form.Bounds; + form.MdiParent.MdiContainer.SizeScrollBars (); + } + + private void FormSizeChangedHandler (object sender, EventArgs e) + { + if (form.window_state == FormWindowState.Maximized && form.Bounds != MaximizedBounds) + form.Bounds = MaximizedBounds; + + form.MdiParent.MdiContainer.SizeScrollBars (); + } + + public MainMenu MergedMenu { + get { + if (merged_menu == null) + merged_menu = CreateMergedMenu (); + return merged_menu; + } + } + + private MainMenu CreateMergedMenu () + { + Form parent = (Form) mdi_container.Parent; + MainMenu clone; + if (parent.Menu != null) + clone = (MainMenu) parent.Menu.CloneMenu (); + else + clone = new MainMenu (); + + if (form.WindowState == FormWindowState.Maximized) { + + } + clone.MergeMenu (form.Menu); + clone.MenuChanged += new EventHandler (MenuChangedHandler); + clone.SetForm (parent); + return clone; + } + + public MainMenu MaximizedMenu { + get { + if (maximized_menu == null) + maximized_menu = CreateMaximizedMenu (); + return maximized_menu; + } + } + + private MainMenu CreateMaximizedMenu () + { + Form parent = (Form) mdi_container.Parent; + + if (form.MainMenuStrip != null || parent.MainMenuStrip != null) + return null; + + MainMenu res = new MainMenu (); + + if (parent.Menu != null) { + MainMenu clone = (MainMenu) parent.Menu.CloneMenu (); + res.MergeMenu (clone); + } + + if (form.Menu != null) { + MainMenu clone = (MainMenu) form.Menu.CloneMenu (); + res.MergeMenu (clone); + } + + if (res.MenuItems.Count == 0) + res.MenuItems.Add (new MenuItem ()); // Dummy item to get the menu height correct + + res.MenuItems.Insert (0, icon_menu); + + res.SetForm (parent); + return res; + } + + private void CreateIconMenus () + { + //TODO: remove this unneeded crap + } + + private void ClickIconMenuItem(object sender, EventArgs e) + { + if ((DateTime.Now - icon_clicked_time).TotalMilliseconds <= SystemInformation.DoubleClickTime) { + form.Close (); + return; + } + icon_clicked_time = DateTime.Now; + Point pnt = Point.Empty; + pnt = form.MdiParent.PointToScreen (pnt); + pnt = form.PointToClient (pnt); + ShowPopup (pnt); + } + + internal void ShowPopup (Point pnt) + { + // If we are using MainMenuStrip, display that menu instead + if (form.WindowState == FormWindowState.Maximized && form.MdiParent.MainMenuStrip != null) + if (form.MdiParent.MainMenuStrip.Items.Count > 0) { + ToolStripItem tsi = form.MdiParent.MainMenuStrip.Items[0]; + + if (tsi is MdiWidgetStrip.SystemMenuItem) { + (tsi as MdiWidgetStrip.SystemMenuItem).ShowDropDown (); + return; + } + } + + } + + private void RestoreItemHandler (object sender, EventArgs e) + { + form.WindowState = FormWindowState.Normal; + } + + private void MoveItemHandler (object sender, EventArgs e) + { + int x = 0; + int y = 0; + + PointToScreen (ref x, ref y); + Cursor.Position = new Point (x, y); + form.Cursor = Cursors.Cross; + state = State.Moving; + form.Capture = true; + } + + private void SizeItemHandler (object sender, EventArgs e) + { + int x = 0; + int y = 0; + + PointToScreen (ref x, ref y); + Cursor.Position = new Point (x, y); + form.Cursor = Cursors.Cross; + state = State.Sizing; + form.Capture = true; + } + + private void MinimizeItemHandler (object sender, EventArgs e) + { + form.WindowState = FormWindowState.Minimized; + } + + private void MaximizeItemHandler (object sender, EventArgs e) + { + if (form.WindowState != FormWindowState.Maximized) + form.WindowState = FormWindowState.Maximized; + } + + private void CloseItemHandler (object sender, EventArgs e) + { + form.Close (); + } + + private void NextItemHandler (object sender, EventArgs e) + { + mdi_container.ActivateNextChild (); + } + + private void DrawIconMenuItem (object sender, DrawItemEventArgs de) + { + de.Graphics.DrawIcon (form.Icon, new Rectangle (de.Bounds.X + 2, de.Bounds.Y + 2, + de.Bounds.Height - 4, de.Bounds.Height - 4)); + } + + private void MeasureIconMenuItem (object sender, MeasureItemEventArgs me) + { + int size = SystemInformation.MenuHeight; + me.ItemHeight = size; + me.ItemWidth = size + 2; // some padding + } + + private void MenuChangedHandler (object sender, EventArgs e) + { + CreateMergedMenu (); + } + + public override void PointToClient (ref int x, ref int y) + { + XplatUI.ScreenToClient (mdi_container.Handle, ref x, ref y); + } + + public override void PointToScreen (ref int x, ref int y) + { + XplatUI.ClientToScreen (mdi_container.Handle, ref x, ref y); + } + + public override void UpdateWindowDecorations (FormWindowState window_state) + { + if (MaximizedMenu != null) { + switch (window_state) { + case FormWindowState.Minimized: + case FormWindowState.Normal: + MaximizedMenu.Paint -= draw_maximized_buttons; + MaximizedTitleButtons.Visible = false; + TitleButtons.Visible = true; + break; + case FormWindowState.Maximized: + MaximizedMenu.Paint += draw_maximized_buttons; + MaximizedTitleButtons.Visible = true; + TitleButtons.Visible = false; + break; + } + } + + base.UpdateWindowDecorations (window_state); + } + + public override void SetWindowState (FormWindowState old_state, FormWindowState window_state) + { + mdi_container.SetWindowState (form, old_state, window_state, false); + } + + private void FormClosed (object sender, EventArgs e) + { + mdi_container.ChildFormClosed (form); + + if (form.MdiParent.MainMenuStrip != null) + form.MdiParent.MainMenuStrip.RefreshMdiItems (); + + mdi_container.RemoveControlMenuItems (this); + } + + public override void DrawMaximizedButtons (object sender, PaintEventArgs pe) + { + Size bs = ThemeEngine.Current.ManagedWindowGetMenuButtonSize (this); + Point pnt = XplatUI.GetMenuOrigin (mdi_container.ParentForm.Handle); + int bw = ThemeEngine.Current.ManagedWindowBorderWidth (this); + TitleButtons buttons = MaximizedTitleButtons; + + buttons.Visible = true; + TitleButtons.Visible = false; + + buttons.CloseButton.Rectangle = new Rectangle (mdi_container.ParentForm.Size.Width - 1 - bw - bs.Width - 2, + pnt.Y + 2, bs.Width, bs.Height); + + buttons.RestoreButton.Rectangle = new Rectangle (buttons.CloseButton.Rectangle.Left - 2 - bs.Width, + pnt.Y + 2, bs.Width, bs.Height); + + buttons.MinimizeButton.Rectangle = new Rectangle (buttons.RestoreButton.Rectangle.Left - bs.Width, + pnt.Y + 2, bs.Width, bs.Height); + + DrawTitleButton (pe.Graphics, buttons.MinimizeButton, pe.ClipRectangle); + DrawTitleButton (pe.Graphics, buttons.RestoreButton, pe.ClipRectangle); + DrawTitleButton (pe.Graphics, buttons.CloseButton, pe.ClipRectangle); + + buttons.MinimizeButton.Rectangle.Y -= pnt.Y; + buttons.RestoreButton.Rectangle.Y -= pnt.Y; + buttons.CloseButton.Rectangle.Y -= pnt.Y; + } + + public bool HandleMenuMouseDown (MainMenu menu, int x, int y) + { + Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y)); + + HandleTitleBarDown (pt.X, pt.Y); + return TitleButtons.AnyPushedTitleButtons; + } + + public void HandleMenuMouseUp (MainMenu menu, int x, int y) + { + Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y)); + + HandleTitleBarUp (pt.X, pt.Y); + } + + public void HandleMenuMouseLeave (MainMenu menu, int x, int y) + { + Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y)); + HandleTitleBarLeave (pt.X, pt.Y); + + } + + public void HandleMenuMouseMove (MainMenu menu, int x, int y) + { + Point pt = MenuTracker.ScreenToMenu (menu, new Point (x, y)); + + HandleTitleBarMouseMove (pt.X, pt.Y); + + } + + protected override void HandleTitleBarLeave (int x, int y) + { + base.HandleTitleBarLeave (x, y); + + if (maximized_title_buttons != null) { + maximized_title_buttons.MouseLeave (x, y); + } + + if (IsMaximized) + XplatUI.InvalidateNC (form.MdiParent.Handle); + } + + protected override void HandleTitleBarUp (int x, int y) + { + if (IconRectangleContains (x, y)) { + if (!icon_dont_show_popup) { + if (IsMaximized) + ClickIconMenuItem (null, null); + else + ShowPopup (Point.Empty); + } else { + icon_dont_show_popup = false; + } + return; + } + + bool was_maximized = IsMaximized; + base.HandleTitleBarUp (x, y); + if (maximized_title_buttons != null && was_maximized) { + maximized_title_buttons.MouseUp (x, y); + } + + if (IsMaximized) + XplatUI.InvalidateNC (mdi_container.Parent.Handle); + } + + protected override void HandleTitleBarDoubleClick (int x, int y) + { + if (IconRectangleContains (x, y)) { + form.Close (); + } else if (form.MaximizeBox == true) { + form.WindowState = FormWindowState.Maximized; + } + base.HandleTitleBarDoubleClick (x, y); + } + + protected override void HandleTitleBarDown (int x, int y) + { + if (IconRectangleContains (x, y)) { + if ((DateTime.Now - icon_clicked_time).TotalMilliseconds <= SystemInformation.DoubleClickTime && icon_clicked.X == x && icon_clicked.Y == y) { + form.Close (); + } else { + icon_clicked_time = DateTime.Now; + icon_clicked.X = x; + icon_clicked.Y = y; + } + + return; + } + + base.HandleTitleBarDown (x, y); + + if (maximized_title_buttons != null) { + maximized_title_buttons.MouseDown (x, y); + } + + if (IsMaximized) { + XplatUI.InvalidateNC (mdi_container.Parent.Handle); + } + } + + protected override void HandleTitleBarMouseMove (int x, int y) + { + base.HandleTitleBarMouseMove (x, y); + + if (maximized_title_buttons != null && maximized_title_buttons.MouseMove (x, y)) + XplatUI.InvalidateNC (form.MdiParent.Handle); + } + + protected override bool HandleLButtonDblClick (ref Message m) + { + + int x = Widget.LowOrder ((int)m.LParam.ToInt32 ()); + int y = Widget.HighOrder ((int)m.LParam.ToInt32 ()); + + // Correct since we are in NC land. + NCClientToNC (ref x, ref y); + + if (IconRectangleContains (x, y)) { + form.Close (); + return true; + } + + return base.HandleLButtonDblClick (ref m); + } + + protected override bool HandleLButtonDown (ref Message m) + { + + int x = Widget.LowOrder ((int)m.LParam.ToInt32 ()); + int y = Widget.HighOrder ((int)m.LParam.ToInt32 ()); + + // Correct y since we are in NC land. + NCClientToNC(ref x, ref y); + + if (IconRectangleContains (x, y)){ + if ((DateTime.Now - icon_clicked_time).TotalMilliseconds <= SystemInformation.DoubleClickTime) { + form.Close (); + return true; + } else if (form.Capture) { + icon_dont_show_popup = true; + } + } + return base.HandleLButtonDown (ref m); + } + + protected override bool ShouldRemoveWindowManager (FormBorderStyle style) + { + return false; + } + + protected override void HandleWindowMove (Message m) + { + Point pos = Cursor.Position; + Point move = MouseMove (pos); + + if (move.X == 0 && move.Y == 0) + return; + + int x = virtual_position.X + move.X; + int y = virtual_position.Y + move.Y; + + Rectangle client = mdi_container.ClientRectangle; + if (mdi_container.VerticalScrollbarVisible) + client.Width -= SystemInformation.VerticalScrollBarWidth; + if (mdi_container.HorizontalScrollbarVisible) + client.Height -= SystemInformation.HorizontalScrollBarHeight; + + UpdateVP (x, y, form.Width, form.Height); + + start = pos; + } + + protected override bool HandleNCMouseMove (ref Message m) + { + XplatUI.RequestAdditionalWM_NCMessages (form.Handle, true, true); + return base.HandleNCMouseMove (ref m); + } + + protected override void DrawVirtualPosition (Rectangle virtual_position) + { + ClearVirtualPosition (); + + if (form.Parent != null) + XplatUI.DrawReversibleRectangle (form.Parent.Handle, virtual_position, 2); + prev_virtual_position = virtual_position; + } + + protected override void ClearVirtualPosition () + { + if (prev_virtual_position != Rectangle.Empty && form.Parent != null) + XplatUI.DrawReversibleRectangle (form.Parent.Handle, + prev_virtual_position, 2); + prev_virtual_position = Rectangle.Empty; + } + + protected override void OnWindowFinishedMoving () + { + form.Refresh (); + } + + public override bool IsActive { + get { + if (mdi_container == null) + return false; + return mdi_container.ActiveMdiChild == form; + } + } + + protected override void Activate () + { + if (mdi_container.ActiveMdiChild != form) { + mdi_container.ActivateChild (form); + } + base.Activate (); + } + } +} + diff --git a/source/ShiftUI/Internal/Message.cs b/source/ShiftUI/Internal/Message.cs new file mode 100644 index 0000000..90881c3 --- /dev/null +++ b/source/ShiftUI/Internal/Message.cs @@ -0,0 +1,118 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] + + +// COMPLETE + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Diagnostics; + +namespace ShiftUI { + public struct Message { + private int msg; + private IntPtr hwnd; + private IntPtr lParam; + private IntPtr wParam; + private IntPtr result; + + #region Public Instance Properties + public IntPtr HWnd { + get { return hwnd; } + set { hwnd=value; } + } + + public IntPtr LParam { + get { return lParam; } + set { lParam=value; } + } + + public int Msg { + get { return msg; } + set { msg=value; } + } + + public IntPtr Result { + get { return result; } + set { result=value; } + } + + public IntPtr WParam { + get { return wParam; } + set { wParam=value; } + } + #endregion // Public Instance Properties + + #region Public Static Methods + public static Message Create(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) { + Message new_message = new Message(); + + new_message.msg=msg; + new_message.hwnd=hWnd; + new_message.wParam=wparam; + new_message.lParam=lparam; + return new_message; + } + + public static bool operator == (Message a, Message b) + { + return (a.hwnd == b.hwnd) && (a.lParam == b.lParam) && (a.msg == b.msg) && (a.result == b.result) && (a.wParam == b.wParam); + } + + public static bool operator != (Message a, Message b) + { + return !(a == b); + } + #endregion // Public Static Methods + + #region Public Instance Methods + public override bool Equals(object o) { + if (!(o is Message)) { + return false; + } + + return ((this.msg == ((Message)o).msg) && + (this.hwnd == ((Message)o).hwnd) && + (this.lParam == ((Message)o).lParam) && + (this.wParam == ((Message)o).wParam) && + (this.result == ((Message)o).result)); + } + + public override int GetHashCode() { + return base.GetHashCode(); + } + + public object GetLParam(Type cls) { + object o = Marshal.PtrToStructure(this.lParam, cls); + + return(o); + } + + public override string ToString() { + return String.Format ("msg=0x{0:x} ({1}) hwnd=0x{2:x} wparam=0x{3:x} lparam=0x{4:x} result=0x{5:x}", msg, ((Msg) msg).ToString (), hwnd.ToInt32 (), wParam.ToInt32 (), lParam.ToInt32 (), result.ToInt32 ()); + } + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/MethodInvoker.cs b/source/ShiftUI/Internal/MethodInvoker.cs new file mode 100644 index 0000000..e0275f8 --- /dev/null +++ b/source/ShiftUI/Internal/MethodInvoker.cs @@ -0,0 +1,31 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +namespace ShiftUI { + public delegate void MethodInvoker(); +} diff --git a/source/ShiftUI/Internal/MouseHandler.cs b/source/ShiftUI/Internal/MouseHandler.cs new file mode 100644 index 0000000..9eb86be --- /dev/null +++ b/source/ShiftUI/Internal/MouseHandler.cs @@ -0,0 +1,228 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; +using System.Runtime.InteropServices; + +namespace ShiftUI.CarbonInternal { + internal class MouseHandler : EventHandlerBase, IEventHandler { + internal const uint kEventMouseDown = 1; + internal const uint kEventMouseUp = 2; + internal const uint kEventMouseMoved = 5; + internal const uint kEventMouseDragged = 6; + internal const uint kEventMouseEntered = 8; + internal const uint kEventMouseExited = 9; + internal const uint kEventMouseWheelMoved = 10; + internal const uint kEventMouseScroll = 11; + + internal const uint kEventParamMouseLocation = 1835822947; + internal const uint kEventParamMouseButton = 1835168878; + internal const uint kEventParamMouseWheelAxis = 1836540280; + internal const uint kEventParamMouseWheelDelta = 1836541036; + internal const uint typeLongInteger = 1819242087; + internal const uint typeMouseWheelAxis = 1836540280; + internal const uint typeMouseButton = 1835168878; + internal const uint typeQDPoint = 1363439732; + + internal const uint kEventMouseWheelAxisX = 0; + internal const uint kEventMouseWheelAxisY = 1; + + internal const uint DoubleClickInterval = 7500000; + internal static ClickStruct ClickPending; + + internal MouseHandler (XplatUICarbon driver) : base (driver) {} + + public bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) { + QDPoint qdpoint = new QDPoint (); + CGPoint point = new CGPoint (); + Rect window_bounds = new Rect (); + IntPtr view_handle = IntPtr.Zero; + IntPtr window_handle = IntPtr.Zero; + bool client = true; + ushort button = 0; + Hwnd hwnd; + + GetEventParameter (eventref, kEventParamMouseLocation, typeQDPoint, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (QDPoint)), IntPtr.Zero, ref qdpoint); + GetEventParameter (eventref, kEventParamMouseButton, typeMouseButton, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (ushort)), IntPtr.Zero, ref button); + + if (button == 1 && ((Driver.ModifierKeys & Keys.Widget) != 0)) + button = 2; + + point.x = qdpoint.x; + point.y = qdpoint.y; + + if (FindWindow (qdpoint, ref window_handle) == 5) + return true; + + GetWindowBounds (handle, 33, ref window_bounds); + HIViewFindByID (HIViewGetRoot (handle), new HIViewID (EventHandler.kEventClassWindow, 1), ref window_handle); + + point.x -= window_bounds.left; + point.y -= window_bounds.top; + + HIViewGetSubviewHit (window_handle, ref point, true, ref view_handle); + HIViewConvertPoint (ref point, window_handle, view_handle); + + hwnd = Hwnd.ObjectFromHandle (view_handle); + + if (hwnd != null) + client = (hwnd.ClientWindow == view_handle ? true : false); + + if (XplatUICarbon.Grab.Hwnd != IntPtr.Zero) { + hwnd = Hwnd.ObjectFromHandle (XplatUICarbon.Grab.Hwnd); + client = true; + } + if (hwnd == null) + return true; + + if (client) { + qdpoint.x = (short) point.x; + qdpoint.y = (short) point.y; + + Driver.ScreenToClient (hwnd.Handle, ref qdpoint); + } else { + point.x = qdpoint.x; + point.y = qdpoint.y; + } + + msg.hwnd = hwnd.Handle; + msg.lParam = (IntPtr) ((ushort) point.y << 16 | (ushort) point.x); + + switch (kind) { + case kEventMouseDown: + UpdateMouseState (button, true); + msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE) + ((button - 1) * 3) + 1; + msg.wParam = Driver.GetMousewParam (0); + if (ClickPending.Pending && (((DateTime.Now.Ticks - ClickPending.Time) < DoubleClickInterval) && (msg.hwnd == ClickPending.Hwnd) && (msg.wParam == ClickPending.wParam) && (msg.lParam == ClickPending.lParam) && (msg.message == ClickPending.Message))) { + msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE) + ((button - 1) * 3) + 3; + ClickPending.Pending = false; + } else { + ClickPending.Pending = true; + ClickPending.Hwnd = msg.hwnd; + ClickPending.Message = msg.message; + ClickPending.wParam = msg.wParam; + ClickPending.lParam = msg.lParam; + ClickPending.Time = DateTime.Now.Ticks; + } + break; + case kEventMouseUp: + UpdateMouseState (button, false); + msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE) + ((button - 1) * 3) + 2; + msg.wParam = Driver.GetMousewParam (0); + break; + case kEventMouseDragged: + case kEventMouseMoved: + if (XplatUICarbon.Grab.Hwnd == IntPtr.Zero) { + IntPtr ht = IntPtr.Zero; + if (client) { + ht = (IntPtr) HitTest.HTCLIENT; + NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT); + } else { + ht = (IntPtr) NativeWindow.WndProc (hwnd.client_window, Msg.WM_NCHITTEST, IntPtr.Zero, msg.lParam).ToInt32 (); + NativeWindow.WndProc(hwnd.client_window, Msg.WM_SETCURSOR, msg.hwnd, ht); + } + } + msg.message = (client ? Msg.WM_MOUSEMOVE : Msg.WM_NCMOUSEMOVE); + msg.wParam = Driver.GetMousewParam (0); + break; + case kEventMouseWheelMoved: + case kEventMouseScroll: + UInt16 axis = 0; + Int32 delta = 0; + + GetEventParameter (eventref, kEventParamMouseWheelAxis, typeMouseWheelAxis, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (UInt16)), IntPtr.Zero, ref axis); + GetEventParameter (eventref, kEventParamMouseWheelDelta, typeLongInteger, IntPtr.Zero, (uint)Marshal.SizeOf (typeof (Int32)), IntPtr.Zero, ref delta); + + if (axis == kEventMouseWheelAxisY) { + msg.hwnd = XplatUICarbon.FocusWindow; + msg.message = Msg.WM_MOUSEWHEEL; + msg.wParam = Driver.GetMousewParam (delta*40); + return true; + } + break; + default: + return false; + } + Driver.mouse_position.X = (int) point.x; + Driver.mouse_position.Y = (int) point.y; + return true; + } + + internal bool TranslateMessage (ref MSG msg) { + if (msg.message == Msg.WM_MOUSEMOVE || msg.message == Msg.WM_NCMOUSEMOVE) { + Hwnd hwnd = Hwnd.ObjectFromHandle (msg.hwnd); + if (XplatUICarbon.MouseHwnd == null) { + Driver.PostMessage (hwnd.Handle, Msg.WM_MOUSE_ENTER, IntPtr.Zero, IntPtr.Zero); + Cursor.SetCursor (hwnd.Cursor); + } else if (XplatUICarbon.MouseHwnd.Handle != hwnd.Handle) { + Driver.PostMessage (XplatUICarbon.MouseHwnd.Handle, Msg.WM_MOUSELEAVE, IntPtr.Zero, IntPtr.Zero); + Driver.PostMessage (hwnd.Handle, Msg.WM_MOUSE_ENTER, IntPtr.Zero, IntPtr.Zero); + Cursor.SetCursor (hwnd.Cursor); + } + XplatUICarbon.MouseHwnd = hwnd; + } + + return false; + } + + private void UpdateMouseState (int button, bool down) { + switch (button) { + case 1: + if (down) XplatUICarbon.MouseState |= MouseButtons.Left; else XplatUICarbon.MouseState &= ~MouseButtons.Left; + break; + case 2: + if (down) XplatUICarbon.MouseState |= MouseButtons.Right; else XplatUICarbon.MouseState &= ~MouseButtons.Right; + break; + case 3: + if (down) XplatUICarbon.MouseState |= MouseButtons.Middle; else XplatUICarbon.MouseState &= ~MouseButtons.Middle; + break; + } + } + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref QDPoint data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref Int32 data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetEventParameter (IntPtr eventref, uint name, uint type, IntPtr outtype, uint size, IntPtr outsize, ref ushort data); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern short FindWindow (QDPoint point, ref IntPtr handle); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern int GetWindowBounds (IntPtr handle, uint region, ref Rect bounds); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern int HIViewConvertPoint (ref CGPoint point, IntPtr source_view, IntPtr target_view); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern IntPtr HIViewGetRoot (IntPtr handle); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern int HIViewGetSubviewHit (IntPtr content_view, ref CGPoint point, bool tval, ref IntPtr hit_view); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern int HIViewFindByID (IntPtr root_window, HIViewID id, ref IntPtr view_handle); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal static extern int GetCurrentEventButtonState (); + } +} diff --git a/source/ShiftUI/Internal/NativeWindow.cs b/source/ShiftUI/Internal/NativeWindow.cs new file mode 100644 index 0000000..4657276 --- /dev/null +++ b/source/ShiftUI/Internal/NativeWindow.cs @@ -0,0 +1,324 @@ +// 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-2008 Novell, Inc. +// +// Authors: +// Peter Dennis Bartok [email protected] +// Ivan N. Zlatev <[email protected]> +// + + +// COMPLETE + +#define ExternalExceptionHandler + +using System.Runtime.Remoting; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Collections; +using System.Diagnostics; +using System.Drawing; +using System; + +namespace ShiftUI +{ + public class NativeWindow : MarshalByRefObject, IWin32Window + { + IntPtr window_handle = IntPtr.Zero; + static Hashtable window_collection = new Hashtable(); + + [ThreadStatic] + static NativeWindow WindowCreating; + + #region Public Constructors + public NativeWindow() + { + window_handle=IntPtr.Zero; + } + #endregion // Public Constructors + + #region Public Instance Properties + public IntPtr Handle { + get { + return window_handle; + } + } + #endregion // Public Instance Properties + + #region Public Static Methods + public static NativeWindow FromHandle(IntPtr handle) + { + return FindFirstInTable (handle); + } + #endregion // Public Static Methods + + #region Private and Internal Methods + internal void InvalidateHandle() + { + RemoveFromTable (this); + window_handle = IntPtr.Zero; + } + #endregion + + #region Public Instance Methods + public void AssignHandle(IntPtr handle) + { + RemoveFromTable (this); + window_handle = handle; + AddToTable (this); + OnHandleChange(); + } + + private static void AddToTable (NativeWindow window) + { + IntPtr handle = window.Handle; + if (handle == IntPtr.Zero) + return; + + lock (window_collection) { + object current = window_collection[handle]; + if (current == null) { + window_collection.Add (handle, window); + } else { + NativeWindow currentWindow = current as NativeWindow; + if (currentWindow != null) { + if (currentWindow != window) { + ArrayList windows = new ArrayList (); + windows.Add (currentWindow); + windows.Add (window); + window_collection[handle] = windows; + } + } else { // list of windows + ArrayList windows = (ArrayList) window_collection[handle]; + if (!windows.Contains (window)) + windows.Add (window); + } + } + } + } + + private static void RemoveFromTable (NativeWindow window) + { + IntPtr handle = window.Handle; + if (handle == IntPtr.Zero) + return; + + lock (window_collection) { + object current = window_collection[handle]; + if (current != null) { + NativeWindow currentWindow = current as NativeWindow; + if (currentWindow != null) { + window_collection.Remove (handle); + } else { // list of windows + ArrayList windows = (ArrayList) window_collection[handle]; + windows.Remove (window); + if (windows.Count == 0) + window_collection.Remove (handle); + else if (windows.Count == 1) + window_collection[handle] = windows[0]; + } + } + } + } + + private static NativeWindow FindFirstInTable (IntPtr handle) + { + if (handle == IntPtr.Zero) + return null; + + NativeWindow window = null; + lock (window_collection) { + object current = window_collection[handle]; + if (current != null) { + window = current as NativeWindow; + if (window == null) { + ArrayList windows = (ArrayList) current; + if (windows.Count > 0) + window = (NativeWindow) windows[0]; + } + } + } + return window; + } + + public virtual void CreateHandle(CreateParams cp) + { + if (cp != null) { + WindowCreating = this; + window_handle=XplatUI.CreateWindow(cp); + WindowCreating = null; + + if (window_handle != IntPtr.Zero) + AddToTable (this); + } + + } + + public void DefWndProc(ref Message m) + { + m.Result=XplatUI.DefWndProc(ref m); + } + + public virtual void DestroyHandle() + { + if (window_handle != IntPtr.Zero) { + XplatUI.DestroyWindow(window_handle); + } + } + + public virtual void ReleaseHandle() + { + RemoveFromTable (this); + window_handle=IntPtr.Zero; + OnHandleChange(); + } + + #endregion // Public Instance Methods + + #region Protected Instance Methods + ~NativeWindow() + { + } + + protected virtual void OnHandleChange() + { + } + + protected virtual void OnThreadException(Exception e) + { + Application.OnThreadException(e); + } + + protected virtual void WndProc(ref Message m) + { + DefWndProc(ref m); + } + + internal static IntPtr WndProc(IntPtr hWnd, Msg msg, IntPtr wParam, IntPtr lParam) + { + IntPtr result = IntPtr.Zero; + Message m = new Message(); + m.HWnd = hWnd; + m.Msg = (int)msg; + m.WParam = wParam; + m.LParam = lParam; + m.Result = IntPtr.Zero; + +#if debug + Console.WriteLine("NativeWindow.cs ({0}, {1}, {2}, {3}): result {4}", hWnd, msg, wParam, lParam, m.Result); +#endif + NativeWindow window = null; + + try { + object current = null; + lock (window_collection) { + current = window_collection[hWnd]; + } + + window = current as NativeWindow; + if (current == null) + window = EnsureCreated (window, hWnd); + + if (window != null) { + window.WndProc (ref m); + result = m.Result; + } else if (current is ArrayList) { + ArrayList windows = (ArrayList) current; + lock (windows) { + if (windows.Count > 0) { + window = EnsureCreated ((NativeWindow)windows[0], hWnd); + window.WndProc (ref m); + // the first one is the Widget's one. all others are synthetic, + // so we want only the result from the Widget + result = m.Result; + for (int i=1; i < windows.Count; i++) + ((NativeWindow)windows[i]).WndProc (ref m); + } + } + } else { + result = XplatUI.DefWndProc (ref m); + } + } + catch (Exception ex) { +#if !ExternalExceptionHandler + if (window != null) { + if (msg == Msg.WM_PAINT && window is Widget.WidgetNativeWindow) { + // Replace Widget with a red cross + var Widget = ((Widget.WidgetNativeWindow)window).Owner; + Widget.Hide (); + var redCross = new Widget (Widget.Parent, string.Empty); + redCross.BackColor = Color.White; + redCross.ForeColor = Color.Red; + redCross.Bounds = Widget.Bounds; + redCross.Paint += HandleRedCrossPaint; + } + window.OnThreadException (ex); + } +#else + throw ex; +#endif + } + #if debug + Console.WriteLine("NativeWindow.cs: Message {0}, result {1}", msg, m.Result); + #endif + + return result; + } + + private static void HandleRedCrossPaint (object sender, PaintEventArgs e) + { + var Widget = sender as Widget; + using (var pen = new Pen (Widget.ForeColor, 2)) { + var paintRect = Widget.DisplayRectangle; + e.Graphics.DrawRectangle (pen, paintRect.Left + 1, + paintRect.Top + 1, paintRect.Width - 1, paintRect.Height - 1); + // NOTE: .NET's drawing of the red cross seems to have a bug + // that draws the bottom and right of the rectangle only 1 pixel + // wide. We would get a nicer rectangle using the following code, + // but that runs into a problem with libgdiplus. + //var paintRect = Widget.DisplayRectangle; + //paintRect.Inflate (-1, -1); + //e.Graphics.DrawRectangle (pen, paintRect); + e.Graphics.DrawLine (pen, paintRect.Location, + paintRect.Location + paintRect.Size); + e.Graphics.DrawLine (pen, new Point (paintRect.Left, paintRect.Bottom), + new Point (paintRect.Right, paintRect.Top)); + } + } + + private static NativeWindow EnsureCreated (NativeWindow window, IntPtr hWnd) + { + // we need to do this AssignHandle here instead of relying on + // Widget.WndProc to do it, because subclasses can override + // WndProc, install their own WM_CREATE block, and look at + // this.Handle, and it needs to be set. Otherwise, we end up + // recursively creating windows and emitting WM_CREATE. + if (window == null && WindowCreating != null) { + window = WindowCreating; + WindowCreating = null; + if (window.Handle == IntPtr.Zero) + window.AssignHandle (hWnd); + } + return window; + } + #endregion // Protected Instance Methods + } +} diff --git a/source/ShiftUI/Internal/OpacityConverter.cs b/source/ShiftUI/Internal/OpacityConverter.cs new file mode 100644 index 0000000..89fcc63 --- /dev/null +++ b/source/ShiftUI/Internal/OpacityConverter.cs @@ -0,0 +1,74 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using System.Text; + +namespace ShiftUI { + public class OpacityConverter : TypeConverter { + #region Public Constructors + public OpacityConverter() { + } + #endregion // Public Constructors + + #region Public Instance Methods + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return false; + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { + if (value is string) { + string s; + + s = (string)value; + if (s.EndsWith("%")) { + s = ((string)value).Substring(0, ((string)value).Length - 1); + } + return Double.Parse(s, NumberStyles.Any, culture) / 100; + } + return base.ConvertFrom (context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { + if (destinationType == typeof(string)) { + double v; + + v = ((double)value) * 100; + return v.ToString() + "%"; + } + return base.ConvertTo (context, culture, value, destinationType); + } + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/OpenTreeNodeEnumerator.cs b/source/ShiftUI/Internal/OpenTreeNodeEnumerator.cs new file mode 100644 index 0000000..f2d9453 --- /dev/null +++ b/source/ShiftUI/Internal/OpenTreeNodeEnumerator.cs @@ -0,0 +1,112 @@ +// 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 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + + +using System; +using System.Collections; + +namespace ShiftUI { + + internal class OpenTreeNodeEnumerator : IEnumerator { + + private TreeNode start; + private TreeNode current; + private bool started; + + public OpenTreeNodeEnumerator (TreeNode start) + { + this.start = start; + } + + public object Current { + get { return current; } + } + + public TreeNode CurrentNode { + get { return current; } + } + + public bool MoveNext () + { + if (!started) { + started = true; + current = start; + return (current != null); + } + + if (current.is_expanded && current.Nodes.Count > 0) { + current = current.Nodes [0]; + return true; + } + + TreeNode prev = current; + TreeNode next = current.NextNode; + while (next == null) { + // The next node is null so we need to move back up the tree until we hit the top + if (prev.parent == null) + return false; + prev = prev.parent; + if (prev.parent != null) + next = prev.NextNode; + } + current = next; + return true; + } + + public bool MovePrevious () + { + if (!started) { + started = true; + current = start; + return (current != null); + } + + if (current.PrevNode != null) { + // Drill down as far as possible + TreeNode prev = current.PrevNode; + TreeNode walk = prev; + while (walk != null) { + prev = walk; + if (!walk.is_expanded) + break; + walk = walk.LastNode; + } + current = prev; + return true; + } + + if (current.Parent == null) + return false; + + current = current.Parent; + return true; + } + + public void Reset () + { + started = false; + } + } +} + diff --git a/source/ShiftUI/Internal/OwnerDrawPropertyBag.cs b/source/ShiftUI/Internal/OwnerDrawPropertyBag.cs new file mode 100644 index 0000000..c9f8e6f --- /dev/null +++ b/source/ShiftUI/Internal/OwnerDrawPropertyBag.cs @@ -0,0 +1,101 @@ +// 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 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + +using System; +using System.Drawing; +using System.Runtime.Serialization; + +namespace ShiftUI { + + [Serializable] + public class OwnerDrawPropertyBag : MarshalByRefObject, ISerializable { + + private Color fore_color; + private Color back_color; + private Font font; + + internal OwnerDrawPropertyBag () + { + fore_color = back_color = Color.Empty; + } + + private OwnerDrawPropertyBag (Color fore_color, Color back_color, Font font) + { + this.fore_color = fore_color; + this.back_color = back_color; + this.font = font; + } + + protected OwnerDrawPropertyBag(SerializationInfo info, StreamingContext context) { + SerializationInfoEnumerator en; + SerializationEntry e; + + en = info.GetEnumerator(); + + while (en.MoveNext()) { + e = en.Current; + switch(e.Name) { + case "Font": font = (Font)e.Value; break; + case "ForeColor": fore_color = (Color)e.Value; break; + case "BackColor": back_color = (Color)e.Value; break; + } + } + } + + + public Color ForeColor { + get { return fore_color; } + set { fore_color = value; } + } + + public Color BackColor { + get { return back_color; } + set { back_color = value; } + } + + public Font Font { + get { return font; } + set { font = value; } + } + + public virtual bool IsEmpty () + { + return (font == null && fore_color.IsEmpty && back_color.IsEmpty); + } + + void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context) + { + si.AddValue ("BackColor", BackColor); + si.AddValue ("ForeColor", ForeColor); + si.AddValue ("Font", Font); + } + + public static OwnerDrawPropertyBag Copy (OwnerDrawPropertyBag value) + { + return new OwnerDrawPropertyBag (value.ForeColor, value.BackColor, value.Font); + } + } +} + + diff --git a/source/ShiftUI/Internal/Padding.cs b/source/ShiftUI/Internal/Padding.cs new file mode 100644 index 0000000..63bdc64 --- /dev/null +++ b/source/ShiftUI/Internal/Padding.cs @@ -0,0 +1,170 @@ +// 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) 2005,2006 Novell, Inc. (http://www.novell.com) +// +// Author: +// Pedro MartÃnez Juliá <[email protected]> +// Daniel Nauck (dna(at)mono-project(dot)de) +// + +using System; +using System.ComponentModel; +using System.Drawing; + +namespace ShiftUI { + + [Serializable] + [TypeConverter(typeof(PaddingConverter))] + public struct Padding { + + //NOTE: "_var" field name is required by serialization. + private int _bottom; + private int _left; + private int _right; + private int _top; + private bool _all; + + public Padding (int all) { + _left = all; + _right = all; + _top = all; + _bottom = all; + _all = true; + } + + public Padding (int left, int top, int right, int bottom) { + _left = left; + _right = right; + _top = top; + _bottom = bottom; + _all = (_left == _top) && (_left == _right) && (_left == _bottom); + } + + public static readonly Padding Empty = new Padding(0); + + [RefreshProperties(RefreshProperties.All)] + public int All { + get { + if(!_all) + return -1; + else + return _top; + } + set { + _all = true; + _left = _top = _right = _bottom = value; + } + } + + [RefreshProperties(RefreshProperties.All)] + public int Bottom { + get { return _bottom; } + set { + _bottom = value; + _all = false; + } + } + + [Browsable(false)] + public int Horizontal { + get { return _left + _right; } + } + + [RefreshProperties(RefreshProperties.All)] + public int Left { + get { return _left; } + set { + _left = value; + _all = false; + } + } + + [RefreshProperties(RefreshProperties.All)] + public int Right { + get { return _right; } + set { + _right = value; + _all = false; + } + } + + [Browsable(false)] + public Size Size { + get { return new Size(Horizontal, Vertical); } + } + + [RefreshProperties(RefreshProperties.All)] + public int Top { + get { return _top; } + set { + _top = value; + _all = false; + } + } + + [Browsable(false)] + public int Vertical { + get { return _top + _bottom; } + } + + public static Padding Add (Padding p1, Padding p2) { + return p1 + p2; + } + + public override bool Equals (object other) { + if (other is Padding) { + Padding other_aux = (Padding) other; + return _left == other_aux.Left && + _top == other_aux.Top && + _right == other_aux.Right && + _bottom == other_aux.Bottom; + } + return false; + } + + public override int GetHashCode () { + return _top ^ _bottom ^ _left ^ _right; + } + + public static Padding operator+ (Padding p1, Padding p2) { + return new Padding(p1.Left + p2.Left, p1.Top + p2.Top, p1.Right + p2.Right, p1.Bottom + p2.Bottom); + } + + public static bool operator== (Padding p1, Padding p2) { + return p1.Equals(p2); + } + + public static bool operator!= (Padding p1, Padding p2) { + return !(p1.Equals(p2)); + } + + public static Padding operator- (Padding p1, Padding p2) { + return new Padding(p1.Left - p2.Left, p1.Top - p2.Top, p1.Right - p2.Right, p1.Bottom - p2.Bottom); + } + + public static Padding Subtract (Padding p1, Padding p2) { + return p1 - p2; + } + + public override string ToString () { + return "{Left=" + Left + ",Top="+ Top + ",Right=" + Right + ",Bottom=" + Bottom + "}"; + } + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/PaddingConverter.cs b/source/ShiftUI/Internal/PaddingConverter.cs new file mode 100644 index 0000000..447f65d --- /dev/null +++ b/source/ShiftUI/Internal/PaddingConverter.cs @@ -0,0 +1,125 @@ +// 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) 2007 Novell, Inc. +// + +using System.ComponentModel; +using System.Collections; +using System.Reflection; +using System.Globalization; +using System.ComponentModel.Design.Serialization; // e.g. InstanceDescriptor +using System; + +namespace ShiftUI +{ + public class PaddingConverter : TypeConverter + { + public PaddingConverter () + { + } + + #region Public Instance Methods + public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof (string)) + return true; + + return false; + } + + public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof (string)) + return true; + else if (destinationType == typeof(InstanceDescriptor)) + return true; + + return false; + } + + public override object ConvertFrom (ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + if ((value == null) || !(value is String)) + return base.ConvertFrom (context, culture, value); + + if (culture == null) + culture = CultureInfo.CurrentCulture; + + string[] parts = ((string)value).Split (culture.TextInfo.ListSeparator.ToCharArray ()); + + return new Padding (int.Parse (parts[0].Trim ()), int.Parse (parts[1].Trim ()), int.Parse (parts[2].Trim ()), int.Parse (parts[3].Trim ())); + } + + public override object ConvertTo (ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + { + if (value is Padding) { + Padding p = (Padding)value; + if (destinationType == typeof (string)) { + if (culture == null) + culture = CultureInfo.CurrentCulture; + return string.Format ("{0}{4} {1}{4} {2}{4} {3}", p.Left, p.Top, p.Right, p.Bottom, culture.TextInfo.ListSeparator); + } else if (destinationType == typeof (InstanceDescriptor)) { + Type[] types; + Object[] values; + if (p.All != -1) { + types = new Type[] { typeof(int) }; + values = new Object[] { p.All }; + } else { + types = new Type[] { typeof(int), typeof(int), typeof(int), typeof(int) }; + values = new Object[] { p.Left, p.Top, p.Right, p.Bottom }; + } + ConstructorInfo ci = typeof(Padding).GetConstructor (types); + return new InstanceDescriptor (ci, values); + } + } + return base.ConvertTo (context, culture, value, destinationType); + } + + public override object CreateInstance (ITypeDescriptorContext context, IDictionary propertyValues) + { + if (propertyValues == null) + throw new ArgumentNullException ("propertyValues"); + if (context == null) + throw new ArgumentNullException ("context"); + + Padding old = (Padding)context.PropertyDescriptor.GetValue (context.Instance); + if (old.All == (int)propertyValues["All"]) + return new Padding ((int)propertyValues["Left"], (int)propertyValues["Top"], (int)propertyValues["Right"], (int)propertyValues["Bottom"]); + else + return new Padding ((int)propertyValues["All"]); + } + + public override bool GetCreateInstanceSupported (ITypeDescriptorContext context) + { + return true; + } + + public override PropertyDescriptorCollection GetProperties (ITypeDescriptorContext context, object value, Attribute[] attributes) + { + return TypeDescriptor.GetProperties (typeof (Padding), attributes); + } + + public override bool GetPropertiesSupported (ITypeDescriptorContext context) + { + return true; + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/PaintEventArgs.cs b/source/ShiftUI/Internal/PaintEventArgs.cs new file mode 100644 index 0000000..0f05b84 --- /dev/null +++ b/source/ShiftUI/Internal/PaintEventArgs.cs @@ -0,0 +1,98 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + + +// COMPLETE + +using System.Drawing; +using System; + +namespace ShiftUI { + public class PaintEventArgs : EventArgs, IDisposable { + private Graphics graphics; + private Rectangle clip_rectangle; + internal bool Handled; + private bool disposed; + + #region Public Constructors + public PaintEventArgs (Graphics graphics, Rectangle clipRect) + { + if (graphics == null) + throw new ArgumentNullException ("graphics"); + + this.graphics=graphics; + this.clip_rectangle=clipRect; + } + #endregion // Public Constructors + + #region Public Instance Properties + public Rectangle ClipRectangle { + get { + return this.clip_rectangle; + } + } + + public Graphics Graphics { + get { + return this.graphics; + } + } + #endregion // Public Instance Properties + + #region Public Instance Methods + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + #endregion // Public Instance Methods + + // Returns the previous graphics + internal Graphics SetGraphics (Graphics g) + { + Graphics res = graphics; + graphics = g; + + return res; + } + + internal void SetClip (Rectangle clip) + { + clip_rectangle = clip; + } + + #region Protected Instance Methods + ~PaintEventArgs() { + Dispose(false); + } + + protected virtual void Dispose (bool disposing) + { + if (!disposed) { + disposed = true; + } + } + #endregion // Protected Instance Methods + } +} diff --git a/source/ShiftUI/Internal/PaintEventHandler.cs b/source/ShiftUI/Internal/PaintEventHandler.cs new file mode 100644 index 0000000..9e074ee --- /dev/null +++ b/source/ShiftUI/Internal/PaintEventHandler.cs @@ -0,0 +1,33 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// +// + + +// COMPLETE + +namespace ShiftUI +{ + public delegate void PaintEventHandler (object sender, PaintEventArgs e); +} diff --git a/source/ShiftUI/Internal/Pasteboard.cs b/source/ShiftUI/Internal/Pasteboard.cs new file mode 100644 index 0000000..57a75b4 --- /dev/null +++ b/source/ShiftUI/Internal/Pasteboard.cs @@ -0,0 +1,106 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton ([email protected]) +// +// + + +using System; +using System.Runtime.InteropServices; +using ShiftUI; + +namespace ShiftUI.CarbonInternal { + internal class Pasteboard { + private static IntPtr primary_pbref; + private static IntPtr app_pbref; + + private static IntPtr internal_format; + + static Pasteboard () { + PasteboardCreate (XplatUICarbon.__CFStringMakeConstantString("com.apple.pasteboard.clipboard"), ref primary_pbref); + PasteboardCreate (IntPtr.Zero, ref app_pbref); + internal_format = XplatUICarbon.__CFStringMakeConstantString ("com.novell.mono.mwf.pasteboard"); + } + + internal static object Retrieve (IntPtr pbref, int key) { + UInt32 count = 0; + + key = (int)internal_format; + + PasteboardGetItemCount (pbref, ref count); + for (int i = 1; i <= count; i++) { + UInt32 itemid = 0; + + PasteboardGetItemIdentifier (pbref, (UInt32)i, ref itemid); + //FIXME: We should get all the flavors and enumerate but we're cheating for now + if (itemid == 0xFACE) { + IntPtr pbdata = IntPtr.Zero; + + PasteboardCopyItemFlavorData (pbref, (UInt32)0xFACE, (UInt32)key, ref pbdata); + if (pbdata != IntPtr.Zero) { + GCHandle handle = (GCHandle) Marshal.ReadIntPtr (CFDataGetBytePtr (pbdata)); + + return handle.Target; + } + } + } + return null; + } + + internal static void Store (IntPtr pbref, object data, int key) { + IntPtr gcdata = (IntPtr) GCHandle.Alloc (data); + IntPtr pbdata = CFDataCreate (IntPtr.Zero, ref gcdata, Marshal.SizeOf (typeof (IntPtr))); + + key = (int)internal_format; + + PasteboardClear (pbref); + PasteboardPutItemFlavor (pbref, (UInt32)0xFACE, (UInt32)key, pbdata, 0); + } + + internal static IntPtr Primary { + get { return primary_pbref; } + } + + internal static IntPtr Application { + get { return app_pbref; } + } + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern IntPtr CFDataCreate (IntPtr allocator, ref IntPtr buf, Int32 length); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern IntPtr CFDataGetBytePtr (IntPtr data); + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int PasteboardClear (IntPtr pbref); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int PasteboardCreate (IntPtr str, ref IntPtr pbref); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int PasteboardCopyItemFlavorData (IntPtr pbref, UInt32 itemid, UInt32 key, ref IntPtr data); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int PasteboardGetItemCount (IntPtr pbref, ref UInt32 count); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int PasteboardGetItemIdentifier (IntPtr pbref, UInt32 itemindex, ref UInt32 itemid); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int PasteboardPutItemFlavor (IntPtr pbref, UInt32 itemid, UInt32 key, IntPtr data, UInt32 flags); + } +} diff --git a/source/ShiftUI/Internal/PopupEventArgs.cs b/source/ShiftUI/Internal/PopupEventArgs.cs new file mode 100644 index 0000000..b11b837 --- /dev/null +++ b/source/ShiftUI/Internal/PopupEventArgs.cs @@ -0,0 +1,71 @@ +// +// PopupEventArgs.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +using System.ComponentModel; +using System.Drawing; + +namespace ShiftUI +{ + public class PopupEventArgs : CancelEventArgs + { + private Widget associated_control; + private IWin32Window associated_window; + private bool is_balloon; + private Size tool_tip_size; + + #region Public Constructors + public PopupEventArgs (IWin32Window associatedWindow, Widget associatedControl, bool isBalloon, Size size) : base () + { + this.associated_window = associatedWindow; + this.associated_control = associatedControl; + this.is_balloon = isBalloon; + this.tool_tip_size = size; + } + #endregion // Public Constructors + + #region Public Instance Properties + public Widget AssociatedControl { + get { return this.associated_control; } + } + + public IWin32Window AssociatedWindow { + get { return this.associated_window; } + } + + public bool IsBalloon { + get { return this.is_balloon; } + } + + public Size ToolTipSize { + get { return this.tool_tip_size; } + set { this.tool_tip_size = value; } + } + #endregion // Public Instance Properties + } +} diff --git a/source/ShiftUI/Internal/PopupEventHandler.cs b/source/ShiftUI/Internal/PopupEventHandler.cs new file mode 100644 index 0000000..ba87315 --- /dev/null +++ b/source/ShiftUI/Internal/PopupEventHandler.cs @@ -0,0 +1,32 @@ +// +// PopupEventHandler.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +namespace ShiftUI +{ + public delegate void PopupEventHandler (object sender, PopupEventArgs e); +} diff --git a/source/ShiftUI/Internal/PreProcessControlState.cs b/source/ShiftUI/Internal/PreProcessControlState.cs new file mode 100644 index 0000000..bd573a5 --- /dev/null +++ b/source/ShiftUI/Internal/PreProcessControlState.cs @@ -0,0 +1,38 @@ +// +// PreProcessWidgetstate.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +namespace ShiftUI +{ + public enum PreProcessWidgetstate + { + MessageProcessed = 0, + MessageNeeded = 1, + MessageNotNeeded = 2 + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/ProfessionalColorTable.cs b/source/ShiftUI/Internal/ProfessionalColorTable.cs new file mode 100644 index 0000000..ff42d37 --- /dev/null +++ b/source/ShiftUI/Internal/ProfessionalColorTable.cs @@ -0,0 +1,654 @@ +// +// ProfessionalColorTable.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System.Drawing; +using ShiftUI.VisualStyles; + +namespace ShiftUI +{ + public class ProfessionalColorTable + { + #region Private Variables + private bool use_system_colors = false; + + private Color button_checked_gradient_begin; + private Color button_checked_gradient_end; + private Color button_checked_gradient_middle; + private Color button_checked_highlight; + private Color button_checked_highlight_border; + private Color button_pressed_border; + private Color button_pressed_gradient_begin; + private Color button_pressed_gradient_end; + private Color button_pressed_gradient_middle; + private Color button_pressed_highlight; + private Color button_pressed_highlight_border; + private Color button_selected_border; + private Color button_selected_gradient_begin; + private Color button_selected_gradient_end; + private Color button_selected_gradient_middle; + private Color button_selected_highlight; + private Color button_selected_highlight_border; + private Color check_background; + private Color check_pressed_background; + private Color check_selected_background; + private Color grip_dark; + private Color grip_light; + private Color image_margin_gradient_begin; + private Color image_margin_gradient_end; + private Color image_margin_gradient_middle; + private Color image_margin_revealed_gradient_begin; + private Color image_margin_revealed_gradient_end; + private Color image_margin_revealed_gradient_middle; + private Color menu_border; + private Color menu_item_border; + private Color menu_item_pressed_gradient_begin; + private Color menu_item_pressed_gradient_end; + private Color menu_item_pressed_gradient_middle; + private Color menu_item_selected; + private Color menu_item_selected_gradient_begin; + private Color menu_item_selected_gradient_end; + private Color menu_strip_gradient_begin; + private Color menu_strip_gradient_end; + private Color overflow_button_gradient_begin; + private Color overflow_button_gradient_end; + private Color overflow_button_gradient_middle; + private Color rafting_container_gradient_begin; + private Color rafting_container_gradient_end; + private Color separator_dark; + private Color separator_light; + private Color status_strip_gradient_begin; + private Color status_strip_gradient_end; + private Color tool_strip_border; + private Color tool_strip_content_panel_gradient_begin; + private Color tool_strip_content_panel_gradient_end; + private Color tool_strip_drop_down_background; + private Color tool_strip_gradient_begin; + private Color tool_strip_gradient_end; + private Color tool_strip_gradient_middle; + private Color tool_strip_panel_gradient_begin; + private Color tool_strip_panel_gradient_end; + #endregion + + #region Public Constructor + public ProfessionalColorTable () + { + CalculateColors (); + } + #endregion + + #region Public Properties + public virtual Color ButtonCheckedGradientBegin { get { return this.button_checked_gradient_begin; } } + public virtual Color ButtonCheckedGradientEnd { get { return this.button_checked_gradient_end; } } + public virtual Color ButtonCheckedGradientMiddle { get { return this.button_checked_gradient_middle; } } + public virtual Color ButtonCheckedHighlight { get { return this.button_checked_highlight; } } + public virtual Color ButtonCheckedHighlightBorder { get { return this.button_checked_highlight_border; } } + public virtual Color ButtonPressedBorder { get { return this.button_pressed_border; } } + public virtual Color ButtonPressedGradientBegin { get { return this.button_pressed_gradient_begin; } } + public virtual Color ButtonPressedGradientEnd { get { return this.button_pressed_gradient_end; } } + public virtual Color ButtonPressedGradientMiddle { get { return this.button_pressed_gradient_middle; } } + public virtual Color ButtonPressedHighlight { get { return this.button_pressed_highlight; } } + public virtual Color ButtonPressedHighlightBorder { get { return this.button_pressed_highlight_border; } } + public virtual Color ButtonSelectedBorder { get { return this.button_selected_border; } } + public virtual Color ButtonSelectedGradientBegin { get { return this.button_selected_gradient_begin; } } + public virtual Color ButtonSelectedGradientEnd { get { return this.button_selected_gradient_end; } } + public virtual Color ButtonSelectedGradientMiddle { get { return this.button_selected_gradient_middle; } } + public virtual Color ButtonSelectedHighlight { get { return this.button_selected_highlight; } } + public virtual Color ButtonSelectedHighlightBorder { get { return this.button_selected_highlight_border; } } + public virtual Color CheckBackground { get { return this.check_background; } } + public virtual Color CheckPressedBackground { get { return this.check_pressed_background; } } + public virtual Color CheckSelectedBackground { get { return this.check_selected_background; } } + public virtual Color GripDark { get { return this.grip_dark; } } + public virtual Color GripLight { get { return this.grip_light; } } + public virtual Color ImageMarginGradientBegin { get { return this.image_margin_gradient_begin; } } + public virtual Color ImageMarginGradientEnd { get { return this.image_margin_gradient_end; } } + public virtual Color ImageMarginGradientMiddle { get { return this.image_margin_gradient_middle; } } + public virtual Color ImageMarginRevealedGradientBegin { get { return this.image_margin_revealed_gradient_begin; } } + public virtual Color ImageMarginRevealedGradientEnd { get { return this.image_margin_revealed_gradient_end; } } + public virtual Color ImageMarginRevealedGradientMiddle { get { return this.image_margin_revealed_gradient_middle; } } + public virtual Color MenuBorder { get { return this.menu_border; } } + public virtual Color MenuItemBorder { get { return this.menu_item_border; } } + public virtual Color MenuItemPressedGradientBegin { get { return this.menu_item_pressed_gradient_begin; } } + public virtual Color MenuItemPressedGradientEnd { get { return this.menu_item_pressed_gradient_end; } } + public virtual Color MenuItemPressedGradientMiddle { get { return this.menu_item_pressed_gradient_middle; } } + public virtual Color MenuItemSelected { get { return this.menu_item_selected; } } + public virtual Color MenuItemSelectedGradientBegin { get { return this.menu_item_selected_gradient_begin; } } + public virtual Color MenuItemSelectedGradientEnd { get { return this.menu_item_selected_gradient_end; } } + public virtual Color MenuStripGradientBegin { get { return this.menu_strip_gradient_begin; } } + public virtual Color MenuStripGradientEnd { get { return this.menu_strip_gradient_end; } } + public virtual Color OverflowButtonGradientBegin { get { return this.overflow_button_gradient_begin; } } + public virtual Color OverflowButtonGradientEnd { get { return this.overflow_button_gradient_end; } } + public virtual Color OverflowButtonGradientMiddle { get { return this.overflow_button_gradient_middle; } } + public virtual Color RaftingContainerGradientBegin { get { return this.rafting_container_gradient_begin; } } + public virtual Color RaftingContainerGradientEnd { get { return this.rafting_container_gradient_end; } } + public virtual Color SeparatorDark { get { return this.separator_dark; } } + public virtual Color SeparatorLight { get { return this.separator_light; } } + public virtual Color StatusStripGradientBegin { get { return this.status_strip_gradient_begin; } } + public virtual Color StatusStripGradientEnd { get { return this.status_strip_gradient_end; } } + public virtual Color ToolStripBorder { get { return this.tool_strip_border; } } + public virtual Color ToolStripContentPanelGradientBegin { get { return this.tool_strip_content_panel_gradient_begin; } } + public virtual Color ToolStripContentPanelGradientEnd { get { return this.tool_strip_content_panel_gradient_end; } } + public virtual Color ToolStripDropDownBackground { get { return this.tool_strip_drop_down_background; } } + public virtual Color ToolStripGradientBegin { get { return this.tool_strip_gradient_begin; } } + public virtual Color ToolStripGradientEnd { get { return this.tool_strip_gradient_end; } } + public virtual Color ToolStripGradientMiddle { get { return this.tool_strip_gradient_middle; } } + public virtual Color ToolStripPanelGradientBegin { get { return this.tool_strip_panel_gradient_begin; } } + public virtual Color ToolStripPanelGradientEnd { get { return this.tool_strip_panel_gradient_end; } } + public bool UseSystemColors { + get { return use_system_colors; } + set { + if (value != use_system_colors) { + use_system_colors = value; CalculateColors (); + } + } + } + #endregion + + #region Private Methods + private void CalculateColors () + { + switch (GetCurrentStyle ()) { + case ColorSchemes.Classic: + button_checked_gradient_begin = Color.Empty; + button_checked_gradient_end = Color.Empty; + button_checked_gradient_middle = Color.Empty; + button_checked_highlight = Color.FromArgb (184, 191, 211); + button_checked_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_pressed_border = Color.FromKnownColor (KnownColor.Highlight); + button_pressed_gradient_begin = Color.FromArgb (133, 146, 181); + button_pressed_gradient_end = Color.FromArgb (133, 146, 181); + button_pressed_gradient_middle = Color.FromArgb (133, 146, 181); + button_pressed_highlight = Color.FromArgb (131, 144, 179); + button_pressed_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_selected_border = Color.FromKnownColor (KnownColor.Highlight); + button_selected_gradient_begin = Color.FromArgb (182, 189, 210); + button_selected_gradient_end = Color.FromArgb (182, 189, 210); + button_selected_gradient_middle = Color.FromArgb (182, 189, 210); + button_selected_highlight = Color.FromArgb (184, 191, 211); + button_selected_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + check_background = Color.FromKnownColor (KnownColor.Highlight); + check_pressed_background = Color.FromArgb (133, 146, 181); + check_selected_background = Color.FromArgb (133, 146, 181); + + grip_dark = Color.FromArgb (160, 160, 160); + grip_light = SystemColors.Window; + + image_margin_gradient_begin = Color.FromArgb (245, 244, 242); + image_margin_gradient_end = SystemColors.Control; + image_margin_gradient_middle = Color.FromArgb (234, 232, 228); + image_margin_revealed_gradient_begin = Color.FromArgb (238, 236, 233); + image_margin_revealed_gradient_end = Color.FromArgb (216, 213, 206); + image_margin_revealed_gradient_middle = Color.FromArgb (225, 222, 217); + + menu_border = Color.FromArgb (102, 102, 102); + menu_item_border = SystemColors.Highlight; + + menu_item_pressed_gradient_begin = Color.FromArgb (245, 244, 242); + menu_item_pressed_gradient_end = Color.FromArgb (234, 232, 228); + menu_item_pressed_gradient_middle = Color.FromArgb (225, 222, 217); + menu_item_selected = SystemColors.Window; + menu_item_selected_gradient_begin = Color.FromArgb (182, 189, 210); + menu_item_selected_gradient_end = Color.FromArgb (182, 189, 210); + + menu_strip_gradient_begin = SystemColors.ButtonFace; + menu_strip_gradient_end = Color.FromArgb (246, 245, 244); + + overflow_button_gradient_begin = Color.FromArgb (225, 222, 217); + overflow_button_gradient_end = SystemColors.ButtonShadow; + overflow_button_gradient_middle = Color.FromArgb (216, 213, 206); + + rafting_container_gradient_begin = SystemColors.ButtonFace; + rafting_container_gradient_end = Color.FromArgb (246, 245, 244); + + separator_dark = Color.FromArgb (166, 166, 166); + separator_light = SystemColors.ButtonHighlight; + + status_strip_gradient_begin = SystemColors.ButtonFace; + status_strip_gradient_end = Color.FromArgb (246, 245, 244); + + tool_strip_border = Color.FromArgb (219, 216, 209); + tool_strip_content_panel_gradient_begin = SystemColors.ButtonFace; + tool_strip_content_panel_gradient_end = Color.FromArgb (246, 245, 244); + tool_strip_drop_down_background = SystemColors.Window; + + tool_strip_gradient_begin = Color.FromArgb (245, 244, 242); + tool_strip_gradient_end = SystemColors.ButtonFace; + tool_strip_gradient_middle = Color.FromArgb (234, 232, 228); + + tool_strip_panel_gradient_begin = SystemColors.ButtonFace; + tool_strip_panel_gradient_end = Color.FromArgb (246, 245, 244); + break; + case ColorSchemes.NormalColor: + button_checked_gradient_begin = use_system_colors ? Color.Empty : Color.FromArgb (255, 223, 154); + button_checked_gradient_end = use_system_colors ? Color.Empty : Color.FromArgb (255, 166, 76); + button_checked_gradient_middle = use_system_colors ? Color.Empty : Color.FromArgb (255, 195, 116); + button_checked_highlight = Color.FromArgb (195, 211, 237); + button_checked_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + button_pressed_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (0, 0, 128); + button_pressed_gradient_begin = use_system_colors ? Color.FromArgb (152, 181, 226) : Color.FromArgb (254, 128, 62); + button_pressed_gradient_end = use_system_colors ? Color.FromArgb (152, 181, 226) : Color.FromArgb (255, 223, 154); + button_pressed_gradient_middle = use_system_colors ? Color.FromArgb (152, 181, 226) : Color.FromArgb (255, 177, 109); + button_pressed_highlight = use_system_colors ? Color.FromArgb (150, 179, 225) : Color.FromArgb (150, 179, 225); + button_pressed_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + button_selected_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (0, 0, 128); + button_selected_gradient_begin = use_system_colors ? Color.FromArgb (193, 210, 238) : Color.FromArgb (255, 255, 222); + button_selected_gradient_end = use_system_colors ? Color.FromArgb (193, 210, 238) : Color.FromArgb (255, 203, 136); + button_selected_gradient_middle = use_system_colors ? Color.FromArgb (193, 210, 238) : Color.FromArgb (255, 225, 172); + button_selected_highlight = use_system_colors ? Color.FromArgb (195, 211, 237) : Color.FromArgb (195, 211, 237); + button_selected_highlight_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (0, 0, 128); + + check_background = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (255, 192, 111); + check_pressed_background = use_system_colors ? Color.FromArgb (152, 181, 226) : Color.FromArgb (254, 128, 62); + check_selected_background = use_system_colors ? Color.FromArgb (152, 181, 226) : Color.FromArgb (254, 128, 62); + + grip_dark = use_system_colors ? Color.FromArgb (193, 190, 179) : Color.FromArgb (39, 65, 118); + grip_light = use_system_colors ? SystemColors.Window : Color.FromArgb (255, 255, 255); + + image_margin_gradient_begin = use_system_colors ? Color.FromArgb (251, 250, 246) : Color.FromArgb (227, 239, 255); + image_margin_gradient_end = use_system_colors ? SystemColors.Control : Color.FromArgb (123, 164, 224); + image_margin_gradient_middle = use_system_colors ? Color.FromArgb (246, 244, 236) : Color.FromArgb (203, 225, 252); + image_margin_revealed_gradient_begin = use_system_colors ? Color.FromArgb (247, 246, 239) : Color.FromArgb (203, 221, 246); + image_margin_revealed_gradient_end = use_system_colors ? Color.FromArgb (238, 235, 220) : Color.FromArgb (114, 155, 215); + image_margin_revealed_gradient_middle = use_system_colors ? Color.FromArgb (242, 240, 228) : Color.FromArgb (161, 197, 249); + + menu_border = use_system_colors ? Color.FromArgb (138, 134, 122) : Color.FromArgb (0, 45, 150); + menu_item_border = use_system_colors ? SystemColors.Highlight : Color.FromArgb (0, 0, 128); + + menu_item_pressed_gradient_begin = use_system_colors ? Color.FromArgb (251, 250, 246) : Color.FromArgb (227, 239, 255); + menu_item_pressed_gradient_end = use_system_colors ? Color.FromArgb (246, 244, 236) : Color.FromArgb (123, 164, 224); + menu_item_pressed_gradient_middle = use_system_colors ? Color.FromArgb (242, 240, 228) : Color.FromArgb (161, 197, 249); + menu_item_selected = use_system_colors ? SystemColors.Window : Color.FromArgb (255, 238, 194); + menu_item_selected_gradient_begin = use_system_colors ? Color.FromArgb (193, 210, 238) : Color.FromArgb (255, 255, 222); + menu_item_selected_gradient_end = use_system_colors ? Color.FromArgb (193, 210, 238) : Color.FromArgb (255, 203, 136); + + menu_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (158, 190, 245); + menu_strip_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (196, 218, 250); + + overflow_button_gradient_begin = use_system_colors ? Color.FromArgb (242, 240, 228) : Color.FromArgb (127, 177, 250); + overflow_button_gradient_end = use_system_colors ? SystemColors.ButtonShadow : Color.FromArgb (0, 53, 145); + overflow_button_gradient_middle = use_system_colors ? Color.FromArgb (238, 235, 220) : Color.FromArgb (82, 127, 208); + + rafting_container_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (158, 190, 245); + rafting_container_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (196, 218, 250); + + separator_dark = use_system_colors ? Color.FromArgb (197, 194, 184) : Color.FromArgb (106, 140, 203); + separator_light = use_system_colors ? SystemColors.ButtonHighlight : Color.FromArgb (241, 249, 255); + + status_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (158, 190, 245); + status_strip_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (196, 218, 250); + + tool_strip_border = use_system_colors ? Color.FromArgb (239, 237, 222) : Color.FromArgb (59, 97, 156); + tool_strip_content_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (158, 190, 245); + tool_strip_content_panel_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (196, 218, 250); + tool_strip_drop_down_background = use_system_colors ? Color.FromArgb (252, 252, 249) : Color.FromArgb (246, 246, 246); + + tool_strip_gradient_begin = use_system_colors ? Color.FromArgb (251, 250, 246) : Color.FromArgb (227, 239, 255); + tool_strip_gradient_end = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (123, 164, 224); + tool_strip_gradient_middle = use_system_colors ? Color.FromArgb (246, 244, 236) : Color.FromArgb (203, 225, 252); + + tool_strip_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (158, 190, 245); + tool_strip_panel_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (196, 218, 250); + break; + case ColorSchemes.HomeStead: + button_checked_gradient_begin = use_system_colors ? Color.Empty : Color.FromArgb (255, 223, 154); + button_checked_gradient_end = use_system_colors ? Color.Empty : Color.FromArgb (255, 166, 76); + button_checked_gradient_middle = use_system_colors ? Color.Empty : Color.FromArgb (255, 195, 116); + button_checked_highlight = Color.FromArgb (223, 227, 213); + button_checked_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_pressed_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (63, 93, 56); + button_pressed_gradient_begin = use_system_colors ? Color.FromArgb (201, 208, 184) : Color.FromArgb (254, 128, 62); + button_pressed_gradient_end = use_system_colors ? Color.FromArgb (201, 208, 184) : Color.FromArgb (255, 223, 154); + button_pressed_gradient_middle = use_system_colors ? Color.FromArgb (201, 208, 184) : Color.FromArgb (255, 177, 109); + button_pressed_highlight = use_system_colors ? Color.FromArgb (200, 206, 182) : Color.FromArgb (200, 206, 182); + button_pressed_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_selected_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (63, 93, 56); + button_selected_gradient_begin = use_system_colors ? Color.FromArgb (223, 227, 212) : Color.FromArgb (255, 255, 222); + button_selected_gradient_end = use_system_colors ? Color.FromArgb (223, 227, 212) : Color.FromArgb (255, 203, 136); + button_selected_gradient_middle = use_system_colors ? Color.FromArgb (223, 227, 212) : Color.FromArgb (255, 225, 172); + button_selected_highlight = use_system_colors ? Color.FromArgb (223, 227, 213) : Color.FromArgb (223, 227, 213); + button_selected_highlight_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (63, 93, 56); + + check_background = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (255, 192, 111); + check_pressed_background = use_system_colors ? Color.FromArgb (201, 208, 184) : Color.FromArgb (254, 128, 62); + check_selected_background = use_system_colors ? Color.FromArgb (201, 208, 184) : Color.FromArgb (254, 128, 62); + + grip_dark = use_system_colors ? Color.FromArgb (193, 190, 179) : Color.FromArgb (81, 94, 51); + grip_light = use_system_colors ? SystemColors.Window : Color.FromArgb (255, 255, 255); + + image_margin_gradient_begin = use_system_colors ? Color.FromArgb (251, 250, 246) : Color.FromArgb (255, 255, 237); + image_margin_gradient_end = use_system_colors ? SystemColors.Control : Color.FromArgb (181, 196, 143); + image_margin_gradient_middle = use_system_colors ? Color.FromArgb (246, 244, 236) : Color.FromArgb (206, 220, 167); + image_margin_revealed_gradient_begin = use_system_colors ? Color.FromArgb (247, 246, 239) : Color.FromArgb (230, 230, 209); + image_margin_revealed_gradient_end = use_system_colors ? Color.FromArgb (238, 235, 220) : Color.FromArgb (160, 177, 116); + image_margin_revealed_gradient_middle = use_system_colors ? Color.FromArgb (242, 240, 228) : Color.FromArgb (186, 201, 143); + + menu_border = use_system_colors ? Color.FromArgb (138, 134, 122) : Color.FromArgb (117, 141, 94); + menu_item_border = use_system_colors ? SystemColors.Highlight : Color.FromArgb (63, 93, 56); + + menu_item_pressed_gradient_begin = use_system_colors ? Color.FromArgb (251, 250, 246) : Color.FromArgb (237, 240, 214); + menu_item_pressed_gradient_end = use_system_colors ? Color.FromArgb (246, 244, 236) : Color.FromArgb (181, 196, 143); + menu_item_pressed_gradient_middle = use_system_colors ? Color.FromArgb (242, 240, 228) : Color.FromArgb (186, 201, 143); + menu_item_selected = use_system_colors ? SystemColors.Window : Color.FromArgb (255, 238, 194); + menu_item_selected_gradient_begin = use_system_colors ? Color.FromArgb (223, 227, 212) : Color.FromArgb (255, 255, 222); + menu_item_selected_gradient_end = use_system_colors ? Color.FromArgb (223, 227, 212) : Color.FromArgb (255, 203, 136); + + menu_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (217, 217, 167); + menu_strip_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (242, 241, 228); + + overflow_button_gradient_begin = use_system_colors ? Color.FromArgb (242, 240, 228) : Color.FromArgb (186, 204, 150); + overflow_button_gradient_end = use_system_colors ? SystemColors.ButtonShadow : Color.FromArgb (96, 119, 107); + overflow_button_gradient_middle = use_system_colors ? Color.FromArgb (238, 235, 220) : Color.FromArgb (141, 160, 107); + + rafting_container_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (217, 217, 167); + rafting_container_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (242, 241, 228); + + separator_dark = use_system_colors ? Color.FromArgb (197, 194, 184) : Color.FromArgb (96, 128, 88); + separator_light = use_system_colors ? SystemColors.ButtonHighlight : Color.FromArgb (244, 247, 222); + + status_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (217, 217, 167); + status_strip_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (242, 241, 228); + + tool_strip_border = use_system_colors ? Color.FromArgb (239, 237, 222) : Color.FromArgb (96, 128, 88); + tool_strip_content_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (217, 217, 167); + tool_strip_content_panel_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (242, 241, 228); + tool_strip_drop_down_background = use_system_colors ? Color.FromArgb (252, 252, 249) : Color.FromArgb (244, 244, 238); + + tool_strip_gradient_begin = use_system_colors ? Color.FromArgb (251, 250, 246) : Color.FromArgb (255, 255, 237); + tool_strip_gradient_end = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (181, 196, 143); + tool_strip_gradient_middle = use_system_colors ? Color.FromArgb (246, 244, 236) : Color.FromArgb (206, 220, 167); + + tool_strip_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (217, 217, 167); + tool_strip_panel_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 247) : Color.FromArgb (242, 241, 228); + break; + case ColorSchemes.Metallic: + button_checked_gradient_begin = use_system_colors ? Color.Empty : Color.FromArgb (255, 223, 154); + button_checked_gradient_end = use_system_colors ? Color.Empty : Color.FromArgb (255, 166, 76); + button_checked_gradient_middle = use_system_colors ? Color.Empty : Color.FromArgb (255, 195, 116); + button_checked_highlight = Color.FromArgb (231, 232, 235); + button_checked_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_pressed_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (75, 75, 111); + button_pressed_gradient_begin = use_system_colors ? Color.FromArgb (217, 218, 223) : Color.FromArgb (254, 128, 62); + button_pressed_gradient_end = use_system_colors ? Color.FromArgb (217, 218, 223) : Color.FromArgb (255, 223, 154); + button_pressed_gradient_middle = use_system_colors ? Color.FromArgb (217, 218, 223) : Color.FromArgb (255, 177, 109); + button_pressed_highlight = use_system_colors ? Color.FromArgb (215, 216, 222) : Color.FromArgb (215, 216, 222); + button_pressed_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_selected_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (75, 75, 111); + button_selected_gradient_begin = use_system_colors ? Color.FromArgb (232, 233, 236) : Color.FromArgb (255, 255, 222); + button_selected_gradient_end = use_system_colors ? Color.FromArgb (232, 233, 236) : Color.FromArgb (255, 203, 136); + button_selected_gradient_middle = use_system_colors ? Color.FromArgb (232, 233, 236) : Color.FromArgb (255, 225, 172); + button_selected_highlight = use_system_colors ? Color.FromArgb (231, 232, 235) : Color.FromArgb (231, 232, 235); + button_selected_highlight_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (75, 75, 111); + + check_background = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (255, 192, 111); + check_pressed_background = use_system_colors ? Color.FromArgb (217, 218, 223) : Color.FromArgb (254, 128, 62); + check_selected_background = use_system_colors ? Color.FromArgb (217, 218, 223) : Color.FromArgb (254, 128, 62); + + grip_dark = use_system_colors ? Color.FromArgb (182, 182, 185) : Color.FromArgb (84, 84, 117); + grip_light = use_system_colors ? SystemColors.Window : Color.FromArgb (255, 255, 255); + + image_margin_gradient_begin = use_system_colors ? Color.FromArgb (248, 248, 249) : Color.FromArgb (249, 249, 255); + image_margin_gradient_end = use_system_colors ? SystemColors.Control : Color.FromArgb (147, 145, 176); + image_margin_gradient_middle = use_system_colors ? Color.FromArgb (240, 239, 241) : Color.FromArgb (225, 226, 236); + image_margin_revealed_gradient_begin = use_system_colors ? Color.FromArgb (243, 242, 244) : Color.FromArgb (215, 215, 226); + image_margin_revealed_gradient_end = use_system_colors ? Color.FromArgb (227, 226, 230) : Color.FromArgb (118, 116, 151); + image_margin_revealed_gradient_middle = use_system_colors ? Color.FromArgb (233, 233, 235) : Color.FromArgb (184, 185, 202); + + menu_border = use_system_colors ? Color.FromArgb (126, 126, 129) : Color.FromArgb (124, 124, 148); + menu_item_border = use_system_colors ? SystemColors.Highlight : Color.FromArgb (75, 75, 111); + + menu_item_pressed_gradient_begin = use_system_colors ? Color.FromArgb (248, 248, 249) : Color.FromArgb (232, 233, 242); + menu_item_pressed_gradient_end = use_system_colors ? Color.FromArgb (240, 239, 241) : Color.FromArgb (172, 170, 194); + menu_item_pressed_gradient_middle = use_system_colors ? Color.FromArgb (233, 233, 235) : Color.FromArgb (184, 185, 202); + menu_item_selected = use_system_colors ? SystemColors.Window : Color.FromArgb (255, 238, 194); + menu_item_selected_gradient_begin = use_system_colors ? Color.FromArgb (232, 233, 236) : Color.FromArgb (255, 255, 222); + menu_item_selected_gradient_end = use_system_colors ? Color.FromArgb (232, 233, 236) : Color.FromArgb (255, 203, 136); + + menu_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (215, 215, 229); + menu_strip_gradient_end = use_system_colors ? Color.FromArgb (249, 248, 249) : Color.FromArgb (243, 243, 247); + + overflow_button_gradient_begin = use_system_colors ? Color.FromArgb (233, 233, 235) : Color.FromArgb (186, 185, 206); + overflow_button_gradient_end = use_system_colors ? SystemColors.ButtonShadow : Color.FromArgb (118, 116, 146); + overflow_button_gradient_middle = use_system_colors ? Color.FromArgb (227, 226, 230) : Color.FromArgb (156, 155, 180); + + rafting_container_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (215, 215, 229); + rafting_container_gradient_end = use_system_colors ? Color.FromArgb (249, 248, 249) : Color.FromArgb (243, 243, 247); + + separator_dark = use_system_colors ? Color.FromArgb (186, 186, 189) : Color.FromArgb (110, 109, 143); + separator_light = use_system_colors ? SystemColors.ButtonHighlight : Color.FromArgb (255, 255, 255); + + status_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (215, 215, 229); + status_strip_gradient_end = use_system_colors ? Color.FromArgb (249, 248, 249) : Color.FromArgb (243, 243, 247); + + tool_strip_border = use_system_colors ? Color.FromArgb (229, 228, 232) : Color.FromArgb (124, 124, 148); + tool_strip_content_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (215, 215, 229); + tool_strip_content_panel_gradient_end = use_system_colors ? Color.FromArgb (249, 248, 249) : Color.FromArgb (243, 243, 247); + tool_strip_drop_down_background = use_system_colors ? Color.FromArgb (251, 250, 251) : Color.FromArgb (253, 250, 255); + + tool_strip_gradient_begin = use_system_colors ? Color.FromArgb (248, 248, 249) : Color.FromArgb (249, 249, 255); + tool_strip_gradient_end = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (147, 145, 176); + tool_strip_gradient_middle = use_system_colors ? Color.FromArgb (240, 239, 241) : Color.FromArgb (225, 226, 236); + + tool_strip_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (215, 215, 229); + tool_strip_panel_gradient_end = use_system_colors ? Color.FromArgb (249, 248, 249) : Color.FromArgb (243, 243, 247); + break; + case ColorSchemes.MediaCenter: + button_checked_gradient_begin = use_system_colors ? Color.Empty : Color.FromArgb (226, 229, 238); + button_checked_gradient_end = use_system_colors ? Color.Empty : Color.FromArgb (226, 229, 238); + button_checked_gradient_middle = use_system_colors ? Color.Empty : Color.FromArgb (226, 229, 238); + button_checked_highlight = Color.FromArgb (196, 208, 229); + button_checked_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_pressed_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (51, 94, 168); + button_pressed_gradient_begin = use_system_colors ? Color.FromArgb (153, 175, 212) : Color.FromArgb (153, 175, 212); + button_pressed_gradient_end = use_system_colors ? Color.FromArgb (153, 175, 212) : Color.FromArgb (153, 175, 212); + button_pressed_gradient_middle = use_system_colors ? Color.FromArgb (153, 175, 212) : Color.FromArgb (153, 175, 212); + button_pressed_highlight = use_system_colors ? Color.FromArgb (152, 173, 210) : Color.FromArgb (152, 173, 210); + button_pressed_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_selected_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (51, 94, 168); + button_selected_gradient_begin = use_system_colors ? Color.FromArgb (194, 207, 229) : Color.FromArgb (194, 207, 229); + button_selected_gradient_end = use_system_colors ? Color.FromArgb (194, 207, 229) : Color.FromArgb (194, 207, 229); + button_selected_gradient_middle = use_system_colors ? Color.FromArgb (194, 207, 229) : Color.FromArgb (194, 207, 229); + button_selected_highlight = use_system_colors ? Color.FromArgb (196, 208, 229) : Color.FromArgb (196, 208, 229); + button_selected_highlight_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (51, 94, 168); + + check_background = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (226, 229, 238); + check_pressed_background = use_system_colors ? Color.FromArgb (153, 175, 212) : Color.FromArgb (51, 94, 168); + check_selected_background = use_system_colors ? Color.FromArgb (153, 175, 212) : Color.FromArgb (51, 94, 168); + + grip_dark = use_system_colors ? Color.FromArgb (189, 188, 191) : Color.FromArgb (189, 188, 191); + grip_light = use_system_colors ? SystemColors.Window : Color.FromArgb (255, 255, 255); + + image_margin_gradient_begin = use_system_colors ? Color.FromArgb (250, 250, 251) : Color.FromArgb (252, 252, 252); + image_margin_gradient_end = use_system_colors ? SystemColors.Control : Color.FromArgb (235, 233, 237); + image_margin_gradient_middle = use_system_colors ? Color.FromArgb (245, 244, 246) : Color.FromArgb (245, 244, 246); + image_margin_revealed_gradient_begin = use_system_colors ? Color.FromArgb (247, 246, 248) : Color.FromArgb (247, 246, 248); + image_margin_revealed_gradient_end = use_system_colors ? Color.FromArgb (237, 235, 239) : Color.FromArgb (228, 226, 230); + image_margin_revealed_gradient_middle = use_system_colors ? Color.FromArgb (241, 240, 242) : Color.FromArgb (241, 240, 242); + + menu_border = use_system_colors ? Color.FromArgb (134, 133, 136) : Color.FromArgb (134, 133, 136); + menu_item_border = use_system_colors ? SystemColors.Highlight : Color.FromArgb (51, 94, 168); + + menu_item_pressed_gradient_begin = use_system_colors ? Color.FromArgb (250, 250, 251) : Color.FromArgb (252, 252, 252); + menu_item_pressed_gradient_end = use_system_colors ? Color.FromArgb (245, 244, 246) : Color.FromArgb (245, 244, 246); + menu_item_pressed_gradient_middle = use_system_colors ? Color.FromArgb (241, 240, 242) : Color.FromArgb (241, 240, 242); + menu_item_selected = use_system_colors ? SystemColors.Window : Color.FromArgb (194, 207, 229); + menu_item_selected_gradient_begin = use_system_colors ? Color.FromArgb (194, 207, 229) : Color.FromArgb (194, 207, 229); + menu_item_selected_gradient_end = use_system_colors ? Color.FromArgb (194, 207, 229) : Color.FromArgb (194, 207, 229); + + menu_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (235, 233, 237); + menu_strip_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 251) : Color.FromArgb (251, 250, 251); + + overflow_button_gradient_begin = use_system_colors ? Color.FromArgb (241, 240, 242) : Color.FromArgb (242, 242, 242); + overflow_button_gradient_end = use_system_colors ? SystemColors.ButtonShadow : Color.FromArgb (167, 166, 170); + overflow_button_gradient_middle = use_system_colors ? Color.FromArgb (237, 235, 239) : Color.FromArgb (224, 224, 225); + + rafting_container_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (235, 233, 237); + rafting_container_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 251) : Color.FromArgb (251, 250, 251); + + separator_dark = use_system_colors ? Color.FromArgb (193, 193, 196) : Color.FromArgb (193, 193, 196); + separator_light = use_system_colors ? SystemColors.ButtonHighlight : Color.FromArgb (255, 255, 255); + + status_strip_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (235, 233, 237); + status_strip_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 251) : Color.FromArgb (251, 250, 251); + + tool_strip_border = use_system_colors ? Color.FromArgb (238, 237, 240) : Color.FromArgb (238, 237, 240); + tool_strip_content_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (235, 233, 237); + tool_strip_content_panel_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 251) : Color.FromArgb (251, 250, 251); + tool_strip_drop_down_background = use_system_colors ? Color.FromArgb (252, 252, 252) : Color.FromArgb (252, 252, 252); + + tool_strip_gradient_begin = use_system_colors ? Color.FromArgb (250, 250, 251) : Color.FromArgb (252, 252, 252); + tool_strip_gradient_end = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (235, 233, 237); + tool_strip_gradient_middle = use_system_colors ? Color.FromArgb (245, 244, 246) : Color.FromArgb (245, 244, 246); + + tool_strip_panel_gradient_begin = use_system_colors ? SystemColors.ButtonFace : Color.FromArgb (235, 233, 237); + tool_strip_panel_gradient_end = use_system_colors ? Color.FromArgb (251, 250, 251) : Color.FromArgb (251, 250, 251); + break; + case ColorSchemes.Aero: + button_checked_gradient_begin = Color.Empty; + button_checked_gradient_end = Color.Empty; + button_checked_gradient_middle = Color.Empty; + button_checked_highlight = Color.FromArgb (196, 225, 255); + button_checked_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_pressed_border = Color.FromKnownColor (KnownColor.Highlight); + button_pressed_gradient_begin = Color.FromArgb (153, 204, 255); + button_pressed_gradient_end = Color.FromArgb (153, 204, 255); + button_pressed_gradient_middle = Color.FromArgb (153, 204, 255); + button_pressed_highlight = Color.FromArgb (152, 203, 255); + button_pressed_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + button_selected_border = use_system_colors ? Color.FromKnownColor (KnownColor.Highlight) : Color.FromArgb (51, 94, 168); + button_selected_gradient_begin = Color.FromArgb (194, 224, 255); + button_selected_gradient_end = Color.FromArgb (194, 224, 255); + button_selected_gradient_middle = Color.FromArgb (194, 224, 255); + button_selected_highlight = Color.FromArgb (196, 225, 255); + button_selected_highlight_border = Color.FromKnownColor (KnownColor.Highlight); + + check_background = Color.FromKnownColor (KnownColor.Highlight); + check_pressed_background = Color.FromArgb (153, 204, 255); + check_selected_background = Color.FromArgb (153, 204, 255); + + grip_dark = Color.FromArgb (184, 184, 184); + grip_light = SystemColors.Window; + + image_margin_gradient_begin = Color.FromArgb (252, 252, 252); + image_margin_gradient_end = SystemColors.Control; + image_margin_gradient_middle = Color.FromArgb (250, 250, 250); + image_margin_revealed_gradient_begin = Color.FromArgb (251, 251, 251); + image_margin_revealed_gradient_end = Color.FromArgb (245, 245, 245); + image_margin_revealed_gradient_middle = Color.FromArgb (247, 247, 247); + + menu_border = Color.FromArgb (128, 128, 128); + menu_item_border = SystemColors.Highlight; + + menu_item_pressed_gradient_begin = Color.FromArgb (252, 252, 252); + menu_item_pressed_gradient_end = Color.FromArgb (250, 250, 250); + menu_item_pressed_gradient_middle = Color.FromArgb (247, 247, 247); + menu_item_selected = SystemColors.Window; + menu_item_selected_gradient_begin = Color.FromArgb (194, 224, 255); + menu_item_selected_gradient_end = Color.FromArgb (194, 224, 255); + + menu_strip_gradient_begin = SystemColors.ButtonFace; + menu_strip_gradient_end = Color.FromArgb (253, 253, 253); + + overflow_button_gradient_begin = Color.FromArgb (247, 247, 247); + overflow_button_gradient_end = SystemColors.ButtonShadow; + overflow_button_gradient_middle = Color.FromArgb (245, 245, 245); + + rafting_container_gradient_begin = SystemColors.ButtonFace; + rafting_container_gradient_end = Color.FromArgb (253, 253, 253); + + separator_dark = Color.FromArgb (189, 189, 189); + separator_light = SystemColors.ButtonHighlight; + + status_strip_gradient_begin = SystemColors.ButtonFace; + status_strip_gradient_end = Color.FromArgb (253, 253, 253); + + tool_strip_border = Color.FromArgb (246, 246, 246); + tool_strip_content_panel_gradient_begin = SystemColors.ButtonFace; + tool_strip_content_panel_gradient_end = Color.FromArgb (253, 253, 253); + tool_strip_drop_down_background = Color.FromArgb (253, 253, 253); + + tool_strip_gradient_begin = Color.FromArgb (252, 252, 252); + tool_strip_gradient_end = SystemColors.ButtonFace; + tool_strip_gradient_middle = Color.FromArgb (250, 250, 250); + + tool_strip_panel_gradient_begin = SystemColors.ButtonFace; + tool_strip_panel_gradient_end = Color.FromArgb (253, 253, 253); + break; + } + } + + private ColorSchemes GetCurrentStyle () + { + if (!VisualStyleInformation.IsEnabledByUser || string.IsNullOrEmpty (VisualStylesEngine.Instance.VisualStyleInformationFileName)) + + + return ColorSchemes.Classic; + else { + switch (System.IO.Path.GetFileNameWithoutExtension (VisualStylesEngine.Instance.VisualStyleInformationFileName).ToLowerInvariant ()) { + case "aero": + return ColorSchemes.Aero; + case "royale": + return ColorSchemes.MediaCenter; + default: + switch (VisualStyleInformation.ColorScheme) { + case "NormalColor": + return ColorSchemes.NormalColor; + case "HomeStead": + return ColorSchemes.HomeStead; + case "Metallic": + return ColorSchemes.Metallic; + default: + return ColorSchemes.Classic; + } + } + } + } + #endregion + + #region Private Enums + private enum ColorSchemes + { + Classic, // Windows Classic (No theme) + NormalColor, // Luna Blue + HomeStead, // Luna Olive + Metallic, // Luna Silver + MediaCenter, // Media Center (Energy Blue) + Aero // Windows Vista + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/PropertyManager.cs b/source/ShiftUI/Internal/PropertyManager.cs new file mode 100644 index 0000000..f31608f --- /dev/null +++ b/source/ShiftUI/Internal/PropertyManager.cs @@ -0,0 +1,166 @@ +// 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) 2005 Novell, Inc. +// +// Authors: +// Jackson Harper [email protected] + +using System; +using System.Collections; +using System.ComponentModel; + +namespace ShiftUI { + + public class PropertyManager : BindingManagerBase { + + internal string property_name; + private PropertyDescriptor prop_desc; + private object data_source; + private EventDescriptor changed_event; + private EventHandler property_value_changed_handler; + + public PropertyManager() { + } + + internal PropertyManager (object data_source) + { + SetDataSource (data_source); + } + + internal PropertyManager (object data_source, string property_name) + { + this.property_name = property_name; + + SetDataSource (data_source); + } + + internal void SetDataSource (object new_data_source) + { + if (changed_event != null) + changed_event.RemoveEventHandler (data_source, property_value_changed_handler); + + data_source = new_data_source; + + if (property_name != null) { + prop_desc = TypeDescriptor.GetProperties (data_source).Find (property_name, true); + + if (prop_desc == null) + return; + + changed_event = TypeDescriptor.GetEvents (data_source).Find (property_name + "Changed", false); + if (changed_event != null) { + property_value_changed_handler = new EventHandler (PropertyValueChanged); + changed_event.AddEventHandler (data_source, property_value_changed_handler); + } + } + } + + void PropertyValueChanged (object sender, EventArgs args) + { + OnCurrentChanged (args); + } + + public override object Current { + get { return prop_desc == null ? data_source : prop_desc.GetValue (data_source); } + } + + public override int Position { + get { return 0; } + set { /* Doesn't do anything on MS" */ } + } + + public override int Count { + get { return 1; } + } + + public override void AddNew () + { + throw new NotSupportedException ("AddNew is not supported for property to property binding"); + } + + public override void CancelCurrentEdit () + { + IEditableObject editable = data_source as IEditableObject; + if (editable == null) + return; + editable.CancelEdit (); + + PushData (); + } + + public override void EndCurrentEdit () + { + PullData (); + + IEditableObject editable = data_source as IEditableObject; + if (editable == null) + return; + editable.EndEdit (); + } + + // Hide this method from the 2.0 public API + internal override PropertyDescriptorCollection GetItemPropertiesInternal () + { + return TypeDescriptor.GetProperties (data_source); + } + + public override void RemoveAt (int index) + { + throw new NotSupportedException ("RemoveAt is not supported for property to property binding"); + } + + public override void ResumeBinding () + { + } + + public override void SuspendBinding () + { + } + + internal override bool IsSuspended { + get { return data_source == null; } + } + + protected internal override string GetListName (ArrayList listAccessors) + { + return String.Empty; + } + + [MonoTODO ("Stub, does nothing")] + protected override void UpdateIsBinding () + { + } + + protected internal override void OnCurrentChanged (EventArgs ea) + { + PushData (); + + if (onCurrentChangedHandler != null) { + onCurrentChangedHandler (this, ea); + } + } + + protected override void OnCurrentItemChanged (EventArgs ea) + { + throw new NotImplementedException (); + } + } +} + diff --git a/source/ShiftUI/Internal/PropertySort.cs b/source/ShiftUI/Internal/PropertySort.cs new file mode 100644 index 0000000..30a9fa6 --- /dev/null +++ b/source/ShiftUI/Internal/PropertySort.cs @@ -0,0 +1,40 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +using System.Runtime.InteropServices; + +namespace ShiftUI +{ + [ComVisible(true)] + public enum PropertySort + { + NoSort = 0, + Alphabetical = 1, + Categorized = 2, + CategorizedAlphabetical = 3 + } +} diff --git a/source/ShiftUI/Internal/PropertyTabChangedEventArgs.cs b/source/ShiftUI/Internal/PropertyTabChangedEventArgs.cs new file mode 100644 index 0000000..3fee1c3 --- /dev/null +++ b/source/ShiftUI/Internal/PropertyTabChangedEventArgs.cs @@ -0,0 +1,66 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +using System; +using System.Runtime.InteropServices; +using ShiftUI.Design; + +namespace ShiftUI +{ + [ComVisible(true)] + public class PropertyTabChangedEventArgs : EventArgs + { + #region Local Variables + private PropertyTab old_tab; + private PropertyTab new_tab; + #endregion // Local Variables + + #region Constructor + public PropertyTabChangedEventArgs ( PropertyTab oldTab , PropertyTab newTab ) + { + old_tab = oldTab; + new_tab = newTab; + } + #endregion // Constructor + + #region Public Instance Properties + public PropertyTab NewTab + { + get { + return new_tab; + } + } + + public PropertyTab OldTab + { + get { + return old_tab; + } + } + #endregion // Public Instance Properties + } +} diff --git a/source/ShiftUI/Internal/PropertyTabChangedEventHandler.cs b/source/ShiftUI/Internal/PropertyTabChangedEventHandler.cs new file mode 100644 index 0000000..498776f --- /dev/null +++ b/source/ShiftUI/Internal/PropertyTabChangedEventHandler.cs @@ -0,0 +1,33 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +namespace ShiftUI +{ + public delegate void PropertyTabChangedEventHandler(object s, PropertyTabChangedEventArgs e); +} + + diff --git a/source/ShiftUI/Internal/PropertyValueChangedEventArgs.cs b/source/ShiftUI/Internal/PropertyValueChangedEventArgs.cs new file mode 100644 index 0000000..ac308af --- /dev/null +++ b/source/ShiftUI/Internal/PropertyValueChangedEventArgs.cs @@ -0,0 +1,66 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +using System; +using System.Runtime.InteropServices; +using ShiftUI.Design; + +namespace ShiftUI +{ + [ComVisible(true)] + public class PropertyValueChangedEventArgs : EventArgs + { + #region Local Variables + private GridItem changed_item; + private object old_value; + #endregion // Local Variables + + #region Constructors + public PropertyValueChangedEventArgs ( GridItem changedItem , object oldValue ) + { + changed_item = changedItem; + old_value = oldValue; + } + #endregion + + #region Public Instance Properties + public GridItem ChangedItem + { + get { + return changed_item; + } + } + + public object OldValue + { + get { + return old_value; + } + } + #endregion // Public Instance Properties + } +} diff --git a/source/ShiftUI/Internal/PropertyValueChangedEventHandler.cs b/source/ShiftUI/Internal/PropertyValueChangedEventHandler.cs new file mode 100644 index 0000000..3e76a86 --- /dev/null +++ b/source/ShiftUI/Internal/PropertyValueChangedEventHandler.cs @@ -0,0 +1,31 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +namespace ShiftUI +{ + public delegate void PropertyValueChangedEventHandler(object s, PropertyValueChangedEventArgs e); +} diff --git a/source/ShiftUI/Internal/RadioButtonRenderer.cs b/source/ShiftUI/Internal/RadioButtonRenderer.cs new file mode 100644 index 0000000..c08eb4f --- /dev/null +++ b/source/ShiftUI/Internal/RadioButtonRenderer.cs @@ -0,0 +1,182 @@ +// +// RadioButtonRenderer.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System.Drawing; +using ShiftUI.VisualStyles; +using System; + +namespace ShiftUI +{ + public sealed class RadioButtonRenderer + { + private static bool always_use_visual_styles = false; + + #region Private Constructor + private RadioButtonRenderer () { } + #endregion + + #region Public Static Methods + public static void DrawRadioButton (Graphics g, Point glyphLocation, RadioButtonState state) + { + DrawRadioButton (g, glyphLocation, Rectangle.Empty, String.Empty, null, TextFormatFlags.HorizontalCenter, null, Rectangle.Empty, false, state); + } + + public static void DrawRadioButton (Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, bool focused, RadioButtonState state) + { + DrawRadioButton (g, glyphLocation, textBounds, radioButtonText, font, TextFormatFlags.HorizontalCenter, null, Rectangle.Empty, focused, state); + } + + public static void DrawRadioButton (Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, TextFormatFlags flags, bool focused, RadioButtonState state) + { + DrawRadioButton (g, glyphLocation, textBounds, radioButtonText, font, flags, null, Rectangle.Empty, focused, state); + } + + public static void DrawRadioButton (Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, Image image, Rectangle imageBounds, bool focused, RadioButtonState state) + { + DrawRadioButton (g, glyphLocation, textBounds, radioButtonText, font, TextFormatFlags.HorizontalCenter, image, imageBounds, focused, state); + } + + public static void DrawRadioButton (Graphics g, Point glyphLocation, Rectangle textBounds, string radioButtonText, Font font, TextFormatFlags flags, Image image, Rectangle imageBounds, bool focused, RadioButtonState state) + { + Rectangle bounds = new Rectangle (glyphLocation, GetGlyphSize (g, state)); + + if (Application.RenderWithVisualStyles || always_use_visual_styles == true) { + VisualStyleRenderer vsr = GetRadioButtonRenderer (state); + + vsr.DrawBackground (g, bounds); + + if (image != null) + vsr.DrawImage (g, imageBounds, image); + + if (focused) + WidgetPaint.DrawFocusRectangle (g, textBounds); + + if (radioButtonText != String.Empty) + if (state == RadioButtonState.CheckedDisabled || state == RadioButtonState.UncheckedDisabled) + TextRenderer.DrawText (g, radioButtonText, font, textBounds, SystemColors.GrayText, flags); + else + TextRenderer.DrawText (g, radioButtonText, font, textBounds, SystemColors.ControlText, flags); + } + else { + switch (state) { + case RadioButtonState.CheckedDisabled: + WidgetPaint.DrawRadioButton (g, bounds, ButtonState.Inactive | ButtonState.Checked); + break; + case RadioButtonState.CheckedHot: + case RadioButtonState.CheckedNormal: + WidgetPaint.DrawRadioButton (g, bounds, ButtonState.Checked); + break; + case RadioButtonState.CheckedPressed: + WidgetPaint.DrawRadioButton (g, bounds, ButtonState.Pushed | ButtonState.Checked); + break; + case RadioButtonState.UncheckedDisabled: + case RadioButtonState.UncheckedPressed: + WidgetPaint.DrawRadioButton (g, bounds, ButtonState.Inactive); + break; + case RadioButtonState.UncheckedHot: + case RadioButtonState.UncheckedNormal: + WidgetPaint.DrawRadioButton (g, bounds, ButtonState.Normal); + break; + } + + if (image != null) + g.DrawImage (image, imageBounds); + + if (focused) + WidgetPaint.DrawFocusRectangle (g, textBounds); + + if (radioButtonText != String.Empty) + TextRenderer.DrawText (g, radioButtonText, font, textBounds, SystemColors.ControlText, flags); + } + + } + + public static bool IsBackgroundPartiallyTransparent (RadioButtonState state) + { + if (!VisualStyleRenderer.IsSupported) + return false; + + VisualStyleRenderer vsr = GetRadioButtonRenderer (state); + + return vsr.IsBackgroundPartiallyTransparent (); + } + + public static void DrawParentBackground (Graphics g, Rectangle bounds, Widget childControl) + { + if (!VisualStyleRenderer.IsSupported) + return; + + VisualStyleRenderer vsr = new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.UncheckedNormal); + + vsr.DrawParentBackground (g, bounds, childControl); + } + + public static Size GetGlyphSize (Graphics g, RadioButtonState state) + { + if (!VisualStyleRenderer.IsSupported) + return new Size (13, 13); + + VisualStyleRenderer vsr = GetRadioButtonRenderer(state); + + return vsr.GetPartSize (g, ThemeSizeType.Draw); + } + #endregion + + #region Private Static Methods + private static VisualStyleRenderer GetRadioButtonRenderer (RadioButtonState state) + { + switch (state) { + case RadioButtonState.CheckedDisabled: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.CheckedDisabled); + case RadioButtonState.CheckedHot: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.CheckedHot); + case RadioButtonState.CheckedNormal: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.CheckedNormal); + case RadioButtonState.CheckedPressed: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.CheckedPressed); + case RadioButtonState.UncheckedDisabled: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.UncheckedDisabled); + case RadioButtonState.UncheckedHot: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.UncheckedHot); + case RadioButtonState.UncheckedNormal: + default: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.UncheckedNormal); + case RadioButtonState.UncheckedPressed: + return new VisualStyleRenderer (VisualStyleElement.Button.RadioButton.UncheckedPressed); + } + } + #endregion + + #region Public Static Properties + public static bool RenderMatchingApplicationState { + get { return !always_use_visual_styles; } + set { always_use_visual_styles = !value; } + } + #endregion + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/RelatedCurrencyManager.cs b/source/ShiftUI/Internal/RelatedCurrencyManager.cs new file mode 100644 index 0000000..2957db0 --- /dev/null +++ b/source/ShiftUI/Internal/RelatedCurrencyManager.cs @@ -0,0 +1,54 @@ +// 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) 2006 Novell, Inc. +// +// Authors: +// Chris Toshok ([email protected]) +// + +using System; +using System.Data; +using System.Reflection; +using System.Collections; +using System.ComponentModel; + +namespace ShiftUI { + [DefaultMember("Item")] + internal class RelatedCurrencyManager : CurrencyManager { + + BindingManagerBase parent; + PropertyDescriptor prop_desc; + + public RelatedCurrencyManager (BindingManagerBase parent, PropertyDescriptor prop_desc) + : base (prop_desc.GetValue (parent.Current)) + { + this.parent = parent; + this.prop_desc = prop_desc; + + parent.PositionChanged += new EventHandler (parent_PositionChanged); + } + + private void parent_PositionChanged (object sender, EventArgs args) + { + SetDataSource (prop_desc.GetValue (parent.Current)); + } + } +} + diff --git a/source/ShiftUI/Internal/RelatedImageListAttribute.cs b/source/ShiftUI/Internal/RelatedImageListAttribute.cs new file mode 100644 index 0000000..4c3af82 --- /dev/null +++ b/source/ShiftUI/Internal/RelatedImageListAttribute.cs @@ -0,0 +1,48 @@ +// +// RelatedImageListAttribute.cs +// +// 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) 2007 Novell, Inc. +// +// Author: +// Jonathan Pobst ([email protected]) + + +using System; +using ShiftUI; + +namespace ShiftUI +{ + [AttributeUsageAttribute (AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public sealed class RelatedImageListAttribute : Attribute + { + private string related_image_list; + + public RelatedImageListAttribute (string relatedImageList) + { + this.related_image_list = relatedImageList; + } + + public string RelatedImageList { + get { return this.related_image_list; } + } + } +} diff --git a/source/ShiftUI/Internal/RelatedPropertyManager.cs b/source/ShiftUI/Internal/RelatedPropertyManager.cs new file mode 100644 index 0000000..0ba4834 --- /dev/null +++ b/source/ShiftUI/Internal/RelatedPropertyManager.cs @@ -0,0 +1,67 @@ +// 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) 2006 Novell, Inc. +// +// Authors: +// Chris Toshok <[email protected]> + +using System; +using System.Collections; +using System.ComponentModel; + +namespace ShiftUI { + + internal class RelatedPropertyManager : PropertyManager { + + BindingManagerBase parent; + + public RelatedPropertyManager (BindingManagerBase parent, string property_name) + { + this.parent = parent; + this.property_name = property_name; + + if (parent.Position != -1) + SetDataSource (parent.Current); + parent.PositionChanged += new EventHandler (parent_PositionChanged); + } + + void parent_PositionChanged (object sender, EventArgs args) + { + if (parent.Position == -1) { + SetDataSource (null); + } + else { + SetDataSource (parent.Current); + } + + OnCurrentChanged (EventArgs.Empty); + } + + public override PropertyDescriptorCollection GetItemProperties () + { + PropertyDescriptor property = parent.GetItemProperties ().Find (property_name, true); + + // We can't just pass property.PropertyType, since the actual object could implement + // more elements and not only those described in the property type + return TypeDescriptor.GetProperties (property.GetValue (parent.Current)); + } + } +} + diff --git a/source/ShiftUI/Internal/RichTextBoxFinds.cs b/source/ShiftUI/Internal/RichTextBoxFinds.cs new file mode 100644 index 0000000..28cd616 --- /dev/null +++ b/source/ShiftUI/Internal/RichTextBoxFinds.cs @@ -0,0 +1,39 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok <[email protected]> +// +// + +// COMPLETE +using System; + +namespace ShiftUI { + [Flags] + public enum RichTextBoxFinds { + None = 0x00000000, + WholeWord = 0x00000002, + MatchCase = 0x00000004, + NoHighlight = 0x00000008, + Reverse = 0x00000010, + } +} diff --git a/source/ShiftUI/Internal/RichTextBoxScrollBars.cs b/source/ShiftUI/Internal/RichTextBoxScrollBars.cs new file mode 100644 index 0000000..b155f53 --- /dev/null +++ b/source/ShiftUI/Internal/RichTextBoxScrollBars.cs @@ -0,0 +1,39 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok <[email protected]> +// +// + +// COMPLETE + +namespace ShiftUI { + public enum RichTextBoxScrollBars { + None = 0, + Horizontal = 1, + Vertical = 2, + Both = 3, + ForcedHorizontal= 17, + ForcedVertical = 18, + ForcedBoth = 19 + } +} diff --git a/source/ShiftUI/Internal/SUI_NOTES.txt b/source/ShiftUI/Internal/SUI_NOTES.txt new file mode 100644 index 0000000..96350b0 --- /dev/null +++ b/source/ShiftUI/Internal/SUI_NOTES.txt @@ -0,0 +1,3 @@ +The code found in this folder is from the Mono project. + +The Mono project can be found on Github at http://github.com/mono/mono. diff --git a/source/ShiftUI/Internal/Screen.cs b/source/ShiftUI/Internal/Screen.cs new file mode 100644 index 0000000..7da251f --- /dev/null +++ b/source/ShiftUI/Internal/Screen.cs @@ -0,0 +1,221 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +using System; +using System.Drawing; + +namespace ShiftUI { + public class Screen { + #region Local Variables + private static Screen[] all_screens; + private bool primary; + private Rectangle bounds; + private Rectangle workarea; + private string name; + private int bits_per_pixel; + #endregion // Local Variables + + #region Constructors + static Screen () + { + try { + all_screens = XplatUI.AllScreens; + } + catch (Exception e) { + Console.WriteLine ("{0} trying to get all screens: {1}", e.GetType (), e.Message); + } + + if (all_screens == null || all_screens.Length == 0) { + // just use a default one + all_screens = new[] { new Screen(true, "Mono MWF Primary Display", + XplatUI.VirtualScreen, XplatUI.WorkingArea) }; + } + } + + internal Screen() { + this.primary = true; + this.bounds = XplatUI.WorkingArea; + } + + internal Screen(bool primary, string name, Rectangle bounds, Rectangle workarea) { + this.primary = primary; + this.name = name; + this.bounds = bounds; + this.workarea = workarea; + this.bits_per_pixel = 32; + } + #endregion // Constructors + + #region Public Static Properties + public static Screen[] AllScreens { + get { + return all_screens; + } + } + + public static Screen PrimaryScreen { + get { + return all_screens[0]; + } + } + #endregion // Public Static Properties + + #region Public Instance Properties + [MonoTODO ("Stub, always returns 32")] + public int BitsPerPixel { + get { return bits_per_pixel; } + } + + public Rectangle Bounds { + get { + return this.bounds; + } + } + + public string DeviceName { + get { + return this.name; + } + } + + public bool Primary { + get { + return this.primary; + } + } + + public Rectangle WorkingArea { + get { + return this.workarea; + } + } + #endregion // Public Instance Properties + + #region Public Static Methods + public static Screen FromControl(Widget control) { + var point = control.Parent != null ? control.Parent.PointToScreen(control.Location) : control.Location; + return Screen.FromPoint(point); + } + + public static Screen FromHandle(IntPtr hwnd) { + Widget control; + + control = Widget.FromHandle(hwnd); + if (control != null) { + var point = control.Parent != null ? control.Parent.PointToScreen(control.Location) : control.Location; + return Screen.FromPoint(point); + } + return Screen.PrimaryScreen; + } + + public static Screen FromPoint(Point point) { + for (int i = 0; i < all_screens.Length; i++) { + if (all_screens[i].Bounds.Contains(point)) { + return all_screens[i]; + } + } + return Screen.PrimaryScreen; + } + + public static Screen FromRectangle(Rectangle rect) { + Screen bestScrn = null; + int closest = Int32.MaxValue; + foreach (Screen scrn in Screen.AllScreens) { + Rectangle rcBounds = scrn.Bounds; + int distance = 0; + if (rect.Left > rcBounds.Right) + distance += rect.Left - rcBounds.Right; + else if (rcBounds.Left > rect.Left) + distance += rcBounds.Left - rect.Left; + if (rcBounds.Left > rect.Right) + distance += rcBounds.Left - rect.Right; + else if (rect.Right > rcBounds.Right) + distance += rect.Right - rcBounds.Right; + if (rect.Top > rcBounds.Bottom) + distance += rect.Top - rcBounds.Bottom; + else if (rcBounds.Top > rect.Top) + distance += rcBounds.Top - rect.Top; + if (rcBounds.Top > rect.Bottom) + distance += rcBounds.Top - rect.Bottom; + else if (rect.Bottom > rcBounds.Bottom) + distance += rect.Bottom - rcBounds.Bottom; + if (distance < closest) { + bestScrn = scrn; + closest = distance; + } + } + return bestScrn; + } + + public static Rectangle GetBounds(Widget ctl) { + return Screen.FromControl(ctl).Bounds; + } + + public static Rectangle GetBounds(Point pt) { + return Screen.FromPoint(pt).Bounds; + } + + public static Rectangle GetBounds(Rectangle rect) { + return Screen.FromRectangle(rect).Bounds; + } + + public static Rectangle GetWorkingArea(Widget ctl) { + return Screen.FromControl(ctl).WorkingArea; + } + + public static Rectangle GetWorkingArea(Point pt) { + return Screen.FromPoint(pt).WorkingArea; + } + + public static Rectangle GetWorkingArea(Rectangle rect) { + return Screen.FromRectangle(rect).WorkingArea; + } + #endregion // Public Static Methods + + #region Public Instance Methods + public override bool Equals(object obj) { + if (obj is Screen) { + Screen s = (Screen)obj; + + if (name.Equals(s.name) && (primary == s.primary) && (bounds.Equals(s.Bounds)) && (workarea.Equals(s.workarea))) { + return true; + } + } + return false; + } + + public override int GetHashCode() { + return base.GetHashCode (); + } + + public override string ToString() { + return "Screen[Bounds={" + this.Bounds + "} WorkingArea={" + this.WorkingArea + "} Primary={" + this.Primary + "} DeviceName=" + this.DeviceName; + } + + + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/ScrollProperties.cs b/source/ShiftUI/Internal/ScrollProperties.cs new file mode 100644 index 0000000..a5f59ee --- /dev/null +++ b/source/ShiftUI/Internal/ScrollProperties.cs @@ -0,0 +1,97 @@ +// 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. +// +// Authors: +// Olivier Dufour [email protected] +// Jonathan Pobst [email protected] +// + +using System.ComponentModel; + +namespace ShiftUI +{ + public abstract class ScrollProperties + { + #region Private Fields + private ScrollableWidget parentControl; + internal ScrollBar scroll_bar; + #endregion + + #region constructor + protected ScrollProperties (ScrollableWidget container) + { + parentControl = container; + } + #endregion + + #region Public Properties + [DefaultValue (true)] + public bool Enabled { + get { return scroll_bar.Enabled; } + set { scroll_bar.Enabled = value; } + } + + [DefaultValue (10)] + [RefreshProperties (RefreshProperties.Repaint)] + public int LargeChange { + get { return scroll_bar.LargeChange; } + set { scroll_bar.LargeChange = value; } + } + + [DefaultValue (100)] + [RefreshProperties (RefreshProperties.Repaint)] + public int Maximum { + get { return scroll_bar.Maximum; } + set { scroll_bar.Maximum = value; } + } + + [DefaultValue (0)] + [RefreshProperties (RefreshProperties.Repaint)] + public int Minimum { + get { return scroll_bar.Minimum; } + set { scroll_bar.Minimum = value; } + } + + [DefaultValue (1)] + public int SmallChange { + get { return scroll_bar.SmallChange; } + set { scroll_bar.SmallChange = value; } + } + + [DefaultValue (0)] + [BindableAttribute (true)] + public int Value { + get { return scroll_bar.Value; } + set { scroll_bar.Value = value; } + } + + [DefaultValue (false)] + public bool Visible { + get { return scroll_bar.Visible; } + set { scroll_bar.Visible = value; } + } + #endregion + + #region Protected Properties + protected ScrollableWidget ParentControl { + get { return parentControl; } + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/SelectedGridItemChangedEventArgs.cs b/source/ShiftUI/Internal/SelectedGridItemChangedEventArgs.cs new file mode 100644 index 0000000..8a6149c --- /dev/null +++ b/source/ShiftUI/Internal/SelectedGridItemChangedEventArgs.cs @@ -0,0 +1,65 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +using System; +using ShiftUI.Design; + +namespace ShiftUI +{ + public class SelectedGridItemChangedEventArgs : EventArgs + { + #region Local Variables + private GridItem new_selection; + private GridItem old_selection; + #endregion + + #region Constructors + public SelectedGridItemChangedEventArgs ( GridItem oldSel , GridItem newSel ) + { + old_selection = oldSel; + new_selection = newSel; + } + #endregion // Constructors + + #region Public Instance Properties + public GridItem NewSelection + { + get { + return new_selection; + } + } + + public GridItem OldSelection + { + get { + return old_selection; + } + } + #endregion // Public Instance Properties + } +} + diff --git a/source/ShiftUI/Internal/SelectedGridItemChangedEventHandler.cs b/source/ShiftUI/Internal/SelectedGridItemChangedEventHandler.cs new file mode 100644 index 0000000..da69bf3 --- /dev/null +++ b/source/ShiftUI/Internal/SelectedGridItemChangedEventHandler.cs @@ -0,0 +1,31 @@ +// 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 Novell, Inc. +// +// Authors: +// Jonathan Chambers ([email protected]) +// + +// COMPLETE + +namespace ShiftUI +{ + public delegate void SelectedGridItemChangedEventHandler(object sender, SelectedGridItemChangedEventArgs e); +} diff --git a/source/ShiftUI/Internal/SelectionRangeConverter.cs b/source/ShiftUI/Internal/SelectionRangeConverter.cs new file mode 100644 index 0000000..07f18ef --- /dev/null +++ b/source/ShiftUI/Internal/SelectionRangeConverter.cs @@ -0,0 +1,110 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// COMPLETE + +using System.ComponentModel; +using System.Globalization; +using System; + +namespace ShiftUI { + public class SelectionRangeConverter : TypeConverter { + #region Public Constructors + public SelectionRangeConverter() { + } + #endregion // Public Constructors + + #region Public Instance Methods + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) { + if (sourceType == typeof(string)) { + return true; + } + return false; + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) { + if (destinationType == typeof(string)) { + return true; + } + return false; + } + + public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) { + string[] parts; + DateTime start; + DateTime end; + + if ((value == null) || !(value is String)) { + return base.ConvertFrom (context, culture, value); + } + + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + + parts = ((string)value).Split(culture.TextInfo.ListSeparator.ToCharArray()); + + start = (DateTime)TypeDescriptor.GetConverter(typeof(DateTime)).ConvertFromString(context, culture, parts[0]); + end = (DateTime)TypeDescriptor.GetConverter(typeof(DateTime)).ConvertFromString(context, culture, parts[1]); + + return new SelectionRange(start, end); + } + + public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) { + SelectionRange s; + + if ((value == null) || !(value is SelectionRange) || (destinationType != typeof(string))) { + return base.ConvertTo (context, culture, value, destinationType); + } + + if (culture == null) { + culture = CultureInfo.CurrentCulture; + } + + s = (SelectionRange)value; + + + return s.Start.ToShortDateString() + culture.TextInfo.ListSeparator + s.End.ToShortDateString(); + } + + public override object CreateInstance(ITypeDescriptorContext context, System.Collections.IDictionary propertyValues) { + return new SelectionRange((DateTime)propertyValues["Start"], (DateTime)propertyValues["End"]); + } + + public override bool GetCreateInstanceSupported(ITypeDescriptorContext context) { + return true; + } + + public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) { + return TypeDescriptor.GetProperties(typeof(SelectionRange), attributes); + } + + public override bool GetPropertiesSupported(ITypeDescriptorContext context) { + return true; + } + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/Splitter.cs b/source/ShiftUI/Internal/Splitter.cs new file mode 100644 index 0000000..761f6ee --- /dev/null +++ b/source/ShiftUI/Internal/Splitter.cs @@ -0,0 +1,707 @@ +// 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) 2005-2008 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Dennis Bartok ([email protected]) +// Ivan N. Zlatev (contact i-nz.net) +// +// + +// COMPLETE + +#undef Debug + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace ShiftUI { + [ComVisible (true)] + [ClassInterface (ClassInterfaceType.AutoDispatch)] + [DefaultEvent("SplitterMoved")] + [Designer("ShiftUI.Design.SplitterDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")] + [DefaultProperty("Dock")] + public class Splitter : Widget + { + #region Local Variables + static private Cursor splitter_ns; + static private Cursor splitter_we; + // XXX this "new" shouldn't be here. Widget shouldn't define border_style as internal. + new private BorderStyle border_style; + private int min_extra; + private int min_size; + private int max_size; + private int splitter_size; // Size (width or height) of our splitter control + private bool horizontal; // true if we've got a horizontal splitter + private Widget affected; // The control that the splitter resizes + private int split_requested; // If the user requests a position before we have ever laid out the doc + private int splitter_prev_move; + private Rectangle splitter_rectangle_moving; + private int moving_offset; + #endregion // Local Variables + + #region Constructors + static Splitter() { + splitter_ns = Cursors.HSplit; + splitter_we = Cursors.VSplit; + } + + public Splitter() { + + min_extra = 25; + min_size = 25; + split_requested = -1; + splitter_size = 3; + horizontal = false; + + SetStyle(Widgetstyles.Selectable, false); + Anchor = AnchorStyles.None; + Dock = DockStyle.Left; + + Layout += new LayoutEventHandler(LayoutSplitter); + this.ParentChanged += new EventHandler(ReparentSplitter); + Cursor = splitter_we; + } + #endregion // Constructors + + #region Public Instance Properties + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool AllowDrop { + get { + return base.AllowDrop; + } + + set { + base.AllowDrop = value; + } + } + + [Browsable(false)] + [DefaultValue(AnchorStyles.None)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override AnchorStyles Anchor { + get { + return AnchorStyles.None; + } + + set { + ; // MS doesn't set it + } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override Image BackgroundImage { + get { + return base.BackgroundImage; + } + + set { + base.BackgroundImage = value; + } + } + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public override ImageLayout BackgroundImageLayout { + get { return base.BackgroundImageLayout; } + set { base.BackgroundImageLayout = value; } + } + + [DispId(-504)] + [DefaultValue (BorderStyle.None)] + [MWFDescription("Sets the border style for the splitter")] + [MWFCategory("Appearance")] + public BorderStyle BorderStyle { + get { + return border_style; + } + + set { + border_style = value; + + switch(value) { + case BorderStyle.FixedSingle: + splitter_size = 4; // We don't get motion events for 1px wide windows on X11. sigh. + break; + + case BorderStyle.Fixed3D: + value = BorderStyle.None; + splitter_size = 3; + break; + + case BorderStyle.None: + splitter_size = 3; + break; + + default: + throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for BorderStyle", value)); + } + + base.InternalBorderStyle = value; + } + } + + [DefaultValue(DockStyle.Left)] + [Localizable(true)] + public override DockStyle Dock { + get { + return base.Dock; + } + + set { + if (!Enum.IsDefined (typeof (DockStyle), value) || (value == DockStyle.None) || (value == DockStyle.Fill)) { + throw new ArgumentException("Splitter must be docked left, top, bottom or right"); + } + + if ((value == DockStyle.Top) || (value == DockStyle.Bottom)) { + horizontal = true; + Cursor = splitter_ns; + } else { + horizontal = false; + Cursor = splitter_we; + } + base.Dock = value; + } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override Font Font { + get { + return base.Font; + } + + set { + base.Font = value; + } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override Color ForeColor { + get { + return base.ForeColor; + } + + set { + base.ForeColor = value; + } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new ImeMode ImeMode { + get { + return base.ImeMode; + } + + set { + base.ImeMode = value; + } + } + + [DefaultValue(25)] + [Localizable(true)] + [MWFDescription("Sets minimum size of undocked window")] + [MWFCategory("Behaviour")] + public int MinExtra { + get { + return min_extra; + } + + set { + min_extra = value; + } + } + + [DefaultValue(25)] + [Localizable(true)] + [MWFDescription("Sets minimum size of the resized control")] + [MWFCategory("Behaviour")] + public int MinSize { + get { + return min_size; + } + + set { + min_size = value; + } + } + + internal int MaxSize { + get { + if (this.Parent == null) + return 0; + + if (affected == null) + affected = AffectedControl; + + int widths = 0; + int heights = 0; + int vert_offset = 0; + int horiz_offset = 0; + foreach (Widget c in this.Parent.Widgets) { + if (c != affected) { + switch (c.Dock) { + case DockStyle.Left: + case DockStyle.Right: + widths += c.Width; + + if (c.Location.X < this.Location.X) + vert_offset += c.Width; + break; + case DockStyle.Top: + case DockStyle.Bottom: + heights += c.Height; + + if (c.Location.Y < this.Location.Y) + horiz_offset += c.Height; + break; + } + } + } + + if (horizontal) { + moving_offset = horiz_offset; + + return Parent.ClientSize.Height - heights - MinExtra; + } else { + moving_offset = vert_offset; + + return Parent.ClientSize.Width - widths - MinExtra; + } + } + } + + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [MWFDescription("Current splitter position")] + [MWFCategory("Layout")] + public int SplitPosition { + get { + affected = AffectedControl; + if (affected == null) { + return -1; + } + + if (Capture) { + return CalculateSplitPosition(); + } + + if (horizontal) { + return affected.Height; + } else { + return affected.Width; + } + } + + set { + if (value > MaxSize) + value = MaxSize; + if (value < MinSize) + value = MinSize; + + affected = AffectedControl; + if (affected == null) + split_requested = value; + else { + if (horizontal) + affected.Height = value; + else + affected.Width = value; + OnSplitterMoved (new SplitterEventArgs (Left, Top, value, value)); + } + } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new bool TabStop { + get { return base.TabStop; } + set { base.TabStop = value; } + } + + [Bindable(false)] + [Browsable(false)] + [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] + [EditorBrowsable(EditorBrowsableState.Never)] + public override string Text { + get { + return base.Text; + } + + set { + base.Text = value; + } + } + + #endregion // Public Instance Properties + + #region Protected Instance Properties + protected override CreateParams CreateParams { + get { + return base.CreateParams; + } + } + + protected override Cursor DefaultCursor { + get { return base.DefaultCursor; } + } + + protected override ImeMode DefaultImeMode { + get { + return ImeMode.Disable; + } + } + + protected override Size DefaultSize { + get { + return new Size (3, 3); + } + } + #endregion // Protected Instance Properties + + #region Public Instance Methods + public override string ToString() { + return base.ToString () + String.Format(", MinExtra: {0}, MinSize: {1}", min_extra, min_size); + } + #endregion // Public Instance Methods + + #region Protected Instance Methods + protected override void OnKeyDown(KeyEventArgs e) { + base.OnKeyDown (e); + if (Capture && (e.KeyCode == Keys.Escape)) { + Capture = false; + SplitterEndMove (Point.Empty, true); + } + } + + protected override void OnMouseDown(MouseEventArgs e) { + base.OnMouseDown (e); + + // Only allow if we are set up properly + if (affected == null) + affected = AffectedControl; + max_size = MaxSize; + + if (affected == null || e.Button != MouseButtons.Left) + return; + + Capture = true; + SplitterBeginMove (Parent.PointToClient (PointToScreen (new Point (e.X, e.Y)))); + } + + protected override void OnMouseMove (MouseEventArgs e) + { + base.OnMouseMove (e); + + if (!Capture || e.Button != MouseButtons.Left || affected == null) + return; + + // We need our mouse coordinates relative to our parent + SplitterMove (Parent.PointToClient (PointToScreen (e.Location))); + } + + protected override void OnMouseUp (MouseEventArgs e) + { + base.OnMouseUp (e); + if (!Capture || e.Button != MouseButtons.Left || affected == null) + return; + + SplitterEndMove (Parent.PointToClient (PointToScreen (e.Location)), false); + Capture = false; + } + + private void SplitterBeginMove (Point location) + { + splitter_rectangle_moving = new Rectangle (Bounds.X, Bounds.Y, + Width, Height); + splitter_prev_move = horizontal ? location.Y : location.X; + } + + private void SplitterMove (Point location) + { + int currentMove = horizontal ? location.Y : location.X; + int delta = currentMove - splitter_prev_move; + Rectangle prev_location = splitter_rectangle_moving; + bool moved = false; + int min = this.MinSize + moving_offset; + int max = max_size + moving_offset; + + if (horizontal) { + if (splitter_rectangle_moving.Y + delta > min && splitter_rectangle_moving.Y + delta < max) { + splitter_rectangle_moving.Y += delta; + moved = true; + } else { + // Ensure that the splitter is set to minimum or maximum position, + // even if the mouse "skips". + // + if (splitter_rectangle_moving.Y + delta <= min && splitter_rectangle_moving.Y != min) { + splitter_rectangle_moving.Y = min; + moved = true; + } else if (splitter_rectangle_moving.Y + delta >= max && splitter_rectangle_moving.Y != max) { + splitter_rectangle_moving.Y = max; + moved = true; + } + } + } else { + if (splitter_rectangle_moving.X + delta > min && splitter_rectangle_moving.X + delta < max) { + splitter_rectangle_moving.X += delta; + moved = true; + } else { + // Ensure that the splitter is set to minimum or maximum position, + // even if the mouse "skips". + // + if (splitter_rectangle_moving.X + delta <= min && splitter_rectangle_moving.X != min) { + splitter_rectangle_moving.X = min; + moved = true; + } else if (splitter_rectangle_moving.X + delta >= max && splitter_rectangle_moving.X != max) { + splitter_rectangle_moving.X = max; + moved = true; + } + } + } + + if (moved) { + splitter_prev_move = currentMove; + OnSplitterMoving (new SplitterEventArgs (location.X, location.Y, + splitter_rectangle_moving.X, + splitter_rectangle_moving.Y)); + XplatUI.DrawReversibleRectangle (this.Parent.Handle, prev_location, 1); + XplatUI.DrawReversibleRectangle (this.Parent.Handle, splitter_rectangle_moving, 1); + } + } + + private void SplitterEndMove (Point location, bool cancel) + { + if (!cancel) { + // Resize the affected window + if (horizontal) + affected.Height = CalculateSplitPosition(); + else + affected.Width = CalculateSplitPosition(); + } + + this.Parent.Refresh (); // to clean up the drag handle artifacts from all Widgets + SplitterEventArgs args = new SplitterEventArgs (location.X, location.Y, + splitter_rectangle_moving.X, + splitter_rectangle_moving.Y); + OnSplitterMoved (args); + } + + protected virtual void OnSplitterMoved(SplitterEventArgs sevent) { + SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovedEvent]); + if (eh != null) + eh (this, sevent); + } + + protected virtual void OnSplitterMoving(SplitterEventArgs sevent) { + SplitterEventHandler eh = (SplitterEventHandler)(Events [SplitterMovingEvent]); + if (eh != null) + eh (this, sevent); + } + + protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { + // enforce our width / height + if (horizontal) { + splitter_size = height; + if (splitter_size < 1) { + splitter_size = 3; + } + base.SetBoundsCore (x, y, width, splitter_size, specified); + } else { + splitter_size = width; + if (splitter_size < 1) { + splitter_size = 3; + } + base.SetBoundsCore (x, y, splitter_size, height, specified); + } + } + #endregion // Protected Instance Methods + + #region Private Properties and Methods + private Widget AffectedControl { + get { + if (Parent == null) + return null; + + // Doc says the first control preceeding us in the zorder + for (int i = Parent.Widgets.GetChildIndex(this) + 1; i < Parent.Widgets.Count; i++) { + switch (Dock) { + case DockStyle.Top: + if (Top == Parent.Widgets[i].Bottom) + return Parent.Widgets[i]; + break; + case DockStyle.Bottom: + if (Bottom == Parent.Widgets[i].Top) + return Parent.Widgets[i]; + break; + case DockStyle.Left: + if (Left == Parent.Widgets[i].Right) + return Parent.Widgets[i]; + break; + case DockStyle.Right: + if (Right == Parent.Widgets[i].Left) + return Parent.Widgets[i]; + break; + } + } + return null; + } + } + + private int CalculateSplitPosition() { + if (horizontal) { + if (Dock == DockStyle.Top) + return splitter_rectangle_moving.Y - affected.Top; + else + return affected.Bottom - splitter_rectangle_moving.Y - splitter_size; + } else { + if (Dock == DockStyle.Left) + return splitter_rectangle_moving.X - affected.Left; + else + return affected.Right - splitter_rectangle_moving.X - splitter_size; + } + } + + internal override void OnPaintInternal (PaintEventArgs e) { + e.Graphics.FillRectangle(ThemeEngine.Current.ResPool.GetSolidBrush(this.BackColor), e.ClipRectangle); + } + + private void LayoutSplitter(object sender, LayoutEventArgs e) { + affected = AffectedControl; + if (split_requested != -1) { + SplitPosition = split_requested; + split_requested = -1; + } + } + + private void ReparentSplitter(object sender, EventArgs e) { + affected = null; + } + + #endregion // Private Properties and Methods + + #region Events + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler BackgroundImageChanged { + add { base.BackgroundImageChanged += value; } + remove { base.BackgroundImageChanged -= value; } + } + + [Browsable (false)] + [EditorBrowsable (EditorBrowsableState.Never)] + public new event EventHandler BackgroundImageLayoutChanged + { + add { base.BackgroundImageLayoutChanged += value; } + remove { base.BackgroundImageLayoutChanged -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Enter { + add { base.Enter += value; } + remove { base.Enter -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler FontChanged { + add { base.FontChanged += value; } + remove { base.FontChanged -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ForeColorChanged { + add { base.ForeColorChanged += value; } + remove { base.ForeColorChanged -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler ImeModeChanged { + add { base.ImeModeChanged += value; } + remove { base.ImeModeChanged -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyDown { + add { base.KeyDown += value; } + remove { base.KeyDown -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyPressEventHandler KeyPress { + add { base.KeyPress += value; } + remove { base.KeyPress -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event KeyEventHandler KeyUp { + add { base.KeyUp += value; } + remove { base.KeyUp -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler Leave { + add { base.Leave += value; } + remove { base.Leave -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TabStopChanged { + add { base.TabStopChanged += value; } + remove { base.TabStopChanged -= value; } + } + + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public new event EventHandler TextChanged { + add { base.TextChanged += value; } + remove { base.TextChanged -= value; } + } + + static object SplitterMovedEvent = new object (); + static object SplitterMovingEvent = new object (); + + public event SplitterEventHandler SplitterMoved { + add { Events.AddHandler (SplitterMovedEvent, value); } + remove { Events.RemoveHandler (SplitterMovedEvent, value); } + } + + public event SplitterEventHandler SplitterMoving { + add { Events.AddHandler (SplitterMovingEvent, value); } + remove { Events.RemoveHandler (SplitterMovingEvent, value); } + } + #endregion // Events + } +} diff --git a/source/ShiftUI/Internal/Structs.cs b/source/ShiftUI/Internal/Structs.cs new file mode 100644 index 0000000..087964d --- /dev/null +++ b/source/ShiftUI/Internal/Structs.cs @@ -0,0 +1,157 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// + +using System; + +namespace ShiftUI.CarbonInternal { + internal struct CGSize { + public float width; + public float height; + + public CGSize (int w, int h) { + this.width = (float)w; + this.height = (float)h; + } + } + + internal struct QDPoint { + public short y; + public short x; + + public QDPoint (short x, short y) { + this.x = x; + this.y = y; + } + } + + internal struct CGPoint { + public float x; + public float y; + + public CGPoint (int x, int y) { + this.x = (float)x; + this.y = (float)y; + } + } + + internal struct HIRect { + public CGPoint origin; + public CGSize size; + + public HIRect (int x, int y, int w, int h) { + this.origin = new CGPoint (x, y); + this.size = new CGSize (w, h); + } + } + + internal struct HIViewID { + public uint type; + public uint id; + + public HIViewID (uint type, uint id) { + this.type = type; + this.id = id; + } + } + + internal struct EventTypeSpec { + public UInt32 eventClass; + public UInt32 eventKind; + + public EventTypeSpec (UInt32 eventClass, UInt32 eventKind) + { + this.eventClass = eventClass; + this.eventKind = eventKind; + } + } + + internal struct CarbonEvent { + public IntPtr hWnd; + public IntPtr evt; + + public CarbonEvent (IntPtr hWnd, IntPtr evt) + { + this.hWnd = hWnd; + this.evt = evt; + } + } + + internal struct RGBColor { + public short red; + public short green; + public short blue; + } + + internal struct Rect { + public short top; + public short left; + public short bottom; + public short right; + } + + internal struct Caret { + internal Timer Timer; + internal IntPtr Hwnd; + internal int X; + internal int Y; + internal int Width; + internal int Height; + internal int Visible; + internal bool On; + internal bool Paused; + } + + internal struct Hover { + internal Timer Timer; + internal IntPtr Hwnd; + internal int X; + internal int Y; + internal int Interval; + } + + internal struct CGAffineTransform { + internal float a; + internal float b; + internal float c; + internal float d; + internal float tx; + internal float ty; + } + + internal struct MouseTrackingRegionID { + public uint signature; + public uint id; + + public MouseTrackingRegionID (uint signature, uint id) { + this.signature = signature; + this.id = id; + } + } + + internal struct ProcessSerialNumber { + public ulong highLongOfPSN; + public ulong lowLongOfPSN; + } +} diff --git a/source/ShiftUI/Internal/SystemInformation.cs b/source/ShiftUI/Internal/SystemInformation.cs new file mode 100644 index 0000000..a27f039 --- /dev/null +++ b/source/ShiftUI/Internal/SystemInformation.cs @@ -0,0 +1,611 @@ +// 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. (http://www.novell.com) +// +// Authors: +// Miguel de Icaza ([email protected]). +// Peter Bartok ([email protected]) +// + +// NOT COMPLETE + +using System; +using System.Drawing; +using System.ComponentModel; + +namespace ShiftUI +{ + public class SystemInformation + { + private SystemInformation () + { + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int ActiveWindowTrackingDelay { get { return XplatUI.ActiveWindowTrackingDelay; } } + + public static ArrangeDirection ArrangeDirection { + get { + return ThemeEngine.Current.ArrangeDirection; + } + } + + public static ArrangeStartingPosition ArrangeStartingPosition { + get { + return ThemeEngine.Current.ArrangeStartingPosition; + } + } + + public static BootMode BootMode { + get { + return BootMode.Normal; + } + } + + public static Size Border3DSize { + get { + return ThemeEngine.Current.Border3DSize; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int BorderMultiplierFactor { get { return ThemeEngine.Current.BorderMultiplierFactor; } } + + public static Size BorderSize { + get { + return ThemeEngine.Current.BorderSize; + } + } + + public static Size CaptionButtonSize { + get { + return ThemeEngine.Current.CaptionButtonSize; + } + } + + public static int CaptionHeight { + get { + return ThemeEngine.Current.CaptionHeight; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int CaretBlinkTime { get { return XplatUI.CaretBlinkTime; } } + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int CaretWidth { get { return XplatUI.CaretWidth; } } + + public static string ComputerName { + get { + return Environment.MachineName; + } + } + + public static Size CursorSize { + get { + return XplatUI.CursorSize; + } + } + + public static bool DbcsEnabled { + get { + return false; + } + } + + public static bool DebugOS { + get { + return false; + } + } + + public static Size DoubleClickSize { + get { + return ThemeEngine.Current.DoubleClickSize; + } + } + + public static int DoubleClickTime { + get { + return ThemeEngine.Current.DoubleClickTime; + } + } + + public static bool DragFullWindows { + get { + return XplatUI.DragFullWindows; + } + } + + public static Size DragSize { + get { + return XplatUI.DragSize; + } + } + + public static Size FixedFrameBorderSize { + get { + return ThemeEngine.Current.FixedFrameBorderSize; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int FontSmoothingContrast { get { return XplatUI.FontSmoothingContrast; } } + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int FontSmoothingType { get { return XplatUI.FontSmoothingType; } } + + public static Size FrameBorderSize { + get { + return ThemeEngine.Current.FrameBorderSize; + } + } + + public static bool HighContrast { + get { + return false; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int HorizontalFocusThickness { get { return ThemeEngine.Current.HorizontalFocusThickness; } } + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int HorizontalResizeBorderThickness { get { return XplatUI.HorizontalResizeBorderThickness; } } + + public static int HorizontalScrollBarArrowWidth { + get { + return ThemeEngine.Current.HorizontalScrollBarArrowWidth; + } + } + + public static int HorizontalScrollBarHeight { + get { + return ThemeEngine.Current.HorizontalScrollBarHeight; + } + } + + public static int HorizontalScrollBarThumbWidth { + get { + return ThemeEngine.Current.HorizontalScrollBarThumbWidth; + } + } + + public static Size IconSize { + get { + return XplatUI.IconSize; + } + } + + public static int IconHorizontalSpacing { + get { + return IconSpacingSize.Width; + } + } + + public static int IconVerticalSpacing { + get { + return IconSpacingSize.Height; + } + } + + public static Size IconSpacingSize { + get { + return ThemeEngine.Current.IconSpacingSize; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsActiveWindowTrackingEnabled { + get { return XplatUI.IsActiveWindowTrackingEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsComboBoxAnimationEnabled { + get { return XplatUI.IsComboBoxAnimationEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsDropShadowEnabled { + get { return XplatUI.IsDropShadowEnabled; } + } + + public static bool IsFlatMenuEnabled { + get { return false; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsFontSmoothingEnabled { + get { return XplatUI.IsFontSmoothingEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsHotTrackingEnabled { + get { return XplatUI.IsHotTrackingEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsIconTitleWrappingEnabled { + get { return XplatUI.IsIconTitleWrappingEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsKeyboardPreferred { + get { return XplatUI.IsKeyboardPreferred; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsListBoxSmoothScrollingEnabled { + get { return XplatUI.IsListBoxSmoothScrollingEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsMenuAnimationEnabled { + get { return XplatUI.IsMenuAnimationEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsMenuFadeEnabled { + get { return XplatUI.IsMenuFadeEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsMinimizeRestoreAnimationEnabled { + get { return XplatUI.IsMinimizeRestoreAnimationEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsSelectionFadeEnabled { + get { return XplatUI.IsSelectionFadeEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsSnapToDefaultEnabled { + get { return XplatUI.IsSnapToDefaultEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsTitleBarGradientEnabled { + get { return XplatUI.IsTitleBarGradientEnabled; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool IsToolTipAnimationEnabled { + get { return XplatUI.IsToolTipAnimationEnabled; } + } + + public static int KanjiWindowHeight { + get { + return 0; + } + } + + public static int KeyboardDelay { + get { + return XplatUI.KeyboardDelay; + } + } + + public static int KeyboardSpeed { + get { + return XplatUI.KeyboardSpeed; + } + } + + public static Size MaxWindowTrackSize { + get { + return XplatUI.MaxWindowTrackSize; + } + } + + public static bool MenuAccessKeysUnderlined { + get { + return ThemeEngine.Current.MenuAccessKeysUnderlined; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static Size MenuBarButtonSize { + get { return ThemeEngine.Current.MenuBarButtonSize; } + } + + public static Size MenuButtonSize { + get { + return ThemeEngine.Current.MenuButtonSize; + } + } + + public static Size MenuCheckSize { + get { + return ThemeEngine.Current.MenuCheckSize; + } + } + + public static Font MenuFont { + get { + // note: we MUST return a clone of the Font instance as anyone + // can dispose it. However we shouldn't expect the theme to do + // the cloning for performance reason + return (Font) ThemeEngine.Current.MenuFont.Clone (); + } + } + + public static int MenuHeight { + get { + return ThemeEngine.Current.MenuHeight; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int MenuShowDelay { get { return XplatUI.MenuShowDelay; } } + + public static bool MidEastEnabled { + get { + return false; // ??? how do we decide? + } + } + + public static Size MinimizedWindowSize { + get { + return XplatUI.MinimizedWindowSize; + } + } + + public static Size MinimizedWindowSpacingSize { + get { + return XplatUI.MinimizedWindowSpacingSize; + } + } + + public static Size MinimumWindowSize { + get { + return XplatUI.MinimumWindowSize; + } + } + + public static Size MinWindowTrackSize { + get { + return XplatUI.MinWindowTrackSize; + } + } + + public static int MonitorCount { + get { + return Screen.AllScreens.Length; + } + } + + public static bool MonitorsSameDisplayFormat { + get { + return true; + } + } + + public static int MouseButtons { + get { + return XplatUI.MouseButtonCount; + } + } + + public static bool MouseButtonsSwapped { + get { + return XplatUI.MouseButtonsSwapped; + } + } + + public static Size MouseHoverSize { + get { + return XplatUI.MouseHoverSize; + } + } + + public static int MouseHoverTime { + get { + return XplatUI.MouseHoverTime; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int MouseSpeed { + get { return XplatUI.MouseSpeed; } + } + + public static int MouseWheelScrollDelta { + get { + return XplatUI.MouseWheelScrollDelta; + } + } + + //[EditorBrowsable (EditorBrowsableState.Never)] + public static bool MousePresent { + get { + return true; + } + } + + public static bool MouseWheelPresent { + get { + return XplatUI.MouseWheelPresent; + } + } + + public static int MouseWheelScrollLines { + get { + return ThemeEngine.Current.MouseWheelScrollLines; + } + } + + public static bool NativeMouseWheelSupport { + get { + return MouseWheelPresent; + } + } + + public static bool Network { + get { + return true; + } + } + + public static bool PenWindows { + get { + return false; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static LeftRightAlignment PopupMenuAlignment { + get { return XplatUI.PopupMenuAlignment; } + } + + [MonoTODO ("Only implemented for Win32.")] + public static PowerStatus PowerStatus { + get { return XplatUI.PowerStatus; } + } + + public static Size PrimaryMonitorMaximizedWindowSize { + get { + var workingArea = Screen.PrimaryScreen.WorkingArea; + return new Size (workingArea.Width, workingArea.Height); + } + } + + public static Size PrimaryMonitorSize { + get { + var bounds = Screen.PrimaryScreen.Bounds; + return new Size (bounds.Width, bounds.Height); + } + } + + public static bool RightAlignedMenus { + get { + return ThemeEngine.Current.RightAlignedMenus; + } + } + + public static ScreenOrientation ScreenOrientation { + get { return ScreenOrientation.Angle0; } + } + + public static bool Secure { + get { + return true; + } + } + + public static bool ShowSounds { + get { + return false; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int SizingBorderWidth { + get { return XplatUI.SizingBorderWidth; } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static Size SmallCaptionButtonSize { + get { return XplatUI.SmallCaptionButtonSize; } + } + + public static Size SmallIconSize { + get { + return XplatUI.SmallIconSize; + } + } + + public static bool TerminalServerSession { + get { + return false; + } + } + + public static Size ToolWindowCaptionButtonSize { + get { + return ThemeEngine.Current.ToolWindowCaptionButtonSize; + } + } + + public static int ToolWindowCaptionHeight { + get { + return ThemeEngine.Current.ToolWindowCaptionHeight; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static bool UIEffectsEnabled { + get { return XplatUI.UIEffectsEnabled; } + } + + public static string UserDomainName { + get { + return Environment.UserDomainName; + } + } + + public static bool UserInteractive { + get { + return Environment.UserInteractive; + } + } + + public static string UserName { + get { + return Environment.UserName; + } + } + + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int VerticalFocusThickness { get { return ThemeEngine.Current.VerticalFocusThickness; } } + [MonoInternalNote ("Determine if we need an X11 implementation or if defaults are good.")] + public static int VerticalResizeBorderThickness { get { return XplatUI.VerticalResizeBorderThickness; } } + + public static int VerticalScrollBarArrowHeight { + get { + return ThemeEngine.Current.VerticalScrollBarArrowHeight; + } + } + + public static int VerticalScrollBarThumbHeight { + get { + return ThemeEngine.Current.VerticalScrollBarThumbHeight; + } + } + + public static int VerticalScrollBarWidth { + get { + return ThemeEngine.Current.VerticalScrollBarWidth; + } + } + + public static Rectangle VirtualScreen { + get { + var rect = new Rectangle (); + foreach (var screen in Screen.AllScreens) + rect = Rectangle.Union (rect, screen.Bounds); + return rect; + } + } + + public static Rectangle WorkingArea { + get { + return Screen.PrimaryScreen.WorkingArea; + } + } + } +} diff --git a/source/ShiftUI/Internal/SystemParameter.cs b/source/ShiftUI/Internal/SystemParameter.cs new file mode 100644 index 0000000..fd4f7c1 --- /dev/null +++ b/source/ShiftUI/Internal/SystemParameter.cs @@ -0,0 +1,49 @@ +// +// SystemParameter.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +using System.Runtime.InteropServices; + +namespace ShiftUI +{ + [ComVisible(true)] + public enum SystemParameter + { + DropShadow = 0, + FlatMenu = 1, + FontSmoothingContrastMetric = 2, + FontSmoothingTypeMetric = 3, + MenuFadeEnabled = 4, + SelectionFade = 5, + ToolTipAnimationMetric = 6, + UIEffects = 7, + CaretWidthMetric = 8, + VerticalFocusThicknessMetric = 9, + HorizontalFocusThicknessMetric = 10 + } +} diff --git a/source/ShiftUI/Internal/TableLayout.cs b/source/ShiftUI/Internal/TableLayout.cs new file mode 100644 index 0000000..f32ca96 --- /dev/null +++ b/source/ShiftUI/Internal/TableLayout.cs @@ -0,0 +1,603 @@ +// +// TableLayout.cs +// +// 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) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +#undef TABLE_DEBUG + +using System; +using System.Drawing; + +namespace ShiftUI.Layout +{ + internal class TableLayout : LayoutEngine + { + private static Widget dummy_Widget = new Widget ("Dummy"); // Used as a placeholder for row/col spans + + public TableLayout () : base () + { + } + + public override void InitLayout (object child, BoundsSpecified specified) + { + base.InitLayout (child, specified); + } + + // There are 3 steps to doing a table layout: + // 1) Figure out which row/column each Widget goes into + // 2) Figure out the sizes of each row/column + // 3) Size and position each Widget + public override bool Layout (object container, LayoutEventArgs args) + { + TableLayoutPanel panel = container as TableLayoutPanel; + TableLayoutSettings settings = panel.LayoutSettings; + +#if TABLE_DEBUG + Console.WriteLine ("Beginning layout on panel: {0}, Widget count: {1}, col/row count: {2}x{3}", panel.Name, panel.Widgets.Count, settings.ColumnCount, settings.RowCount); +#endif + + // STEP 1: + // - Figure out which row/column each Widget goes into + // - Store data in the TableLayoutPanel.actual_positions + panel.actual_positions = CalculateWidgetPositions (panel, Math.Max (settings.ColumnCount, 1), Math.Max (settings.RowCount, 1)); + + // STEP 2: + // - Figure out the sizes of each row/column + // - Store data in the TableLayoutPanel.widths/heights + CalculateColumnRowSizes (panel, panel.actual_positions.GetLength (0), panel.actual_positions.GetLength (1)); + + // STEP 3: + // - Size and position each Widget + LayoutWidgets(panel); + +#if TABLE_DEBUG + Console.WriteLine ("-- CalculatedPositions:"); + OutputWidgetGrid (panel.actual_positions, panel); + + Console.WriteLine ("Finished layout on panel: {0}", panel.Name); + Console.WriteLine (); +#endif + + return false; + } + + internal Widget[,] CalculateWidgetPositions (TableLayoutPanel panel, int columns, int rows) + { + Widget[,] grid = new Widget[columns, rows]; + + TableLayoutSettings settings = panel.LayoutSettings; + + // First place all Widgets that have an explicit col/row + foreach (Widget c in panel.Widgets) { + int col = settings.GetColumn (c); + int row = settings.GetRow (c); + if (col >= 0 && row >= 0) { + if (col >= columns) + return CalculateWidgetPositions (panel, col + 1, rows); + if (row >= rows) + return CalculateWidgetPositions (panel, columns, row + 1); + + if (grid[col, row] == null) { + int col_span = Math.Min (settings.GetColumnSpan (c), columns); + int row_span = Math.Min (settings.GetRowSpan (c), rows); + + if (col + col_span > columns) { + if (row + 1 < rows) { + grid[col, row] = dummy_Widget; + row++; + col = 0; + } + else if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddColumns) + return CalculateWidgetPositions (panel, columns + 1, rows); + else + throw new ArgumentException (); + } + + if (row + row_span > rows) { + if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddRows) + return CalculateWidgetPositions (panel, columns, rows + 1); + else + throw new ArgumentException (); + } + + grid[col, row] = c; + + // Fill in the rest of this Widget's row/column extent with dummy + // Widgets, so that other Widgets don't get put there. + for (int i = 0; i < col_span; i++) + for (int j = 0; j < row_span; j++) + if (i != 0 || j != 0) + grid[col + i, row + j] = dummy_Widget; + } + } + } + + int x_pointer = 0; + int y_pointer = 0; + + // Fill in gaps with Widgets that do not have an explicit col/row + foreach (Widget c in panel.Widgets) { + int col = settings.GetColumn (c); + int row = settings.GetRow (c); + + if ((col >= 0 && col < columns) && (row >= 0 && row < rows) && (grid[col, row] == c || grid[col, row] == dummy_Widget)) + continue; + + for (int y = y_pointer; y < rows; y++) { + y_pointer = y; + x_pointer = 0; + + for (int x = x_pointer; x < columns; x++) { + x_pointer = x; + + if (grid[x, y] == null) { + int col_span = Math.Min (settings.GetColumnSpan (c), columns); + int row_span = Math.Min (settings.GetRowSpan (c), rows); + + if (x + col_span > columns) { + if (y + 1 < rows) + break; + else if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddColumns) + return CalculateWidgetPositions (panel, columns + 1, rows); + else + throw new ArgumentException (); + } + + if (y + row_span > rows) { + if (x + 1 < columns) + break; + else if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddRows) + return CalculateWidgetPositions (panel, columns, rows + 1); + else + throw new ArgumentException (); + } + + grid[x, y] = c; + + // Fill in the rest of this Widget's row/column extent with dummy + // Widgets, so that other Widgets don't get put there. + for (int i = 0; i < col_span; i++) + for (int j = 0; j < row_span; j++) + if (i != 0 || j != 0) + grid[x + i, y + j] = dummy_Widget; + + // I know someone will kill me for using a goto, but + // sometimes they really are the easiest way... + goto Found; + } else { + // MS adds the Widgets only to the first row if + // GrowStyle is AddColumns and RowCount is 0, + // so interrupt the search for a free horizontal cell + // beyond the first one in the given vertical + if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddColumns && + settings.RowCount == 0) + break; + } + } + } + + // MS adds rows instead of columns even when GrowStyle is AddColumns, + // but RowCount is 0. + TableLayoutPanelGrowStyle adjustedGrowStyle = settings.GrowStyle; + if (settings.GrowStyle == TableLayoutPanelGrowStyle.AddColumns) { + if (settings.RowCount == 0) + adjustedGrowStyle = TableLayoutPanelGrowStyle.AddRows; + } + + switch (adjustedGrowStyle) { + case TableLayoutPanelGrowStyle.AddColumns: + return CalculateWidgetPositions (panel, columns + 1, rows); + case TableLayoutPanelGrowStyle.AddRows: + default: + return CalculateWidgetPositions (panel, columns, rows + 1); + case TableLayoutPanelGrowStyle.FixedSize: + throw new ArgumentException (); + } + + Found: ; + } + + return grid; + } + + private void CalculateColumnRowSizes (TableLayoutPanel panel, int columns, int rows) + { + TableLayoutSettings settings = panel.LayoutSettings; + + panel.column_widths = new int[panel.actual_positions.GetLength (0)]; + panel.row_heights = new int[panel.actual_positions.GetLength (1)]; + + int border_width = TableLayoutPanel.GetCellBorderWidth (panel.CellBorderStyle); + + Rectangle parentDisplayRectangle = panel.DisplayRectangle; + + TableLayoutColumnStyleCollection col_styles = new TableLayoutColumnStyleCollection (panel); + + foreach (ColumnStyle cs in settings.ColumnStyles) + col_styles.Add( new ColumnStyle(cs.SizeType, cs.Width)); + + TableLayoutRowStyleCollection row_styles = new TableLayoutRowStyleCollection (panel); + + foreach (RowStyle rs in settings.RowStyles) + row_styles.Add (new RowStyle (rs.SizeType, rs.Height)); + + // If we have more columns than columnstyles, temporarily add enough columnstyles + if (columns > col_styles.Count) + { + for (int i = col_styles.Count; i < columns; i++) + col_styles.Add(new ColumnStyle()); + } + + // Same for rows.. + if (rows > row_styles.Count) + { + for (int i = row_styles.Count; i < rows; i++) + row_styles.Add (new RowStyle ()); + } + + while (row_styles.Count > rows) + row_styles.RemoveAt (row_styles.Count - 1); + while (col_styles.Count > columns) + col_styles.RemoveAt (col_styles.Count - 1); + + // Find the largest column-span/row-span values. + int max_colspan = 0, max_rowspan = 0; + foreach (Widget c in panel.Widgets) { + max_colspan = Math.Max (max_colspan, settings.GetColumnSpan (c)); + max_rowspan = Math.Max (max_rowspan, settings.GetRowSpan (c)); + } + + // Figure up all the column widths + int total_width = parentDisplayRectangle.Width - (border_width * (columns + 1)); + int index = 0; + + // First assign all the Absolute sized columns.. + foreach (ColumnStyle cs in col_styles) { + if (cs.SizeType == SizeType.Absolute) { + panel.column_widths[index] = (int)cs.Width; + total_width -= (int)cs.Width; + } + + index++; + } + + // Next, assign all the AutoSize columns to the width of their widest + // Widget. If the table-layout is auto-sized, then make sure that + // no column with Percent styling clips its contents. + // (per http://msdn.microsoft.com/en-us/library/ms171690.aspx) + for (int colspan = 0; colspan < max_colspan; ++colspan) + { + for (index = colspan; index < col_styles.Count - colspan; ++index) + { + ColumnStyle cs = col_styles[index]; + if (cs.SizeType == SizeType.AutoSize + || (panel.AutoSize && cs.SizeType == SizeType.Percent)) + { + int max_width = panel.column_widths[index]; + + // Find the widest Widget in the column + for (int i = 0; i < rows; i ++) + { + Widget c = panel.actual_positions[index - colspan, i]; + + if (c != null && c != dummy_Widget && c.VisibleInternal) + { + // Skip any Widgets not being sized in this pass. + if (settings.GetColumnSpan (c) != colspan + 1) + continue; + + // Calculate the maximum Widget width. + if (c.AutoSize) + max_width = Math.Max (max_width, c.PreferredSize.Width + c.Margin.Horizontal); + else + max_width = Math.Max (max_width, c.ExplicitBounds.Width + c.Margin.Horizontal); + max_width = Math.Max (max_width, c.Width + c.Margin.Left + c.Margin.Right); + } + } + + // Subtract the width of prior columns, if any. + for (int i = Math.Max (index - colspan, 0); i < index; ++i) + max_width -= panel.column_widths[i]; + + // If necessary, increase this column's width. + if (max_width > panel.column_widths[index]) + { + max_width -= panel.column_widths[index]; + panel.column_widths[index] += max_width; + total_width -= max_width; + } + } + } + } + + index = 0; + float total_percent = 0; + + // Finally, assign the remaining space to Percent columns, if any. + if (total_width > 0) + { + int percent_width = total_width; + + // Find the total percent (not always 100%) + foreach (ColumnStyle cs in col_styles) + { + if (cs.SizeType == SizeType.Percent) + total_percent += cs.Width; + } + + // Divvy up the space.. + foreach (ColumnStyle cs in col_styles) + { + if (cs.SizeType == SizeType.Percent) + { + int width_change = (int)(((cs.Width / total_percent) * percent_width) + - panel.column_widths[index]); + if (width_change > 0) + { + panel.column_widths[index] += width_change; + total_width -= width_change; + } + } + + index++; + } + } + + if (total_width > 0) + { + // Find the last column that isn't an Absolute SizeType, and give it + // all this free space. (Absolute sized columns need to retain their + // absolute width if at all possible!) + int col = col_styles.Count - 1; + for (; col >= 0; --col) + { + if (col_styles[col].SizeType != SizeType.Absolute) + break; + } + if (col < 0) + col = col_styles.Count - 1; + panel.column_widths[col] += total_width; + } + + // Figure up all the row heights + int total_height = parentDisplayRectangle.Height - (border_width * (rows + 1)); + index = 0; + + // First assign all the Absolute sized rows.. + foreach (RowStyle rs in row_styles) { + if (rs.SizeType == SizeType.Absolute) { + panel.row_heights[index] = (int)rs.Height; + total_height -= (int)rs.Height; + } + + index++; + } + + index = 0; + + // Next, assign all the AutoSize rows to the height of their tallest + // Widget. If the table-layout is auto-sized, then make sure that + // no row with Percent styling clips its contents. + // (per http://msdn.microsoft.com/en-us/library/ms171690.aspx) + for (int rowspan = 0; rowspan < max_rowspan; ++rowspan) + { + for (index = rowspan; index < row_styles.Count - rowspan; ++index) + { + RowStyle rs = row_styles[index]; + if (rs.SizeType == SizeType.AutoSize + || (panel.AutoSize && rs.SizeType == SizeType.Percent)) + { + int max_height = panel.row_heights[index]; + + // Find the tallest Widget in the row + for (int i = 0; i < columns; i++) { + Widget c = panel.actual_positions[i, index - rowspan]; + + if (c != null && c != dummy_Widget && c.VisibleInternal) + { + // Skip any Widgets not being sized in this pass. + if (settings.GetRowSpan (c) != rowspan + 1) + continue; + + // Calculate the maximum Widget height. + if (c.AutoSize) + max_height = Math.Max (max_height, c.PreferredSize.Height + c.Margin.Vertical); + else + max_height = Math.Max (max_height, c.ExplicitBounds.Height + c.Margin.Vertical); + max_height = Math.Max (max_height, c.Height + c.Margin.Top + c.Margin.Bottom); + } + } + + // Subtract the height of prior rows, if any. + for (int i = Math.Max (index - rowspan, 0); i < index; ++i) + max_height -= panel.row_heights[i]; + + // If necessary, increase this row's height. + if (max_height > panel.row_heights[index]) + { + max_height -= panel.row_heights[index]; + panel.row_heights[index] += max_height; + total_height -= max_height; + } + } + } + } + + index = 0; + total_percent = 0; + + // Finally, assign the remaining space to Percent rows, if any. + if (total_height > 0) { + int percent_height = total_height; + + // Find the total percent (not always 100%) + foreach (RowStyle rs in row_styles) { + if (rs.SizeType == SizeType.Percent) + total_percent += rs.Height; + } + + // Divvy up the space.. + foreach (RowStyle rs in row_styles) { + if (rs.SizeType == SizeType.Percent) { + int height_change = (int)(((rs.Height / total_percent) * percent_height) + - panel.row_heights[index]); + if (height_change > 0) + { + panel.row_heights[index] += height_change; + total_height -= height_change; + } + } + + index++; + } + } + + if (total_height > 0) + { + // Find the last row that isn't an Absolute SizeType, and give it + // all this free space. (Absolute sized rows need to retain their + // absolute height if at all possible!) + int row = row_styles.Count - 1; + for (; row >= 0; --row) + { + if (row_styles[row].SizeType != SizeType.Absolute) + break; + } + if (row < 0) + row = row_styles.Count - 1; + panel.row_heights[row] += total_height; + } + } + + private void LayoutWidgets (TableLayoutPanel panel) + { + TableLayoutSettings settings = panel.LayoutSettings; + + int border_width = TableLayoutPanel.GetCellBorderWidth (panel.CellBorderStyle); + + int columns = panel.actual_positions.GetLength(0); + int rows = panel.actual_positions.GetLength(1); + + Point current_pos = new Point (panel.DisplayRectangle.Left + border_width, panel.DisplayRectangle.Top + border_width); + + for (int y = 0; y < rows; y++) + { + for (int x = 0; x < columns; x++) + { + Widget c = panel.actual_positions[x,y]; + + if(c != null && c != dummy_Widget) { + Size preferred; + + if (c.AutoSize) + preferred = c.PreferredSize; + else + preferred = c.ExplicitBounds.Size; + + int new_x = 0; + int new_y = 0; + int new_width = 0; + int new_height = 0; + + // Figure out the width of the Widget + int column_width = panel.column_widths[x]; + + for (int i = 1; i < Math.Min (settings.GetColumnSpan(c), panel.column_widths.Length); i++) + column_width += panel.column_widths[x + i]; + + if (c.Dock == DockStyle.Fill || c.Dock == DockStyle.Top || c.Dock == DockStyle.Bottom || ((c.Anchor & AnchorStyles.Left) == AnchorStyles.Left && (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right)) + new_width = column_width - c.Margin.Left - c.Margin.Right; + else + new_width = Math.Min (preferred.Width, column_width - c.Margin.Left - c.Margin.Right); + + // Figure out the height of the Widget + int column_height = panel.row_heights[y]; + + for (int i = 1; i < Math.Min (settings.GetRowSpan (c), panel.row_heights.Length); i++) + column_height += panel.row_heights[y + i]; + + if (c.Dock == DockStyle.Fill || c.Dock == DockStyle.Left || c.Dock == DockStyle.Right || ((c.Anchor & AnchorStyles.Top) == AnchorStyles.Top && (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom)) + new_height = column_height - c.Margin.Top - c.Margin.Bottom; + else + new_height = Math.Min (preferred.Height, column_height - c.Margin.Top - c.Margin.Bottom); + + // Figure out the left location of the Widget + if (c.Dock == DockStyle.Left || c.Dock == DockStyle.Fill || (c.Anchor & AnchorStyles.Left) == AnchorStyles.Left) + new_x = current_pos.X + c.Margin.Left; + else if (c.Dock == DockStyle.Right || (c.Anchor & AnchorStyles.Right) == AnchorStyles.Right) + new_x = (current_pos.X + column_width) - new_width - c.Margin.Right; + else // (center Widget) + new_x = (current_pos.X + (column_width - c.Margin.Left - c.Margin.Right) / 2) + c.Margin.Left - (new_width / 2); + + // Figure out the top location of the Widget + if (c.Dock == DockStyle.Top || c.Dock == DockStyle.Fill || (c.Anchor & AnchorStyles.Top) == AnchorStyles.Top) + new_y = current_pos.Y + c.Margin.Top; + else if (c.Dock == DockStyle.Bottom || (c.Anchor & AnchorStyles.Bottom) == AnchorStyles.Bottom) + new_y = (current_pos.Y + column_height) - new_height - c.Margin.Bottom; + else // (center Widget) + new_y = (current_pos.Y + (column_height - c.Margin.Top - c.Margin.Bottom) / 2) + c.Margin.Top - (new_height / 2); + + c.SetBoundsInternal (new_x, new_y, new_width, new_height, BoundsSpecified.None); + } + + current_pos.Offset (panel.column_widths[x] + border_width, 0); + } + + current_pos.Offset ((-1 * current_pos.X) + border_width + panel.DisplayRectangle.Left, panel.row_heights[y] + border_width); + } + } + +#if TABLE_DEBUG + private void OutputWidgetGrid (Widget[,] grid, TableLayoutPanel panel) + { + Console.WriteLine (" Size: {0}x{1}", grid.GetLength (0), grid.GetLength (1)); + + Console.Write (" "); + + foreach (int i in panel.column_widths) + Console.Write (" {0}px ", i.ToString ().PadLeft (3)); + + Console.WriteLine (); + + for (int y = 0; y < grid.GetLength (1); y++) { + Console.Write (" {0}px |", panel.row_heights[y].ToString ().PadLeft (3)); + + for (int x = 0; x < grid.GetLength (0); x++) { + if (grid[x, y] == null) + Console.Write (" --- |"); + else if (string.IsNullOrEmpty (grid[x, y].Name)) + Console.Write (" ??? |"); + else + Console.Write (" {0} |", grid[x, y].Name.PadRight (5).Substring (0, 5)); + } + + Console.WriteLine (); + } + } +#endif + } +} diff --git a/source/ShiftUI/Internal/TableLayoutSettings.cs b/source/ShiftUI/Internal/TableLayoutSettings.cs new file mode 100644 index 0000000..786a98b --- /dev/null +++ b/source/ShiftUI/Internal/TableLayoutSettings.cs @@ -0,0 +1,362 @@ +// +// TableLayoutSettings.cs +// +// 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) 2006 Jonathan Pobst +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +using System; +using System.ComponentModel; +using System.Collections.Generic; +using ShiftUI.Layout; +using System.Runtime.Serialization; + +namespace ShiftUI +{ + [Serializable] + [TypeConverter (typeof (TableLayoutSettingsTypeConverter))] + public sealed class TableLayoutSettings : LayoutSettings, ISerializable + { + private TableLayoutColumnStyleCollection column_styles; + private TableLayoutRowStyleCollection row_styles; + private TableLayoutPanelGrowStyle grow_style; + private int column_count; + private int row_count; + private Dictionary<Object, int> columns; + private Dictionary<Object, int> column_spans; + private Dictionary<Object, int> rows; + private Dictionary<Object, int> row_spans; + internal TableLayoutPanel panel; + internal bool isSerialized; + + #region Internal Constructor + internal TableLayoutSettings (TableLayoutPanel panel) + { + this.column_styles = new TableLayoutColumnStyleCollection (panel); + this.row_styles = new TableLayoutRowStyleCollection (panel); + this.grow_style = TableLayoutPanelGrowStyle.AddRows; + this.column_count = 0; + this.row_count = 0; + this.columns = new Dictionary<object, int> (); + this.column_spans = new Dictionary<object, int> (); + this.rows = new Dictionary<object, int> (); + this.row_spans = new Dictionary<object, int> (); + this.panel = panel; + } + + private TableLayoutSettings (SerializationInfo serializationInfo, StreamingContext context) + { + TypeConverter converter = TypeDescriptor.GetConverter (this); + string text = serializationInfo.GetString ("SerializedString"); + if (!string.IsNullOrEmpty (text) && (converter != null)) { + TableLayoutSettings settings = converter.ConvertFromInvariantString (text) as TableLayoutSettings; + this.column_styles = settings.column_styles; + this.row_styles = settings.row_styles; + this.grow_style = settings.grow_style; + this.column_count = settings.column_count; + this.row_count = settings.row_count; + this.columns = settings.columns; + this.column_spans = settings.column_spans; + this.rows = settings.rows; + this.row_spans = settings.row_spans; + this.panel = settings.panel; + this.isSerialized = true; + } + } + #endregion + + #region Public Properties + [DefaultValue (0)] + public int ColumnCount { + get { return this.column_count; } + set { + if (value < 0) + throw new ArgumentOutOfRangeException(); + + if (column_count != value) { + column_count = value; + if (panel != null) + panel.PerformLayout (panel, "ColumnCount"); + } + } + } + + //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] + public TableLayoutColumnStyleCollection ColumnStyles { + get { return this.column_styles; } + } + + [DefaultValue (TableLayoutPanelGrowStyle.AddRows)] + public TableLayoutPanelGrowStyle GrowStyle { + get { return this.grow_style; } + set { + if (!Enum.IsDefined (typeof(TableLayoutPanelGrowStyle), value)) + throw new ArgumentException(); + + if (grow_style != value) { + grow_style = value; + if (panel != null) + panel.PerformLayout (panel, "GrowStyle"); + } + } + } + + public override LayoutEngine LayoutEngine { + get { + if (panel != null) + return panel.LayoutEngine; + return base.LayoutEngine; + } + } + + [DefaultValue (0)] + public int RowCount { + get { return this.row_count; } + set { + if (value < 0) + throw new ArgumentOutOfRangeException (); + + if (row_count != value) { + row_count = value; + + if (panel != null) + panel.PerformLayout (); + } + } + } + + //[DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] + public TableLayoutRowStyleCollection RowStyles { + get { return row_styles; } + } + #endregion + + #region Public Methods + [DefaultValue (-1)] + public TableLayoutPanelCellPosition GetCellPosition (Object control) + { + if (control == null) + throw new ArgumentNullException (); + + int column; + int row; + + if (!columns.TryGetValue (control, out column)) + { + if (!(control is Widget) || !columns.TryGetValue ((control as Widget).Name, out column)) + column = -1; + } + if (!rows.TryGetValue (control, out row)) + { + if (!(control is Widget) || !rows.TryGetValue ((control as Widget), out row)) + row = -1; + } + + return new TableLayoutPanelCellPosition (column, row); + } + + [DefaultValue (-1)] + public int GetColumn (Object control) + { + if (control == null) + throw new ArgumentNullException (); + + int retval; + + if (columns.TryGetValue (control, out retval)) + return retval; + if ((control is Widget) && columns.TryGetValue ((control as Widget).Name, out retval)) + return retval; + + return -1; + } + + public int GetColumnSpan (Object control) + { + if (control == null) + throw new ArgumentNullException (); + + int retval; + + if (column_spans.TryGetValue (control, out retval)) + return retval; + if ((control is Widget) && column_spans.TryGetValue ((control as Widget).Name, out retval)) + return retval; + + return 1; + } + + [DefaultValue (-1)] + public int GetRow (Object control) + { + if (control == null) + throw new ArgumentNullException (); + + int retval; + + if (rows.TryGetValue (control, out retval)) + return retval; + if ((control is Widget) && rows.TryGetValue ((control as Widget).Name, out retval)) + return retval; + + return -1; + } + + public int GetRowSpan (Object control) + { + if (control == null) + throw new ArgumentNullException (); + + int retval; + + if (row_spans.TryGetValue (control, out retval)) + return retval; + if ((control is Widget) && row_spans.TryGetValue ((control as Widget).Name, out retval)) + return retval; + + return 1; + } + + [DefaultValue (-1)] + public void SetCellPosition (Object control, TableLayoutPanelCellPosition cellPosition) + { + if (control == null) + throw new ArgumentNullException (); + + columns[control] = cellPosition.Column; + rows[control] = cellPosition.Row; + + if (panel != null) + panel.PerformLayout (); + } + + public void SetColumn (Object control, int column) + { + if (control == null) + throw new ArgumentNullException (); + if (column < -1) + throw new ArgumentException (); + + columns[control] = column; + + if (panel != null) + panel.PerformLayout (); + } + + public void SetColumnSpan (Object control, int value) + { + if (control == null) + throw new ArgumentNullException (); + if (value < -1) + throw new ArgumentException (); + + column_spans[control] = value; + + if (panel != null) + panel.PerformLayout (); + } + + public void SetRow (Object control, int row) + { + if (control == null) + throw new ArgumentNullException (); + if (row < -1) + throw new ArgumentException (); + + rows[control] = row; + + if (panel != null) + panel.PerformLayout (); + } + + public void SetRowSpan (Object control, int value) + { + if (control == null) + throw new ArgumentNullException (); + if (value < -1) + throw new ArgumentException (); + + row_spans[control] = value; + + if (panel != null) + panel.PerformLayout (); + } + #endregion + + #region Internal Methods + internal List<WidgetInfo> GetWidgets () + { + List<WidgetInfo> list = new List<WidgetInfo>(); + foreach (KeyValuePair <object, int> control in columns) { + WidgetInfo info = new WidgetInfo(); + info.Widget = control.Key; + info.Col = GetColumn(control.Key); + info.ColSpan = GetColumnSpan (control.Key); + info.Row = GetRow (control.Key); + info.RowSpan = GetRowSpan (control.Key); + list.Add (info); + } + return list; + } + + #endregion + + #region ISerializable Members + void ISerializable.GetObjectData (SerializationInfo si, StreamingContext context) + { + TableLayoutSettingsTypeConverter conv = new TableLayoutSettingsTypeConverter (); + string text = conv.ConvertToInvariantString (this); + si.AddValue ("SerializedString", text); + } + #endregion + + internal class StyleConverter : TypeConverter + { + public override object ConvertFrom (ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + if ((value == null) || !(value is String)) + return base.ConvertFrom (context, culture, value); + + return Enum.Parse (typeof (StyleConverter), (string)value, true); + } + + public override object ConvertTo (ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + { + if ((value == null) || !(value is StyleConverter) || (destinationType != typeof (string))) + return base.ConvertTo (context, culture, value, destinationType); + + return ((StyleConverter)value).ToString (); + } + } + } + + internal struct WidgetInfo + { + public object Widget; + public int Row; + public int RowSpan; + public int Col; + public int ColSpan; + } +} diff --git a/source/ShiftUI/Internal/TableLayoutSettingsTypeConverter.cs b/source/ShiftUI/Internal/TableLayoutSettingsTypeConverter.cs new file mode 100644 index 0000000..d9396b9 --- /dev/null +++ b/source/ShiftUI/Internal/TableLayoutSettingsTypeConverter.cs @@ -0,0 +1,233 @@ +// 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) 2006 Novell, Inc. +// + + +using System; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; +using System.Xml; +using System.IO; +using System.Collections.Generic; + +namespace ShiftUI.Layout +{ + public class TableLayoutSettingsTypeConverter : TypeConverter + { + public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof (string)) + return true; + + return base.CanConvertTo (context, destinationType); + } + + public override bool CanConvertFrom (ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + return true; + + return base.CanConvertFrom (context, sourceType); + } + + + + public override object ConvertTo (ITypeDescriptorContext context, + CultureInfo culture, + object value, + Type destinationType) + { + if (!(value is TableLayoutSettings) || destinationType != typeof (string)) + return base.ConvertTo (context, culture, value, destinationType); + + TableLayoutSettings settings = value as TableLayoutSettings; + StringWriter sw = new StringWriter (); + XmlTextWriter xw = new XmlTextWriter (sw); + xw.WriteStartDocument (); + List<WidgetInfo> list = settings.GetWidgets (); + xw.WriteStartElement ("TableLayoutSettings"); + xw.WriteStartElement ("Widgets"); + + foreach (WidgetInfo info in list) { + xw.WriteStartElement ("Widget"); + xw.WriteAttributeString ("Name", info.Widget.ToString ()); + xw.WriteAttributeString ("Row", info.Row.ToString ()); + xw.WriteAttributeString ("RowSpan", info.RowSpan.ToString ()); + xw.WriteAttributeString ("Column", info.Col.ToString ()); + xw.WriteAttributeString ("ColumnSpan", info.ColSpan.ToString ()); + xw.WriteEndElement (); + } + xw.WriteEndElement (); + + + List<string> styles = new List<string> (); + + foreach (ColumnStyle style in settings.ColumnStyles) { + styles.Add (style.SizeType.ToString ()); + styles.Add (style.Width.ToString (CultureInfo.InvariantCulture)); + } + + + xw.WriteStartElement ("Columns"); + xw.WriteAttributeString ("Styles", String.Join (",", styles.ToArray ())); + xw.WriteEndElement (); + + styles.Clear(); + foreach (RowStyle style in settings.RowStyles) { + styles.Add (style.SizeType.ToString ()); + styles.Add (style.Height.ToString (CultureInfo.InvariantCulture)); + } + + xw.WriteStartElement ("Rows"); + xw.WriteAttributeString ("Styles", String.Join (",", styles.ToArray ())); + xw.WriteEndElement (); + + xw.WriteEndElement (); + xw.WriteEndDocument (); + xw.Close (); + + return sw.ToString (); + + } + + public override object ConvertFrom (ITypeDescriptorContext context, + CultureInfo culture, + object value) + { + if (!(value is string)) + return base.ConvertFrom(context, culture, value); + + XmlDocument xmldoc = new XmlDocument(); + xmldoc.LoadXml (value as string); + TableLayoutSettings settings = new TableLayoutSettings(new TableLayoutPanel()); + int count = ParseWidget (xmldoc, settings); + ParseColumnStyle (xmldoc, settings); + ParseRowStyle (xmldoc, settings); + settings.RowCount = count; + + + return settings; + } + + + private int ParseWidget (XmlDocument xmldoc, TableLayoutSettings settings) + { + int count = 0; + foreach (XmlNode node in xmldoc.GetElementsByTagName ("Widget")) { + if (node.Attributes["Name"] == null || string.IsNullOrEmpty(node.Attributes["Name"].Value)) + continue; + if (node.Attributes["Row"] != null) { + settings.SetRow (node.Attributes["Name"].Value, GetValue (node.Attributes["Row"].Value)); + count++; + } + if (node.Attributes["RowSpan"] != null) { + settings.SetRowSpan (node.Attributes["Name"].Value, GetValue (node.Attributes["RowSpan"].Value)); + } + if (node.Attributes["Column"] != null) + settings.SetColumn (node.Attributes["Name"].Value, GetValue (node.Attributes["Column"].Value)); + if (node.Attributes["ColumnSpan"] != null) + settings.SetColumnSpan (node.Attributes["Name"].Value, GetValue (node.Attributes["ColumnSpan"].Value)); + } + return count; + } + + private void ParseColumnStyle (XmlDocument xmldoc, TableLayoutSettings settings) + { + foreach (XmlNode node in xmldoc.GetElementsByTagName ("Columns")) { + if (node.Attributes["Styles"] == null) + continue; + string styles = node.Attributes["Styles"].Value; + if (string.IsNullOrEmpty (styles)) + continue; + string[] list = BuggySplit (styles); + for (int i = 0; i < list.Length; i+=2) { + float width = 0f; + SizeType type = (SizeType) Enum.Parse (typeof (SizeType), list[i]); + float.TryParse (list[i+1], NumberStyles.Float, CultureInfo.InvariantCulture, out width); + settings.ColumnStyles.Add (new ColumnStyle (type, width)); + } + } + } + + private void ParseRowStyle (XmlDocument xmldoc, TableLayoutSettings settings) + { + foreach (XmlNode node in xmldoc.GetElementsByTagName ("Rows")) { + if (node.Attributes["Styles"] == null) + continue; + string styles = node.Attributes["Styles"].Value; + if (string.IsNullOrEmpty(styles)) + continue; + string[] list = BuggySplit (styles); + for (int i = 0; i < list.Length; i += 2) { + float height = 0f; + SizeType type = (SizeType) Enum.Parse (typeof (SizeType), list[i]); + float.TryParse (list[i + 1], NumberStyles.Float, CultureInfo.InvariantCulture, out height); + settings.RowStyles.Add (new RowStyle (type, height)); + } + } + } + + private int GetValue (string attValue) + { + int val = -1; + if (!string.IsNullOrEmpty (attValue)) { + int.TryParse (attValue, out val); + } + return val; + } + + // .Net accidently uses the local culture separator, so + // Percent,50.0,Percent,50.0 can be + // Percent,50,0,Percent,50,0 + // Make sure we can handle this + private string[] BuggySplit (string s) + { + List<string> l = new List<string> (); + + string[] split = s.Split (','); + + for (int i = 0; i < split.Length; i++) { + switch (split[i].ToLowerInvariant ()) { + case "autosize": + case "absolute": + case "percent": + l.Add (split[i]); + break; + default: + if (i + 1 < split.Length) { + float test; + + if (float.TryParse (split[i + 1], out test)) { + l.Add (string.Format ("{0}.{1}", split[i], split[i + 1])); + i++; + } else + l.Add (split[i]); + } else + l.Add (split[i]); + break; + } + } + + return l.ToArray (); + } + } +} diff --git a/source/ShiftUI/Internal/TextBoxTextRenderer.cs b/source/ShiftUI/Internal/TextBoxTextRenderer.cs new file mode 100644 index 0000000..3e908f3 --- /dev/null +++ b/source/ShiftUI/Internal/TextBoxTextRenderer.cs @@ -0,0 +1,131 @@ +// 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) 2007 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Jonathan Pobst [email protected] +// +// + +using System; +using System.Drawing; +using System.Drawing.Text; +using System.Text; +using System.Collections; + +namespace ShiftUI +{ + internal class TextBoxTextRenderer + { + private static Size max_size; + private static bool use_textrenderer; + private static StringFormat sf_nonprinting; + private static StringFormat sf_printing; + private static Hashtable measure_cache; + + static TextBoxTextRenderer () + { + // On Windows, we want to use TextRenderer (GDI) + // On Linux, we want to use DrawString (GDI+) + // TextRenderer provides translation from TextRenderer to + // DrawString, but I doubt it's exact enough. + // Another option would be to put Pango here for Linux. + int platform = (int)Environment.OSVersion.Platform; + + if (platform == 4 || platform == 128 || platform == 6) + use_textrenderer = false; + else + use_textrenderer = true; + + // windows 2000 doesn't draw with gdi if bounds are In32.MaxValue + max_size = new Size (Int16.MaxValue, Int16.MaxValue); + + sf_nonprinting = new StringFormat (StringFormat.GenericTypographic); + sf_nonprinting.Trimming = StringTrimming.None; + sf_nonprinting.FormatFlags = StringFormatFlags.DisplayFormatControl; + sf_nonprinting.HotkeyPrefix = HotkeyPrefix.None; + + sf_printing = StringFormat.GenericTypographic; + sf_printing.HotkeyPrefix = HotkeyPrefix.None; + + measure_cache = new Hashtable (); + } + + public static void DrawText (Graphics g, string text, Font font, Color color, float x, float y, bool showNonPrint) + { + if (!use_textrenderer) { + if (showNonPrint) + g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (color), x, y, sf_nonprinting); + else + g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (color), x, y, sf_printing); + } else { + if (showNonPrint) + TextRenderer.DrawTextInternal (g, text, font, new Rectangle (new Point ((int)x, (int)y), max_size), color, TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix, false); + else + TextRenderer.DrawTextInternal (g, text, font, new Rectangle (new Point ((int)x, (int)y), max_size), color, TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix, false); + } + } + + public static SizeF MeasureText (Graphics g, string text, Font font) + { + // Due to the way the TextBox currently works, it measures each + // character one at a time. And it does this alot. So here we + // are implementing a cache for each font/character combination + // measurement. Since the number of fonts and number of characters + // used tends to be small, this is a good performance gain for + // not too much memory. + if (text.Length == 1) { + // If g.VisibleClipBounds is {X=0, Y=0, Width=1, Height=1}, then some characters + // (in some fonts for some point sizes) return a different width then when the + // VisibleClipBounds has a different (usually but not always more reasonable) value. + // This state of the Graphics object can occur during initialization of text boxes + // with preset Text values. See https://bugzilla.xamarin.com/show_bug.cgi?id=26258 + // for more details. + string sep; + var bounds = g.VisibleClipBounds; + if (bounds.Width == 1 && bounds.Height == 1 && bounds.X == 0 && bounds.Y == 0) + sep = "-1x1|"; + else + sep = "|"; + string key = font.GetHashCode ().ToString () + sep + text; + + if (measure_cache.ContainsKey (key)) { + return (SizeF)measure_cache[key]; + } else { + SizeF size; + + if (!use_textrenderer) + size = g.MeasureString (text, font, 10000, sf_nonprinting); + else + size = TextRenderer.MeasureTextInternal (g, text, font, Size.Empty, TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix, false); + + measure_cache[key] = size; + + return size; + } + } + + if (!use_textrenderer) + return g.MeasureString (text, font, 10000, sf_nonprinting); + else + return TextRenderer.MeasureTextInternal (g, text, font, Size.Empty, TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix, false); + } + } +} diff --git a/source/ShiftUI/Internal/TextControl.cs b/source/ShiftUI/Internal/TextControl.cs new file mode 100644 index 0000000..65dd5c6 --- /dev/null +++ b/source/ShiftUI/Internal/TextControl.cs @@ -0,0 +1,4613 @@ +// 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. (http://www.novell.com) +// +// Authors: +// Peter Bartok [email protected] +// +// + +// NOT COMPLETE + +// There's still plenty of things missing, I've got most of it planned, just hadn't had +// the time to write it all yet. +// Stuff missing (in no particular order): +// - Align text after RecalculateLine +// - Implement tag types for hotlinks, etc. +// - Implement CaretPgUp/PgDown + +// NOTE: +// selection_start.pos and selection_end.pos are 0-based +// selection_start.pos = first selected char +// selection_end.pos = first NOT-selected char +// +// FormatText methods are 1-based (as are all tags, LineTag.Start is 1 for +// the first character on a line; the reason is that 0 is the position +// *before* the first character on a line + + +#undef Debug + +using System; +using System.Collections; +using System.Drawing; +using System.Drawing.Text; +using System.Text; +using RTF=ShiftUI.RTF; + +namespace ShiftUI { + internal enum LineColor { + Red = 0, + Black = 1 + } + + internal enum CaretSelection { + Position, // Selection=Caret + Word, // Selection=Word under caret + Line // Selection=Line under caret + } + + [Flags] + internal enum FormatSpecified { + None, + + BackColor = 2, + Font = 4, + Color = 8, + } + + internal enum CaretDirection { + CharForward, // Move a char to the right + CharBack, // Move a char to the left + LineUp, // Move a line up + LineDown, // Move a line down + Home, // Move to the beginning of the line + End, // Move to the end of the line + PgUp, // Move one page up + PgDn, // Move one page down + CtrlPgUp, // Move caret to the first visible char in the viewport + CtrlPgDn, // Move caret to the last visible char in the viewport + CtrlHome, // Move to the beginning of the document + CtrlEnd, // Move to the end of the document + WordBack, // Move to the beginning of the previous word (or beginning of line) + WordForward, // Move to the beginning of the next word (or end of line) + SelectionStart, // Move to the beginning of the current selection + SelectionEnd, // Move to the end of the current selection + CharForwardNoWrap, // Move a char forward, but don't wrap onto the next line + CharBackNoWrap // Move a char backward, but don't wrap onto the previous line + } + + internal enum LineEnding { + Wrap = 1, // line wraps to the next line + Limp = 2, // \r + Hard = 4, // \r\n + Soft = 8, // \r\r\n + Rich = 16, // \n + + None = 0 + } + + internal class Document : ICloneable, IEnumerable { + #region Structures + // FIXME - go through code and check for places where + // we do explicit comparisons instead of using the compare overloads + internal struct Marker { + internal Line line; + internal LineTag tag; + internal int pos; + internal int height; + + public static bool operator<(Marker lhs, Marker rhs) { + if (lhs.line.line_no < rhs.line.line_no) { + return true; + } + + if (lhs.line.line_no == rhs.line.line_no) { + if (lhs.pos < rhs.pos) { + return true; + } + } + return false; + } + + public static bool operator>(Marker lhs, Marker rhs) { + if (lhs.line.line_no > rhs.line.line_no) { + return true; + } + + if (lhs.line.line_no == rhs.line.line_no) { + if (lhs.pos > rhs.pos) { + return true; + } + } + return false; + } + + public static bool operator==(Marker lhs, Marker rhs) { + if ((lhs.line.line_no == rhs.line.line_no) && (lhs.pos == rhs.pos)) { + return true; + } + return false; + } + + public static bool operator!=(Marker lhs, Marker rhs) { + if ((lhs.line.line_no != rhs.line.line_no) || (lhs.pos != rhs.pos)) { + return true; + } + return false; + } + + public void Combine(Line move_to_line, int move_to_line_length) { + line = move_to_line; + pos += move_to_line_length; + tag = LineTag.FindTag(line, pos); + } + + // This is for future use, right now Document.Split does it by hand, with some added shortcut logic + public void Split(Line move_to_line, int split_at) { + line = move_to_line; + pos -= split_at; + tag = LineTag.FindTag(line, pos); + } + + public override bool Equals(object obj) { + return this==(Marker)obj; + } + + public override int GetHashCode() { + return base.GetHashCode (); + } + + public override string ToString() { + return "Marker Line " + line + ", Position " + pos; + } + + } + #endregion Structures + + #region Local Variables + private Line document; + private int lines; + private Line sentinel; + private int document_id; + private Random random = new Random(); + internal string password_char; + private StringBuilder password_cache; + private bool calc_pass; + private int char_count; + private bool enable_links; + + // For calculating widths/heights + public static readonly StringFormat string_format = new StringFormat (StringFormat.GenericTypographic); + + private int recalc_suspended; + private bool recalc_pending; + private int recalc_start = 1; // This starts at one, since lines are 1 based + private int recalc_end; + private bool recalc_optimize; + + private int update_suspended; + private bool update_pending; + private int update_start = 1; + + internal bool multiline; + internal HorizontalAlignment alignment; + internal bool wrap; + + internal UndoManager undo; + + internal Marker caret; + internal Marker selection_start; + internal Marker selection_end; + internal bool selection_visible; + internal Marker selection_anchor; + internal Marker selection_prev; + internal bool selection_end_anchor; + + internal int viewport_x; + internal int viewport_y; // The visible area of the document + internal int offset_x; + internal int offset_y; + internal int viewport_width; + internal int viewport_height; + + internal int document_x; // Width of the document + internal int document_y; // Height of the document + + internal int crlf_size; // 1 or 2, depending on whether we use \r\n or just \n + + internal TextBoxBase owner; // Who's owning us? + static internal int caret_width = 1; + static internal int caret_shift = 1; + + internal int left_margin = 2; // A left margin for all lines + internal int top_margin = 2; + internal int right_margin = 2; + #endregion // Local Variables + + #region Constructors + internal Document (TextBoxBase owner) + { + lines = 0; + + this.owner = owner; + + multiline = true; + password_char = ""; + calc_pass = false; + recalc_pending = false; + + // Tree related stuff + sentinel = new Line (this, LineEnding.None); + sentinel.color = LineColor.Black; + + document = sentinel; + + // We always have a blank line + owner.HandleCreated += new EventHandler(owner_HandleCreated); + owner.VisibleChanged += new EventHandler(owner_VisibleChanged); + + Add (1, String.Empty, owner.Font, owner.ForeColor, LineEnding.None); + + undo = new UndoManager (this); + + selection_visible = false; + selection_start.line = this.document; + selection_start.pos = 0; + selection_start.tag = selection_start.line.tags; + selection_end.line = this.document; + selection_end.pos = 0; + selection_end.tag = selection_end.line.tags; + selection_anchor.line = this.document; + selection_anchor.pos = 0; + selection_anchor.tag = selection_anchor.line.tags; + caret.line = this.document; + caret.pos = 0; + caret.tag = caret.line.tags; + + viewport_x = 0; + viewport_y = 0; + + offset_x = 0; + offset_y = 0; + + crlf_size = 2; + + // Default selection is empty + + document_id = random.Next(); + + string_format.Trimming = StringTrimming.None; + string_format.FormatFlags = StringFormatFlags.DisplayFormatControl; + + UpdateMargins (); + } + #endregion + + #region Internal Properties + internal Line Root { + get { + return document; + } + + set { + document = value; + } + } + + // UIA: Method used via reflection in TextRangeProvider + internal int Lines { + get { + return lines; + } + } + + internal Line CaretLine { + get { + return caret.line; + } + } + + internal int CaretPosition { + get { + return caret.pos; + } + } + + internal Point Caret { + get { + return new Point((int)caret.tag.Line.widths[caret.pos] + caret.line.X, caret.line.Y); + } + } + + internal LineTag CaretTag { + get { + return caret.tag; + } + + set { + caret.tag = value; + } + } + + internal int CRLFSize { + get { + return crlf_size; + } + + set { + crlf_size = value; + } + } + + /// <summary> + /// Whether text is scanned for links + /// </summary> + internal bool EnableLinks { + get { return enable_links; } + set { enable_links = value; } + } + + internal string PasswordChar { + get { + return password_char; + } + + set { + password_char = value; + PasswordCache.Length = 0; + if ((password_char.Length != 0) && (password_char[0] != '\0')) { + calc_pass = true; + } else { + calc_pass = false; + } + } + } + + private StringBuilder PasswordCache { + get { + if (password_cache == null) + password_cache = new StringBuilder(); + return password_cache; + } + } + + internal int ViewPortX { + get { + return viewport_x; + } + + set { + viewport_x = value; + } + } + + internal int Length { + get { + return char_count + lines - 1; // Add \n for each line but the last + } + } + + private int CharCount { + get { + return char_count; + } + + set { + char_count = value; + + if (LengthChanged != null) { + LengthChanged(this, EventArgs.Empty); + } + } + } + + internal int ViewPortY { + get { + return viewport_y; + } + + set { + viewport_y = value; + } + } + + internal int OffsetX + { + get + { + return offset_x; + } + + set + { + offset_x = value; + } + } + + internal int OffsetY + { + get + { + return offset_y; + } + + set + { + offset_y = value; + } + } + + internal int ViewPortWidth { + get { + return viewport_width; + } + + set { + viewport_width = value; + } + } + + internal int ViewPortHeight { + get { + return viewport_height; + } + + set { + viewport_height = value; + } + } + + + internal int Width { + get { + return this.document_x; + } + } + + internal int Height { + get { + return this.document_y; + } + } + + internal bool SelectionVisible { + get { + return selection_visible; + } + } + + internal bool Wrap { + get { + return wrap; + } + + set { + wrap = value; + } + } + + #endregion // Internal Properties + + #region Private Methods + + internal void UpdateMargins () + { + switch (owner.actual_border_style) { + case BorderStyle.None: + left_margin = 0; + top_margin = 0; + right_margin = 1; + break; + case BorderStyle.FixedSingle: + left_margin = 2; + top_margin = 2; + right_margin = 3; + break; + case BorderStyle.Fixed3D: + left_margin = 1; + top_margin = 1; + right_margin = 2; + break; + } + } + + internal void SuspendRecalc () + { + if (recalc_suspended == 0) { + recalc_start = int.MaxValue; + recalc_end = int.MinValue; + } + + recalc_suspended++; + } + + internal void ResumeRecalc (bool immediate_update) + { + if (recalc_suspended > 0) + recalc_suspended--; + + if (recalc_suspended == 0 && (immediate_update || recalc_pending) && !(recalc_start == int.MaxValue && recalc_end == int.MinValue)) { + RecalculateDocument (owner.CreateGraphicsInternal (), recalc_start, recalc_end, recalc_optimize); + recalc_pending = false; + } + } + + internal void SuspendUpdate () + { + update_suspended++; + } + + internal void ResumeUpdate (bool immediate_update) + { + if (update_suspended > 0) + update_suspended--; + + if (immediate_update && update_suspended == 0 && update_pending) { + UpdateView (GetLine (update_start), 0); + update_pending = false; + } + } + + // For debugging + internal int DumpTree(Line line, bool with_tags) { + int total; + + total = 1; + + Console.Write("Line {0} [# {1}], Y: {2}, ending style: {3}, Text: '{4}'", + line.line_no, line.GetHashCode(), line.Y, line.ending, + line.text != null ? line.text.ToString() : "undefined"); + + if (line.left == sentinel) { + Console.Write(", left = sentinel"); + } else if (line.left == null) { + Console.Write(", left = NULL"); + } + + if (line.right == sentinel) { + Console.Write(", right = sentinel"); + } else if (line.right == null) { + Console.Write(", right = NULL"); + } + + Console.WriteLine(""); + + if (with_tags) { + LineTag tag; + int count; + int length; + + tag = line.tags; + count = 1; + length = 0; + Console.Write(" Tags: "); + while (tag != null) { + Console.Write("{0} <{1}>-<{2}>", count++, tag.Start, tag.End + /*line.text.ToString (tag.start - 1, tag.length)*/); + length += tag.Length; + + if (tag.Line != line) { + Console.Write("BAD line link"); + throw new Exception("Bad line link in tree"); + } + tag = tag.Next; + if (tag != null) { + Console.Write(", "); + } + } + if (length > line.text.Length) { + throw new Exception(String.Format("Length of tags more than length of text on line (expected {0} calculated {1})", line.text.Length, length)); + } else if (length < line.text.Length) { + throw new Exception(String.Format("Length of tags less than length of text on line (expected {0} calculated {1})", line.text.Length, length)); + } + Console.WriteLine(""); + } + if (line.left != null) { + if (line.left != sentinel) { + total += DumpTree(line.left, with_tags); + } + } else { + if (line != sentinel) { + throw new Exception("Left should not be NULL"); + } + } + + if (line.right != null) { + if (line.right != sentinel) { + total += DumpTree(line.right, with_tags); + } + } else { + if (line != sentinel) { + throw new Exception("Right should not be NULL"); + } + } + + for (int i = 1; i <= this.lines; i++) { + if (GetLine(i) == null) { + throw new Exception(String.Format("Hole in line order, missing {0}", i)); + } + } + + if (line == this.Root) { + if (total < this.lines) { + throw new Exception(String.Format("Not enough nodes in tree, found {0}, expected {1}", total, this.lines)); + } else if (total > this.lines) { + throw new Exception(String.Format("Too many nodes in tree, found {0}, expected {1}", total, this.lines)); + } + } + + return total; + } + + private void SetSelectionVisible (bool value) + { + bool old_selection_visible = selection_visible; + selection_visible = value; + + // cursor and selection are enemies, we can't have both in the same room at the same time + if (owner.IsHandleCreated && !owner.show_caret_w_selection) + XplatUI.CaretVisible (owner.Handle, !selection_visible); + if (UIASelectionChanged != null && (selection_visible || old_selection_visible)) + UIASelectionChanged (this, EventArgs.Empty); + } + + private void DecrementLines(int line_no) { + int current; + + current = line_no; + while (current <= lines) { + GetLine(current).line_no--; + current++; + } + return; + } + + private void IncrementLines(int line_no) { + int current; + + current = this.lines; + while (current >= line_no) { + GetLine(current).line_no++; + current--; + } + return; + } + + private void RebalanceAfterAdd(Line line1) { + Line line2; + + while ((line1 != document) && (line1.parent.color == LineColor.Red)) { + if (line1.parent == line1.parent.parent.left) { + line2 = line1.parent.parent.right; + + if ((line2 != null) && (line2.color == LineColor.Red)) { + line1.parent.color = LineColor.Black; + line2.color = LineColor.Black; + line1.parent.parent.color = LineColor.Red; + line1 = line1.parent.parent; + } else { + if (line1 == line1.parent.right) { + line1 = line1.parent; + RotateLeft(line1); + } + + line1.parent.color = LineColor.Black; + line1.parent.parent.color = LineColor.Red; + + RotateRight(line1.parent.parent); + } + } else { + line2 = line1.parent.parent.left; + + if ((line2 != null) && (line2.color == LineColor.Red)) { + line1.parent.color = LineColor.Black; + line2.color = LineColor.Black; + line1.parent.parent.color = LineColor.Red; + line1 = line1.parent.parent; + } else { + if (line1 == line1.parent.left) { + line1 = line1.parent; + RotateRight(line1); + } + + line1.parent.color = LineColor.Black; + line1.parent.parent.color = LineColor.Red; + RotateLeft(line1.parent.parent); + } + } + } + document.color = LineColor.Black; + } + + private void RebalanceAfterDelete(Line line1) { + Line line2; + + while ((line1 != document) && (line1.color == LineColor.Black)) { + if (line1 == line1.parent.left) { + line2 = line1.parent.right; + if (line2.color == LineColor.Red) { + line2.color = LineColor.Black; + line1.parent.color = LineColor.Red; + RotateLeft(line1.parent); + line2 = line1.parent.right; + } + if ((line2.left.color == LineColor.Black) && (line2.right.color == LineColor.Black)) { + line2.color = LineColor.Red; + line1 = line1.parent; + } else { + if (line2.right.color == LineColor.Black) { + line2.left.color = LineColor.Black; + line2.color = LineColor.Red; + RotateRight(line2); + line2 = line1.parent.right; + } + line2.color = line1.parent.color; + line1.parent.color = LineColor.Black; + line2.right.color = LineColor.Black; + RotateLeft(line1.parent); + line1 = document; + } + } else { + line2 = line1.parent.left; + if (line2.color == LineColor.Red) { + line2.color = LineColor.Black; + line1.parent.color = LineColor.Red; + RotateRight(line1.parent); + line2 = line1.parent.left; + } + if ((line2.right.color == LineColor.Black) && (line2.left.color == LineColor.Black)) { + line2.color = LineColor.Red; + line1 = line1.parent; + } else { + if (line2.left.color == LineColor.Black) { + line2.right.color = LineColor.Black; + line2.color = LineColor.Red; + RotateLeft(line2); + line2 = line1.parent.left; + } + line2.color = line1.parent.color; + line1.parent.color = LineColor.Black; + line2.left.color = LineColor.Black; + RotateRight(line1.parent); + line1 = document; + } + } + } + line1.color = LineColor.Black; + } + + private void RotateLeft(Line line1) { + Line line2 = line1.right; + + line1.right = line2.left; + + if (line2.left != sentinel) { + line2.left.parent = line1; + } + + if (line2 != sentinel) { + line2.parent = line1.parent; + } + + if (line1.parent != null) { + if (line1 == line1.parent.left) { + line1.parent.left = line2; + } else { + line1.parent.right = line2; + } + } else { + document = line2; + } + + line2.left = line1; + if (line1 != sentinel) { + line1.parent = line2; + } + } + + private void RotateRight(Line line1) { + Line line2 = line1.left; + + line1.left = line2.right; + + if (line2.right != sentinel) { + line2.right.parent = line1; + } + + if (line2 != sentinel) { + line2.parent = line1.parent; + } + + if (line1.parent != null) { + if (line1 == line1.parent.right) { + line1.parent.right = line2; + } else { + line1.parent.left = line2; + } + } else { + document = line2; + } + + line2.right = line1; + if (line1 != sentinel) { + line1.parent = line2; + } + } + + + internal void UpdateView(Line line, int pos) { + if (!owner.IsHandleCreated) { + return; + } + + if (update_suspended > 0) { + update_start = Math.Min (update_start, line.line_no); + // update_end = Math.Max (update_end, line.line_no); + // recalc_optimize = true; + update_pending = true; + return; + } + + // Optimize invalidation based on Line alignment + if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no, true)) { + // Lineheight changed, invalidate the rest of the document + if ((line.Y - viewport_y) >=0 ) { + // We formatted something that's in view, only draw parts of the screen + owner.Invalidate(new Rectangle( + offset_x, + line.Y - viewport_y + offset_y, + viewport_width, + owner.Height - (line.Y - viewport_y))); + } else { + // The tag was above the visible area, draw everything + owner.Invalidate(); + } + } else { + switch(line.alignment) { + case HorizontalAlignment.Left: { + owner.Invalidate(new Rectangle( + line.X + ((int)line.widths[pos] - viewport_x - 1) + offset_x, + line.Y - viewport_y + offset_y, + viewport_width, + line.height + 1)); + break; + } + + case HorizontalAlignment.Center: { + owner.Invalidate(new Rectangle( + line.X + offset_x, + line.Y - viewport_y + offset_y, + viewport_width, + line.height + 1)); + break; + } + + case HorizontalAlignment.Right: { + owner.Invalidate(new Rectangle( + line.X + offset_x, + line.Y - viewport_y + offset_y, + (int)line.widths[pos + 1] - viewport_x + line.X, + line.height + 1)); + break; + } + } + } + } + + + // Update display from line, down line_count lines; pos is unused, but required for the signature + internal void UpdateView(Line line, int line_count, int pos) { + if (!owner.IsHandleCreated) { + return; + } + + if (recalc_suspended > 0) { + recalc_start = Math.Min (recalc_start, line.line_no); + recalc_end = Math.Max (recalc_end, line.line_no + line_count); + recalc_optimize = true; + recalc_pending = true; + return; + } + + int start_line_top = line.Y; + + Line end_line = GetLine (line.line_no + line_count); + if (end_line == null) + end_line = GetLine (lines); + + if (end_line == null) + return; + + int end_line_bottom = end_line.Y + end_line.height; + + if (RecalculateDocument(owner.CreateGraphicsInternal(), line.line_no, line.line_no + line_count, true)) { + // Lineheight changed, invalidate the rest of the document + if ((line.Y - viewport_y) >=0 ) { + // We formatted something that's in view, only draw parts of the screen + owner.Invalidate(new Rectangle( + offset_x, + line.Y - viewport_y + offset_y, + viewport_width, + owner.Height - (line.Y - viewport_y))); + } else { + // The tag was above the visible area, draw everything + owner.Invalidate(); + } + } else { + int x = 0 - viewport_x + offset_x; + int w = viewport_width; + int y = Math.Min (start_line_top - viewport_y, line.Y - viewport_y) + offset_y; + int h = Math.Max (end_line_bottom - y, end_line.Y + end_line.height - y); + + owner.Invalidate (new Rectangle (x, y, w, h)); + } + } + + /// <summary> + /// Scans the next paragraph for http:/ ftp:/ www. https:/ etc and marks the tags + /// as links. + /// </summary> + /// <param name="start_line">The line to start on</param> + /// <param name="link_changed">marks as true if something is changed</param> + private void ScanForLinks (Line start_line, ref bool link_changed) + { + Line current_line = start_line; + StringBuilder line_no_breaks = new StringBuilder (); + StringBuilder line_link_record = new StringBuilder (); + ArrayList cumulative_length_list = new ArrayList (); + bool update_caret_tag = false; + + cumulative_length_list.Add (0); + + while (current_line != null) { + line_no_breaks.Append (current_line.text); + + if (link_changed == false) + current_line.LinkRecord (line_link_record); + + current_line.ClearLinks (); + + cumulative_length_list.Add (line_no_breaks.Length); + + if (current_line.ending == LineEnding.Wrap) + current_line = GetLine (current_line.LineNo + 1); + else + break; + } + + // search for protocols.. make sure www. is first! + string [] search_terms = new string [] { "www.", "http:/", "ftp:/", "https:/" }; + int search_found = 0; + int index_found = 0; + string line_no_breaks_string = line_no_breaks.ToString (); + int line_no_breaks_index = 0; + int link_end = 0; + + while (true) { + if (line_no_breaks_index >= line_no_breaks_string.Length) + break; + + index_found = FirstIndexOfAny (line_no_breaks_string, search_terms, line_no_breaks_index, out search_found); + + //no links found on this line + if (index_found == -1) + break; + + if (search_found == 0) { + // if we are at the end of the line to analyse and the end of the line + // is "www." then there are no links here + if (line_no_breaks_string.Length == index_found + search_terms [0].Length) + break; + + // if after www. we don't have a letter a digit or a @ or - or / + // then it is not a web address, we should continue searching + if (char.IsLetterOrDigit (line_no_breaks_string [index_found + search_terms [0].Length]) == false && + "@/~".IndexOf (line_no_breaks_string [index_found + search_terms [0].Length].ToString ()) == -1) { + line_no_breaks_index = index_found + search_terms [0].Length; + continue; + } + } + + link_end = line_no_breaks_string.Length - 1; + line_no_breaks_index = line_no_breaks_string.Length; + + // we've found a link, we just need to find where it ends now + for (int i = index_found + search_terms [search_found].Length; i < line_no_breaks_string.Length; i++) { + if (line_no_breaks_string [i - 1] == '.') { + if (char.IsLetterOrDigit (line_no_breaks_string [i]) == false && + "@/~".IndexOf (line_no_breaks_string [i].ToString ()) == -1) { + link_end = i - 1; + line_no_breaks_index = i; + break; + } + } else { + if (char.IsLetterOrDigit (line_no_breaks_string [i]) == false && + "@-/:~.?=_&".IndexOf (line_no_breaks_string [i].ToString ()) == -1) { + link_end = i - 1; + line_no_breaks_index = i; + break; + } + } + } + + string link_text = line_no_breaks_string.Substring (index_found, link_end - index_found + 1); + int current_cumulative = 0; + + // we've found a link - index_found -> link_end + // now we just make all the tags as containing link and + // point them to the text for the whole link + + current_line = start_line; + + //find the line we start on + for (current_cumulative = 1; current_cumulative < cumulative_length_list.Count; current_cumulative++) + if ((int)cumulative_length_list [current_cumulative] > index_found) + break; + + current_line = GetLine (start_line.LineNo + current_cumulative - 1); + + // find the tag we start on + LineTag current_tag = current_line.FindTag (index_found - (int)cumulative_length_list [current_cumulative - 1] + 1); + + if (current_tag.Start != (index_found - (int)cumulative_length_list [current_cumulative - 1]) + 1) { + if (current_tag == CaretTag) + update_caret_tag = true; + + current_tag = current_tag.Break ((index_found - (int)cumulative_length_list [current_cumulative - 1]) + 1); + } + + // set the tag + current_tag.IsLink = true; + current_tag.LinkText = link_text; + + //go through each character + // find the tag we are in + // skip the number of characters in the tag + for (int i = 1; i < link_text.Length; i++) { + // on to a new word-wrapped line + if ((int)cumulative_length_list [current_cumulative] <= index_found + i) { + + current_line = GetLine (start_line.LineNo + current_cumulative++); + current_tag = current_line.FindTag (index_found + i - (int)cumulative_length_list [current_cumulative - 1] + 1); + + current_tag.IsLink = true; + current_tag.LinkText = link_text; + + continue; + } + + if (current_tag.End < index_found + 1 + i - (int)cumulative_length_list [current_cumulative - 1]) { + // skip empty tags in the middle of the URL + do { + current_tag = current_tag.Next; + } while (current_tag.Length == 0); + + current_tag.IsLink = true; + current_tag.LinkText = link_text; + } + } + + //if there are characters left in the tag after the link + // split the tag + // make the second part a non link + if (current_tag.End > (index_found + link_text.Length + 1) - (int)cumulative_length_list [current_cumulative - 1]) { + if (current_tag == CaretTag) + update_caret_tag = true; + + current_tag.Break ((index_found + link_text.Length + 1) - (int)cumulative_length_list [current_cumulative - 1]); + } + } + + if (update_caret_tag) { + CaretTag = LineTag.FindTag (CaretLine, CaretPosition); + link_changed = true; + } else { + if (link_changed == false) { + current_line = start_line; + StringBuilder new_link_record = new StringBuilder (); + + while (current_line != null) { + current_line.LinkRecord (new_link_record); + + if (current_line.ending == LineEnding.Wrap) + current_line = GetLine (current_line.LineNo + 1); + else + break; + } + + if (new_link_record.Equals (line_link_record) == false) + link_changed = true; + } + } + } + + private int FirstIndexOfAny (string haystack, string [] needles, int start_index, out int term_found) + { + term_found = -1; + int best_index = -1; + + for (int i = 0; i < needles.Length; i++) { + int index = haystack.IndexOf (needles [i], start_index, StringComparison.InvariantCultureIgnoreCase); + + if (index > -1) { + if (term_found > -1) { + if (index < best_index) { + best_index = index; + term_found = i; + } + } else { + best_index = index; + term_found = i; + } + } + } + + return best_index; + } + + + + private void InvalidateLinks (Rectangle clip) + { + for (int i = (owner.list_links.Count - 1); i >= 0; i--) { + TextBoxBase.LinkRectangle link = (TextBoxBase.LinkRectangle) owner.list_links [i]; + + if (clip.IntersectsWith (link.LinkAreaRectangle)) + owner.list_links.RemoveAt (i); + } + } + #endregion // Private Methods + + #region Internal Methods + + internal void ScanForLinks (int start, int end, ref bool link_changed) + { + Line line = null; + LineEnding lastending = LineEnding.Rich; + + // make sure we start scanning at the real begining of the line + while (true) { + if (start != 1 && GetLine (start - 1).ending == LineEnding.Wrap) + start--; + else + break; + } + + for (int i = start; i <= end && i <= lines; i++) { + line = GetLine (i); + + if (lastending != LineEnding.Wrap) + ScanForLinks (line, ref link_changed); + + lastending = line.ending; + + if (lastending == LineEnding.Wrap && (i + 1) <= end) + end++; + } + } + + // Clear the document and reset state + internal void Empty() { + + document = sentinel; + lines = 0; + + // We always have a blank line + Add (1, String.Empty, owner.Font, owner.ForeColor, LineEnding.None); + + this.RecalculateDocument(owner.CreateGraphicsInternal()); + PositionCaret(0, 0); + + SetSelectionVisible (false); + + selection_start.line = this.document; + selection_start.pos = 0; + selection_start.tag = selection_start.line.tags; + selection_end.line = this.document; + selection_end.pos = 0; + selection_end.tag = selection_end.line.tags; + char_count = 0; + + viewport_x = 0; + viewport_y = 0; + + document_x = 0; + document_y = 0; + + if (owner.IsHandleCreated) + owner.Invalidate (); + } + + internal void PositionCaret(Line line, int pos) { + caret.tag = line.FindTag (pos); + + MoveCaretToTextTag (); + + caret.line = line; + caret.pos = pos; + + if (owner.IsHandleCreated) { + if (owner.Focused) { + if (caret.height != caret.tag.Height) + XplatUI.CreateCaret (owner.Handle, caret_width, caret.height); + XplatUI.SetCaretPos(owner.Handle, + offset_x + (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x, + offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift); + } + + if (CaretMoved != null) CaretMoved(this, EventArgs.Empty); + } + + // We set this at the end because we use the heights to determine whether or + // not we need to recreate the caret + caret.height = caret.tag.Height; + + } + + internal void PositionCaret(int x, int y) { + if (!owner.IsHandleCreated) { + return; + } + + caret.tag = FindCursor(x, y, out caret.pos); + + MoveCaretToTextTag (); + + caret.line = caret.tag.Line; + caret.height = caret.tag.Height; + + if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) { + XplatUI.CreateCaret (owner.Handle, caret_width, caret.height); + XplatUI.SetCaretPos(owner.Handle, + (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x + offset_x, + offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift); + } + + if (CaretMoved != null) CaretMoved(this, EventArgs.Empty); + } + + internal void CaretHasFocus() { + if ((caret.tag != null) && owner.IsHandleCreated) { + XplatUI.CreateCaret(owner.Handle, caret_width, caret.height); + XplatUI.SetCaretPos(owner.Handle, + offset_x + (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x, + offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift); + + DisplayCaret (); + } + + if (owner.IsHandleCreated && SelectionLength () > 0) { + InvalidateSelectionArea (); + } + } + + internal void CaretLostFocus() { + if (!owner.IsHandleCreated) { + return; + } + XplatUI.DestroyCaret(owner.Handle); + } + + internal void AlignCaret () + { + AlignCaret (true); + } + + internal void AlignCaret(bool changeCaretTag) { + if (!owner.IsHandleCreated) { + return; + } + + if (changeCaretTag) { + caret.tag = LineTag.FindTag (caret.line, caret.pos); + + MoveCaretToTextTag (); + } + + // if the caret has had SelectionFont changed to a + // different height, we reflect changes unless the new + // font is larger than the line (line recalculations + // ignore empty tags) in which case we make it equal + // the line height and then when text is entered + if (caret.tag.Height > caret.tag.Line.Height) { + caret.height = caret.line.height; + } else { + caret.height = caret.tag.Height; + } + + if (owner.Focused) { + XplatUI.CreateCaret(owner.Handle, caret_width, caret.height); + XplatUI.SetCaretPos (owner.Handle, + offset_x + (int) caret.tag.Line.widths [caret.pos] + caret.line.X - viewport_x, + offset_y + caret.line.Y + viewport_y + caret_shift); + DisplayCaret (); + } + + if (CaretMoved != null) CaretMoved(this, EventArgs.Empty); + } + + internal void UpdateCaret() { + if (!owner.IsHandleCreated || caret.tag == null) { + return; + } + + MoveCaretToTextTag (); + + if (caret.tag.Height != caret.height) { + caret.height = caret.tag.Height; + if (owner.Focused) { + XplatUI.CreateCaret(owner.Handle, caret_width, caret.height); + } + } + + if (owner.Focused) { + XplatUI.SetCaretPos(owner.Handle, + offset_x + (int)caret.tag.Line.widths[caret.pos] + caret.line.X - viewport_x, + offset_y + caret.line.Y + caret.tag.Shift - viewport_y + caret_shift); + DisplayCaret (); + } + + if (CaretMoved != null) CaretMoved(this, EventArgs.Empty); + } + + internal void DisplayCaret() { + if (!owner.IsHandleCreated) { + return; + } + + if (owner.ShowSelection && (!selection_visible || owner.show_caret_w_selection)) { + XplatUI.CaretVisible(owner.Handle, true); + } + } + + internal void HideCaret() { + if (!owner.IsHandleCreated) { + return; + } + + if (owner.Focused) { + XplatUI.CaretVisible(owner.Handle, false); + } + } + + + internal void MoveCaretToTextTag () + { + if (caret.tag == null || caret.tag.IsTextTag) + return; + + + + if (caret.pos < caret.tag.Start) { + caret.tag = caret.tag.Previous; + } else { + caret.tag = caret.tag.Next; + } + } + + internal void MoveCaret(CaretDirection direction) { + // FIXME should we use IsWordSeparator to detect whitespace, instead + // of looking for actual spaces in the Word move cases? + + bool nowrap = false; + switch(direction) { + case CaretDirection.CharForwardNoWrap: + nowrap = true; + goto case CaretDirection.CharForward; + case CaretDirection.CharForward: { + caret.pos++; + if (caret.pos > caret.line.TextLengthWithoutEnding ()) { + if (!nowrap) { + // Go into next line + if (caret.line.line_no < this.lines) { + caret.line = GetLine(caret.line.line_no+1); + caret.pos = 0; + caret.tag = caret.line.tags; + } else { + caret.pos--; + } + } else { + // Single line; we stay where we are + caret.pos--; + } + } else { + if ((caret.tag.Start - 1 + caret.tag.Length) < caret.pos) { + caret.tag = caret.tag.Next; + } + } + UpdateCaret(); + return; + } + + case CaretDirection.CharBackNoWrap: + nowrap = true; + goto case CaretDirection.CharBack; + case CaretDirection.CharBack: { + if (caret.pos > 0) { + // caret.pos--; // folded into the if below + + if (--caret.pos > 0) { + if (caret.tag.Start > caret.pos) { + caret.tag = caret.tag.Previous; + } + } + } else { + if (caret.line.line_no > 1 && !nowrap) { + caret.line = GetLine(caret.line.line_no - 1); + caret.pos = caret.line.TextLengthWithoutEnding (); + caret.tag = LineTag.FindTag(caret.line, caret.pos); + } + } + UpdateCaret(); + return; + } + + case CaretDirection.WordForward: { + int len; + + len = caret.line.text.Length; + if (caret.pos < len) { + while ((caret.pos < len) && (caret.line.text[caret.pos] != ' ')) { + caret.pos++; + } + if (caret.pos < len) { + // Skip any whitespace + while ((caret.pos < len) && (caret.line.text[caret.pos] == ' ')) { + caret.pos++; + } + } + caret.tag = LineTag.FindTag(caret.line, caret.pos); + } else { + if (caret.line.line_no < this.lines) { + caret.line = GetLine(caret.line.line_no + 1); + caret.pos = 0; + caret.tag = caret.line.tags; + } + } + UpdateCaret(); + return; + } + + case CaretDirection.WordBack: { + if (caret.pos > 0) { + caret.pos--; + + while ((caret.pos > 0) && (caret.line.text[caret.pos] == ' ')) { + caret.pos--; + } + + while ((caret.pos > 0) && (caret.line.text[caret.pos] != ' ')) { + caret.pos--; + } + + if (caret.line.text.ToString(caret.pos, 1) == " ") { + if (caret.pos != 0) { + caret.pos++; + } else { + caret.line = GetLine(caret.line.line_no - 1); + caret.pos = caret.line.text.Length; + } + } + caret.tag = LineTag.FindTag(caret.line, caret.pos); + } else { + if (caret.line.line_no > 1) { + caret.line = GetLine(caret.line.line_no - 1); + caret.pos = caret.line.text.Length; + caret.tag = LineTag.FindTag(caret.line, caret.pos); + } + } + UpdateCaret(); + return; + } + + case CaretDirection.LineUp: { + if (caret.line.line_no > 1) { + int pixel; + + pixel = (int)caret.line.widths[caret.pos]; + PositionCaret(pixel, GetLine(caret.line.line_no - 1).Y); + + DisplayCaret (); + } + return; + } + + case CaretDirection.LineDown: { + if (caret.line.line_no < lines) { + int pixel; + + pixel = (int)caret.line.widths[caret.pos]; + PositionCaret(pixel, GetLine(caret.line.line_no + 1).Y); + + DisplayCaret (); + } + return; + } + + case CaretDirection.Home: { + if (caret.pos > 0) { + caret.pos = 0; + caret.tag = caret.line.tags; + UpdateCaret(); + } + return; + } + + case CaretDirection.End: { + if (caret.pos < caret.line.TextLengthWithoutEnding ()) { + caret.pos = caret.line.TextLengthWithoutEnding (); + caret.tag = LineTag.FindTag(caret.line, caret.pos); + UpdateCaret(); + } + return; + } + + case CaretDirection.PgUp: { + + if (caret.line.line_no == 1 && owner.richtext) { + owner.vscroll.Value = 0; + Line line = GetLine (1); + PositionCaret (line, 0); + } + + int y_offset = caret.line.Y + caret.line.height - 1 - viewport_y; + int index; + LineTag top = FindCursor ((int) caret.line.widths [caret.pos], + viewport_y - viewport_height, out index); + + owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height); + PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y); + + return; + } + + case CaretDirection.PgDn: { + + if (caret.line.line_no == lines && owner.richtext) { + owner.vscroll.Value = owner.vscroll.Maximum - viewport_height + 1; + Line line = GetLine (lines); + PositionCaret (line, line.TextLengthWithoutEnding()); + } + + int y_offset = caret.line.Y - viewport_y; + int index; + LineTag top = FindCursor ((int) caret.line.widths [caret.pos], + viewport_y + viewport_height, out index); + + owner.vscroll.Value = Math.Min (top.Line.Y, owner.vscroll.Maximum - viewport_height); + PositionCaret ((int) caret.line.widths [caret.pos], y_offset + viewport_y); + + return; + } + + case CaretDirection.CtrlPgUp: { + PositionCaret(0, viewport_y); + DisplayCaret (); + return; + } + + case CaretDirection.CtrlPgDn: { + Line line; + LineTag tag; + int index; + + tag = FindCursor (0, viewport_y + viewport_height, out index); + if (tag.Line.line_no > 1) { + line = GetLine(tag.Line.line_no - 1); + } else { + line = tag.Line; + } + PositionCaret(line, line.Text.Length); + DisplayCaret (); + return; + } + + case CaretDirection.CtrlHome: { + caret.line = GetLine(1); + caret.pos = 0; + caret.tag = caret.line.tags; + + UpdateCaret(); + return; + } + + case CaretDirection.CtrlEnd: { + caret.line = GetLine(lines); + caret.pos = caret.line.TextLengthWithoutEnding (); + caret.tag = LineTag.FindTag(caret.line, caret.pos); + + UpdateCaret(); + return; + } + + case CaretDirection.SelectionStart: { + caret.line = selection_start.line; + caret.pos = selection_start.pos; + caret.tag = selection_start.tag; + + UpdateCaret(); + return; + } + + case CaretDirection.SelectionEnd: { + caret.line = selection_end.line; + caret.pos = selection_end.pos; + caret.tag = selection_end.tag; + + UpdateCaret(); + return; + } + } + } + + internal void DumpDoc () + { + Console.WriteLine ("<doc lines='{0}'>", lines); + for (int i = 1; i <= lines ; i++) { + Line line = GetLine (i); + Console.WriteLine ("<line no='{0}' ending='{1}'>", line.line_no, line.ending); + + LineTag tag = line.tags; + while (tag != null) { + Console.Write ("\t<tag type='{0}' span='{1}->{2}' font='{3}' color='{4}'>", + tag.GetType (), tag.Start, tag.Length, tag.Font, tag.Color); + Console.Write (tag.Text ()); + Console.WriteLine ("</tag>"); + tag = tag.Next; + } + Console.WriteLine ("</line>"); + } + Console.WriteLine ("</doc>"); + } + + // UIA: Used via reflection by TextProviderBehavior + internal void GetVisibleLineIndexes (Rectangle clip, out int start, out int end) + { + if (multiline) { + /* Expand the region slightly to be sure to + * paint the full extent of the line of text. + * See bug 464464. + */ + start = GetLineByPixel(clip.Top + viewport_y - offset_y - 1, false).line_no; + end = GetLineByPixel(clip.Bottom + viewport_y - offset_y + 1, false).line_no; + } else { + start = GetLineByPixel(clip.Left + viewport_x - offset_x, false).line_no; + end = GetLineByPixel(clip.Right + viewport_x - offset_x, false).line_no; + } + } + + internal void Draw (Graphics g, Rectangle clip) + { + Line line; // Current line being drawn + LineTag tag; // Current tag being drawn + int start; // First line to draw + int end; // Last line to draw + StringBuilder text; // String representing the current line + int line_no; + Color tag_color; + Color current_color; + + // First, figure out from what line to what line we need to draw + GetVisibleLineIndexes (clip, out start, out end); + + // remove links in the list (used for mouse down events) that are within the clip area. + InvalidateLinks (clip); + + /// + /// We draw the single border ourself + /// + if (owner.actual_border_style == BorderStyle.FixedSingle) { + WidgetPaint.DrawBorder (g, owner.ClientRectangle, Color.Black, ButtonBorderStyle.Solid); + } + + /// Make sure that we aren't drawing one more line then we need to + line = GetLine (end - 1); + if (line != null && clip.Bottom == offset_y + line.Y + line.height - viewport_y) + end--; + + line_no = start; + + #if Debug + DateTime n = DateTime.Now; + Console.WriteLine ("Started drawing: {0}s {1}ms", n.Second, n.Millisecond); + Console.WriteLine ("CLIP: {0}", clip); + Console.WriteLine ("S: {0}", GetLine (start).text); + Console.WriteLine ("E: {0}", GetLine (end).text); + #endif + + // Non multiline selection can be handled outside of the loop + if (!multiline && selection_visible && owner.ShowSelection) { + g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHighlight), + offset_x + selection_start.line.widths [selection_start.pos] + + selection_start.line.X - viewport_x, + offset_y + selection_start.line.Y, + (selection_end.line.X + selection_end.line.widths [selection_end.pos]) - + (selection_start.line.X + selection_start.line.widths [selection_start.pos]), + selection_start.line.height); + } + + while (line_no <= end) { + line = GetLine (line_no); + float line_y = line.Y - viewport_y + offset_y; + + tag = line.tags; + if (!calc_pass) { + text = line.text; + } else { + if (PasswordCache.Length < line.text.Length) + PasswordCache.Append(Char.Parse(password_char), line.text.Length - PasswordCache.Length); + else if (PasswordCache.Length > line.text.Length) + PasswordCache.Remove(line.text.Length, PasswordCache.Length - line.text.Length); + text = PasswordCache; + } + + int line_selection_start = text.Length + 1; + int line_selection_end = text.Length + 1; + if (selection_visible && owner.ShowSelection && + (line_no >= selection_start.line.line_no) && + (line_no <= selection_end.line.line_no)) { + + if (line_no == selection_start.line.line_no) + line_selection_start = selection_start.pos + 1; + else + line_selection_start = 1; + + if (line_no == selection_end.line.line_no) + line_selection_end = selection_end.pos + 1; + else + line_selection_end = text.Length + 1; + + if (line_selection_end == line_selection_start) { + // There isn't really selection + line_selection_start = text.Length + 1; + line_selection_end = line_selection_start; + } else if (multiline) { + // lets draw some selection baby!! (non multiline selection is drawn outside the loop) + g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (ThemeEngine.Current.ColorHighlight), + offset_x + line.widths [line_selection_start - 1] + line.X - viewport_x, + line_y, line.widths [line_selection_end - 1] - line.widths [line_selection_start - 1], + line.height); + } + } + + current_color = line.tags.ColorToDisplay; + while (tag != null) { + + // Skip empty tags + if (tag.Length == 0) { + tag = tag.Next; + continue; + } + + if (((tag.X + tag.Width) < (clip.Left - viewport_x - offset_x)) && + (tag.X > (clip.Right - viewport_x - offset_x))) { + tag = tag.Next; + continue; + } + + if (tag.BackColor != Color.Empty) { + g.FillRectangle (ThemeEngine.Current.ResPool.GetSolidBrush (tag.BackColor), + offset_x + tag.X + line.X - viewport_x, + line_y + tag.Shift, tag.Width, line.height); + } + + tag_color = tag.ColorToDisplay; + current_color = tag_color; + + if (!owner.Enabled) { + Color a = tag.Color; + Color b = ThemeEngine.Current.ColorWindowText; + + if ((a.R == b.R) && (a.G == b.G) && (a.B == b.B)) + tag_color = ThemeEngine.Current.ColorGrayText; + + } + + int tag_pos = tag.Start; + current_color = tag_color; + while (tag_pos < tag.Start + tag.Length) { + int old_tag_pos = tag_pos; + + if (tag_pos >= line_selection_start && tag_pos < line_selection_end) { + current_color = ThemeEngine.Current.ColorHighlightText; + tag_pos = Math.Min (tag.End, line_selection_end); + } else if (tag_pos < line_selection_start) { + current_color = tag_color; + tag_pos = Math.Min (tag.End, line_selection_start); + } else { + current_color = tag_color; + tag_pos = tag.End; + } + + Rectangle text_size; + + tag.Draw (g, current_color, + offset_x + line.X - viewport_x, + line_y + tag.Shift, + old_tag_pos - 1, Math.Min (tag.Start + tag.Length, tag_pos) - 1, + text.ToString (), out text_size, tag.IsLink); + + if (tag.IsLink) { + TextBoxBase.LinkRectangle link = new TextBoxBase.LinkRectangle (text_size); + link.LinkTag = tag; + owner.list_links.Add (link); + } + } + tag = tag.Next; + } + + line.DrawEnding (g, line_y); + line_no++; + } + } + + private int GetLineEnding (string line, int start, out LineEnding ending) + { + int res; + int rich_index; + + if (start >= line.Length) { + ending = LineEnding.Wrap; + return -1; + } + + res = line.IndexOf ('\r', start); + rich_index = line.IndexOf ('\n', start); + + // Handle the case where we find both of them, and the \n is before the \r + if (res != -1 && rich_index != -1) + if (rich_index < res) { + ending = LineEnding.Rich; + return rich_index; + } + + if (res != -1) { + if (res + 2 < line.Length && line [res + 1] == '\r' && line [res + 2] == '\n') { + ending = LineEnding.Soft; + return res; + } + if (res + 1 < line.Length && line [res + 1] == '\n') { + ending = LineEnding.Hard; + return res; + } + ending = LineEnding.Limp; + return res; + } + + if (rich_index != -1) { + ending = LineEnding.Rich; + return rich_index; + } + + ending = LineEnding.Wrap; + return line.Length; + } + + // Get the line ending, but only of the types specified + private int GetLineEnding (string line, int start, out LineEnding ending, LineEnding type) + { + int index = start; + int last_length = 0; + + do { + index = GetLineEnding (line, index + last_length, out ending); + last_length = LineEndingLength (ending); + } while + ((ending & type) != ending && index != -1); + + return index == -1 ? line.Length : index; + } + + internal int LineEndingLength (LineEnding ending) + { + switch (ending) { + case LineEnding.Limp: + case LineEnding.Rich: + return 1; + case LineEnding.Hard: + return 2; + case LineEnding.Soft: + return 3; + } + + return 0; + } + + internal string LineEndingToString (LineEnding ending) + { + switch (ending) { + case LineEnding.Limp: + return "\r"; + case LineEnding.Hard: + return "\r\n"; + case LineEnding.Soft: + return "\r\r\n"; + case LineEnding.Rich: + return "\n"; + } + + return string.Empty; + } + + internal LineEnding StringToLineEnding (string ending) + { + switch (ending) { + case "\r": + return LineEnding.Limp; + case "\r\n": + return LineEnding.Hard; + case "\r\r\n": + return LineEnding.Soft; + case "\n": + return LineEnding.Rich; + default: + return LineEnding.None; + } + } + + internal void Insert (Line line, int pos, bool update_caret, string s) + { + Insert (line, pos, update_caret, s, line.FindTag (pos)); + } + + // Insert text at the given position; use formatting at insertion point for inserted text + internal void Insert (Line line, int pos, bool update_caret, string s, LineTag tag) + { + int break_index; + int base_line; + int old_line_count; + int count = 1; + LineEnding ending; + Line split_line; + + // Don't recalculate while we mess around + SuspendRecalc (); + + base_line = line.line_no; + old_line_count = lines; + + // Discard chars after any possible -unlikely- end of file + int eof_index = s.IndexOf ('\0'); + if (eof_index != -1) + s = s.Substring (0, eof_index); + + break_index = GetLineEnding (s, 0, out ending, LineEnding.Hard | LineEnding.Rich); + + // There are no line feeds in our text to be pasted + if (break_index == s.Length) { + line.InsertString (pos, s, tag); + } else { + // Add up to the first line feed to our current position + line.InsertString (pos, s.Substring (0, break_index + LineEndingLength (ending)), tag); + + // Split the rest of the original line to a new line + Split (line, pos + (break_index + LineEndingLength (ending))); + line.ending = ending; + break_index += LineEndingLength (ending); + split_line = GetLine (line.line_no + 1); + + // Insert brand new lines for any more line feeds in the inserted string + while (true) { + int next_break = GetLineEnding (s, break_index, out ending, LineEnding.Hard | LineEnding.Rich); + + if (next_break == s.Length) + break; + + string line_text = s.Substring (break_index, next_break - break_index + + LineEndingLength (ending)); + + Add (base_line + count, line_text, line.alignment, tag.Font, tag.Color, ending); + + Line last = GetLine (base_line + count); + last.ending = ending; + + count++; + break_index = next_break + LineEndingLength (ending); + } + + // Add the remainder of the insert text to the split + // part of the original line + split_line.InsertString (0, s.Substring (break_index)); + } + + // Allow the document to recalculate things + ResumeRecalc (false); + + // Update our character count + CharCount += s.Length; + + UpdateView (line, lines - old_line_count + 1, pos); + + // Move the caret to the end of the inserted text if requested + if (update_caret) { + Line l = GetLine (line.line_no + lines - old_line_count); + PositionCaret (l, l.text.Length); + DisplayCaret (); + } + } + + // Inserts a string at the given position + internal void InsertString (Line line, int pos, string s) + { + // Update our character count + CharCount += s.Length; + + // Insert the text into the Line + line.InsertString (pos, s); + } + + // Inserts a character at the current caret position + internal void InsertCharAtCaret (char ch, bool move_caret) + { + caret.line.InsertString (caret.pos, ch.ToString(), caret.tag); + + // Update our character count + CharCount++; + + undo.RecordTyping (caret.line, caret.pos, ch); + + UpdateView (caret.line, caret.pos); + + if (move_caret) { + caret.pos++; + UpdateCaret (); + SetSelectionToCaret (true); + } + } + + internal void InsertPicture (Line line, int pos, RTF.Picture picture) + { + //LineTag next_tag; + LineTag tag; + int len; + + len = 1; + + // Just a place holder basically + line.text.Insert (pos, "I"); + + PictureTag picture_tag = new PictureTag (line, pos + 1, picture); + + tag = LineTag.FindTag (line, pos); + picture_tag.CopyFormattingFrom (tag); + /*next_tag = */tag.Break (pos + 1); + picture_tag.Previous = tag; + picture_tag.Next = tag.Next; + tag.Next = picture_tag; + + // + // Picture tags need to be surrounded by text tags + // + if (picture_tag.Next == null) { + picture_tag.Next = new LineTag (line, pos + 1); + picture_tag.Next.CopyFormattingFrom (tag); + picture_tag.Next.Previous = picture_tag; + } + + tag = picture_tag.Next; + while (tag != null) { + tag.Start += len; + tag = tag.Next; + } + + line.Grow (len); + line.recalc = true; + + UpdateView (line, pos); + } + + internal void DeleteMultiline (Line start_line, int pos, int length) + { + Marker start = new Marker (); + Marker end = new Marker (); + int start_index = LineTagToCharIndex (start_line, pos); + + start.line = start_line; + start.pos = pos; + start.tag = LineTag.FindTag (start_line, pos); + + CharIndexToLineTag (start_index + length, out end.line, + out end.tag, out end.pos); + + SuspendUpdate (); + + if (start.line == end.line) { + DeleteChars (start.line, pos, end.pos - pos); + } else { + + // Delete first and last lines + DeleteChars (start.line, start.pos, start.line.text.Length - start.pos); + DeleteChars (end.line, 0, end.pos); + + int current = start.line.line_no + 1; + if (current < end.line.line_no) { + for (int i = end.line.line_no - 1; i >= current; i--) { + Delete (i); + } + } + + // BIG FAT WARNING - selection_end.line might be stale due + // to the above Delete() call. DONT USE IT before hitting the end of this method! + + // Join start and end + Combine (start.line.line_no, current); + } + + ResumeUpdate (true); + } + + + // Deletes n characters at the given position; it will not delete past line limits + // pos is 0-based + public void DeleteChars (Line line, int pos, int count) + { + // Reduce our character count + CharCount -= count; + + line.DeleteCharacters (pos, count); + + if (pos >= line.TextLengthWithoutEnding ()) { + LineEnding ending = line.ending; + GetLineEnding (line.text.ToString (), 0, out ending); + + if (ending != line.ending) { + line.ending = ending; + + if (!multiline) { + UpdateView (line, lines, pos); + owner.Invalidate (); + return; + } + } + } + if (!multiline) { + UpdateView (line, lines, pos); + owner.Invalidate (); + } else + UpdateView (line, pos); + } + + // Deletes a character at or after the given position (depending on forward); it will not delete past line limits + public void DeleteChar (Line line, int pos, bool forward) + { + if ((pos == 0 && forward == false) || (pos == line.text.Length && forward == true)) + return; + + undo.BeginUserAction ("Delete"); + + if (forward) { + undo.RecordDeleteString (line, pos, line, pos + 1); + DeleteChars (line, pos, 1); + } else { + undo.RecordDeleteString (line, pos - 1, line, pos); + DeleteChars (line, pos - 1, 1); + } + + undo.EndUserAction (); + } + + // Combine two lines + internal void Combine(int FirstLine, int SecondLine) { + Combine(GetLine(FirstLine), GetLine(SecondLine)); + } + + internal void Combine(Line first, Line second) { + LineTag last; + int shift; + + // strip the ending off of the first lines text + first.text.Length = first.text.Length - LineEndingLength (first.ending); + + // Combine the two tag chains into one + last = first.tags; + + // Maintain the line ending style + first.ending = second.ending; + + while (last.Next != null) { + last = last.Next; + } + + // need to get the shift before setting the next tag since that effects length + shift = last.Start + last.Length - 1; + last.Next = second.tags; + last.Next.Previous = last; + + // Fix up references within the chain + last = last.Next; + while (last != null) { + last.Line = first; + last.Start += shift; + last = last.Next; + } + + // Combine both lines' strings + first.text.Insert(first.text.Length, second.text.ToString()); + first.Grow(first.text.Length); + + // Remove the reference to our (now combined) tags from the doomed line + second.tags = null; + + // Renumber lines + DecrementLines(first.line_no + 2); // first.line_no + 1 will be deleted, so we need to start renumbering one later + + // Mop up + first.recalc = true; + first.height = 0; // This forces RecalcDocument/UpdateView to redraw from this line on + first.Streamline(lines); + + // Update Caret, Selection, etc + if (caret.line == second) { + caret.Combine(first, shift); + } + if (selection_anchor.line == second) { + selection_anchor.Combine(first, shift); + } + if (selection_start.line == second) { + selection_start.Combine(first, shift); + } + if (selection_end.line == second) { + selection_end.Combine(first, shift); + } + + #if Debug + Line check_first; + Line check_second; + + check_first = GetLine(first.line_no); + check_second = GetLine(check_first.line_no + 1); + + Console.WriteLine("Pre-delete: Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y); + #endif + + this.Delete(second); + + #if Debug + check_first = GetLine(first.line_no); + check_second = GetLine(check_first.line_no + 1); + + Console.WriteLine("Post-delete Y of first line: {0}, second line: {1}", check_first.Y, check_second.Y); + #endif + } + + // Split the line at the position into two + internal void Split(int LineNo, int pos) { + Line line; + LineTag tag; + + line = GetLine(LineNo); + tag = LineTag.FindTag(line, pos); + Split(line, tag, pos); + } + + internal void Split(Line line, int pos) { + LineTag tag; + + tag = LineTag.FindTag(line, pos); + Split(line, tag, pos); + } + + ///<summary>Split line at given tag and position into two lines</summary> + ///if more space becomes available on previous line + internal void Split(Line line, LineTag tag, int pos) { + LineTag new_tag; + Line new_line; + bool move_caret; + bool move_sel_start; + bool move_sel_end; + + move_caret = false; + move_sel_start = false; + move_sel_end = false; + +#if DEBUG + SanityCheck(); + + if (tag.End < pos) + throw new Exception ("Split called with the wrong tag"); +#endif + + // Adjust selection and cursors + if (caret.line == line && caret.pos >= pos) { + move_caret = true; + } + if (selection_start.line == line && selection_start.pos > pos) { + move_sel_start = true; + } + + if (selection_end.line == line && selection_end.pos > pos) { + move_sel_end = true; + } + + // cover the easy case first + if (pos == line.text.Length) { + Add (line.line_no + 1, String.Empty, line.alignment, tag.Font, tag.Color, line.ending); + + new_line = GetLine (line.line_no + 1); + + if (move_caret) { + caret.line = new_line; + caret.tag = new_line.tags; + caret.pos = 0; + + if (selection_visible == false) { + SetSelectionToCaret (true); + } + } + + if (move_sel_start) { + selection_start.line = new_line; + selection_start.pos = 0; + selection_start.tag = new_line.tags; + } + + if (move_sel_end) { + selection_end.line = new_line; + selection_end.pos = 0; + selection_end.tag = new_line.tags; + } + +#if DEBUG + SanityCheck (); +#endif + return; + } + + // We need to move the rest of the text into the new line + Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.Font, tag.Color, line.ending); + + // Now transfer our tags from this line to the next + new_line = GetLine(line.line_no + 1); + + line.recalc = true; + new_line.recalc = true; + + //make sure that if we are at the end of a tag, we start on the begining + //of a new one, if one exists... Stops us creating an empty tag and + //make the operation easier. + if (tag.Next != null && (tag.Next.Start - 1) == pos) + tag = tag.Next; + + if ((tag.Start - 1) == pos) { + int shift; + + // We can simply break the chain and move the tag into the next line + + // if the tag we are moving is the first, create an empty tag + // for the line we are leaving behind + if (tag == line.tags) { + new_tag = new LineTag(line, 1); + new_tag.CopyFormattingFrom (tag); + line.tags = new_tag; + } + + if (tag.Previous != null) { + tag.Previous.Next = null; + } + new_line.tags = tag; + tag.Previous = null; + tag.Line = new_line; + + // Walk the list and correct the start location of the tags we just bumped into the next line + shift = tag.Start - 1; + + new_tag = tag; + while (new_tag != null) { + new_tag.Start -= shift; + new_tag.Line = new_line; + new_tag = new_tag.Next; + } + } else { + int shift; + + new_tag = new LineTag (new_line, 1); + new_tag.Next = tag.Next; + new_tag.CopyFormattingFrom (tag); + new_line.tags = new_tag; + if (new_tag.Next != null) { + new_tag.Next.Previous = new_tag; + } + tag.Next = null; + + shift = pos; + new_tag = new_tag.Next; + while (new_tag != null) { + new_tag.Start -= shift; + new_tag.Line = new_line; + new_tag = new_tag.Next; + + } + } + + if (move_caret) { + caret.line = new_line; + caret.pos = caret.pos - pos; + caret.tag = caret.line.FindTag(caret.pos); + + if (selection_visible == false) { + SetSelectionToCaret (true); + move_sel_start = false; + move_sel_end = false; + } + } + + if (move_sel_start) { + selection_start.line = new_line; + selection_start.pos = selection_start.pos - pos; + if (selection_start.Equals(selection_end)) + selection_start.tag = new_line.FindTag(selection_start.pos); + else + selection_start.tag = new_line.FindTag (selection_start.pos + 1); + } + + if (move_sel_end) { + selection_end.line = new_line; + selection_end.pos = selection_end.pos - pos; + selection_end.tag = new_line.FindTag(selection_end.pos); + } + + CharCount -= line.text.Length - pos; + line.text.Remove(pos, line.text.Length - pos); +#if DEBUG + SanityCheck (); +#endif + } + +#if DEBUG + private void SanityCheck () { + for (int i = 1; i < lines; i++) { + LineTag tag = GetLine (i).tags; + + if (tag.Start != 1) + throw new Exception ("Line doesn't start at the begining"); + + int start = 1; + tag = tag.Next; + + while (tag != null) { + if (tag.Start == start) + throw new Exception ("Empty tag!"); + + if (tag.Start < start) + throw new Exception ("Insane!!"); + + start = tag.Start; + tag = tag.Next; + } + } + } +#endif + + // Adds a line of text, with given font. + // Bumps any line at that line number that already exists down + internal void Add (int LineNo, string Text, Font font, Color color, LineEnding ending) + { + Add (LineNo, Text, alignment, font, color, ending); + } + + internal void Add (int LineNo, string Text, HorizontalAlignment align, Font font, Color color, LineEnding ending) + { + Line add; + Line line; + int line_no; + + CharCount += Text.Length; + + if (LineNo<1 || Text == null) { + if (LineNo<1) { + throw new ArgumentNullException("LineNo", "Line numbers must be positive"); + } else { + throw new ArgumentNullException("Text", "Cannot insert NULL line"); + } + } + + add = new Line (this, LineNo, Text, align, font, color, ending); + + line = document; + while (line != sentinel) { + add.parent = line; + line_no = line.line_no; + + if (LineNo > line_no) { + line = line.right; + } else if (LineNo < line_no) { + line = line.left; + } else { + // Bump existing line numbers; walk all nodes to the right of this one and increment line_no + IncrementLines(line.line_no); + line = line.left; + } + } + + add.left = sentinel; + add.right = sentinel; + + if (add.parent != null) { + if (LineNo > add.parent.line_no) { + add.parent.right = add; + } else { + add.parent.left = add; + } + } else { + // Root node + document = add; + } + + RebalanceAfterAdd(add); + + lines++; + } + + internal virtual void Clear() { + lines = 0; + CharCount = 0; + document = sentinel; + } + + public virtual object Clone() { + Document clone; + + clone = new Document(null); + + clone.lines = this.lines; + clone.document = (Line)document.Clone(); + + return clone; + } + + private void Delete (int LineNo) + { + Line line; + + if (LineNo > lines) + return; + + line = GetLine (LineNo); + + CharCount -= line.text.Length; + + DecrementLines (LineNo + 1); + Delete (line); + } + + private void Delete(Line line1) { + Line line2;// = new Line(); + Line line3; + + if ((line1.left == sentinel) || (line1.right == sentinel)) { + line3 = line1; + } else { + line3 = line1.right; + while (line3.left != sentinel) { + line3 = line3.left; + } + } + + if (line3.left != sentinel) { + line2 = line3.left; + } else { + line2 = line3.right; + } + + line2.parent = line3.parent; + if (line3.parent != null) { + if(line3 == line3.parent.left) { + line3.parent.left = line2; + } else { + line3.parent.right = line2; + } + } else { + document = line2; + } + + if (line3 != line1) { + LineTag tag; + + if (selection_start.line == line3) { + selection_start.line = line1; + } + + if (selection_end.line == line3) { + selection_end.line = line1; + } + + if (selection_anchor.line == line3) { + selection_anchor.line = line1; + } + + if (caret.line == line3) { + caret.line = line1; + } + + + line1.alignment = line3.alignment; + line1.ascent = line3.ascent; + line1.hanging_indent = line3.hanging_indent; + line1.height = line3.height; + line1.indent = line3.indent; + line1.line_no = line3.line_no; + line1.recalc = line3.recalc; + line1.right_indent = line3.right_indent; + line1.ending = line3.ending; + line1.space = line3.space; + line1.tags = line3.tags; + line1.text = line3.text; + line1.widths = line3.widths; + line1.offset = line3.offset; + + tag = line1.tags; + while (tag != null) { + tag.Line = line1; + tag = tag.Next; + } + } + + if (line3.color == LineColor.Black) + RebalanceAfterDelete(line2); + + this.lines--; + } + + // Invalidates the start line until the end of the viewstate + internal void InvalidateLinesAfter (Line start) { + owner.Invalidate (new Rectangle (0, start.Y - viewport_y, viewport_width, viewport_height - start.Y)); + } + + // Invalidate a section of the document to trigger redraw + internal void Invalidate(Line start, int start_pos, Line end, int end_pos) { + Line l1; + Line l2; + int p1; + int p2; + + if ((start == end) && (start_pos == end_pos)) { + return; + } + + if (end_pos == -1) { + end_pos = end.text.Length; + } + + // figure out what's before what so the logic below is straightforward + if (start.line_no < end.line_no) { + l1 = start; + p1 = start_pos; + + l2 = end; + p2 = end_pos; + } else if (start.line_no > end.line_no) { + l1 = end; + p1 = end_pos; + + l2 = start; + p2 = start_pos; + } else { + if (start_pos < end_pos) { + l1 = start; + p1 = start_pos; + + l2 = end; + p2 = end_pos; + } else { + l1 = end; + p1 = end_pos; + + l2 = start; + p2 = start_pos; + } + + int endpoint = (int) l1.widths [p2]; + if (p2 == l1.text.Length + 1) { + endpoint = (int) viewport_width; + } + + #if Debug + Console.WriteLine("Invaliding backwards from {0}:{1} to {2}:{3} {4}", + l1.line_no, p1, l2.line_no, p2, + new Rectangle( + (int)l1.widths[p1] + l1.X - viewport_x, + l1.Y - viewport_y, + (int)l1.widths[p2], + l1.height + ) + ); + #endif + + owner.Invalidate(new Rectangle ( + offset_x + (int)l1.widths[p1] + l1.X - viewport_x, + offset_y + l1.Y - viewport_y, + endpoint - (int) l1.widths [p1] + 1, + l1.height)); + return; + } + + #if Debug + Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Start => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l1.widths[p1] + l1.X - viewport_x, l1.Y - viewport_y, viewport_width, l1.height); + Console.WriteLine ("invalidate start line: {0} position: {1}", l1.text, p1); + #endif + + // Three invalidates: + // First line from start + owner.Invalidate(new Rectangle( + offset_x + (int)l1.widths[p1] + l1.X - viewport_x, + offset_y + l1.Y - viewport_y, + viewport_width, + l1.height)); + + + // lines inbetween + if ((l1.line_no + 1) < l2.line_no) { + int y; + + y = GetLine(l1.line_no + 1).Y; + owner.Invalidate(new Rectangle( + offset_x, + offset_y + y - viewport_y, + viewport_width, + l2.Y - y)); + + #if Debug + Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} Middle => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, 0, y - viewport_y, viewport_width, l2.Y - y); + #endif + } + + + // Last line to end + owner.Invalidate(new Rectangle( + offset_x + (int)l2.widths[0] + l2.X - viewport_x, + offset_y + l2.Y - viewport_y, + (int)l2.widths[p2] + 1, + l2.height)); + + #if Debug + Console.WriteLine("Invaliding from {0}:{1} to {2}:{3} End => x={4}, y={5}, {6}x{7}", l1.line_no, p1, l2.line_no, p2, (int)l2.widths[0] + l2.X - viewport_x, l2.Y - viewport_y, (int)l2.widths[p2] + 1, l2.height); + #endif + } + + /// <summary>Select text around caret</summary> + internal void ExpandSelection(CaretSelection mode, bool to_caret) { + if (to_caret) { + // We're expanding the selection to the caret position + switch(mode) { + case CaretSelection.Line: { + // Invalidate the selection delta + if (caret > selection_prev) { + Invalidate(selection_prev.line, 0, caret.line, caret.line.text.Length); + } else { + Invalidate(selection_prev.line, selection_prev.line.text.Length, caret.line, 0); + } + + if (caret.line.line_no <= selection_anchor.line.line_no) { + selection_start.line = caret.line; + selection_start.tag = caret.line.tags; + selection_start.pos = 0; + + selection_end.line = selection_anchor.line; + selection_end.tag = selection_anchor.tag; + selection_end.pos = selection_anchor.pos; + + selection_end_anchor = true; + } else { + selection_start.line = selection_anchor.line; + selection_start.pos = selection_anchor.height; + selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height + 1); + + selection_end.line = caret.line; + selection_end.tag = caret.line.tags; + selection_end.pos = caret.line.text.Length; + + selection_end_anchor = false; + } + selection_prev.line = caret.line; + selection_prev.tag = caret.tag; + selection_prev.pos = caret.pos; + + break; + } + + case CaretSelection.Word: { + int start_pos; + int end_pos; + + start_pos = FindWordSeparator(caret.line, caret.pos, false); + end_pos = FindWordSeparator(caret.line, caret.pos, true); + + + // Invalidate the selection delta + if (caret > selection_prev) { + Invalidate(selection_prev.line, selection_prev.pos, caret.line, end_pos); + } else { + Invalidate(selection_prev.line, selection_prev.pos, caret.line, start_pos); + } + if (caret < selection_anchor) { + selection_start.line = caret.line; + selection_start.tag = caret.line.FindTag(start_pos + 1); + selection_start.pos = start_pos; + + selection_end.line = selection_anchor.line; + selection_end.tag = selection_anchor.tag; + selection_end.pos = selection_anchor.pos; + + selection_prev.line = caret.line; + selection_prev.tag = caret.tag; + selection_prev.pos = start_pos; + + selection_end_anchor = true; + } else { + selection_start.line = selection_anchor.line; + selection_start.pos = selection_anchor.height; + selection_start.tag = selection_anchor.line.FindTag(selection_anchor.height + 1); + + selection_end.line = caret.line; + selection_end.tag = caret.line.FindTag(end_pos); + selection_end.pos = end_pos; + + selection_prev.line = caret.line; + selection_prev.tag = caret.tag; + selection_prev.pos = end_pos; + + selection_end_anchor = false; + } + break; + } + + case CaretSelection.Position: { + SetSelectionToCaret(false); + return; + } + } + } else { + // We're setting the selection 'around' the caret position + switch(mode) { + case CaretSelection.Line: { + this.Invalidate(caret.line, 0, caret.line, caret.line.text.Length); + + selection_start.line = caret.line; + selection_start.tag = caret.line.tags; + selection_start.pos = 0; + + selection_end.line = caret.line; + selection_end.pos = caret.line.text.Length; + selection_end.tag = caret.line.FindTag(selection_end.pos); + + selection_anchor.line = selection_end.line; + selection_anchor.tag = selection_end.tag; + selection_anchor.pos = selection_end.pos; + selection_anchor.height = 0; + + selection_prev.line = caret.line; + selection_prev.tag = caret.tag; + selection_prev.pos = caret.pos; + + this.selection_end_anchor = true; + + break; + } + + case CaretSelection.Word: { + int start_pos; + int end_pos; + + start_pos = FindWordSeparator(caret.line, caret.pos, false); + end_pos = FindWordSeparator(caret.line, caret.pos, true); + + this.Invalidate(selection_start.line, start_pos, caret.line, end_pos); + + selection_start.line = caret.line; + selection_start.tag = caret.line.FindTag(start_pos + 1); + selection_start.pos = start_pos; + + selection_end.line = caret.line; + selection_end.tag = caret.line.FindTag(end_pos); + selection_end.pos = end_pos; + + selection_anchor.line = selection_end.line; + selection_anchor.tag = selection_end.tag; + selection_anchor.pos = selection_end.pos; + selection_anchor.height = start_pos; + + selection_prev.line = caret.line; + selection_prev.tag = caret.tag; + selection_prev.pos = caret.pos; + + this.selection_end_anchor = true; + + break; + } + } + } + + SetSelectionVisible (!(selection_start == selection_end)); + } + + internal void SetSelectionToCaret(bool start) { + if (start) { + // Invalidate old selection; selection is being reset to empty + this.Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + + selection_start.line = caret.line; + selection_start.tag = caret.tag; + selection_start.pos = caret.pos; + + // start always also selects end + selection_end.line = caret.line; + selection_end.tag = caret.tag; + selection_end.pos = caret.pos; + + selection_anchor.line = caret.line; + selection_anchor.tag = caret.tag; + selection_anchor.pos = caret.pos; + } else { + // Invalidate from previous end to caret (aka new end) + if (selection_end_anchor) { + if (selection_start != caret) { + this.Invalidate(selection_start.line, selection_start.pos, caret.line, caret.pos); + } + } else { + if (selection_end != caret) { + this.Invalidate(selection_end.line, selection_end.pos, caret.line, caret.pos); + } + } + + if (caret < selection_anchor) { + selection_start.line = caret.line; + selection_start.tag = caret.tag; + selection_start.pos = caret.pos; + + selection_end.line = selection_anchor.line; + selection_end.tag = selection_anchor.tag; + selection_end.pos = selection_anchor.pos; + + selection_end_anchor = true; + } else { + selection_start.line = selection_anchor.line; + selection_start.tag = selection_anchor.tag; + selection_start.pos = selection_anchor.pos; + + selection_end.line = caret.line; + selection_end.tag = caret.tag; + selection_end.pos = caret.pos; + + selection_end_anchor = false; + } + } + + SetSelectionVisible (!(selection_start == selection_end)); + } + + internal void SetSelection(Line start, int start_pos, Line end, int end_pos) { + if (selection_visible) { + Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + } + + if ((end.line_no < start.line_no) || ((end == start) && (end_pos <= start_pos))) { + selection_start.line = end; + selection_start.tag = LineTag.FindTag(end, end_pos); + selection_start.pos = end_pos; + + selection_end.line = start; + selection_end.tag = LineTag.FindTag(start, start_pos); + selection_end.pos = start_pos; + + selection_end_anchor = true; + } else { + selection_start.line = start; + selection_start.tag = LineTag.FindTag(start, start_pos); + selection_start.pos = start_pos; + + selection_end.line = end; + selection_end.tag = LineTag.FindTag(end, end_pos); + selection_end.pos = end_pos; + + selection_end_anchor = false; + } + + selection_anchor.line = start; + selection_anchor.tag = selection_start.tag; + selection_anchor.pos = start_pos; + + if (((start == end) && (start_pos == end_pos)) || start == null || end == null) { + SetSelectionVisible (false); + } else { + SetSelectionVisible (true); + Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + } + } + + internal void SetSelectionStart(Line start, int start_pos, bool invalidate) { + // Invalidate from the previous to the new start pos + if (invalidate) + Invalidate(selection_start.line, selection_start.pos, start, start_pos); + + selection_start.line = start; + selection_start.pos = start_pos; + selection_start.tag = LineTag.FindTag(start, start_pos); + + selection_anchor.line = start; + selection_anchor.pos = start_pos; + selection_anchor.tag = selection_start.tag; + + selection_end_anchor = false; + + + if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) { + SetSelectionVisible (true); + } else { + SetSelectionVisible (false); + } + + if (invalidate) + Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + } + + internal void SetSelectionStart(int character_index, bool invalidate) { + Line line; + LineTag tag; + int pos; + + if (character_index < 0) { + return; + } + + CharIndexToLineTag(character_index, out line, out tag, out pos); + SetSelectionStart(line, pos, invalidate); + } + + internal void SetSelectionEnd(Line end, int end_pos, bool invalidate) { + + if (end == selection_end.line && end_pos == selection_start.pos) { + selection_anchor.line = selection_start.line; + selection_anchor.tag = selection_start.tag; + selection_anchor.pos = selection_start.pos; + + selection_end.line = selection_start.line; + selection_end.tag = selection_start.tag; + selection_end.pos = selection_start.pos; + + selection_end_anchor = false; + } else if ((end.line_no < selection_anchor.line.line_no) || ((end == selection_anchor.line) && (end_pos <= selection_anchor.pos))) { + selection_start.line = end; + selection_start.tag = LineTag.FindTag(end, end_pos); + selection_start.pos = end_pos; + + selection_end.line = selection_anchor.line; + selection_end.tag = selection_anchor.tag; + selection_end.pos = selection_anchor.pos; + + selection_end_anchor = true; + } else { + selection_start.line = selection_anchor.line; + selection_start.tag = selection_anchor.tag; + selection_start.pos = selection_anchor.pos; + + selection_end.line = end; + selection_end.tag = LineTag.FindTag(end, end_pos); + selection_end.pos = end_pos; + + selection_end_anchor = false; + } + + if ((selection_end.line != selection_start.line) || (selection_end.pos != selection_start.pos)) { + SetSelectionVisible (true); + if (invalidate) + Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + } else { + SetSelectionVisible (false); + // ?? Do I need to invalidate here, tests seem to work without it, but I don't think they should :-s + } + } + + internal void SetSelectionEnd(int character_index, bool invalidate) { + Line line; + LineTag tag; + int pos; + + if (character_index < 0) { + return; + } + + CharIndexToLineTag(character_index, out line, out tag, out pos); + SetSelectionEnd(line, pos, invalidate); + } + + internal void SetSelection(Line start, int start_pos) { + if (selection_visible) { + Invalidate(selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + } + + selection_start.line = start; + selection_start.pos = start_pos; + selection_start.tag = LineTag.FindTag(start, start_pos); + + selection_end.line = start; + selection_end.tag = selection_start.tag; + selection_end.pos = start_pos; + + selection_anchor.line = start; + selection_anchor.tag = selection_start.tag; + selection_anchor.pos = start_pos; + + selection_end_anchor = false; + SetSelectionVisible (false); + } + + internal void InvalidateSelectionArea() { + Invalidate (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + } + + // Return the current selection, as string + internal string GetSelection() { + // We return String.Empty if there is no selection + if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) { + return string.Empty; + } + + if (selection_start.line == selection_end.line) { + return selection_start.line.text.ToString (selection_start.pos, selection_end.pos - selection_start.pos); + } else { + StringBuilder sb; + int i; + int start; + int end; + + sb = new StringBuilder(); + start = selection_start.line.line_no; + end = selection_end.line.line_no; + + sb.Append(selection_start.line.text.ToString(selection_start.pos, selection_start.line.text.Length - selection_start.pos)); + + if ((start + 1) < end) { + for (i = start + 1; i < end; i++) { + sb.Append(GetLine(i).text.ToString()); + } + } + + sb.Append(selection_end.line.text.ToString(0, selection_end.pos)); + + return sb.ToString(); + } + } + + internal void ReplaceSelection(string s, bool select_new) { + int i; + + int selection_start_pos = LineTagToCharIndex (selection_start.line, selection_start.pos); + SuspendRecalc (); + + // First, delete any selected text + if ((selection_start.pos != selection_end.pos) || (selection_start.line != selection_end.line)) { + if (selection_start.line == selection_end.line) { + undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + + DeleteChars (selection_start.line, selection_start.pos, selection_end.pos - selection_start.pos); + + // The tag might have been removed, we need to recalc it + selection_start.tag = selection_start.line.FindTag(selection_start.pos + 1); + } else { + int start; + int end; + + start = selection_start.line.line_no; + end = selection_end.line.line_no; + + undo.RecordDeleteString (selection_start.line, selection_start.pos, selection_end.line, selection_end.pos); + + InvalidateLinesAfter(selection_start.line); + + // Delete first line + DeleteChars (selection_start.line, selection_start.pos, selection_start.line.text.Length - selection_start.pos); + selection_start.line.recalc = true; + + // Delete last line + DeleteChars(selection_end.line, 0, selection_end.pos); + + start++; + if (start < end) { + for (i = end - 1; i >= start; i--) { + Delete(i); + } + } + + // BIG FAT WARNING - selection_end.line might be stale due + // to the above Delete() call. DONT USE IT before hitting the end of this method! + + // Join start and end + Combine(selection_start.line.line_no, start); + } + } + + + Insert(selection_start.line, selection_start.pos, false, s); + undo.RecordInsertString (selection_start.line, selection_start.pos, s); + ResumeRecalc (false); + + Line begin_update_line = selection_start.line; + int begin_update_pos = selection_start.pos; + + if (!select_new) { + CharIndexToLineTag(selection_start_pos + s.Length, out selection_start.line, + out selection_start.tag, out selection_start.pos); + + selection_end.line = selection_start.line; + selection_end.pos = selection_start.pos; + selection_end.tag = selection_start.tag; + selection_anchor.line = selection_start.line; + selection_anchor.pos = selection_start.pos; + selection_anchor.tag = selection_start.tag; + + SetSelectionVisible (false); + } else { + CharIndexToLineTag(selection_start_pos, out selection_start.line, + out selection_start.tag, out selection_start.pos); + + CharIndexToLineTag(selection_start_pos + s.Length, out selection_end.line, + out selection_end.tag, out selection_end.pos); + + selection_anchor.line = selection_start.line; + selection_anchor.pos = selection_start.pos; + selection_anchor.tag = selection_start.tag; + + SetSelectionVisible (true); + } + + PositionCaret (selection_start.line, selection_start.pos); + UpdateView (begin_update_line, selection_end.line.line_no - begin_update_line.line_no, begin_update_pos); + } + + internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) { + Line line; + LineTag tag; + int i; + int chars; + int start; + + chars = 0; + + for (i = 1; i <= lines; i++) { + line = GetLine(i); + + start = chars; + chars += line.text.Length; + + if (index <= chars) { + // we found the line + tag = line.tags; + + while (tag != null) { + if (index < (start + tag.Start + tag.Length - 1)) { + line_out = line; + tag_out = LineTag.GetFinalTag (tag); + pos = index - start; + return; + } + if (tag.Next == null) { + Line next_line; + + next_line = GetLine(line.line_no + 1); + + if (next_line != null) { + line_out = next_line; + tag_out = LineTag.GetFinalTag (next_line.tags); + pos = 0; + return; + } else { + line_out = line; + tag_out = LineTag.GetFinalTag (tag); + pos = line_out.text.Length; + return; + } + } + tag = tag.Next; + } + } + } + + line_out = GetLine(lines); + tag = line_out.tags; + while (tag.Next != null) { + tag = tag.Next; + } + tag_out = tag; + pos = line_out.text.Length; + } + + internal int LineTagToCharIndex(Line line, int pos) { + int i; + int length; + + // Count first and last line + length = 0; + + // Count the lines in the middle + + for (i = 1; i < line.line_no; i++) { + length += GetLine(i).text.Length; + } + + length += pos; + + return length; + } + + internal int SelectionLength() { + if ((selection_start.pos == selection_end.pos) && (selection_start.line == selection_end.line)) { + return 0; + } + + if (selection_start.line == selection_end.line) { + return selection_end.pos - selection_start.pos; + } else { + int i; + int start; + int end; + int length; + + // Count first and last line + length = selection_start.line.text.Length - selection_start.pos + selection_end.pos + crlf_size; + + // Count the lines in the middle + start = selection_start.line.line_no + 1; + end = selection_end.line.line_no; + + if (start < end) { + for (i = start; i < end; i++) { + Line line = GetLine (i); + length += line.text.Length + LineEndingLength (line.ending); + } + } + + return length; + } + + + } + + + // UIA: Method used via reflection in TextRangeProvider + + /// <summary>Give it a Line number and it returns the Line object at with that line number</summary> + internal Line GetLine(int LineNo) { + Line line = document; + + while (line != sentinel) { + if (LineNo == line.line_no) { + return line; + } else if (LineNo < line.line_no) { + line = line.left; + } else { + line = line.right; + } + } + + return null; + } + + /// <summary>Retrieve the previous tag; walks line boundaries</summary> + internal LineTag PreviousTag(LineTag tag) { + Line l; + + if (tag.Previous != null) { + return tag.Previous; + } + + // Next line + if (tag.Line.line_no == 1) { + return null; + } + + l = GetLine(tag.Line.line_no - 1); + if (l != null) { + LineTag t; + + t = l.tags; + while (t.Next != null) { + t = t.Next; + } + return t; + } + + return null; + } + + /// <summary>Retrieve the next tag; walks line boundaries</summary> + internal LineTag NextTag(LineTag tag) { + Line l; + + if (tag.Next != null) { + return tag.Next; + } + + // Next line + l = GetLine(tag.Line.line_no + 1); + if (l != null) { + return l.tags; + } + + return null; + } + + internal Line ParagraphStart(Line line) { + Line lastline = line; + do { + if (line.line_no <= 1) + break; + + line = lastline; + lastline = GetLine (line.line_no - 1); + } while (lastline.ending == LineEnding.Wrap); + + return line; + } + + internal Line ParagraphEnd(Line line) { + Line l; + + while (line.ending == LineEnding.Wrap) { + l = GetLine(line.line_no + 1); + if ((l == null) || (l.ending != LineEnding.Wrap)) { + break; + } + line = l; + } + return line; + } + + /// <summary>Give it a pixel offset coordinate and it returns the Line covering that are (offset + /// is either X or Y depending on if we are multiline + /// </summary> + internal Line GetLineByPixel (int offset, bool exact) + { + Line line = document; + Line last = null; + + if (multiline) { + while (line != sentinel) { + last = line; + if ((offset >= line.Y) && (offset < (line.Y+line.height))) { + return line; + } else if (offset < line.Y) { + line = line.left; + } else { + line = line.right; + } + } + } else { + while (line != sentinel) { + last = line; + if ((offset >= line.X) && (offset < (line.X + line.Width))) + return line; + else if (offset < line.X) + line = line.left; + else + line = line.right; + } + } + + if (exact) { + return null; + } + return last; + } + + // UIA: Method used via reflection in TextProviderBehavior + + // Give it x/y pixel coordinates and it returns the Tag at that position + internal LineTag FindCursor (int x, int y, out int index) + { + Line line; + + x -= offset_x; + y -= offset_y; + + line = GetLineByPixel (multiline ? y : x, false); + + LineTag tag = line.GetTag (x); + + if (tag.Length == 0 && tag.Start == 1) + index = 0; + else + index = tag.GetCharIndex (x - line.align_shift); + + return tag; + } + + /// <summary>Format area of document in specified font and color</summary> + /// <param name="start_pos">1-based start position on start_line</param> + /// <param name="end_pos">1-based end position on end_line </param> + internal void FormatText (Line start_line, int start_pos, Line end_line, int end_pos, Font font, + Color color, Color back_color, FormatSpecified specified) + { + Line l; + + // First, format the first line + if (start_line != end_line) { + // First line + LineTag.FormatText(start_line, start_pos, start_line.text.Length - start_pos + 1, font, color, back_color, specified); + + // Format last line + LineTag.FormatText(end_line, 1, end_pos, font, color, back_color, specified); + + // Now all the lines inbetween + for (int i = start_line.line_no + 1; i < end_line.line_no; i++) { + l = GetLine(i); + LineTag.FormatText(l, 1, l.text.Length, font, color, back_color, specified); + } + } else { + // Special case, single line + LineTag.FormatText(start_line, start_pos, end_pos - start_pos, font, color, back_color, specified); + + if ((end_pos - start_pos) == 0 && CaretTag.Length != 0) + CaretTag = CaretTag.Next; + } + } + + internal void RecalculateAlignments () + { + Line line; + int line_no; + + line_no = 1; + + + + while (line_no <= lines) { + line = GetLine(line_no); + + if (line != null) { + switch (line.alignment) { + case HorizontalAlignment.Left: + line.align_shift = 0; + break; + case HorizontalAlignment.Center: + line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2; + break; + case HorizontalAlignment.Right: + line.align_shift = viewport_width - (int)line.widths[line.text.Length] - right_margin; + break; + } + } + + line_no++; + } + return; + } + + /// <summary>Calculate formatting for the whole document</summary> + internal bool RecalculateDocument(Graphics g) { + return RecalculateDocument(g, 1, this.lines, false); + } + + /// <summary>Calculate formatting starting at a certain line</summary> + internal bool RecalculateDocument(Graphics g, int start) { + return RecalculateDocument(g, start, this.lines, false); + } + + /// <summary>Calculate formatting within two given line numbers</summary> + internal bool RecalculateDocument(Graphics g, int start, int end) { + return RecalculateDocument(g, start, end, false); + } + + /// <summary>With optimize on, returns true if line heights changed</summary> + internal bool RecalculateDocument(Graphics g, int start, int end, bool optimize) { + Line line; + int line_no; + int offset; + int new_width; + bool changed; + int shift; + + if (recalc_suspended > 0) { + recalc_pending = true; + recalc_start = Math.Min (recalc_start, start); + recalc_end = Math.Max (recalc_end, end); + recalc_optimize = optimize; + return false; + } + + // Fixup the positions, they can go kinda nuts + // (this is suspend and resume recalc - they set them to 1 and max) + start = Math.Max (start, 1); + end = Math.Min (end, lines); + + offset = GetLine(start).offset; + line_no = start; + new_width = 0; + shift = this.lines; + if (!optimize) { + changed = true; // We always return true if we run non-optimized + } else { + changed = false; + } + + while (line_no <= (end + this.lines - shift)) { + line = GetLine(line_no++); + line.offset = offset; + + // if we are not calculating a password + if (!calc_pass) { + if (!optimize) { + line.RecalculateLine(g, this); + } else { + if (line.recalc && line.RecalculateLine(g, this)) { + changed = true; + // If the height changed, all subsequent lines change + end = this.lines; + shift = this.lines; + } + } + } else { + if (!optimize) { + line.RecalculatePasswordLine(g, this); + } else { + if (line.recalc && line.RecalculatePasswordLine(g, this)) { + changed = true; + // If the height changed, all subsequent lines change + end = this.lines; + shift = this.lines; + } + } + } + + if (line.widths[line.text.Length] > new_width) { + new_width = (int)line.widths[line.text.Length]; + } + + // Calculate alignment + if (line.alignment != HorizontalAlignment.Left) { + if (line.alignment == HorizontalAlignment.Center) { + line.align_shift = (viewport_width - (int)line.widths[line.text.Length]) / 2; + } else { + line.align_shift = viewport_width - (int)line.widths[line.text.Length] - 1; + } + } + + if (multiline) + offset += line.height; + else + offset += (int) line.widths [line.text.Length]; + + if (line_no > lines) { + break; + } + } + + if (document_x != new_width) { + document_x = new_width; + if (WidthChanged != null) { + WidthChanged(this, null); + } + } + + RecalculateAlignments(); + + line = GetLine(lines); + + if (document_y != line.Y + line.height) { + document_y = line.Y + line.height; + if (HeightChanged != null) { + HeightChanged(this, null); + } + } + + // scan for links and tell us if its all + // changed, so we can update everything + if (EnableLinks) + ScanForLinks (start, end, ref changed); + + UpdateCaret(); + return changed; + } + + internal int Size() { + return lines; + } + + private void owner_HandleCreated(object sender, EventArgs e) { + RecalculateDocument(owner.CreateGraphicsInternal()); + AlignCaret(); + } + + private void owner_VisibleChanged(object sender, EventArgs e) { + if (owner.Visible) { + RecalculateDocument(owner.CreateGraphicsInternal()); + } + } + + internal static bool IsWordSeparator (char ch) + { + switch (ch) { + case ' ': + case '\t': + case '(': + case ')': + case '\r': + case '\n': + return true; + default: + return false; + } + } + + internal int FindWordSeparator(Line line, int pos, bool forward) { + int len; + + len = line.text.Length; + + if (forward) { + for (int i = pos + 1; i < len; i++) { + if (IsWordSeparator(line.Text[i])) { + return i + 1; + } + } + return len; + } else { + for (int i = pos - 1; i > 0; i--) { + if (IsWordSeparator(line.Text[i - 1])) { + return i; + } + } + return 0; + } + } + + /* Search document for text */ + internal bool FindChars(char[] chars, Marker start, Marker end, out Marker result) { + Line line; + int line_no; + int pos; + int line_len; + + // Search for occurence of any char in the chars array + result = new Marker(); + + line = start.line; + line_no = start.line.line_no; + pos = start.pos; + while (line_no <= end.line.line_no) { + line_len = line.text.Length; + while (pos < line_len) { + for (int i = 0; i < chars.Length; i++) { + if (line.text[pos] == chars[i]) { + // Special case + if ((line.line_no == end.line.line_no) && (pos >= end.pos)) { + return false; + } + + result.line = line; + result.pos = pos; + return true; + } + } + pos++; + } + + pos = 0; + line_no++; + line = GetLine(line_no); + } + + return false; + } + + // This version does not build one big string for searching, instead it handles + // line-boundaries, which is faster and less memory intensive + // FIXME - Depending on culture stuff we might have to create a big string and use culturespecific + // search stuff and change it to accept and return positions instead of Markers (which would match + // RichTextBox behaviour better but would be inconsistent with the rest of TextControl) + internal bool Find(string search, Marker start, Marker end, out Marker result, RichTextBoxFinds options) { + Marker last; + string search_string; + Line line; + int line_no; + int pos; + int line_len; + int current; + bool word; + bool word_option; + bool ignore_case; + bool reverse; + char c; + + result = new Marker(); + word_option = ((options & RichTextBoxFinds.WholeWord) != 0); + ignore_case = ((options & RichTextBoxFinds.MatchCase) == 0); + reverse = ((options & RichTextBoxFinds.Reverse) != 0); + + line = start.line; + line_no = start.line.line_no; + pos = start.pos; + current = 0; + + // Prep our search string, lowercasing it if we do case-independent matching + if (ignore_case) { + StringBuilder sb; + sb = new StringBuilder(search); + for (int i = 0; i < sb.Length; i++) { + sb[i] = Char.ToLower(sb[i]); + } + search_string = sb.ToString(); + } else { + search_string = search; + } + + // We need to check if the character before our start position is a wordbreak + if (word_option) { + if (line_no == 1) { + if ((pos == 0) || (IsWordSeparator(line.text[pos - 1]))) { + word = true; + } else { + word = false; + } + } else { + if (pos > 0) { + if (IsWordSeparator(line.text[pos - 1])) { + word = true; + } else { + word = false; + } + } else { + // Need to check the end of the previous line + Line prev_line; + + prev_line = GetLine(line_no - 1); + if (prev_line.ending == LineEnding.Wrap) { + if (IsWordSeparator(prev_line.text[prev_line.text.Length - 1])) { + word = true; + } else { + word = false; + } + } else { + word = true; + } + } + } + } else { + word = false; + } + + // To avoid duplication of this loop with reverse logic, we search + // through the document, remembering the last match and when returning + // report that last remembered match + + last = new Marker(); + last.height = -1; // Abused - we use it to track change + + while (line_no <= end.line.line_no) { + if (line_no != end.line.line_no) { + line_len = line.text.Length; + } else { + line_len = end.pos; + } + + while (pos < line_len) { + + if (word_option && (current == search_string.Length)) { + if (IsWordSeparator(line.text[pos])) { + if (!reverse) { + goto FindFound; + } else { + last = result; + current = 0; + } + } else { + current = 0; + } + } + + if (ignore_case) { + c = Char.ToLower(line.text[pos]); + } else { + c = line.text[pos]; + } + + if (c == search_string[current]) { + + if (current == 0) { + result.line = line; + result.pos = pos; + } + if (!word_option || (word_option && (word || (current > 0)))) { + current++; + } + + if (!word_option && (current == search_string.Length)) { + if (!reverse) { + goto FindFound; + } else { + last = result; + current = 0; + } + } + } else { + current = 0; + } + pos++; + + if (!word_option) { + continue; + } + + if (IsWordSeparator(c)) { + word = true; + } else { + word = false; + } + } + + if (word_option) { + // Mark that we just saw a word boundary + if (line.ending != LineEnding.Wrap || line.line_no == lines - 1) { + word = true; + } + + if (current == search_string.Length) { + if (word) { + if (!reverse) { + goto FindFound; + } else { + last = result; + current = 0; + } + } else { + current = 0; + } + } + } + + pos = 0; + line_no++; + line = GetLine(line_no); + } + + if (reverse) { + if (last.height != -1) { + result = last; + return true; + } + } + + return false; + + FindFound: + if (!reverse) { +// if ((line.line_no == end.line.line_no) && (pos >= end.pos)) { +// return false; +// } + return true; + } + + result = last; + return true; + + } + + /* Marker stuff */ + internal void GetMarker(out Marker mark, bool start) { + mark = new Marker(); + + if (start) { + mark.line = GetLine(1); + mark.tag = mark.line.tags; + mark.pos = 0; + } else { + mark.line = GetLine(lines); + mark.tag = mark.line.tags; + while (mark.tag.Next != null) { + mark.tag = mark.tag.Next; + } + mark.pos = mark.line.text.Length; + } + } + #endregion // Internal Methods + + #region Events + internal event EventHandler CaretMoved; + internal event EventHandler WidthChanged; + internal event EventHandler HeightChanged; + internal event EventHandler LengthChanged; + internal event EventHandler UIASelectionChanged; + #endregion // Events + + #region Administrative + public IEnumerator GetEnumerator() { + // FIXME + return null; + } + + public override bool Equals(object obj) { + if (obj == null) { + return false; + } + + if (!(obj is Document)) { + return false; + } + + if (obj == this) { + return true; + } + + if (ToString().Equals(((Document)obj).ToString())) { + return true; + } + + return false; + } + + public override int GetHashCode() { + return document_id; + } + + public override string ToString() { + return "document " + this.document_id; + } + #endregion // Administrative + } + + internal class PictureTag : LineTag { + + internal RTF.Picture picture; + + internal PictureTag (Line line, int start, RTF.Picture picture) : base (line, start) + { + this.picture = picture; + } + + public override bool IsTextTag { + get { return false; } + } + + public override SizeF SizeOfPosition (Graphics dc, int pos) + { + return picture.Size; + } + + internal override int MaxHeight () + { + return (int) (picture.Height + 0.5F); + } + + public override void Draw (Graphics dc, Color color, float xoff, float y, int start, int end) + { + picture.DrawImage (dc, xoff + Line.widths [start], y, false); + } + + public override void Draw (Graphics dc, Color color, float xoff, float y, int start, int end, string text) + { + picture.DrawImage (dc, xoff + + Line.widths [start], y, false); + } + + public override string Text () + { + return "I"; + } + } + + internal class UndoManager { + + internal enum ActionType { + + Typing, + + // This is basically just cut & paste + InsertString, + DeleteString, + + UserActionBegin, + UserActionEnd + } + + internal class Action { + internal ActionType type; + internal int line_no; + internal int pos; + internal object data; + } + + #region Local Variables + private Document document; + private Stack undo_actions; + private Stack redo_actions; + + //private int caret_line; + //private int caret_pos; + + // When performing an action, we lock the queue, so that the action can't be undone + private bool locked; + #endregion // Local Variables + + #region Constructors + internal UndoManager (Document document) + { + this.document = document; + undo_actions = new Stack (50); + redo_actions = new Stack (50); + } + #endregion // Constructors + + #region Properties + internal bool CanUndo { + get { return undo_actions.Count > 0; } + } + + internal bool CanRedo { + get { return redo_actions.Count > 0; } + } + + internal string UndoActionName { + get { + foreach (Action action in undo_actions) { + if (action.type == ActionType.UserActionBegin) + return (string) action.data; + if (action.type == ActionType.Typing) + return String.Format ("Typing"); + } + return String.Empty; + } + } + + internal string RedoActionName { + get { + foreach (Action action in redo_actions) { + if (action.type == ActionType.UserActionBegin) + return (string) action.data; + if (action.type == ActionType.Typing) + return String.Format ("Typing"); + } + return String.Empty; + } + } + #endregion // Properties + + #region Internal Methods + internal void Clear () + { + undo_actions.Clear(); + redo_actions.Clear(); + } + + internal bool Undo () + { + Action action; + bool user_action_finished = false; + + if (undo_actions.Count == 0) + return false; + + locked = true; + do { + Line start; + action = (Action) undo_actions.Pop (); + + // Put onto redo stack + redo_actions.Push(action); + + // Do the thing + switch(action.type) { + + case ActionType.UserActionBegin: + user_action_finished = true; + break; + + case ActionType.UserActionEnd: + // noop + break; + + case ActionType.InsertString: + start = document.GetLine (action.line_no); + document.SuspendUpdate (); + document.DeleteMultiline (start, action.pos, ((string) action.data).Length + 1); + document.PositionCaret (start, action.pos); + document.SetSelectionToCaret (true); + document.ResumeUpdate (true); + break; + + case ActionType.Typing: + start = document.GetLine (action.line_no); + document.SuspendUpdate (); + document.DeleteMultiline (start, action.pos, ((StringBuilder) action.data).Length); + document.PositionCaret (start, action.pos); + document.SetSelectionToCaret (true); + document.ResumeUpdate (true); + + // This is an open ended operation, so only a single typing operation can be undone at once + user_action_finished = true; + break; + + case ActionType.DeleteString: + start = document.GetLine (action.line_no); + document.SuspendUpdate (); + Insert (start, action.pos, (Line) action.data, true); + document.ResumeUpdate (true); + break; + } + } while (!user_action_finished && undo_actions.Count > 0); + + locked = false; + + return true; + } + + internal bool Redo () + { + Action action; + bool user_action_finished = false; + + if (redo_actions.Count == 0) + return false; + + locked = true; + do { + Line start; + int start_index; + + action = (Action) redo_actions.Pop (); + undo_actions.Push (action); + + switch (action.type) { + + case ActionType.UserActionBegin: + // Noop + break; + + case ActionType.UserActionEnd: + user_action_finished = true; + break; + + case ActionType.InsertString: + start = document.GetLine (action.line_no); + document.SuspendUpdate (); + start_index = document.LineTagToCharIndex (start, action.pos); + document.InsertString (start, action.pos, (string) action.data); + document.CharIndexToLineTag (start_index + ((string) action.data).Length, + out document.caret.line, out document.caret.tag, + out document.caret.pos); + document.UpdateCaret (); + document.SetSelectionToCaret (true); + document.ResumeUpdate (true); + break; + + case ActionType.Typing: + start = document.GetLine (action.line_no); + document.SuspendUpdate (); + start_index = document.LineTagToCharIndex (start, action.pos); + document.InsertString (start, action.pos, ((StringBuilder) action.data).ToString ()); + document.CharIndexToLineTag (start_index + ((StringBuilder) action.data).Length, + out document.caret.line, out document.caret.tag, + out document.caret.pos); + document.UpdateCaret (); + document.SetSelectionToCaret (true); + document.ResumeUpdate (true); + + // This is an open ended operation, so only a single typing operation can be undone at once + user_action_finished = true; + break; + + case ActionType.DeleteString: + start = document.GetLine (action.line_no); + document.SuspendUpdate (); + document.DeleteMultiline (start, action.pos, ((Line) action.data).text.Length); + document.PositionCaret (start, action.pos); + document.SetSelectionToCaret (true); + document.ResumeUpdate (true); + + break; + } + } while (!user_action_finished && redo_actions.Count > 0); + + locked = false; + + return true; + } + #endregion // Internal Methods + + #region Private Methods + + public void BeginUserAction (string name) + { + if (locked) + return; + + // Nuke the redo queue + redo_actions.Clear (); + + Action ua = new Action (); + ua.type = ActionType.UserActionBegin; + ua.data = name; + + undo_actions.Push (ua); + } + + public void EndUserAction () + { + if (locked) + return; + + Action ua = new Action (); + ua.type = ActionType.UserActionEnd; + + undo_actions.Push (ua); + } + + // start_pos, end_pos = 1 based + public void RecordDeleteString (Line start_line, int start_pos, Line end_line, int end_pos) + { + if (locked) + return; + + // Nuke the redo queue + redo_actions.Clear (); + + Action a = new Action (); + + // We cant simply store the string, because then formatting would be lost + a.type = ActionType.DeleteString; + a.line_no = start_line.line_no; + a.pos = start_pos; + a.data = Duplicate (start_line, start_pos, end_line, end_pos); + + undo_actions.Push(a); + } + + public void RecordInsertString (Line line, int pos, string str) + { + if (locked || str.Length == 0) + return; + + // Nuke the redo queue + redo_actions.Clear (); + + Action a = new Action (); + + a.type = ActionType.InsertString; + a.data = str; + a.line_no = line.line_no; + a.pos = pos; + + undo_actions.Push (a); + } + + public void RecordTyping (Line line, int pos, char ch) + { + if (locked) + return; + + // Nuke the redo queue + redo_actions.Clear (); + + Action a = null; + + if (undo_actions.Count > 0) + a = (Action) undo_actions.Peek (); + + if (a == null || a.type != ActionType.Typing) { + a = new Action (); + a.type = ActionType.Typing; + a.data = new StringBuilder (); + a.line_no = line.line_no; + a.pos = pos; + + undo_actions.Push (a); + } + + StringBuilder data = (StringBuilder) a.data; + data.Append (ch); + } + + // start_pos = 1-based + // end_pos = 1-based + public Line Duplicate(Line start_line, int start_pos, Line end_line, int end_pos) + { + Line ret; + Line line; + Line current; + LineTag tag; + LineTag current_tag; + int start; + int end; + int tag_start; + + line = new Line (start_line.document, start_line.ending); + ret = line; + + for (int i = start_line.line_no; i <= end_line.line_no; i++) { + current = document.GetLine(i); + + if (start_line.line_no == i) { + start = start_pos; + } else { + start = 0; + } + + if (end_line.line_no == i) { + end = end_pos; + } else { + end = current.text.Length; + } + + if (end_pos == 0) + continue; + + // Text for the tag + line.text = new StringBuilder (current.text.ToString (start, end - start)); + + // Copy tags from start to start+length onto new line + current_tag = current.FindTag (start + 1); + while ((current_tag != null) && (current_tag.Start <= end)) { + if ((current_tag.Start <= start) && (start < (current_tag.Start + current_tag.Length))) { + // start tag is within this tag + tag_start = start; + } else { + tag_start = current_tag.Start; + } + + tag = new LineTag(line, tag_start - start + 1); + tag.CopyFormattingFrom (current_tag); + + current_tag = current_tag.Next; + + // Add the new tag to the line + if (line.tags == null) { + line.tags = tag; + } else { + LineTag tail; + tail = line.tags; + + while (tail.Next != null) { + tail = tail.Next; + } + tail.Next = tag; + tag.Previous = tail; + } + } + + if ((i + 1) <= end_line.line_no) { + line.ending = current.ending; + + // Chain them (we use right/left as next/previous) + line.right = new Line (start_line.document, start_line.ending); + line.right.left = line; + line = line.right; + } + } + + return ret; + } + + // Insert multi-line text at the given position; use formatting at insertion point for inserted text + internal void Insert(Line line, int pos, Line insert, bool select) + { + Line current; + LineTag tag; + int offset; + int lines; + Line first; + + // Handle special case first + if (insert.right == null) { + + // Single line insert + document.Split(line, pos); + + if (insert.tags == null) { + return; // Blank line + } + + //Insert our tags at the end + tag = line.tags; + + while (tag.Next != null) { + tag = tag.Next; + } + + offset = tag.Start + tag.Length - 1; + + tag.Next = insert.tags; + line.text.Insert(offset, insert.text.ToString()); + + // Adjust start locations + tag = tag.Next; + while (tag != null) { + tag.Start += offset; + tag.Line = line; + tag = tag.Next; + } + // Put it back together + document.Combine(line.line_no, line.line_no + 1); + + if (select) { + document.SetSelectionStart (line, pos, false); + document.SetSelectionEnd (line, pos + insert.text.Length, false); + } + + document.UpdateView(line, pos); + return; + } + + first = line; + lines = 1; + current = insert; + + while (current != null) { + + if (current == insert) { + // Inserting the first line we split the line (and make space) + document.Split(line.line_no, pos); + //Insert our tags at the end of the line + tag = line.tags; + + + if (tag != null && tag.Length != 0) { + while (tag.Next != null) { + tag = tag.Next; + } + offset = tag.Start + tag.Length - 1; + tag.Next = current.tags; + tag.Next.Previous = tag; + + tag = tag.Next; + + } else { + offset = 0; + line.tags = current.tags; + line.tags.Previous = null; + tag = line.tags; + } + + line.ending = current.ending; + } else { + document.Split(line.line_no, 0); + offset = 0; + line.tags = current.tags; + line.tags.Previous = null; + line.ending = current.ending; + tag = line.tags; + } + + // Adjust start locations and line pointers + while (tag != null) { + tag.Start += offset - 1; + tag.Line = line; + tag = tag.Next; + } + + line.text.Insert(offset, current.text.ToString()); + line.Grow(line.text.Length); + + line.recalc = true; + line = document.GetLine(line.line_no + 1); + + // FIXME? Test undo of line-boundaries + if ((current.right == null) && (current.tags.Length != 0)) { + document.Combine(line.line_no - 1, line.line_no); + } + current = current.right; + lines++; + + } + + // Recalculate our document + document.UpdateView(first, lines, pos); + return; + } + #endregion // Private Methods + } +} diff --git a/source/ShiftUI/Internal/TextFormatFlags.cs b/source/ShiftUI/Internal/TextFormatFlags.cs new file mode 100644 index 0000000..6e2c798 --- /dev/null +++ b/source/ShiftUI/Internal/TextFormatFlags.cs @@ -0,0 +1,62 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Author: +// Pedro MartÃnez Juliá <[email protected]> +// +using System; + + + +namespace ShiftUI { + + [FlagsAttribute()] + public enum TextFormatFlags { + Left = 0, + Top = 0, + Default = 0, + GlyphOverhangPadding = 0, + HorizontalCenter = 1, + Right = 2, + VerticalCenter = 4, + Bottom = 8, + WordBreak = 16, + SingleLine = 32, + ExpandTabs = 64, + NoClipping = 256, + ExternalLeading = 512, + NoPrefix = 2048, + Internal = 4096, + TextBoxControl = 8192, + PathEllipsis = 16384, + EndEllipsis = 32768, + ModifyString = 65536, + RightToLeft = 131072, + WordEllipsis = 262144, + NoFullWidthCharacterBreak = 524288, + HidePrefix = 1048576, + PrefixOnly = 2097152, + PreserveGraphicsClipping = 16777216, + PreserveGraphicsTranslateTransform = 33554432, + NoPadding = 268435456, + LeftAndRightPadding = 536870912 + } +}
\ No newline at end of file diff --git a/source/ShiftUI/Internal/TextRenderer.cs b/source/ShiftUI/Internal/TextRenderer.cs new file mode 100644 index 0000000..89b3dfa --- /dev/null +++ b/source/ShiftUI/Internal/TextRenderer.cs @@ -0,0 +1,527 @@ +// +// TextRenderer.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + +// This has become a monster class for all things text measuring and drawing. +// +// The public API is MeasureText/DrawText, which uses GDI on Win32, and +// GDI+ on other platforms. +// +// There is an internal API MeasureTextInternal/DrawTextInternal, which allows +// you to pass a flag of whether to use GDI or GDI+. This is used mainly for +// Widgets that have the UseCompatibleTextRendering flag. +// +// There are also thread-safe versions of MeasureString/MeasureCharacterRanges +// for things that want to measure strings without having a Graphics object. + +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Drawing.Text; +using System; + +namespace ShiftUI +{ + public sealed class TextRenderer + { + private TextRenderer () + { + } + + #region Public Methods + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor) + { + DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor) + { + DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, pt, foreColor, backColor, flags, false); + } + + public static void DrawText (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags) + { + DrawTextInternal (dc, text, font, bounds, foreColor, backColor, flags, false); + } + + public static Size MeasureText (string text, Font font) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, false); + } + + public static Size MeasureText (IDeviceContext dc, string text, Font font) + { + return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, false); + } + + public static Size MeasureText (string text, Font font, Size proposedSize) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, false); + } + + public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize) + { + return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, false); + } + + public static Size MeasureText (string text, Font font, Size proposedSize, TextFormatFlags flags) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, false); + } + + public static Size MeasureText (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags) + { + return MeasureTextInternal (dc, text, font, proposedSize, flags, false); + } + #endregion + + #region Internal Methods That Do Stuff + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString) + { + if (dc == null) + throw new ArgumentNullException ("dc"); + + if (text == null || text.Length == 0) + return; + + // We use MS GDI API's unless told not to, or we aren't on Windows + if (!useDrawString && !XplatUI.RunningOnUnix) { + if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter || (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) + flags |= TextFormatFlags.SingleLine; + + // Calculate the text bounds (there is often padding added) + Rectangle new_bounds = PadRectangle (bounds, flags); + new_bounds.Offset ((int)(dc as Graphics).Transform.OffsetX, (int)(dc as Graphics).Transform.OffsetY); + + IntPtr hdc = IntPtr.Zero; + bool clear_clip_region = false; + + // If we need to use the graphics clipping region, add it to our hdc + if ((flags & TextFormatFlags.PreserveGraphicsClipping) == TextFormatFlags.PreserveGraphicsClipping) { + Graphics graphics = (Graphics)dc; + Region clip_region = graphics.Clip; + + if (!clip_region.IsInfinite (graphics)) { + IntPtr hrgn = clip_region.GetHrgn (graphics); + hdc = dc.GetHdc (); + SelectClipRgn (hdc, hrgn); + DeleteObject (hrgn); + + clear_clip_region = true; + } + } + + if (hdc == IntPtr.Zero) + hdc = dc.GetHdc (); + + // Set the fore color + if (foreColor != Color.Empty) + SetTextColor (hdc, ColorTranslator.ToWin32 (foreColor)); + + // Set the back color + if (backColor != Color.Transparent && backColor != Color.Empty) { + SetBkMode (hdc, 2); //1-Transparent, 2-Opaque + SetBkColor (hdc, ColorTranslator.ToWin32 (backColor)); + } + else { + SetBkMode (hdc, 1); //1-Transparent, 2-Opaque + } + + XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new_bounds); + + IntPtr prevobj; + + if (font != null) { + prevobj = SelectObject (hdc, font.ToHfont ()); + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + prevobj = SelectObject (hdc, prevobj); + DeleteObject (prevobj); + } + else { + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + } + + if (clear_clip_region) + SelectClipRgn (hdc, IntPtr.Zero); + + dc.ReleaseHdc (); + } + // Use Graphics.DrawString as a fallback method + else { + Graphics g; + IntPtr hdc = IntPtr.Zero; + + if (dc is Graphics) + g = (Graphics)dc; + else { + hdc = dc.GetHdc (); + g = Graphics.FromHdc (hdc); + } + + StringFormat sf = FlagsToStringFormat (flags); + + Rectangle new_bounds = PadDrawStringRectangle (bounds, flags); + + g.DrawString (text, font, ThemeEngine.Current.ResPool.GetSolidBrush (foreColor), new_bounds, sf); + + if (!(dc is Graphics)) { + g.Dispose (); + dc.ReleaseHdc (); + } + } + } + + internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString) + { + if (!useMeasureString && !XplatUI.RunningOnUnix) { + // Tell DrawText to calculate size instead of draw + flags |= (TextFormatFlags)1024; // DT_CALCRECT + + IntPtr hdc = dc.GetHdc (); + + XplatUIWin32.RECT r = XplatUIWin32.RECT.FromRectangle (new Rectangle (Point.Empty, proposedSize)); + + IntPtr prevobj; + + if (font != null) { + prevobj = SelectObject (hdc, font.ToHfont ()); + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + prevobj = SelectObject (hdc, prevobj); + DeleteObject (prevobj); + } + else { + Win32DrawText (hdc, text, text.Length, ref r, (int)flags); + } + + dc.ReleaseHdc (); + + // Really, I am just making something up here, which as far as I can tell, MS + // just makes something up as well. This will require lots of tweaking to match MS. :( + Size retval = r.ToRectangle ().Size; + + if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) { + retval.Width += 6; + retval.Width += (int)retval.Height / 8; + } + + return retval; + } + else { + StringFormat sf = FlagsToStringFormat (flags); + + Size retval; + + int proposedWidth; + if (proposedSize.Width == 0) + proposedWidth = Int32.MaxValue; + else { + proposedWidth = proposedSize.Width; + if ((flags & TextFormatFlags.NoPadding) == 0) + proposedWidth -= 9; + } + if (dc is Graphics) + retval = (dc as Graphics).MeasureString (text, font, proposedWidth, sf).ToSize (); + else + retval = TextRenderer.MeasureString (text, font, proposedWidth, sf).ToSize (); + + if (retval.Width > 0 && (flags & TextFormatFlags.NoPadding) == 0) + retval.Width += 9; + + return retval; + } + } + #endregion + +#region Internal Methods That Are Just Overloads + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, TextFormatFlags.Default, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, pt, foreColor, backColor, TextFormatFlags.Default, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, TextFormatFlags flags, bool useDrawString) + { + DrawTextInternal (dc, text, font, pt, foreColor, Color.Transparent, flags, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, Color backColor, bool useDrawString) + { + DrawTextInternal (dc, text, font, bounds, foreColor, backColor, TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter, useDrawString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Rectangle bounds, Color foreColor, TextFormatFlags flags, bool useDrawString) + { + DrawTextInternal (dc, text, font, bounds, foreColor, Color.Transparent, flags, useDrawString); + } + + internal static Size MeasureTextInternal (string text, Font font, bool useMeasureString) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString); + } + + internal static void DrawTextInternal (IDeviceContext dc, string text, Font font, Point pt, Color foreColor, Color backColor, TextFormatFlags flags, bool useDrawString) + { + Size sz = MeasureTextInternal (dc, text, font, useDrawString); + DrawTextInternal (dc, text, font, new Rectangle (pt, sz), foreColor, backColor, flags, useDrawString); + } + + internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, bool useMeasureString) + { + return MeasureTextInternal (dc, text, font, Size.Empty, TextFormatFlags.Default, useMeasureString); + } + + internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, bool useMeasureString) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, TextFormatFlags.Default, useMeasureString); + } + + internal static Size MeasureTextInternal (IDeviceContext dc, string text, Font font, Size proposedSize, bool useMeasureString) + { + return MeasureTextInternal (dc, text, font, proposedSize, TextFormatFlags.Default, useMeasureString); + } + + internal static Size MeasureTextInternal (string text, Font font, Size proposedSize, TextFormatFlags flags, bool useMeasureString) + { + return MeasureTextInternal (Hwnd.GraphicsContext, text, font, proposedSize, flags, useMeasureString); + } +#endregion + + #region Thread-Safe Static Graphics Methods + internal static SizeF MeasureString (string text, Font font) + { + return Hwnd.GraphicsContext.MeasureString (text, font); + } + + internal static SizeF MeasureString (string text, Font font, int width) + { + return Hwnd.GraphicsContext.MeasureString (text, font, width); + } + + internal static SizeF MeasureString (string text, Font font, SizeF layoutArea) + { + return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea); + } + + internal static SizeF MeasureString (string text, Font font, int width, StringFormat format) + { + return Hwnd.GraphicsContext.MeasureString (text, font, width, format); + } + + internal static SizeF MeasureString (string text, Font font, PointF origin, StringFormat stringFormat) + { + return Hwnd.GraphicsContext.MeasureString (text, font, origin, stringFormat); + } + + internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat) + { + return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat); + } + + internal static SizeF MeasureString (string text, Font font, SizeF layoutArea, StringFormat stringFormat, out int charactersFitted, out int linesFilled) + { + return Hwnd.GraphicsContext.MeasureString (text, font, layoutArea, stringFormat, out charactersFitted, out linesFilled); + } + + internal static Region[] MeasureCharacterRanges (string text, Font font, RectangleF layoutRect, StringFormat stringFormat) + { + return Hwnd.GraphicsContext.MeasureCharacterRanges (text, font, layoutRect, stringFormat); + } + + internal static SizeF GetDpi () + { + return new SizeF (Hwnd.GraphicsContext.DpiX, Hwnd.GraphicsContext.DpiY); + } + #endregion + +#region Private Methods + private static StringFormat FlagsToStringFormat (TextFormatFlags flags) + { + StringFormat sf = new StringFormat (); + + // Translation table: http://msdn.microsoft.com/msdnmag/issues/06/03/TextRendering/default.aspx?fig=true#fig4 + + // Horizontal Alignment + if ((flags & TextFormatFlags.HorizontalCenter) == TextFormatFlags.HorizontalCenter) + sf.Alignment = StringAlignment.Center; + else if ((flags & TextFormatFlags.Right) == TextFormatFlags.Right) + sf.Alignment = StringAlignment.Far; + else + sf.Alignment = StringAlignment.Near; + + // Vertical Alignment + if ((flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) + sf.LineAlignment = StringAlignment.Far; + else if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) + sf.LineAlignment = StringAlignment.Center; + else + sf.LineAlignment = StringAlignment.Near; + + // Ellipsis + if ((flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis) + sf.Trimming = StringTrimming.EllipsisCharacter; + else if ((flags & TextFormatFlags.PathEllipsis) == TextFormatFlags.PathEllipsis) + sf.Trimming = StringTrimming.EllipsisPath; + else if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis) + sf.Trimming = StringTrimming.EllipsisWord; + else + sf.Trimming = StringTrimming.Character; + + // Hotkey Prefix + if ((flags & TextFormatFlags.NoPrefix) == TextFormatFlags.NoPrefix) + sf.HotkeyPrefix = HotkeyPrefix.None; + else if ((flags & TextFormatFlags.HidePrefix) == TextFormatFlags.HidePrefix) + sf.HotkeyPrefix = HotkeyPrefix.Hide; + else + sf.HotkeyPrefix = HotkeyPrefix.Show; + + // Text Padding + if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) + sf.FormatFlags |= StringFormatFlags.FitBlackBox; + + // Text Wrapping + if ((flags & TextFormatFlags.SingleLine) == TextFormatFlags.SingleLine) + sf.FormatFlags |= StringFormatFlags.NoWrap; + else if ((flags & TextFormatFlags.TextBoxControl) == TextFormatFlags.TextBoxControl) + sf.FormatFlags |= StringFormatFlags.LineLimit; + + // Other Flags + //if ((flags & TextFormatFlags.RightToLeft) == TextFormatFlags.RightToLeft) + // sf.FormatFlags |= StringFormatFlags.DirectionRightToLeft; + if ((flags & TextFormatFlags.NoClipping) == TextFormatFlags.NoClipping) + sf.FormatFlags |= StringFormatFlags.NoClip; + + return sf; + } + + private static Rectangle PadRectangle (Rectangle r, TextFormatFlags flags) + { + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) { + r.X += 3; + r.Width -= 3; + } + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) { + r.Width -= 4; + } + if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) { + r.X += 2; + r.Width -= 2; + } + if ((flags & TextFormatFlags.WordEllipsis) == TextFormatFlags.WordEllipsis || (flags & TextFormatFlags.EndEllipsis) == TextFormatFlags.EndEllipsis || (flags & TextFormatFlags.WordBreak) == TextFormatFlags.WordBreak) { + r.Width -= 4; + } + if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter) { + r.Y += 1; + } + + return r; + } + + private static Rectangle PadDrawStringRectangle (Rectangle r, TextFormatFlags flags) + { + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == 0 && (flags & TextFormatFlags.HorizontalCenter) == 0) { + r.X += 1; + r.Width -= 1; + } + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Right) == TextFormatFlags.Right) { + r.Width -= 4; + } + if ((flags & TextFormatFlags.NoPadding) == TextFormatFlags.NoPadding) { + r.X -= 2; + } + if ((flags & TextFormatFlags.NoPadding) == 0 && (flags & TextFormatFlags.Bottom) == TextFormatFlags.Bottom) { + r.Y += 1; + } + if ((flags & TextFormatFlags.LeftAndRightPadding) == TextFormatFlags.LeftAndRightPadding) { + r.X += 2; + r.Width -= 2; + } + if ((flags & TextFormatFlags.VerticalCenter) == TextFormatFlags.VerticalCenter && XplatUI.RunningOnUnix) { + r.Y -= 1; + } + + return r; + } +#endregion + +#region DllImports (Windows) + [DllImport ("user32", CharSet = CharSet.Unicode, EntryPoint = "DrawText")] + static extern int Win32DrawText (IntPtr hdc, string lpStr, int nCount, ref XplatUIWin32.RECT lpRect, int wFormat); + + [DllImport ("gdi32")] + static extern int SetTextColor (IntPtr hdc, int crColor); + + [DllImport ("gdi32")] + static extern IntPtr SelectObject (IntPtr hDC, IntPtr hObject); + + [DllImport ("gdi32")] + static extern int SetBkColor (IntPtr hdc, int crColor); + + [DllImport ("gdi32")] + static extern int SetBkMode (IntPtr hdc, int iBkMode); + + [DllImport ("gdi32")] + static extern bool DeleteObject (IntPtr objectHandle); + + [DllImport("gdi32")] + static extern bool SelectClipRgn(IntPtr hdc, IntPtr hrgn); +#endregion + } +} diff --git a/source/ShiftUI/Internal/Theme.cs b/source/ShiftUI/Internal/Theme.cs new file mode 100644 index 0000000..4f1eac6 --- /dev/null +++ b/source/ShiftUI/Internal/Theme.cs @@ -0,0 +1,1063 @@ +// 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-2005 Novell, Inc. +// +// Authors: +// Jordi Mas i Hernandez, [email protected] +// Peter Dennis Bartok, [email protected] +// + +using System.Collections; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Reflection; +using System; + +namespace ShiftUI +{ + internal enum UIIcon { + PlacesRecentDocuments, + PlacesDesktop, + PlacesPersonal, + PlacesMyComputer, + PlacesMyNetwork, + MessageBoxError, + MessageBoxQuestion, + MessageBoxWarning, + MessageBoxInfo, + + NormalFolder + } + + internal struct CPColor { + internal Color Dark; + internal Color DarkDark; + internal Color Light; + internal Color LightLight; + + internal static CPColor Empty; + } + + // Implements a pool of system resources + internal class SystemResPool + { + private Hashtable pens = new Hashtable (); + private Hashtable dashpens = new Hashtable (); + private Hashtable sizedpens = new Hashtable (); + private Hashtable solidbrushes = new Hashtable (); + private Hashtable hatchbrushes = new Hashtable (); + private Hashtable uiImages = new Hashtable(); + private Hashtable cpcolors = new Hashtable (); + + public SystemResPool () {} + + public Pen GetPen (Color color) + { + int hash = color.ToArgb (); + + lock (pens) { + Pen res = pens [hash] as Pen; + if (res != null) + return res; + + Pen pen = new Pen (color); + pens.Add (hash, pen); + return pen; + } + } + + public Pen GetDashPen (Color color, DashStyle dashStyle) + { + string hash = color.ToString() + dashStyle; + + lock (dashpens) { + Pen res = dashpens [hash] as Pen; + if (res != null) + return res; + + Pen pen = new Pen (color); + pen.DashStyle = dashStyle; + dashpens [hash] = pen; + return pen; + } + } + + public Pen GetSizedPen (Color color, int size) + { + string hash = color.ToString () + size; + + lock (sizedpens) { + Pen res = sizedpens [hash] as Pen; + if (res != null) + return res; + + Pen pen = new Pen (color, size); + sizedpens [hash] = pen; + return pen; + } + } + + public SolidBrush GetSolidBrush (Color color) + { + int hash = color.ToArgb (); + + lock (solidbrushes) { + SolidBrush res = solidbrushes [hash] as SolidBrush; + if (res != null) + return res; + + SolidBrush brush = new SolidBrush (color); + solidbrushes.Add (hash, brush); + return brush; + } + } + + public HatchBrush GetHatchBrush (HatchStyle hatchStyle, Color foreColor, Color backColor) + { + string hash = ((int)hatchStyle).ToString () + foreColor.ToString () + backColor.ToString (); + + lock (hatchbrushes) { + HatchBrush brush = (HatchBrush) hatchbrushes[hash]; + if (brush == null) { + brush = new HatchBrush (hatchStyle, foreColor, backColor); + hatchbrushes.Add (hash, brush); + } + return brush; + } + } + + public void AddUIImage (Image image, string name, int size) + { + string hash = name + size.ToString(); + + lock (uiImages) { + if (uiImages.Contains (hash)) + return; + uiImages.Add (hash, image); + } + } + + public Image GetUIImage(string name, int size) + { + string hash = name + size.ToString(); + + Image image = uiImages [hash] as Image; + + return image; + } + + public CPColor GetCPColor (Color color) + { + lock (cpcolors) { + object tmp = cpcolors [color]; + + if (tmp == null) { + CPColor cpcolor = new CPColor (); + cpcolor.Dark = WidgetPaint.Dark (color); + cpcolor.DarkDark = WidgetPaint.DarkDark (color); + cpcolor.Light = WidgetPaint.Light (color); + cpcolor.LightLight = WidgetPaint.LightLight (color); + + cpcolors.Add (color, cpcolor); + + return cpcolor; + } + + return (CPColor)tmp; + } + } + } + + internal abstract class Theme + { + protected Array syscolors; + Font default_font; + protected Color defaultWindowBackColor; + protected Color defaultWindowForeColor; + internal SystemResPool ResPool = new SystemResPool (); + private MethodInfo update; + + protected Theme () + { + } + + private void SetSystemColors (KnownColor kc, Color value) + { + if (update == null) { + Type known_colors = Type.GetType ("System.Drawing.KnownColors, " + Consts.AssemblySystem_Drawing); + if (known_colors != null) + update = known_colors.GetMethod ("Update", BindingFlags.Static | BindingFlags.Public); + } + if (update != null) + update.Invoke (null, new object [2] { (int)kc, value.ToArgb () }); + } + + /* OS Feature support */ + public abstract Version Version { + get; + } + + /* Default properties */ + public virtual Color ColorScrollBar { + get { return SystemColors.ScrollBar; } + set { SetSystemColors (KnownColor.ScrollBar, value); } + } + + public virtual Color ColorDesktop { + get { return SystemColors.Desktop;} + set { SetSystemColors (KnownColor.Desktop, value); } + } + + public virtual Color ColorActiveCaption { + get { return SystemColors.ActiveCaption;} + set { SetSystemColors (KnownColor.ActiveCaption, value); } + } + + public virtual Color ColorInactiveCaption { + get { return SystemColors.InactiveCaption;} + set { SetSystemColors (KnownColor.InactiveCaption, value); } + } + + public virtual Color ColorMenu { + get { return SystemColors.Menu;} + set { SetSystemColors (KnownColor.Menu, value); } + } + + public virtual Color ColorWindow { + get { return SystemColors.Window;} + set { SetSystemColors (KnownColor.Window, value); } + } + + public virtual Color ColorWindowFrame { + get { return SystemColors.WindowFrame;} + set { SetSystemColors (KnownColor.WindowFrame, value); } + } + + public virtual Color ColorMenuText { + get { return SystemColors.MenuText;} + set { SetSystemColors (KnownColor.MenuText, value); } + } + + public virtual Color ColorWindowText { + get { return SystemColors.WindowText;} + set { SetSystemColors (KnownColor.WindowText, value); } + } + + public virtual Color ColorActiveCaptionText { + get { return SystemColors.ActiveCaptionText;} + set { SetSystemColors (KnownColor.ActiveCaptionText, value); } + } + + public virtual Color ColorActiveBorder { + get { return SystemColors.ActiveBorder;} + set { SetSystemColors (KnownColor.ActiveBorder, value); } + } + + public virtual Color ColorInactiveBorder{ + get { return SystemColors.InactiveBorder;} + set { SetSystemColors (KnownColor.InactiveBorder, value); } + } + + public virtual Color ColorAppWorkspace { + get { return SystemColors.AppWorkspace;} + set { SetSystemColors (KnownColor.AppWorkspace, value); } + } + + public virtual Color ColorHighlight { + get { return SystemColors.Highlight;} + set { SetSystemColors (KnownColor.Highlight, value); } + } + + public virtual Color ColorHighlightText { + get { return SystemColors.HighlightText;} + set { SetSystemColors (KnownColor.HighlightText, value); } + } + + public virtual Color ColorControl { + get { return SystemColors.Control; } + set { SetSystemColors (KnownColor.Control, value); } + } + + public virtual Color ColorControlDark { + get { return SystemColors.ControlDark;} + set { SetSystemColors (KnownColor.ControlDark, value); } + } + + public virtual Color ColorGrayText { + get { return SystemColors.GrayText;} + set { SetSystemColors (KnownColor.GrayText, value); } + } + + public virtual Color ColorControlText { + get { return SystemColors.ControlText;} + set { SetSystemColors (KnownColor.ControlText, value); } + } + + public virtual Color ColorInactiveCaptionText { + get { return SystemColors.InactiveCaptionText;} + set { SetSystemColors (KnownColor.InactiveCaptionText, value); } + } + + public virtual Color ColorControlLight { + get { return SystemColors.ControlLight;} + set { SetSystemColors (KnownColor.ControlLight, value); } + } + + public virtual Color ColorControlDarkDark { + get { return SystemColors.ControlDarkDark;} + set { SetSystemColors (KnownColor.ControlDarkDark, value); } + } + + public virtual Color ColorControlLightLight { + get { return SystemColors.ControlLightLight;} + set { SetSystemColors (KnownColor.ControlLightLight, value); } + } + + public virtual Color ColorInfoText { + get { return SystemColors.InfoText;} + set { SetSystemColors (KnownColor.InfoText, value); } + } + + public virtual Color ColorInfo { + get { return SystemColors.Info;} + set { SetSystemColors (KnownColor.Info, value); } + } + + public virtual Color ColorHotTrack { + get { return SystemColors.HotTrack;} + set { SetSystemColors (KnownColor.HotTrack, value);} + } + + public virtual Color DefaultControlBackColor { + get { return ColorControl; } + set { ColorControl = value; } + } + + public virtual Color DefaultControlForeColor { + get { return ColorControlText; } + set { ColorControlText = value; } + } + + public virtual Font DefaultFont { + get { return default_font ?? (default_font = SystemFonts.DefaultFont); } + } + + public virtual Color DefaultWindowBackColor { + get { return defaultWindowBackColor; } + } + + public virtual Color DefaultWindowForeColor { + get { return defaultWindowForeColor; } + } + + public virtual Color GetColor (XplatUIWin32.GetSysColorIndex idx) + { + return (Color) syscolors.GetValue ((int)idx); + } + + public virtual void SetColor (XplatUIWin32.GetSysColorIndex idx, Color color) + { + syscolors.SetValue (color, (int) idx); + } + + // Theme/UI specific defaults + public virtual ArrangeDirection ArrangeDirection { + get { + return ArrangeDirection.Down; + } + } + + public virtual ArrangeStartingPosition ArrangeStartingPosition { + get { + return ArrangeStartingPosition.BottomLeft; + } + } + + public virtual int BorderMultiplierFactor { get { return 1; } } + + public virtual Size BorderSizableSize { + get { + return new Size (3, 3); + } + } + + public virtual Size Border3DSize { + get { + return XplatUI.Border3DSize; + } + } + + public virtual Size BorderStaticSize { + get { + return new Size(1, 1); + } + } + + public virtual Size BorderSize { + get { + return XplatUI.BorderSize; + } + } + + public virtual Size CaptionButtonSize { + get { + return XplatUI.CaptionButtonSize; + } + } + + public virtual int CaptionHeight { + get { + return XplatUI.CaptionHeight; + } + } + + public virtual Size DoubleClickSize { + get { + return new Size(4, 4); + } + } + + public virtual int DoubleClickTime { + get { + return XplatUI.DoubleClickTime; + } + } + + public virtual Size FixedFrameBorderSize { + get { + return XplatUI.FixedFrameBorderSize; + } + } + + public virtual Size FrameBorderSize { + get { + return XplatUI.FrameBorderSize; + } + } + + public virtual int HorizontalFocusThickness { get { return 1; } } + + public virtual int HorizontalScrollBarArrowWidth { + get { + return 16; + } + } + + public virtual int HorizontalScrollBarHeight { + get { + return 16; + } + } + + public virtual int HorizontalScrollBarThumbWidth { + get { + return 16; + } + } + + public virtual Size IconSpacingSize { + get { + return new Size(75, 75); + } + } + + public virtual bool MenuAccessKeysUnderlined { + get { + try { + return XplatUI.MenuAccessKeysUnderlined; + } + catch(Exception ex) { + Console.WriteLine("[ShiftUI] XplatUI error: {0}\r\nInnerException: {1}", ex.Message, ex.InnerException.Message); + return false; + } + } + } + + public virtual Size MenuBarButtonSize { + get { return XplatUI.MenuBarButtonSize; } + } + + public virtual Size MenuButtonSize { + get { + return XplatUI.MenuButtonSize; + } + } + + public virtual Size MenuCheckSize { + get { + return new Size(13, 13); + } + } + + public virtual Font MenuFont { + get { + return default_font ?? (default_font = SystemFonts.DefaultFont); + } + } + + public virtual int MenuHeight { + get { + return XplatUI.MenuHeight; + } + } + + public virtual int MouseWheelScrollLines { + get { + return 3; + } + } + + public virtual bool RightAlignedMenus { + get { + return false; + } + } + + public virtual Size ToolWindowCaptionButtonSize { + get { + return XplatUI.ToolWindowCaptionButtonSize; + } + } + + public virtual int ToolWindowCaptionHeight { + get { + return XplatUI.ToolWindowCaptionHeight; + } + } + + public virtual int VerticalFocusThickness { get { return 1; } } + + public virtual int VerticalScrollBarArrowHeight { + get { + return 16; + } + } + + public virtual int VerticalScrollBarThumbHeight { + get { + return 16; + } + } + + public virtual int VerticalScrollBarWidth { + get { + return 16; + } + } + + public abstract Font WindowBorderFont { + get; + } + + public int Clamp (int value, int lower, int upper) + { + if (value < lower) return lower; + else if (value > upper) return upper; + else return value; + } + + [MonoInternalNote ("Figure out where to point for My Network Places")] + public virtual string Places(UIIcon index) { + switch (index) { + case UIIcon.PlacesRecentDocuments: { + // Default = "Recent Documents" + return Environment.GetFolderPath(Environment.SpecialFolder.Recent); + } + + case UIIcon.PlacesDesktop: { + // Default = "Desktop" + return Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory); + } + + case UIIcon.PlacesPersonal: { + // Default = "My Documents" + return Environment.GetFolderPath(Environment.SpecialFolder.Personal); + } + + case UIIcon.PlacesMyComputer: { + // Default = "My Computer" + return Environment.GetFolderPath(Environment.SpecialFolder.MyComputer); + } + + case UIIcon.PlacesMyNetwork: { + // Default = "My Network Places" + return "/tmp"; + } + + default: { + throw new ArgumentOutOfRangeException("index", index, "Unsupported place"); + } + } + } + + // + // This routine fetches images embedded as assembly resources (not + // resgen resources). It optionally scales the image to fit the + // specified size x dimension (it adjusts y automatically to fit that). + // + private Image GetSizedResourceImage(string name, int width) + { + Image image = ResPool.GetUIImage (name, width); + if (image != null) + return image; + + string fullname; + + if (width > 0) { + // Try name_width + fullname = String.Format("{1}_{0}", name, width); + if (image != null){ + ResPool.AddUIImage (image, name, width); + return image; + } + } + + // Just try name + if (image == null) + return null; + + ResPool.AddUIImage (image, name, 0); + if (image.Width != width && width != 0){ + Console.Error.WriteLine ("warning: requesting icon that not been tuned {0}_{1} {2}", width, name, image.Width); + int height = (image.Height * width)/image.Width; + Bitmap b = new Bitmap (width, height); + using (Graphics g = Graphics.FromImage (b)) + g.DrawImage (image, 0, 0, width, height); + ResPool.AddUIImage (b, name, width); + + return b; + } + return image; + } + + public virtual Image Images(UIIcon index) { + return Images(index, 0); + } + + public virtual Image Images(UIIcon index, int size) { + switch (index) { + case UIIcon.PlacesRecentDocuments: + return GetSizedResourceImage ("document-open.png", size); + case UIIcon.PlacesDesktop: + return GetSizedResourceImage ("user-desktop.png", size); + case UIIcon.PlacesPersonal: + return GetSizedResourceImage ("user-home.png", size); + case UIIcon.PlacesMyComputer: + return GetSizedResourceImage ("computer.png", size); + case UIIcon.PlacesMyNetwork: + return GetSizedResourceImage ("folder-remote.png", size); + + // Icons for message boxes + case UIIcon.MessageBoxError: + return GetSizedResourceImage ("dialog-error.png", size); + case UIIcon.MessageBoxInfo: + return GetSizedResourceImage ("dialog-information.png", size); + case UIIcon.MessageBoxQuestion: + return GetSizedResourceImage ("dialog-question.png", size); + case UIIcon.MessageBoxWarning: + return GetSizedResourceImage ("dialog-warning.png", size); + + // misc Icons + case UIIcon.NormalFolder: + return GetSizedResourceImage ("folder.png", size); + + default: { + throw new ArgumentException("Invalid Icon type requested", "index"); + } + } + } + + public virtual Image Images(string mimetype, string extension, int size) { + return null; + } + + #region Principal Theme Methods + // To let the theme now that a change of defaults (colors, etc) was detected and force a re-read (and possible recreation of cached resources) + public abstract void ResetDefaults(); + + // If the theme writes directly to a window instead of a device context + public abstract bool DoubleBufferingSupported {get;} + #endregion // Principal Theme Methods + + #region OwnerDraw Support + public abstract void DrawOwnerDrawBackground (DrawItemEventArgs e); + public abstract void DrawOwnerDrawFocusRectangle (DrawItemEventArgs e); + #endregion // OwnerDraw Support + + #region Button + public abstract Size CalculateButtonAutoSize (Button button); + public abstract void CalculateButtonTextAndImageLayout (Graphics g, ButtonBase b, out Rectangle textRectangle, out Rectangle imageRectangle); + public abstract void DrawButton (Graphics g, Button b, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle); + public abstract void DrawFlatButton (Graphics g, ButtonBase b, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle); + public abstract void DrawPopupButton (Graphics g, Button b, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle); + #endregion // Button + + #region ButtonBase + // Drawing + public abstract void DrawButtonBase(Graphics dc, Rectangle clip_area, ButtonBase button); + + // Sizing + public abstract Size ButtonBaseDefaultSize{get;} + #endregion // ButtonBase + + #region CheckBox + public abstract Size CalculateCheckBoxAutoSize (CheckBox checkBox); + public abstract void CalculateCheckBoxTextAndImageLayout (ButtonBase b, Point offset, out Rectangle glyphArea, out Rectangle textRectangle, out Rectangle imageRectangle); + public abstract void DrawCheckBox (Graphics g, CheckBox cb, Rectangle glyphArea, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle); + public abstract void DrawCheckBox (Graphics dc, Rectangle clip_area, CheckBox checkbox); + + #endregion // CheckBox + + #region CheckedListBox + // Drawing + public abstract void DrawCheckedListBoxItem (CheckedListBox ctrl, DrawItemEventArgs e); + #endregion // CheckedListBox + + #region ComboBox + // Drawing + public abstract void DrawComboBoxItem (ComboBox ctrl, DrawItemEventArgs e); + public abstract void DrawFlatStyleComboButton (Graphics graphics, Rectangle rectangle, ButtonState state); + public abstract void ComboBoxDrawNormalDropDownButton (ComboBox comboBox, Graphics g, Rectangle clippingArea, Rectangle area, ButtonState state); + public abstract bool ComboBoxNormalDropDownButtonHasTransparentBackground (ComboBox comboBox, ButtonState state); + public abstract bool ComboBoxDropDownButtonHasHotElementStyle (ComboBox comboBox); + public abstract void ComboBoxDrawBackground (ComboBox comboBox, Graphics g, Rectangle clippingArea, FlatStyle style); + public abstract bool CombBoxBackgroundHasHotElementStyle (ComboBox comboBox); + #endregion // ComboBox + + #region Widget + public abstract Font GetLinkFont (Widget control); + #endregion // Widget + + + /* FIXME: I really don't feel like implementing DataGrids. Too much code. + #region Datagrid + public abstract int DataGridPreferredColumnWidth { get; } + public abstract int DataGridMinimumColumnCheckBoxHeight { get; } + public abstract int DataGridMinimumColumnCheckBoxWidth { get; } + + // Default colours + public abstract Color DataGridAlternatingBackColor { get; } + public abstract Color DataGridBackColor { get; } + public abstract Color DataGridBackgroundColor { get; } + public abstract Color DataGridCaptionBackColor { get; } + public abstract Color DataGridCaptionForeColor { get; } + public abstract Color DataGridGridLineColor { get; } + public abstract Color DataGridHeaderBackColor { get; } + public abstract Color DataGridHeaderForeColor { get; } + public abstract Color DataGridLinkColor { get; } + public abstract Color DataGridLinkHoverColor { get; } + public abstract Color DataGridParentRowsBackColor { get; } + public abstract Color DataGridParentRowsForeColor { get; } + public abstract Color DataGridSelectionBackColor { get; } + public abstract Color DataGridSelectionForeColor { get; } + // Paint + public abstract void DataGridPaint (PaintEventArgs pe, DataGrid grid); + public abstract void DataGridPaintCaption (Graphics g, Rectangle clip, DataGrid grid); + public abstract void DataGridPaintColumnHeaders (Graphics g, Rectangle clip, DataGrid grid); + public abstract void DataGridPaintColumnHeader (Graphics g, Rectangle bounds, DataGrid grid, int col); + public abstract void DataGridPaintRowContents (Graphics g, int row, Rectangle row_rect, bool is_newrow, Rectangle clip, DataGrid grid); + public abstract void DataGridPaintRowHeader (Graphics g, Rectangle bounds, int row, DataGrid grid); + public abstract void DataGridPaintRowHeaderArrow (Graphics g, Rectangle bounds, DataGrid grid); + public abstract void DataGridPaintRowHeaderStar (Graphics g, Rectangle bounds, DataGrid grid); + public abstract void DataGridPaintParentRows (Graphics g, Rectangle bounds, DataGrid grid); + public abstract void DataGridPaintParentRow (Graphics g, Rectangle bounds, DataGridDataSource row, DataGrid grid); + public abstract void DataGridPaintRows (Graphics g, Rectangle cells, Rectangle clip, DataGrid grid); + public abstract void DataGridPaintRow (Graphics g, int row, Rectangle row_rect, bool is_newrow, Rectangle clip, DataGrid grid); + public abstract void DataGridPaintRelationRow (Graphics g, int row, Rectangle row_rect, bool is_newrow, Rectangle clip, DataGrid grid); + + #endregion // Datagrid + + #region DataGridView + #region DataGridViewHeaderCell + #region DataGridViewRowHeaderCell + public abstract bool DataGridViewRowHeaderCellDrawBackground (DataGridViewRowHeaderCell cell, Graphics g, Rectangle bounds); + public abstract bool DataGridViewRowHeaderCellDrawSelectionBackground (DataGridViewRowHeaderCell cell); + public abstract bool DataGridViewRowHeaderCellDrawBorder (DataGridViewRowHeaderCell cell, Graphics g, Rectangle bounds); + #endregion + #region DataGridViewColumnHeaderCell + public abstract bool DataGridViewColumnHeaderCellDrawBackground (DataGridViewColumnHeaderCell cell, Graphics g, Rectangle bounds); + public abstract bool DataGridViewColumnHeaderCellDrawBorder (DataGridViewColumnHeaderCell cell, Graphics g, Rectangle bounds); + #endregion + public abstract bool DataGridViewHeaderCellHasPressedStyle (DataGridView dataGridView); + public abstract bool DataGridViewHeaderCellHasHotStyle (DataGridView dataGridView); + #endregion + #endregion +*/ + + #region DateTimePicker + public abstract void DrawDateTimePicker(Graphics dc, Rectangle clip_rectangle, DateTimePicker dtp); + public abstract bool DateTimePickerBorderHasHotElementStyle { get; } + public abstract Rectangle DateTimePickerGetDropDownButtonArea (DateTimePicker dateTimePicker); + public abstract Rectangle DateTimePickerGetDateArea (DateTimePicker dateTimePicker); + public abstract bool DateTimePickerDropDownButtonHasHotElementStyle { get; } + #endregion // DateTimePicker + + #region GroupBox + // Drawing + public abstract void DrawGroupBox (Graphics dc, Rectangle clip_area, GroupBox box); + + // Sizing + public abstract Size GroupBoxDefaultSize{get;} + #endregion // GroupBox + + #region HScrollBar + public abstract Size HScrollBarDefaultSize{get;} // Default size of the scrollbar + #endregion // HScrollBar + + #region ListBox + // Drawing + public abstract void DrawListBoxItem (ListBox ctrl, DrawItemEventArgs e); + #endregion // ListBox + + #region ListView + // Drawing + public abstract void DrawListViewItems (Graphics dc, Rectangle clip_rectangle, ListView control); + public abstract void DrawListViewHeader (Graphics dc, Rectangle clip_rectangle, ListView control); + public abstract void DrawListViewHeaderDragDetails (Graphics dc, ListView control, ColumnHeader drag_column, int target_x); + public abstract bool ListViewHasHotHeaderStyle { get; } + + // Sizing + public abstract int ListViewGetHeaderHeight (ListView listView, Font font); + public abstract Size ListViewCheckBoxSize { get; } + public abstract int ListViewColumnHeaderHeight { get; } + public abstract int ListViewDefaultColumnWidth { get; } + public abstract int ListViewVerticalSpacing { get; } + public abstract int ListViewEmptyColumnWidth { get; } + public abstract int ListViewHorizontalSpacing { get; } + public abstract Size ListViewDefaultSize { get; } + public abstract int ListViewGroupHeight { get; } + public abstract int ListViewItemPaddingWidth { get; } + public abstract int ListViewTileWidthFactor { get; } + public abstract int ListViewTileHeightFactor { get; } + #endregion // ListView + + #region Menus + public abstract void CalcItemSize (Graphics dc, MenuItem item, int y, int x, bool menuBar); + public abstract void CalcPopupMenuSize (Graphics dc, Menu menu); + public abstract int CalcMenuBarSize (Graphics dc, Menu menu, int width); + public abstract void DrawMenuBar (Graphics dc, Menu menu, Rectangle rect); + public abstract void DrawMenuItem (MenuItem item, DrawItemEventArgs e); + public abstract void DrawPopupMenu (Graphics dc, Menu menu, Rectangle cliparea, Rectangle rect); + #endregion // Menus + + #region MonthCalendar + public abstract void DrawMonthCalendar(Graphics dc, Rectangle clip_rectangle, MonthCalendar month_calendar); + #endregion // MonthCalendar + + #region Panel + // Sizing + public abstract Size PanelDefaultSize{get;} + #endregion // Panel + + #region PictureBox + // Drawing + public abstract void DrawPictureBox (Graphics dc, Rectangle clip, PictureBox pb); + + // Sizing + public abstract Size PictureBoxDefaultSize{get;} + #endregion // PictureBox + + /* FIXME: No printing. + #region PrintPreviewControl + public abstract int PrintPreviewControlPadding{get;} + public abstract Size PrintPreviewControlGetPageSize (PrintPreviewControl preview); + public abstract void PrintPreviewWidgetPaint (PaintEventArgs pe, PrintPreviewControl preview, Size page_image_size); + #endregion // PrintPreviewControl +*/ + #region ProgressBar + // Drawing + public abstract void DrawProgressBar (Graphics dc, Rectangle clip_rectangle, ProgressBar progress_bar); + + // Sizing + public abstract Size ProgressBarDefaultSize{get;} + #endregion // ProgressBar + + #region RadioButton + // Drawing + public abstract Size CalculateRadioButtonAutoSize (RadioButton rb); + public abstract void CalculateRadioButtonTextAndImageLayout (ButtonBase b, Point offset, out Rectangle glyphArea, out Rectangle textRectangle, out Rectangle imageRectangle); + public abstract void DrawRadioButton (Graphics g, RadioButton rb, Rectangle glyphArea, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle); + public abstract void DrawRadioButton (Graphics dc, Rectangle clip_rectangle, RadioButton radio_button); + + // Sizing + public abstract Size RadioButtonDefaultSize{get;} + #endregion // RadioButton + + #region ScrollBar + // Drawing + //public abstract void DrawScrollBar (Graphics dc, Rectangle area, ScrollBar bar, ref Rectangle thumb_pos, ref Rectangle first_arrow_area, ref Rectangle second_arrow_area, ButtonState first_arrow, ButtonState second_arrow, ref int scrollbutton_width, ref int scrollbutton_height, bool vert); + public abstract void DrawScrollBar (Graphics dc, Rectangle clip_rectangle, ScrollBar bar); + + // Sizing + public abstract int ScrollBarButtonSize {get;} // Size of the scroll button + + public abstract bool ScrollBarHasHotElementStyles { get; } + + public abstract bool ScrollBarHasPressedThumbStyle { get; } + + public abstract bool ScrollBarHasHoverArrowButtonStyle { get; } + #endregion // ScrollBar + + #region StatusBar + // Drawing + public abstract void DrawStatusBar (Graphics dc, Rectangle clip_rectangle, StatusBar sb); + + // Sizing + public abstract int StatusBarSizeGripWidth {get;} // Size of Resize area + public abstract int StatusBarHorzGapWidth {get;} // Gap between panels + public abstract Size StatusBarDefaultSize{get;} + #endregion // StatusBar + + #region TabControl + public abstract Size TabControlDefaultItemSize {get; } + public abstract Point TabControlDefaultPadding {get; } + public abstract int TabControlMinimumTabWidth {get; } + public abstract Rectangle TabWidgetselectedDelta { get; } + public abstract int TabWidgetselectedSpacing { get; } + public abstract int TabPanelOffsetX { get; } + public abstract int TabPanelOffsetY { get; } + public abstract int TabControlColSpacing { get; } + public abstract Point TabControlImagePadding { get; } + public abstract int TabWidgetscrollerWidth { get; } + + public abstract Rectangle TabControlGetDisplayRectangle (TabWidget tab); + public abstract Rectangle TabControlGetPanelRect (TabWidget tab); + public abstract Size TabControlGetSpacing (TabWidget tab); + public abstract void DrawTabControl (Graphics dc, Rectangle area, TabWidget tab); + #endregion + + #region TextBoxBase + public abstract void TextBoxBaseFillBackground (TextBoxBase textBoxBase, Graphics g, Rectangle clippingArea); + public abstract bool TextBoxBaseHandleWmNcPaint (TextBoxBase textBoxBase, ref Message m); + public abstract bool TextBoxBaseShouldPaintBackground (TextBoxBase textBoxBase); + #endregion + + /*#region ToolBar + // Drawing + public abstract void DrawToolBar (Graphics dc, Rectangle clip_rectangle, ToolBar control); + + // Sizing + public abstract int ToolBarGripWidth {get;} // Grip width for the ToolBar + public abstract int ToolBarImageGripWidth {get;} // Grip width for the Image on the ToolBarButton + public abstract int ToolBarSeparatorWidth {get;} // width of the separator + public abstract int ToolBarDropDownWidth { get; } // width of the dropdown arrow rect + public abstract int ToolBarDropDownArrowWidth { get; } // width for the dropdown arrow on the ToolBarButton + public abstract int ToolBarDropDownArrowHeight { get; } // height for the dropdown arrow on the ToolBarButton + public abstract Size ToolBarDefaultSize{get;} + + public abstract bool ToolBarHasHotElementStyles (ToolBar toolBar); + public abstract bool ToolBarHasHotCheckedElementStyles { get; } + #endregion // ToolBar +*/ + + #region ToolTip + public abstract void DrawToolTip(Graphics dc, Rectangle clip_rectangle, ToolTip.ToolTipWindow control); + public abstract Size ToolTipSize(ToolTip.ToolTipWindow tt, string text); + public abstract bool ToolTipTransparentBackground { get; } + #endregion // ToolTip + + #region BalloonWindow + public abstract void ShowBalloonWindow (IntPtr handle, int timeout, string title, string text, ToolTipIcon icon); + public abstract void HideBalloonWindow (IntPtr handle); + public abstract void DrawBalloonWindow (Graphics dc, Rectangle clip, NotifyIcon.BalloonWindow control); + public abstract Rectangle BalloonWindowRect (NotifyIcon.BalloonWindow control); + #endregion // BalloonWindow + + #region TrackBar + // Drawing + public abstract void DrawTrackBar (Graphics dc, Rectangle clip_rectangle, TrackBar tb); + + // Sizing + public abstract Size TrackBarDefaultSize{get; } // Default size for the TrackBar control + + public abstract int TrackBarValueFromMousePosition (int x, int y, TrackBar tb); + + public abstract bool TrackBarHasHotThumbStyle { get; } + #endregion // TrackBar + + #region UpDownBase + public abstract void UpDownBaseDrawButton (Graphics g, Rectangle bounds, bool top, VisualStyles.PushButtonState state); + public abstract bool UpDownBaseHasHotButtonStyle { get; } + #endregion + + #region VScrollBar + public abstract Size VScrollBarDefaultSize{get;} // Default size of the scrollbar + #endregion // VScrollBar + + #region TreeView + public abstract Size TreeViewDefaultSize { get; } + public abstract void TreeViewDrawNodePlusMinus (TreeView treeView, TreeNode node, Graphics dc, int x, int middle); + #endregion + + #region Managed window + public abstract void DrawManagedWindowDecorations (Graphics dc, Rectangle clip, InternalWindowManager wm); + public abstract int ManagedWindowTitleBarHeight (InternalWindowManager wm); + public abstract int ManagedWindowBorderWidth (InternalWindowManager wm); + public abstract int ManagedWindowIconWidth (InternalWindowManager wm); + public abstract Size ManagedWindowButtonSize (InternalWindowManager wm); + public abstract void ManagedWindowSetButtonLocations (InternalWindowManager wm); + public abstract Rectangle ManagedWindowGetTitleBarIconArea (InternalWindowManager wm); + public abstract Size ManagedWindowGetMenuButtonSize (InternalWindowManager wm); + public abstract bool ManagedWindowTitleButtonHasHotElementStyle (TitleButton button, Form form); + public abstract void ManagedWindowDrawMenuButton (Graphics dc, TitleButton button, Rectangle clip, InternalWindowManager wm); + public abstract void ManagedWindowOnSizeInitializedOrChanged (Form form); + public const int ManagedWindowSpacingAfterLastTitleButton = 2; + #endregion + + #region WidgetPaint Methods + public abstract void CPDrawBorder (Graphics graphics, Rectangle bounds, Color leftColor, int leftWidth, + ButtonBorderStyle leftStyle, Color topColor, int topWidth, ButtonBorderStyle topStyle, + Color rightColor, int rightWidth, ButtonBorderStyle rightStyle, Color bottomColor, + int bottomWidth, ButtonBorderStyle bottomStyle); + + public abstract void CPDrawBorder (Graphics graphics, RectangleF bounds, Color leftColor, int leftWidth, + ButtonBorderStyle leftStyle, Color topColor, int topWidth, ButtonBorderStyle topStyle, + Color rightColor, int rightWidth, ButtonBorderStyle rightStyle, Color bottomColor, + int bottomWidth, ButtonBorderStyle bottomStyle); + + public abstract void CPDrawBorder3D (Graphics graphics, Rectangle rectangle, Border3DStyle style, Border3DSide sides); + public abstract void CPDrawBorder3D (Graphics graphics, Rectangle rectangle, Border3DStyle style, Border3DSide sides, Color control_color); + public abstract void CPDrawButton (Graphics graphics, Rectangle rectangle, ButtonState state); + public abstract void CPDrawCaptionButton (Graphics graphics, Rectangle rectangle, CaptionButton button, ButtonState state); + public abstract void CPDrawCheckBox (Graphics graphics, Rectangle rectangle, ButtonState state); + public abstract void CPDrawComboButton (Graphics graphics, Rectangle rectangle, ButtonState state); + public abstract void CPDrawContainerGrabHandle (Graphics graphics, Rectangle bounds); + public abstract void CPDrawFocusRectangle (Graphics graphics, Rectangle rectangle, Color foreColor, Color backColor); + public abstract void CPDrawGrabHandle (Graphics graphics, Rectangle rectangle, bool primary, bool enabled); + public abstract void CPDrawGrid (Graphics graphics, Rectangle area, Size pixelsBetweenDots, Color backColor); + public abstract void CPDrawImageDisabled (Graphics graphics, Image image, int x, int y, Color background); + public abstract void CPDrawLockedFrame (Graphics graphics, Rectangle rectangle, bool primary); + public abstract void CPDrawMenuGlyph (Graphics graphics, Rectangle rectangle, MenuGlyph glyph, Color color, Color backColor); + public abstract void CPDrawMixedCheckBox (Graphics graphics, Rectangle rectangle, ButtonState state); + public abstract void CPDrawRadioButton (Graphics graphics, Rectangle rectangle, ButtonState state); + public abstract void CPDrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style); + public abstract void CPDrawReversibleLine (Point start, Point end, Color backColor); + public abstract void CPDrawScrollButton (Graphics graphics, Rectangle rectangle, ScrollButton button, ButtonState state); + public abstract void CPDrawSelectionFrame (Graphics graphics, bool active, Rectangle outsideRect, Rectangle insideRect, + Color backColor); + public abstract void CPDrawSizeGrip (Graphics graphics, Color backColor, Rectangle bounds); + public abstract void CPDrawStringDisabled (Graphics graphics, string s, Font font, Color color, RectangleF layoutRectangle, + StringFormat format); + public abstract void CPDrawStringDisabled (IDeviceContext dc, string s, Font font, Color color, Rectangle layoutRectangle, TextFormatFlags format); + public abstract void CPDrawVisualStyleBorder (Graphics graphics, Rectangle bounds); + public abstract void CPDrawBorderStyle (Graphics dc, Rectangle area, BorderStyle border_style); + #endregion // WidgetPaint Methods + } +} diff --git a/source/ShiftUI/Internal/Timer.cs b/source/ShiftUI/Internal/Timer.cs new file mode 100644 index 0000000..e51c40d --- /dev/null +++ b/source/ShiftUI/Internal/Timer.cs @@ -0,0 +1,170 @@ +// 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 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + + +using System; +using System.Threading; +using System.ComponentModel; + +namespace ShiftUI { + [DefaultProperty("Interval")] + [DefaultEvent("Tick")] + [ToolboxItemFilter("ShiftUI", ToolboxItemFilterType.Allow)] + public class Timer : Component { + + private bool enabled; + private int interval = 100; + private DateTime expires; + internal Thread thread; + internal bool Busy; + internal IntPtr window; + object Widget_tag; + + internal static readonly int Minimum = 15; + + public Timer () + { + enabled = false; + } + + public Timer (IContainer container) : this () + { + container.Add (this); + } + + [DefaultValue (false)] + public virtual bool Enabled { + get { + return enabled; + } + set { + if (value != enabled) { + enabled = value; + if (value) { + // Use AddTicks so we get some rounding + expires = DateTime.UtcNow.AddMilliseconds (interval > Minimum ? interval : Minimum); + + thread = Thread.CurrentThread; + XplatUI.SetTimer (this); + } else { + XplatUI.KillTimer (this); + thread = null; + } + } + } + } + + [DefaultValue (100)] + public int Interval { + get { + return interval; + } + set { + if (value <= 0) + throw new ArgumentOutOfRangeException ("Interval", string.Format ("'{0}' is not a valid value for Interval. Interval must be greater than 0.", value)); + + if (interval == value) { + return; + } + + interval = value; + + // Use AddTicks so we get some rounding + expires = DateTime.UtcNow.AddMilliseconds (interval > Minimum ? interval : Minimum); + + if (enabled == true) { + XplatUI.KillTimer (this); + XplatUI.SetTimer (this); + } + } + } + + [Localizable(false)] + [Bindable(true)] + [TypeConverter(typeof(StringConverter))] + [DefaultValue(null)] + [MWFCategory("Data")] + public object Tag { + get { + return Widget_tag; + } + + set { + Widget_tag = value; + } + } + + public void Start () + { + Enabled = true; + } + + public void Stop () + { + Enabled = false; + } + + internal DateTime Expires { + get { + return expires; + } + } + + public event EventHandler Tick; + + public override string ToString () + { + return base.ToString () + ", Interval: " + Interval; + } + + internal void Update (DateTime update) + { + expires = update.AddMilliseconds (interval > Minimum ? interval : Minimum); + } + + internal void FireTick () + { + OnTick (EventArgs.Empty); + } + + + protected virtual void OnTick (EventArgs e) + { + if (Tick != null) + Tick (this, e); + } + + protected override void Dispose (bool disposing) + { + base.Dispose (disposing); + Enabled = false; + } + + internal void TickHandler (object sender, EventArgs e) + { + OnTick (e); + } + } +} + diff --git a/source/ShiftUI/Internal/ToolBarAppearance.cs b/source/ShiftUI/Internal/ToolBarAppearance.cs new file mode 100644 index 0000000..ef3e110 --- /dev/null +++ b/source/ShiftUI/Internal/ToolBarAppearance.cs @@ -0,0 +1,40 @@ +// +// ShiftUI.ToolBarAppearance.cs +// +// 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 Novell, Inc. +// +// Authors: +// Ravindra ([email protected]) +// +// + + +// COMPLETE + +namespace ShiftUI +{ + public enum ToolBarAppearance + { + Normal = 0, + Flat = 1 + } +} diff --git a/source/ShiftUI/Internal/ToolBarButtonStyle.cs b/source/ShiftUI/Internal/ToolBarButtonStyle.cs new file mode 100644 index 0000000..fffcd99 --- /dev/null +++ b/source/ShiftUI/Internal/ToolBarButtonStyle.cs @@ -0,0 +1,41 @@ +// +// ShiftUI.ToolBarButtonStyle.cs +// +// 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 Novell, Inc. +// +// Authors: +// Ravindra ([email protected]) +// + + +// COMPLETE + +namespace ShiftUI +{ + public enum ToolBarButtonStyle + { + PushButton = 1, + ToggleButton = 2, + Separator = 3, + DropDownButton =4 + } +} diff --git a/source/ShiftUI/Internal/ToolBarTextAlign.cs b/source/ShiftUI/Internal/ToolBarTextAlign.cs new file mode 100644 index 0000000..9850ca6 --- /dev/null +++ b/source/ShiftUI/Internal/ToolBarTextAlign.cs @@ -0,0 +1,39 @@ +// +// ShiftUI.ToolBarTextAlign.cs +// +// 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 Novell, Inc. +// +// Authors: +// Ravindra ([email protected]) +// + + +// COMPLETE + +namespace ShiftUI +{ + public enum ToolBarTextAlign + { + Underneath = 0, + Right = 1 + } +} diff --git a/source/ShiftUI/Internal/ToolTip.cs b/source/ShiftUI/Internal/ToolTip.cs new file mode 100644 index 0000000..24895fb --- /dev/null +++ b/source/ShiftUI/Internal/ToolTip.cs @@ -0,0 +1,1004 @@ +// 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 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok [email protected] +// +// + +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Text; +using ShiftUI; +using System; + +namespace ShiftUI { + [DefaultEvent ("Popup")] + [ProvideProperty ("ToolTip", typeof(Widget))] + [ToolboxItemFilter("ShiftUI", ToolboxItemFilterType.Allow)] + public class ToolTip : System.ComponentModel.Component, System.ComponentModel.IExtenderProvider { + #region Local variables + internal bool is_active; + internal int automatic_delay; + internal int autopop_delay; + internal int initial_delay; + internal int re_show_delay; + internal bool show_always; + + internal Color back_color; + internal Color fore_color; + + internal ToolTipWindow tooltip_window; // The actual tooltip window + internal Hashtable tooltip_strings; // List of strings for each Widget, indexed by Widget + internal ArrayList Widgets; + internal Widget active_Widget; // Widget for which the tooltip is currently displayed + internal Widget last_Widget; // last Widget the mouse was in + internal Timer timer; // Used for the various intervals + private Form hooked_form; + + private bool isBalloon; + private bool owner_draw; + private bool stripAmpersands; + private ToolTipIcon tool_tip_icon; + private bool useAnimation; + private bool useFading; + private object tag; + + #endregion // Local variables + + #region ToolTipWindow Class + internal class ToolTipWindow : Widget { + #region ToolTipWindow Class Local Variables + private Widget associated_Widget; + internal Icon icon; + internal string title = String.Empty; + internal Rectangle icon_rect; + internal Rectangle title_rect; + internal Rectangle text_rect; + #endregion // ToolTipWindow Class Local Variables + + #region ToolTipWindow Class Constructor + internal ToolTipWindow() { + Visible = false; + Size = new Size(100, 20); + ForeColor = ThemeEngine.Current.ColorInfoText; + BackColor = ThemeEngine.Current.ColorInfo; + + VisibleChanged += new EventHandler(ToolTipWindow_VisibleChanged); + + // UIA Framework: Used to generate UnPopup + VisibleChanged += new EventHandler (OnUIAToolTip_VisibleChanged); + + SetStyle (Widgetstyles.UserPaint | Widgetstyles.AllPaintingInWmPaint, true); + SetStyle (Widgetstyles.ResizeRedraw, true); + if (ThemeEngine.Current.ToolTipTransparentBackground) { + SetStyle (Widgetstyles.SupportsTransparentBackColor, true); + BackColor = Color.Transparent; + } else + SetStyle (Widgetstyles.Opaque, true); + } + + #endregion // ToolTipWindow Class Constructor + + #region ToolTipWindow Class Protected Instance Methods + protected override void OnCreateWidget() { + base.OnCreateWidget (); + XplatUI.SetTopmost(this.window.Handle, true); + } + + protected override CreateParams CreateParams { + get { + CreateParams cp; + + cp = base.CreateParams; + + cp.Style = (int)WindowStyles.WS_POPUP; + cp.Style |= (int)WindowStyles.WS_CLIPSIBLINGS; + + cp.ExStyle = (int)(WindowExStyles.WS_EX_TOOLWINDOW | WindowExStyles.WS_EX_TOPMOST); + + return cp; + } + } + + protected override void OnPaint(PaintEventArgs pevent) { + // We don't do double-buffering on purpose: + // 1) we'd have to meddle with is_visible, it destroys the buffers if !visible + // 2) We don't draw much, no need to double buffer + base.OnPaint(pevent); + + OnDraw (new DrawToolTipEventArgs (pevent.Graphics, associated_Widget, associated_Widget, ClientRectangle, this.Text, this.BackColor, this.ForeColor, this.Font)); + } + + protected override void OnTextChanged (EventArgs args) + { + Invalidate (); + base.OnTextChanged (args); + } + + protected override void WndProc(ref Message m) { + if (m.Msg == (int)Msg.WM_SETFOCUS) { + if (m.WParam != IntPtr.Zero) { + XplatUI.SetFocus(m.WParam); + } + } + base.WndProc (ref m); + } + + + #endregion // ToolTipWindow Class Protected Instance Methods + + #region ToolTipWindow Class Private Methods + internal virtual void OnDraw (DrawToolTipEventArgs e) + { + DrawToolTipEventHandler eh = (DrawToolTipEventHandler)(Events[DrawEvent]); + if (eh != null) + eh (this, e); + else + ThemeEngine.Current.DrawToolTip (e.Graphics, e.Bounds, this); + } + + internal virtual void OnPopup (PopupEventArgs e) + { + PopupEventHandler eh = (PopupEventHandler)(Events[PopupEvent]); + if (eh != null) + eh (this, e); + else + e.ToolTipSize = ThemeEngine.Current.ToolTipSize (this, Text); + } + + private void ToolTipWindow_VisibleChanged(object sender, EventArgs e) { + Widget Widget = (Widget)sender; + + if (Widget.is_visible) { + XplatUI.SetTopmost(Widget.window.Handle, true); + } else { + XplatUI.SetTopmost(Widget.window.Handle, false); + } + } + + // UIA Framework + private void OnUIAToolTip_VisibleChanged (object sender, EventArgs e) + { + if (Visible == false) + OnUnPopup (new PopupEventArgs (associated_Widget, associated_Widget, false, Size.Empty)); + } + + private void OnUnPopup (PopupEventArgs e) + { + PopupEventHandler eh = (PopupEventHandler) (Events [UnPopupEvent]); + if (eh != null) + eh (this, e); + } + + + #endregion // ToolTipWindow Class Protected Instance Methods + + #region Internal Properties + internal override bool ActivateOnShow { get { return false; } } + #endregion + + // This Present is used when we are using the expicit Show methods for 2.0. + // It will not reposition the window. + public void PresentModal (Widget Widget, string text) + { + if (IsDisposed) + return; + + Size display_size; + XplatUI.GetDisplaySize (out display_size); + + associated_Widget = Widget; + + Text = text; + + PopupEventArgs pea = new PopupEventArgs (Widget, Widget, false, Size.Empty); + OnPopup (pea); + + if (pea.Cancel) + return; + + Size = pea.ToolTipSize; + + Visible = true; + } + + public void Present (Widget Widget, string text) + { + if (IsDisposed) + return; + + Size display_size; + XplatUI.GetDisplaySize (out display_size); + + associated_Widget = Widget; + + Text = text; + + PopupEventArgs pea = new PopupEventArgs (Widget, Widget, false, Size.Empty); + OnPopup (pea); + + if (pea.Cancel) + return; + + Size size = pea.ToolTipSize; + + Width = size.Width; + Height = size.Height; + + int cursor_w, cursor_h, hot_x, hot_y; + XplatUI.GetCursorInfo (Widget.Cursor.Handle, out cursor_w, out cursor_h, out hot_x, out hot_y); + Point loc = Widget.MousePosition; + loc.Y += (cursor_h - hot_y); + + if ((loc.X + Width) > display_size.Width) + loc.X = display_size.Width - Width; + + if ((loc.Y + Height) > display_size.Height) + loc.Y = Widget.MousePosition.Y - Height - hot_y; + + Location = loc; + Visible = true; + BringToFront (); + } + + + #region Internal Events + static object DrawEvent = new object (); + static object PopupEvent = new object (); + + // UIA Framework + static object UnPopupEvent = new object (); + + public event DrawToolTipEventHandler Draw { + add { Events.AddHandler (DrawEvent, value); } + remove { Events.RemoveHandler (DrawEvent, value); } + } + + public event PopupEventHandler Popup { + add { Events.AddHandler (PopupEvent, value); } + remove { Events.RemoveHandler (PopupEvent, value); } + } + + internal event PopupEventHandler UnPopup { + add { Events.AddHandler (UnPopupEvent, value); } + remove { Events.RemoveHandler (UnPopupEvent, value); } + } + #endregion + } + #endregion // ToolTipWindow Class + + #region Public Constructors & Destructors + public ToolTip() { + + // Defaults from MS + is_active = true; + automatic_delay = 500; + autopop_delay = 5000; + initial_delay = 500; + re_show_delay = 100; + show_always = false; + back_color = SystemColors.Info; + fore_color = SystemColors.InfoText; + + isBalloon = false; + stripAmpersands = false; + useAnimation = true; + useFading = true; + tooltip_strings = new Hashtable(5); + Widgets = new ArrayList(5); + + tooltip_window = new ToolTipWindow(); + tooltip_window.MouseLeave += new EventHandler(Widget_MouseLeave); + tooltip_window.Draw += new DrawToolTipEventHandler (tooltip_window_Draw); + tooltip_window.Popup += new PopupEventHandler (tooltip_window_Popup); + + // UIA Framework: Static event handlers + tooltip_window.UnPopup += delegate (object sender, PopupEventArgs args) { + OnUnPopup (args); + }; + UnPopup += new PopupEventHandler (OnUIAUnPopup); + + timer = new Timer(); + timer.Enabled = false; + timer.Tick +=new EventHandler(timer_Tick); + + } + + + #region UIA Framework: Events, Delegates and Methods + // NOTE: + // We are using Reflection to add/remove internal events. + // Class ToolTipListener uses the events. + // + // - UIAUnPopup. Event used to generate ChildRemoved in ToolTip + // - UIAToolTipHookUp. Event used to keep track of associated Widgets + // - UIAToolTipUnhookUp. Event used to remove track of associated Widgets + static object UnPopupEvent = new object (); + + internal event PopupEventHandler UnPopup { + add { Events.AddHandler (UnPopupEvent, value); } + remove { Events.RemoveHandler (UnPopupEvent, value); } + } + + internal static event PopupEventHandler UIAUnPopup; + internal static event WidgetEventHandler UIAToolTipHookUp; + internal static event WidgetEventHandler UIAToolTipUnhookUp; + + internal Rectangle UIAToolTipRectangle { + get { return tooltip_window.Bounds; } + } + + internal static void OnUIAUnPopup (object sender, PopupEventArgs args) + { + if (UIAUnPopup != null) + UIAUnPopup (sender, args); + } + + internal static void OnUIAToolTipHookUp (object sender, WidgetEventArgs args) + { + if (UIAToolTipHookUp != null) + UIAToolTipHookUp (sender, args); + } + + internal static void OnUIAToolTipUnhookUp (object sender, WidgetEventArgs args) + { + if (UIAToolTipUnhookUp != null) + UIAToolTipUnhookUp (sender, args); + } + + #endregion + + public ToolTip(System.ComponentModel.IContainer cont) : this() { + cont.Add (this); + } + + ~ToolTip() { + } + #endregion // Public Constructors & Destructors + + #region Public Instance Properties + [DefaultValue (true)] + public bool Active { + get { + return is_active; + } + + set { + if (is_active != value) { + is_active = value; + + if (tooltip_window.Visible) { + tooltip_window.Visible = false; + active_Widget = null; + } + } + } + } + + [DefaultValue (500)] + [RefreshProperties (RefreshProperties.All)] + public int AutomaticDelay { + get { + return automatic_delay; + } + + set { + if (automatic_delay != value) { + automatic_delay = value; + autopop_delay = automatic_delay * 10; + initial_delay = automatic_delay; + re_show_delay = automatic_delay / 5; + } + } + } + + [RefreshProperties (RefreshProperties.All)] + public int AutoPopDelay { + get { + return autopop_delay; + } + + set { + if (autopop_delay != value) { + autopop_delay = value; + } + } + } + + [DefaultValue ("Color [Info]")] + public Color BackColor { + get { return this.back_color; } + set { this.back_color = value; tooltip_window.BackColor = value; } + } + + [DefaultValue ("Color [InfoText]")] + public Color ForeColor + { + get { return this.fore_color; } + set { this.fore_color = value; tooltip_window.ForeColor = value; } + } + + [RefreshProperties (RefreshProperties.All)] + public int InitialDelay { + get { + return initial_delay; + } + + set { + if (initial_delay != value) { + initial_delay = value; + } + } + } + + [DefaultValue (false)] + public bool OwnerDraw { + get { return this.owner_draw; } + set { this.owner_draw = value; } + } + + [RefreshProperties (RefreshProperties.All)] + public int ReshowDelay { + get { + return re_show_delay; + } + + set { + if (re_show_delay != value) { + re_show_delay = value; + } + } + } + + [DefaultValue (false)] + public bool ShowAlways { + get { + return show_always; + } + + set { + if (show_always != value) { + show_always = value; + } + } + } + + + [DefaultValue (false)] + public bool IsBalloon { + get { return isBalloon; } + set { isBalloon = value; } + } + + [Browsable (true)] + [DefaultValue (false)] + public bool StripAmpersands { + get { return stripAmpersands; } + set { stripAmpersands = value; } + } + + [Localizable (false)] + [Bindable (true)] + [TypeConverter (typeof (StringConverter))] + [DefaultValue (null)] + public object Tag { + get { return tag; } + set { tag = value; } + } + + [DefaultValue (ToolTipIcon.None)] + public ToolTipIcon ToolTipIcon { + get { return this.tool_tip_icon; } + set { + switch (value) { + case ToolTipIcon.None: + tooltip_window.icon = null; + break; + case ToolTipIcon.Error: + tooltip_window.icon = SystemIcons.Error; + break; + case ToolTipIcon.Warning: + tooltip_window.icon = SystemIcons.Warning; + break; + case ToolTipIcon.Info: + tooltip_window.icon = SystemIcons.Information; + break; + } + + tool_tip_icon = value; + } + } + + [DefaultValue ("")] + public string ToolTipTitle { + get { return tooltip_window.title; } + set { + if (value == null) + value = String.Empty; + + tooltip_window.title = value; + } + } + + [Browsable (true)] + [DefaultValue (true)] + public bool UseAnimation { + get { return useAnimation; } + set { useAnimation = value; } + } + + [Browsable (true)] + [DefaultValue (true)] + public bool UseFading { + get { return useFading; } + set { useFading = value; } + } + + #endregion // Public Instance Properties + + #region Protected Properties + protected virtual CreateParams CreateParams + { + get + { + CreateParams cp = new CreateParams (); + + cp.Style = 2; + + return cp; + } + } + #endregion + + #region Public Instance Methods + public bool CanExtend(object target) { + return false; + } + + [Localizable (true)] + [DefaultValue ("")] + public string GetToolTip (Widget Widget) + { + string tooltip = (string)tooltip_strings[Widget]; + if (tooltip == null) + return ""; + return tooltip; + } + + public void RemoveAll() { + tooltip_strings.Clear(); + //UIA Framework: ToolTip isn't associated anymore + foreach (Widget Widget in Widgets) + OnUIAToolTipUnhookUp (this, new WidgetEventArgs (Widget)); + + Widgets.Clear(); + } + + public void SetToolTip(Widget Widget, string caption) { + // UIA Framework + OnUIAToolTipHookUp (this, new WidgetEventArgs (Widget)); + tooltip_strings[Widget] = caption; + + // no need for duplicates + if (!Widgets.Contains(Widget)) { + Widget.MouseEnter += new EventHandler(Widget_MouseEnter); + Widget.MouseMove += new MouseEventHandler(Widget_MouseMove); + Widget.MouseLeave += new EventHandler(Widget_MouseLeave); + Widget.MouseDown += new MouseEventHandler (Widget_MouseDown); + Widgets.Add(Widget); + } + + // if SetToolTip is called from a Widget and the mouse is currently over that Widget, + // make sure that tooltip_window.Text gets updated if it's being shown, + // or show the tooltip for it if is not + if (active_Widget == Widget && caption != null && state == TipState.Show) { + Size size = ThemeEngine.Current.ToolTipSize(tooltip_window, caption); + tooltip_window.Width = size.Width; + tooltip_window.Height = size.Height; + tooltip_window.Text = caption; + timer.Stop (); + timer.Start (); + } else if (Widget.IsHandleCreated && MouseInWidget (Widget, false)) + ShowTooltip (Widget); + } + + public override string ToString() { + return base.ToString() + " InitialDelay: " + initial_delay + ", ShowAlways: " + show_always; + } + + public void Show (string text, IWin32Window window) + { + Show (text, window, 0); + } + + public void Show (string text, IWin32Window window, int duration) + { + if (window == null) + throw new ArgumentNullException ("window"); + if (duration < 0) + throw new ArgumentOutOfRangeException ("duration", "duration cannot be less than zero"); + + if (!Active) + return; + + timer.Stop (); + + Widget c = (Widget)window; + + XplatUI.SetOwner (tooltip_window.Handle, c.TopLevelWidget.Handle); + + // If the mouse is in the requested window, use that position + // Else, center in the requested window + if (c.ClientRectangle.Contains (c.PointToClient (Widget.MousePosition))) { + tooltip_window.Location = Widget.MousePosition; + tooltip_strings[c] = text; + HookupWidgetEvents (c); + } + else + tooltip_window.Location = c.PointToScreen (new Point (c.Width / 2, c.Height / 2)); + + // We need to hide our tooltip if the form loses focus, is closed, or is minimized + HookupFormEvents ((Form)c.TopLevelWidget); + + tooltip_window.PresentModal ((Widget)window, text); + + state = TipState.Show; + + if (duration > 0) { + timer.Interval = duration; + timer.Start (); + } + } + + public void Show (string text, IWin32Window window, Point point) + { + Show (text, window, point, 0); + } + + public void Show (string text, IWin32Window window, int x, int y) + { + Show (text, window, new Point (x, y), 0); + } + + public void Show (string text, IWin32Window window, Point point, int duration) + { + if (window == null) + throw new ArgumentNullException ("window"); + if (duration < 0) + throw new ArgumentOutOfRangeException ("duration", "duration cannot be less than zero"); + + if (!Active) + return; + + timer.Stop (); + + Widget c = (Widget)window; + + Point display_point = c.PointToScreen (Point.Empty); + display_point.X += point.X; + display_point.Y += point.Y; + + XplatUI.SetOwner (tooltip_window.Handle, c.TopLevelWidget.Handle); + + // We need to hide our tooltip if the form loses focus, is closed, or is minimized + HookupFormEvents ((Form)c.TopLevelWidget); + + tooltip_window.Location = display_point; + tooltip_window.PresentModal ((Widget)window, text); + + state = TipState.Show; + + if (duration > 0) { + timer.Interval = duration; + timer.Start (); + } + } + + public void Show (string text, IWin32Window window, int x, int y, int duration) + { + Show (text, window, new Point (x, y), duration); + } + + public void Hide (IWin32Window win) + { + timer.Stop (); + state = TipState.Initial; + + UnhookFormEvents (); + tooltip_window.Visible = false; + } + #endregion // Public Instance Methods + + #region Protected Instance Methods + protected override void Dispose(bool disposing) { + // call the base impl first to avoid conflicts with any parent's events + base.Dispose (disposing); + + if (disposing) { + // Mop up the mess; or should we wait for the GC to kick in? + timer.Stop(); + timer.Dispose(); + + // Not sure if we should clean up tooltip_window + tooltip_window.Dispose(); + + tooltip_strings.Clear(); + + //UIA Framework: ToolTip isn't associated anymore + foreach (Widget Widget in Widgets) + OnUIAToolTipUnhookUp (this, new WidgetEventArgs (Widget)); + Widgets.Clear(); + } + } + + protected void StopTimer () + { + timer.Stop (); + } + #endregion // Protected Instance Methods + + internal enum TipState { + Initial, + Show, + Down + } + + TipState state = TipState.Initial; + + #region Private Methods + + private void HookupFormEvents (Form form) + { + hooked_form = form; + + form.Deactivate += new EventHandler (Form_Deactivate); + form.Closed += new EventHandler (Form_Closed); + form.Resize += new EventHandler (Form_Resize); + } + + private void HookupWidgetEvents (Widget widget) + { + if (!Widgets.Contains (widget)) { + widget.MouseEnter += new EventHandler (Widget_MouseEnter); + widget.MouseMove += new MouseEventHandler (Widget_MouseMove); + widget.MouseLeave += new EventHandler (Widget_MouseLeave); + widget.MouseDown += new MouseEventHandler (Widget_MouseDown); + Widgets.Add (widget); + } + } + + private void UnhookWidgetEvents (Widget widget) + { + widget.MouseEnter -= new EventHandler (Widget_MouseEnter); + widget.MouseMove -= new MouseEventHandler (Widget_MouseMove); + widget.MouseLeave -= new EventHandler (Widget_MouseLeave); + widget.MouseDown -= new MouseEventHandler (Widget_MouseDown); + } + private void UnhookFormEvents () + { + if (hooked_form == null) + return; + + hooked_form.Deactivate -= new EventHandler (Form_Deactivate); + hooked_form.Closed -= new EventHandler (Form_Closed); + hooked_form.Resize -= new EventHandler (Form_Resize); + + hooked_form = null; + } + + + private void Form_Resize (object sender, EventArgs e) + { + Form f = (Form)sender; + + if (f.WindowState == FormWindowState.Minimized) + tooltip_window.Visible = false; + } + + private void Form_Closed (object sender, EventArgs e) + { + tooltip_window.Visible = false; + } + + private void Form_Deactivate (object sender, EventArgs e) + { + tooltip_window.Visible = false; + } + + internal void Present (Widget Widget, string text) + { + tooltip_window.Present (Widget, text); + } + + private void Widget_MouseEnter (object sender, EventArgs e) + { + ShowTooltip (sender as Widget); + } + + private void ShowTooltip (Widget Widget) + { + last_Widget = Widget; + + // Whatever we're displaying right now, we don't want it anymore + tooltip_window.Visible = false; + timer.Stop(); + state = TipState.Initial; + + if (!is_active) + return; + + // ShowAlways Widgets whether the Widgets in non-active forms + // can display its tooltips, even if they are not current active Widget. + if (!show_always && Widget.FindForm () != Form.ActiveForm) + return; + + string text = (string)tooltip_strings[Widget]; + if (text != null && text.Length > 0) { + if (active_Widget == null) { + timer.Interval = Math.Max (initial_delay, 1); + } else { + timer.Interval = Math.Max (re_show_delay, 1); + } + + active_Widget = Widget; + timer.Start (); + } + } + + private void timer_Tick(object sender, EventArgs e) { + timer.Stop(); + + switch (state) { + case TipState.Initial: + if (active_Widget == null) + return; + tooltip_window.Present (active_Widget, (string)tooltip_strings[active_Widget]); + state = TipState.Show; + timer.Interval = autopop_delay; + timer.Start(); + break; + + case TipState.Show: + tooltip_window.Visible = false; + state = TipState.Down; + break; + + default: + throw new Exception ("Timer shouldn't be running in state: " + state); + } + } + + private void tooltip_window_Popup (object sender, PopupEventArgs e) + { + e.ToolTipSize = ThemeEngine.Current.ToolTipSize (tooltip_window, tooltip_window.Text); + OnPopup (e); + } + + private void tooltip_window_Draw (object sender, DrawToolTipEventArgs e) + { + if (OwnerDraw) + OnDraw (e); + else + ThemeEngine.Current.DrawToolTip (e.Graphics, e.Bounds, tooltip_window); + } + + private bool MouseInWidget (Widget Widget, bool fuzzy) { + Point m; + Point c; + Size cw; + + if (Widget == null) { + return false; + } + + m = Widget.MousePosition; + c = new Point(Widget.Bounds.X, Widget.Bounds.Y); + if (Widget.Parent != null) { + c = Widget.Parent.PointToScreen(c); + } + cw = Widget.ClientSize; + + + Rectangle rect = new Rectangle (c, cw); + + // + // We won't get mouse move events on all platforms with the exact same + // frequency, so cheat a bit. + if (fuzzy) + rect.Inflate (2, 2); + + return rect.Contains (m); + } + + private void Widget_MouseLeave(object sender, EventArgs e) + { + timer.Stop (); + + active_Widget = null; + tooltip_window.Visible = false; + + if (last_Widget == sender) + last_Widget = null; + } + + + void Widget_MouseDown (object sender, MouseEventArgs e) + { + timer.Stop(); + + active_Widget = null; + tooltip_window.Visible = false; + + if (last_Widget == sender) + last_Widget = null; + } + + private void Widget_MouseMove(object sender, MouseEventArgs e) { + if (state != TipState.Down) { + timer.Stop(); + timer.Start(); + } + } + + internal void OnDraw (DrawToolTipEventArgs e) + { + DrawToolTipEventHandler eh = (DrawToolTipEventHandler)(Events[DrawEvent]); + if (eh != null) + eh (this, e); + } + + internal void OnPopup (PopupEventArgs e) + { + PopupEventHandler eh = (PopupEventHandler) (Events [PopupEvent]); + if (eh != null) + eh (this, e); + } + + internal void OnUnPopup (PopupEventArgs e) + { + PopupEventHandler eh = (PopupEventHandler) (Events [UnPopupEvent]); + if (eh != null) + eh (this, e); + } + + internal bool Visible { + get { return tooltip_window.Visible; } + } + #endregion // Private Methods + + #region Events + static object PopupEvent = new object (); + static object DrawEvent = new object (); + + public event PopupEventHandler Popup { + add { Events.AddHandler (PopupEvent, value); } + remove { Events.RemoveHandler (PopupEvent, value); } + } + + public event DrawToolTipEventHandler Draw { + add { Events.AddHandler (DrawEvent, value); } + remove { Events.RemoveHandler (DrawEvent, value); } + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/ToolTipIcon.cs b/source/ShiftUI/Internal/ToolTipIcon.cs new file mode 100644 index 0000000..2adcca8 --- /dev/null +++ b/source/ShiftUI/Internal/ToolTipIcon.cs @@ -0,0 +1,39 @@ +// +// ToolTipIcon.cs +// +// 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) 2006 Novell, Inc. +// +// Authors: +// Jonathan Pobst ([email protected]) +// + + +namespace ShiftUI +{ + public enum ToolTipIcon + { + None = 0, + Info = 1, + Warning = 2, + Error = 3 + } +} diff --git a/source/ShiftUI/Internal/ToolWindowManager.cs b/source/ShiftUI/Internal/ToolWindowManager.cs new file mode 100644 index 0000000..ba080d7 --- /dev/null +++ b/source/ShiftUI/Internal/ToolWindowManager.cs @@ -0,0 +1,47 @@ +// 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) 2007 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Rolf Bjarne Kvinge ([email protected]) +// +// + + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +namespace ShiftUI +{ + internal class ToolWindowManager : InternalWindowManager + { + public ToolWindowManager (Form form) + : base (form) + { + + } + + public override void SetWindowState (FormWindowState old_state, FormWindowState window_state) + { + // Do nothing here. + } + } +} diff --git a/source/ShiftUI/Internal/TreeNodeCollection.cs b/source/ShiftUI/Internal/TreeNodeCollection.cs new file mode 100644 index 0000000..5323579 --- /dev/null +++ b/source/ShiftUI/Internal/TreeNodeCollection.cs @@ -0,0 +1,611 @@ +// 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: +// Jackson Harper ([email protected]) + + +using System; +using System.Collections; +using System.ComponentModel; +using System.Globalization; +using System.Collections.Generic; + +namespace ShiftUI { + //[Editor("ShiftUI.Design.TreeNodeCollectionEditor, " + Consts.AssemblySystem_Design, typeof(System.Drawing.Design.UITypeEditor))] + public class TreeNodeCollection : IList, ICollection, IEnumerable { + + private static readonly int OrigSize = 50; + + private TreeNode owner; + private int count; + private TreeNode [] nodes; + + private TreeNodeCollection () + { + } + + internal TreeNodeCollection (TreeNode owner) + { + this.owner = owner; + nodes = new TreeNode [OrigSize]; + } + + [Browsable(false)] + public int Count { + get { return count; } + } + + public bool IsReadOnly { + get { return false; } + } + + bool ICollection.IsSynchronized { + get { return false; } + } + + object ICollection.SyncRoot { + get { return this; } + } + + bool IList.IsFixedSize { + get { return false; } + } + + object IList.this [int index] { + get { + return this [index]; + } + set { + if (!(value is TreeNode)) + throw new ArgumentException ("Parameter must be of type TreeNode.", "value"); + this [index] = (TreeNode) value; + } + } + + public virtual TreeNode this [int index] { + get { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException ("index"); + return nodes [index]; + } + set { + if (index < 0 || index >= Count) + throw new ArgumentOutOfRangeException ("index"); + SetupNode (value); + nodes [index] = value; + } + } + + public virtual TreeNode this [string key] { + get { + for (int i = 0; i < count; i++) + if (string.Compare (key, nodes[i].Name, true) == 0) + return nodes[i]; + + return null; + } + } + + bool UsingSorting { + get { + TreeView tv = owner == null ? null : owner.TreeView; + return tv != null && (tv.Sorted || tv.TreeViewNodeSorter != null); + } + } + + public virtual TreeNode Add (string text) + { + TreeNode res = new TreeNode (text); + Add (res); + return res; + } + + public virtual int Add (TreeNode node) + { + if (node == null) + throw new ArgumentNullException("node"); + + int index; + TreeView tree_view = null; + + if (owner != null) + tree_view = owner.TreeView; + + if (tree_view != null && UsingSorting) { + index = AddSorted (node); + } else { + if (count >= nodes.Length) + Grow (); + index = count; + count++; + nodes[index] = node; + } + + SetupNode (node); + + // UIA Framework Event: Collection Changed + if (tree_view != null) + tree_view.OnUIACollectionChanged (owner, new CollectionChangeEventArgs (CollectionChangeAction.Add, node)); + return index; + } + + public virtual TreeNode Add (string key, string text) + { + TreeNode node = new TreeNode (text); + node.Name = key; + Add (node); + return node; + } + + public virtual TreeNode Add (string key, string text, int imageIndex) + { + TreeNode node = Add (key, text); + node.ImageIndex = imageIndex; + return node; + } + + public virtual TreeNode Add (string key, string text, string imageKey) + { + TreeNode node = Add (key, text); + node.ImageKey = imageKey; + return node; + + } + + public virtual TreeNode Add (string key, string text, int imageIndex, int selectedImageIndex) + { + TreeNode node = Add (key, text); + node.ImageIndex = imageIndex; + node.SelectedImageIndex = selectedImageIndex; + return node; + } + + public virtual TreeNode Add (string key, string text, string imageKey, string selectedImageKey) + { + TreeNode node = Add (key, text); + node.ImageKey = imageKey; + node.SelectedImageKey = selectedImageKey; + return node; + } + + public virtual void AddRange (TreeNode [] nodes) + { + if (nodes == null) + throw new ArgumentNullException("nodes"); + + // We can't just use Array.Copy because the nodes also + // need to have some properties set when they are added. + for (int i = 0; i < nodes.Length; i++) + Add (nodes [i]); + } + + public virtual void Clear () + { + while (count > 0) + RemoveAt (0, false); + + Array.Clear (nodes, 0, count); + count = 0; + + TreeView tree_view = null; + if (owner != null) { + tree_view = owner.TreeView; + if (tree_view != null) { + tree_view.UpdateBelow (owner); + tree_view.RecalculateVisibleOrder (owner); + tree_view.UpdateScrollBars (false); + } + } + } + + public bool Contains (TreeNode node) + { + return Array.IndexOf (nodes, node, 0, count) != -1; + } + + public virtual bool ContainsKey (string key) + { + for (int i = 0; i < count; i++) { + if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0) + return true; + } + return false; + } + + public void CopyTo (Array dest, int index) + { + Array.Copy (nodes, index, dest, index, count); + } + + public IEnumerator GetEnumerator () + { + return new TreeNodeEnumerator (this); + } + + public int IndexOf (TreeNode node) + { + return Array.IndexOf (nodes, node); + } + + public virtual int IndexOfKey (string key) + { + for (int i = 0; i < count; i++) { + if (string.Compare (nodes [i].Name, key, true, CultureInfo.InvariantCulture) == 0) + return i; + } + return -1; + } + + public virtual TreeNode Insert (int index, string text) + { + TreeNode node = new TreeNode (text); + Insert (index, node); + return node; + } + + public virtual void Insert (int index, TreeNode node) + { + if (count >= nodes.Length) + Grow (); + + Array.Copy (nodes, index, nodes, index + 1, count - index); + nodes [index] = node; + count++; + + // If we can use sorting, it means we have an owner *and* a TreeView + if (UsingSorting) + Sort (owner.TreeView.TreeViewNodeSorter); + + SetupNode (node); + } + + public virtual TreeNode Insert (int index, string key, string text) + { + TreeNode node = new TreeNode (text); + node.Name = key; + Insert (index, node); + return node; + } + + public virtual TreeNode Insert (int index, string key, string text, int imageIndex) + { + TreeNode node = new TreeNode (text); + node.Name = key; + node.ImageIndex = imageIndex; + Insert (index, node); + return node; + } + + public virtual TreeNode Insert (int index, string key, string text, string imageKey) + { + TreeNode node = new TreeNode (text); + node.Name = key; + node.ImageKey = imageKey; + Insert (index, node); + return node; + } + + public virtual TreeNode Insert (int index, string key, string text, int imageIndex, int selectedImageIndex) + { + TreeNode node = new TreeNode (text, imageIndex, selectedImageIndex); + node.Name = key; + Insert (index, node); + return node; + } + + public virtual TreeNode Insert (int index, string key, string text, string imageKey, string selectedImageKey) + { + TreeNode node = new TreeNode (text); + node.Name = key; + node.ImageKey = imageKey; + node.SelectedImageKey = selectedImageKey; + Insert (index, node); + return node; + } + + public void Remove (TreeNode node) + { + if (node == null) + throw new NullReferenceException (); + + int index = IndexOf (node); + if (index != -1) + RemoveAt (index); + } + + public virtual void RemoveAt (int index) + { + RemoveAt (index, true); + } + + private void RemoveAt (int index, bool update) + { + TreeNode removed = nodes [index]; + TreeNode prev = GetPrevNode (removed); + TreeNode new_selected = null; + bool re_set_selected = false; + bool visible = removed.IsVisible; + + TreeView tree_view = null; + if (owner != null) + tree_view = owner.TreeView; + + if (tree_view != null) { + tree_view.RecalculateVisibleOrder (prev); + + if (removed == tree_view.SelectedNode) { + if (removed.IsExpanded) + removed.Collapse(); // Fix Xamarin Bugzilla 5010. + re_set_selected = true; + OpenTreeNodeEnumerator oe = new OpenTreeNodeEnumerator (removed); + if (oe.MoveNext () && oe.MoveNext ()) { + new_selected = oe.CurrentNode; + } else { + oe = new OpenTreeNodeEnumerator (removed); + oe.MovePrevious (); + new_selected = oe.CurrentNode == removed ? null : oe.CurrentNode; + } + } + } + + Array.Copy (nodes, index + 1, nodes, index, count - index - 1); + count--; + + nodes[count] = null; + + if (nodes.Length > OrigSize && nodes.Length > (count * 2)) + Shrink (); + + if (tree_view != null && re_set_selected) { + tree_view.SelectedNode = new_selected; + } + + TreeNode parent = removed.parent; + removed.parent = null; + + if (update && tree_view != null && visible) { + tree_view.RecalculateVisibleOrder (prev); + tree_view.UpdateScrollBars (false); + tree_view.UpdateBelow (parent); + } + + // UIA Framework Event: Collection Changed + if (tree_view != null) + tree_view.OnUIACollectionChanged (owner, new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed)); + } + + public virtual void RemoveByKey (string key) + { + TreeNode node = this[key]; + + if (node != null) + Remove (node); + } + + private TreeNode GetPrevNode (TreeNode node) + { + OpenTreeNodeEnumerator one = new OpenTreeNodeEnumerator (node); + + if (one.MovePrevious () && one.MovePrevious ()) + return one.CurrentNode; + return null; + } + + private void SetupNode (TreeNode node) + { + // We used to remove this from the previous parent, but .Net + // skips this step (even if setting the owner field). + //node.Remove (); + + node.parent = owner; + + TreeView tree_view = null; + if (owner != null) + tree_view = owner.TreeView; + + if (tree_view != null) { + // We may need to invalidate this entire node collection if sorted. + TreeNode prev = UsingSorting ? owner : GetPrevNode (node); + + if (tree_view.IsHandleCreated && node.ArePreviousNodesExpanded) + tree_view.RecalculateVisibleOrder (prev); + if (owner == tree_view.root_node || node.Parent.IsVisible && node.Parent.IsExpanded) + tree_view.UpdateScrollBars (false); + + tree_view.UpdateBelow (owner); + } + } + + int IList.Add (object node) + { + return Add ((TreeNode) node); + } + + bool IList.Contains (object node) + { + return Contains ((TreeNode) node); + } + + int IList.IndexOf (object node) + { + return IndexOf ((TreeNode) node); + } + + void IList.Insert (int index, object node) + { + Insert (index, (TreeNode) node); + } + + void IList.Remove (object node) + { + Remove ((TreeNode) node); + } + + private int AddSorted (TreeNode node) + { + if (count >= nodes.Length) + Grow (); + + TreeView tree_view = owner.TreeView; + if (tree_view.TreeViewNodeSorter != null) { // Custom sorting + nodes [count++] = node; + Sort (tree_view.TreeViewNodeSorter); + return count - 1; + } + + CompareInfo compare = Application.CurrentCulture.CompareInfo; + int index = 0; + bool found = false; + for (int i = 0; i < count; i++) { + index = i; + int comp = compare.Compare (node.Text, nodes [i].Text); + if (comp < 0) { + found = true; + break; + } + } + + // Stick it at the end + if (!found) + index = count; + + // Move the nodes up and adjust their indices + for (int i = count - 1; i >= index; i--) { + nodes [i + 1] = nodes [i]; + } + count++; + nodes [index] = node; + + return index; + } + + // Would be nice to do this without running through the collection twice + internal void Sort (IComparer sorter) { + Array.Sort (nodes, 0, count, sorter == null ? new TreeNodeComparer (Application.CurrentCulture.CompareInfo) : sorter); + + for (int i = 0; i < count; i++) { + nodes [i].Nodes.Sort (sorter); + } + + // Sorted may have been set to false even if TreeViewNodeSorter is being used. + TreeView tv = owner == null ? null : owner.TreeView; + if (tv != null) + tv.sorted = true; + } + + private void Grow () + { + TreeNode [] nn = new TreeNode [nodes.Length + 50]; + Array.Copy (nodes, nn, nodes.Length); + nodes = nn; + } + + private void Shrink () + { + int len = (count + 1 > OrigSize ? count + 1 : OrigSize); + TreeNode [] nn = new TreeNode [len]; + Array.Copy (nodes, nn, count); + nodes = nn; + } + + public TreeNode[] Find (string key, bool searchAllChildren) + { + List<TreeNode> results = new List<TreeNode> (0); + Find (key, searchAllChildren, this, results); + + return results.ToArray (); + } + + private static void Find (string key, bool searchAllChildren, TreeNodeCollection nodes, List<TreeNode> results) + { + for (int i = 0; i < nodes.Count; i++) { + TreeNode thisNode = nodes [i]; + + if (string.Compare (thisNode.Name, key, true, CultureInfo.InvariantCulture) == 0) + results.Add (thisNode); + + } + // Need to match the Microsoft order. + + if (searchAllChildren){ + for (int i = 0; i < nodes.Count; i++){ + TreeNodeCollection childNodes = nodes [i].Nodes; + if (childNodes.Count > 0) { + Find (key, searchAllChildren, childNodes, results); + } + } + } + } + + internal class TreeNodeEnumerator : IEnumerator { + + private TreeNodeCollection collection; + private int index = -1; + + public TreeNodeEnumerator (TreeNodeCollection collection) + { + this.collection = collection; + } + + public object Current { + get { + if (index == -1) + return null; + return collection [index]; + } + } + + public bool MoveNext () + { + if (index + 1 >= collection.Count) + return false; + index++; + return true; + } + + public void Reset () + { + index = -1; + } + } + + private class TreeNodeComparer : IComparer { + + private CompareInfo compare; + + public TreeNodeComparer (CompareInfo compare) + { + this.compare = compare; + } + + public int Compare (object x, object y) + { + TreeNode l = (TreeNode) x; + TreeNode r = (TreeNode) y; + int res = compare.Compare (l.Text, r.Text); + + return (res == 0 ? l.Index - r.Index : res); + } + } + } +} + diff --git a/source/ShiftUI/Internal/TreeNodeConverter.cs b/source/ShiftUI/Internal/TreeNodeConverter.cs new file mode 100644 index 0000000..d9682b4 --- /dev/null +++ b/source/ShiftUI/Internal/TreeNodeConverter.cs @@ -0,0 +1,55 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// +// + +// NOT COMPLETE + +using System.ComponentModel; +using System.Globalization; +using System; + +namespace ShiftUI { + public class TreeNodeConverter : TypeConverter { + + #region Public Instance Methods + public override bool CanConvertTo (ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof (string)) + return true; + return base.CanConvertTo (context, destinationType); + } + + public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, + object value, Type destinationType) + { + // Regardless of the type of object passed in this does a ToString + if (destinationType == typeof (string) && value != null) + return value.ToString (); + return base.ConvertTo (context, culture, value, destinationType); + } + + #endregion // Public Instance Methods + } +} diff --git a/source/ShiftUI/Internal/TreeViewHitTestInfo.cs b/source/ShiftUI/Internal/TreeViewHitTestInfo.cs new file mode 100644 index 0000000..7e5c19a --- /dev/null +++ b/source/ShiftUI/Internal/TreeViewHitTestInfo.cs @@ -0,0 +1,51 @@ +// +// TreeViewHitTestLocations.cs +// +// 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) 2007 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) +// + + +namespace ShiftUI { + + public class TreeViewHitTestInfo { + + private TreeNode node; + private TreeViewHitTestLocations location; + + public TreeViewHitTestInfo (TreeNode hitNode, TreeViewHitTestLocations hitLocation) + { + this.node = hitNode; + this.location = hitLocation; + } + + public TreeNode Node { + get { return node; } + } + + public TreeViewHitTestLocations Location { + get { return location; } + } + } +} diff --git a/source/ShiftUI/Internal/TreeViewImageIndexConverter.cs b/source/ShiftUI/Internal/TreeViewImageIndexConverter.cs new file mode 100644 index 0000000..f66b205 --- /dev/null +++ b/source/ShiftUI/Internal/TreeViewImageIndexConverter.cs @@ -0,0 +1,89 @@ +// 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 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) + + +using System; +using System.Drawing; +using System.Collections; +using System.ComponentModel; + + +namespace ShiftUI { + + public class TreeViewImageIndexConverter : ImageIndexConverter { + + public TreeViewImageIndexConverter () + { + } + + #region Protected Properties + protected override bool IncludeNoneAsStandardValue { + get { return false; } + } + #endregion + + #region Public Methods + public override object ConvertFrom (System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) + { + string indexStr; + + if (value != null && value is string) { + indexStr = (string)value; + + if (indexStr.Equals ("(default)", StringComparison.InvariantCultureIgnoreCase)) + return -1; + else if (indexStr.Equals ("(none)", StringComparison.InvariantCultureIgnoreCase)) + return -2; + + return Int32.Parse (indexStr); + } else + return base.ConvertFrom (context, culture, value); + } + + public override object ConvertTo (System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType) + { + if (destinationType == typeof (string)) { + if (value == null) + return string.Empty; + else if (value is int && (int)value == -1) + return "(default)"; + else if (value is int && (int)value == -2) + return "(none)"; + else if (value is string && ((string)value).Length == 0) + return string.Empty; + else + return value.ToString (); + } else + return base.ConvertTo (context, culture, value, destinationType); + } + + public override StandardValuesCollection GetStandardValues (System.ComponentModel.ITypeDescriptorContext context) + { + int[] stdVal = new int[] { -1, -2 }; + return new TypeConverter.StandardValuesCollection (stdVal); + } + #endregion + } +} + diff --git a/source/ShiftUI/Internal/TreeViewImageKeyConverter.cs b/source/ShiftUI/Internal/TreeViewImageKeyConverter.cs new file mode 100644 index 0000000..78ff1d5 --- /dev/null +++ b/source/ShiftUI/Internal/TreeViewImageKeyConverter.cs @@ -0,0 +1,51 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Jonathan Pobst [email protected] +// + +using System.Drawing; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI +{ + public class TreeViewImageKeyConverter : ImageKeyConverter + { + #region Constructors + public TreeViewImageKeyConverter () { } + #endregion Constructors + + #region Public Methods + // XXX - Can't find the difference from the base method + public override object ConvertTo (ITypeDescriptorContext context, CultureInfo culture, + object value, Type destinationType) + { + return base.ConvertTo (context, culture, value, destinationType); + } + #endregion Public Methods + } +} diff --git a/source/ShiftUI/Internal/VScrollProperties.cs b/source/ShiftUI/Internal/VScrollProperties.cs new file mode 100644 index 0000000..e790787 --- /dev/null +++ b/source/ShiftUI/Internal/VScrollProperties.cs @@ -0,0 +1,33 @@ +// 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. +// +// Authors: +// Olivier Dufour [email protected] +// + +namespace ShiftUI +{ + public class VScrollProperties : ScrollProperties + { + public VScrollProperties (ScrollableWidget container) : base (container) + { + scroll_bar = container.vscrollbar; + } + } +} diff --git a/source/ShiftUI/Internal/Win32DnD.cs b/source/ShiftUI/Internal/Win32DnD.cs new file mode 100644 index 0000000..24cf2da --- /dev/null +++ b/source/ShiftUI/Internal/Win32DnD.cs @@ -0,0 +1,1081 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Bartok ([email protected]) +// Srikanth Madikeri ([email protected]) - Win32 Drop files. +// + +// NOT COMPLETE + +using System; +using System.Collections; +using System.Drawing; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Text; + +namespace ShiftUI { + internal class Win32DnD { + #region Local Variables + private const uint DATADIR_GET = 1; + private const uint S_OK = 0x00000000; + private const uint S_FALSE = 0x00000001; + private const uint DRAGDROP_S_DROP = 0x00040100; + private const uint DRAGDROP_S_CANCEL = 0x00040101; + private const uint DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102; + private const uint E_NOTIMPL = unchecked((uint)0x80004001); + private const uint E_NOINTERFACE = unchecked((uint)0x80004002); + private const uint E_FAIL = unchecked((uint)0x80004005); + private const uint OLE_E_ADVISENOTSUPPORTED = unchecked((uint)0x80040003); + private const uint DV_E_FORMATETC = unchecked((uint)0x80040064); + + // To call function pointers + //private static object[] GetDataArgs; + + // IDataObject Delegates + private static QueryInterfaceDelegate DOQueryInterface; + private static AddRefDelegate DOAddRef; + private static ReleaseDelegate DORelease; + private static GetDataDelegate GetData; + private static GetDataHereDelegate GetDataHere; + private static QueryGetDataDelegate QueryGetData; + private static GetCanonicalFormatEtcDelegate GetCanonicalFormatEtc; + private static SetDataDelegate SetData; + private static EnumFormatEtcDelegate EnumFormatEtc; + private static DAdviseDelegate DAdvise; + private static DUnadviseDelegate DUnadvise; + private static EnumDAdviseDelegate EnumDAdvise; + + // IDropSource Delegates + private static QueryInterfaceDelegate DSQueryInterface; + private static AddRefDelegate DSAddRef; + private static ReleaseDelegate DSRelease; + private static QueryContinueDragDelegate QueryContinueDrag; + private static GiveFeedbackDelegate GiveFeedback; + + // IDropTarget Delegates + private static QueryInterfaceDelegate DTQueryInterface; + private static AddRefDelegate DTAddRef; + private static ReleaseDelegate DTRelease; + private static DragEnterDelegate DragEnter; + private static DragOverDelegate DragOver; + private static DragLeaveDelegate DragLeave; + private static DropDelegate Drop; + + private static DragEventArgs DragDropEventArgs; + private static GiveFeedbackEventArgs DragFeedbackEventArgs; + private static QueryContinueDragEventArgs DragContinueEventArgs; + private static ArrayList DragFormats; + private static FORMATETC[] DragFormatArray; + private static ArrayList DragMediums; + #endregion // Local Variables + + #region Delegate Definitions + // IUnknown + internal delegate uint QueryInterfaceDelegate(IntPtr @this, ref Guid riid, IntPtr ppvObject); + internal delegate uint AddRefDelegate(IntPtr @this); + internal delegate uint ReleaseDelegate(IntPtr @this); + + // IDataObject + internal delegate uint GetDataDelegate(IntPtr @this, ref FORMATETC pformatetcIn, IntPtr pmedium); + internal delegate uint GetDataHereDelegate(IntPtr @this, ref FORMATETC pformatetc, ref STGMEDIUM pmedium); + internal delegate uint QueryGetDataDelegate(IntPtr @this, ref FORMATETC pformatetc); + internal delegate uint GetCanonicalFormatEtcDelegate(IntPtr @this, ref FORMATETC pformatetcIn, IntPtr pformatetcOut); + internal delegate uint SetDataDelegate(IntPtr @this, ref FORMATETC pformatetc, ref STGMEDIUM pmedium, bool release); + internal delegate uint EnumFormatEtcDelegate(IntPtr @this, uint direction, IntPtr ppenumFormatEtc); + internal delegate uint DAdviseDelegate(IntPtr @this, ref FORMATETC pformatetc, uint advf, IntPtr pAdvSink, ref uint pdwConnection); + internal delegate uint DUnadviseDelegate(IntPtr @this, uint pdwConnection); + internal delegate uint EnumDAdviseDelegate(IntPtr @this, IntPtr ppenumAdvise); + + // IDropSource + internal delegate uint QueryContinueDragDelegate(IntPtr @this, bool fEscapePressed, uint grfkeyState); + internal delegate uint GiveFeedbackDelegate(IntPtr @this, uint pdwEffect); + + // IDropTarget + internal delegate uint DragEnterDelegate(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect); + internal delegate uint DragOverDelegate(IntPtr @this, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect); + internal delegate uint DragLeaveDelegate(IntPtr @this); + internal delegate uint DropDelegate(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect); + #endregion // Delegate Definitions + + [StructLayout(LayoutKind.Sequential)] + internal struct FORMATETC { + [MarshalAs(UnmanagedType.U2)] + internal ClipboardFormats cfFormat; + internal IntPtr ptd; + internal DVASPECT dwAspect; + internal int lindex; + internal TYMED tymed; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct STGMEDIUM { + internal TYMED tymed; + internal IntPtr hHandle; + internal IntPtr pUnkForRelease; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + internal struct DROPFILES { + internal uint pFiles; + internal uint pt_x; + internal uint pt_y; + internal bool fNC; + internal bool fWide; + internal string pText; + } + + internal enum DVASPECT { + DVASPECT_CONTENT = 1, + DVASPECT_THUMBNAIL = 2, + DVASPECT_ICON = 4, + DVASPECT_DOCPRINT = 8 + } + + internal enum TYMED { + TYMED_HGLOBAL = 1, + TYMED_FILE = 2, + TYMED_ISTREAM = 4, + TYMED_ISTORAGE = 8, + TYMED_GDI = 16, + TYMED_MFPICT = 32, + TYMED_ENHMF = 64, + TYMED_NULL = 0 + } + + private static readonly Guid IID_IUnknown = new Guid("00000000-0000-0000-C000-000000000046"); + private static readonly Guid IID_IDataObject = new Guid("0000010e-0000-0000-C000-000000000046"); + private static readonly Guid IID_IDropSource = new Guid("00000121-0000-0000-C000-000000000046"); + private static readonly Guid IID_IDropTarget = new Guid("00000122-0000-0000-C000-000000000046"); + + static Win32DnD() + { + // Required for all other OLE functions to work + Win32OleInitialize(IntPtr.Zero); + + // We reuse those + DragDropEventArgs = new DragEventArgs(new DataObject(DataFormats.FileDrop, new string[0]), 0, 0, 0, DragDropEffects.None, DragDropEffects.None); + DragFeedbackEventArgs = new GiveFeedbackEventArgs(DragDropEffects.None, true); + DragContinueEventArgs = new QueryContinueDragEventArgs(0, false, DragAction.Continue); + DragFormats = new ArrayList(); + DragFormatArray = new FORMATETC[0]; + DragMediums = new ArrayList(); + + // Set up delegates + // IDataObject + DOQueryInterface = new QueryInterfaceDelegate(ComIDataObject.QueryInterface); + DOAddRef = new AddRefDelegate(ComIDataObject.AddRef); + DORelease = new ReleaseDelegate(ComIDataObject.Release); + GetData = new GetDataDelegate(ComIDataObject.GetData); + GetDataHere = new GetDataHereDelegate(ComIDataObject.GetDataHere); + QueryGetData = new QueryGetDataDelegate(ComIDataObject.QueryGetData); + GetCanonicalFormatEtc = new GetCanonicalFormatEtcDelegate(ComIDataObject.GetCanonicalFormatEtc); + SetData = new SetDataDelegate(ComIDataObject.SetData); + EnumFormatEtc = new EnumFormatEtcDelegate(ComIDataObject.EnumFormatEtc); + DAdvise = new DAdviseDelegate(ComIDataObject.DAdvise); + DUnadvise = new DUnadviseDelegate(ComIDataObject.DUnadvise); + EnumDAdvise = new EnumDAdviseDelegate(ComIDataObject.EnumDAdvise); + + // IDropSource + DSQueryInterface = new QueryInterfaceDelegate(ComIDropSource.QueryInterface); + DSAddRef = new AddRefDelegate(ComIDropSource.AddRef); + DSRelease = new ReleaseDelegate(ComIDropSource.Release); + QueryContinueDrag = new QueryContinueDragDelegate(ComIDropSource.QueryContinueDrag); + GiveFeedback = new GiveFeedbackDelegate(ComIDropSource.GiveFeedback); + + // IDropTarget + DTQueryInterface = new QueryInterfaceDelegate(ComIDropTarget.QueryInterface); + DTAddRef = new AddRefDelegate(ComIDropTarget.AddRef); + DTRelease = new ReleaseDelegate(ComIDropTarget.Release); + DragEnter = new DragEnterDelegate(ComIDropTarget.DragEnter); + DragOver = new DragOverDelegate(ComIDropTarget.DragOver); + DragLeave = new DragLeaveDelegate(ComIDropTarget.DragLeave); + Drop = new DropDelegate(ComIDropTarget.Drop); + } + + internal class ComIDataObject { + [StructLayout(LayoutKind.Sequential)] + internal struct DataObjectStruct { + internal IntPtr vtbl; + internal QueryInterfaceDelegate QueryInterface; + internal AddRefDelegate AddRef; + internal ReleaseDelegate Release; + internal GetDataDelegate GetData; + internal GetDataHereDelegate GetDataHere; + internal QueryGetDataDelegate QueryGetData; + internal GetCanonicalFormatEtcDelegate GetCanonicalFormatEtc; + internal SetDataDelegate SetData; + internal EnumFormatEtcDelegate EnumFormatEtc; + internal DAdviseDelegate DAdvise; + internal DUnadviseDelegate DUnadvise; + internal EnumDAdviseDelegate EnumDAdvise; + } + + internal static IntPtr GetUnmanaged() { + DataObjectStruct data_object; + IntPtr data_object_ptr; + long offset; + + data_object = new DataObjectStruct(); + + data_object.QueryInterface = Win32DnD.DOQueryInterface; + data_object.AddRef = Win32DnD.DOAddRef; + data_object.Release = Win32DnD.DORelease; + data_object.GetData = Win32DnD.GetData; + data_object.GetDataHere = Win32DnD.GetDataHere; + data_object.QueryGetData = Win32DnD.QueryGetData; + data_object.GetCanonicalFormatEtc = Win32DnD.GetCanonicalFormatEtc; + data_object.SetData = Win32DnD.SetData; + data_object.EnumFormatEtc = Win32DnD.EnumFormatEtc; + data_object.DAdvise = Win32DnD.DAdvise; + data_object.DUnadvise = Win32DnD.DUnadvise; + data_object.EnumDAdvise = Win32DnD.EnumDAdvise; + + data_object_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(DataObjectStruct))); + Marshal.StructureToPtr(data_object, data_object_ptr, false); + + // Update vtbl pointer + offset = data_object_ptr.ToInt64(); + offset += Marshal.SizeOf(typeof(IntPtr)); + Marshal.WriteIntPtr(data_object_ptr, new IntPtr(offset)); + + return data_object_ptr; + } + + internal static void ReleaseUnmanaged(IntPtr data_object_ptr) { + Marshal.FreeHGlobal(data_object_ptr); + } + + internal static uint QueryInterface(IntPtr @this, ref Guid riid, IntPtr ppvObject) { + try { + if (IID_IUnknown.Equals(riid) || IID_IDataObject.Equals(riid)) { + Marshal.WriteIntPtr(ppvObject, @this); + return S_OK; + } + } + + catch (Exception e) { + Console.WriteLine("Got exception {0}", e.Message); + } + + Marshal.WriteIntPtr(ppvObject, IntPtr.Zero); + return E_NOINTERFACE; + } + + internal static uint AddRef(IntPtr @this) { + // We only use this for DnD, try and fake it + return 1; + } + + internal static uint Release(IntPtr @this) { + // We only use this for DnD, try and fake it + return 0; + } + + internal static STGMEDIUM medium = new STGMEDIUM(); + internal static uint GetData(IntPtr this_, ref FORMATETC pformatetcIn, IntPtr pmedium) { + int index; + + index = FindFormat(pformatetcIn); + if (index != -1) { + medium.tymed = TYMED.TYMED_HGLOBAL; + medium.hHandle = XplatUIWin32.DupGlobalMem(((STGMEDIUM)DragMediums[index]).hHandle); + medium.pUnkForRelease = IntPtr.Zero; + try { + Marshal.StructureToPtr(medium, pmedium, false); + } + catch (Exception e) { + Console.WriteLine("Error: {0}", e.Message); + } + return S_OK; + } + + return DV_E_FORMATETC; + } + + internal static uint GetDataHere(IntPtr @this, ref FORMATETC pformatetc, ref STGMEDIUM pmedium) { + return DV_E_FORMATETC; + } + + internal static uint QueryGetData(IntPtr @this, ref FORMATETC pformatetc) { + if (FindFormat(pformatetc) != -1) { + return S_OK; + } + return DV_E_FORMATETC; + } + + internal static uint GetCanonicalFormatEtc(IntPtr @this, ref FORMATETC pformatetcIn, IntPtr pformatetcOut) { + Marshal.WriteIntPtr(pformatetcOut, Marshal.SizeOf(typeof(IntPtr)), IntPtr.Zero); + return E_NOTIMPL; + } + + internal static uint SetData(IntPtr this_, ref FORMATETC pformatetc, ref STGMEDIUM pmedium, bool release) { + return E_NOTIMPL; + } + + internal static uint EnumFormatEtc(IntPtr this_, uint direction, IntPtr ppenumFormatEtc) { + if (direction == DATADIR_GET) { + IntPtr ppenum_ptr; + + ppenum_ptr = IntPtr.Zero; + DragFormatArray = new FORMATETC[DragFormats.Count]; + + for (int i = 0; i < DragFormats.Count; i++) { + DragFormatArray[i] = (FORMATETC)DragFormats[i]; + } + Win32SHCreateStdEnumFmtEtc((uint)DragFormatArray.Length, DragFormatArray, ref ppenum_ptr); + Marshal.WriteIntPtr(ppenumFormatEtc, ppenum_ptr); + return S_OK; + } + return E_NOTIMPL; + } + + internal static uint DAdvise(IntPtr this_, ref FORMATETC pformatetc, uint advf, IntPtr pAdvSink, ref uint pdwConnection) { + return OLE_E_ADVISENOTSUPPORTED; + } + + internal static uint DUnadvise(IntPtr this_, uint pdwConnection) { + return OLE_E_ADVISENOTSUPPORTED; + } + + internal static uint EnumDAdvise(IntPtr this_, IntPtr ppenumAdvise) { + return OLE_E_ADVISENOTSUPPORTED; + } + } + + internal class ComIDataObjectUnmanaged { + [StructLayout(LayoutKind.Sequential)] + internal struct IDataObjectUnmanaged { + internal IntPtr QueryInterface; + internal IntPtr AddRef; + internal IntPtr Release; + internal IntPtr GetData; + internal IntPtr GetDataHere; + internal IntPtr QueryGetData; + internal IntPtr GetCanonicalFormatEtc; + internal IntPtr SetData; + internal IntPtr EnumFormatEtc; + internal IntPtr DAdvise; + internal IntPtr DUnadvise; + internal IntPtr EnumDAdvise; + } + + private static bool Initialized; + private static MethodInfo GetDataMethod; + //private static MethodInfo GetDataHereMethod; + private static MethodInfo QueryGetDataMethod; + //private static MethodInfo GetCanonicalFormatEtcMethod; + //private static MethodInfo SetDataMethod; + //private static MethodInfo EnumFormatEtcMethod; + //private static MethodInfo DAdviseMethod; + //private static MethodInfo DUnadviseMethod; + //private static MethodInfo EnumDAdviseMethod; + private static object[] MethodArguments; + + private IDataObjectUnmanaged vtbl; + private IntPtr @this; + + internal ComIDataObjectUnmanaged(IntPtr data_object_ptr) { + if (!Initialized) { + Initialize(); + } + + vtbl = new IDataObjectUnmanaged(); + @this = data_object_ptr; + try { + vtbl = (IDataObjectUnmanaged)Marshal.PtrToStructure(Marshal.ReadIntPtr(data_object_ptr), typeof(IDataObjectUnmanaged)); + } + + catch (Exception e) { + Console.WriteLine("Exception {0}", e.Message); + } + } + + private static void Initialize() { + AssemblyName assembly; + AssemblyBuilder assembly_builder; + + if (Initialized) { + return; + } + + assembly = new AssemblyName(); + assembly.Name = "XplatUIWin32.FuncPtrInterface"; + assembly_builder = AppDomain.CurrentDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run); + + MethodArguments = new object[6]; + GetDataMethod = CreateFuncPtrInterface(assembly_builder, "GetData", typeof(uint), 3); + //GetDataHereMethod = CreateFuncPtrInterface(assembly_builder, "GetDataHere", typeof(uint), 3); + QueryGetDataMethod = CreateFuncPtrInterface(assembly_builder, "QueryGetData", typeof(uint), 2); + //GetCanonicalFormatEtcMethod = CreateFuncPtrInterface(assembly_builder, "GetCanonicalFormatEtc", typeof(uint), 3); + //SetDataMethod = CreateFuncPtrInterface(assembly_builder, "SetData", typeof(uint), 4); + //EnumFormatEtcMethod = CreateFuncPtrInterface(assembly_builder, "EnumFormatEtc", typeof(uint), 3); + //DAdviseMethod = CreateFuncPtrInterface(assembly_builder, "DAdvise", typeof(uint), 5); + //DUnadviseMethod = CreateFuncPtrInterface(assembly_builder, "DUnadvise", typeof(uint), 2); + //EnumDAdviseMethod = CreateFuncPtrInterface(assembly_builder, "EnumDAdvise", typeof(uint), 2); + + Initialized = true; + } + + internal uint QueryInterface(Guid riid, IntPtr ppvObject) { + uint ret; + IntPtr riid_ptr; + + riid_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))); + Marshal.StructureToPtr(riid, riid_ptr, false); + + MethodArguments[0] = vtbl.QueryInterface; + MethodArguments[1] = this.@this; + MethodArguments[2] = riid_ptr; + MethodArguments[3] = ppvObject; + + try { + ret = (uint)GetDataMethod.Invoke(null, MethodArguments); + } + + catch (Exception e) { + Console.WriteLine("Caught exception {0}", e.Message); + ret = E_FAIL; + } + + Marshal.FreeHGlobal(riid_ptr); + + return ret; + } + + internal uint AddRef() { + // We only use this for DnD, try and fake it + return 1; + } + + internal uint Release() { + // We only use this for DnD, try and fake it + return 0; + } + + internal uint GetData(FORMATETC pformatetcIn, ref STGMEDIUM pmedium) { + uint ret; + IntPtr pformatetcIn_ptr; + IntPtr pmedium_ptr; + + pformatetcIn_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FORMATETC))); + Marshal.StructureToPtr(pformatetcIn, pformatetcIn_ptr, false); + + pmedium_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(STGMEDIUM))); + + MethodArguments[0] = vtbl.GetData; + MethodArguments[1] = this.@this; + MethodArguments[2] = pformatetcIn_ptr; + MethodArguments[3] = pmedium_ptr; + + try { + ret = (uint)GetDataMethod.Invoke(null, MethodArguments); + Marshal.PtrToStructure(pmedium_ptr, pmedium); + } + + catch (Exception e) { + Console.WriteLine("Caught exception {0}", e.Message); + ret = E_FAIL; + } + + Marshal.FreeHGlobal(pformatetcIn_ptr); + Marshal.FreeHGlobal(pmedium_ptr); + + return ret; + } + + internal uint GetDataHere(FORMATETC pformatetc, ref STGMEDIUM pmedium) { + return E_NOTIMPL; + } + + internal uint QueryGetData(FORMATETC pformatetc) { + uint ret; + IntPtr pformatetc_ptr; + + pformatetc_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FORMATETC))); + Marshal.StructureToPtr(pformatetc, pformatetc_ptr, false); + + MethodArguments[0] = vtbl.GetData; + MethodArguments[1] = this.@this; + MethodArguments[2] = pformatetc_ptr; + + try { + ret = (uint)QueryGetDataMethod.Invoke(null, MethodArguments); + } + + catch (Exception e) { + Console.WriteLine("Caught exception {0}", e.Message); + ret = E_FAIL; + } + + Marshal.FreeHGlobal(pformatetc_ptr); + + return ret; + } + + internal uint GetCanonicalFormatEtc(FORMATETC pformatetcIn, ref FORMATETC pformatetcOut) { + return E_NOTIMPL; + } + + internal uint SetData(FORMATETC pformatetc, STGMEDIUM pmedium, bool release) { + return E_NOTIMPL; + } + + internal uint EnumFormatEtc(uint direction, IntPtr ppenumFormatEtc) { + return E_NOTIMPL; + } + + internal uint DAdvise(FORMATETC pformatetc, uint advf, IntPtr pAdvSink, ref uint pdwConnection) { + return OLE_E_ADVISENOTSUPPORTED; + } + + internal uint DUnadvise(uint pdwConnection) { + return OLE_E_ADVISENOTSUPPORTED; + } + + internal uint EnumDAdvise(IntPtr ppenumAdvise) { + return OLE_E_ADVISENOTSUPPORTED; + } + } + + + internal class ComIDropSource { + [StructLayout(LayoutKind.Sequential)] + internal struct IDropSource { + internal IntPtr vtbl; + internal IntPtr Window; + internal QueryInterfaceDelegate QueryInterface; + internal AddRefDelegate AddRef; + internal ReleaseDelegate Release; + internal QueryContinueDragDelegate QueryContinueDrag; + internal GiveFeedbackDelegate GiveFeedback; + } + + internal static IntPtr GetUnmanaged(IntPtr Window) { + IDropSource drop_source; + IntPtr drop_source_ptr; + long offset; + + drop_source = new IDropSource(); + drop_source.QueryInterface = Win32DnD.DSQueryInterface; + drop_source.AddRef = Win32DnD.DSAddRef; + drop_source.Release = Win32DnD.DSRelease; + drop_source.QueryContinueDrag = Win32DnD.QueryContinueDrag; + drop_source.GiveFeedback = Win32DnD.GiveFeedback; + drop_source.Window = Window; + + drop_source_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(drop_source)); + Marshal.StructureToPtr(drop_source, drop_source_ptr, false); + + // Update vtbl pointer + offset = drop_source_ptr.ToInt64(); + offset += 2 * Marshal.SizeOf(typeof(IntPtr)); + Marshal.WriteIntPtr(drop_source_ptr, new IntPtr(offset)); + + return drop_source_ptr; + } + + internal static void ReleaseUnmanaged(IntPtr drop_source_ptr) { + Marshal.FreeHGlobal(drop_source_ptr); + } + + internal static uint QueryInterface(IntPtr @this, ref Guid riid, IntPtr ppvObject) { + try { + if (IID_IUnknown.Equals(riid) || IID_IDropSource.Equals(riid)) { + Marshal.WriteIntPtr(ppvObject, @this); + return S_OK; + } + } + + catch (Exception e) { + Console.WriteLine("Got exception {0}", e.Message); + } + + Marshal.WriteIntPtr(ppvObject, IntPtr.Zero); + return E_NOINTERFACE; + } + + internal static uint AddRef(IntPtr @this) { + // We only use this for DnD, try and fake it + return 1; + } + + internal static uint Release(IntPtr @this) { + // We only use this for DnD, try and fake it + return 0; + } + + internal static uint QueryContinueDrag(IntPtr @this, bool fEscapePressed, uint grfkeyState) { + IntPtr window; + + window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); + + // LAMESPEC? - according to MSDN, when the any mousebutton is *pressed* it defaults to Drop. + // According to COM customary behaviour it's the other way round; which is what we do here + if (fEscapePressed) { + DragContinueEventArgs.drag_action = DragAction.Cancel; + } else if ((grfkeyState & (1+2+16)) == 0) { // Left, middle and right mouse button not pressed + DragContinueEventArgs.drag_action = DragAction.Drop; + } else { + DragContinueEventArgs.drag_action = DragAction.Continue; + } + + DragContinueEventArgs.escape_pressed = fEscapePressed; + DragContinueEventArgs.key_state = (int)grfkeyState; + + Widget.FromHandle(window).DndContinueDrag(DragContinueEventArgs); + + if (DragContinueEventArgs.drag_action == DragAction.Cancel) { + return DRAGDROP_S_CANCEL; + } else if (DragContinueEventArgs.drag_action == DragAction.Drop) { + return DRAGDROP_S_DROP; + } + return S_OK; + } + + internal static uint GiveFeedback(IntPtr @this, uint pdwEffect) { + IntPtr window; + + window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); + + DragFeedbackEventArgs.effect = (DragDropEffects)pdwEffect; + DragFeedbackEventArgs.use_default_cursors = true; + + Widget.FromHandle(window).DndFeedback(DragFeedbackEventArgs); + + if (DragFeedbackEventArgs.use_default_cursors) { + return DRAGDROP_S_USEDEFAULTCURSORS; + } + return S_OK; + } + } + + internal class ComIDropTarget { + [StructLayout(LayoutKind.Sequential)] + internal struct IDropTarget { + internal IntPtr vtbl; + internal IntPtr Window; + internal QueryInterfaceDelegate QueryInterface; + internal AddRefDelegate AddRef; + internal ReleaseDelegate Release; + + internal DragEnterDelegate DragEnter; + internal DragOverDelegate DragOver; + internal DragLeaveDelegate DragLeave; + internal DropDelegate Drop; + } + + internal static IntPtr GetUnmanaged(IntPtr Window) { + IDropTarget drop_target; + IntPtr drop_target_ptr; + long offset; + + drop_target = new IDropTarget(); + drop_target.QueryInterface = Win32DnD.DTQueryInterface; + drop_target.AddRef = Win32DnD.DTAddRef; + drop_target.Release = Win32DnD.DTRelease; + drop_target.DragEnter = Win32DnD.DragEnter; + drop_target.DragOver = Win32DnD.DragOver; + drop_target.DragLeave = Win32DnD.DragLeave; + drop_target.Drop = Win32DnD.Drop; + drop_target.Window = Window; + + drop_target_ptr = Marshal.AllocHGlobal(Marshal.SizeOf(drop_target)); + Marshal.StructureToPtr(drop_target, drop_target_ptr, false); + + // Update vtbl pointer + offset = drop_target_ptr.ToInt64(); + offset += 2 * Marshal.SizeOf(typeof(IntPtr)); + Marshal.WriteIntPtr(drop_target_ptr, new IntPtr(offset)); + + return drop_target_ptr; + } + + internal static void ReleaseUnmanaged(IntPtr drop_target_ptr) { + Marshal.FreeHGlobal(drop_target_ptr); + } + + internal static uint QueryInterface(IntPtr @this, ref Guid riid, IntPtr ppvObject) { + try { + if (IID_IUnknown.Equals(riid) || IID_IDropTarget.Equals(riid)) { + Marshal.WriteIntPtr(ppvObject, @this); + return S_OK; + } + } + + catch (Exception e) { + Console.WriteLine("Got exception {0}", e.Message); + } + + Marshal.WriteIntPtr(ppvObject, IntPtr.Zero); + return E_NOINTERFACE; + } + + internal static uint AddRef(IntPtr @this) { + // We only use this for DnD, try and fake it + return 1; + } + + internal static uint Release(IntPtr @this) { + // We only use this for DnD, try and fake it + return 0; + } + + internal static uint DragEnter(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect) { + IntPtr window; + + window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); + + DragDropEventArgs.x = pt_x.ToInt32(); + DragDropEventArgs.y = pt_y.ToInt32(); + DragDropEventArgs.allowed_effect = (DragDropEffects)Marshal.ReadIntPtr(pdwEffect).ToInt32(); + DragDropEventArgs.current_effect = DragDropEventArgs.AllowedEffect; + DragDropEventArgs.keystate = (int)grfkeyState; + + Widget.FromHandle(window).DndEnter(DragDropEventArgs); + + Marshal.WriteInt32(pdwEffect, (int)DragDropEventArgs.Effect); + + return S_OK; + } + + internal static uint DragOver(IntPtr @this, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect) { + IntPtr window; + + window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); + + DragDropEventArgs.x = pt_x.ToInt32(); + DragDropEventArgs.y = pt_y.ToInt32(); + DragDropEventArgs.allowed_effect = (DragDropEffects)Marshal.ReadIntPtr(pdwEffect).ToInt32(); + DragDropEventArgs.current_effect = DragDropEventArgs.AllowedEffect; + DragDropEventArgs.keystate = (int)grfkeyState; + + Widget.FromHandle(window).DndOver(DragDropEventArgs); + + Marshal.WriteInt32(pdwEffect, (int)DragDropEventArgs.Effect); + + return S_OK; + } + + internal static uint DragLeave(IntPtr @this) { + IntPtr window; + + window = Marshal.ReadIntPtr(@this, Marshal.SizeOf(typeof(IntPtr))); + + Widget.FromHandle(window).DndLeave(EventArgs.Empty); + + return S_OK; + } + + internal static uint Drop(IntPtr @this, IntPtr pDataObj, uint grfkeyState, IntPtr pt_x, IntPtr pt_y, IntPtr pdwEffect) + { + IntPtr window; + + window = Marshal.ReadIntPtr (@this, Marshal.SizeOf (typeof (IntPtr))); + + DragDropEventArgs.x = pt_x.ToInt32 (); + DragDropEventArgs.y = pt_y.ToInt32 (); + DragDropEventArgs.allowed_effect = (DragDropEffects) Marshal.ReadIntPtr (pdwEffect).ToInt32(); + DragDropEventArgs.current_effect = DragDropEventArgs.AllowedEffect; + DragDropEventArgs.keystate = (int) grfkeyState; + + Widget control = Widget.FromHandle (window); + if (control != null) { + control.DndDrop (DragDropEventArgs); + return S_FALSE; + } + + Marshal.WriteInt32 (pdwEffect, (int) DragDropEventArgs.Effect); + + return S_OK; + } + } + + internal static bool HandleWMDropFiles(ref MSG msg) { + IntPtr hDrop; + int count; + StringBuilder sb; + string[] dropfiles; + + hDrop = msg.wParam; + count = Win32DragQueryFile(hDrop, -1, IntPtr.Zero, 0); + + dropfiles = new string[count]; + + sb = new StringBuilder(256); + for (int i = 0; i < count; i++) { + Win32DragQueryFile(hDrop, i, sb, sb.Capacity); + dropfiles[i] = sb.ToString(); + } + + DragDropEventArgs.Data.SetData(DataFormats.FileDrop, dropfiles); + + Widget.FromHandle(msg.hwnd).DndDrop(DragDropEventArgs); + + return true; + } + + private static bool AddFormatAndMedium(ClipboardFormats cfFormat, object data) { + STGMEDIUM medium; + FORMATETC format; + IntPtr hmem; + IntPtr hmem_ptr; + byte[] b; + + switch(cfFormat) { + case ClipboardFormats.CF_TEXT: { + b = XplatUIWin32.StringToAnsi ((string)data); + hmem = XplatUIWin32.CopyToMoveableMemory (b); + break; + } + + case ClipboardFormats.CF_UNICODETEXT: { + b = XplatUIWin32.StringToUnicode ((string)data); + hmem = XplatUIWin32.CopyToMoveableMemory (b); + break; + } + + case ClipboardFormats.CF_HDROP: { + IEnumerator e; + StringBuilder sb; + long hmem_string_ptr; + IntPtr string_buffer; + int string_buffer_size; + + sb = new StringBuilder(); + + // Make sure object is enumerable; otherwise + if ((data is string) || !(data is IEnumerable)) { + sb.Append(data.ToString()); + sb.Append('\0'); + sb.Append('\0'); + } else { + e = ((IEnumerable)data).GetEnumerator(); + while (e.MoveNext()) { + sb.Append(e.Current.ToString()); + sb.Append('\0'); + } + sb.Append('\0'); + } + + string_buffer = Marshal.StringToHGlobalUni(sb.ToString()); + string_buffer_size = (int)XplatUIWin32.Win32GlobalSize(string_buffer); + + // Write DROPFILES structure + hmem = XplatUIWin32.Win32GlobalAlloc(XplatUIWin32.GAllocFlags.GMEM_MOVEABLE | XplatUIWin32.GAllocFlags.GMEM_DDESHARE, 0x14 + string_buffer_size); + hmem_ptr = XplatUIWin32.Win32GlobalLock(hmem); + Marshal.WriteInt32(hmem_ptr, 0x14); // pFiles + Marshal.WriteInt32(hmem_ptr, 1 * Marshal.SizeOf(typeof(uint)), 0); // point.x + Marshal.WriteInt32(hmem_ptr, 2 * Marshal.SizeOf(typeof(uint)), 0); // point.y + Marshal.WriteInt32(hmem_ptr, 3 * Marshal.SizeOf(typeof(uint)), 0); // fNc + Marshal.WriteInt32(hmem_ptr, 4 * Marshal.SizeOf(typeof(uint)), 1); // fWide + + hmem_string_ptr = (long)hmem_ptr; + hmem_string_ptr += 0x14; + + XplatUIWin32.Win32CopyMemory(new IntPtr(hmem_string_ptr), string_buffer, string_buffer_size); + Marshal.FreeHGlobal(string_buffer); + XplatUIWin32.Win32GlobalUnlock(hmem_ptr); + + break; + } + + case ClipboardFormats.CF_DIB: { + b = XplatUIWin32.ImageToDIB((Image)data); + hmem = XplatUIWin32.CopyToMoveableMemory (b); + break; + } + + default: { + hmem = IntPtr.Zero; + break; + } + } + + if (hmem != IntPtr.Zero) { + medium = new STGMEDIUM(); + medium.tymed = TYMED.TYMED_HGLOBAL; + medium.hHandle = hmem; + medium.pUnkForRelease = IntPtr.Zero; + DragMediums.Add(medium); + + format = new FORMATETC(); + format.ptd = IntPtr.Zero; + format.dwAspect = DVASPECT.DVASPECT_CONTENT; + format.lindex = -1; + format.tymed = TYMED.TYMED_HGLOBAL; + format.cfFormat = cfFormat; + DragFormats.Add(format); + + return true; + } + + return false; + } + + private static int FindFormat(FORMATETC pformatetc) { + for (int i = 0; i < DragFormats.Count; i++) { + if ((((FORMATETC)DragFormats[i]).cfFormat == pformatetc.cfFormat) && + (((FORMATETC)DragFormats[i]).dwAspect == pformatetc.dwAspect) && + ((((FORMATETC)DragFormats[i]).tymed & pformatetc.tymed)) != 0) { + return i; + } + } + return -1; + } + + private static void BuildFormats(object data) { + + DragFormats.Clear(); + DragMediums.Clear(); + + // Build our formats based on object data + if (data is String) { + AddFormatAndMedium(ClipboardFormats.CF_TEXT, data); + AddFormatAndMedium(ClipboardFormats.CF_UNICODETEXT, data); + AddFormatAndMedium(ClipboardFormats.CF_HDROP, data); + } else if (data is Bitmap) { + AddFormatAndMedium(ClipboardFormats.CF_DIB, data); + } else if (data is ICollection) { + // FIXME - test with .Net and found how this is handled + AddFormatAndMedium(ClipboardFormats.CF_HDROP, data); + } else if (data is ISerializable) { + // FIXME - test with .Net and found how this is handled + } + } + + internal static DragDropEffects StartDrag(IntPtr Window, object data, DragDropEffects allowed) { + IntPtr result; + IntPtr data_object; + IntPtr drop_source; + + BuildFormats(data); + + data_object = ComIDataObject.GetUnmanaged(); + drop_source = ComIDropSource.GetUnmanaged(Window); + + result = (IntPtr)DragDropEffects.None; + + Win32DoDragDrop(data_object, drop_source, (IntPtr)allowed, ref result); + + // Cleanup again + ComIDataObject.ReleaseUnmanaged(data_object); + ComIDropSource.ReleaseUnmanaged(drop_source); + DragFormats.Clear(); + DragFormatArray = null; + DragMediums.Clear(); + + return (DragDropEffects)result.ToInt32(); + } + + internal static bool UnregisterDropTarget(IntPtr Window) { + Win32RevokeDragDrop(Window); + return true; + } + + internal static bool RegisterDropTarget(IntPtr Window) { + Hwnd hwnd; + IntPtr drop_target; + uint result; + + hwnd = Hwnd.ObjectFromWindow(Window); + if (hwnd == null) { + return false; + } + + drop_target = ComIDropTarget.GetUnmanaged(Window); + hwnd.marshal_free_list.Add(drop_target); + result = Win32RegisterDragDrop(Window, drop_target); + + if (result != S_OK) { + return false; + } + return true; + } + + // Thanks, Martin + static MethodInfo CreateFuncPtrInterface(AssemblyBuilder assembly, string MethodName, Type ret_type, int param_count) { + ModuleBuilder mb; + TypeBuilder tb; + Type[] il_param_types; + Type[] param_types; + + mb = assembly.DefineDynamicModule("XplatUIWin32.FuncInterface" + MethodName); + tb = mb.DefineType ("XplatUIWin32.FuncInterface" + MethodName, TypeAttributes.Public); + + param_types = new Type[param_count]; + il_param_types = new Type[param_count + 1]; + + il_param_types[param_count] = typeof(IntPtr); + for (int i = 0; i < param_count; i++) { + param_types[i] = typeof(IntPtr); + il_param_types[i] = typeof(IntPtr); + } + + MethodBuilder method = tb.DefineMethod ( + MethodName, MethodAttributes.Static | MethodAttributes.Public, + ret_type, il_param_types); + + ILGenerator ig = method.GetILGenerator (); + if (param_count > 5) ig.Emit (OpCodes.Ldarg_S, 6); + if (param_count > 4) ig.Emit (OpCodes.Ldarg_S, 5); + if (param_count > 3) ig.Emit (OpCodes.Ldarg_S, 4); + if (param_count > 2) ig.Emit (OpCodes.Ldarg_3); + if (param_count > 1) ig.Emit (OpCodes.Ldarg_2); + if (param_count > 0) ig.Emit (OpCodes.Ldarg_1); + ig.Emit (OpCodes.Ldarg_0); + ig.EmitCalli (OpCodes.Calli, CallingConvention.StdCall, ret_type, param_types); + ig.Emit (OpCodes.Ret); + + Type t = tb.CreateType (); + MethodInfo m = t.GetMethod (MethodName); + + return m; + } + + [DllImport ("ole32.dll", EntryPoint="RegisterDragDrop", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32RegisterDragDrop(IntPtr Window, IntPtr pDropTarget); + + [DllImport ("ole32.dll", EntryPoint="RevokeDragDrop", CallingConvention=CallingConvention.StdCall)] + private extern static int Win32RevokeDragDrop(IntPtr Window); + + [DllImport ("ole32.dll", EntryPoint="DoDragDrop", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32DoDragDrop(IntPtr pDataObject, IntPtr pDropSource, IntPtr dwOKEffect, ref IntPtr pdwEffect); + + //[DllImport ("shell32.dll", EntryPoint="DragAcceptFiles", CallingConvention=CallingConvention.StdCall)] + //private extern static int Win32DragAcceptFiles(IntPtr Window, bool fAccept); + + [DllImport ("ole32.dll", EntryPoint="OleInitialize", CallingConvention=CallingConvention.StdCall)] + private extern static int Win32OleInitialize(IntPtr pvReserved); + + [DllImport ("shell32.dll", EntryPoint="DragQueryFileW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static int Win32DragQueryFile(IntPtr hDrop, int iFile, IntPtr lpszFile, int cch); + + [DllImport ("shell32.dll", EntryPoint="DragQueryFileW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static int Win32DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch); + + [DllImport ("shell32.dll", EntryPoint="SHCreateStdEnumFmtEtc", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32SHCreateStdEnumFmtEtc(uint cfmt, FORMATETC[] afmt, ref IntPtr ppenumFormatEtc); + } +} diff --git a/source/ShiftUI/Internal/WindowHandler.cs b/source/ShiftUI/Internal/WindowHandler.cs new file mode 100644 index 0000000..cb59ca4 --- /dev/null +++ b/source/ShiftUI/Internal/WindowHandler.cs @@ -0,0 +1,235 @@ +// 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) 2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; +using System.Drawing; +using ShiftUI; +using System.Runtime.InteropServices; + +namespace ShiftUI.CarbonInternal { + internal class WindowHandler : EventHandlerBase, IEventHandler { + internal const uint kEventWindowUpdate = 1; + internal const uint kEventWindowDrawContent = 2; + internal const uint kEventWindowActivated = 5; + internal const uint kEventWindowDeactivated = 6; + internal const uint kEventWindowGetClickActivation = 7; + internal const uint kEventWindowShowing = 22; + internal const uint kEventWindowHiding = 23; + internal const uint kEventWindowShown = 24; + internal const uint kEventWindowHidden = 25; + internal const uint kEventWindowCollapsing = 86; + internal const uint kEventWindowExpanding = 87; + internal const uint kEventWindowZoomed = 76; + internal const uint kEventWindowBoundsChanging = 26; + internal const uint kEventWindowBoundsChanged = 27; + internal const uint kEventWindowResizeStarted = 28; + internal const uint kEventWindowResizeCompleted = 29; + internal const uint kEventWindowDragStarted = 30; + internal const uint kEventWindowDragCompleted = 31; + internal const uint kEventWindowTransitionStarted = 88; + internal const uint kEventWindowTransitionCompleted = 89; + internal const uint kEventWindowClickDragRgn = 32; + internal const uint kEventWindowClickResizeRgn = 33; + internal const uint kEventWindowClickCollapseRgn = 34; + internal const uint kEventWindowClickCloseRgn = 35; + internal const uint kEventWindowClickZoomRgn = 36; + internal const uint kEventWindowClickContentRgn = 37; + internal const uint kEventWindowClickProxyIconRgn = 38; + internal const uint kEventWindowClickToolbarButtonRgn = 41; + internal const uint kEventWindowClickStructureRgn = 42; + internal const uint kEventWindowCursorChange = 40; + internal const uint kEventWindowCollapse = 66; + internal const uint kEventWindowCollapsed = 67; + internal const uint kEventWindowCollapseAll = 68; + internal const uint kEventWindowExpand = 69; + internal const uint kEventWindowExpanded = 70; + internal const uint kEventWindowExpandAll = 71; + internal const uint kEventWindowClose = 72; + internal const uint kEventWindowClosed = 73; + internal const uint kEventWindowCloseAll = 74; + internal const uint kEventWindowZoom = 75; + internal const uint kEventWindowZoomAll = 77; + internal const uint kEventWindowContextualMenuSelect = 78; + internal const uint kEventWindowPathSelect = 79; + internal const uint kEventWindowGetIdealSize = 80; + internal const uint kEventWindowGetMinimumSize = 81; + internal const uint kEventWindowGetMaximumSize = 82; + internal const uint kEventWindowConstrain = 83; + internal const uint kEventWindowHandleContentClick = 85; + internal const uint kEventWindowGetDockTileMenu = 90; + internal const uint kEventWindowHandleActivate = 91; + internal const uint kEventWindowHandleDeactivate = 92; + internal const uint kEventWindowProxyBeginDrag = 128; + internal const uint kEventWindowProxyEndDrag = 129; + internal const uint kEventWindowToolbarSwitchMode = 150; + internal const uint kEventWindowFocusAcquired = 200; + internal const uint kEventWindowFocusRelinquish = 201; + internal const uint kEventWindowFocusContent = 202; + internal const uint kEventWindowFocusToolbar = 203; + internal const uint kEventWindowDrawerOpening = 220; + internal const uint kEventWindowDrawerOpened = 221; + internal const uint kEventWindowDrawerClosing = 222; + internal const uint kEventWindowDrawerClosed = 223; + internal const uint kEventWindowDrawFrame = 1000; + internal const uint kEventWindowDrawPart = 1001; + internal const uint kEventWindowGetRegion = 1002; + internal const uint kEventWindowHitTest = 1003; + internal const uint kEventWindowInit = 1004; + internal const uint kEventWindowDispose = 1005; + internal const uint kEventWindowDragHilite = 1006; + internal const uint kEventWindowModified = 1007; + internal const uint kEventWindowSetupProxyDragImage = 1008; + internal const uint kEventWindowStateChanged = 1009; + internal const uint kEventWindowMeasureTitle = 1010; + internal const uint kEventWindowDrawGrowBox = 1011; + internal const uint kEventWindowGetGrowImageRegion = 1012; + internal const uint kEventWindowPaint = 1013; + + internal WindowHandler (XplatUICarbon driver) : base (driver) {} + + public bool ProcessEvent (IntPtr callref, IntPtr eventref, IntPtr handle, uint kind, ref MSG msg) { + IntPtr window = Driver.HandleToWindow (handle); + Hwnd hwnd = Hwnd.ObjectFromHandle (window); + if (window != IntPtr.Zero) { + switch (kind) { + case kEventWindowActivated: { + Widget c = Widget.FromHandle (hwnd.client_window); + if (c != null) { + Form form = c.FindForm (); + if (form != null && !form.IsDisposed) { + Driver.SendMessage (form.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero); + XplatUICarbon.ActiveWindow = hwnd.client_window; + } + } + + foreach (IntPtr utility_window in XplatUICarbon.UtilityWindows) { + if (utility_window != handle && !XplatUICarbon.IsWindowVisible (utility_window)) + XplatUICarbon.ShowWindow (utility_window); + } + break; + } + case kEventWindowExpanding: + foreach (IntPtr utility_window in XplatUICarbon.UtilityWindows) { + if (utility_window != handle && !XplatUICarbon.IsWindowVisible (utility_window)) + XplatUICarbon.ShowWindow (utility_window); + } + msg.hwnd = hwnd.Handle; + msg.message = Msg.WM_ENTERSIZEMOVE; + return true; + case kEventWindowExpanded: + NativeWindow.WndProc (hwnd.Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + msg.hwnd = hwnd.Handle; + msg.message = Msg.WM_EXITSIZEMOVE; + return true; + case kEventWindowDeactivated: { + Widget c = Widget.FromHandle (hwnd.client_window); + if (c != null) { + Form form = c.FindForm (); + if (form != null && XplatUICarbon.UnactiveWindow != form.Handle) { + Driver.SendMessage (form.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); + XplatUICarbon.ActiveWindow = IntPtr.Zero; + } + } + foreach (IntPtr utility_window in XplatUICarbon.UtilityWindows) { + if (utility_window != handle && XplatUICarbon.IsWindowVisible (utility_window)) + XplatUICarbon.HideWindow (utility_window); + } + break; + } + case kEventWindowCollapsing: + foreach (IntPtr utility_window in XplatUICarbon.UtilityWindows) { + if (utility_window != handle && XplatUICarbon.IsWindowVisible (utility_window)) + XplatUICarbon.HideWindow (utility_window); + } + msg.hwnd = hwnd.Handle; + msg.message = Msg.WM_ENTERSIZEMOVE; + return true; + case kEventWindowCollapsed: + NativeWindow.WndProc (hwnd.Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + msg.hwnd = hwnd.Handle; + msg.message = Msg.WM_EXITSIZEMOVE; + return true; + case kEventWindowClose: + NativeWindow.WndProc (hwnd.Handle, Msg.WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + return false; + case kEventWindowShown: { + msg.message = Msg.WM_SHOWWINDOW; + msg.lParam = (IntPtr) 1; + msg.wParam = (IntPtr) 0; + msg.hwnd = hwnd.Handle; + + return true; + } + case kEventWindowResizeStarted: { + msg.message = Msg.WM_ENTERSIZEMOVE; + msg.hwnd = hwnd.Handle; + return true; + } + case kEventWindowResizeCompleted: { + msg.message = Msg.WM_EXITSIZEMOVE; + msg.hwnd = hwnd.Handle; + + return true; + } + case kEventWindowBoundsChanged: { + Rect window_bounds = new Rect (); + HIRect view_bounds = new HIRect (); + Size size; + + GetWindowBounds (handle, 33, ref window_bounds); + + view_bounds.size.width = window_bounds.right - window_bounds.left; + view_bounds.size.height = window_bounds.bottom - window_bounds.top; + + HIViewSetFrame (hwnd.WholeWindow, ref view_bounds); + + size = XplatUICarbon.TranslateQuartzWindowSizeToWindowSize (Widget.FromHandle (hwnd.Handle).GetCreateParams (), (int)view_bounds.size.width, (int)view_bounds.size.height); + + hwnd.X = (int) window_bounds.left; + hwnd.Y = (int) window_bounds.top; + hwnd.Width = (int) size.Width; + hwnd.Height = (int) size.Height; + + Driver.PerformNCCalc (hwnd); + + msg.hwnd = hwnd.Handle; + msg.message = Msg.WM_WINDOWPOSCHANGED; + Driver.SetCaretPos (XplatUICarbon.Caret.Hwnd, XplatUICarbon.Caret.X, XplatUICarbon.Caret.Y); + + return true; + } + } + } + return false; + } + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int GetWindowBounds (IntPtr handle, uint region, ref Rect bounds); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int HIViewSetFrame (IntPtr handle, ref HIRect bounds); + } +} diff --git a/source/ShiftUI/Internal/WindowsFormsSynchronizationContext.cs b/source/ShiftUI/Internal/WindowsFormsSynchronizationContext.cs new file mode 100644 index 0000000..ec8ab1c --- /dev/null +++ b/source/ShiftUI/Internal/WindowsFormsSynchronizationContext.cs @@ -0,0 +1,94 @@ +// 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) 2007 Novell, Inc. +// + +using System; +using System.Threading; +using System.ComponentModel; + + +// Some implementation details: +// http://msdn.microsoft.com/msdnmag/issues/06/06/NETMatters/default.aspx +namespace ShiftUI +{ + public sealed class WindowsFormsSynchronizationContext : SynchronizationContext, IDisposable + { + private static bool auto_installed; + private static Widget invoke_control; + private static SynchronizationContext previous_context; + + #region Public Constructor + public WindowsFormsSynchronizationContext () + { + } + + static WindowsFormsSynchronizationContext () + { + try { + invoke_control = new Widget (); + invoke_control.CreateWidget (); + auto_installed = true; + previous_context = SynchronizationContext.Current; + } + catch (Exception ex) { + Console.WriteLine (ex.Message); + } + } + #endregion + + #region Public Properties + //[EditorBrowsable (EditorBrowsableState.Advanced)] + public static bool AutoInstall { + get { return auto_installed; } + set { auto_installed = value; } + } + #endregion + + #region Public Methods + public override SynchronizationContext CreateCopy () + { + return base.CreateCopy (); + } + + public void Dispose () + { + } + + public override void Post (SendOrPostCallback d, object state) + { + invoke_control.BeginInvoke (d, new object[] { state }); + } + + public override void Send (SendOrPostCallback d, object state) + { + invoke_control.Invoke (d, new object[] { state }); + } + + public static void Uninstall () + { + if (previous_context == null) + previous_context = new SynchronizationContext (); + + SynchronizationContext.SetSynchronizationContext (previous_context); + } + #endregion + } +} diff --git a/source/ShiftUI/Internal/X11Atoms.cs b/source/ShiftUI/Internal/X11Atoms.cs new file mode 100644 index 0000000..ec0232c --- /dev/null +++ b/source/ShiftUI/Internal/X11Atoms.cs @@ -0,0 +1,329 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// +// + +using System; +using ShiftUI; + +namespace ShiftUI.X11Internal { + + internal class X11Atoms { + + // Our atoms + public readonly IntPtr AnyPropertyType = (IntPtr)0; + public readonly IntPtr XA_PRIMARY = (IntPtr)1; + public readonly IntPtr XA_SECONDARY = (IntPtr)2; + public readonly IntPtr XA_ARC = (IntPtr)3; + public readonly IntPtr XA_ATOM = (IntPtr)4; + public readonly IntPtr XA_BITMAP = (IntPtr)5; + public readonly IntPtr XA_CARDINAL = (IntPtr)6; + public readonly IntPtr XA_COLORMAP = (IntPtr)7; + public readonly IntPtr XA_CURSOR = (IntPtr)8; + public readonly IntPtr XA_CUT_BUFFER0 = (IntPtr)9; + public readonly IntPtr XA_CUT_BUFFER1 = (IntPtr)10; + public readonly IntPtr XA_CUT_BUFFER2 = (IntPtr)11; + public readonly IntPtr XA_CUT_BUFFER3 = (IntPtr)12; + public readonly IntPtr XA_CUT_BUFFER4 = (IntPtr)13; + public readonly IntPtr XA_CUT_BUFFER5 = (IntPtr)14; + public readonly IntPtr XA_CUT_BUFFER6 = (IntPtr)15; + public readonly IntPtr XA_CUT_BUFFER7 = (IntPtr)16; + public readonly IntPtr XA_DRAWABLE = (IntPtr)17; + public readonly IntPtr XA_FONT = (IntPtr)18; + public readonly IntPtr XA_INTEGER = (IntPtr)19; + public readonly IntPtr XA_PIXMAP = (IntPtr)20; + public readonly IntPtr XA_POINT = (IntPtr)21; + public readonly IntPtr XA_RECTANGLE = (IntPtr)22; + public readonly IntPtr XA_RESOURCE_MANAGER = (IntPtr)23; + public readonly IntPtr XA_RGB_COLOR_MAP = (IntPtr)24; + public readonly IntPtr XA_RGB_BEST_MAP = (IntPtr)25; + public readonly IntPtr XA_RGB_BLUE_MAP = (IntPtr)26; + public readonly IntPtr XA_RGB_DEFAULT_MAP = (IntPtr)27; + public readonly IntPtr XA_RGB_GRAY_MAP = (IntPtr)28; + public readonly IntPtr XA_RGB_GREEN_MAP = (IntPtr)29; + public readonly IntPtr XA_RGB_RED_MAP = (IntPtr)30; + public readonly IntPtr XA_STRING = (IntPtr)31; + public readonly IntPtr XA_VISUALID = (IntPtr)32; + public readonly IntPtr XA_WINDOW = (IntPtr)33; + public readonly IntPtr XA_WM_COMMAND = (IntPtr)34; + public readonly IntPtr XA_WM_HINTS = (IntPtr)35; + public readonly IntPtr XA_WM_CLIENT_MACHINE = (IntPtr)36; + public readonly IntPtr XA_WM_ICON_NAME = (IntPtr)37; + public readonly IntPtr XA_WM_ICON_SIZE = (IntPtr)38; + public readonly IntPtr XA_WM_NAME = (IntPtr)39; + public readonly IntPtr XA_WM_NORMAL_HINTS = (IntPtr)40; + public readonly IntPtr XA_WM_SIZE_HINTS = (IntPtr)41; + public readonly IntPtr XA_WM_ZOOM_HINTS = (IntPtr)42; + public readonly IntPtr XA_MIN_SPACE = (IntPtr)43; + public readonly IntPtr XA_NORM_SPACE = (IntPtr)44; + public readonly IntPtr XA_MAX_SPACE = (IntPtr)45; + public readonly IntPtr XA_END_SPACE = (IntPtr)46; + public readonly IntPtr XA_SUPERSCRIPT_X = (IntPtr)47; + public readonly IntPtr XA_SUPERSCRIPT_Y = (IntPtr)48; + public readonly IntPtr XA_SUBSCRIPT_X = (IntPtr)49; + public readonly IntPtr XA_SUBSCRIPT_Y = (IntPtr)50; + public readonly IntPtr XA_UNDERLINE_POSITION = (IntPtr)51; + public readonly IntPtr XA_UNDERLINE_THICKNESS = (IntPtr)52; + public readonly IntPtr XA_STRIKEOUT_ASCENT = (IntPtr)53; + public readonly IntPtr XA_STRIKEOUT_DESCENT = (IntPtr)54; + public readonly IntPtr XA_ITALIC_ANGLE = (IntPtr)55; + public readonly IntPtr XA_X_HEIGHT = (IntPtr)56; + public readonly IntPtr XA_QUAD_WIDTH = (IntPtr)57; + public readonly IntPtr XA_WEIGHT = (IntPtr)58; + public readonly IntPtr XA_POINT_SIZE = (IntPtr)59; + public readonly IntPtr XA_RESOLUTION = (IntPtr)60; + public readonly IntPtr XA_COPYRIGHT = (IntPtr)61; + public readonly IntPtr XA_NOTICE = (IntPtr)62; + public readonly IntPtr XA_FONT_NAME = (IntPtr)63; + public readonly IntPtr XA_FAMILY_NAME = (IntPtr)64; + public readonly IntPtr XA_FULL_NAME = (IntPtr)65; + public readonly IntPtr XA_CAP_HEIGHT = (IntPtr)66; + public readonly IntPtr XA_WM_CLASS = (IntPtr)67; + public readonly IntPtr XA_WM_TRANSIENT_FOR = (IntPtr)68; + + public readonly IntPtr WM_PROTOCOLS; + public readonly IntPtr WM_DELETE_WINDOW; + public readonly IntPtr WM_TAKE_FOCUS; + public readonly IntPtr _NET_SUPPORTED; + public readonly IntPtr _NET_CLIENT_LIST; + public readonly IntPtr _NET_NUMBER_OF_DESKTOPS; + public readonly IntPtr _NET_DESKTOP_GEOMETRY; + public readonly IntPtr _NET_DESKTOP_VIEWPORT; + public readonly IntPtr _NET_CURRENT_DESKTOP; + public readonly IntPtr _NET_DESKTOP_NAMES; + public readonly IntPtr _NET_ACTIVE_WINDOW; + public readonly IntPtr _NET_WORKAREA; + public readonly IntPtr _NET_SUPPORTING_WM_CHECK; + public readonly IntPtr _NET_VIRTUAL_ROOTS; + public readonly IntPtr _NET_DESKTOP_LAYOUT; + public readonly IntPtr _NET_SHOWING_DESKTOP; + public readonly IntPtr _NET_CLOSE_WINDOW; + public readonly IntPtr _NET_MOVERESIZE_WINDOW; + public readonly IntPtr _NET_WM_MOVERESIZE; + public readonly IntPtr _NET_RESTACK_WINDOW; + public readonly IntPtr _NET_REQUEST_FRAME_EXTENTS; + public readonly IntPtr _NET_WM_NAME; + public readonly IntPtr _NET_WM_VISIBLE_NAME; + public readonly IntPtr _NET_WM_ICON_NAME; + public readonly IntPtr _NET_WM_VISIBLE_ICON_NAME; + public readonly IntPtr _NET_WM_DESKTOP; + public readonly IntPtr _NET_WM_WINDOW_TYPE; + public readonly IntPtr _NET_WM_STATE; + public readonly IntPtr _NET_WM_ALLOWED_ACTIONS; + public readonly IntPtr _NET_WM_STRUT; + public readonly IntPtr _NET_WM_STRUT_PARTIAL; + public readonly IntPtr _NET_WM_ICON_GEOMETRY; + public readonly IntPtr _NET_WM_ICON; + public readonly IntPtr _NET_WM_PID; + public readonly IntPtr _NET_WM_HANDLED_ICONS; + public readonly IntPtr _NET_WM_USER_TIME; + public readonly IntPtr _NET_FRAME_EXTENTS; + public readonly IntPtr _NET_WM_PING; + public readonly IntPtr _NET_WM_SYNC_REQUEST; + public readonly IntPtr _NET_SYSTEM_TRAY_S; + public readonly IntPtr _NET_SYSTEM_TRAY_ORIENTATION; + public readonly IntPtr _NET_SYSTEM_TRAY_OPCODE; + public readonly IntPtr _NET_WM_STATE_MAXIMIZED_HORZ; + public readonly IntPtr _NET_WM_STATE_MAXIMIZED_VERT; + public readonly IntPtr _XEMBED; + public readonly IntPtr _XEMBED_INFO; + public readonly IntPtr _MOTIF_WM_HINTS; + public readonly IntPtr _NET_WM_STATE_SKIP_TASKBAR; + public readonly IntPtr _NET_WM_STATE_ABOVE; + public readonly IntPtr _NET_WM_STATE_MODAL; + public readonly IntPtr _NET_WM_STATE_HIDDEN; + public readonly IntPtr _NET_WM_CONTEXT_HELP; + public readonly IntPtr _NET_WM_WINDOW_OPACITY; + public readonly IntPtr _NET_WM_WINDOW_TYPE_DESKTOP; + public readonly IntPtr _NET_WM_WINDOW_TYPE_DOCK; + public readonly IntPtr _NET_WM_WINDOW_TYPE_TOOLBAR; + public readonly IntPtr _NET_WM_WINDOW_TYPE_MENU; + public readonly IntPtr _NET_WM_WINDOW_TYPE_UTILITY; + public readonly IntPtr _NET_WM_WINDOW_TYPE_SPLASH; + public readonly IntPtr _NET_WM_WINDOW_TYPE_DIALOG; + public readonly IntPtr _NET_WM_WINDOW_TYPE_NORMAL; + public readonly IntPtr CLIPBOARD; + public readonly IntPtr PRIMARY; + public readonly IntPtr DIB; + public readonly IntPtr OEMTEXT; + public readonly IntPtr UNICODETEXT; + public readonly IntPtr TARGETS; + public readonly IntPtr PostAtom; + public readonly IntPtr AsyncAtom; + + + public X11Atoms (X11Display display) { + + // make sure this array stays in sync with the statements below + string [] atom_names = new string[] { + "WM_PROTOCOLS", + "WM_DELETE_WINDOW", + "WM_TAKE_FOCUS", + "_NET_SUPPORTED", + "_NET_CLIENT_LIST", + "_NET_NUMBER_OF_DESKTOPS", + "_NET_DESKTOP_GEOMETRY", + "_NET_DESKTOP_VIEWPORT", + "_NET_CURRENT_DESKTOP", + "_NET_DESKTOP_NAMES", + "_NET_ACTIVE_WINDOW", + "_NET_WORKAREA", + "_NET_SUPPORTING_WM_CHECK", + "_NET_VIRTUAL_ROOTS", + "_NET_DESKTOP_LAYOUT", + "_NET_SHOWING_DESKTOP", + "_NET_CLOSE_WINDOW", + "_NET_MOVERESIZE_WINDOW", + "_NET_WM_MOVERESIZE", + "_NET_RESTACK_WINDOW", + "_NET_REQUEST_FRAME_EXTENTS", + "_NET_WM_NAME", + "_NET_WM_VISIBLE_NAME", + "_NET_WM_ICON_NAME", + "_NET_WM_VISIBLE_ICON_NAME", + "_NET_WM_DESKTOP", + "_NET_WM_WINDOW_TYPE", + "_NET_WM_STATE", + "_NET_WM_ALLOWED_ACTIONS", + "_NET_WM_STRUT", + "_NET_WM_STRUT_PARTIAL", + "_NET_WM_ICON_GEOMETRY", + "_NET_WM_ICON", + "_NET_WM_PID", + "_NET_WM_HANDLED_ICONS", + "_NET_WM_USER_TIME", + "_NET_FRAME_EXTENTS", + "_NET_WM_PING", + "_NET_WM_SYNC_REQUEST", + "_NET_SYSTEM_TRAY_OPCODE", + "_NET_SYSTEM_TRAY_ORIENTATION", + "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_STATE_MAXIMIZED_VERT", + "_NET_WM_STATE_HIDDEN", + "_XEMBED", + "_XEMBED_INFO", + "_MOTIF_WM_HINTS", + "_NET_WM_STATE_SKIP_TASKBAR", + "_NET_WM_STATE_ABOVE", + "_NET_WM_STATE_MODAL", + "_NET_WM_CONTEXT_HELP", + "_NET_WM_WINDOW_OPACITY", + "_NET_WM_WINDOW_TYPE_DESKTOP", + "_NET_WM_WINDOW_TYPE_DOCK", + "_NET_WM_WINDOW_TYPE_TOOLBAR", + "_NET_WM_WINDOW_TYPE_MENU", + "_NET_WM_WINDOW_TYPE_UTILITY", + "_NET_WM_WINDOW_TYPE_DIALOG", + "_NET_WM_WINDOW_TYPE_SPLASH", + "_NET_WM_WINDOW_TYPE_NORMAL", + "CLIPBOARD", + "PRIMARY", + "COMPOUND_TEXT", + "UTF8_STRING", + "TARGETS", + "_SWF_AsyncAtom", + "_SWF_PostMessageAtom", + "_SWF_HoverAtom" }; + + IntPtr[] atoms = new IntPtr [atom_names.Length];; + + Xlib.XInternAtoms (display.Handle, atom_names, atom_names.Length, false, atoms); + + int off = 0; + WM_PROTOCOLS = atoms [off++]; + WM_DELETE_WINDOW = atoms [off++]; + WM_TAKE_FOCUS = atoms [off++]; + _NET_SUPPORTED = atoms [off++]; + _NET_CLIENT_LIST = atoms [off++]; + _NET_NUMBER_OF_DESKTOPS = atoms [off++]; + _NET_DESKTOP_GEOMETRY = atoms [off++]; + _NET_DESKTOP_VIEWPORT = atoms [off++]; + _NET_CURRENT_DESKTOP = atoms [off++]; + _NET_DESKTOP_NAMES = atoms [off++]; + _NET_ACTIVE_WINDOW = atoms [off++]; + _NET_WORKAREA = atoms [off++]; + _NET_SUPPORTING_WM_CHECK = atoms [off++]; + _NET_VIRTUAL_ROOTS = atoms [off++]; + _NET_DESKTOP_LAYOUT = atoms [off++]; + _NET_SHOWING_DESKTOP = atoms [off++]; + _NET_CLOSE_WINDOW = atoms [off++]; + _NET_MOVERESIZE_WINDOW = atoms [off++]; + _NET_WM_MOVERESIZE = atoms [off++]; + _NET_RESTACK_WINDOW = atoms [off++]; + _NET_REQUEST_FRAME_EXTENTS = atoms [off++]; + _NET_WM_NAME = atoms [off++]; + _NET_WM_VISIBLE_NAME = atoms [off++]; + _NET_WM_ICON_NAME = atoms [off++]; + _NET_WM_VISIBLE_ICON_NAME = atoms [off++]; + _NET_WM_DESKTOP = atoms [off++]; + _NET_WM_WINDOW_TYPE = atoms [off++]; + _NET_WM_STATE = atoms [off++]; + _NET_WM_ALLOWED_ACTIONS = atoms [off++]; + _NET_WM_STRUT = atoms [off++]; + _NET_WM_STRUT_PARTIAL = atoms [off++]; + _NET_WM_ICON_GEOMETRY = atoms [off++]; + _NET_WM_ICON = atoms [off++]; + _NET_WM_PID = atoms [off++]; + _NET_WM_HANDLED_ICONS = atoms [off++]; + _NET_WM_USER_TIME = atoms [off++]; + _NET_FRAME_EXTENTS = atoms [off++]; + _NET_WM_PING = atoms [off++]; + _NET_WM_SYNC_REQUEST = atoms [off++]; + _NET_SYSTEM_TRAY_OPCODE = atoms [off++]; + _NET_SYSTEM_TRAY_ORIENTATION = atoms [off++]; + _NET_WM_STATE_MAXIMIZED_HORZ = atoms [off++]; + _NET_WM_STATE_MAXIMIZED_VERT = atoms [off++]; + _NET_WM_STATE_HIDDEN = atoms [off++]; + _XEMBED = atoms [off++]; + _XEMBED_INFO = atoms [off++]; + _MOTIF_WM_HINTS = atoms [off++]; + _NET_WM_STATE_SKIP_TASKBAR = atoms [off++]; + _NET_WM_STATE_ABOVE = atoms [off++]; + _NET_WM_STATE_MODAL = atoms [off++]; + _NET_WM_CONTEXT_HELP = atoms [off++]; + _NET_WM_WINDOW_OPACITY = atoms [off++]; + _NET_WM_WINDOW_TYPE_DESKTOP = atoms [off++]; + _NET_WM_WINDOW_TYPE_DOCK = atoms [off++]; + _NET_WM_WINDOW_TYPE_TOOLBAR = atoms [off++]; + _NET_WM_WINDOW_TYPE_MENU = atoms [off++]; + _NET_WM_WINDOW_TYPE_UTILITY = atoms [off++]; + _NET_WM_WINDOW_TYPE_DIALOG = atoms [off++]; + _NET_WM_WINDOW_TYPE_SPLASH = atoms [off++]; + _NET_WM_WINDOW_TYPE_NORMAL = atoms [off++]; + CLIPBOARD = atoms [off++]; + PRIMARY = atoms [off++]; + OEMTEXT = atoms [off++]; + UNICODETEXT = atoms [off++]; + TARGETS = atoms [off++]; + AsyncAtom = atoms [off++]; + PostAtom = atoms [off++]; + display.HoverState.Atom = atoms [off++]; + + DIB = XA_PIXMAP; + + // XXX multi screen stuff here + _NET_SYSTEM_TRAY_S = Xlib.XInternAtom (display.Handle, "_NET_SYSTEM_TRAY_S" + display.DefaultScreen.ToString(), false); + } + + } + +} + diff --git a/source/ShiftUI/Internal/X11Clipboard.cs b/source/ShiftUI/Internal/X11Clipboard.cs new file mode 100644 index 0000000..b951cc3 --- /dev/null +++ b/source/ShiftUI/Internal/X11Clipboard.cs @@ -0,0 +1,105 @@ +// 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) 2009 Novell, Inc. +// +// Authors: +// Carlos Alberto Cortez ([email protected]) +// + +using System; +using System.Drawing; +using System.Collections; +using System.Collections.Specialized; + +namespace ShiftUI { + + internal class ClipboardData { + ListDictionary source_data; // Source in its different formats, if any + string plain_text_source; // Cached source as plain-text string + Image image_source; // Cached source as image + + internal object Item; // Object on the clipboard + internal ArrayList Formats; // list of formats available in the clipboard + internal bool Retrieving; // true if we are requesting an item + internal bool Enumerating; // true if we are enumerating through all known types + internal XplatUI.ObjectToClipboard Converter; + + public ClipboardData () + { + source_data = new ListDictionary (); + } + + public void ClearSources () + { + source_data.Clear (); + plain_text_source = null; + image_source = null; + } + + public void AddSource (int type, object source) + { + // Try to detect plain text, based on the old behaviour of XplatUIX11, which usually assigns + // -1 as the type when a string is stored in the Clipboard + if (source is string && (type == DataFormats.GetFormat (DataFormats.Text).Id || type == -1)) + plain_text_source = source as string; + else if (source is Image) + image_source = source as Image; + + source_data [type] = source; + } + + public object GetSource (int type) + { + return source_data [type]; + } + + public string GetPlainText () + { + return plain_text_source; + } + + public string GetRtfText () + { + DataFormats.Format format = DataFormats.GetFormat (DataFormats.Rtf); + if (format == null) + return null; // FIXME - is RTF not supported on any system? + + return (string)GetSource (format.Id); + } + + public Image GetImage () + { + return image_source; + } + + public bool IsSourceText { + get { + return plain_text_source != null; + } + } + + public bool IsSourceImage { + get { + return image_source != null; + } + } + } +} + diff --git a/source/ShiftUI/Internal/X11DesktopColors.cs b/source/ShiftUI/Internal/X11DesktopColors.cs new file mode 100644 index 0000000..d3cdd26 --- /dev/null +++ b/source/ShiftUI/Internal/X11DesktopColors.cs @@ -0,0 +1,298 @@ +// 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) 2005 Novell, Inc. (http://www.novell.com) +// +// Authors: +// Peter Dennis Bartok ([email protected]) +// Alexander Olk ([email protected]) +// +// + +using System.Drawing; +using System.Runtime.InteropServices; +using System.IO; +using System; + +namespace ShiftUI { + internal class X11DesktopColors { + #region Structs & Enums + [StructLayout(LayoutKind.Sequential)] + internal struct GdkColorStruct { + internal int pixel; + internal short red; + internal short green; + internal short blue; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct GObjectStruct { + public IntPtr Instance; + public IntPtr ref_count; + public IntPtr data; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct GtkStyleStruct { + internal GObjectStruct obj; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] fg; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] bg; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] light; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] dark; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] mid; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] text; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] baseclr; + [MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=5)] + internal GdkColorStruct[] text_aa; /* Halfway between text/base */ + + internal GdkColorStruct black; + internal GdkColorStruct white; + + /* TODO: There is more stuff that we will add when we need it*/ + } + + private enum Desktop { + Gtk, + KDE, + Unknown + } + #endregion // Structs & Enums + + #region Local Variables + static private Desktop desktop; + #endregion // Local Variables + + #region Constructors + static X11DesktopColors() { + FindDesktopEnvironment(); + + switch(desktop) { + case Desktop.Gtk: { + //IntPtr dispmgr; + //IntPtr gdkdisplay; + IntPtr widget; + IntPtr style_ptr; + GtkStyleStruct style; + + try { + GtkInit(); + //dispmgr = gdk_display_manager_get (); + //gdkdisplay = gdk_display_manager_get_default_display (dispmgr); + + widget = gtk_invisible_new (); + gtk_widget_ensure_style (widget); + style_ptr = gtk_widget_get_style (widget); + + style = (GtkStyleStruct) Marshal.PtrToStructure (style_ptr, typeof (GtkStyleStruct)); + + ThemeEngine.Current.ColorControl = ColorFromGdkColor (style.bg[0]); + ThemeEngine.Current.ColorControlText = ColorFromGdkColor (style.fg[0]); + ThemeEngine.Current.ColorControlDark = ColorFromGdkColor (style.dark[0]); + ThemeEngine.Current.ColorControlLight = ColorFromGdkColor (style.light[0]); + ThemeEngine.Current.ColorControlLightLight = WidgetPaint.Light (ThemeEngine.Current.ColorControlLight); + ThemeEngine.Current.ColorControlDarkDark = WidgetPaint.Dark (ThemeEngine.Current.ColorControlDark); + + // We don't want WidgetLight to disappear on a white background! + if (ThemeEngine.Current.ColorControlLight.ToArgb () == Color.White.ToArgb ()) { + ThemeEngine.Current.ColorControlLight = Color.FromArgb (255, 227, 227, 227); + } + widget = gtk_menu_new (); + gtk_widget_ensure_style (widget); + style_ptr = gtk_widget_get_style (widget); + + style = (GtkStyleStruct) Marshal.PtrToStructure (style_ptr, typeof (GtkStyleStruct)); + + ThemeEngine.Current.ColorMenu = ColorFromGdkColor (style.bg [0]); + ThemeEngine.Current.ColorMenuText = ColorFromGdkColor (style.text [0]); + } + + catch (DllNotFoundException) { + Console.Error.WriteLine("Gtk not found (missing LD_LIBRARY_PATH to libgtk-x11-2.0.so.0?), using built-in colorscheme"); + } + + catch { + Console.Error.WriteLine("Gtk colorscheme read failure, using built-in colorscheme"); + } + break; + } + + case Desktop.KDE: { + if (! ReadKDEColorsheme() ) + Console.Error.WriteLine("KDE colorscheme read failure, using built-in colorscheme"); + break; + } + + default: { + break; + } + } + } + + static void GtkInit () + { + gtk_init_check (IntPtr.Zero, IntPtr.Zero); + } + #endregion // Constructors + + #region Properties + static void FindDesktopEnvironment() { + desktop = Desktop.Gtk; + string session = Environment.GetEnvironmentVariable("DESKTOP_SESSION"); + + if ( session != null ) { + session = session.ToUpper( ); + + if ( session == "DEFAULT" ) { + string helper = Environment.GetEnvironmentVariable("KDE_FULL_SESSION"); + + if ( helper != null ) + desktop = Desktop.KDE; + } else + if ( session.StartsWith("KDE") ) + desktop = Desktop.KDE; + } + } + #endregion // Properties + + #region Methods + static internal void Initialize() { + // Do nothing; all is done in our static ctor + } + + private static Color ColorFromGdkColor (GdkColorStruct gtkcolor) { + return Color.FromArgb (255, + (gtkcolor.red >> 8) & 0xff, + (gtkcolor.green >> 8) & 0xff, + (gtkcolor.blue >> 8) & 0xff ); + } + + private static bool ReadKDEColorsheme() { + string full_kdegloabals_filename = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + + "/" + + ".kde/share/config/kdeglobals"; + + if (!File.Exists(full_kdegloabals_filename)) + return false; + + StreamReader sr = new StreamReader(full_kdegloabals_filename); + + string line = sr.ReadLine(); + + Color tmp_color; + + while (line != null) { + line = line.Trim(); + + if (line.StartsWith( "background=")) { + tmp_color = GetColorFromKDEString(line); + + if (tmp_color != Color.Empty) { + ThemeEngine.Current.ColorControl = tmp_color; + ThemeEngine.Current.ColorMenu = tmp_color; + } + } else + if (line.StartsWith( "foreground=")) { + tmp_color = GetColorFromKDEString(line); + + if (tmp_color != Color.Empty) { + ThemeEngine.Current.ColorControlText = tmp_color; + ThemeEngine.Current.ColorMenuText = tmp_color; + } + } else + if (line.StartsWith("selectBackground")) { + tmp_color = GetColorFromKDEString(line); + + if (tmp_color != Color.Empty) { + ThemeEngine.Current.ColorHighlight = tmp_color; + } + } else + if (line.StartsWith("selectForeground")) { + tmp_color = GetColorFromKDEString(line); + + if (tmp_color != Color.Empty) { + ThemeEngine.Current.ColorHighlightText = tmp_color; + } + } + + line = sr.ReadLine(); + } + + sr.Close(); + + return true; + } + + private static Color GetColorFromKDEString(string line) { + string[] split = line.Split(new char[] {'='}); + + if (split.Length > 0) { + line = split[1]; + + split = line.Split(new char[] {','}); + + if (split.Length == 3) { + int r = System.Convert.ToInt32(split[0]); + int g = System.Convert.ToInt32(split[1]); + int b = System.Convert.ToInt32(split[2]); + + return Color.FromArgb(r, g, b); + } + } + + return Color.Empty; + } + #endregion // Methods + + #region DllImports + const string libgdk = "libgdk-x11-2.0.so.0"; + const string libgtk = "libgtk-x11-2.0.so.0"; + + [DllImport(libgtk)] + static extern bool gtk_init_check (IntPtr argc, IntPtr argv); + + [DllImport(libgdk)] + internal static extern IntPtr gdk_display_manager_get (); + + [DllImport(libgdk)] + internal static extern IntPtr gdk_display_manager_get_default_display (IntPtr display_manager); + + [DllImport(libgtk)] + static extern IntPtr gtk_invisible_new (); + + [DllImport(libgtk)] + static extern IntPtr gtk_menu_new (); + + //[DllImport(libgtk)] + //static extern IntPtr gtk_menu_item_new_with_label (string label); + + [DllImport(libgtk)] + static extern void gtk_widget_ensure_style (IntPtr raw); + + [DllImport(libgtk)] + static extern IntPtr gtk_widget_get_style (IntPtr raw); + #endregion // DllImports + } +} diff --git a/source/ShiftUI/Internal/X11Display.cs b/source/ShiftUI/Internal/X11Display.cs new file mode 100644 index 0000000..221e5f1 --- /dev/null +++ b/source/ShiftUI/Internal/X11Display.cs @@ -0,0 +1,2693 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// +// +using System; +using System.Collections; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using ShiftUI; +// Only do the poll when building with mono for now +#if __MonoCS__ +//using Mono.Unix.Native; +#endif + +namespace ShiftUI.X11Internal { + + internal class X11Display { + + IntPtr display; /* our X handle */ + + // XXX internal because X11Hwnd needs them + internal IntPtr CustomVisual; // Visual for window creation + internal IntPtr CustomColormap; // Colormap for window creation + + X11Keyboard Keyboard; + internal X11Dnd Dnd; // XXX X11Hwnd needs it to enable Dnd + bool detectable_key_auto_repeat; + + X11Atoms atoms; + X11RootHwnd root_hwnd; + X11Hwnd foster_hwnd; + + // Clipboard + IntPtr ClipMagic; + + // Focus tracking + internal X11Hwnd ActiveWindow; + X11Hwnd FocusWindow; + + // Modality support + Stack ModalWindows; // Stack of our modal windows + + // Caret + CaretStruct Caret; + + // mouse hover message generation + // XXX internal because X11Atoms needs to access it.. + internal HoverStruct HoverState; + + // double click message generation + ClickStruct ClickPending; + int DoubleClickInterval; // msec; max interval between clicks to count as double click + + // Support for mouse grab + GrabStruct Grab; + + // Cursors + IntPtr LastCursorWindow; // The last window we set the cursor on + IntPtr LastCursorHandle; // The handle that was last set on LastCursorWindow + IntPtr OverrideCursorHandle; // The cursor that is set to override any other cursors + + // State + Point MousePosition; // Last position of mouse, in screen coords + MouseButtons MouseState; // Last state of mouse buttons + + XErrorHandler ErrorHandler; // Error handler delegate + bool ErrorExceptions; // Throw exceptions on X errors + + Thread event_thread; // the background thread that just watches our X socket + +#if __MonoCS__ + Pollfd[] pollfds; +#endif + + public X11Display (IntPtr display) + { + if (display == IntPtr.Zero) { + throw new ArgumentNullException("Display", + "Could not open display (X-Server required. Check your DISPLAY environment variable)"); + } + + this.display = display; + + // Debugging support + if (Environment.GetEnvironmentVariable ("MONO_XSYNC") != null) { + Xlib.XSynchronize (display, true); + } + + if (Environment.GetEnvironmentVariable ("MONO_XEXCEPTIONS") != null) { + ErrorExceptions = true; + } + + atoms = new X11Atoms (this); + + DoubleClickInterval = 500; + + HoverState.Interval = 500; + HoverState.Timer = new Timer(); + HoverState.Timer.Enabled = false; + HoverState.Timer.Interval = HoverState.Interval; + HoverState.Timer.Tick += new EventHandler(MouseHover); + HoverState.Size = new Size(4, 4); + HoverState.X = -1; + HoverState.Y = -1; + + ActiveWindow = null; + FocusWindow = null; + ModalWindows = new Stack(3); + + MouseState = MouseButtons.None; + MousePosition = new Point(0, 0); + + Caret.Timer = new Timer(); + Caret.Timer.Interval = 500; // FIXME - where should this number come from? + Caret.Timer.Tick += new EventHandler(CaretCallback); + + // XXX multiscreen work here + root_hwnd = new X11RootHwnd (this, Xlib.XRootWindow (display, DefaultScreen)); + + // XXX do we need a per-screen foster parent? + // Create the foster parent + foster_hwnd = new X11Hwnd (this, + Xlib.XCreateSimpleWindow (display, root_hwnd.WholeWindow, + 0, 0, 1, 1, 4, UIntPtr.Zero, UIntPtr.Zero)); + +#if __MonoCS__ + pollfds = new Pollfd [1]; + pollfds [0] = new Pollfd (); + pollfds [0].fd = Xlib.XConnectionNumber (display); + pollfds [0].events = PollEvents.POLLIN; +#endif + + Keyboard = new X11Keyboard(display, foster_hwnd.Handle); + Dnd = new X11Dnd (display, Keyboard); + + ErrorExceptions = false; + + // Handle any upcoming errors + ErrorHandler = new XErrorHandler (HandleError); + Xlib.XSetErrorHandler (ErrorHandler); + + X11DesktopColors.Initialize(); // XXX we need to figure out how to make this display specific? + + // Disable keyboard autorepeat + try { + Xlib.XkbSetDetectableAutoRepeat (display, true, IntPtr.Zero); + detectable_key_auto_repeat = true; + } catch { + Console.Error.WriteLine ("Could not disable keyboard auto repeat, will attempt to disable manually."); + detectable_key_auto_repeat = false; + } + + // we re-set our error handler here, X11DesktopColor stuff might have stolen it (gtk does) + Xlib.XSetErrorHandler (ErrorHandler); + + // create our event thread (just sits on the X socket waiting for events) + event_thread = new Thread (new ThreadStart (XEventThread)); + event_thread.IsBackground = true; + event_thread.Start (); + } + + #region Callbacks + private void MouseHover(object sender, EventArgs e) + { + HoverState.Timer.Enabled = false; + + if (HoverState.Window != IntPtr.Zero) { + X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (HoverState.Window); + if (hwnd != null) { + XEvent xevent = new XEvent (); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = display; + xevent.ClientMessageEvent.window = HoverState.Window; + xevent.ClientMessageEvent.message_type = HoverState.Atom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = (IntPtr) (HoverState.Y << 16 | HoverState.X); + + hwnd.Queue.Enqueue (xevent); + } + } + } + + private void CaretCallback (object sender, EventArgs e) + { + if (Caret.Paused) { + return; + } + Caret.On = !Caret.On; + + Xlib.XDrawLine (display, Caret.Hwnd, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + + internal string WhereString () + { + StackTrace stack; + StackFrame frame; + string newline; + string unknown; + StringBuilder sb; + MethodBase method; + + newline = String.Format("{0}\t {1} ", Environment.NewLine, "at"); + unknown = "<unknown method>"; + sb = new StringBuilder(); + stack = new StackTrace(true); + + for (int i = 0; i < stack.FrameCount; i++) { + frame = stack.GetFrame (i); + sb.Append(newline); + + method = frame.GetMethod(); + if (method != null) { + if (frame.GetFileLineNumber() != 0) + sb.AppendFormat ("{0}.{1} () [{2}:{3}]", + method.DeclaringType.FullName, method.Name, + Path.GetFileName(frame.GetFileName()), frame.GetFileLineNumber()); + else + sb.AppendFormat ("{0}.{1} ()", method.DeclaringType.FullName, method.Name); + } else { + sb.Append(unknown); + } + } + return sb.ToString(); + } + + private int HandleError (IntPtr display, ref XErrorEvent error_event) + { + if (ErrorExceptions) + throw new X11Exception (error_event.display, error_event.resourceid, + error_event.serial, error_event.error_code, + error_event.request_code, error_event.minor_code); + else + Console.WriteLine ("X11 Error encountered: {0}{1}\n", + X11Exception.GetMessage(error_event.display, error_event.resourceid, + error_event.serial, error_event.error_code, + error_event.request_code, error_event.minor_code), + WhereString()); + return 0; + } + #endregion // Callbacks + + private void ShowCaret() + { + if ((Caret.gc == IntPtr.Zero) || Caret.On) { + return; + } + Caret.On = true; + + Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + + private void HideCaret() + { + if ((Caret.gc == IntPtr.Zero) || !Caret.On) { + return; + } + Caret.On = false; + + Xlib.XDrawLine (display, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + + public void CaretVisible (IntPtr handle, bool visible) + { + if (Caret.Hwnd == handle) { + if (visible) { + if (!Caret.Visible) { + Caret.Visible = true; + ShowCaret(); + Caret.Timer.Start(); + } + } else { + Caret.Visible = false; + Caret.Timer.Stop(); + HideCaret(); + } + } + } + + public void AudibleAlert () + { + Xlib.XBell (display, 0); + } + + public void Flush () + { + Xlib.XFlush (display); + } + + public void Close () + { + // XXX shut down the event_thread + Xlib.XCloseDisplay (display); + } + + public IntPtr XGetParent(IntPtr handle) + { + IntPtr Root; + IntPtr Parent; + IntPtr Children; + int ChildCount; + + Xlib.XQueryTree (display, handle, out Root, out Parent, out Children, out ChildCount); + + if (Children!=IntPtr.Zero) { + Xlib.XFree(Children); + } + + return Parent; + } + + public bool SystrayAdd(IntPtr handle, string tip, Icon icon, out ToolTip tt) + { + IntPtr SystrayMgrWindow; + + Xlib.XGrabServer (display); + SystrayMgrWindow = Xlib.XGetSelectionOwner (display, Atoms._NET_SYSTEM_TRAY_S); + Xlib.XUngrabServer (display); + + if (SystrayMgrWindow != IntPtr.Zero) { + XSizeHints size_hints; + X11Hwnd hwnd; + + hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); +#if DriverDebug + Console.WriteLine("Adding Systray Whole:{0:X}, Client:{1:X}", + hwnd.WholeWindow.ToInt32(), hwnd.ClientWindow.ToInt32()); +#endif + + // Oh boy. + if (hwnd.ClientWindow != hwnd.WholeWindow) { + Xlib.XDestroyWindow (display, hwnd.ClientWindow); + hwnd.ClientWindow = hwnd.WholeWindow; + + try { + hwnd.Queue.Lock (); + + /* by virtue of the way the tests are ordered when determining if it's PAINT + or NCPAINT, ClientWindow == WholeWindow will always be PAINT. So, if we're + waiting on an nc_expose, drop it and remove the hwnd from the list (unless + there's a pending expose). */ + hwnd.PendingNCExpose = false; + } + finally { + hwnd.Queue.Unlock (); + } + } + + size_hints = new XSizeHints(); + + size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize); + + size_hints.min_width = 24; + size_hints.min_height = 24; + size_hints.max_width = 24; + size_hints.max_height = 24; + size_hints.base_width = 24; + size_hints.base_height = 24; + + Xlib.XSetWMNormalHints (display, hwnd.WholeWindow, ref size_hints); + + int[] atoms = new int[2]; + atoms [0] = 1; // Version 1 + atoms [1] = 1; // we want to be mapped + + // This line cost me 3 days... + Xlib.XChangeProperty (display, + hwnd.WholeWindow, Atoms._XEMBED_INFO, Atoms._XEMBED_INFO, 32, + PropertyMode.Replace, atoms, 2); + + // Need to pick some reasonable defaults + tt = new ToolTip(); + tt.AutomaticDelay = 100; + tt.InitialDelay = 250; + tt.ReshowDelay = 250; + tt.ShowAlways = true; + + if ((tip != null) && (tip != string.Empty)) { + tt.SetToolTip(Widget.FromHandle(handle), tip); + tt.Active = true; + } else { + tt.Active = false; + } + + SendNetClientMessage (SystrayMgrWindow, + Atoms._NET_SYSTEM_TRAY_OPCODE, + IntPtr.Zero, + (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK, + hwnd.WholeWindow); + + return true; + } + + tt = null; + return false; + } + + public bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt) + { + Widget Widget; + + Widget = Widget.FromHandle(handle); + if (Widget != null && tt != null) { + tt.SetToolTip(Widget, tip); + tt.Active = true; + return true; + } else { + return false; + } + } + + public void SystrayRemove(IntPtr handle, ref ToolTip tt) + { +#if GTKSOCKET_SUPPORTS_REPARENTING + X11Hwnd hwnd; + + hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + /* in the XEMBED spec, it mentions 3 ways for a client window to break the protocol with the embedder. + * 1. The embedder can unmap the window and reparent to the root window (we should probably handle this...) + * 2. The client can reparent its window out of the embedder window. + * 3. The client can destroy its window. + * + * this call to SetParent is case 2, but in + * the spec it also mentions that gtk doesn't + * support this at present. Looking at HEAD + * gtksocket-x11.c jives with this statement. + * + * so we can't reparent. we have to destroy. + */ + SetParent(hwnd.WholeWindow, FosterParent); +#else + Widget Widget = Widget.FromHandle(handle); + if (Widget is NotifyIcon.NotifyIconWindow) + ((NotifyIcon.NotifyIconWindow)Widget).InternalRecreateHandle (); +#endif + + // The caller can now re-dock it later... + if (tt != null) { + tt.Dispose(); + tt = null; + } + } + + public void ResetMouseHover (X11Hwnd hovering) + { + HoverState.Timer.Enabled = hovering != null; + HoverState.X = MousePosition.X; + HoverState.Y = MousePosition.Y; + HoverState.Window = hovering == null ? IntPtr.Zero : hovering.Handle; + } + + public void ShowCursor (bool show) + { + ; // FIXME - X11 doesn't 'hide' the cursor. we could create an empty cursor + } + + public void SetModal (X11Hwnd hwnd, bool Modal) + { + if (Modal) { + ModalWindows.Push(hwnd); + } else { + // XXX do we need to pop until the + // hwnd is off the stack? or just the + // most recently pushed hwnd? + if (ModalWindows.Contains(hwnd)) { + ModalWindows.Pop(); + } + + if (ModalWindows.Count > 0) { + X11Hwnd top_hwnd = (X11Hwnd)ModalWindows.Peek(); + top_hwnd.Activate(); + } + } + } + + public TransparencySupport SupportsTransparency () + { + // compiz adds _NET_WM_WINDOW_OPACITY to _NET_SUPPORTED on the root window, check for that + return ((IList)root_hwnd._NET_SUPPORTED).Contains (Atoms._NET_WM_WINDOW_OPACITY) ? TransparencySupport.GetSet : TransparencySupport.None; + } + + public void SendAsyncMethod (AsyncMethodData method) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(method.Handle); + XEvent xevent = new XEvent (); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = display; + xevent.ClientMessageEvent.window = method.Handle; + xevent.ClientMessageEvent.message_type = Atoms.AsyncAtom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = (IntPtr) GCHandle.Alloc (method); + + hwnd.Queue.Enqueue (xevent); + } + + delegate IntPtr WndProcDelegate (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam); + + public IntPtr SendMessage (IntPtr handle, Msg message, IntPtr wParam, IntPtr lParam) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + if (hwnd == null) + return IntPtr.Zero; + + if (hwnd.Queue.Thread != Thread.CurrentThread) { + AsyncMethodResult result; + AsyncMethodData data; + + result = new AsyncMethodResult (); + data = new AsyncMethodData (); + + data.Handle = hwnd.Handle; + data.Method = new WndProcDelegate (NativeWindow.WndProc); + data.Args = new object[] { hwnd.Handle, message, wParam, lParam }; + data.Result = result; + + SendAsyncMethod (data); +#if DriverDebug || DriverDebugThreads + Console.WriteLine ("Sending {0} message across.", message); +#endif + + return IntPtr.Zero; + } + else { + return NativeWindow.WndProc (hwnd.Handle, message, wParam, lParam); + } + } + + public int SendInput (IntPtr handle, Queue keys) { + if (handle == IntPtr.Zero) + return 0; + + int count = keys.Count; + Hwnd hwnd = Hwnd.ObjectFromHandle(handle); + + while (keys.Count > 0) { + + MSG msg = (MSG)keys.Dequeue(); + + XEvent xevent = new XEvent (); + + xevent.type = (msg.message == Msg.WM_KEYUP ? XEventName.KeyRelease : XEventName.KeyPress); + xevent.KeyEvent.display = display; + + if (hwnd != null) { + xevent.KeyEvent.window = hwnd.whole_window; + } else { + xevent.KeyEvent.window = IntPtr.Zero; + } + + xevent.KeyEvent.keycode = Keyboard.ToKeycode((int)msg.wParam); + + hwnd.Queue.EnqueueLocked (xevent); + } + return count; + } + + // FIXME - I think this should just enqueue directly + public bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam) + { + XEvent xevent = new XEvent (); + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = display; + + if (hwnd != null) { + xevent.ClientMessageEvent.window = hwnd.WholeWindow; + } else { + xevent.ClientMessageEvent.window = IntPtr.Zero; + } + + xevent.ClientMessageEvent.message_type = Atoms.PostAtom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = handle; + xevent.ClientMessageEvent.ptr2 = (IntPtr) message; + xevent.ClientMessageEvent.ptr3 = wparam; + xevent.ClientMessageEvent.ptr4 = lparam; + + hwnd.Queue.Enqueue (xevent); + + return true; + } + + public void SendNetWMMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2) + { + XEvent xev; + + xev = new XEvent(); + xev.ClientMessageEvent.type = XEventName.ClientMessage; + xev.ClientMessageEvent.send_event = true; + xev.ClientMessageEvent.window = window; + xev.ClientMessageEvent.message_type = message_type; + xev.ClientMessageEvent.format = 32; + xev.ClientMessageEvent.ptr1 = l0; + xev.ClientMessageEvent.ptr2 = l1; + xev.ClientMessageEvent.ptr3 = l2; + + Xlib.XSendEvent (display, root_hwnd.Handle, false, + new IntPtr ((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev); + } + + public void SendNetClientMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2) + { + XEvent xev; + + xev = new XEvent(); + xev.ClientMessageEvent.type = XEventName.ClientMessage; + xev.ClientMessageEvent.send_event = true; + xev.ClientMessageEvent.window = window; + xev.ClientMessageEvent.message_type = message_type; + xev.ClientMessageEvent.format = 32; + xev.ClientMessageEvent.ptr1 = l0; + xev.ClientMessageEvent.ptr2 = l1; + xev.ClientMessageEvent.ptr3 = l2; + + Xlib.XSendEvent (display, window, false, new IntPtr ((int)EventMask.NoEventMask), ref xev); + } + + public bool TranslateMessage (ref MSG msg) + { + return Keyboard.TranslateMessage (ref msg); + } + + public IntPtr DispatchMessage (ref MSG msg) + { + return NativeWindow.WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + + private void QueryPointer (IntPtr w, out IntPtr root, out IntPtr child, + out int root_x, out int root_y, out int child_x, out int child_y, + out int mask) + { + /* this code was written with the help of + glance at gdk. I never would have realized we + needed a loop in order to traverse down in the + hierarchy. I would have assumed you'd get the + most deeply nested child and have to do + XQueryTree to move back up the hierarchy.. + stupid me, of course. */ + IntPtr c; + + // Xlib.XGrabServer (display); + + Xlib.XQueryPointer (display, w, out root, out c, + out root_x, out root_y, out child_x, out child_y, + out mask); + + if (root != w) + c = root; + + IntPtr child_last = IntPtr.Zero; + while (c != IntPtr.Zero) { + child_last = c; + Xlib.XQueryPointer (display, c, out root, out c, + out root_x, out root_y, out child_x, out child_y, + out mask); + } + + // Xlib.XUngrabServer (display); + + child = child_last; + } + + public void SetCursorPos (int x, int y) + { + IntPtr root, child; + int root_x, root_y, child_x, child_y, mask; + + /* we need to do a + * QueryPointer before warping + * because if the warp is on + * the RootWindow, the x/y are + * relative to the current + * mouse position + */ + QueryPointer (RootWindow.Handle, + out root, + out child, + out root_x, out root_y, + out child_x, out child_y, + out mask); + + Xlib.XWarpPointer (display, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x - root_x, y - root_y); + + Xlib.XFlush (display); + + /* then we need to a + * QueryPointer after warping + * to manually generate a + * motion event for the window + * we move into. + */ + QueryPointer (RootWindow.Handle, + out root, + out child, + out root_x, out root_y, + out child_x, out child_y, + out mask); + + X11Hwnd child_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(child); + if (child_hwnd == null) + return; + + XEvent xevent = new XEvent (); + + xevent.type = XEventName.MotionNotify; + xevent.MotionEvent.display = display; + xevent.MotionEvent.window = child_hwnd.Handle; + xevent.MotionEvent.root = RootWindow.Handle; + xevent.MotionEvent.x = child_x; + xevent.MotionEvent.y = child_y; + xevent.MotionEvent.x_root = root_x; + xevent.MotionEvent.y_root = root_y; + xevent.MotionEvent.state = mask; + + child_hwnd.Queue.Enqueue (xevent); + } + + public void SetFocus (X11Hwnd new_focus) + { + if (new_focus == FocusWindow) + return; + + X11Hwnd prev_focus = FocusWindow; + FocusWindow = new_focus; + + if (prev_focus != null) + SendMessage (prev_focus.Handle, Msg.WM_KILLFOCUS, + FocusWindow == null ? IntPtr.Zero : FocusWindow.Handle, IntPtr.Zero); + if (FocusWindow != null) + SendMessage (FocusWindow.Handle, Msg.WM_SETFOCUS, + prev_focus == null ? IntPtr.Zero : prev_focus.Handle, IntPtr.Zero); + + //XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).ClientWindow, RevertTo.None, IntPtr.Zero); + } + + public IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) + { + IntPtr cursor; + Bitmap cursor_bitmap; + Bitmap cursor_mask; + Byte[] cursor_bits; + Byte[] mask_bits; + Color c_pixel; + Color m_pixel; + int width; + int height; + IntPtr cursor_pixmap; + IntPtr mask_pixmap; + XColor fg; + XColor bg; + bool and; + bool xor; + + if (Xlib.XQueryBestCursor (display, RootWindow.Handle, bitmap.Width, bitmap.Height, out width, out height) == 0) { + return IntPtr.Zero; + } + + // Win32 only allows creation cursors of a certain size + if ((bitmap.Width != width) || (bitmap.Width != height)) { + cursor_bitmap = new Bitmap(bitmap, new Size(width, height)); + cursor_mask = new Bitmap(mask, new Size(width, height)); + } else { + cursor_bitmap = bitmap; + cursor_mask = mask; + } + + width = cursor_bitmap.Width; + height = cursor_bitmap.Height; + + cursor_bits = new Byte[(width / 8) * height]; + mask_bits = new Byte[(width / 8) * height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + c_pixel = cursor_bitmap.GetPixel(x, y); + m_pixel = cursor_mask.GetPixel(x, y); + + and = c_pixel == cursor_pixel; + xor = m_pixel == mask_pixel; + + if (!and && !xor) { + // Black + // cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); // The bit already is 0 + mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); + } else if (and && !xor) { + // White + cursor_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); + mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); +#if notneeded + } else if (and && !xor) { + // Screen + } else if (and && xor) { + // Inverse Screen + + // X11 doesn't know the 'reverse screen' concept, so we'll treat them the same + // we want both to be 0 so nothing to be done + //cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); + //mask_bits[y * width / 8 + x / 8] |= (byte)(01 << (x % 8)); +#endif + } + } + } + + cursor_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle, + cursor_bits, width, height, (IntPtr)1, (IntPtr)0, 1); + mask_pixmap = Xlib.XCreatePixmapFromBitmapData (display, RootWindow.Handle, + mask_bits, width, height, (IntPtr)1, (IntPtr)0, 1); + fg = new XColor(); + bg = new XColor(); + + fg.pixel = Xlib.XWhitePixel (display, DefaultScreen); + fg.red = (ushort)65535; + fg.green = (ushort)65535; + fg.blue = (ushort)65535; + + bg.pixel = Xlib.XBlackPixel (display, DefaultScreen); + + cursor = Xlib.XCreatePixmapCursor (display, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot); + + Xlib.XFreePixmap (display, cursor_pixmap); + Xlib.XFreePixmap (display, mask_pixmap); + + return cursor; + } + + public Bitmap DefineStdCursorBitmap (StdCursor id) + { + CursorFontShape shape; + string name; + IntPtr theme; + int size; + Bitmap bmp = null; + + try { + shape = XplatUIX11.StdCursorToFontShape (id); + name = shape.ToString ().Replace ("XC_", string.Empty); + size = XplatUIX11.XcursorGetDefaultSize (Handle); + theme = XplatUIX11.XcursorGetTheme (Handle); + IntPtr images_ptr = XplatUIX11.XcursorLibraryLoadImages (name, theme, size); +#if debug + Console.WriteLine ("DefineStdCursorBitmap, id={0}, #id={1}, name{2}, size={3}, theme: {4}, images_ptr={5}", id, (int) id, name, size, Marshal.PtrToStringAnsi (theme), images_ptr); +#endif + + if (images_ptr == IntPtr.Zero) { + return null; + } + + XcursorImages images = (XcursorImages)Marshal.PtrToStructure (images_ptr, typeof (XcursorImages)); +#if debug + Console.WriteLine ("DefineStdCursorBitmap, cursor has {0} images", images.nimage); +#endif + + if (images.nimage > 0) { + // We only care about the first image. + XcursorImage image = (XcursorImage)Marshal.PtrToStructure (Marshal.ReadIntPtr (images.images), typeof (XcursorImage)); + +#if debug + Console.WriteLine ("DefineStdCursorBitmap, loaded image <size={0}, height={1}, width={2}, xhot={3}, yhot={4}, pixels={5}", image.size, image.height, image.width, image.xhot, image.yhot, image.pixels); +#endif + // A sanity check + if (image.width <= short.MaxValue && image.height <= short.MaxValue) { + int [] pixels = new int [image.width * image.height]; + Marshal.Copy (image.pixels, pixels, 0, pixels.Length); + bmp = new Bitmap (image.width, image.height); + for (int w = 0; w < image.width; w++) { + for (int h = 0; h < image.height; h++) { + bmp.SetPixel (w, h, Color.FromArgb (pixels [h * image.width + w])); + } + } + } + } + + XplatUIX11.XcursorImagesDestroy (images_ptr); + + } catch (DllNotFoundException ex) { + Console.WriteLine ("Could not load libXcursor: " + ex.Message + " (" + ex.GetType ().Name + ")"); + return null; + } + + return bmp; + } + + public IntPtr DefineStdCursor (StdCursor id) + { + CursorFontShape shape; + + // FIXME - define missing shapes + + switch (id) { + case StdCursor.AppStarting: + shape = CursorFontShape.XC_watch; + break; + + case StdCursor.Arrow: + shape = CursorFontShape.XC_top_left_arrow; + break; + + case StdCursor.Cross: + shape = CursorFontShape.XC_crosshair; + break; + + case StdCursor.Default: + shape = CursorFontShape.XC_top_left_arrow; + break; + + case StdCursor.Hand: + shape = CursorFontShape.XC_hand1; + break; + + case StdCursor.Help: + shape = CursorFontShape.XC_question_arrow; + break; + + case StdCursor.HSplit: + shape = CursorFontShape.XC_sb_v_double_arrow; + break; + + case StdCursor.IBeam: + shape = CursorFontShape.XC_xterm; + break; + + case StdCursor.No: + shape = CursorFontShape.XC_circle; + break; + + case StdCursor.NoMove2D: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.NoMoveHoriz: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.NoMoveVert: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanEast: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanNE: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanNorth: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanNW: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanSE: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanSouth: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanSW: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.PanWest: + shape = CursorFontShape.XC_sizing; + break; + + case StdCursor.SizeAll: + shape = CursorFontShape.XC_fleur; + break; + + case StdCursor.SizeNESW: + shape = CursorFontShape.XC_top_right_corner; + break; + + case StdCursor.SizeNS: + shape = CursorFontShape.XC_sb_v_double_arrow; + break; + + case StdCursor.SizeNWSE: + shape = CursorFontShape.XC_top_left_corner; + break; + + case StdCursor.SizeWE: + shape = CursorFontShape.XC_sb_h_double_arrow; + break; + + case StdCursor.UpArrow: + shape = CursorFontShape.XC_center_ptr; + break; + + case StdCursor.VSplit: + shape = CursorFontShape.XC_sb_h_double_arrow; + break; + + case StdCursor.WaitCursor: + shape = CursorFontShape.XC_watch; + break; + + default: + return IntPtr.Zero; + } + + return Xlib.XCreateFontCursor (display, shape); + } + + // XXX this should take an X11Hwnd. + public void CreateCaret (IntPtr handle, int width, int height) + { + XGCValues gc_values; + X11Hwnd hwnd; + + hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (Caret.Hwnd != IntPtr.Zero) + DestroyCaret(Caret.Hwnd); + + Caret.Hwnd = handle; + Caret.Window = hwnd.ClientWindow; + Caret.Width = width; + Caret.Height = height; + Caret.Visible = false; + Caret.On = false; + + gc_values = new XGCValues(); + gc_values.line_width = width; + + Caret.gc = Xlib.XCreateGC (display, Caret.Window, new IntPtr ((int)GCFunction.GCLineWidth), ref gc_values); + if (Caret.gc == IntPtr.Zero) { + Caret.Hwnd = IntPtr.Zero; + return; + } + + Xlib.XSetFunction (display, Caret.gc, GXFunction.GXinvert); + } + + + // XXX this should take an X11Hwnd. + public void DestroyCaret (IntPtr handle) + { + if (Caret.Hwnd == handle) { + if (Caret.Visible == true) { + Caret.Timer.Stop (); + } + if (Caret.gc != IntPtr.Zero) { + Xlib.XFreeGC (display, Caret.gc); + Caret.gc = IntPtr.Zero; + } + Caret.Hwnd = IntPtr.Zero; + Caret.Visible = false; + Caret.On = false; + } + } + + public void SetCaretPos (IntPtr handle, int x, int y) + { + if (Caret.Hwnd == handle) { + Caret.Timer.Stop(); + HideCaret(); + + Caret.X = x; + Caret.Y = y; + + if (Caret.Visible == true) { + ShowCaret(); + Caret.Timer.Start(); + } + } + } + + public void DestroyCursor (IntPtr cursor) + { + Xlib.XFreeCursor (display, cursor); + } + + private void AccumulateDestroyedHandles (Widget c, ArrayList list) + { + if (c != null) { + Widget[] Widgets = c.Widgets.GetAllWidgets (); + + if (c.IsHandleCreated && !c.IsDisposed) { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(c.Handle); + +#if DriverDebug || DriverDebugDestroy + Console.WriteLine (" + adding {0} to the list of zombie windows", XplatUI.Window (hwnd.Handle)); + Console.WriteLine (" + parent X window is {0:X}", XGetParent (hwnd.WholeWindow).ToInt32()); +#endif + + list.Add (hwnd); + CleanupCachedWindows (hwnd); + hwnd.zombie = true; + } + + for (int i = 0; i < Widgets.Length; i ++) { + AccumulateDestroyedHandles (Widgets[i], list); + } + } + + } + + void CleanupCachedWindows (X11Hwnd hwnd) + { + if (ActiveWindow == hwnd) { + SendMessage (hwnd.ClientWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); + ActiveWindow = null; + } + + if (FocusWindow == hwnd) { + SendMessage (hwnd.ClientWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero); + FocusWindow = null; + } + + if (Grab.Hwnd == hwnd.Handle) { + Grab.Hwnd = IntPtr.Zero; + Grab.Confined = false; + } + + DestroyCaret (hwnd.Handle); + } + + + public void DestroyWindow (X11Hwnd hwnd) + { + CleanupCachedWindows (hwnd); + + hwnd.SendParentNotify (Msg.WM_DESTROY, int.MaxValue, int.MaxValue); + + ArrayList windows = new ArrayList (); + + AccumulateDestroyedHandles (Widget.WidgetNativeWindow.WidgetFromHandle(hwnd.Handle), windows); + + hwnd.DestroyWindow (); + + foreach (X11Hwnd h in windows) { + SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero); + } + } + + public X11Hwnd GetActiveWindow () + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr active = IntPtr.Zero; + + Xlib.XGetWindowProperty (display, RootWindow.Handle, + Atoms._NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false, + Atoms.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (((long)nitems > 0) && (prop != IntPtr.Zero)) { + active = (IntPtr)Marshal.ReadInt32(prop); + Xlib.XFree(prop); + } + + return (X11Hwnd)Hwnd.GetObjectFromWindow(active); + } + + public void SetActiveWindow (X11Hwnd new_active_window) + { + if (new_active_window != ActiveWindow) { + if (ActiveWindow != null) + PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, + (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); + + ActiveWindow = new_active_window; + + if (ActiveWindow != null) + PostMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, + (IntPtr)WindowActiveFlags.WA_ACTIVE, IntPtr.Zero); + } + + if (ModalWindows.Count > 0) { + // Modality handling, if we are modal and the new active window is one + // of ours but not the modal one, switch back to the modal window + + if (ActiveWindow != null && + NativeWindow.FromHandle (ActiveWindow.Handle) != null) { + if (ActiveWindow != (X11Hwnd)ModalWindows.Peek()) + ((X11Hwnd)ModalWindows.Peek()).Activate (); + } + } + } + + public void GetDisplaySize (out Size size) + { + XWindowAttributes attributes = new XWindowAttributes(); + + // FIXME - use _NET_WM messages instead? + Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes); + + size = new Size(attributes.width, attributes.height); + } + + // XXX this method doesn't really fit well anywhere in the backend + public SizeF GetAutoScaleSize (Font font) + { + Graphics g; + float width; + string magic_string = "The quick brown fox jumped over the lazy dog."; + double magic_number = 44.549996948242189; // XXX my god, where did this number come from? + + g = Graphics.FromHwnd (FosterParent.Handle); + + width = (float) (g.MeasureString (magic_string, font).Width / magic_number); + return new SizeF(width, font.Height); + } + + public void GetCursorPos (X11Hwnd hwnd, out int x, out int y) + { + IntPtr use_handle; + IntPtr root; + IntPtr child; + int root_x; + int root_y; + int win_x; + int win_y; + int keys_buttons; + + if (hwnd != null) + use_handle = hwnd.Handle; + else + use_handle = RootWindow.Handle; + + QueryPointer (use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons); + + if (hwnd != null) { + x = win_x; + y = win_y; + } else { + x = root_x; + y = root_y; + } + } + + public IntPtr GetFocus () + { + return FocusWindow.Handle; + } + + public IntPtr GetMousewParam (int Delta) + { + int result = 0; + + if ((MouseState & MouseButtons.Left) != 0) { + result |= (int)MsgButtons.MK_LBUTTON; + } + + if ((MouseState & MouseButtons.Middle) != 0) { + result |= (int)MsgButtons.MK_MBUTTON; + } + + if ((MouseState & MouseButtons.Right) != 0) { + result |= (int)MsgButtons.MK_RBUTTON; + } + + Keys mods = ModifierKeys; + if ((mods & Keys.Widget) != 0) { + result |= (int)MsgButtons.MK_CONTROL; + } + + if ((mods & Keys.Shift) != 0) { + result |= (int)MsgButtons.MK_SHIFT; + } + + result |= Delta << 16; + + return (IntPtr)result; + } + + public void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea) + { + handle = Grab.Hwnd; + GrabConfined = Grab.Confined; + GrabArea = Grab.Area; + } + + public void GrabWindow (X11Hwnd hwnd, X11Hwnd confine_to) + { + IntPtr confine_to_window; + + confine_to_window = IntPtr.Zero; + + if (confine_to != null) { + Console.WriteLine (Environment.StackTrace); + + XWindowAttributes attributes = new XWindowAttributes(); + + Xlib.XGetWindowAttributes (display, confine_to.ClientWindow, ref attributes); + + Grab.Area.X = attributes.x; + Grab.Area.Y = attributes.y; + Grab.Area.Width = attributes.width; + Grab.Area.Height = attributes.height; + Grab.Confined = true; + confine_to_window = confine_to.ClientWindow; + } + + Grab.Hwnd = hwnd.ClientWindow; + + Xlib.XGrabPointer (display, hwnd.ClientWindow, false, + EventMask.ButtonPressMask | EventMask.ButtonMotionMask | + EventMask.ButtonReleaseMask | EventMask.PointerMotionMask, + GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_to_window, IntPtr.Zero, IntPtr.Zero); + } + + public void UngrabWindow (X11Hwnd hwnd) + { + Xlib.XUngrabPointer (display, IntPtr.Zero); + Xlib.XFlush (display); + + // XXX make sure hwnd is what should have the grab and throw if not + Grab.Hwnd = IntPtr.Zero; + Grab.Confined = false; + } + +#if notyet + private void TranslatePropertyToClipboard (IntPtr property) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Clipboard.Item = null; + + Xlib.XGetWindowProperty (display, FosterParent.Handle, + property, IntPtr.Zero, new IntPtr (0x7fffffff), true, + Atoms.AnyPropertyType, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if ((long)nitems > 0) { + if (property == Atoms.XA_STRING) { + Clipboard.Item = Marshal.PtrToStringAnsi(prop); + } else if (property == Atoms.XA_BITMAP) { + // FIXME - convert bitmap to image + } else if (property == Atoms.XA_PIXMAP) { + // FIXME - convert pixmap to image + } else if (property == Atoms.OEMTEXT) { + Clipboard.Item = Marshal.PtrToStringAnsi(prop); + } else if (property == Atoms.UNICODETEXT) { + Clipboard.Item = Marshal.PtrToStringAnsi(prop); + } + + Xlib.XFree(prop); + } + } +#endif + + // XXX should we be using @handle instead of Atoms.CLIPBOARD here? + public int[] ClipboardAvailableFormats (IntPtr handle) + { + // XXX deal with the updatemessagequeue stuff +#if true + return new int[0]; +#else + DataFormats.Format f; + int[] result; + + f = DataFormats.Format.List; + + if (Xlib.XGetSelectionOwner (display, Atoms.CLIPBOARD) == IntPtr.Zero) { + return null; + } + + Clipboard.Formats = new ArrayList(); + + while (f != null) { + Xlib.XConvertSelection (display, Atoms.CLIPBOARD, (IntPtr)f.Id, (IntPtr)f.Id, FosterParent.Handle, IntPtr.Zero); + + Clipboard.Enumerating = true; + while (Clipboard.Enumerating) { + UpdateMessageQueue(null); + } + f = f.Next; + } + + result = new int[Clipboard.Formats.Count]; + + for (int i = 0; i < Clipboard.Formats.Count; i++) { + result[i] = ((IntPtr)Clipboard.Formats[i]).ToInt32 (); + } + + Clipboard.Formats = null; + return result; +#endif + } + + public void ClipboardClose (IntPtr handle) + { + if (handle != ClipMagic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + return; + } + + public int ClipboardGetID (IntPtr handle, string format) + { + if (handle != ClipMagic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + + if (format == "Text" ) return Atoms.XA_STRING.ToInt32(); + else if (format == "Bitmap" ) return Atoms.XA_BITMAP.ToInt32(); + //else if (format == "MetaFilePict" ) return 3; + //else if (format == "SymbolicLink" ) return 4; + //else if (format == "DataInterchangeFormat" ) return 5; + //else if (format == "Tiff" ) return 6; + else if (format == "OEMText" ) return Atoms.OEMTEXT.ToInt32(); + else if (format == "DeviceIndependentBitmap" ) return Atoms.XA_PIXMAP.ToInt32(); + else if (format == "Palette" ) return Atoms.XA_COLORMAP.ToInt32(); // Useless + //else if (format == "PenData" ) return 10; + //else if (format == "RiffAudio" ) return 11; + //else if (format == "WaveAudio" ) return 12; + else if (format == "UnicodeText" ) return Atoms.UNICODETEXT.ToInt32(); + //else if (format == "EnhancedMetafile" ) return 14; + //else if (format == "FileDrop" ) return 15; + //else if (format == "Locale" ) return 16; + + return Xlib.XInternAtom (display, format, false).ToInt32(); + } + + public IntPtr ClipboardOpen (bool primary_selection) + { + if (!primary_selection) + ClipMagic = Atoms.CLIPBOARD; + else + ClipMagic = Atoms.PRIMARY; + + return ClipMagic; + } + + // XXX @converter? + public object ClipboardRetrieve (IntPtr handle, int type, XplatUI.ClipboardToObject converter) + { + // XXX deal with the UpdateMessageQueue stuff +#if true + return null; +#else + Xlib.XConvertSelection (display, handle, (IntPtr)type, (IntPtr)type, FosterParent, IntPtr.Zero); + + Clipboard.Retrieving = true; + while (Clipboard.Retrieving) { + UpdateMessageQueue(null); + } + + return Clipboard.Item; +#endif + } + + public PaintEventArgs PaintEventStart (ref Message m, IntPtr handle, bool client) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (Caret.Visible == true) { + Caret.Paused = true; + HideCaret(); + } + + return hwnd.PaintEventStart (ref m, client); + } + + public void PaintEventEnd (ref Message m, IntPtr handle, bool client) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + hwnd.PaintEventEnd (ref m, client); + + if (Caret.Visible == true) { + ShowCaret(); + Caret.Paused = false; + } + } + + public void SetCursor (IntPtr handle, IntPtr cursor) + { + Hwnd hwnd; + + if (OverrideCursorHandle == IntPtr.Zero) { + if ((LastCursorWindow == handle) && (LastCursorHandle == cursor)) + return; + + LastCursorHandle = cursor; + LastCursorWindow = handle; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (cursor != IntPtr.Zero) + Xlib.XDefineCursor (display, hwnd.whole_window, cursor); + else + Xlib.XUndefineCursor (display, hwnd.whole_window); + Xlib.XFlush (display); + } + else { + hwnd = Hwnd.ObjectFromHandle(handle); + Xlib.XDefineCursor (display, hwnd.whole_window, OverrideCursorHandle); + } + } + + public DragDropEffects StartDrag (IntPtr handle, object data, + DragDropEffects allowed_effects) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) + throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ")."); + + return Dnd.StartDrag (hwnd.ClientWindow, data, allowed_effects); + } + + public X11Atoms Atoms { + get { return atoms; } + } + + public int CurrentTimestamp { + get { + TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); + + return (int) t.TotalSeconds; + } + } + + public Size CursorSize { + get { + int x; + int y; + + if (Xlib.XQueryBestCursor (display, RootWindow.Handle, 32, 32, out x, out y) != 0) { + return new Size (x, y); + } else { + return new Size (16, 16); + } + } + } + + public IntPtr Handle { + get { return display; } + } + + public Size IconSize { + get { + IntPtr list; + XIconSize size; + int count; + + if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) { + long current; + int largest; + + current = (long)list; + largest = 0; + + size = new XIconSize(); + + for (int i = 0; i < count; i++) { + size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType()); + current += Marshal.SizeOf(size); + + // Look for our preferred size + if (size.min_width == 32) { + Xlib.XFree(list); + return new Size(32, 32); + } + + if (size.max_width == 32) { + Xlib.XFree(list); + return new Size(32, 32); + } + + if (size.min_width < 32 && size.max_width > 32) { + int x; + + // check if we can fit one + x = size.min_width; + while (x < size.max_width) { + x += size.width_inc; + if (x == 32) { + Xlib.XFree(list); + return new Size(32, 32); + } + } + } + + if (largest < size.max_width) { + largest = size.max_width; + } + } + + // We didn't find a match or we wouldn't be here + return new Size(largest, largest); + + } else { + return new Size(32, 32); + } + } + } + + public int KeyboardSpeed { + get { + // + // A lot harder: need to do: + // XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 1 + // XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 0x080517a8 + // XkbGetWidgets(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58) = 0 + // + // And from that we can tell the repetition rate + // + // Notice, the values must map to: + // [0, 31] which maps to 2.5 to 30 repetitions per second. + // + return 0; + } + } + + public int KeyboardDelay { + get { + // + // Return values must range from 0 to 4, 0 meaning 250ms, + // and 4 meaning 1000 ms. + // + return 1; // ie, 500 ms + } + } + + public int DefaultScreen { + get { return Xlib.XDefaultScreen (display); } + } + + public IntPtr DefaultColormap { + // XXX multiscreen + get { return Xlib.XDefaultColormap (display, DefaultScreen); } + } + + public Keys ModifierKeys { + get { return Keyboard.ModifierKeys; } + } + + public IntPtr OverrideCursor { + get { return OverrideCursorHandle; } + set { + if (Grab.Hwnd != IntPtr.Zero) { + Xlib.XChangeActivePointerGrab (display, + EventMask.ButtonMotionMask | + EventMask.PointerMotionMask | + EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask, + value, IntPtr.Zero); + return; + } + + OverrideCursorHandle = value; + } + } + + public X11RootHwnd RootWindow { + get { return root_hwnd; } + } + + public Size SmallIconSize { + get { + IntPtr list; + XIconSize size; + int count; + + if (Xlib.XGetIconSizes (display, RootWindow.Handle, out list, out count) != 0) { + long current; + int smallest; + + current = (long)list; + smallest = 0; + + size = new XIconSize(); + + for (int i = 0; i < count; i++) { + size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType()); + current += Marshal.SizeOf(size); + + // Look for our preferred size + if (size.min_width == 16) { + Xlib.XFree(list); + return new Size(16, 16); + } + + if (size.max_width == 16) { + Xlib.XFree(list); + return new Size(16, 16); + } + + if (size.min_width < 16 && size.max_width > 16) { + int x; + + // check if we can fit one + x = size.min_width; + while (x < size.max_width) { + x += size.width_inc; + if (x == 16) { + Xlib.XFree(list); + return new Size(16, 16); + } + } + } + + if (smallest == 0 || smallest > size.min_width) { + smallest = size.min_width; + } + } + + // We didn't find a match or we wouldn't be here + return new Size(smallest, smallest); + + } else { + return new Size(16, 16); + } + } + } + + public X11Hwnd FosterParent { + get { return foster_hwnd; } + } + + public int MouseHoverTime { + get { return HoverState.Interval; } + } + + public Rectangle VirtualScreen { + get { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + int width; + int height; + + Xlib.XGetWindowProperty (display, RootWindow.Handle, + Atoms._NET_DESKTOP_GEOMETRY, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL, + out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if ((long)nitems < 2) + goto failsafe; + + width = Marshal.ReadIntPtr(prop, 0).ToInt32(); + height = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32(); + Xlib.XFree(prop); + + return new Rectangle(0, 0, width, height); + + failsafe: + XWindowAttributes attributes = new XWindowAttributes(); + + Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes); + + return new Rectangle(0, 0, attributes.width, attributes.height); + } + } + + public Rectangle WorkingArea { + get { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + int width; + int height; + int current_desktop; + int x; + int y; + + Xlib.XGetWindowProperty (display, RootWindow.Handle, + Atoms._NET_CURRENT_DESKTOP, IntPtr.Zero, new IntPtr(1), false, Atoms.XA_CARDINAL, + out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if ((long)nitems < 1) { + goto failsafe; + } + + current_desktop = Marshal.ReadIntPtr(prop, 0).ToInt32(); + Xlib.XFree(prop); + + Xlib.XGetWindowProperty (display, RootWindow.Handle, + Atoms._NET_WORKAREA, IntPtr.Zero, new IntPtr (256), false, Atoms.XA_CARDINAL, + out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if ((long)nitems < 4 * current_desktop) { + goto failsafe; + } + + x = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop).ToInt32(); + y = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size).ToInt32(); + width = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 2).ToInt32(); + height = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 3).ToInt32(); + Xlib.XFree(prop); + + return new Rectangle(x, y, width, height); + + failsafe: + XWindowAttributes attributes = new XWindowAttributes(); + + Xlib.XGetWindowAttributes (display, RootWindow.Handle, ref attributes); + + return new Rectangle(0, 0, attributes.width, attributes.height); + } + } + + private void XEventThread () + { + while (true) { +#if __MonoCS__ + Syscall.poll (pollfds, 1U, -1); + + while (Xlib.XPending (display) > 0) { +#endif + XEvent xevent = new XEvent (); + Xlib.XNextEvent (display, ref xevent); + + // this is kind of a gross place to put this, but we don't know about the + // key repeat state in X11ThreadQueue, nor to we want the queue code calling + // XPeekEvent. + if (!detectable_key_auto_repeat && + xevent.type == XEventName.KeyRelease && + Xlib.XPending (display) > 0) { + + XEvent nextevent = new XEvent (); + Xlib.XPeekEvent (display, ref nextevent); + + if (nextevent.type == XEventName.KeyPress && + nextevent.KeyEvent.keycode == xevent.KeyEvent.keycode && + nextevent.KeyEvent.time == xevent.KeyEvent.time) { + continue; + } + } + + X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(xevent.AnyEvent.window); + if (hwnd != null) + hwnd.Queue.Enqueue (xevent); +#if __MonoCS__ + } +#endif + } + } + + private void RedirectMsgToEnabledAncestor (X11Hwnd hwnd, MSG msg, IntPtr window, + ref int event_x, ref int event_y) + { + int x, y; + + IntPtr dummy; + msg.hwnd = hwnd.EnabledHwnd; + Xlib.XTranslateCoordinates (display, window, + Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow, + event_x, event_y, + out x, out y, out dummy); + event_x = x; + event_y = y; + msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X); + } + + + // This is called from the thread owning the corresponding X11ThreadQueue + [MonoTODO("Implement filtering")] + public bool GetMessage (object queue_id, ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax) + { + X11ThreadQueue queue = (X11ThreadQueue)queue_id; + XEvent xevent; + bool client; + bool got_xevent = false; + + X11Hwnd hwnd; + + ProcessNextMessage: + do { + got_xevent = queue.Dequeue (out xevent); + + if (!got_xevent) { +#if spew + Console.WriteLine (">"); + Console.Out.Flush (); +#endif + break; + } + +#if spew + Console.Write ("-"); + Console.Out.Flush (); +#endif + + hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window); + + // Handle messages for windows that are already or are about to be destroyed. + + // we need a special block for this because unless we remove the hwnd from the paint + // queue it will always stay there (since we don't handle the expose), and we'll + // effectively loop infinitely trying to repaint a non-existant window. + if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) { + hwnd.PendingExpose = hwnd.PendingNCExpose = false; + goto ProcessNextMessage; + } + + // We need to make sure we only allow DestroyNotify events through for zombie + // hwnds, since much of the event handling code makes requests using the hwnd's + // ClientWindow, and that'll result in BadWindow errors if there's some lag + // between the XDestroyWindow call and the DestroyNotify event. + if (hwnd == null || hwnd.zombie) { +#if DriverDebug || DriverDebugDestroy + Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}", + xevent.type, xevent.AnyEvent.window.ToInt32()); +#endif + goto ProcessNextMessage; + } + + client = hwnd.ClientWindow == xevent.AnyEvent.window; + + msg.hwnd = hwnd.Handle; + + switch (xevent.type) { + case XEventName.KeyPress: + Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg); + return true; + + case XEventName.KeyRelease: + Keyboard.KeyEvent (FocusWindow.Handle, xevent, ref msg); + return true; + + case XEventName.ButtonPress: { + switch(xevent.ButtonEvent.button) { + case 1: + MouseState |= MouseButtons.Left; + if (client) { + msg.message = Msg.WM_LBUTTONDOWN; + } else { + msg.message = Msg.WM_NCLBUTTONDOWN; + hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down + msg.wParam=GetMousewParam(0); + break; + + case 2: + MouseState |= MouseButtons.Middle; + if (client) { + msg.message = Msg.WM_MBUTTONDOWN; + } else { + msg.message = Msg.WM_NCMBUTTONDOWN; + hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + msg.wParam=GetMousewParam(0); + break; + + case 3: + MouseState |= MouseButtons.Right; + if (client) { + msg.message = Msg.WM_RBUTTONDOWN; + } else { + msg.message = Msg.WM_NCRBUTTONDOWN; + hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + msg.wParam=GetMousewParam(0); + break; + + case 4: + msg.hwnd = FocusWindow.Handle; + msg.message=Msg.WM_MOUSEWHEEL; + msg.wParam=GetMousewParam(120); + break; + + case 5: + msg.hwnd = FocusWindow.Handle; + msg.message=Msg.WM_MOUSEWHEEL; + msg.wParam=GetMousewParam(-120); + break; + } + + msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x); + MousePosition.X = xevent.ButtonEvent.x; + MousePosition.Y = xevent.ButtonEvent.y; + + if (!hwnd.Enabled) { + RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window, + ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + + if (Grab.Hwnd != IntPtr.Zero) + msg.hwnd = Grab.Hwnd; + + if (ClickPending.Pending && + ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) && + (msg.wParam == ClickPending.wParam) && + (msg.lParam == ClickPending.lParam) && + (msg.message == ClickPending.Message))) { + // Looks like a genuine double click, clicked twice on the same spot with the same keys + switch(xevent.ButtonEvent.button) { + case 1: + msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK; + break; + + case 2: + msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK; + break; + + case 3: + msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK; + break; + } + + ClickPending.Pending = false; + + } + else { + ClickPending.Pending = true; + ClickPending.Hwnd = msg.hwnd; + ClickPending.Message = msg.message; + ClickPending.wParam = msg.wParam; + ClickPending.lParam = msg.lParam; + ClickPending.Time = (long)xevent.ButtonEvent.time; + } + + if (msg.message == Msg.WM_LBUTTONDOWN || msg.message == Msg.WM_MBUTTONDOWN || msg.message == Msg.WM_RBUTTONDOWN) { + hwnd.SendParentNotify (msg.message, MousePosition.X, MousePosition.Y); + + // Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or + // not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after + // mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh* + XEvent motionEvent = new XEvent (); + motionEvent.type = XEventName.MotionNotify; + motionEvent.MotionEvent.display = display; + motionEvent.MotionEvent.window = xevent.ButtonEvent.window; + motionEvent.MotionEvent.x = xevent.ButtonEvent.x; + motionEvent.MotionEvent.y = xevent.ButtonEvent.y; + hwnd.Queue.Enqueue (motionEvent); + } + + return true; + } + + case XEventName.ButtonRelease: + switch(xevent.ButtonEvent.button) { + case 1: + if (client) { + msg.message = Msg.WM_LBUTTONUP; + } else { + msg.message = Msg.WM_NCLBUTTONUP; + hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + MouseState &= ~MouseButtons.Left; + msg.wParam=GetMousewParam(0); + break; + + case 2: + if (client) { + msg.message = Msg.WM_MBUTTONUP; + } else { + msg.message = Msg.WM_NCMBUTTONUP; + hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + MouseState &= ~MouseButtons.Middle; + msg.wParam=GetMousewParam(0); + break; + + case 3: + if (client) { + msg.message = Msg.WM_RBUTTONUP; + } else { + msg.message = Msg.WM_NCRBUTTONUP; + hwnd.MenuToScreen (ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + MouseState &= ~MouseButtons.Right; + msg.wParam=GetMousewParam(0); + break; + + case 4: + goto ProcessNextMessage; + + case 5: + goto ProcessNextMessage; + } + + if (!hwnd.Enabled) { + RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window, + ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + + if (Grab.Hwnd != IntPtr.Zero) + msg.hwnd = Grab.Hwnd; + + msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x); + MousePosition.X = xevent.ButtonEvent.x; + MousePosition.Y = xevent.ButtonEvent.y; + + // Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or + // not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after + // mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh* + if (msg.message == Msg.WM_LBUTTONUP || msg.message == Msg.WM_MBUTTONUP || msg.message == Msg.WM_RBUTTONUP) { + XEvent motionEvent = new XEvent (); + motionEvent.type = XEventName.MotionNotify; + motionEvent.MotionEvent.display = display; + motionEvent.MotionEvent.window = xevent.ButtonEvent.window; + motionEvent.MotionEvent.x = xevent.ButtonEvent.x; + motionEvent.MotionEvent.y = xevent.ButtonEvent.y; + hwnd.Queue.Enqueue (motionEvent); + } + return true; + + case XEventName.MotionNotify: + /* XXX move the compression stuff here */ + + if (client) { +#if DriverDebugExtra + Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}", + client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(), + xevent.MotionEvent.x, xevent.MotionEvent.y); +#endif + + if (Grab.Hwnd != IntPtr.Zero) + msg.hwnd = Grab.Hwnd; + else + NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT); + + msg.message = Msg.WM_MOUSEMOVE; + msg.wParam = GetMousewParam(0); + msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF); + + if (!hwnd.Enabled) { + RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window, + ref xevent.MotionEvent.x, ref xevent.MotionEvent.y); + } + + MousePosition.X = xevent.MotionEvent.x; + MousePosition.Y = xevent.MotionEvent.y; + + if ((HoverState.Timer.Enabled) && + (((MousePosition.X + HoverState.Size.Width) < HoverState.X) || + ((MousePosition.X - HoverState.Size.Width) > HoverState.X) || + ((MousePosition.Y + HoverState.Size.Height) < HoverState.Y) || + ((MousePosition.Y - HoverState.Size.Height) > HoverState.Y))) { + + HoverState.Timer.Stop(); + HoverState.Timer.Start(); + HoverState.X = MousePosition.X; + HoverState.Y = MousePosition.Y; + } + } + else { + HitTest ht; + IntPtr dummy; + int screen_x; + int screen_y; + + #if DriverDebugExtra + Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}", + client ? hwnd.ClientWindow.ToInt32() : hwnd.WholeWindow.ToInt32(), + xevent.MotionEvent.x, xevent.MotionEvent.y); + #endif + msg.message = Msg.WM_NCMOUSEMOVE; + + if (!hwnd.Enabled) { + RedirectMsgToEnabledAncestor (hwnd, msg, xevent.AnyEvent.window, + ref xevent.MotionEvent.x, ref xevent.MotionEvent.y); + } + + // The hit test is sent in screen coordinates + Xlib.XTranslateCoordinates (display, xevent.AnyEvent.window, RootWindow.Handle, + xevent.MotionEvent.x, xevent.MotionEvent.y, + out screen_x, out screen_y, out dummy); + + msg.lParam = (IntPtr) (screen_y << 16 | screen_x & 0xFFFF); + ht = (HitTest)NativeWindow.WndProc (hwnd.ClientWindow, Msg.WM_NCHITTEST, + IntPtr.Zero, msg.lParam).ToInt32 (); + NativeWindow.WndProc(hwnd.ClientWindow, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht); + + MousePosition.X = xevent.MotionEvent.x; + MousePosition.Y = xevent.MotionEvent.y; + } + + return true; + + case XEventName.EnterNotify: + if (!hwnd.Enabled) + goto ProcessNextMessage; + + if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) + goto ProcessNextMessage; + + msg.message = Msg.WM_MOUSE_ENTER; + HoverState.X = xevent.CrossingEvent.x; + HoverState.Y = xevent.CrossingEvent.y; + HoverState.Timer.Enabled = true; + HoverState.Window = xevent.CrossingEvent.window; + + return true; + + case XEventName.LeaveNotify: + if (!hwnd.Enabled) + goto ProcessNextMessage; + + if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) || + (xevent.CrossingEvent.window != hwnd.ClientWindow)) + goto ProcessNextMessage; + + msg.message=Msg.WM_MOUSELEAVE; + HoverState.Timer.Enabled = false; + HoverState.Window = IntPtr.Zero; + + return true; + + case XEventName.ReparentNotify: + if (hwnd.parent == null) { // Toplevel + if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.WholeWindow)) { + // We need to adjust x/y + // This sucks ass, part 2 + // Every WM does the reparenting of toplevel windows different, so there's + // no standard way of getting our adjustment considering frames/decorations + // The code below is needed for metacity. KDE doesn't works just fine without this + int dummy_int; + IntPtr dummy_ptr; + int new_x; + int new_y; + int frame_left; + int frame_top; + + hwnd.Reparented = true; + + Xlib.XGetGeometry(display, XGetParent(hwnd.WholeWindow), + out dummy_ptr, out new_x, out new_y, + out dummy_int, out dummy_int, out dummy_int, out dummy_int); + hwnd.FrameExtents(out frame_left, out frame_top); + if ((frame_left != 0) && (frame_top != 0) && (new_x != frame_left) && (new_y != frame_top)) { + hwnd.x = new_x; + hwnd.y = new_y; + hwnd.whacky_wm = true; + } + + if (hwnd.opacity != 0xffffffff) { + IntPtr opacity; + + opacity = (IntPtr)(Int32)hwnd.opacity; + Xlib.XChangeProperty (display, XGetParent(hwnd.WholeWindow), + Atoms._NET_WM_WINDOW_OPACITY, Atoms.XA_CARDINAL, 32, + PropertyMode.Replace, ref opacity, 1); + } + SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam); + goto ProcessNextMessage; + } else { + hwnd.Reparented = false; + goto ProcessNextMessage; + } + } + goto ProcessNextMessage; + + case XEventName.ConfigureNotify: + hwnd.HandleConfigureNotify (xevent); + goto ProcessNextMessage; + + case XEventName.MapNotify: { + if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas + hwnd.Mapped = true; + msg.message = Msg.WM_SHOWWINDOW; + msg.wParam = (IntPtr) 1; + // XXX we're missing the lParam.. + break; + } + goto ProcessNextMessage; + } + + case XEventName.UnmapNotify: { + if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas + hwnd.Mapped = false; + msg.message = Msg.WM_SHOWWINDOW; + msg.wParam = (IntPtr) 0; + // XXX we're missing the lParam.. + break; + } + goto ProcessNextMessage; + } + + case XEventName.FocusIn: + // We received focus. We use X11 focus only to know if the app window does or does not have focus + // We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally + // Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know + // about it having focus again + if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear) + goto ProcessNextMessage; + + if (FocusWindow == null) { + Widget c = Widget.FromHandle (hwnd.ClientWindow); + if (c == null) + goto ProcessNextMessage; + Form form = c.FindForm (); + if (form == null) + goto ProcessNextMessage; + X11Hwnd new_active = (X11Hwnd)Hwnd.ObjectFromHandle (form.Handle); + if (ActiveWindow != new_active) { + ActiveWindow = new_active; + SendMessage (ActiveWindow.Handle, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero); + } + goto ProcessNextMessage; + } + Keyboard.FocusIn(FocusWindow.Handle); + SendMessage(FocusWindow.Handle, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero); + goto ProcessNextMessage; + + case XEventName.FocusOut: + // Se the comment for our FocusIn handler + if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear) + goto ProcessNextMessage; + + if (FocusWindow == null) + goto ProcessNextMessage; + + Keyboard.FocusOut(FocusWindow.Handle); + + while (Keyboard.ResetKeyState(FocusWindow.Handle, ref msg)) + SendMessage(FocusWindow.Handle, msg.message, msg.wParam, msg.lParam); + + SendMessage(FocusWindow.Handle, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero); + goto ProcessNextMessage; + + case XEventName.Expose: + if (!hwnd.Mapped) { + hwnd.PendingExpose = hwnd.PendingNCExpose = false; + continue; + } + + msg.hwnd = hwnd.Handle; + + if (client) { +#if DriverDebugExtra + Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}", + hwnd.client_window.ToInt32(), + xevent.ExposeEvent.x, xevent.ExposeEvent.y, + xevent.ExposeEvent.width, xevent.ExposeEvent.height); +#endif + msg.message = Msg.WM_PAINT; + } + else { + Graphics g; + + switch (hwnd.border_style) { + case FormBorderStyle.Fixed3D: + g = Graphics.FromHwnd(hwnd.WholeWindow); + WidgetPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), + Border3DStyle.Sunken); + g.Dispose(); + break; + + case FormBorderStyle.FixedSingle: + g = Graphics.FromHwnd(hwnd.WholeWindow); + WidgetPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), + Color.Black, ButtonBorderStyle.Solid); + g.Dispose(); + break; + } +#if DriverDebugExtra + Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}", + hwnd.ClientWindow.ToInt32(), + xevent.ExposeEvent.x, xevent.ExposeEvent.y, + xevent.ExposeEvent.width, xevent.ExposeEvent.height); +#endif + + Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y, + xevent.ExposeEvent.width, xevent.ExposeEvent.height); + Region region = new Region (rect); + IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed + msg.message = Msg.WM_NCPAINT; + msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn; + msg.refobject = region; + } + + return true; + + case XEventName.DestroyNotify: + + // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children + hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window); + + // We may get multiple for the same window, act only one the first (when Hwnd still knows about it) + if ((hwnd != null) && (hwnd.ClientWindow == xevent.DestroyWindowEvent.window)) { + CleanupCachedWindows (hwnd); + + #if DriverDebugDestroy + Console.WriteLine("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.ClientWindow)); + #endif + + msg.hwnd = hwnd.ClientWindow; + msg.message=Msg.WM_DESTROY; + hwnd.Dispose(); + } + else + goto ProcessNextMessage; + + return true; + + case XEventName.ClientMessage: + if (Dnd.HandleClientMessage (ref xevent)) + goto ProcessNextMessage; + + if (xevent.ClientMessageEvent.message_type == Atoms.AsyncAtom) { + XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1); + goto ProcessNextMessage; + } + + if (xevent.ClientMessageEvent.message_type == HoverState.Atom) { + msg.message = Msg.WM_MOUSEHOVER; + msg.wParam = GetMousewParam(0); + msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1); + return true; + } + + if (xevent.ClientMessageEvent.message_type == Atoms.PostAtom) { + msg.hwnd = xevent.ClientMessageEvent.ptr1; + msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 (); + msg.wParam = xevent.ClientMessageEvent.ptr3; + msg.lParam = xevent.ClientMessageEvent.ptr4; + + // if we posted a WM_QUIT message, make sure we return + // false here as well. + if (msg.message == (Msg)Msg.WM_QUIT) + return false; + else + return true; + } + + if (xevent.ClientMessageEvent.message_type == Atoms._XEMBED) { +#if DriverDebugXEmbed + Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}", + xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32()); +#endif + + if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) { + XSizeHints hints = new XSizeHints(); + IntPtr dummy; + + Xlib.XGetWMNormalHints (display, hwnd.WholeWindow, ref hints, out dummy); + + hwnd.width = hints.max_width; + hwnd.height = hints.max_height; + hwnd.ClientRect = Rectangle.Empty; + SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + } + } + + if (xevent.ClientMessageEvent.message_type == Atoms.WM_PROTOCOLS) { + if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_DELETE_WINDOW) { + msg.message = Msg.WM_CLOSE; + return true; + } + + // We should not get this, but I'll leave the code in case we need it in the future + if (xevent.ClientMessageEvent.ptr1 == Atoms.WM_TAKE_FOCUS) { + goto ProcessNextMessage; + } + } + + goto ProcessNextMessage; + + case XEventName.PropertyNotify: + // The Hwnd's themselves handle this + hwnd.PropertyChanged (xevent); + goto ProcessNextMessage; + } + } while (true); + + msg.hwnd= IntPtr.Zero; + msg.message = Msg.WM_ENTERIDLE; + return true; + } + + [MonoTODO("Implement filtering and PM_NOREMOVE")] + public bool PeekMessage (object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) + { + X11ThreadQueue queue = (X11ThreadQueue) queue_id; + bool pending; + + if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) { + throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag + } + + try { + queue.Lock (); + pending = false; + if (queue.CountUnlocked > 0) + pending = true; + } + catch { + return false; + } + finally { + queue.Unlock (); + } + + queue.CheckTimers (); + + if (!pending) + return false; + + return GetMessage(queue_id, ref msg, hWnd, wFilterMin, wFilterMax); + } + + public void DoEvents (X11ThreadQueue queue) + { + MSG msg = new MSG (); + + if (OverrideCursorHandle != IntPtr.Zero) + OverrideCursorHandle = IntPtr.Zero; + + queue.DispatchIdle = false; + + while (PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) { + TranslateMessage (ref msg); + DispatchMessage (ref msg); + } + + queue.DispatchIdle = true; + } + + // double buffering support + public void CreateOffscreenDrawable (IntPtr handle, + int width, int height, + out object offscreen_drawable) + { + IntPtr root_out; + int x_out, y_out, width_out, height_out, border_width_out, depth_out; + + Xlib.XGetGeometry (display, handle, + out root_out, + out x_out, out y_out, + out width_out, out height_out, + out border_width_out, out depth_out); + + IntPtr pixmap = Xlib.XCreatePixmap (display, handle, width, height, depth_out); + + offscreen_drawable = pixmap; + } + + public void DestroyOffscreenDrawable (object offscreen_drawable) + { + Xlib.XFreePixmap (display, (IntPtr)offscreen_drawable); + } + + public Graphics GetOffscreenGraphics (object offscreen_drawable) + { + return Graphics.FromHwnd ((IntPtr) offscreen_drawable); + } + + public void BlitFromOffscreen (IntPtr dest_handle, + Graphics dest_dc, + object offscreen_drawable, + Graphics offscreen_dc, + Rectangle r) + { + XGCValues gc_values; + IntPtr gc; + + gc_values = new XGCValues(); + + gc = Xlib.XCreateGC (display, dest_handle, IntPtr.Zero, ref gc_values); + + Xlib.XCopyArea (display, (IntPtr)offscreen_drawable, dest_handle, + gc, r.X, r.Y, r.Width, r.Height, r.X, r.Y); + + Xlib.XFreeGC (display, gc); + } + + + // reversible screen-level drawing + IntPtr GetReversibleScreenGC (Color backColor) + { + XGCValues gc_values; + IntPtr gc; + uint pixel; + + XColor xcolor = new XColor(); + xcolor.red = (ushort)(backColor.R * 257); + xcolor.green = (ushort)(backColor.G * 257); + xcolor.blue = (ushort)(backColor.B * 257); + Xlib.XAllocColor (display, DefaultColormap, ref xcolor); + pixel = (uint)xcolor.pixel.ToInt32(); + + + gc_values = new XGCValues(); + + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + gc_values.foreground = (IntPtr)pixel; + + gc = Xlib.XCreateGC (display, RootWindow.Handle, new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCForeground)), ref gc_values); + Xlib.XSetForeground (display, gc, (UIntPtr)pixel); + Xlib.XSetFunction (display, gc, GXFunction.GXxor); + + return gc; + } + + public void DrawReversibleLine (Point start, Point end, Color backColor) + { + if (backColor.GetBrightness() < 0.5) + backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B); + + IntPtr gc = GetReversibleScreenGC (backColor); + + Xlib.XDrawLine (display, RootWindow.Handle, gc, start.X, start.Y, end.X, end.Y); + + Xlib.XFreeGC (display, gc); + } + + public void FillReversibleRectangle (Rectangle rectangle, Color backColor) + { + if (backColor.GetBrightness() < 0.5) + backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B); + + IntPtr gc = GetReversibleScreenGC (backColor); + + if (rectangle.Width < 0) { + rectangle.X += rectangle.Width; + rectangle.Width = -rectangle.Width; + } + if (rectangle.Height < 0) { + rectangle.Y += rectangle.Height; + rectangle.Height = -rectangle.Height; + } + + Xlib.XFillRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height); + + Xlib.XFreeGC (display, gc); + } + + public void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style) + { + if (backColor.GetBrightness() < 0.5) + backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B); + + IntPtr gc = GetReversibleScreenGC (backColor); + + if (rectangle.Width < 0) { + rectangle.X += rectangle.Width; + rectangle.Width = -rectangle.Width; + } + if (rectangle.Height < 0) { + rectangle.Y += rectangle.Height; + rectangle.Height = -rectangle.Height; + } + + int line_width = 1; + GCLineStyle line_style = GCLineStyle.LineSolid; + GCCapStyle cap_style = GCCapStyle.CapButt; + GCJoinStyle join_style = GCJoinStyle.JoinMiter; + + switch (style) { + case FrameStyle.Dashed: + line_style = GCLineStyle.LineOnOffDash; + break; + case FrameStyle.Thick: + line_width = 2; + break; + } + + Xlib.XSetLineAttributes (display, gc, line_width, line_style, cap_style, join_style); + + Xlib.XDrawRectangle (display, RootWindow.Handle, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height); + + Xlib.XFreeGC (display, gc); + } + } +} diff --git a/source/ShiftUI/Internal/X11Dnd.cs b/source/ShiftUI/Internal/X11Dnd.cs new file mode 100644 index 0000000..82f6568 --- /dev/null +++ b/source/ShiftUI/Internal/X11Dnd.cs @@ -0,0 +1,1421 @@ +// 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) 2005 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) +// +// NOTE: We have some tests in Test/ShiftUI/DragAndDropTest.cs, which I *highly* recommend +// to run after any change made here, since those tests are interactive, and thus are not part of +// the common tests. +// + + +using System; +using System.IO; +using System.Text; +using System.Drawing; +using System.Threading; +using System.Collections; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System.Runtime.Serialization.Formatters.Binary; + +namespace ShiftUI { + + internal class X11Dnd { + + private enum State { + Accepting, + Dragging + } + + private enum DragState { + None, + Beginning, + Dragging, + Entered + } + + private interface IDataConverter { + void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent); + void SetData (X11Dnd dnd, object data, ref XEvent xevent); + } + + private delegate void MimeConverter (IntPtr dsp, + IDataObject data, ref XEvent xevent); + + private class MimeHandler { + public string Name; + public string [] Aliases; + public IntPtr Type; + public IntPtr NonProtocol; + public IDataConverter Converter; + + public MimeHandler (string name, IDataConverter converter) : this (name, converter, name) + { + } + + public MimeHandler (string name, IDataConverter converter, params string [] aliases) + { + Name = name; + Converter = converter; + Aliases = aliases; + } + + public override string ToString () + { + return "MimeHandler {" + Name + "}"; + } + } + + private MimeHandler [] MimeHandlers = { +// new MimeHandler ("WCF_DIB"), +// new MimeHandler ("image/gif", new MimeConverter (ImageConverter)), +// new MimeHandler ("text/rtf", new MimeConverter (RtfConverter)), +// new MimeHandler ("text/richtext", new MimeConverter (RtfConverter)), + + new MimeHandler ("text/plain", new TextConverter ()), + new MimeHandler ("text/plain", new TextConverter (), "System.String", DataFormats.Text), + new MimeHandler ("text/html", new HtmlConverter (), DataFormats.Html), + new MimeHandler ("text/uri-list", new UriListConverter (), DataFormats.FileDrop), + new MimeHandler ("application/x-mono-serialized-object", + new SerializedObjectConverter ()) + }; + + private class SerializedObjectConverter : IDataConverter { + + public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent) + { + MemoryStream stream = dnd.GetData (ref xevent); + BinaryFormatter bf = new BinaryFormatter (); + + if (stream.Length == 0) + return; + + stream.Seek (0, 0); + object obj = bf.Deserialize (stream); + data.SetData (obj); + } + + public void SetData (X11Dnd dnd, object data, ref XEvent xevent) + { + if (data == null) + return; + + MemoryStream stream = new MemoryStream (); + BinaryFormatter bf = new BinaryFormatter (); + + bf.Serialize (stream, data); + + IntPtr buffer = Marshal.AllocHGlobal ((int) stream.Length); + stream.Seek (0, 0); + + for (int i = 0; i < stream.Length; i++) { + Marshal.WriteByte (buffer, i, (byte) stream.ReadByte ()); + } + + dnd.SetProperty (ref xevent, buffer, (int) stream.Length); + } + } + + private class HtmlConverter : IDataConverter { + + public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent) + { + string text = dnd.GetText (ref xevent, false); + if (text == null) + return; + data.SetData (DataFormats.Text, text); + data.SetData (DataFormats.UnicodeText, text); + } + + public void SetData (X11Dnd dnd, object data, ref XEvent xevent) + { + IntPtr buffer; + int len; + string str = data as string; + + if (str == null) + return; + + if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) { + byte [] bytes = Encoding.ASCII.GetBytes (str); + buffer = Marshal.AllocHGlobal (bytes.Length); + len = bytes.Length; + for (int i = 0; i < len; i++) + Marshal.WriteByte (buffer, i, bytes [i]); + } else { + buffer = Marshal.StringToHGlobalAnsi (str); + len = 0; + while (Marshal.ReadByte (buffer, len) != 0) + len++; + } + + dnd.SetProperty (ref xevent, buffer, len); + + Marshal.FreeHGlobal (buffer); + } + } + + private class TextConverter : IDataConverter { + + public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent) + { + string text = dnd.GetText (ref xevent, true); + if (text == null) + return; + data.SetData (DataFormats.Text, text); + data.SetData (DataFormats.UnicodeText, text); + } + + public void SetData (X11Dnd dnd, object data, ref XEvent xevent) + { + IntPtr buffer; + int len; + string str = data as string; + + if (str == null) { + IDataObject dobj = data as IDataObject; + if (dobj == null) + return; + str = (string) dobj.GetData ("System.String", true); + } + + if (xevent.SelectionRequestEvent.target == (IntPtr)Atom.XA_STRING) { + byte [] bytes = Encoding.ASCII.GetBytes (str); + buffer = Marshal.AllocHGlobal (bytes.Length); + len = bytes.Length; + for (int i = 0; i < len; i++) + Marshal.WriteByte (buffer, i, bytes [i]); + } else { + buffer = Marshal.StringToHGlobalAnsi (str); + len = 0; + while (Marshal.ReadByte (buffer, len) != 0) + len++; + } + + dnd.SetProperty (ref xevent, buffer, len); + + Marshal.FreeHGlobal (buffer); + } + } + + private class UriListConverter : IDataConverter { + + public void GetData (X11Dnd dnd, IDataObject data, ref XEvent xevent) + { + string text = dnd.GetText (ref xevent, false); + if (text == null) + return; + + // TODO: Do this in a loop instead of just splitting + ArrayList uri_list = new ArrayList (); + string [] lines = text.Split (new char [] { '\r', '\n' }); + foreach (string line in lines) { + // # is a comment line (see RFC 2483) + if (line.StartsWith ("#")) + continue; + try { + Uri uri = new Uri (line); + uri_list.Add (uri.LocalPath); + } catch { } + } + + string [] l = (string []) uri_list.ToArray (typeof (string)); + if (l.Length < 1) + return; + data.SetData (DataFormats.FileDrop, l); + data.SetData ("FileName", l [0]); + data.SetData ("FileNameW", l [0]); + } + + public void SetData (X11Dnd dnd, object data, ref XEvent xevent) + { + string [] uri_list = data as string []; + + if (uri_list == null) { + IDataObject dobj = data as IDataObject; + if (dobj == null) + return; + uri_list = dobj.GetData (DataFormats.FileDrop, true) as string []; + } + + if (uri_list == null) + return; + + StringBuilder res = new StringBuilder (); + foreach (string uri_str in uri_list) { + Uri uri = new Uri (uri_str); + res.Append (uri.ToString ()); + res.Append ("\r\n"); + } + + IntPtr buffer = Marshal.StringToHGlobalAnsi ((string) res.ToString ()); + int len = 0; + while (Marshal.ReadByte (buffer, len) != 0) + len++; + + dnd.SetProperty (ref xevent, buffer, len); + } + } + + private class DragData { + public IntPtr Window; + public DragState State; + public object Data; + public IntPtr Action; + public IntPtr [] SupportedTypes; + public MouseButtons MouseState; + public DragDropEffects AllowedEffects; + public Point CurMousePos; + + public IntPtr LastWindow; + public IntPtr LastTopLevel; + + public bool WillAccept; + + public void Reset () + { + State = DragState.None; + Data = null; + SupportedTypes = null; + WillAccept = false; + } + } + + // This version seems to be the most common + private static readonly IntPtr [] XdndVersion = new IntPtr [] { new IntPtr (4) }; + + private IntPtr display; + private DragData drag_data; + + private IntPtr XdndAware; + private IntPtr XdndSelection; + private IntPtr XdndEnter; + private IntPtr XdndLeave; + private IntPtr XdndPosition; + private IntPtr XdndDrop; + private IntPtr XdndFinished; + private IntPtr XdndStatus; + private IntPtr XdndTypeList; + private IntPtr XdndActionCopy; + private IntPtr XdndActionMove; + private IntPtr XdndActionLink; + //private IntPtr XdndActionPrivate; + private IntPtr XdndActionList; + //private IntPtr XdndActionDescription; + //private IntPtr XdndActionAsk; + + //private State state; + + private int converts_pending; + private bool position_recieved; + private bool status_sent; + private IntPtr target; + private IntPtr source; + private IntPtr toplevel; + private IDataObject data; + + private Widget Widget; + private int pos_x, pos_y; + private DragDropEffects allowed; + private DragEventArgs drag_event; + + private Cursor CursorNo; + private Cursor CursorCopy; + private Cursor CursorMove; + private Cursor CursorLink; + // check out the TODO below + //private IntPtr CurrentCursorHandle; + + private bool tracking = false; + private bool dropped = false; + private int motion_poll; + //private X11Keyboard keyboard; + + public X11Dnd (IntPtr display, X11Keyboard keyboard) + { + this.display = display; + //this.keyboard = keyboard; + + Init (); + } + + public bool InDrag() + { + if (drag_data == null) + return false; + return drag_data.State != DragState.None; + } + + public void SetAllowDrop (Hwnd hwnd, bool allow) + { + int[] atoms; + + if (hwnd.allow_drop == allow) + return; + + atoms = new int[XdndVersion.Length]; + for (int i = 0; i < XdndVersion.Length; i++) { + atoms[i] = XdndVersion[i].ToInt32(); + } + + XplatUIX11.XChangeProperty (display, hwnd.whole_window, XdndAware, + (IntPtr) Atom.XA_ATOM, 32, + PropertyMode.Replace, atoms, allow ? 1 : 0); + hwnd.allow_drop = allow; + } + + public DragDropEffects StartDrag (IntPtr handle, object data, + DragDropEffects allowed_effects) + { + drag_data = new DragData (); + drag_data.Window = handle; + drag_data.State = DragState.Beginning; + drag_data.MouseState = XplatUIX11.MouseState; + drag_data.Data = data; + drag_data.SupportedTypes = DetermineSupportedTypes (data); + drag_data.AllowedEffects = allowed_effects; + drag_data.Action = ActionFromEffect (allowed_effects); + + if (CursorNo == null) { + // Make sure the cursors are created + CursorNo = new Cursor (Properties.Resources.DnDNo); + CursorCopy = new Cursor (Properties.Resources.DnDCopy); + CursorMove = new Cursor (Properties.Resources.DnDMove); + CursorLink = new Cursor (Properties.Resources.DnDLink); + } + + drag_data.LastTopLevel = IntPtr.Zero; + Widget = null; + + ShiftUI.MSG msg = new MSG(); + object queue_id = XplatUI.StartLoop (Thread.CurrentThread); + + Timer timer = new Timer (); + timer.Tick += new EventHandler (DndTickHandler); + timer.Interval = 100; + + int suc; + drag_data.State = DragState.Dragging; + + suc = XplatUIX11.XSetSelectionOwner (display, XdndSelection, + drag_data.Window, IntPtr.Zero); + + if (suc == 0) { + Console.Error.WriteLine ("Could not take ownership of XdndSelection aborting drag."); + drag_data.Reset (); + return DragDropEffects.None; + } + + drag_data.State = DragState.Dragging; + drag_data.CurMousePos = new Point (); + source = toplevel = target = IntPtr.Zero; + dropped = false; + tracking = true; + motion_poll = -1; + timer.Start (); + + // Send Enter to the window initializing the dnd operation - which initializes the data + SendEnter (drag_data.Window, drag_data.Window, drag_data.SupportedTypes); + drag_data.LastTopLevel = toplevel; + + while (tracking && XplatUI.GetMessage (queue_id, ref msg, IntPtr.Zero, 0, 0)) { + + if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST) { + HandleKeyMessage (msg); + } else { + switch (msg.message) { + case Msg.WM_LBUTTONUP: + case Msg.WM_RBUTTONUP: + case Msg.WM_MBUTTONUP: + if (msg.message == Msg.WM_LBUTTONDOWN && drag_data.MouseState != MouseButtons.Left) + break;; + if (msg.message == Msg.WM_RBUTTONDOWN && drag_data.MouseState != MouseButtons.Right) + break; + if (msg.message == Msg.WM_MBUTTONDOWN && drag_data.MouseState != MouseButtons.Middle) + break; + + HandleButtonUpMsg (); + + // We don't want to dispatch button up neither (Match .Net) + // Thus we have to remove capture by ourselves + RemoveCapture (msg.hwnd); + continue; + case Msg.WM_MOUSEMOVE: + motion_poll = 0; + + drag_data.CurMousePos.X = Widget.LowOrder ((int) msg.lParam.ToInt32 ()); + drag_data.CurMousePos.Y = Widget.HighOrder ((int) msg.lParam.ToInt32 ()); + + HandleMouseOver (); + // We don't want to dispatch mouse move + continue; + } + + XplatUI.DispatchMessage (ref msg); + } + } + + timer.Stop (); + + // If the target is a mwf Widget, return until DragEnter/DragLeave has been fired, + // which means the respective -already sent- dnd ClientMessages have been received and handled. + if (Widget != null) + Application.DoEvents (); + + if (!dropped) + return DragDropEffects.None; + if (drag_event != null) + return drag_event.Effect; + + // Fallback. + return DragDropEffects.None; + } + + private void DndTickHandler (object sender, EventArgs e) + { + // This is to make sure we don't get stuck in a loop if another + // app doesn't finish the DND operation + if (dropped) { + Timer t = (Timer) sender; + if (t.Interval == 500) + tracking = false; + else + t.Interval = 500; + } + + + // If motion_poll is -1, there hasn't been motion at all, so don't simulate motion yet. + // Otherwise if more than 100 milliseconds have lapsed, we assume the pointer is not + // in motion anymore, and we simulate the mouse over operation, like .Net does. + if (motion_poll > 1) + HandleMouseOver (); + else if (motion_poll > -1) + motion_poll++; + } + + // This routines helps us to have a DndEnter/DndLeave fallback when there wasn't any mouse movement + // as .Net does + private void DefaultEnterLeave (object user_data) + { + IntPtr toplevel, window; + int x_root, y_root; + + // The window generating the operation could be a different than the one under pointer + GetWindowsUnderPointer (out window, out toplevel, out x_root, out y_root); + Widget source_Widget = Widget.FromHandle (window); + if (source_Widget == null || !source_Widget.AllowDrop) + return; + + // `data' and other members are already available + Point pos = Widget.MousePosition; + DragEventArgs drag_args = new DragEventArgs (data, 0, pos.X, pos.Y, drag_data.AllowedEffects, DragDropEffects.None); + + source_Widget.DndEnter (drag_args); + if ((drag_args.Effect & drag_data.AllowedEffects) != 0) + source_Widget.DndDrop (drag_args); + else + source_Widget.DndLeave (EventArgs.Empty); + } + + public void HandleButtonUpMsg () + { + if (drag_data.State == DragState.Beginning) { + //state = State.Accepting; + } else if (drag_data.State != DragState.None) { + + if (drag_data.WillAccept) { + + if (QueryContinue (false, DragAction.Drop)) + return; + } else { + + if (QueryContinue (false, DragAction.Cancel)) + return; + + // fallback if no movement was detected, as .net does. + if (motion_poll == -1) + DefaultEnterLeave (drag_data.Data); + } + + drag_data.State = DragState.None; + // WE can't reset the drag data yet as it is still + // most likely going to be used by the SelectionRequest + // handlers + } + + return; + } + + private void RemoveCapture (IntPtr handle) + { + Widget c = MwfWindow (handle); + if (c.InternalCapture) + c.InternalCapture = false; + } + + public bool HandleMouseOver () + { + IntPtr toplevel, window; + int x_root, y_root; + + GetWindowsUnderPointer (out window, out toplevel, out x_root, out y_root); + + if (window != drag_data.LastWindow && drag_data.State == DragState.Entered) { + drag_data.State = DragState.Dragging; + + // TODO: Send a Leave if this is an MWF window + + if (toplevel != drag_data.LastTopLevel) + SendLeave (drag_data.LastTopLevel, toplevel); + } + + drag_data.State = DragState.Entered; + if (toplevel != drag_data.LastTopLevel) { + // Entering a new toplevel window + SendEnter (toplevel, drag_data.Window, drag_data.SupportedTypes); + } else { + // Already in a toplevel window, so send a position + SendPosition (toplevel, drag_data.Window, + drag_data.Action, + x_root, y_root, + IntPtr.Zero); + } + + drag_data.LastTopLevel = toplevel; + drag_data.LastWindow = window; + return true; + } + + void GetWindowsUnderPointer (out IntPtr window, out IntPtr toplevel, out int x_root, out int y_root) + { + toplevel = IntPtr.Zero; + window = XplatUIX11.RootWindowHandle; + + IntPtr root, child; + bool dnd_aware = false; + int x_temp, y_temp; + int mask_return; + int x = x_root = drag_data.CurMousePos.X; + int y = y_root = drag_data.CurMousePos.Y; + + while (XplatUIX11.XQueryPointer (display, window, out root, out child, + out x_temp, out y_temp, out x, out y, out mask_return)) { + + if (!dnd_aware) { + dnd_aware = IsWindowDndAware (window); + if (dnd_aware) { + toplevel = window; + x_root = x_temp; + y_root = y_temp; + } + } + + if (child == IntPtr.Zero) + break; + + window = child; + } + } + + public void HandleKeyMessage (MSG msg) + { + if (VirtualKeys.VK_ESCAPE == (VirtualKeys) msg.wParam.ToInt32()) { + QueryContinue (true, DragAction.Cancel); + } + } + + // return true if the event is handled here + public bool HandleClientMessage (ref XEvent xevent) + { + // most common so we check it first + if (xevent.ClientMessageEvent.message_type == XdndPosition) + return Accepting_HandlePositionEvent (ref xevent); + if (xevent.ClientMessageEvent.message_type == XdndEnter) + return Accepting_HandleEnterEvent (ref xevent); + if (xevent.ClientMessageEvent.message_type == XdndDrop) + return Accepting_HandleDropEvent (ref xevent); + if (xevent.ClientMessageEvent.message_type == XdndLeave) + return Accepting_HandleLeaveEvent (ref xevent); + if (xevent.ClientMessageEvent.message_type == XdndStatus) + return HandleStatusEvent (ref xevent); + if (xevent.ClientMessageEvent.message_type == XdndFinished) + return HandleFinishedEvent (ref xevent); + + return false; + } + + public bool HandleSelectionNotifyEvent (ref XEvent xevent) + { + MimeHandler handler = FindHandler ((IntPtr) xevent.SelectionEvent.target); + if (handler == null) + return false; + if (data == null) + data = new DataObject (); + + handler.Converter.GetData (this, data, ref xevent); + + converts_pending--; + if (converts_pending <= 0 && position_recieved) { + drag_event = new DragEventArgs (data, 0, pos_x, pos_y, + allowed, DragDropEffects.None); + Widget.DndEnter (drag_event); + SendStatus (source, drag_event.Effect); + status_sent = true; + } + return true; + } + + public bool HandleSelectionRequestEvent (ref XEvent xevent) + { + if (xevent.SelectionRequestEvent.selection != XdndSelection) + return false; + + MimeHandler handler = FindHandler (xevent.SelectionRequestEvent.target); + if (handler == null) + return false; + + handler.Converter.SetData (this, drag_data.Data, ref xevent); + + return true; + } + + private bool QueryContinue (bool escape, DragAction action) + { + QueryContinueDragEventArgs qce = new QueryContinueDragEventArgs ((int) XplatUI.State.ModifierKeys, + escape, action); + + Widget c = MwfWindow (source); + + if (c == null) { + tracking = false; + return false; + } + + c.DndContinueDrag (qce); + + switch (qce.Action) { + case DragAction.Continue: + return true; + case DragAction.Drop: + SendDrop (drag_data.LastTopLevel, source, IntPtr.Zero); + tracking = false; + return true; + case DragAction.Cancel: + drag_data.Reset (); + c.InternalCapture = false; + break; + } + + SendLeave (drag_data.LastTopLevel, toplevel); + + RestoreDefaultCursor (); + tracking = false; + return false; + } + + private void RestoreDefaultCursor () + { + // Releasing the mouse buttons should automatically restore the default cursor, + // but canceling the operation using QueryContinue should restore it even if the + // mouse buttons are not released yet. + XplatUIX11.XChangeActivePointerGrab (display, + EventMask.ButtonMotionMask | + EventMask.PointerMotionMask | + EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask, + Cursors.Default.Handle, IntPtr.Zero); + + } + + private void GiveFeedback (IntPtr action) + { + GiveFeedbackEventArgs gfe = new GiveFeedbackEventArgs (EffectFromAction (drag_data.Action), true); + + Widget c = MwfWindow (source); + c.DndFeedback (gfe); + + if (gfe.UseDefaultCursors) { + Cursor cursor = CursorNo; + if (drag_data.WillAccept) { + // Same order as on MS + if (action == XdndActionCopy) + cursor = CursorCopy; + else if (action == XdndActionLink) + cursor = CursorLink; + else if (action == XdndActionMove) + cursor = CursorMove; + } + // TODO: Try not to set the cursor so much + //if (cursor.Handle != CurrentCursorHandle) { + XplatUIX11.XChangeActivePointerGrab (display, + EventMask.ButtonMotionMask | + EventMask.PointerMotionMask | + EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask, + cursor.Handle, IntPtr.Zero); + //CurrentCursorHandle = cursor.Handle; + //} + } + } + + private void SetProperty (ref XEvent xevent, IntPtr data, int length) + { + XEvent sel = new XEvent(); + sel.SelectionEvent.type = XEventName.SelectionNotify; + sel.SelectionEvent.send_event = true; + sel.SelectionEvent.display = display; + sel.SelectionEvent.selection = xevent.SelectionRequestEvent.selection; + sel.SelectionEvent.target = xevent.SelectionRequestEvent.target; + sel.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor; + sel.SelectionEvent.time = xevent.SelectionRequestEvent.time; + sel.SelectionEvent.property = IntPtr.Zero; + + XplatUIX11.XChangeProperty (display, xevent.SelectionRequestEvent.requestor, + xevent.SelectionRequestEvent.property, + xevent.SelectionRequestEvent.target, + 8, PropertyMode.Replace, data, length); + sel.SelectionEvent.property = xevent.SelectionRequestEvent.property; + + XplatUIX11.XSendEvent (display, xevent.SelectionRequestEvent.requestor, false, + (IntPtr)EventMask.NoEventMask, ref sel); + return; + } + + private void Reset () + { + ResetSourceData (); + ResetTargetData (); + } + + private void ResetSourceData () + { + converts_pending = 0; + data = null; + } + + private void ResetTargetData () + { + position_recieved = false; + status_sent = false; + } + + private bool Accepting_HandleEnterEvent (ref XEvent xevent) + { + Reset (); + + source = xevent.ClientMessageEvent.ptr1; + toplevel = xevent.AnyEvent.window; + target = IntPtr.Zero; + + ConvertData (ref xevent); + + return true; + } + + private bool Accepting_HandlePositionEvent (ref XEvent xevent) + { + pos_x = (int) xevent.ClientMessageEvent.ptr3 >> 16; + pos_y = (int) xevent.ClientMessageEvent.ptr3 & 0xFFFF; + + // Copy is implicitly allowed + Widget source_Widget = MwfWindow (source); + if (source_Widget == null) + allowed = EffectsFromX11Source (source, xevent.ClientMessageEvent.ptr5) | DragDropEffects.Copy; + else + allowed = drag_data.AllowedEffects; + + IntPtr parent, child, new_child, last_drop_child; + parent = XplatUIX11.XRootWindow (display, 0); + child = toplevel; + last_drop_child = IntPtr.Zero; + while (true) { + int xd, yd; + new_child = IntPtr.Zero; + + if (!XplatUIX11.XTranslateCoordinates (display, + parent, child, pos_x, pos_y, + out xd, out yd, out new_child)) + break; + if (new_child == IntPtr.Zero) + break; + child = new_child; + + Hwnd h = Hwnd.ObjectFromHandle (child); + if (h != null) { + Widget d = Widget.FromHandle (h.client_window); + if (d != null && d.allow_drop) + last_drop_child = child; + } + } + + if (last_drop_child != IntPtr.Zero) + child = last_drop_child; + + if (target != child) { + // We have moved into a new Widget + // or into a Widget for the first time + Finish (); + } + target = child; + Hwnd hwnd = Hwnd.ObjectFromHandle (target); + if (hwnd == null) + return true; + + Widget c = Widget.FromHandle (hwnd.client_window); + + if (c == null) + return true; + if (!c.allow_drop) { + SendStatus (source, DragDropEffects.None); + Finish (); + return true; + } + + Widget = c; + position_recieved = true; + + if (converts_pending > 0) + return true; + + if (!status_sent) { + drag_event = new DragEventArgs (data, 0, pos_x, pos_y, + allowed, DragDropEffects.None); + Widget.DndEnter (drag_event); + + SendStatus (source, drag_event.Effect); + status_sent = true; + } else { + drag_event.x = pos_x; + drag_event.y = pos_y; + Widget.DndOver (drag_event); + + SendStatus (source, drag_event.Effect); + } + + return true; + } + + private void Finish () + { + if (Widget != null) { + if (drag_event == null) { + if (data == null) + data = new DataObject (); + drag_event = new DragEventArgs (data, + 0, pos_x, pos_y, + allowed, DragDropEffects.None); + } + Widget.DndLeave (drag_event); + Widget = null; + } + ResetTargetData (); + } + + private bool Accepting_HandleDropEvent (ref XEvent xevent) + { + if (Widget != null && drag_event != null) { + drag_event = new DragEventArgs (data, + 0, pos_x, pos_y, + allowed, drag_event.Effect); + Widget.DndDrop (drag_event); + } + SendFinished (); + return true; + } + + private bool Accepting_HandleLeaveEvent (ref XEvent xevent) + { + if (Widget != null && drag_event != null) + Widget.DndLeave (drag_event); + // Reset (); + return true; + } + + private bool HandleStatusEvent (ref XEvent xevent) + { + if (drag_data != null && drag_data.State == DragState.Entered) { + + if (!QueryContinue (false, DragAction.Continue)) + return true; + + drag_data.WillAccept = ((int) xevent.ClientMessageEvent.ptr2 & 0x1) != 0; + + GiveFeedback (xevent.ClientMessageEvent.ptr5); + } + return true; + } + + private bool HandleFinishedEvent (ref XEvent xevent) + { + return true; + } + + private DragDropEffects EffectsFromX11Source (IntPtr source, IntPtr action_atom) + { + DragDropEffects allowed = DragDropEffects.None; + IntPtr type, count, remaining, data = IntPtr.Zero; + int format; + + XplatUIX11.XGetWindowProperty (display, source, XdndActionList, + IntPtr.Zero, new IntPtr (32), false, (IntPtr) Atom.AnyPropertyType, + out type, out format, out count, out remaining, ref data); + + int intptr_size = Marshal.SizeOf (typeof (IntPtr)); + for (int i = 0; i < count.ToInt32 (); i++) { + IntPtr current_atom = Marshal.ReadIntPtr (data, i * intptr_size); + allowed |= EffectFromAction (current_atom); + } + + // if source is not providing the action list, use the + // default action passed in the x11 dnd position message + if (allowed == DragDropEffects.None) + allowed = EffectFromAction (action_atom); + + return allowed; + } + + private DragDropEffects EffectFromAction (IntPtr action) + { + if (action == XdndActionCopy) + return DragDropEffects.Copy; + else if (action == XdndActionMove) + return DragDropEffects.Move; + if (action == XdndActionLink) + return DragDropEffects.Link; + + return DragDropEffects.None; + } + + private IntPtr ActionFromEffect (DragDropEffects effect) + { + IntPtr action = IntPtr.Zero; + + // We can't OR together actions on XDND so sadly the primary + // is the only one shown here + if ((effect & DragDropEffects.Copy) != 0) + action = XdndActionCopy; + else if ((effect & DragDropEffects.Move) != 0) + action = XdndActionMove; + else if ((effect & DragDropEffects.Link) != 0) + action = XdndActionLink; + return action; + } + + private bool ConvertData (ref XEvent xevent) + { + bool match = false; + + Widget mwfWidget = MwfWindow (source); + + /* To take advantage of the mwfWidget, we have to be sure + that the dnd operation is still happening (since messages are asynchronous) */ + if (mwfWidget != null && drag_data != null) { + if (!tracking) + return false; + + IDataObject dragged = drag_data.Data as IDataObject; + if (dragged != null) { + data = dragged; + } else { + if (data == null) + data = new DataObject (); + SetDataWithFormats (drag_data.Data); + } + return true; + } + + foreach (IntPtr atom in SourceSupportedList (ref xevent)) { + MimeHandler handler = FindHandler (atom); + if (handler == null) + continue; + XplatUIX11.XConvertSelection (display, XdndSelection, handler.Type, + handler.NonProtocol, toplevel, IntPtr.Zero /* CurrentTime */); + converts_pending++; + match = true; + } + return match; + } + + private void SetDataWithFormats (object value) + { + if (value is string) { + data.SetData (DataFormats.Text, value); + data.SetData (DataFormats.UnicodeText, value); + } + + data.SetData (value); + } + + private MimeHandler FindHandler (IntPtr atom) + { + if (atom == IntPtr.Zero) + return null; + foreach (MimeHandler handler in MimeHandlers) { + if (handler.Type == atom) + return handler; + } + return null; + } + + private MimeHandler FindHandler (string name) + { + foreach (MimeHandler handler in MimeHandlers) { + foreach (string alias in handler.Aliases) { + if (alias == name) + return handler; + } + } + return null; + } + + private void SendStatus (IntPtr source, DragDropEffects effect) + { + XEvent xevent = new XEvent (); + + xevent.AnyEvent.type = XEventName.ClientMessage; + xevent.AnyEvent.display = display; + xevent.ClientMessageEvent.window = source; + xevent.ClientMessageEvent.message_type = XdndStatus; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = toplevel; + if (effect != DragDropEffects.None && (effect & allowed) != 0) + xevent.ClientMessageEvent.ptr2 = (IntPtr) 1; + + xevent.ClientMessageEvent.ptr5 = ActionFromEffect (effect); + XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent); + } + + private void SendEnter (IntPtr handle, IntPtr from, IntPtr [] supported) + { + XEvent xevent = new XEvent (); + + xevent.AnyEvent.type = XEventName.ClientMessage; + xevent.AnyEvent.display = display; + xevent.ClientMessageEvent.window = handle; + xevent.ClientMessageEvent.message_type = XdndEnter; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = from; + + // (int) xevent.ClientMessageEvent.ptr2 & 0x1) + // int ptr2 = 0x1; + // xevent.ClientMessageEvent.ptr2 = (IntPtr) ptr2; + // (e)->xclient.data.l[1] = ((e)->xclient.data.l[1] & ~(0xFF << 24)) | ((v) << 24) + xevent.ClientMessageEvent.ptr2 = (IntPtr) ((long)XdndVersion [0] << 24); + + if (supported.Length > 0) + xevent.ClientMessageEvent.ptr3 = supported [0]; + if (supported.Length > 1) + xevent.ClientMessageEvent.ptr4 = supported [1]; + if (supported.Length > 2) + xevent.ClientMessageEvent.ptr5 = supported [2]; + + XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent); + } + + private void SendDrop (IntPtr handle, IntPtr from, IntPtr time) + { + XEvent xevent = new XEvent (); + + xevent.AnyEvent.type = XEventName.ClientMessage; + xevent.AnyEvent.display = display; + xevent.ClientMessageEvent.window = handle; + xevent.ClientMessageEvent.message_type = XdndDrop; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = from; + xevent.ClientMessageEvent.ptr3 = time; + + XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent); + dropped = true; + } + + private void SendPosition (IntPtr handle, IntPtr from, IntPtr action, int x, int y, IntPtr time) + { + XEvent xevent = new XEvent (); + + xevent.AnyEvent.type = XEventName.ClientMessage; + xevent.AnyEvent.display = display; + xevent.ClientMessageEvent.window = handle; + xevent.ClientMessageEvent.message_type = XdndPosition; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = from; + xevent.ClientMessageEvent.ptr3 = (IntPtr) ((x << 16) | (y & 0xFFFF)); + xevent.ClientMessageEvent.ptr4 = time; + xevent.ClientMessageEvent.ptr5 = action; + + XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent); + } + + private void SendLeave (IntPtr handle, IntPtr from) + { + XEvent xevent = new XEvent (); + + xevent.AnyEvent.type = XEventName.ClientMessage; + xevent.AnyEvent.display = display; + xevent.ClientMessageEvent.window = handle; + xevent.ClientMessageEvent.message_type = XdndLeave; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = from; + + XplatUIX11.XSendEvent (display, handle, false, IntPtr.Zero, ref xevent); + } + + private void SendFinished () + { + XEvent xevent = new XEvent (); + + xevent.AnyEvent.type = XEventName.ClientMessage; + xevent.AnyEvent.display = display; + xevent.ClientMessageEvent.window = source; + xevent.ClientMessageEvent.message_type = XdndFinished; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = toplevel; + + XplatUIX11.XSendEvent (display, source, false, IntPtr.Zero, ref xevent); + } + + // There is a somewhat decent amount of overhead + // involved in setting up dnd so we do it lazily + // as a lot of applications do not even use it. + private void Init () + { + XdndAware = XplatUIX11.XInternAtom (display, "XdndAware", false); + XdndEnter = XplatUIX11.XInternAtom (display, "XdndEnter", false); + XdndLeave = XplatUIX11.XInternAtom (display, "XdndLeave", false); + XdndPosition = XplatUIX11.XInternAtom (display, "XdndPosition", false); + XdndStatus = XplatUIX11.XInternAtom (display, "XdndStatus", false); + XdndDrop = XplatUIX11.XInternAtom (display, "XdndDrop", false); + XdndSelection = XplatUIX11.XInternAtom (display, "XdndSelection", false); + XdndFinished = XplatUIX11.XInternAtom (display, "XdndFinished", false); + XdndTypeList = XplatUIX11.XInternAtom (display, "XdndTypeList", false); + XdndActionCopy = XplatUIX11.XInternAtom (display, "XdndActionCopy", false); + XdndActionMove = XplatUIX11.XInternAtom (display, "XdndActionMove", false); + XdndActionLink = XplatUIX11.XInternAtom (display, "XdndActionLink", false); + //XdndActionPrivate = XplatUIX11.XInternAtom (display, "XdndActionPrivate", false); + XdndActionList = XplatUIX11.XInternAtom (display, "XdndActionList", false); + //XdndActionDescription = XplatUIX11.XInternAtom (display, "XdndActionDescription", false); + //XdndActionAsk = XplatUIX11.XInternAtom (display, "XdndActionAsk", false); + + foreach (MimeHandler handler in MimeHandlers) { + handler.Type = XplatUIX11.XInternAtom (display, handler.Name, false); + handler.NonProtocol = XplatUIX11.XInternAtom (display, + String.Concat ("MWFNonP+", handler.Name), false); + } + + } + + private IntPtr [] SourceSupportedList (ref XEvent xevent) + { + IntPtr [] res; + + + if (((int) xevent.ClientMessageEvent.ptr2 & 0x1) == 0) { + res = new IntPtr [3]; + res [0] = xevent.ClientMessageEvent.ptr3; + res [1] = xevent.ClientMessageEvent.ptr4; + res [2] = xevent.ClientMessageEvent.ptr5; + } else { + IntPtr type; + int format; + IntPtr count; + IntPtr remaining; + IntPtr data = IntPtr.Zero; + + XplatUIX11.XGetWindowProperty (display, source, XdndTypeList, + IntPtr.Zero, new IntPtr(32), false, (IntPtr) Atom.XA_ATOM, + out type, out format, out count, + out remaining, ref data); + + res = new IntPtr [count.ToInt32()]; + for (int i = 0; i < count.ToInt32(); i++) { + res [i] = (IntPtr) Marshal.ReadInt32 (data, i * + Marshal.SizeOf (typeof (int))); + } + + XplatUIX11.XFree (data); + } + + return res; + } + + private string GetText (ref XEvent xevent, bool unicode) + { + int nread = 0; + IntPtr nitems; + IntPtr bytes_after; + + StringBuilder builder = new StringBuilder (); + do { + IntPtr actual_type; + int actual_fmt; + IntPtr data = IntPtr.Zero; + + if (0 != XplatUIX11.XGetWindowProperty (display, + xevent.AnyEvent.window, + (IntPtr) xevent.SelectionEvent.property, + IntPtr.Zero, new IntPtr(0xffffff), false, + (IntPtr) Atom.AnyPropertyType, out actual_type, + out actual_fmt, out nitems, out bytes_after, + ref data)) { + XplatUIX11.XFree (data); + break; + } + + if (unicode) + builder.Append (Marshal.PtrToStringUni (data)); + else + builder.Append (Marshal.PtrToStringAnsi (data)); + nread += nitems.ToInt32(); + + XplatUIX11.XFree (data); + } while (bytes_after.ToInt32() > 0); + if (nread == 0) + return null; + return builder.ToString (); + } + + private MemoryStream GetData (ref XEvent xevent) + { + int nread = 0; + IntPtr nitems; + IntPtr bytes_after; + + MemoryStream res = new MemoryStream (); + do { + IntPtr actual_type; + int actual_fmt; + IntPtr data = IntPtr.Zero; + + if (0 != XplatUIX11.XGetWindowProperty (display, + xevent.AnyEvent.window, + (IntPtr) xevent.SelectionEvent.property, + IntPtr.Zero, new IntPtr(0xffffff), false, + (IntPtr) Atom.AnyPropertyType, out actual_type, + out actual_fmt, out nitems, out bytes_after, + ref data)) { + XplatUIX11.XFree (data); + break; + } + + for (int i = 0; i < nitems.ToInt32(); i++) + res.WriteByte (Marshal.ReadByte (data, i)); + nread += nitems.ToInt32(); + + XplatUIX11.XFree (data); + } while (bytes_after.ToInt32() > 0); + return res; + } + + private Widget MwfWindow (IntPtr window) + { + Hwnd hwnd = Hwnd.ObjectFromHandle (window); + if (hwnd == null) + return null; + + Widget res = Widget.FromHandle (hwnd.client_window); + + if (res == null) + res = Widget.FromHandle (window); + + return res; + } + + private bool IsWindowDndAware (IntPtr handle) + { + bool res = true; + // Check the version number, we need greater than 3 + IntPtr actual; + int format; + IntPtr count; + IntPtr remaining; + IntPtr data = IntPtr.Zero; + + XplatUIX11.XGetWindowProperty (display, handle, XdndAware, IntPtr.Zero, new IntPtr(0x8000000), false, + (IntPtr) Atom.XA_ATOM, out actual, out format, + out count, out remaining, ref data); + + if (actual != (IntPtr) Atom.XA_ATOM || format != 32 || + count.ToInt32() == 0 || data == IntPtr.Zero) { + if (data != IntPtr.Zero) + XplatUIX11.XFree (data); + return false; + } + + int version = Marshal.ReadInt32 (data, 0); + + if (version < 3) { + Console.Error.WriteLine ("XDND Version too old (" + version + ")."); + XplatUIX11.XFree (data); + return false; + } + + // First type is actually the XDND version + if (count.ToInt32() > 1) { + res = false; + for (int i = 1; i < count.ToInt32(); i++) { + IntPtr type = (IntPtr) Marshal.ReadInt32 (data, i * + Marshal.SizeOf (typeof (int))); + for (int j = 0; j < drag_data.SupportedTypes.Length; j++) { + if (drag_data.SupportedTypes [j] == type) { + res = true; + break; + } + } + } + } + + XplatUIX11.XFree (data); + return res; + } + + private IntPtr [] DetermineSupportedTypes (object data) + { + ArrayList res = new ArrayList (); + + if (data is string) { + MimeHandler handler = FindHandler ("text/plain"); + if (handler != null) + res.Add (handler.Type); + }/* else if (data is Bitmap) + res.Add (data); + + */ + + IDataObject data_object = data as IDataObject; + if (data_object != null) { + foreach (string format in data_object.GetFormats (true)) { + MimeHandler handler = FindHandler (format); + if (handler != null && !res.Contains (handler.Type)) + res.Add (handler.Type); + } + } + + if (data is ISerializable) { + MimeHandler handler = FindHandler ("application/x-mono-serialized-object"); + if (handler != null) + res.Add (handler.Type); + } + + return (IntPtr []) res.ToArray (typeof (IntPtr)); + } + } +} diff --git a/source/ShiftUI/Internal/X11Exception.cs b/source/ShiftUI/Internal/X11Exception.cs new file mode 100644 index 0000000..5d3d47e --- /dev/null +++ b/source/ShiftUI/Internal/X11Exception.cs @@ -0,0 +1,87 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// +// + +using System; +using System.Text; +using System.Threading; +using ShiftUI; + +namespace ShiftUI.X11Internal { + + internal class X11Exception : ApplicationException { + IntPtr Display; + IntPtr ResourceID; + IntPtr Serial; + XRequest RequestCode; + byte ErrorCode; + byte MinorCode; + + public X11Exception (IntPtr Display, IntPtr ResourceID, IntPtr Serial, byte ErrorCode, XRequest RequestCode, byte MinorCode) + { + this.Display = Display; + this.ResourceID = ResourceID; + this.Serial = Serial; + this.RequestCode = RequestCode; + this.ErrorCode = ErrorCode; + this.MinorCode = MinorCode; + } + + public override string Message { + get { + return GetMessage (Display, ResourceID, Serial, ErrorCode, RequestCode, MinorCode); + } + } + + public static string GetMessage (IntPtr Display, IntPtr ResourceID, IntPtr Serial, byte ErrorCode, XRequest RequestCode, byte MinorCode) + { + StringBuilder sb; + string x_error_text; + string error; + string hwnd_text; + string Widget_text; + Hwnd hwnd; + Widget c; + + sb = new StringBuilder(160); + Xlib.XGetErrorText (Display, ErrorCode, sb, sb.Capacity); + x_error_text = sb.ToString(); + hwnd = Hwnd.ObjectFromHandle(ResourceID); + if (hwnd != null) { + hwnd_text = hwnd.ToString(); + c = Widget.FromHandle(hwnd.Handle); + if (c != null) { + Widget_text = c.ToString(); + } else { + Widget_text = String.Format("<handle {0:X} non-existant>", hwnd.Handle); + } + } + else { + hwnd_text = "<null>"; + Widget_text = "<null>"; + } + + error = String.Format("\n Error: {0}\n Request: {1:D} ({2})\n Resource ID: 0x{3:X}\n Serial: {4}\n Hwnd: {5}\n Widget: {6}", x_error_text, RequestCode, RequestCode, ResourceID.ToInt32(), Serial, hwnd_text, Widget_text); + return error; + } + } +} diff --git a/source/ShiftUI/Internal/X11Hwnd.cs b/source/ShiftUI/Internal/X11Hwnd.cs new file mode 100644 index 0000000..0ef248d --- /dev/null +++ b/source/ShiftUI/Internal/X11Hwnd.cs @@ -0,0 +1,1750 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// +// + +using System; +using System.Collections; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using ShiftUI; + +namespace ShiftUI.X11Internal { + + internal class X11Hwnd : Hwnd + { + X11Display display; + + bool refetch_window_type = true; + bool refetch_window_opacity = true; + + IntPtr[] wm_state = new IntPtr[0]; + IntPtr[] window_type = new IntPtr[0]; + double trans = 1.0; + + string text; + new X11ThreadQueue queue; + + const EventMask SelectInputMask = (EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask | + EventMask.KeyPressMask | + EventMask.KeyReleaseMask | + EventMask.EnterWindowMask | + EventMask.LeaveWindowMask | + EventMask.ExposureMask | + EventMask.FocusChangeMask | + EventMask.PointerMotionMask | + EventMask.SubstructureNotifyMask); + + public X11Hwnd (X11Display display) + { + this.display = display; + Queue = XplatUIX11_new.GetInstance().ThreadQueue(Thread.CurrentThread); + } + + public X11Hwnd (X11Display display, IntPtr handle) : this (display) + { + if (handle == IntPtr.Zero) + throw new ArgumentNullException ("handle"); + WholeWindow = ClientWindow = handle; + } + + // XXX this needs to be here so we don't have to + // change Hwnd. once we land, remove this and make + // Hwnd.Queue virtual or abstract + public new X11ThreadQueue Queue { + get { return queue; } + set { queue = value; } + } + + public virtual void CreateWindow (CreateParams cp) + { + if (WholeWindow != IntPtr.Zero || ClientWindow != IntPtr.Zero) + throw new Exception ("createwindow called a second time on live X11Hwnd"); + + XSetWindowAttributes Attributes; + int x; + int y; + int width; + int height; + IntPtr ParentHandle; + SetWindowValuemask ValueMask; + + Attributes = new XSetWindowAttributes(); + x = cp.X; + y = cp.Y; + width = cp.Width; + height = cp.Height; + initial_ex_style = (WindowExStyles) cp.ExStyle; + + /* Figure out our parent handle */ + if (cp.Parent != IntPtr.Zero) + // the parent handle is specified in the CreateParams + ParentHandle = Hwnd.ObjectFromHandle(cp.Parent).ClientWindow; + else if (StyleSet (cp.Style, WindowStyles.WS_CHILD)) + // a child Widget with an unassigned parent gets created under the FosterParent + ParentHandle = display.FosterParent.Handle; + else + // for all other cases, the parent is the root window + ParentHandle = display.RootWindow.Handle; + + ValueMask = SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; + + Attributes.bit_gravity = Gravity.NorthWestGravity; + Attributes.win_gravity = Gravity.NorthWestGravity; + + // Save what's under the toolwindow + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + Attributes.save_under = true; + ValueMask |= SetWindowValuemask.SaveUnder; + } + + // If we're a popup without caption we override the WM + if (StyleSet (cp.Style, WindowStyles.WS_POPUP) && !StyleSet (cp.Style, WindowStyles.WS_CAPTION)) { + Attributes.override_redirect = true; + ValueMask |= SetWindowValuemask.OverrideRedirect; + } + + // Default position on screen, if window manager doesn't place us somewhere else + if (!StyleSet (cp.Style, WindowStyles.WS_CHILD) + && !StyleSet (cp.Style, WindowStyles.WS_POPUP)) { + if (x<0) x = 50; + if (y<0) y = 50; + } + // minimum width/height + if (width<1) width=1; + if (height<1) height=1; + + X = x; + Y = y; + Width = width; + Height = height; + Parent = Hwnd.ObjectFromHandle (cp.Parent); + + Enabled = !StyleSet (cp.Style, WindowStyles.WS_DISABLED); + + ClientWindow = IntPtr.Zero; + + WholeWindow = Xlib.XCreateWindow (display.Handle, ParentHandle, + X, Y, Width, Height, 0, + (int)CreateWindowArgs.CopyFromParent, (int)CreateWindowArgs.InputOutput, + IntPtr.Zero, new UIntPtr ((uint)ValueMask), ref Attributes); + if (WholeWindow == IntPtr.Zero) + throw new Exception ("Coult not create X11 nc window"); + + ValueMask &= ~(SetWindowValuemask.OverrideRedirect | SetWindowValuemask.SaveUnder); + + if (display.CustomVisual != IntPtr.Zero && display.CustomColormap != IntPtr.Zero) { + ValueMask |= SetWindowValuemask.ColorMap; + Attributes.colormap = display.CustomColormap; + } + + ClientWindow = Xlib.XCreateWindow (display.Handle, WholeWindow, + ClientRect.X, ClientRect.Y, ClientRect.Width, ClientRect.Height, 0, + (int)CreateWindowArgs.CopyFromParent, (int)CreateWindowArgs.InputOutput, + display.CustomVisual, new UIntPtr ((uint)ValueMask), ref Attributes); + + if (ClientWindow == IntPtr.Zero) + throw new Exception("Could not create X11 client window"); + +#if DriverDebug || DriverDebugCreate + Console.WriteLine("Created window {0:X} / {1:X} parent {2:X}, Style {3}, ExStyle {4}", ClientWindow.ToInt32(), WholeWindow.ToInt32(), Parent != null ? Parent.Handle.ToInt32() : 0, (WindowStyles)cp.Style, (WindowExStyles)cp.ExStyle); +#endif + + if (!StyleSet (cp.Style, WindowStyles.WS_CHILD)) { + if ((X != unchecked((int)0x80000000)) && (Y != unchecked((int)0x80000000))) { + XSizeHints hints; + + hints = new XSizeHints(); + hints.x = X; + hints.y = Y; + hints.flags = (IntPtr)(XSizeHintsFlags.USPosition | XSizeHintsFlags.PPosition); + Xlib.XSetWMNormalHints (display.Handle, WholeWindow, ref hints); + } + } + + Xlib.XSelectInput (display.Handle, WholeWindow, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask))); + if (WholeWindow != ClientWindow) + Xlib.XSelectInput (display.Handle, ClientWindow, new IntPtr ((int)SelectInputMask)); + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOPMOST)) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_NORMAL; + Xlib.XSetTransientForHint (display.Handle, WholeWindow, display.RootWindow.Handle); + } + + SetWMStyles (cp); + + // set the group leader + XWMHints wm_hints = new XWMHints (); + + wm_hints.flags = (IntPtr)(XWMHintsFlags.InputHint | XWMHintsFlags.StateHint | XWMHintsFlags.WindowGroupHint); + wm_hints.input = !StyleSet (cp.Style, WindowStyles.WS_DISABLED); + wm_hints.initial_state = StyleSet (cp.Style, WindowStyles.WS_MINIMIZE) ? XInitialState.IconicState : XInitialState.NormalState; + wm_hints.window_group = ParentHandle == display.RootWindow.Handle ? ParentHandle : WholeWindow; + + Xlib.XSetWMHints (display.Handle, WholeWindow, ref wm_hints ); + + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZE)) + SetWindowState (FormWindowState.Minimized); + else if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZE)) + SetWindowState (FormWindowState.Maximized); + + // for now make all windows dnd enabled + display.Dnd.SetAllowDrop (this, true); + + // Set caption/window title + Text = cp.Caption; + + display.SendMessage (Handle, Msg.WM_CREATE, (IntPtr)1, IntPtr.Zero /* XXX unused */); + SendParentNotify (Msg.WM_CREATE, int.MaxValue, int.MaxValue); + + if (StyleSet (cp.Style, WindowStyles.WS_VISIBLE)) { + visible = true; + Map (); + if (!(Widget.FromHandle(Handle) is Form)) + display.SendMessage (Handle, Msg.WM_SHOWWINDOW, (IntPtr)1, IntPtr.Zero); + } + } + + public virtual void DestroyWindow () + { + if (WholeWindow != IntPtr.Zero) { +#if DriverDebug || DriverDebugDestroy + Console.WriteLine ("XDestroyWindow (whole_window = {0:X})", WholeWindow.ToInt32()); +#endif + Xlib.XDestroyWindow (display.Handle, WholeWindow); + } + else if (ClientWindow != IntPtr.Zero) { +#if DriverDebug || DriverDebugDestroy + Console.WriteLine ("XDestroyWindow (client_window = {0:X})", ClientWindow.ToInt32()); +#endif + Xlib.XDestroyWindow (display.Handle, ClientWindow); + } + } + + public void Activate () + { + if (((IList)display.RootWindow._NET_SUPPORTED).Contains (display.Atoms._NET_ACTIVE_WINDOW)) { + display.SendNetWMMessage (WholeWindow, display.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, IntPtr.Zero, IntPtr.Zero); + } + else { + Xlib.XRaiseWindow (display.Handle, WholeWindow); + } + } + + public void Update () + { + try { + Queue.Lock (); + if (!Visible || !PendingExpose || !Mapped) + return; + + // XXX this SendMessage call should probably not be inside the lock + display.SendMessage (ClientWindow, Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + + PendingExpose = false; + } + finally { + Queue.Unlock (); + } + } + + public void MenuToScreen (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + WholeWindow, display.RootWindow.Handle, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + public virtual void PropertyChanged (XEvent xevent) + { + if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_WINDOW_TYPE) { + // we need to recache our WINDOW_TYPE on the next query + refetch_window_type = true; + window_type = null; + } + else if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_STATE) { + // we need to recache our WM_STATE on the next query + } + else if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_NAME) { + // update our Text property + } + else if (xevent.PropertyEvent.atom == display.Atoms._NET_WM_WINDOW_OPACITY) { + // we need to recache our _NET_WM_WINDOW_OPACITY on the next query. + refetch_window_opacity = true; + } + // else we don't care about it + + } + + public void SetIcon (Icon icon) + { + if (icon == null) { + Xlib.XDeleteProperty (display.Handle, WholeWindow, display.Atoms._NET_WM_ICON); + } + else { + Bitmap bitmap; + int size; + IntPtr[] data; + int index; + + bitmap = icon.ToBitmap(); + index = 0; + size = bitmap.Width * bitmap.Height + 2; + data = new IntPtr[size]; + + data[index++] = (IntPtr)bitmap.Width; + data[index++] = (IntPtr)bitmap.Height; + + for (int y = 0; y < bitmap.Height; y++) { + for (int x = 0; x < bitmap.Width; x++) { + data[index++] = (IntPtr)bitmap.GetPixel(x, y).ToArgb(); + } + } + + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_ICON, display.Atoms.XA_CARDINAL, 32, + PropertyMode.Replace, data, size); + } + } + + public double GetWindowTransparency () + { + if (refetch_window_opacity) { + trans = 1.0; + + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + IntPtr w = WholeWindow; + if (reparented) + w = display.XGetParent (WholeWindow); + + Xlib.XGetWindowProperty (display.Handle, w, + display.Atoms._NET_WM_WINDOW_OPACITY, IntPtr.Zero, new IntPtr (16), false, + display.Atoms.XA_CARDINAL, + out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (((long)nitems == 1) && (prop != IntPtr.Zero)) { + uint x11_opacity = (uint)Marshal.ReadInt32(prop, 0); + trans = ((double)x11_opacity) / (uint)0xffffffff; + } + + if (prop != IntPtr.Zero) { + Xlib.XFree(prop); + } + } + + return trans; + } + + public void SetWindowTransparency (double transparency, Color key) + { + IntPtr x11_opacity; + + opacity = (uint)(0xffffffff * transparency); + x11_opacity = (IntPtr)((int)opacity); + + IntPtr w = WholeWindow; + if (reparented) + w = display.XGetParent (WholeWindow); + Xlib.XChangeProperty (display.Handle, w, + display.Atoms._NET_WM_WINDOW_OPACITY, display.Atoms.XA_CARDINAL, 32, + PropertyMode.Replace, ref x11_opacity, 1); + } + + public IntPtr DefWndProc (ref Message msg) + { + switch ((Msg)msg.Msg) { + case Msg.WM_PAINT: + Queue.Lock (); + PendingExpose = false; + Queue.Unlock (); + return IntPtr.Zero; + + case Msg.WM_NCPAINT: + Queue.Lock (); + PendingNCExpose = false; + Queue.Unlock (); + return IntPtr.Zero; + + case Msg.WM_CONTEXTMENU: + if (Parent != null) + display.SendMessage (Parent.ClientWindow, Msg.WM_CONTEXTMENU, msg.WParam, msg.LParam); + return IntPtr.Zero; + + case Msg.WM_MOUSEWHEEL: + if (Parent != null) { + display.SendMessage (Parent.ClientWindow, Msg.WM_MOUSEWHEEL, msg.WParam, msg.LParam); + if (msg.Result == IntPtr.Zero) + return IntPtr.Zero; + } + return IntPtr.Zero; + + case Msg.WM_SETCURSOR: + X11Hwnd parent = (X11Hwnd)Parent; + // Pass to parent window first + while ((parent != null) && (msg.Result == IntPtr.Zero)) { + msg.Result = NativeWindow.WndProc (parent.Handle, Msg.WM_SETCURSOR, msg.HWnd, msg.LParam); + parent = (X11Hwnd)Parent; + } + + if (msg.Result == IntPtr.Zero) { + IntPtr handle; + + switch((HitTest)(msg.LParam.ToInt32() & 0xffff)) { + case HitTest.HTBOTTOM: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBORDER: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBOTTOMLEFT: handle = Cursors.SizeNESW.handle; break; + case HitTest.HTBOTTOMRIGHT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTERROR: + if ((msg.LParam.ToInt32() >> 16) == (int)Msg.WM_LBUTTONDOWN) + display.AudibleAlert(); + handle = Cursors.Default.handle; + break; + + case HitTest.HTHELP: handle = Cursors.Help.handle; break; + case HitTest.HTLEFT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTRIGHT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTTOP: handle = Cursors.SizeNS.handle; break; + case HitTest.HTTOPLEFT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTTOPRIGHT: handle = Cursors.SizeNESW.handle; break; + +#if SameAsDefault + case HitTest.HTGROWBOX: + case HitTest.HTSIZE: + case HitTest.HTZOOM: + case HitTest.HTVSCROLL: + case HitTest.HTSYSMENU: + case HitTest.HTREDUCE: + case HitTest.HTNOWHERE: + case HitTest.HTMAXBUTTON: + case HitTest.HTMINBUTTON: + case HitTest.HTMENU: + case HitTest.HSCROLL: + case HitTest.HTBOTTOM: + case HitTest.HTCAPTION: + case HitTest.HTCLIENT: + case HitTest.HTCLOSE: +#endif + default: handle = Cursors.Default.handle; break; + } + + display.SetCursor (msg.HWnd, handle); + } + return (IntPtr)1; + + default: + return IntPtr.Zero; + } + } + + + public void AddExpose (bool client, int x, int y, int width, int height) + { + // Don't waste time + if ((x > Width) || (y > Height) || ((x + width) <= 0) || ((y + height) <= 0)) + return; + + // Keep the invalid area as small as needed + if ((x + width) > Width) + width = Width - x; + + if ((y + height) > Height) + height = Height - y; + + if (client) { + AddInvalidArea(x, y, width, height); + PendingExpose = true; + } + else { + AddNcInvalidArea (x, y, width, height); + PendingNCExpose = true; + } + } + + public void AddConfigureNotify (XEvent xevent) + { + // We drop configure events for Client windows + if ((xevent.ConfigureEvent.window != WholeWindow) || (xevent.ConfigureEvent.window != xevent.ConfigureEvent.xevent)) + return; + + if (!reparented) { + X = xevent.ConfigureEvent.x; + Y = xevent.ConfigureEvent.y; + } else { + // This sucks ass, part 1 + // Every WM does the ConfigureEvents of toplevel windows different, so there's + // no standard way of getting our adjustment. + // The code below is needed for KDE and FVWM, the 'whacky_wm' part is for metacity + // Several other WMs do their decorations different yet again and we fail to deal + // with that, since I couldn't find any frigging commonality between them. + // The only sane WM seems to be KDE + + if (!xevent.ConfigureEvent.send_event) { + IntPtr dummy_ptr; + + int trans_x; + int trans_y; + + Xlib.XTranslateCoordinates (display.Handle, WholeWindow, display.RootWindow.Handle, + -xevent.ConfigureEvent.x, -xevent.ConfigureEvent.y, + out trans_x, out trans_y, out dummy_ptr); + + X = trans_x; + Y = trans_y; + } else { + // This is a synthetic event, coordinates are in root space + X = xevent.ConfigureEvent.x; + Y = xevent.ConfigureEvent.y; + if (whacky_wm) { + int frame_left; + int frame_top; + + FrameExtents (out frame_left, out frame_top); + X -= frame_left; + Y -= frame_top; + } + } + } + + Width = xevent.ConfigureEvent.width; + Height = xevent.ConfigureEvent.height; + ClientRect = Rectangle.Empty; + + if (!configure_pending) { + Queue.AddConfigure (this); + configure_pending = true; + } + } + + public void HandleMapEvent (XEvent xevent) + { + if (xevent.type == XEventName.MapNotify) { + } + else { + } + } + + public void HandleConfigureNotify (XEvent xevent) + { + configure_pending = false; + + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + + // We need to adjust our client window to track the resize of whole_window + if (WholeWindow != ClientWindow) + PerformNCCalc (); + } + + public void Invalidate (Rectangle rc, bool clear) + { + if (clear) { + AddExpose (true, X, Y, Width, Height); + } else { + AddExpose (true, rc.X, rc.Y, rc.Width, rc.Height); + } + } + + public void InvalidateNC () + { + AddExpose (false, 0, 0, Width, Height); + } + + // XXX this assumes the queue lock is held + public bool PendingNCExpose { + get { return nc_expose_pending; } + set { + if (nc_expose_pending == value) + return; + nc_expose_pending = value; + + if (nc_expose_pending && !expose_pending) + Queue.AddPaint (this); + else if (!nc_expose_pending && !expose_pending) + Queue.RemovePaint (this); + } + } + + // XXX this assumes the queue lock is held + public bool PendingExpose { + get { return expose_pending; } + set { + if (expose_pending == value) + return; + expose_pending = value; + + if (expose_pending && !nc_expose_pending) + Queue.AddPaint (this); + else if (!expose_pending && !nc_expose_pending) + Queue.RemovePaint (this); + } + } + + public PaintEventArgs PaintEventStart (ref Message m, bool client) + { + PaintEventArgs paint_event; + Graphics dc; + + if (client) { + dc = Graphics.FromHwnd (ClientWindow); + + Region clip_region = new Region (); + clip_region.MakeEmpty(); + + foreach (Rectangle r in ClipRectangles) + clip_region.Union (r); + + if (UserClip != null) + clip_region.Intersect(UserClip); + + dc.Clip = clip_region; + paint_event = new PaintEventArgs(dc, Invalid); + PendingExpose = false; + + ClearInvalidArea(); + + drawing_stack.Push (paint_event); + drawing_stack.Push (dc); + + return paint_event; + } + else { + dc = Graphics.FromHwnd (WholeWindow); + + if (!nc_invalid.IsEmpty) { + dc.SetClip (nc_invalid); + paint_event = new PaintEventArgs(dc, nc_invalid); + } + else { + paint_event = new PaintEventArgs(dc, new Rectangle(0, 0, width, height)); + } + PendingNCExpose = false; + + ClearNcInvalidArea (); + + drawing_stack.Push (paint_event); + drawing_stack.Push (dc); + + return paint_event; + } + } + + public void PaintEventEnd (ref Message m, bool client) + { + Graphics dc = (Graphics)drawing_stack.Pop (); + dc.Flush(); + dc.Dispose(); + + PaintEventArgs pe = (PaintEventArgs)drawing_stack.Pop(); + pe.SetGraphics (null); + pe.Dispose (); + } + + public void DrawReversibleRectangle (Rectangle rect, int line_width) + { + XGCValues gc_values; + IntPtr gc; + + gc_values = new XGCValues (); + + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + gc_values.line_width = line_width; + + // XXX multiscreen support + gc_values.foreground = Xlib.XBlackPixel (display.Handle, display.DefaultScreen); + + // This logic will give us true rubber bands: (libsx, SANE_XOR) + //mask = foreground ^ background; + //XSetForeground(DisplayHandle, gc, 0xffffffff); + //XSetBackground(DisplayHandle, gc, background); + //XSetFunction(DisplayHandle, gc, GXxor); + //XSetPlaneMask(DisplayHandle, gc, mask); + + + gc = Xlib.XCreateGC (display.Handle, ClientWindow, + new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCLineWidth | GCFunction.GCForeground)), ref gc_values); + uint foreground; + uint background; + + Widget Widget; + Widget = Widget.FromHandle(Handle); + + XColor xcolor = new XColor(); + + xcolor.red = (ushort)(Widget.ForeColor.R * 257); + xcolor.green = (ushort)(Widget.ForeColor.G * 257); + xcolor.blue = (ushort)(Widget.ForeColor.B * 257); + Xlib.XAllocColor (display.Handle, display.DefaultColormap, ref xcolor); + foreground = (uint)xcolor.pixel.ToInt32(); + + xcolor.red = (ushort)(Widget.BackColor.R * 257); + xcolor.green = (ushort)(Widget.BackColor.G * 257); + xcolor.blue = (ushort)(Widget.BackColor.B * 257); + Xlib.XAllocColor (display.Handle, display.DefaultColormap, ref xcolor); + background = (uint)xcolor.pixel.ToInt32(); + + uint mask = foreground ^ background; + + Xlib.XSetForeground (display.Handle, gc, (UIntPtr)0xffffffff); + Xlib.XSetBackground (display.Handle, gc, (UIntPtr)background); + Xlib.XSetFunction (display.Handle, gc, GXFunction.GXxor); + Xlib.XSetPlaneMask (display.Handle, gc, (IntPtr)mask); + + if ((rect.Width > 0) && (rect.Height > 0)) + Xlib.XDrawRectangle (display.Handle, ClientWindow, gc, rect.Left, rect.Top, rect.Width, rect.Height); + else if (rect.Width > 0) + Xlib.XDrawLine (display.Handle, ClientWindow, gc, rect.X, rect.Y, rect.Right, rect.Y); + else + Xlib.XDrawLine (display.Handle, ClientWindow, gc, rect.X, rect.Y, rect.X, rect.Bottom); + + Xlib.XFreeGC (display.Handle, gc); + } + + private void WaitForMessage (Msg message) + { + MSG msg = new MSG (); + + queue.DispatchIdle = false; + + bool done = false; + do { + if (display.PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) { + if ((Msg)msg.message == Msg.WM_QUIT) { + // XXX this should live someplace else + XplatUI.PostQuitMessage (0); + done = true; + } + else { + if ((msg.hwnd == Handle) && + ((Msg)msg.message == message || (Msg)msg.message == Msg.WM_DESTROY)) + done = true; + display.TranslateMessage (ref msg); + display.DispatchMessage (ref msg); + } + } + } while (!done); + + queue.DispatchIdle = true; + } + + public void Map () + { + // FIXME why do we set this here and also in the MapNotify event handling? + if (!mapped) { + + Xlib.XMapWindow (display.Handle, WholeWindow); + Xlib.XMapWindow (display.Handle, ClientWindow); + + mapped = true; + + if (Widget.FromHandle(Handle) is Form) + WaitForMessage (Msg.WM_SHOWWINDOW); + } + } + + public void Unmap () + { + // FIXME why do we set this here and also in the UnmapNotify event handling? + if (mapped) { + Xlib.XUnmapWindow (display.Handle, ClientWindow); + Xlib.XUnmapWindow (display.Handle, WholeWindow); + + mapped = false; + + if (Widget.FromHandle(Handle) is Form) + WaitForMessage (Msg.WM_SHOWWINDOW); + } + } + + public void PerformNCCalc () + { + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + IntPtr ptr; + Rectangle rect; + + rect = DefaultClientRect; + + ncp = new XplatUIWin32.NCCALCSIZE_PARAMS (); + ptr = Marshal.AllocHGlobal (Marshal.SizeOf(ncp)); + + ncp.rgrc1.left = rect.Left; + ncp.rgrc1.top = rect.Top; + ncp.rgrc1.right = rect.Right; + ncp.rgrc1.bottom = rect.Bottom; + + Marshal.StructureToPtr (ncp, ptr, true); + NativeWindow.WndProc (ClientWindow, Msg.WM_NCCALCSIZE, (IntPtr)1, ptr); + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (ptr, typeof(XplatUIWin32.NCCALCSIZE_PARAMS)); + Marshal.FreeHGlobal(ptr); + + // FIXME - debug this with Menus + + rect = new Rectangle(ncp.rgrc1.left, ncp.rgrc1.top, ncp.rgrc1.right - ncp.rgrc1.left, ncp.rgrc1.bottom - ncp.rgrc1.top); + ClientRect = rect; + + if (Visible) { + if ((rect.Width < 1) || (rect.Height < 1)) + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, -5, -5, 1, 1); + else + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, rect.X, rect.Y, rect.Width, rect.Height); + } + + InvalidateNC (); + } + + public void RequestNCRecalc () + { + PerformNCCalc (); + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + InvalidateNC (); + } + + [MonoTODO] + public void RequestAdditionalWM_NCMessages (bool hover, bool leave) + { + // Missing messages won't crash anything so just don't generate them for the moment. + // throw new NotImplementedException( ); + } + + public void FrameExtents (out int left, out int top) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + display.Atoms._NET_FRAME_EXTENTS, IntPtr.Zero, new IntPtr (16), false, + display.Atoms.XA_CARDINAL, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if (((long)nitems == 4) && (prop != IntPtr.Zero)) { + left = Marshal.ReadIntPtr(prop, 0).ToInt32(); + //right = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32(); + top = Marshal.ReadIntPtr(prop, IntPtr.Size * 2).ToInt32(); + //bottom = Marshal.ReadIntPtr(prop, IntPtr.Size * 3).ToInt32(); + } else { + left = 0; + top = 0; + } + + if (prop != IntPtr.Zero) { + Xlib.XFree(prop); + } + } + + static bool StyleSet (int s, WindowStyles ws) + { + return (s & (int)ws) == (int)ws; + } + + static bool ExStyleSet (int ex, WindowExStyles exws) + { + return (ex & (int)exws) == (int)exws; + } + + // XXX this should be a static method on Hwnd so other backends can use it + public static void DeriveStyles(int Style, int ExStyle, out FormBorderStyle border_style, out bool border_static, + out TitleStyle title_style, out int caption_height, out int tool_caption_height) + { + + // Only MDI windows get caption_heights + caption_height = 0; + tool_caption_height = 19; + border_static = false; + + if (StyleSet (Style, WindowStyles.WS_CHILD)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) { + border_style = FormBorderStyle.Fixed3D; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (!StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.None; + } else { + border_style = FormBorderStyle.FixedSingle; + } + title_style = TitleStyle.None; + + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_MDICHILD)) { + caption_height = 26; + + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + title_style = TitleStyle.Tool; + else + title_style = TitleStyle.Normal; + } + + if (StyleSet (Style, WindowStyles.WS_OVERLAPPEDWINDOW) || + ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + border_style = (FormBorderStyle) 0xFFFF; + else + border_style = FormBorderStyle.None; + } + } + else { + title_style = TitleStyle.None; + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + title_style = TitleStyle.Tool; + else + title_style = TitleStyle.Normal; + } + + border_style = FormBorderStyle.None; + + if (StyleSet (Style, WindowStyles.WS_THICKFRAME)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + border_style = FormBorderStyle.SizableToolWindow; + else + border_style = FormBorderStyle.Sizable; + } else { + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) + border_style = FormBorderStyle.Fixed3D; + else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_DLGMODALFRAME)) + border_style = FormBorderStyle.FixedDialog; + else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + border_style = FormBorderStyle.FixedToolWindow; + else if (StyleSet (Style, WindowStyles.WS_BORDER)) + border_style = FormBorderStyle.FixedSingle; + } else if (StyleSet (Style, WindowStyles.WS_BORDER)) + border_style = FormBorderStyle.FixedSingle; + } + } + } + + public void SetHwndStyles (CreateParams cp) + { + DeriveStyles(cp.Style, cp.ExStyle, out this.border_style, out this.border_static, out this.title_style, out this.caption_height, out this.tool_caption_height); + } + + public void SetWMStyles (CreateParams cp) + { + MotifWmHints mwmHints; + MotifFunctions functions; + MotifDecorations decorations; + IntPtr[] atoms; + int atom_count; + Rectangle client_rect; + + // Child windows don't need WM window styles + if (StyleSet (cp.Style, WindowStyles.WS_CHILDWINDOW)) + return; + + atoms = new IntPtr[8]; + mwmHints = new MotifWmHints(); + functions = 0; + decorations = 0; + + mwmHints.flags = (IntPtr)(MotifFlags.Functions | MotifFlags.Decorations); + mwmHints.functions = (IntPtr)0; + mwmHints.decorations = (IntPtr)0; + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW) + || !StyleSet (cp.Style, WindowStyles.WS_CAPTION | WindowStyles.WS_BORDER | WindowStyles.WS_DLGFRAME)) { + /* tool windows get no window manager + decorations, and neither do windows + which lack CAPTION/BORDER/DLGFRAME + styles. + */ + + /* just because the window doesn't get any decorations doesn't + mean we should disable the functions. for instance, without + MotifFunctions.Maximize, changing the windowstate to Maximized + is ignored by metacity. */ + functions |= MotifFunctions.Move | MotifFunctions.Resize | MotifFunctions.Minimize | MotifFunctions.Maximize; + } + else { + if (StyleSet (cp.Style, WindowStyles.WS_CAPTION)) { + functions |= MotifFunctions.Move; + decorations |= MotifDecorations.Title | MotifDecorations.Menu; + } + + if (StyleSet (cp.Style, WindowStyles.WS_THICKFRAME)) { + functions |= MotifFunctions.Move | MotifFunctions.Resize; + decorations |= MotifDecorations.Border | MotifDecorations.ResizeH; + } + + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZEBOX)) { + functions |= MotifFunctions.Minimize; + decorations |= MotifDecorations.Minimize; + } + + if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZEBOX)) { + functions |= MotifFunctions.Maximize; + decorations |= MotifDecorations.Maximize; + } + + if (StyleSet (cp.Style, WindowStyles.WS_SIZEBOX)) { + functions |= MotifFunctions.Resize; + decorations |= MotifDecorations.ResizeH; + } + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_DLGMODALFRAME)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_BORDER)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_DLGFRAME)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_SYSMENU)) { + functions |= MotifFunctions.Close; + } + else { + functions &= ~(MotifFunctions.Maximize | MotifFunctions.Minimize | MotifFunctions.Close); + decorations &= ~(MotifDecorations.Menu | MotifDecorations.Maximize | MotifDecorations.Minimize); + if (cp.Caption == "") { + functions &= ~MotifFunctions.Move; + decorations &= ~(MotifDecorations.Title | MotifDecorations.ResizeH); + } + } + } + + if ((functions & MotifFunctions.Resize) == 0) { + fixed_size = true; + SetMinMax (new Rectangle(cp.X, cp.Y, cp.Width, cp.Height), new Size(cp.Width, cp.Height), new Size(cp.Width, cp.Height)); + } else { + fixed_size = false; + } + + mwmHints.functions = (IntPtr)functions; + mwmHints.decorations = (IntPtr)decorations; + + FormWindowState current_state = GetWindowState (); + if (current_state == (FormWindowState)(-1)) + current_state = FormWindowState.Normal; + + client_rect = ClientRect; + + atom_count = 0; + + // needed! map toolwindows to _NET_WM_WINDOW_TYPE_UTILITY to make newer metacity versions happy + // and get those windows in front of their parents + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_UTILITY; + + Form f = Widget.FromHandle(Handle) as Form; + if (f != null && !reparented) { + if (f.Owner != null && f.Owner.Handle != IntPtr.Zero) { + Hwnd owner_hwnd = Hwnd.ObjectFromHandle(f.Owner.Handle); + if (owner_hwnd != null) + Xlib.XSetTransientForHint (display.Handle, WholeWindow, + owner_hwnd.WholeWindow); + } + } + } + + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._MOTIF_WM_HINTS, display.Atoms._MOTIF_WM_HINTS, 32, + PropertyMode.Replace, ref mwmHints, 5); + + if (StyleSet (cp.Style, WindowStyles.WS_POPUP) && (parent != null) && (parent.WholeWindow != IntPtr.Zero)) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_NORMAL; + Xlib.XSetTransientForHint(display.Handle, WholeWindow, parent.WholeWindow); + } else if (!ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_APPWINDOW)) { + /* this line keeps the window from showing up in gnome's taskbar */ + atoms[atom_count++] = display.Atoms._NET_WM_STATE_SKIP_TASKBAR; + } + if ((client_rect.Width < 1) || (client_rect.Height < 1)) { + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, -5, -5, 1, 1); + } else { + Xlib.XMoveResizeWindow (display.Handle, ClientWindow, client_rect.X, client_rect.Y, client_rect.Width, client_rect.Height); + } + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) + atoms[atom_count++] = display.Atoms._NET_WM_STATE_SKIP_TASKBAR; + + /* we need to add these atoms in the + * event we're maximized, since we're + * replacing the existing + * _NET_WM_STATE here. If we don't + * add them, future calls to + * GetWindowState will return Normal + * for a window which is maximized. */ + if (current_state == FormWindowState.Maximized) { + atoms[atom_count++] = display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ; + atoms[atom_count++] = display.Atoms._NET_WM_STATE_MAXIMIZED_VERT; + } + + Set_WM_STATE (atoms, atom_count); + + atom_count = 0; + atoms[atom_count++] = display.Atoms.WM_DELETE_WINDOW; + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_CONTEXTHELP)) + atoms[atom_count++] = display.Atoms._NET_WM_CONTEXT_HELP; + + Xlib.XSetWMProtocols (display.Handle, WholeWindow, atoms, atom_count); + } + + public void ClientToScreen (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + ClientWindow, display.RootWindow.Handle, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + public void ScreenToClient (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + display.RootWindow.Handle, ClientWindow, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + + public void ScreenToMenu (ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + + Xlib.XTranslateCoordinates (display.Handle, + display.RootWindow.Handle, WholeWindow, + x, y, out dest_x_return, out dest_y_return, out child); + + x = dest_x_return; + y = dest_y_return; + } + + public void ScrollWindow (Rectangle area, int XAmount, int YAmount, bool with_children) + { + IntPtr gc; + XGCValues gc_values; + + Rectangle r = Rectangle.Intersect (Invalid, area); + if (!r.IsEmpty) { + /* We have an invalid area in the window we're scrolling. + Adjust our stored invalid rectangle to to match the scrolled amount */ + + r.X += XAmount; + r.Y += YAmount; + + if (r.X < 0) { + r.Width += r.X; + r.X =0; + } + + if (r.Y < 0) { + r.Height += r.Y; + r.Y =0; + } + + if (area.Contains (Invalid)) + ClearInvalidArea(); + AddInvalidArea(r); + } + + gc_values = new XGCValues(); + + gc_values.graphics_exposures = false; + if (with_children) + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + + gc = Xlib.XCreateGC (display.Handle, ClientWindow, IntPtr.Zero, ref gc_values); + + int src_x, src_y; + int dest_x, dest_y; + int width, height; + + if (YAmount > 0) { + src_y = area.Y; + height = area.Height - YAmount; + dest_y = area.Y + YAmount; + } + else { + src_y = area.Y - YAmount; + height = area.Height + YAmount; + dest_y = area.Y; + } + + if (XAmount > 0) { + src_x = area.X; + width = area.Width - XAmount; + dest_x = area.X + XAmount; + } + else { + src_x = area.X - XAmount; + width = area.Width + XAmount; + dest_x = area.X; + } + + Xlib.XCopyArea (display.Handle, ClientWindow, ClientWindow, gc, src_x, src_y, width, height, dest_x, dest_y); + + // Generate an expose for the area exposed by the horizontal scroll + // We don't use AddExpose since we're + if (XAmount > 0) { + AddExpose (true, area.X, area.Y, XAmount, area.Height); + } else if (XAmount < 0) { + AddExpose (true, XAmount + area.X + area.Width, area.Y, -XAmount, area.Height); + } + + // Generate an expose for the area exposed by the vertical scroll + if (YAmount > 0) { + AddExpose (true, area.X, area.Y, area.Width, YAmount); + } else if (YAmount < 0) { + AddExpose (true, area.X, YAmount + area.Y + area.Height, area.Width, -YAmount); + } + + Xlib.XFreeGC (display.Handle, gc); + } + + + public void SetBorderStyle (FormBorderStyle border_style) + { + Form form = Widget.FromHandle (Handle) as Form; + if (form != null && form.window_manager == null && (border_style == FormBorderStyle.FixedToolWindow || + border_style == FormBorderStyle.SizableToolWindow)) { + form.window_manager = new ToolWindowManager (form); + } + + BorderStyle = border_style; + RequestNCRecalc (); + } + + // XXX this should probably be in Hwnd + public void SetClipRegion (Region region) + { + UserClip = region; + Invalidate (new Rectangle(0, 0, Width, Height), false); + } + + // XXX this should probably be in Hwnd + public Region GetClipRegion () + { + return UserClip; + } + + public void SetMenu (Menu menu) + { + Menu = menu; + + RequestNCRecalc (); + } + + public void SetMinMax (Rectangle maximized, Size min, Size max) + { + XSizeHints hints; + IntPtr dummy; + + hints = new XSizeHints(); + + Xlib.XGetWMNormalHints (display.Handle, WholeWindow, ref hints, out dummy); + if ((min != Size.Empty) && (min.Width > 0) && (min.Height > 0)) { + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMinSize); + hints.min_width = min.Width; + hints.min_height = min.Height; + } + + if ((max != Size.Empty) && (max.Width > 0) && (max.Height > 0)) { + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMaxSize); + hints.max_width = max.Width; + hints.max_height = max.Height; + } + + if (hints.flags != IntPtr.Zero) + Xlib.XSetWMNormalHints (display.Handle, WholeWindow, ref hints); + + if ((maximized != Rectangle.Empty) && (maximized.Width > 0) && (maximized.Height > 0)) { + hints.flags = (IntPtr)XSizeHintsFlags.PPosition; + hints.x = maximized.X; + hints.y = maximized.Y; + hints.width = maximized.Width; + hints.height = maximized.Height; + + // Metacity does not seem to follow this constraint for maximized (zoomed) windows + Xlib.XSetZoomHints (display.Handle, WholeWindow, ref hints); + } + } + + // For WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN, WM_XBUTTONDOWN + // WM_CREATE and WM_DESTROY causes + public void SendParentNotify (Msg cause, int x, int y) + { + if (Handle == IntPtr.Zero) + return; + + if (ExStyleSet ((int) initial_ex_style, WindowExStyles.WS_EX_NOPARENTNOTIFY)) + return; + + if (Parent == null || Parent.Handle == IntPtr.Zero) + return; + + if (cause == Msg.WM_CREATE || cause == Msg.WM_DESTROY) { + display.SendMessage(Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), Handle); + } else { + display.SendMessage(Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), Widget.MakeParam(x, y)); + } + + ((X11Hwnd)Parent).SendParentNotify (cause, x, y); + } + + + public void GetPosition (bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) + { + x = X; + y = Y; + width = Width; + height = Height; + + PerformNCCalc (); + + client_width = ClientRect.Width; + client_height = ClientRect.Height; + } + + public void SetPosition (int x, int y, int width, int height) + { + // Win32 automatically changes negative width/height to 0. + if (width < 0) + width = 0; + if (height < 0) + height = 0; + + // X requires a sanity check for width & height; otherwise it dies + if (zero_sized && width > 0 && height > 0) { + if (Visible) { + Map (); + } + zero_sized = false; + } + + if ((width < 1) || (height < 1)) { + zero_sized = true; + Unmap (); + } + + // Save a server roundtrip (and prevent a feedback loop) + if ((X == x) && (Y == y) && + (Width == width) && (Height == height)) { + return; + } + + if (!zero_sized) { + //Hack? + X = x; + Y = y; + Width = width; + Height = height; + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + + if (fixed_size) { + SetMinMax (Rectangle.Empty, new Size(width, height), new Size(width, height)); + } + + Xlib.XMoveResizeWindow (display.Handle, WholeWindow, x, y, width, height); + PerformNCCalc (); + } + + // Update our position/size immediately, so + // that future calls to SetWindowPos aren't + // kept from calling XMoveResizeWindow (by the + // "Save a server roundtrip" block above). + X = x; + Y = y; + Width = width; + Height = height; + ClientRect = Rectangle.Empty; + } + + public void SetParent (X11Hwnd parent_hwnd) + { + Parent = parent_hwnd; + +#if DriverDebug || DriverDebugParent + Console.WriteLine("Parent for window {0} = {1}", XplatUI.Window(Handle), XplatUI.Window(hwnd.parent != null ? parent_hwnd.Handle : IntPtr.Zero)); +#endif + Xlib.XReparentWindow (display.Handle, WholeWindow, + parent_hwnd == null ? display.FosterParent.ClientWindow : parent_hwnd.ClientWindow, + X, Y); + } + + public void SetCursorPos (int x, int y) + { + Xlib.XWarpPointer (display.Handle, IntPtr.Zero, ClientWindow, 0, 0, 0, 0, x, y); + } + + public bool SetTopmost (bool enabled) + { + if (enabled) { + int[] atoms = new int[8]; + atoms[0] = display.Atoms._NET_WM_STATE_ABOVE.ToInt32(); + Xlib.XChangeProperty (display.Handle, WholeWindow, display.Atoms._NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1); + } + else { + Xlib.XDeleteProperty (display.Handle, WholeWindow, display.Atoms._NET_WM_STATE); + } + + return true; + } + + public bool SetOwner (X11Hwnd owner) + { + if (owner != null) { + WINDOW_TYPE = display.Atoms._NET_WM_WINDOW_TYPE_NORMAL; + if (owner != null) + Xlib.XSetTransientForHint (display.Handle, WholeWindow, owner.WholeWindow); + else + Xlib.XSetTransientForHint (display.Handle, WholeWindow, display.RootWindow.WholeWindow); + } + else { + Xlib.XDeleteProperty (display.Handle, WholeWindow, display.Atoms.XA_WM_TRANSIENT_FOR); + } + + return true; + } + + public bool SetVisible (bool visible, bool activate) + { + Visible = visible; + + if (visible) { + Map (); + + if (Widget.FromHandle (Handle) is Form) { + FormWindowState s; + + s = ((Form)Widget.FromHandle(Handle)).WindowState; + + switch(s) { + case FormWindowState.Minimized: SetWindowState (FormWindowState.Minimized); break; + case FormWindowState.Maximized: SetWindowState (FormWindowState.Maximized); break; + } + + } + + display.SendMessage (Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + } + else { + Unmap (); + } + + return true; + } + + public FormWindowState GetWindowState () + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr atom; + int maximized; + bool minimized; + XWindowAttributes attributes; + + maximized = 0; + minimized = false; + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_STATE, IntPtr.Zero, new IntPtr (256), false, + display.Atoms.XA_ATOM, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (((long)nitems > 0) && (prop != IntPtr.Zero)) { + for (int i = 0; i < (long)nitems; i++) { + // XXX 64 bit clean? + atom = (IntPtr)Marshal.ReadInt32(prop, i * 4); + if ((atom == display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ) || (atom == display.Atoms._NET_WM_STATE_MAXIMIZED_VERT)) + maximized++; + else if (atom == display.Atoms._NET_WM_STATE_HIDDEN) + minimized = true; + } + Xlib.XFree(prop); + } + + if (minimized) + return FormWindowState.Minimized; + else if (maximized == 2) + return FormWindowState.Maximized; + + attributes = new XWindowAttributes(); + Xlib.XGetWindowAttributes (display.Handle, ClientWindow, ref attributes); + if (attributes.map_state == MapState.IsUnmapped) + return (FormWindowState)(-1); + + return FormWindowState.Normal; + } + + + public void SetWindowState (FormWindowState state) + { + FormWindowState current_state; + + current_state = GetWindowState (); + + if (current_state == state) + return; + + switch (state) { + case FormWindowState.Normal: + if (current_state == FormWindowState.Minimized) + Map (); + else if (current_state == FormWindowState.Maximized) + display.SendNetWMMessage (WholeWindow, + display.Atoms._NET_WM_STATE, (IntPtr)2 /* toggle */, + display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ, + display.Atoms._NET_WM_STATE_MAXIMIZED_VERT); + Activate (); + break; + + case FormWindowState.Minimized: + if (current_state == FormWindowState.Maximized) + display.SendNetWMMessage (WholeWindow, + display.Atoms._NET_WM_STATE, (IntPtr)2 /* toggle */, + display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ, + display.Atoms._NET_WM_STATE_MAXIMIZED_VERT); + + // FIXME multiscreen support + Xlib.XIconifyWindow (display.Handle, WholeWindow, display.DefaultScreen); + break; + + case FormWindowState.Maximized: + if (current_state == FormWindowState.Minimized) + Map (); + + display.SendNetWMMessage (WholeWindow, + display.Atoms._NET_WM_STATE, (IntPtr)1 /* Add */, + display.Atoms._NET_WM_STATE_MAXIMIZED_HORZ, + display.Atoms._NET_WM_STATE_MAXIMIZED_VERT); + Activate (); + break; + } + } + + public bool SetZOrder (X11Hwnd after_hwnd, bool top, bool bottom) + { + if (top) { + Xlib.XRaiseWindow (display.Handle, WholeWindow); + return true; + } + else if (bottom) { + Xlib.XLowerWindow (display.Handle, WholeWindow); + return true; + } + else { + if (after_hwnd == null) { + Update_USER_TIME (); + Xlib.XRaiseWindow (display.Handle, WholeWindow); + display.SendNetWMMessage (WholeWindow, display.Atoms._NET_ACTIVE_WINDOW, (IntPtr)1, IntPtr.Zero, IntPtr.Zero); + return true; + } + + XWindowChanges values = new XWindowChanges(); + values.sibling = after_hwnd.WholeWindow; + values.stack_mode = StackMode.Below; + + Xlib.XConfigureWindow (display.Handle, WholeWindow, ChangeWindowFlags.CWStackMode | ChangeWindowFlags.CWSibling, ref values); + } + return false; + } + + public X11Display Display { + get { return display; } + } + + public string Text { + get { return text; } + set { + if (value == null) + value = ""; + + if (value == text) + return; + + text = value; + + Xlib.XChangeProperty(display.Handle, WholeWindow, + display.Atoms._NET_WM_NAME, display.Atoms.UNICODETEXT, 8, + PropertyMode.Replace, text, Encoding.UTF8.GetByteCount (text)); + + // XXX this has problems with UTF8. + // we need to either use the actual + // text if it's latin-1, or convert it + // to compound text if it's in a + // different charset. + Xlib.XStoreName(display.Handle, WholeWindow, text); + } + } + + public bool GetText (out string text) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_NAME, IntPtr.Zero, new IntPtr (1), false, + display.Atoms.UNICODETEXT, out actual_atom, out actual_format, + out nitems, out bytes_after, ref prop); + + if ((long)nitems > 0 && prop != IntPtr.Zero) { + text = Marshal.PtrToStringUni (prop, (int)nitems); + Xlib.XFree (prop); + return true; + } + else { + // fallback on the non-_NET property + IntPtr textptr; + + textptr = IntPtr.Zero; + + Xlib.XFetchName (display.Handle, WholeWindow, ref textptr); + if (textptr != IntPtr.Zero) { + text = Marshal.PtrToStringAnsi(textptr); + Xlib.XFree(textptr); + return true; + } else { + text = ""; + return false; + } + } + } + + public IntPtr WINDOW_TYPE { + get { + if (refetch_window_type) { + window_type = GetAtomListProperty (display.Atoms._NET_WM_WINDOW_TYPE); + refetch_window_type = false; + } + + return window_type.Length > 0 ? window_type[0] : IntPtr.Zero; + } + set { + Set_WINDOW_TYPE (new IntPtr[] {value}, 1); + } + } + + public void Set_WINDOW_TYPE (IntPtr[] value, int count) + { + if (refetch_window_type) { + window_type = GetAtomListProperty (display.Atoms._NET_WM_WINDOW_TYPE); + refetch_window_type = false; + } + + if (ArrayDifferent (window_type, value)) { + window_type = value; + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_WINDOW_TYPE, display.Atoms.XA_ATOM, 32, + PropertyMode.Replace, window_type, window_type.Length); + } + } + + public void Set_WM_STATE (IntPtr[] value, int count) + { + if (ArrayDifferent (wm_state, value)) { + wm_state = value; + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_STATE, display.Atoms.XA_ATOM, 32, + PropertyMode.Replace, wm_state, wm_state.Length); + } + } + + public void Update_USER_TIME () + { + int[] args; + + args = new int[2]; + args[0] = display.CurrentTimestamp; + Xlib.XChangeProperty (display.Handle, WholeWindow, + display.Atoms._NET_WM_USER_TIME, display.Atoms.XA_CARDINAL, 32, + PropertyMode.Replace, args, 1); + } + + public IntPtr[] GetAtomListProperty (IntPtr atom) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Xlib.XGetWindowProperty (display.Handle, WholeWindow, + atom, IntPtr.Zero, new IntPtr (Int32.MaxValue), false, + display.Atoms.XA_ATOM, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (actual_atom != display.Atoms.XA_ATOM || + (long)nitems == 0 || + prop == IntPtr.Zero) { + return new IntPtr[0]; + } + + IntPtr[] values = new IntPtr[(long)nitems]; + int ofs = 0; + + for (int i = 0; i < values.Length; i ++) { + values[i] = Marshal.ReadIntPtr (prop, ofs); ofs += IntPtr.Size; + } + + Xlib.XFree (prop); + + return values; + } + + bool ArrayDifferent (IntPtr[] a, IntPtr[] b) + { + if (a.Length != b.Length) + return true; + + for (int i = 0; i < a.Length; i ++) { + if (a[i] != b[i]) + return true; + } + + return false; + } + } +} diff --git a/source/ShiftUI/Internal/X11Keyboard.cs b/source/ShiftUI/Internal/X11Keyboard.cs new file mode 100644 index 0000000..70a6c03 --- /dev/null +++ b/source/ShiftUI/Internal/X11Keyboard.cs @@ -0,0 +1,1478 @@ +// 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 Novell, Inc. +// +// Authors: +// Jackson Harper ([email protected]) +// +// + + +// +// TODO: +// - dead chars are not translated properly +// - There is a lot of potential for optimmization in here +// +using System; +using System.Collections; +using System.Diagnostics; +using System.Drawing; +using System.Text; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace ShiftUI { + + public enum XLookupStatus + { + XBufferOverflow = -1, + XLookupNone = 1, + XLookupChars = 2, + XLookupKeySym = 3, + XLookupBoth = 4 + } + + internal class X11Keyboard : IDisposable { + internal static object XlibLock; + + private IntPtr display; + private IntPtr client_window; + private IntPtr xim; + private Hashtable xic_table = new Hashtable (); + private XIMPositionContext positionContext; + private XIMCallbackContext callbackContext; + private XIMProperties ximStyle; + private EventMask xic_event_mask = EventMask.NoEventMask; + private StringBuilder lookup_buffer; + private byte [] lookup_byte_buffer = new byte [100]; + private int min_keycode, max_keycode, keysyms_per_keycode, syms; + private int [] keyc2vkey = new int [256]; + private int [] keyc2scan = new int [256]; + private byte [] key_state_table = new byte [256]; + private int lcid; + private bool num_state, cap_state; + private bool initialized; + private bool menu_state = false; + private Encoding encoding; + + private int NumLockMask; + private int AltGrMask; + + public X11Keyboard (IntPtr display, IntPtr clientWindow) + { + this.display = display; + lookup_buffer = new StringBuilder (24); + EnsureLayoutInitialized (); + } + + private Encoding AnsiEncoding + { + get + { + if (encoding == null) + encoding = Encoding.GetEncoding(new CultureInfo(lcid).TextInfo.ANSICodePage); + return encoding; + } + } + + public IntPtr ClientWindow { + get { return client_window; } + } + + void IDisposable.Dispose () + { + if (xim != IntPtr.Zero) { + foreach (IntPtr xic in xic_table.Values) + XDestroyIC (xic); + xic_table.Clear (); + + XCloseIM (xim); + xim = IntPtr.Zero; + } + } + + public void DestroyICForWindow (IntPtr window) + { + IntPtr xic = GetXic (window); + if (xic != IntPtr.Zero) { + xic_table.Remove ((long) window); + XDestroyIC (xic); + } + } + + public void EnsureLayoutInitialized () + { + if (initialized) + return; + + KeyboardLayouts layouts = new KeyboardLayouts (); + KeyboardLayout layout = DetectLayout (layouts); + lcid = layout.Lcid; + CreateConversionArray (layouts, layout); + SetupXIM (); + initialized = true; + } + + private void SetupXIM () + { + xim = IntPtr.Zero; + + if (!XSupportsLocale ()) { + Console.Error.WriteLine ("X does not support your locale"); + return; + } + + if (!XSetLocaleModifiers (String.Empty)) { + Console.Error.WriteLine ("Could not set X locale modifiers"); + return; + } + + if (Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE) == "disabled") { + return; + } + + xim = XOpenIM (display, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + if (xim == IntPtr.Zero) + Console.Error.WriteLine ("Could not get XIM"); + + initialized = true; + } + + void CreateXicForWindow (IntPtr window) + { + IntPtr xic = CreateXic (window, xim); + xic_table [(long) window] = xic; + if (xic == IntPtr.Zero) + Console.Error.WriteLine ("Could not get XIC"); + else { + if (XGetICValues (xic, "filterEvents", out xic_event_mask, IntPtr.Zero) != null) + Console.Error.WriteLine ("Could not get XIC values"); + EventMask mask = EventMask.ExposureMask | EventMask.KeyPressMask | EventMask.FocusChangeMask; + if ((xic_event_mask | mask) == xic_event_mask) { + xic_event_mask |= mask; + lock (XlibLock) { + XplatUIX11.XSelectInput(display, window, new IntPtr ((int) xic_event_mask)); + } + } + } + } + + public EventMask KeyEventMask { + get { return xic_event_mask; } + } + + public Keys ModifierKeys { + get { + Keys keys = Keys.None; + if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) + keys |= Keys.Shift; + if ((key_state_table [(int) VirtualKeys.VK_CONTROL ] & 0x80) != 0) + keys |= Keys.Widget; + if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) + keys |= Keys.Alt; + return keys; + } + } + + private IntPtr GetXic (IntPtr window) + { + if (xim != IntPtr.Zero && xic_table.ContainsKey ((long) window)) + return (IntPtr) xic_table [(long) window]; + else + return IntPtr.Zero; + } + + private bool FilterKey (XEvent e, int vkey) + { + if (XplatUI.key_filters.Count == 0) + return false; + XLookupStatus status; + XKeySym ks; + KeyFilterData data; + data.Down = (e.type == XEventName.KeyPress); + data.ModifierKeys = ModifierKeys; + LookupString (ref e, 0, out ks, out status); + data.keysym = (int)ks; + data.keycode = e.KeyEvent.keycode; + data.str = lookup_buffer.ToString (0, lookup_buffer.Length); + return XplatUI.FilterKey (data); + } + + public void FocusIn (IntPtr window) + { + this.client_window = window; + if (xim == IntPtr.Zero) + return; + + if (!xic_table.ContainsKey ((long) window)) + CreateXicForWindow (window); + IntPtr xic = GetXic (window); + if (xic != IntPtr.Zero) + XSetICFocus (xic); + } + + private bool have_Xutf8ResetIC = true; + + public void FocusOut (IntPtr window) + { + this.client_window = IntPtr.Zero; + if (xim == IntPtr.Zero) + return; + + IntPtr xic = GetXic (window); + if (xic != IntPtr.Zero) { + if (have_Xutf8ResetIC) { + try { + Xutf8ResetIC (xic); + } catch (EntryPointNotFoundException) { + have_Xutf8ResetIC = false; + } + } + XUnsetICFocus (xic); + } + } + + public bool ResetKeyState(IntPtr hwnd, ref MSG msg) { + // FIXME - keep defining events/msg and return true until we've 'restored' all + // pending keypresses + if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) { + key_state_table [(int) VirtualKeys.VK_SHIFT] &= unchecked((byte)~0x80); + } + + if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) { + key_state_table [(int) VirtualKeys.VK_CONTROL] &= unchecked((byte)~0x80); + } + + if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) { + key_state_table [(int) VirtualKeys.VK_MENU] &= unchecked((byte)~0x80); + } + return false; + } + + // Almost identical to UpdateKeyState() but does not call LookupString(). + // The actual purpose is to handle shift state correctly. + public void PreFilter (XEvent xevent) + { + // It is still possible that some keyboards could have some shift + // keys outside this range, but let's think about such cases only + // if it actually happened. + if (xevent.KeyEvent.keycode >= keyc2vkey.Length) + return; + + int vkey = keyc2vkey [xevent.KeyEvent.keycode]; + + switch (xevent.type) { + case XEventName.KeyRelease: + key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80); + break; + case XEventName.KeyPress: + if ((key_state_table [vkey & 0xff] & 0x80) == 0) { + key_state_table [vkey & 0xff] ^= 0x01; + } + key_state_table [vkey & 0xff] |= 0x80; + break; + } + } + + public void KeyEvent (IntPtr hwnd, XEvent xevent, ref MSG msg) + { + XKeySym keysym; + int ascii_chars; + + XLookupStatus status; + ascii_chars = LookupString (ref xevent, 24, out keysym, out status); + + if (((int) keysym >= (int) MiscKeys.XK_ISO_Lock && + (int) keysym <= (int) MiscKeys.XK_ISO_Last_Group_Lock) || + (int) keysym == (int) MiscKeys.XK_Mode_switch) { + UpdateKeyState (xevent); + return; + } + + if ((xevent.KeyEvent.keycode >> 8) == 0x10) + xevent.KeyEvent.keycode = xevent.KeyEvent.keycode & 0xFF; + + int event_time = (int)xevent.KeyEvent.time; + + if (status == XLookupStatus.XLookupChars) { + // do not ignore those inputs. They are mostly from XIM. + msg = SendImeComposition (lookup_buffer.ToString (0, lookup_buffer.Length)); + msg.hwnd = hwnd; + return; + } + + AltGrMask = xevent.KeyEvent.state & (0x6000 | (int) KeyMasks.ModMasks); + int vkey = EventToVkey (xevent); + if (vkey == 0 && ascii_chars != 0) { + vkey = (int) VirtualKeys.VK_NONAME; + } + + if (FilterKey (xevent, vkey)) + return; + switch ((VirtualKeys) (vkey & 0xFF)) { + case VirtualKeys.VK_NUMLOCK: + GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, xevent.type, event_time); + break; + case VirtualKeys.VK_CAPITAL: + GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, xevent.type, event_time); + break; + default: + if (((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) == 0) != ((xevent.KeyEvent.state & NumLockMask) == 0)) { + GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time); + GenerateMessage (VirtualKeys.VK_NUMLOCK, 0x45, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time); + } + + if (((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) == 0) != ((xevent.KeyEvent.state & (int) KeyMasks.LockMask) == 0)) { + GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyPress, event_time); + GenerateMessage (VirtualKeys.VK_CAPITAL, 0x3A, xevent.KeyEvent.keycode, XEventName.KeyRelease, event_time); + } + + num_state = false; + cap_state = false; + + int bscan = (keyc2scan [xevent.KeyEvent.keycode] & 0xFF); + KeybdEventFlags dw_flags = KeybdEventFlags.None; + if (xevent.type == XEventName.KeyRelease) + dw_flags |= KeybdEventFlags.KeyUp; + if ((vkey & 0x100) != 0) + dw_flags |= KeybdEventFlags.ExtendedKey; + msg = SendKeyboardInput ((VirtualKeys) (vkey & 0xFF), bscan, xevent.KeyEvent.keycode, dw_flags, event_time); + msg.hwnd = hwnd; + break; + } + } + + public bool TranslateMessage (ref MSG msg) + { + bool res = false; + + if (msg.message >= Msg.WM_KEYFIRST && msg.message <= Msg.WM_KEYLAST) + res = true; + + if (msg.message == Msg.WM_SYSKEYUP && msg.wParam == (IntPtr) 0x12 && menu_state) { + msg.message = Msg.WM_KEYUP; + menu_state = false; + } + + if (msg.message != Msg.WM_KEYDOWN && msg.message != Msg.WM_SYSKEYDOWN) + return res; + + if ((key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 && msg.wParam != (IntPtr) 0x12) + menu_state = true; + + EnsureLayoutInitialized (); + + string buffer; + Msg message; + int tu = ToUnicode ((int) msg.wParam, Widget.HighOrder ((int) msg.lParam), out buffer); + switch (tu) { + case 1: + message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_CHAR : Msg.WM_SYSCHAR); + XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam); + break; + case -1: + message = (msg.message == Msg.WM_KEYDOWN ? Msg.WM_DEADCHAR : Msg.WM_SYSDEADCHAR); + XplatUI.PostMessage (msg.hwnd, message, (IntPtr) buffer [0], msg.lParam); + return true; + } + + return res; + } + + public int ToKeycode(int key) + { + int keycode = 0; + + if (nonchar_vkey_key[key] > 0) + keycode = XKeysymToKeycode (display, nonchar_vkey_key[key]); + + if (keycode == 0) + keycode = XKeysymToKeycode (display, key); + + return keycode; + } + + public int ToUnicode (int vkey, int scan, out string buffer) + { + if ((scan & 0x8000) != 0) { + buffer = String.Empty; + return 0; + } + + XEvent e = new XEvent (); + e.AnyEvent.type = XEventName.KeyPress; + e.KeyEvent.display = display; + e.KeyEvent.keycode = 0; + e.KeyEvent.state = 0; + + if ((key_state_table [(int) VirtualKeys.VK_SHIFT] & 0x80) != 0) { + e.KeyEvent.state |= (int) KeyMasks.ShiftMask; + } + + if ((key_state_table [(int) VirtualKeys.VK_CAPITAL] & 0x01) != 0) { + e.KeyEvent.state |= (int) KeyMasks.LockMask; + } + + if ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) { + e.KeyEvent.state |= (int) KeyMasks.ControlMask; + } + + if ((key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x01) != 0) { + e.KeyEvent.state |= NumLockMask; + } + + e.KeyEvent.state |= AltGrMask; + + for (int keyc = min_keycode; (keyc <= max_keycode) && (e.KeyEvent.keycode == 0); keyc++) { + // find keycode that could have generated this vkey + if ((keyc2vkey [keyc] & 0xFF) == vkey) { + // filter extended bit because it is not known + e.KeyEvent.keycode = keyc; + if ((EventToVkey (e) & 0xFF) != vkey) { + // Wrong one (ex: because of num,lock state) + e.KeyEvent.keycode = 0; + } + } + } + + if ((vkey >= (int) VirtualKeys.VK_NUMPAD0) && (vkey <= (int) VirtualKeys.VK_NUMPAD9)) + e.KeyEvent.keycode = XKeysymToKeycode (display, vkey - (int) VirtualKeys.VK_NUMPAD0 + (int) KeypadKeys.XK_KP_0); + + if (vkey == (int) VirtualKeys.VK_DECIMAL) + e.KeyEvent.keycode = XKeysymToKeycode (display, (int) KeypadKeys.XK_KP_Decimal); + + if (vkey == (int) VirtualKeys.VK_SEPARATOR) + e.KeyEvent.keycode = XKeysymToKeycode(display, (int) KeypadKeys.XK_KP_Separator); + + if (e.KeyEvent.keycode == 0 && vkey != (int) VirtualKeys.VK_NONAME) { + // And I couldn't find the keycode so i returned the vkey and was like whatever + Console.Error.WriteLine ("unknown virtual key {0:X}", vkey); + buffer = String.Empty; + return vkey; + } + + XKeySym t; + XLookupStatus status; + int res = LookupString (ref e, 24, out t, out status); + int keysym = (int) t; + + buffer = String.Empty; + if (res == 0) { + int dead_char = MapDeadKeySym (keysym); + if (dead_char != 0) { + byte [] bytes = new byte [1]; + bytes [0] = (byte) dead_char; + buffer = new string (AnsiEncoding.GetChars (bytes)); + res = -1; + } + } else { + // Shift + arrow, shift + home, .... + // X returns a char for it, but windows doesn't + if (((e.KeyEvent.state & NumLockMask) == 0) && ((e.KeyEvent.state & (int) KeyMasks.ShiftMask) != 0) && + (keysym >= (int) KeypadKeys.XK_KP_0) && (keysym <= (int) KeypadKeys.XK_KP_9)) { + buffer = String.Empty; + res = 0; + } + + // CTRL + number, X returns chars, windows does not + if ((e.KeyEvent.state & (int) KeyMasks.ControlMask) != 0) { + if (((keysym >= 33) && (keysym < 'A')) || ((keysym > 'Z') && (keysym < 'a'))) { + buffer = String.Empty; + res = 0; + } + } + + // X returns a char for delete key on extended keyboards, windows does not + if (keysym == (int) TtyKeys.XK_Delete) { + buffer = String.Empty; + res = 0; + } + + if (keysym == (int) TtyKeys.XK_BackSpace && (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0) { + buffer = new string (new char [] { (char) 127 }); + + return 1; + } + + // For some special chars, such backspace and enter, looking up for a string + // can randomly fail to properly fill the buffer (either marshaling or X11), so we use + // the keysysm value to fill the gap + if (keysym == (int) XKeySym.XK_BackSpace) { + buffer = new string (new char [] { (char) 8 }); + return 1; + } + if (keysym == (int) XKeySym.XK_Return) { + buffer = new string (new char [] { (char)13 }); + return 1; + } + + if (res != 0) { + buffer = lookup_buffer.ToString (); + res = buffer.Length; + } + } + + return res; + } + + string stored_keyevent_string; + + internal string GetCompositionString () + { + return stored_keyevent_string; + } + + private MSG SendImeComposition (string s) + { + MSG msg = new MSG (); + msg.message = Msg.WM_IME_COMPOSITION; + msg.refobject = s; + stored_keyevent_string = s; + return msg; + } + + private MSG SendKeyboardInput (VirtualKeys vkey, int scan, int keycode, KeybdEventFlags dw_flags, int time) + { + Msg message; + + if ((dw_flags & KeybdEventFlags.KeyUp) != 0) { + bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 && + ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0); + key_state_table [(int) vkey] &= unchecked ((byte) ~0x80); + message = (sys_key ? Msg.WM_SYSKEYUP : Msg.WM_KEYUP); + } else { + if ((key_state_table [(int) vkey] & 0x80) == 0) { + key_state_table [(int) vkey] ^= 0x01; + } + key_state_table [(int) vkey] |= 0x80; + bool sys_key = (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 && + ((key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) == 0); + message = (sys_key ? Msg.WM_SYSKEYDOWN : Msg.WM_KEYDOWN); + } + + MSG msg = new MSG (); + msg.message = message; + msg.wParam = (IntPtr) vkey; + msg.lParam = GenerateLParam (msg, keycode); + return msg; + } + + private IntPtr GenerateLParam (MSG m, int keyCode) + { + // http://msdn.microsoft.com/en-us/library/ms646267(VS.85).aspx + // + byte flags = 0; + + if (m.message == Msg.WM_SYSKEYUP || m.message == Msg.WM_KEYUP) + flags |= 0x80; // transition state flag = 1 + + flags |= 0x40; // previous key state flag = 1 + + if ((key_state_table [(int) VirtualKeys.VK_RMENU] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_LMENU] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0) + flags |= 0x20; // context code flag = 1 + + if ((key_state_table [(int) VirtualKeys.VK_INSERT] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_DELETE] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_HOME] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_END] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_UP] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_DOWN] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_LEFT] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_RIGHT] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_CONTROL] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_MENU] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_NUMLOCK] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_PRINT] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_RETURN] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_DIVIDE] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_PRIOR] & 0x80) != 0 || + (key_state_table [(int) VirtualKeys.VK_NEXT] & 0x80) != 0) + flags |= 0x01; // extended key flag = 1 + + int lparam = ((((int)flags) & 0x000000FF) << 3*8); // message flags + lparam |= ((keyCode & 0x000000FF) << 2*8); // scan code + lparam |= 0x00000001; // repeat count = 1 + + return (IntPtr)lparam; + } + + private void GenerateMessage (VirtualKeys vkey, int scan, int key_code, XEventName type, int event_time) + { + bool state = (vkey == VirtualKeys.VK_NUMLOCK ? num_state : cap_state); + KeybdEventFlags up, down; + + if (state) { + // The INTERMEDIARY state means : just after a 'press' event, if a 'release' event comes, + // don't treat it. It's from the same key press. Then the state goes to ON. + // And from there, a 'release' event will switch off the toggle key. + SetState (vkey, false); + } else { + down = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : KeybdEventFlags.None); + up = (vkey == VirtualKeys.VK_NUMLOCK ? KeybdEventFlags.ExtendedKey : + KeybdEventFlags.None) | KeybdEventFlags.KeyUp; + if ((key_state_table [(int) vkey] & 0x1) != 0) { // it was on + if (type != XEventName.KeyPress) { + SendKeyboardInput (vkey, scan, key_code, down, event_time); + SendKeyboardInput (vkey, scan, key_code, up, event_time); + SetState (vkey, false); + key_state_table [(int) vkey] &= unchecked ((byte) ~0x01); + } + } else { + if (type == XEventName.KeyPress) { + SendKeyboardInput (vkey, scan, key_code, down, event_time); + SendKeyboardInput (vkey, scan, key_code, up, event_time); + SetState (vkey, true); + key_state_table [(int) vkey] |= 0x01; + } + } + } + } + + private void UpdateKeyState (XEvent xevent) + { + int vkey = EventToVkey (xevent); + + switch (xevent.type) { + case XEventName.KeyRelease: + key_state_table [vkey & 0xff] &= unchecked ((byte) ~0x80); + break; + case XEventName.KeyPress: + if ((key_state_table [vkey & 0xff] & 0x80) == 0) { + key_state_table [vkey & 0xff] ^= 0x01; + } + key_state_table [vkey & 0xff] |= 0x80; + break; + } + } + + private void SetState (VirtualKeys key, bool state) + { + if (VirtualKeys.VK_NUMLOCK == key) + num_state = state; + else + cap_state = state; + } + + public int EventToVkey (XEvent e) + { + XLookupStatus status; + XKeySym ks; + + LookupString (ref e, 0, out ks, out status); + int keysym = (int) ks; + + if (((e.KeyEvent.state & NumLockMask) != 0) && + (keysym == (int)KeypadKeys.XK_KP_Separator || keysym == (int)KeypadKeys.XK_KP_Decimal || + (keysym >= (int)KeypadKeys.XK_KP_0 && keysym <= (int)KeypadKeys.XK_KP_9))) { + // Only the Keypad keys 0-9 and . send different keysyms + // depending on the NumLock state + return nonchar_key_vkey [keysym & 0xFF]; + } + + return keyc2vkey [e.KeyEvent.keycode]; + } + + private void CreateConversionArray (KeyboardLayouts layouts, KeyboardLayout layout) + { + XEvent e2 = new XEvent (); + uint keysym = 0; + int [] ckey = new int [] { 0, 0, 0, 0 }; + + e2.KeyEvent.display = display; + e2.KeyEvent.state = 0; + + for (int keyc = min_keycode; keyc <= max_keycode; keyc++) { + int vkey = 0; + int scan = 0; + + e2.KeyEvent.keycode = keyc; + XKeySym t; + + XLookupStatus status; + LookupString (ref e2, 0, out t, out status); + + keysym = (uint) t; + if (keysym != 0) { + if ((keysym >> 8) == 0xFF) { + vkey = nonchar_key_vkey [keysym & 0xFF]; + scan = nonchar_key_scan [keysym & 0xFF]; + // Set extended bit + if ((scan & 0x100) != 0) + vkey |= 0x100; + } else if (keysym == 0x20) { // spacebar + vkey = (int) VirtualKeys.VK_SPACE; + scan = 0x39; + } else { + // Search layout dependent scancodes + int maxlen = 0; + int maxval = -1;; + + for (int i = 0; i < syms; i++) { + keysym = XKeycodeToKeysym (display, keyc, i); + if ((keysym < 0x800) && (keysym != ' ')) + ckey [i] = (sbyte) (keysym & 0xFF); + else + ckey [i] = (sbyte) MapDeadKeySym ((int) keysym); + } + + for (int keyn = 0; keyn < layout.Keys.Length; keyn++) { + int ml = Math.Min (layout.Keys [keyn].Length, 4); + int ok = -1; + for (int i = 0; (ok != 0) && (i < ml); i++) { + sbyte ck = (sbyte) layout.Keys [keyn][i]; + if (ck != ckey [i]) + ok = 0; + if ((ok != 0) || (i > maxlen)) { + maxlen = i; + maxval = keyn; + } + if (ok != 0) + break; + } + } + if (maxval >= 0) { + if (maxval < layouts.scan_table [(int) layout.ScanIndex].Length) + scan = layouts.scan_table [(int) layout.ScanIndex][maxval]; + if (maxval < layouts.vkey_table [(int) layout.VKeyIndex].Length) + vkey = layouts.vkey_table [(int) layout.VKeyIndex][maxval]; + } + } + } + keyc2vkey [e2.KeyEvent.keycode] = vkey; + keyc2scan [e2.KeyEvent.keycode] = scan; + } + + + } + + private KeyboardLayout DetectLayout (KeyboardLayouts layouts) + { + XDisplayKeycodes (display, out min_keycode, out max_keycode); + IntPtr ksp = XGetKeyboardMapping (display, (byte) min_keycode, + max_keycode + 1 - min_keycode, out keysyms_per_keycode); + lock (XlibLock) { + XplatUIX11.XFree (ksp); + } + + syms = keysyms_per_keycode; + if (syms > 4) { + //Console.Error.WriteLine ("{0} keysymbols per a keycode is not supported, setting to 4", syms); + syms = 2; + } + + IntPtr modmap_unmanaged; + XModifierKeymap xmk = new XModifierKeymap (); + + modmap_unmanaged = XGetModifierMapping (display); + xmk = (XModifierKeymap) Marshal.PtrToStructure (modmap_unmanaged, typeof (XModifierKeymap)); + + int mmp = 0; + for (int i = 0; i < 8; i++) { + for (int j = 0; j < xmk.max_keypermod; j++, mmp++) { + byte b = Marshal.ReadByte (xmk.modifiermap, mmp); + if (b != 0) { + for (int k = 0; k < keysyms_per_keycode; k++) { + if ((int) XKeycodeToKeysym (display, b, k) == (int) MiscKeys.XK_Num_Lock) + NumLockMask = 1 << i; + } + } + } + } + XFreeModifiermap (modmap_unmanaged); + + int [] ckey = new int [4]; + KeyboardLayout layout = null; + int max_score = 0; + int max_seq = 0; + + foreach (KeyboardLayout current in layouts.Layouts) { + int ok = 0; + int score = 0; + int match = 0; + int mismatch = 0; + int seq = 0; + int pkey = -1; + int key = min_keycode; + int i; + + for (int keyc = min_keycode; keyc <= max_keycode; keyc++) { + for (i = 0; i < syms; i++) { + uint keysym = XKeycodeToKeysym (display, keyc, i); + + if ((keysym < 0x800) && (keysym != ' ')) { + ckey [i] = (sbyte) (keysym & 0xFF); + } else { + ckey [i] = (sbyte) MapDeadKeySym ((int) keysym); + } + } + if (ckey [0] != 0) { + for (key = 0; key < current.Keys.Length; key++) { + int ml = Math.Min (syms, current.Keys [key].Length); + for (ok = 0, i = 0; (ok >= 0) && (i < ml); i++) { + sbyte ck = (sbyte) current.Keys [key][i]; + if (ck != 0 && ck == ckey[i]) + ok++; + if (ck != 0 && ck != ckey[i]) + ok = -1; + } + if (ok > 0) { + score += ok; + break; + } + } + if (ok > 0) { + match++; + /* and how much the keycode order matches */ + if (key > pkey) + seq++; + pkey = key; + } else { + /* print spaces instead of \0's */ + mismatch++; + score -= syms; + } + } + } + + if ((score > max_score) || ((score == max_score) && (seq > max_seq))) { + // best match so far + layout = current; + max_score = score; + max_seq = seq; + } + } + + if (layout != null) { + return layout; + } else { + Console.WriteLine ("Keyboard layout not recognized, using default layout: " + + layouts.Layouts [0].Name); + } + + return layouts.Layouts [0]; + } + + // TODO + private int MapDeadKeySym (int val) + { + switch (val) { + case (int) DeadKeys.XK_dead_tilde : + case 0x1000FE7E : // Xfree's Dtilde + return '~'; + case (int) DeadKeys.XK_dead_acute : + case 0x1000FE27 : // Xfree's XK_Dacute_accent + return 0xb4; + case (int) DeadKeys.XK_dead_circumflex: + case 0x1000FE5E : // Xfree's XK_.Dcircumflex_accent + return '^'; + case (int) DeadKeys.XK_dead_grave : + case 0x1000FE60 : // Xfree's XK_.Dgrave_accent + return '`'; + case (int) DeadKeys.XK_dead_diaeresis : + case 0x1000FE22 : // Xfree's XK_.Ddiaeresis + return 0xa8; + case (int) DeadKeys.XK_dead_cedilla : + return 0xb8; + case (int) DeadKeys.XK_dead_macron : + return '-'; + case (int) DeadKeys.XK_dead_breve : + return 0xa2; + case (int) DeadKeys.XK_dead_abovedot : + return 0xff; + case (int) DeadKeys.XK_dead_abovering : + return '0'; + case (int) DeadKeys.XK_dead_doubleacute : + return 0xbd; + case (int) DeadKeys.XK_dead_caron : + return 0xb7; + case (int) DeadKeys.XK_dead_ogonek : + return 0xb2; + } + + return 0; + } + + private XIMProperties [] GetSupportedInputStyles (IntPtr xim) + { + IntPtr stylesPtr; + string ret = XGetIMValues (xim, XNames.XNQueryInputStyle, out stylesPtr, IntPtr.Zero); + if (ret != null || stylesPtr == IntPtr.Zero) + return new XIMProperties [0]; + XIMStyles styles = (XIMStyles) Marshal.PtrToStructure (stylesPtr, typeof (XIMStyles)); + XIMProperties [] supportedStyles = new XIMProperties [styles.count_styles]; + for (int i = 0; i < styles.count_styles; i++) + supportedStyles [i] = (XIMProperties) Marshal.PtrToStructure (new IntPtr ((long) styles.supported_styles + i * Marshal.SizeOf (typeof (IntPtr))), typeof (XIMProperties)); + lock (XlibLock) { + XplatUIX11.XFree (stylesPtr); + } + return supportedStyles; + } + + const XIMProperties styleRoot = XIMProperties.XIMPreeditNothing | XIMProperties.XIMStatusNothing; + const XIMProperties styleOverTheSpot = XIMProperties.XIMPreeditPosition | XIMProperties.XIMStatusNothing; + const XIMProperties styleOnTheSpot = XIMProperties.XIMPreeditCallbacks | XIMProperties.XIMStatusNothing; + const string ENV_NAME_XIM_STYLE = "MONO_WINFORMS_XIM_STYLE"; + + private XIMProperties [] GetPreferredStyles () + { + string env = Environment.GetEnvironmentVariable (ENV_NAME_XIM_STYLE); + if (env == null) + env = "over-the-spot"; + string [] list = env.Split (' '); + XIMProperties [] ret = new XIMProperties [list.Length]; + for (int i = 0; i < list.Length; i++) { + string s = list [i]; + switch (s) { + case "over-the-spot": + ret [i] = styleOverTheSpot; + break; + case "on-the-spot": + ret [i] = styleOnTheSpot; + break; + case "root": + ret [i] = styleRoot; + break; + } + } + return ret; + } + + private IEnumerable GetMatchingStylesInPreferredOrder (IntPtr xim) + { + XIMProperties [] supportedStyles = GetSupportedInputStyles (xim); + foreach (XIMProperties p in GetPreferredStyles ()) + if (Array.IndexOf (supportedStyles, p) >= 0) + yield return p; + } + + private IntPtr CreateXic (IntPtr window, IntPtr xim) + { + IntPtr xic = IntPtr.Zero; + foreach (XIMProperties targetStyle in GetMatchingStylesInPreferredOrder (xim)) { + ximStyle = targetStyle; + // FIXME: use __arglist when it gets working. See bug #321686 + switch (targetStyle) { + case styleOverTheSpot: + xic = CreateOverTheSpotXic (window, xim); + if (xic != IntPtr.Zero) + break; + //Console.WriteLine ("failed to create XIC in over-the-spot mode."); + continue; + case styleOnTheSpot: + // Since .NET/Winforms seems to support only over-the-spot mode,, + // I'm not likely to continue on-the-spot implementation. But in + // case we need it, this code will be still useful. + xic = CreateOnTheSpotXic (window, xim); + if (xic != IntPtr.Zero) + break; + //Console.WriteLine ("failed to create XIC in on-the-spot mode."); + continue; + case styleRoot: + xic = XCreateIC (xim, + XNames.XNInputStyle, styleRoot, + XNames.XNClientWindow, window, + IntPtr.Zero); + break; + } + } + // fall back to root mode if all modes failed + if (xic == IntPtr.Zero) { + ximStyle = styleRoot; + xic = XCreateIC (xim, + XNames.XNInputStyle, styleRoot, + XNames.XNClientWindow, window, + XNames.XNFocusWindow, window, + IntPtr.Zero); + } + return xic; + } + + private IntPtr CreateOverTheSpotXic (IntPtr window, IntPtr xim) + { + IntPtr list; + int count; + Widget c = Widget.FromHandle (window); + string xlfd = String.Format ("-*-*-*-*-*-*-{0}-*-*-*-*-*-*-*", (int) c.Font.Size); + IntPtr fontSet = XCreateFontSet (display, xlfd, out list, out count, IntPtr.Zero); + XPoint spot = new XPoint (); + spot.X = 0; + spot.Y = 0; + IntPtr pSL = IntPtr.Zero, pFS = IntPtr.Zero; + try { + pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation); + pFS = Marshal.StringToHGlobalAnsi (XNames.XNFontSet); + IntPtr preedit = XVaCreateNestedList (0, + pSL, spot, + pFS, fontSet, + IntPtr.Zero); + return XCreateIC (xim, + XNames.XNInputStyle, styleOverTheSpot, + XNames.XNClientWindow, window, + XNames.XNPreeditAttributes, preedit, + IntPtr.Zero); + } finally { + if (pSL != IntPtr.Zero) + Marshal.FreeHGlobal (pSL); + if (pFS != IntPtr.Zero) + Marshal.FreeHGlobal (pFS); + XFreeStringList (list); + //XplatUIX11.XFree (preedit); + //XFreeFontSet (fontSet); + } + } + + private IntPtr CreateOnTheSpotXic (IntPtr window, IntPtr xim) + { + callbackContext = new XIMCallbackContext (window); + return callbackContext.CreateXic (window, xim); + } + + class XIMCallbackContext + { + XIMCallback startCB, doneCB, drawCB, caretCB; + IntPtr pStartCB = IntPtr.Zero, pDoneCB = IntPtr.Zero, pDrawCB = IntPtr.Zero, pCaretCB = IntPtr.Zero; + IntPtr pStartCBN = IntPtr.Zero, pDoneCBN = IntPtr.Zero, pDrawCBN = IntPtr.Zero, pCaretCBN = IntPtr.Zero; + + public XIMCallbackContext (IntPtr clientWindow) + { + startCB = new XIMCallback (clientWindow, DoPreeditStart); + doneCB = new XIMCallback (clientWindow, DoPreeditDone); + drawCB = new XIMCallback (clientWindow, DoPreeditDraw); + caretCB = new XIMCallback (clientWindow, DoPreeditCaret); + pStartCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback))); + pDoneCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback))); + pDrawCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback))); + pCaretCB = Marshal.AllocHGlobal (Marshal.SizeOf (typeof (XIMCallback))); + pStartCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditStartCallback); + pDoneCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDoneCallback); + pDrawCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDrawCallback); + pCaretCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditCaretCallback); + } + + ~XIMCallbackContext () + { + if (pStartCBN != IntPtr.Zero) + Marshal.FreeHGlobal (pStartCBN); + if (pDoneCBN != IntPtr.Zero) + Marshal.FreeHGlobal (pDoneCBN); + if (pDrawCBN != IntPtr.Zero) + Marshal.FreeHGlobal (pDrawCBN); + if (pCaretCBN != IntPtr.Zero) + Marshal.FreeHGlobal (pCaretCBN); + + if (pStartCB != IntPtr.Zero) + Marshal.FreeHGlobal (pStartCB); + if (pDoneCB != IntPtr.Zero) + Marshal.FreeHGlobal (pDoneCB); + if (pDrawCB != IntPtr.Zero) + Marshal.FreeHGlobal (pDrawCB); + if (pCaretCB != IntPtr.Zero) + Marshal.FreeHGlobal (pCaretCB); + } + + int DoPreeditStart (IntPtr xic, IntPtr clientData, IntPtr callData) + { + Debug.WriteLine ("DoPreeditStart"); + XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITSTART, clientData, callData); + return 100; + } + + int DoPreeditDone (IntPtr xic, IntPtr clientData, IntPtr callData) + { + Debug.WriteLine ("DoPreeditDone"); + XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDONE, clientData, callData); + return 0; + } + + int DoPreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData) + { + Debug.WriteLine ("DoPreeditDraw"); + XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITDRAW, clientData, callData); + return 0; + } + + int DoPreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData) + { + Debug.WriteLine ("DoPreeditCaret"); + XplatUI.SendMessage(clientData, Msg.WM_XIM_PREEDITCARET, clientData, callData); + return 0; + } + + public IntPtr CreateXic (IntPtr window, IntPtr xim) + { + Marshal.StructureToPtr (startCB, pStartCB, false); + Marshal.StructureToPtr (doneCB, pDoneCB, false); + Marshal.StructureToPtr (drawCB, pDrawCB, false); + Marshal.StructureToPtr (caretCB, pCaretCB, false); + IntPtr preedit = XVaCreateNestedList (0, + pStartCBN, pStartCB, + pDoneCBN, pDoneCB, + pDrawCBN, pDrawCB, + pCaretCBN, pCaretCB, + IntPtr.Zero); + return XCreateIC (xim, + XNames.XNInputStyle, styleOnTheSpot, + XNames.XNClientWindow, window, + XNames.XNPreeditAttributes, preedit, + IntPtr.Zero); + } + } + + class XIMPositionContext + { + public CaretStruct Caret; + public int X; + public int Y; + } + + internal void SetCaretPos (CaretStruct caret, IntPtr handle, int x, int y) + { + if (ximStyle != styleOverTheSpot) + return; + + if (positionContext == null) + this.positionContext = new XIMPositionContext (); + + positionContext.Caret = caret; + positionContext.X = x; + positionContext.Y = y + caret.Height; + + MoveCurrentCaretPos (); + } + + internal void MoveCurrentCaretPos () + { + if (positionContext == null || ximStyle != styleOverTheSpot || client_window == IntPtr.Zero) + return; + + int x = positionContext.X; + int y = positionContext.Y; + CaretStruct caret = positionContext.Caret; + IntPtr xic = GetXic (client_window); + if (xic == IntPtr.Zero) + return; + Widget Widget = Widget.FromHandle (client_window); + if (Widget == null || !Widget.IsHandleCreated) + return; + Widget = Widget.FromHandle (caret.Hwnd); + if (Widget == null || !Widget.IsHandleCreated) + return; + Hwnd hwnd = Hwnd.ObjectFromHandle (client_window); + if (!hwnd.mapped) + return; + + int dx, dy; + IntPtr child; + lock (XlibLock) { + XplatUIX11.XTranslateCoordinates (display, client_window, client_window, x, y, out dx, out dy, out child); + } + + XPoint spot = new XPoint (); + spot.X = (short) dx; + spot.Y = (short) dy; + + IntPtr pSL = IntPtr.Zero; + try { + pSL = Marshal.StringToHGlobalAnsi (XNames.XNSpotLocation); + IntPtr preedit = XVaCreateNestedList (0, pSL, spot, IntPtr.Zero); + XSetICValues (xic, XNames.XNPreeditAttributes, preedit, IntPtr.Zero); + } finally { + if (pSL != IntPtr.Zero) + Marshal.FreeHGlobal (pSL); + } + } + + private bool have_Xutf8LookupString = true; + + private int LookupString (ref XEvent xevent, int len, out XKeySym keysym, out XLookupStatus status) + { + IntPtr keysym_res; + int res; + + status = XLookupStatus.XLookupNone; + IntPtr xic = GetXic (client_window); + if (xic != IntPtr.Zero && have_Xutf8LookupString && xevent.type == XEventName.KeyPress) { + do { + try { + res = Xutf8LookupString (xic, ref xevent, lookup_byte_buffer, 100, out keysym_res, out status); + } catch (EntryPointNotFoundException) { + have_Xutf8LookupString = false; + + // call again, this time we'll go through the non-xic clause + return LookupString (ref xevent, len, out keysym, out status); + } + if (status != XLookupStatus.XBufferOverflow) + break; + lookup_byte_buffer = new byte [lookup_byte_buffer.Length << 1]; + } while (true); + lookup_buffer.Length = 0; + string s = Encoding.UTF8.GetString (lookup_byte_buffer, 0, res); + lookup_buffer.Append (s); + keysym = (XKeySym) keysym_res.ToInt32 (); + return s.Length; + } else { + IntPtr statusPtr = IntPtr.Zero; + lookup_buffer.Length = 0; + res = XLookupString (ref xevent, lookup_buffer, len, out keysym_res, out statusPtr); + keysym = (XKeySym) keysym_res.ToInt32 (); + return res; + } + } + + [DllImport ("libX11")] + private static extern IntPtr XOpenIM (IntPtr display, IntPtr rdb, IntPtr res_name, IntPtr res_class); + + [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, IntPtr terminator); + [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, IntPtr terminator); +// [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)] +// private static extern IntPtr XCreateIC (IntPtr xim, string name, XIMProperties im_style, string name2, IntPtr value2, string name3, IntPtr value3, string name4, IntPtr value4, IntPtr terminator); + + [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr terminator); + [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, XPoint value0, IntPtr name1, IntPtr value1, IntPtr terminator); + [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr XVaCreateNestedList (int dummy, IntPtr name0, IntPtr value0, IntPtr name1, IntPtr value1, IntPtr name2, IntPtr value2, IntPtr name3, IntPtr value3, IntPtr terminator); + + [DllImport ("libX11")] + private static extern IntPtr XCreateFontSet (IntPtr display, string name, out IntPtr list, out int count, IntPtr terminator); + + [DllImport ("libX11")] + internal extern static void XFreeFontSet (IntPtr data); + + [DllImport ("libX11")] + private static extern void XFreeStringList (IntPtr ptr); + + //[DllImport ("libX11")] + //private static extern IntPtr XIMOfIC (IntPtr xic); + + [DllImport ("libX11")] + private static extern void XCloseIM (IntPtr xim); + + [DllImport ("libX11")] + private static extern void XDestroyIC (IntPtr xic); + + [DllImport ("libX11")] + private static extern string XGetIMValues (IntPtr xim, string name, out IntPtr value, IntPtr terminator); + + [DllImport ("libX11")] + private static extern string XGetICValues (IntPtr xic, string name, out EventMask value, IntPtr terminator); + + [DllImport ("libX11", CallingConvention = CallingConvention.Cdecl)] + private static extern void XSetICValues (IntPtr xic, string name, IntPtr value, IntPtr terminator); + + [DllImport ("libX11")] + private static extern void XSetICFocus (IntPtr xic); + + [DllImport ("libX11")] + private static extern void XUnsetICFocus (IntPtr xic); + + [DllImport ("libX11")] + private static extern string Xutf8ResetIC (IntPtr xic); + + [DllImport ("libX11")] + private static extern bool XSupportsLocale (); + + [DllImport ("libX11")] + private static extern bool XSetLocaleModifiers (string mods); + + [DllImport ("libX11")] + internal extern static int XLookupString(ref XEvent xevent, StringBuilder buffer, int num_bytes, out IntPtr keysym, out IntPtr status); + [DllImport ("libX11")] + internal extern static int Xutf8LookupString(IntPtr xic, ref XEvent xevent, byte [] buffer, int num_bytes, out IntPtr keysym, out XLookupStatus status); + + [DllImport ("libX11")] + private static extern IntPtr XGetKeyboardMapping (IntPtr display, byte first_keycode, int keycode_count, + out int keysyms_per_keycode_return); + + [DllImport ("libX11")] + private static extern void XDisplayKeycodes (IntPtr display, out int min, out int max); + + [DllImport ("libX11", EntryPoint="XKeycodeToKeysym")] + private static extern uint XKeycodeToKeysym (IntPtr display, int keycode, int index); + + [DllImport ("libX11")] + private static extern int XKeysymToKeycode (IntPtr display, IntPtr keysym); + private static int XKeysymToKeycode (IntPtr display, int keysym) { + return XKeysymToKeycode(display, (IntPtr)keysym); + } + + [DllImport ("libX11")] + internal extern static IntPtr XGetModifierMapping (IntPtr display); + + [DllImport ("libX11")] + internal extern static int XFreeModifiermap (IntPtr modmap); + + + private readonly static int [] nonchar_key_vkey = new int [] + { + /* unused */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FF00 */ + /* special keys */ + (int) VirtualKeys.VK_BACK, (int) VirtualKeys.VK_TAB, 0, (int) VirtualKeys.VK_CLEAR, 0, (int) VirtualKeys.VK_RETURN, 0, 0, /* FF08 */ + 0, 0, 0, (int) VirtualKeys.VK_PAUSE, (int) VirtualKeys.VK_SCROLL, 0, 0, 0, /* FF10 */ + 0, 0, 0, (int) VirtualKeys.VK_ESCAPE, 0, 0, 0, 0, /* FF18 */ + 0, 0, (int) VirtualKeys.VK_NONCONVERT, (int) VirtualKeys.VK_CONVERT, 0, 0, 0, 0, /* FF20 */ + 0, 0, (int) VirtualKeys.VK_OEM_AUTO, 0, 0, 0, 0, 0, /* FF28 */ + /* unused */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FF30 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FF38 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FF40 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FF48 */ + /* cursor keys */ + (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, (int) VirtualKeys.VK_RIGHT, /* FF50 */ + (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, (int) VirtualKeys.VK_END, + 0, 0, 0, 0, 0, 0, 0, 0, /* FF58 */ + /* misc keys */ + (int) VirtualKeys.VK_SELECT, (int) VirtualKeys.VK_SNAPSHOT, (int) VirtualKeys.VK_EXECUTE, (int) VirtualKeys.VK_INSERT, 0, 0, 0, 0, /* FF60 */ + (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_HELP, (int) VirtualKeys.VK_CANCEL, (int) VirtualKeys.VK_CANCEL, 0, 0, 0, 0, /* FF68 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FF70 */ + /* keypad keys */ + 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_NUMLOCK, /* FF78 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FF80 */ + 0, 0, 0, 0, 0, (int) VirtualKeys.VK_RETURN, 0, 0, /* FF88 */ + 0, 0, 0, 0, 0, (int) VirtualKeys.VK_HOME, (int) VirtualKeys.VK_LEFT, (int) VirtualKeys.VK_UP, /* FF90 */ + (int) VirtualKeys.VK_RIGHT, (int) VirtualKeys.VK_DOWN, (int) VirtualKeys.VK_PRIOR, (int) VirtualKeys.VK_NEXT, /* FF98 */ + (int) VirtualKeys.VK_END, 0, (int) VirtualKeys.VK_INSERT, (int) VirtualKeys.VK_DELETE, + 0, 0, 0, 0, 0, 0, 0, 0, /* FFA0 */ + 0, 0, (int) VirtualKeys.VK_MULTIPLY, (int) VirtualKeys.VK_ADD, /* FFA8 */ + (int) VirtualKeys.VK_SEPARATOR, (int) VirtualKeys.VK_SUBTRACT, (int) VirtualKeys.VK_DECIMAL, (int) VirtualKeys.VK_DIVIDE, + (int) VirtualKeys.VK_NUMPAD0, (int) VirtualKeys.VK_NUMPAD1, (int) VirtualKeys.VK_NUMPAD2, (int) VirtualKeys.VK_NUMPAD3, /* FFB0 */ + (int) VirtualKeys.VK_NUMPAD4, (int) VirtualKeys.VK_NUMPAD5, (int) VirtualKeys.VK_NUMPAD6, (int) VirtualKeys.VK_NUMPAD7, + (int) VirtualKeys.VK_NUMPAD8, (int) VirtualKeys.VK_NUMPAD9, 0, 0, 0, 0, /* FFB8 */ + /* function keys */ + (int) VirtualKeys.VK_F1, (int) VirtualKeys.VK_F2, + (int) VirtualKeys.VK_F3, (int) VirtualKeys.VK_F4, (int) VirtualKeys.VK_F5, (int) VirtualKeys.VK_F6, (int) VirtualKeys.VK_F7, (int) VirtualKeys.VK_F8, (int) VirtualKeys.VK_F9, (int) VirtualKeys.VK_F10, /* FFC0 */ + (int) VirtualKeys.VK_F11, (int) VirtualKeys.VK_F12, (int) VirtualKeys.VK_F13, (int) VirtualKeys.VK_F14, (int) VirtualKeys.VK_F15, (int) VirtualKeys.VK_F16, 0, 0, /* FFC8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FFD0 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FFD8 */ + /* modifier keys */ + 0, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_SHIFT, (int) VirtualKeys.VK_CONTROL, /* FFE0 */ + (int) VirtualKeys.VK_CONTROL, (int) VirtualKeys.VK_CAPITAL, 0, (int) VirtualKeys.VK_MENU, + (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, (int) VirtualKeys.VK_MENU, 0, 0, 0, 0, 0, /* FFE8 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* FFF0 */ + 0, 0, 0, 0, 0, 0, 0, (int) VirtualKeys.VK_DELETE /* FFF8 */ + }; + + private static readonly int [] nonchar_key_scan = new int [] + { + /* unused */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF00 */ + /* special keys */ + 0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00, /* FF08 */ + 0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00, /* FF10 */ + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, /* FF18 */ + /* unused */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF20 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF28 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF30 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF38 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF48 */ + /* cursor keys */ + 0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F, /* FF50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF58 */ + /* misc keys */ + /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x00, /* FF60 */ + /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00, /* FF68 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF70 */ + /* keypad keys */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x138, 0x145, /* FF78 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FF80 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00, /* FF88 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48, /* FF90 */ + 0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53, /* FF98 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFA0 */ + 0x00, 0x00, 0x37, 0x4E, /*?*/ 0, 0x4A, 0x53, 0x135, /* FFA8 */ + 0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47, /* FFB0 */ + 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, /* FFB8 */ + /* function keys */ + 0x3B, 0x3C, + 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, /* FFC0 */ + 0x57, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFC8 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFD8 */ + /* modifier keys */ + 0x00, 0x2A, 0x36, 0x1D, 0x11D, 0x3A, 0x00, 0x38, /* FFE0 */ + 0x138, 0x38, 0x138, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFE8 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* FFF0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153 /* FFF8 */ + }; + + private readonly static int [] nonchar_vkey_key = new int [] + { + 0, 0, 0, 0, 0, /* 00-04 */ + 0, 0, 0, (int)XKeySym.XK_BackSpace, (int)XKeySym.XK_Tab, /* 05-09 */ + 0, 0, (int)XKeySym.XK_Clear, (int)XKeySym.XK_Return, 0, 0, /* 0A-0F */ + (int)XKeySym.XK_Shift_L, (int)XKeySym.XK_Control_L, (int)XKeySym.XK_Menu, 0, (int)XKeySym.XK_Caps_Lock, /* 10-14 */ + 0, 0, 0, 0, 0, /* 15-19 */ + 0, 0, 0, 0, 0, 0, /* 1A-1F */ + 0, 0, 0, (int)XKeySym.XK_End, (int)XKeySym.XK_Home, /* 20-24 */ + (int)XKeySym.XK_Left, (int)XKeySym.XK_Up, (int)XKeySym.XK_Right, (int)XKeySym.XK_Down, 0, /* 25-29 */ + 0, 0, 0, 0, 0, 0, /* 2A-2F */ + 0, 0, 0, 0, 0, /* 30-34 */ + 0, 0, 0, 0, 0, /* 35-39 */ + 0, 0, 0, 0, 0, 0, /* 3A-3F */ + 0, 0, 0, 0, 0, /* 40-44 */ + 0, 0, 0, 0, 0, /* 45-49 */ + 0, 0, 0, 0, 0, 0, /* 4A-4F */ + 0, 0, 0, 0, 0, /* 50-54 */ + 0, 0, 0, 0, 0, /* 55-59 */ + 0, (int)XKeySym.XK_Meta_L, (int)XKeySym.XK_Meta_R, 0, 0, 0, /* 5A-5F */ + 0, 0, 0, 0, 0, /* 60-64 */ + 0, 0, 0, 0, 0, /* 65-69 */ + 0, 0, 0, 0, 0, 0, /* 6A-6F */ + 0, 0, 0, 0, 0, /* 70-74 */ + 0, 0, 0, 0, 0, /* 75-79 */ + 0, 0, 0, 0, 0, 0, /* 7A-7F */ + 0, 0, 0, 0, 0, /* 80-84 */ + 0, 0, 0, 0, 0, /* 85-89 */ + 0, 0, 0, 0, 0, 0, /* 8A-8F */ + 0, 0, 0, 0, 0, /* 90-94 */ + 0, 0, 0, 0, 0, /* 95-99 */ + 0, 0, 0, 0, 0, 0, /* 9A-9F */ + (int)XKeySym.XK_Shift_L, (int)XKeySym.XK_Shift_R, (int)XKeySym.XK_Control_L, (int)XKeySym.XK_Control_R, (int)XKeySym.XK_Alt_L, /* A0-A4 */ + (int)XKeySym.XK_Alt_R, 0, 0, 0, 0, /* A5-A9 */ + 0, 0, 0, 0, 0, 0, /* AA-AF */ + 0, 0, 0, 0, 0, /* B0-B4 */ + 0, 0, 0, 0, 0, /* B5-B9 */ + 0, 0, 0, 0, 0, 0, /* BA-BF */ + 0, 0, 0, 0, 0, /* C0-C4 */ + 0, 0, 0, 0, 0, /* C5-C9 */ + 0, 0, 0, 0, 0, 0, /* CA-CF */ + 0, 0, 0, 0, 0, /* D0-D4 */ + 0, 0, 0, 0, 0, /* D5-D9 */ + 0, 0, 0, 0, 0, 0, /* DA-DF */ + 0, 0, 0, 0, 0, /* E0-E4 */ + 0, 0, 0, 0, 0, /* E5-E9 */ + 0, 0, 0, 0, 0, 0, /* EA-EF */ + 0, 0, 0, 0, 0, /* F0-F4 */ + 0, 0, 0, 0, 0, /* F5-F9 */ + 0, 0, 0, 0, 0, 0 /* FA-FF */ + }; + + } + +} + diff --git a/source/ShiftUI/Internal/X11RootHwnd.cs b/source/ShiftUI/Internal/X11RootHwnd.cs new file mode 100644 index 0000000..45a7bee --- /dev/null +++ b/source/ShiftUI/Internal/X11RootHwnd.cs @@ -0,0 +1,87 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// +// + +using System; +using System.Runtime.InteropServices; +using ShiftUI; + +namespace ShiftUI.X11Internal { + + internal class X11RootHwnd : X11Hwnd + { + public X11RootHwnd (X11Display display, IntPtr window_handle) : base (display) + { + WholeWindow = ClientWindow = window_handle; + + Xlib.XSelectInput(display.Handle, WholeWindow, new IntPtr ((int)EventMask.PropertyChangeMask)); + } + + public override void CreateWindow (CreateParams cp) + { + // we don't do anything here + } + + public override void PropertyChanged (XEvent xevent) + { + if (xevent.PropertyEvent.atom == Display.Atoms._NET_ACTIVE_WINDOW) { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Xlib.XGetWindowProperty (Display.Handle, WholeWindow, + Display.Atoms._NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false, + Display.Atoms.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (((long)nitems > 0) && (prop != IntPtr.Zero)) { + // FIXME - is this 64 bit clean? + Display.SetActiveWindow ((X11Hwnd)Hwnd.ObjectFromHandle((IntPtr)Marshal.ReadInt32(prop))); + Xlib.XFree(prop); + } + } + else if (xevent.PropertyEvent.atom == Display.Atoms._NET_SUPPORTED) { + // we'll need to refetch the supported protocols list + refetch_net_supported = true; + _net_supported = null; + } + else + base.PropertyChanged (xevent); + } + + bool refetch_net_supported = true; + IntPtr[] _net_supported; + public IntPtr[] _NET_SUPPORTED { + get { + if (refetch_net_supported) { + _net_supported = GetAtomListProperty (Display.Atoms._NET_SUPPORTED); + refetch_net_supported = false; + } + + return _net_supported; + } + } + } + +} + diff --git a/source/ShiftUI/Internal/X11Structs.cs b/source/ShiftUI/Internal/X11Structs.cs new file mode 100644 index 0000000..52bd880 --- /dev/null +++ b/source/ShiftUI/Internal/X11Structs.cs @@ -0,0 +1,1824 @@ +// 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 Novell, Inc. +// +// Authors: +// Peter Bartok [email protected] +// + + +// NOT COMPLETE + +using System; +using System.ComponentModel; +using System.Collections; +using System.Drawing; +using System.Diagnostics; +using System.Runtime.InteropServices; + +/// X11 Version +using System.Reflection; + + +namespace ShiftUI { + // + // In the structures below, fields of type long are mapped to IntPtr. + // This will work on all platforms where sizeof(long)==sizeof(void*), which + // is almost all platforms except WIN64. + // + + [StructLayout(LayoutKind.Sequential)] + internal struct XAnyEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XKeyEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal int state; + internal int keycode; + internal bool same_screen; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XButtonEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal int state; + internal int button; + internal bool same_screen; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMotionEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal int state; + internal byte is_hint; + internal bool same_screen; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCrossingEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr root; + internal IntPtr subwindow; + internal IntPtr time; + internal int x; + internal int y; + internal int x_root; + internal int y_root; + internal NotifyMode mode; + internal NotifyDetail detail; + internal bool same_screen; + internal bool focus; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XFocusChangeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int mode; + internal NotifyDetail detail; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XKeymapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal byte key_vector0; + internal byte key_vector1; + internal byte key_vector2; + internal byte key_vector3; + internal byte key_vector4; + internal byte key_vector5; + internal byte key_vector6; + internal byte key_vector7; + internal byte key_vector8; + internal byte key_vector9; + internal byte key_vector10; + internal byte key_vector11; + internal byte key_vector12; + internal byte key_vector13; + internal byte key_vector14; + internal byte key_vector15; + internal byte key_vector16; + internal byte key_vector17; + internal byte key_vector18; + internal byte key_vector19; + internal byte key_vector20; + internal byte key_vector21; + internal byte key_vector22; + internal byte key_vector23; + internal byte key_vector24; + internal byte key_vector25; + internal byte key_vector26; + internal byte key_vector27; + internal byte key_vector28; + internal byte key_vector29; + internal byte key_vector30; + internal byte key_vector31; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XExposeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int count; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XGraphicsExposeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr drawable; + internal int x; + internal int y; + internal int width; + internal int height; + internal int count; + internal int major_code; + internal int minor_code; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XNoExposeEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr drawable; + internal int major_code; + internal int minor_code; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XVisibilityEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCreateWindowEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XDestroyWindowEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XUnmapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal bool from_configure; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMapRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XReparentEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal IntPtr parent; + internal int x; + internal int y; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XConfigureEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal IntPtr above; + internal bool override_redirect; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XGravityEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal int x; + internal int y; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XResizeRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int width; + internal int height; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XConfigureRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal IntPtr above; + internal int detail; + internal IntPtr value_mask; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCirculateEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr xevent; + internal IntPtr window; + internal int place; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XCirculateRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr parent; + internal IntPtr window; + internal int place; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XPropertyEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr atom; + internal IntPtr time; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSelectionClearEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr selection; + internal IntPtr time; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSelectionRequestEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr owner; + internal IntPtr requestor; + internal IntPtr selection; + internal IntPtr target; + internal IntPtr property; + internal IntPtr time; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSelectionEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr requestor; + internal IntPtr selection; + internal IntPtr target; + internal IntPtr property; + internal IntPtr time; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XColormapEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr colormap; + internal bool c_new; + internal int state; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XClientMessageEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal IntPtr message_type; + internal int format; + internal IntPtr ptr1; + internal IntPtr ptr2; + internal IntPtr ptr3; + internal IntPtr ptr4; + internal IntPtr ptr5; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XMappingEvent { + internal XEventName type; + internal IntPtr serial; + internal bool send_event; + internal IntPtr display; + internal IntPtr window; + internal int request; + internal int first_keycode; + internal int count; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XErrorEvent { + internal XEventName type; + internal IntPtr display; + internal IntPtr resourceid; + internal IntPtr serial; + internal byte error_code; + internal XRequest request_code; + internal byte minor_code; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XEventPad { + internal IntPtr pad0; + internal IntPtr pad1; + internal IntPtr pad2; + internal IntPtr pad3; + internal IntPtr pad4; + internal IntPtr pad5; + internal IntPtr pad6; + internal IntPtr pad7; + internal IntPtr pad8; + internal IntPtr pad9; + internal IntPtr pad10; + internal IntPtr pad11; + internal IntPtr pad12; + internal IntPtr pad13; + internal IntPtr pad14; + internal IntPtr pad15; + internal IntPtr pad16; + internal IntPtr pad17; + internal IntPtr pad18; + internal IntPtr pad19; + internal IntPtr pad20; + internal IntPtr pad21; + internal IntPtr pad22; + internal IntPtr pad23; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct XEvent { + [ FieldOffset(0) ] internal XEventName type; + [ FieldOffset(0) ] internal XAnyEvent AnyEvent; + [ FieldOffset(0) ] internal XKeyEvent KeyEvent; + [ FieldOffset(0) ] internal XButtonEvent ButtonEvent; + [ FieldOffset(0) ] internal XMotionEvent MotionEvent; + [ FieldOffset(0) ] internal XCrossingEvent CrossingEvent; + [ FieldOffset(0) ] internal XFocusChangeEvent FocusChangeEvent; + [ FieldOffset(0) ] internal XExposeEvent ExposeEvent; + [ FieldOffset(0) ] internal XGraphicsExposeEvent GraphicsExposeEvent; + [ FieldOffset(0) ] internal XNoExposeEvent NoExposeEvent; + [ FieldOffset(0) ] internal XVisibilityEvent VisibilityEvent; + [ FieldOffset(0) ] internal XCreateWindowEvent CreateWindowEvent; + [ FieldOffset(0) ] internal XDestroyWindowEvent DestroyWindowEvent; + [ FieldOffset(0) ] internal XUnmapEvent UnmapEvent; + [ FieldOffset(0) ] internal XMapEvent MapEvent; + [ FieldOffset(0) ] internal XMapRequestEvent MapRequestEvent; + [ FieldOffset(0) ] internal XReparentEvent ReparentEvent; + [ FieldOffset(0) ] internal XConfigureEvent ConfigureEvent; + [ FieldOffset(0) ] internal XGravityEvent GravityEvent; + [ FieldOffset(0) ] internal XResizeRequestEvent ResizeRequestEvent; + [ FieldOffset(0) ] internal XConfigureRequestEvent ConfigureRequestEvent; + [ FieldOffset(0) ] internal XCirculateEvent CirculateEvent; + [ FieldOffset(0) ] internal XCirculateRequestEvent CirculateRequestEvent; + [ FieldOffset(0) ] internal XPropertyEvent PropertyEvent; + [ FieldOffset(0) ] internal XSelectionClearEvent SelectionClearEvent; + [ FieldOffset(0) ] internal XSelectionRequestEvent SelectionRequestEvent; + [ FieldOffset(0) ] internal XSelectionEvent SelectionEvent; + [ FieldOffset(0) ] internal XColormapEvent ColormapEvent; + [ FieldOffset(0) ] internal XClientMessageEvent ClientMessageEvent; + [ FieldOffset(0) ] internal XMappingEvent MappingEvent; + [ FieldOffset(0) ] internal XErrorEvent ErrorEvent; + [ FieldOffset(0) ] internal XKeymapEvent KeymapEvent; + + //[MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst=24)] + //[ FieldOffset(0) ] internal int[] pad; + [ FieldOffset(0) ] internal XEventPad Pad; + public override string ToString() { + switch (type) + { + case XEventName.ButtonPress: + case XEventName.ButtonRelease: + return ToString (ButtonEvent); + case XEventName.CirculateNotify: + case XEventName.CirculateRequest: + return ToString (CirculateEvent); + case XEventName.ClientMessage: + return ToString (ClientMessageEvent); + case XEventName.ColormapNotify: + return ToString (ColormapEvent); + case XEventName.ConfigureNotify: + return ToString (ConfigureEvent); + case XEventName.ConfigureRequest: + return ToString (ConfigureRequestEvent); + case XEventName.CreateNotify: + return ToString (CreateWindowEvent); + case XEventName.DestroyNotify: + return ToString (DestroyWindowEvent); + case XEventName.Expose: + return ToString (ExposeEvent); + case XEventName.FocusIn: + case XEventName.FocusOut: + return ToString (FocusChangeEvent); + case XEventName.GraphicsExpose: + return ToString (GraphicsExposeEvent); + case XEventName.GravityNotify: + return ToString (GravityEvent); + case XEventName.KeymapNotify: + return ToString (KeymapEvent); + case XEventName.MapNotify: + return ToString (MapEvent); + case XEventName.MappingNotify: + return ToString (MappingEvent); + case XEventName.MapRequest: + return ToString (MapRequestEvent); + case XEventName.MotionNotify: + return ToString (MotionEvent); + case XEventName.NoExpose: + return ToString (NoExposeEvent); + case XEventName.PropertyNotify: + return ToString (PropertyEvent); + case XEventName.ReparentNotify: + return ToString (ReparentEvent); + case XEventName.ResizeRequest: + return ToString (ResizeRequestEvent); + case XEventName.SelectionClear: + return ToString (SelectionClearEvent); + case XEventName.SelectionNotify: + return ToString (SelectionEvent); + case XEventName.SelectionRequest: + return ToString (SelectionRequestEvent); + case XEventName.UnmapNotify: + return ToString (UnmapEvent); + case XEventName.VisibilityNotify: + return ToString (VisibilityEvent); + case XEventName.EnterNotify: + case XEventName.LeaveNotify: + return ToString (CrossingEvent); + default: + return type.ToString (); + } + } + + public static string ToString (object ev) + { + string result = string.Empty; + Type type = ev.GetType (); + FieldInfo [] fields = type.GetFields (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance); + for (int i = 0; i < fields.Length; i++) { + if (result != string.Empty) { + result += ", "; + } + object value = fields [i].GetValue (ev); + result += fields [i].Name + "=" + (value == null ? "<null>" : value.ToString ()); + } + return type.Name + " (" + result + ")"; + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSetWindowAttributes { + internal IntPtr background_pixmap; + internal IntPtr background_pixel; + internal IntPtr border_pixmap; + internal IntPtr border_pixel; + internal Gravity bit_gravity; + internal Gravity win_gravity; + internal int backing_store; + internal IntPtr backing_planes; + internal IntPtr backing_pixel; + internal bool save_under; + internal IntPtr event_mask; + internal IntPtr do_not_propagate_mask; + internal bool override_redirect; + internal IntPtr colormap; + internal IntPtr cursor; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XWindowAttributes { + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal int depth; + internal IntPtr visual; + internal IntPtr root; + internal int c_class; + internal Gravity bit_gravity; + internal Gravity win_gravity; + internal int backing_store; + internal IntPtr backing_planes; + internal IntPtr backing_pixel; + internal bool save_under; + internal IntPtr colormap; + internal bool map_installed; + internal MapState map_state; + internal IntPtr all_event_masks; + internal IntPtr your_event_mask; + internal IntPtr do_not_propagate_mask; + internal bool override_direct; + internal IntPtr screen; + + public override string ToString () + { + return XEvent.ToString (this); + } + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XTextProperty { + internal string value; + internal IntPtr encoding; + internal int format; + internal IntPtr nitems; + } + + internal enum XWindowClass { + InputOutput = 1, + InputOnly = 2 + } + + internal enum XEventName { + KeyPress = 2, + KeyRelease = 3, + ButtonPress = 4, + ButtonRelease = 5, + MotionNotify = 6, + EnterNotify = 7, + LeaveNotify = 8, + FocusIn = 9, + FocusOut = 10, + KeymapNotify = 11, + Expose = 12, + GraphicsExpose = 13, + NoExpose = 14, + VisibilityNotify = 15, + CreateNotify = 16, + DestroyNotify = 17, + UnmapNotify = 18, + MapNotify = 19, + MapRequest = 20, + ReparentNotify = 21, + ConfigureNotify = 22, + ConfigureRequest = 23, + GravityNotify = 24, + ResizeRequest = 25, + CirculateNotify = 26, + CirculateRequest = 27, + PropertyNotify = 28, + SelectionClear = 29, + SelectionRequest = 30, + SelectionNotify = 31, + ColormapNotify = 32, + ClientMessage = 33, + MappingNotify = 34, + + LASTEvent + } + + [Flags] + internal enum SetWindowValuemask { + Nothing = 0, + BackPixmap = 1, + BackPixel = 2, + BorderPixmap = 4, + BorderPixel = 8, + BitGravity = 16, + WinGravity = 32, + BackingStore = 64, + BackingPlanes = 128, + BackingPixel = 256, + OverrideRedirect = 512, + SaveUnder = 1024, + EventMask = 2048, + DontPropagate = 4096, + ColorMap = 8192, + Cursor = 16384 + } + + internal enum SendEventValues { + PointerWindow = 0, + InputFocus = 1 + } + + internal enum CreateWindowArgs { + CopyFromParent = 0, + ParentRelative = 1, + InputOutput = 1, + InputOnly = 2 + } + + internal enum Gravity { + ForgetGravity = 0, + NorthWestGravity= 1, + NorthGravity = 2, + NorthEastGravity= 3, + WestGravity = 4, + CenterGravity = 5, + EastGravity = 6, + SouthWestGravity= 7, + SouthGravity = 8, + SouthEastGravity= 9, + StaticGravity = 10 + } + + internal enum XKeySym : uint { + XK_BackSpace = 0xFF08, + XK_Tab = 0xFF09, + XK_Clear = 0xFF0B, + XK_Return = 0xFF0D, + XK_Home = 0xFF50, + XK_Left = 0xFF51, + XK_Up = 0xFF52, + XK_Right = 0xFF53, + XK_Down = 0xFF54, + XK_Page_Up = 0xFF55, + XK_Page_Down = 0xFF56, + XK_End = 0xFF57, + XK_Begin = 0xFF58, + XK_Menu = 0xFF67, + XK_Shift_L = 0xFFE1, + XK_Shift_R = 0xFFE2, + XK_Control_L = 0xFFE3, + XK_Control_R = 0xFFE4, + XK_Caps_Lock = 0xFFE5, + XK_Shift_Lock = 0xFFE6, + XK_Meta_L = 0xFFE7, + XK_Meta_R = 0xFFE8, + XK_Alt_L = 0xFFE9, + XK_Alt_R = 0xFFEA, + XK_Super_L = 0xFFEB, + XK_Super_R = 0xFFEC, + XK_Hyper_L = 0xFFED, + XK_Hyper_R = 0xFFEE, + } + + [Flags] + internal enum EventMask { + NoEventMask = 0, + KeyPressMask = 1<<0, + KeyReleaseMask = 1<<1, + ButtonPressMask = 1<<2, + ButtonReleaseMask = 1<<3, + EnterWindowMask = 1<<4, + LeaveWindowMask = 1<<5, + PointerMotionMask = 1<<6, + PointerMotionHintMask = 1<<7, + Button1MotionMask = 1<<8, + Button2MotionMask = 1<<9, + Button3MotionMask = 1<<10, + Button4MotionMask = 1<<11, + Button5MotionMask = 1<<12, + ButtonMotionMask = 1<<13, + KeymapStateMask = 1<<14, + ExposureMask = 1<<15, + VisibilityChangeMask = 1<<16, + StructureNotifyMask = 1<<17, + ResizeRedirectMask = 1<<18, + SubstructureNotifyMask = 1<<19, + SubstructureRedirectMask= 1<<20, + FocusChangeMask = 1<<21, + PropertyChangeMask = 1<<22, + ColormapChangeMask = 1<<23, + OwnerGrabButtonMask = 1<<24 + } + + internal enum GrabMode { + GrabModeSync = 0, + GrabModeAsync = 1 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XStandardColormap { + internal IntPtr colormap; + internal IntPtr red_max; + internal IntPtr red_mult; + internal IntPtr green_max; + internal IntPtr green_mult; + internal IntPtr blue_max; + internal IntPtr blue_mult; + internal IntPtr base_pixel; + internal IntPtr visualid; + internal IntPtr killid; + } + + [StructLayout(LayoutKind.Sequential, Pack=2)] + internal struct XColor { + internal IntPtr pixel; + internal ushort red; + internal ushort green; + internal ushort blue; + internal byte flags; + internal byte pad; + } + + internal enum Atom { + AnyPropertyType = 0, + XA_PRIMARY = 1, + XA_SECONDARY = 2, + XA_ARC = 3, + XA_ATOM = 4, + XA_BITMAP = 5, + XA_CARDINAL = 6, + XA_COLORMAP = 7, + XA_CURSOR = 8, + XA_CUT_BUFFER0 = 9, + XA_CUT_BUFFER1 = 10, + XA_CUT_BUFFER2 = 11, + XA_CUT_BUFFER3 = 12, + XA_CUT_BUFFER4 = 13, + XA_CUT_BUFFER5 = 14, + XA_CUT_BUFFER6 = 15, + XA_CUT_BUFFER7 = 16, + XA_DRAWABLE = 17, + XA_FONT = 18, + XA_INTEGER = 19, + XA_PIXMAP = 20, + XA_POINT = 21, + XA_RECTANGLE = 22, + XA_RESOURCE_MANAGER = 23, + XA_RGB_COLOR_MAP = 24, + XA_RGB_BEST_MAP = 25, + XA_RGB_BLUE_MAP = 26, + XA_RGB_DEFAULT_MAP = 27, + XA_RGB_GRAY_MAP = 28, + XA_RGB_GREEN_MAP = 29, + XA_RGB_RED_MAP = 30, + XA_STRING = 31, + XA_VISUALID = 32, + XA_WINDOW = 33, + XA_WM_COMMAND = 34, + XA_WM_HINTS = 35, + XA_WM_CLIENT_MACHINE = 36, + XA_WM_ICON_NAME = 37, + XA_WM_ICON_SIZE = 38, + XA_WM_NAME = 39, + XA_WM_NORMAL_HINTS = 40, + XA_WM_SIZE_HINTS = 41, + XA_WM_ZOOM_HINTS = 42, + XA_MIN_SPACE = 43, + XA_NORM_SPACE = 44, + XA_MAX_SPACE = 45, + XA_END_SPACE = 46, + XA_SUPERSCRIPT_X = 47, + XA_SUPERSCRIPT_Y = 48, + XA_SUBSCRIPT_X = 49, + XA_SUBSCRIPT_Y = 50, + XA_UNDERLINE_POSITION = 51, + XA_UNDERLINE_THICKNESS = 52, + XA_STRIKEOUT_ASCENT = 53, + XA_STRIKEOUT_DESCENT = 54, + XA_ITALIC_ANGLE = 55, + XA_X_HEIGHT = 56, + XA_QUAD_WIDTH = 57, + XA_WEIGHT = 58, + XA_POINT_SIZE = 59, + XA_RESOLUTION = 60, + XA_COPYRIGHT = 61, + XA_NOTICE = 62, + XA_FONT_NAME = 63, + XA_FAMILY_NAME = 64, + XA_FULL_NAME = 65, + XA_CAP_HEIGHT = 66, + XA_WM_CLASS = 67, + XA_WM_TRANSIENT_FOR = 68, + + XA_LAST_PREDEFINED = 68 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XScreen { + internal IntPtr ext_data; + internal IntPtr display; + internal IntPtr root; + internal int width; + internal int height; + internal int mwidth; + internal int mheight; + internal int ndepths; + internal IntPtr depths; + internal int root_depth; + internal IntPtr root_visual; + internal IntPtr default_gc; + internal IntPtr cmap; + internal IntPtr white_pixel; + internal IntPtr black_pixel; + internal int max_maps; + internal int min_maps; + internal int backing_store; + internal bool save_unders; + internal IntPtr root_input_mask; + } + + [Flags] + internal enum ChangeWindowFlags { + CWX = 1<<0, + CWY = 1<<1, + CWWidth = 1<<2, + CWHeight = 1<<3, + CWBorderWidth = 1<<4, + CWSibling = 1<<5, + CWStackMode = 1<<6 + } + + internal enum StackMode { + Above = 0, + Below = 1, + TopIf = 2, + BottomIf = 3, + Opposite = 4 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XWindowChanges { + internal int x; + internal int y; + internal int width; + internal int height; + internal int border_width; + internal IntPtr sibling; + internal StackMode stack_mode; + } + + [Flags] + internal enum ColorFlags { + DoRed = 1<<0, + DoGreen = 1<<1, + DoBlue = 1<<2 + } + + internal enum NotifyMode { + NotifyNormal = 0, + NotifyGrab = 1, + NotifyUngrab = 2 + } + + internal enum NotifyDetail { + NotifyAncestor = 0, + NotifyVirtual = 1, + NotifyInferior = 2, + NotifyNonlinear = 3, + NotifyNonlinearVirtual = 4, + NotifyPointer = 5, + NotifyPointerRoot = 6, + NotifyDetailNone = 7 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MotifWmHints { + internal IntPtr flags; + internal IntPtr functions; + internal IntPtr decorations; + internal IntPtr input_mode; + internal IntPtr status; + + public override string ToString () + { + return string.Format("MotifWmHints <flags={0}, functions={1}, decorations={2}, input_mode={3}, status={4}", (MotifFlags) flags.ToInt32 (), (MotifFunctions) functions.ToInt32 (), (MotifDecorations) decorations.ToInt32 (), (MotifInputMode) input_mode.ToInt32 (), status.ToInt32 ()); + } + } + + [Flags] + internal enum MotifFlags { + Functions = 1, + Decorations = 2, + InputMode = 4, + Status = 8 + } + + [Flags] + internal enum MotifFunctions { + All = 0x01, + Resize = 0x02, + Move = 0x04, + Minimize = 0x08, + Maximize = 0x10, + Close = 0x20 + } + + [Flags] + internal enum MotifDecorations { + All = 0x01, + Border = 0x02, + ResizeH = 0x04, + Title = 0x08, + Menu = 0x10, + Minimize = 0x20, + Maximize = 0x40, + + } + + [Flags] + internal enum MotifInputMode { + Modeless = 0, + ApplicationModal = 1, + SystemModal = 2, + FullApplicationModal = 3 + } + + [Flags] + internal enum KeyMasks { + ShiftMask = (1 << 0), + LockMask = (1 << 1), + ControlMask = (1 << 2), + Mod1Mask = (1 << 3), + Mod2Mask = (1 << 4), + Mod3Mask = (1 << 5), + Mod4Mask = (1 << 6), + Mod5Mask = (1 << 7), + + ModMasks = Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask + } + + [Flags] + internal enum MouseKeyMasks { + Button1Mask = (1 << 8), + Button2Mask = (1 << 9), + Button3Mask = (1 << 10), + Button4Mask = (1 << 11), + Button5Mask = (1 << 12), + } + + [StructLayout (LayoutKind.Sequential)] + internal struct XModifierKeymap { + public int max_keypermod; + public IntPtr modifiermap; + } + + internal enum PropertyMode { + Replace = 0, + Prepend = 1, + Append = 2 + } + + [StructLayout (LayoutKind.Sequential)] + internal struct XKeyBoardState { + public int key_click_percent; + public int bell_percent; + public uint bell_pitch, bell_duration; + public IntPtr led_mask; + public int global_auto_repeat; + public AutoRepeats auto_repeats; + + [StructLayout (LayoutKind.Explicit)] + public struct AutoRepeats { + [FieldOffset (0)] + public byte first; + + [FieldOffset (31)] + public byte last; + } + } + + [Flags] + internal enum GCFunction { + GCFunction = 1<<0, + GCPlaneMask = 1<<1, + GCForeground = 1<<2, + GCBackground = 1<<3, + GCLineWidth = 1<<4, + GCLineStyle = 1<<5, + GCCapStyle = 1<<6, + GCJoinStyle = 1<<7, + GCFillStyle = 1<<8, + GCFillRule = 1<<9, + GCTile = 1<<10, + GCStipple = 1<<11, + GCTileStipXOrigin = 1<<12, + GCTileStipYOrigin = 1<<13, + GCFont = 1<<14, + GCSubwindowMode = 1<<15, + GCGraphicsExposures = 1<<16, + GCClipXOrigin = 1<<17, + GCClipYOrigin = 1<<18, + GCClipMask = 1<<19, + GCDashOffset = 1<<20, + GCDashList = 1<<21, + GCArcMode = 1<<22 + } + + internal enum GCJoinStyle { + JoinMiter = 0, + JoinRound = 1, + JoinBevel = 2 + } + + internal enum GCLineStyle { + LineSolid = 0, + LineOnOffDash = 1, + LineDoubleDash = 2 + } + + internal enum GCCapStyle { + CapNotLast = 0, + CapButt = 1, + CapRound = 2, + CapProjecting = 3 + } + + internal enum GCFillStyle { + FillSolid = 0, + FillTiled = 1, + FillStippled = 2, + FillOpaqueStppled = 3 + } + + internal enum GCFillRule { + EvenOddRule = 0, + WindingRule = 1 + } + + internal enum GCArcMode { + ArcChord = 0, + ArcPieSlice = 1 + } + + internal enum GCSubwindowMode { + ClipByChildren = 0, + IncludeInferiors = 1 + } + + [StructLayout (LayoutKind.Sequential)] + internal struct XGCValues { + internal GXFunction function; + internal IntPtr plane_mask; + internal IntPtr foreground; + internal IntPtr background; + internal int line_width; + internal GCLineStyle line_style; + internal GCCapStyle cap_style; + internal GCJoinStyle join_style; + internal GCFillStyle fill_style; + internal GCFillRule fill_rule; + internal GCArcMode arc_mode; + internal IntPtr tile; + internal IntPtr stipple; + internal int ts_x_origin; + internal int ts_y_origin; + internal IntPtr font; + internal GCSubwindowMode subwindow_mode; + internal bool graphics_exposures; + internal int clip_x_origin; + internal int clib_y_origin; + internal IntPtr clip_mask; + internal int dash_offset; + internal byte dashes; + } + + internal enum GXFunction { + GXclear = 0x0, /* 0 */ + GXand = 0x1, /* src AND dst */ + GXandReverse = 0x2, /* src AND NOT dst */ + GXcopy = 0x3, /* src */ + GXandInverted = 0x4, /* NOT src AND dst */ + GXnoop = 0x5, /* dst */ + GXxor = 0x6, /* src XOR dst */ + GXor = 0x7, /* src OR dst */ + GXnor = 0x8, /* NOT src AND NOT dst */ + GXequiv = 0x9, /* NOT src XOR dst */ + GXinvert = 0xa, /* NOT dst */ + GXorReverse = 0xb, /* src OR NOT dst */ + GXcopyInverted = 0xc, /* NOT src */ + GXorInverted = 0xd, /* NOT src OR dst */ + GXnand = 0xe, /* NOT src OR NOT dst */ + GXset = 0xf /* 1 */ + } + + internal enum NetWindowManagerState { + Remove = 0, + Add = 1, + Toggle = 2 + } + + internal enum RevertTo { + None = 0, + PointerRoot = 1, + Parent = 2 + } + + internal enum MapState { + IsUnmapped = 0, + IsUnviewable = 1, + IsViewable = 2 + } + + internal enum CursorFontShape { + XC_X_cursor = 0, + XC_arrow = 2, + XC_based_arrow_down = 4, + XC_based_arrow_up = 6, + XC_boat = 8, + XC_bogosity = 10, + XC_bottom_left_corner = 12, + XC_bottom_right_corner = 14, + XC_bottom_side = 16, + XC_bottom_tee = 18, + XC_box_spiral = 20, + XC_center_ptr = 22, + + XC_circle = 24, + XC_clock = 26, + XC_coffee_mug = 28, + XC_cross = 30, + XC_cross_reverse = 32, + XC_crosshair = 34, + XC_diamond_cross = 36, + XC_dot = 38, + XC_dotbox = 40, + XC_double_arrow = 42, + XC_draft_large = 44, + XC_draft_small = 46, + + XC_draped_box = 48, + XC_exchange = 50, + XC_fleur = 52, + XC_gobbler = 54, + XC_gumby = 56, + XC_hand1 = 58, + XC_hand2 = 60, + XC_heart = 62, + XC_icon = 64, + XC_iron_cross = 66, + XC_left_ptr = 68, + XC_left_side = 70, + + XC_left_tee = 72, + XC_left_button = 74, + XC_ll_angle = 76, + XC_lr_angle = 78, + XC_man = 80, + XC_middlebutton = 82, + XC_mouse = 84, + XC_pencil = 86, + XC_pirate = 88, + XC_plus = 90, + XC_question_arrow = 92, + XC_right_ptr = 94, + + XC_right_side = 96, + XC_right_tee = 98, + XC_rightbutton = 100, + XC_rtl_logo = 102, + XC_sailboat = 104, + XC_sb_down_arrow = 106, + XC_sb_h_double_arrow = 108, + XC_sb_left_arrow = 110, + XC_sb_right_arrow = 112, + XC_sb_up_arrow = 114, + XC_sb_v_double_arrow = 116, + XC_sb_shuttle = 118, + + XC_sizing = 120, + XC_spider = 122, + XC_spraycan = 124, + XC_star = 126, + XC_target = 128, + XC_tcross = 130, + XC_top_left_arrow = 132, + XC_top_left_corner = 134, + XC_top_right_corner = 136, + XC_top_side = 138, + XC_top_tee = 140, + XC_trek = 142, + + XC_ul_angle = 144, + XC_umbrella = 146, + XC_ur_angle = 148, + XC_watch = 150, + XC_xterm = 152, + XC_num_glyphs = 154 + } + + internal enum SystrayRequest { + SYSTEM_TRAY_REQUEST_DOCK = 0, + SYSTEM_TRAY_BEGIN_MESSAGE = 1, + SYSTEM_TRAY_CANCEL_MESSAGE = 2 + } + + internal enum NetWmStateRequest { + _NET_WM_STATE_REMOVE = 0, + _NET_WM_STATE_ADD = 1, + _NET_WM_STATE_TOGGLE = 2 + } + + internal enum NetWmMoveResize { + _NET_WM_MOVERESIZE_SIZE_TOPLEFT = 0, + _NET_WM_MOVERESIZE_SIZE_TOP = 1, + _NET_WM_MOVERESIZE_SIZE_TOPRIGHT = 2, + _NET_WM_MOVERESIZE_SIZE_RIGHT = 3, + _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT = 4, + _NET_WM_MOVERESIZE_SIZE_BOTTOM = 5, + _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT = 6, + _NET_WM_MOVERESIZE_SIZE_LEFT = 7, + _NET_WM_MOVERESIZE_MOVE = 8, + _NET_WM_MOVERESIZE_SIZE_KEYBOARD = 9, + _NET_WM_MOVERESIZE_MOVE_KEYBOARD = 10, + _NET_WM_MOVERESIZE_CANCEL = 11 + } + + [Flags] + internal enum XSizeHintsFlags { + USPosition = (1 << 0), + USSize = (1 << 1), + PPosition = (1 << 2), + PSize = (1 << 3), + PMinSize = (1 << 4), + PMaxSize = (1 << 5), + PResizeInc = (1 << 6), + PAspect = (1 << 7), + PAllHints = (PPosition | PSize | PMinSize | PMaxSize | PResizeInc | PAspect), + PBaseSize = (1 << 8), + PWinGravity = (1 << 9), + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XSizeHints { + internal IntPtr flags; + internal int x; + internal int y; + internal int width; + internal int height; + internal int min_width; + internal int min_height; + internal int max_width; + internal int max_height; + internal int width_inc; + internal int height_inc; + internal int min_aspect_x; + internal int min_aspect_y; + internal int max_aspect_x; + internal int max_aspect_y; + internal int base_width; + internal int base_height; + internal int win_gravity; + } + + [Flags] + internal enum XWMHintsFlags { + InputHint = (1 << 0), + StateHint = (1 << 1), + IconPixmapHint = (1 << 2), + IconWindowHint = (1 << 3), + IconPositionHint = (1 << 4), + IconMaskHint = (1 << 5), + WindowGroupHint = (1 << 6), + AllHints = (InputHint | StateHint | IconPixmapHint | IconWindowHint | IconPositionHint | IconMaskHint | WindowGroupHint) + } + + internal enum XInitialState { + DontCareState = 0, + NormalState = 1, + ZoomState = 2, + IconicState = 3, + InactiveState = 4 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XWMHints { + internal IntPtr flags; + internal bool input; + internal XInitialState initial_state; + internal IntPtr icon_pixmap; + internal IntPtr icon_window; + internal int icon_x; + internal int icon_y; + internal IntPtr icon_mask; + internal IntPtr window_group; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct XIconSize { + internal int min_width; + internal int min_height; + internal int max_width; + internal int max_height; + internal int width_inc; + internal int height_inc; + } + + internal struct CaretStruct { + internal Timer Timer; // Blink interval + internal IntPtr Hwnd; // Window owning the caret + internal IntPtr Window; // Actual X11 handle of the window + internal int X; // X position of the caret + internal int Y; // Y position of the caret + internal int Width; // Width of the caret; if no image used + internal int Height; // Height of the caret, if no image used + internal bool Visible; // Is caret visible? + internal bool On; // Caret blink display state: On/Off + internal IntPtr gc; // Graphics context + internal bool Paused; // Don't update right now + } + + internal struct HoverStruct { + internal Timer Timer; // for hovering + internal IntPtr Window; // Last window we entered; used to generate WM_MOUSEHOVER (handle is X11 handle) + internal int X; // Last MouseMove X coordinate; used to generate WM_MOUSEHOVER + internal int Y; // Last MouseMove Y coordinate; used to generate WM_MOUSEHOVER + internal Size Size; // Size of the rectangle the mouse has to stay in to generate hover + internal int Interval; // in milliseconds, how long to hold before hover is generated + internal IntPtr Atom; // X Atom + } + + internal struct ClickStruct { + internal IntPtr Hwnd; // + internal Msg Message; // + internal IntPtr wParam; // + internal IntPtr lParam; // + internal long Time; // Last time we received a mouse click + internal bool Pending; // True if we haven't sent the last mouse click + } + + internal struct GrabStruct { + internal bool Confined; // Is the current grab (if any) confined to grab_area? + internal IntPtr Hwnd; // The window that is grabbed + internal Rectangle Area; // The area the current grab is confined to + } + + internal delegate int XErrorHandler(IntPtr DisplayHandle, ref XErrorEvent error_event); + + internal enum XRequest : byte { + X_CreateWindow = 1, + X_ChangeWindowAttributes = 2, + X_GetWindowAttributes = 3, + X_DestroyWindow = 4, + X_DestroySubwindows = 5, + X_ChangeSaveSet = 6, + X_ReparentWindow = 7, + X_MapWindow = 8, + X_MapSubwindows = 9, + X_UnmapWindow = 10, + X_UnmapSubwindows = 11, + X_ConfigureWindow = 12, + X_CirculateWindow = 13, + X_GetGeometry = 14, + X_QueryTree = 15, + X_InternAtom = 16, + X_GetAtomName = 17, + X_ChangeProperty = 18, + X_DeleteProperty = 19, + X_GetProperty = 20, + X_ListProperties = 21, + X_SetSelectionOwner = 22, + X_GetSelectionOwner = 23, + X_ConvertSelection = 24, + X_SendEvent = 25, + X_GrabPointer = 26, + X_UngrabPointer = 27, + X_GrabButton = 28, + X_UngrabButton = 29, + X_ChangeActivePointerGrab = 30, + X_GrabKeyboard = 31, + X_UngrabKeyboard = 32, + X_GrabKey = 33, + X_UngrabKey = 34, + X_AllowEvents = 35, + X_GrabServer = 36, + X_UngrabServer = 37, + X_QueryPointer = 38, + X_GetMotionEvents = 39, + X_TranslateCoords = 40, + X_WarpPointer = 41, + X_SetInputFocus = 42, + X_GetInputFocus = 43, + X_QueryKeymap = 44, + X_OpenFont = 45, + X_CloseFont = 46, + X_QueryFont = 47, + X_QueryTextExtents = 48, + X_ListFonts = 49, + X_ListFontsWithInfo = 50, + X_SetFontPath = 51, + X_GetFontPath = 52, + X_CreatePixmap = 53, + X_FreePixmap = 54, + X_CreateGC = 55, + X_ChangeGC = 56, + X_CopyGC = 57, + X_SetDashes = 58, + X_SetClipRectangles = 59, + X_FreeGC = 60, + X_ClearArea = 61, + X_CopyArea = 62, + X_CopyPlane = 63, + X_PolyPoint = 64, + X_PolyLine = 65, + X_PolySegment = 66, + X_PolyRectangle = 67, + X_PolyArc = 68, + X_FillPoly = 69, + X_PolyFillRectangle = 70, + X_PolyFillArc = 71, + X_PutImage = 72, + X_GetImage = 73, + X_PolyText8 = 74, + X_PolyText16 = 75, + X_ImageText8 = 76, + X_ImageText16 = 77, + X_CreateColormap = 78, + X_FreeColormap = 79, + X_CopyColormapAndFree = 80, + X_InstallColormap = 81, + X_UninstallColormap = 82, + X_ListInstalledColormaps = 83, + X_AllocColor = 84, + X_AllocNamedColor = 85, + X_AllocColorCells = 86, + X_AllocColorPlanes = 87, + X_FreeColors = 88, + X_StoreColors = 89, + X_StoreNamedColor = 90, + X_QueryColors = 91, + X_LookupColor = 92, + X_CreateCursor = 93, + X_CreateGlyphCursor = 94, + X_FreeCursor = 95, + X_RecolorCursor = 96, + X_QueryBestSize = 97, + X_QueryExtension = 98, + X_ListExtensions = 99, + X_ChangeKeyboardMapping = 100, + X_GetKeyboardMapping = 101, + X_ChangeKeyboardWidget = 102, + X_GetKeyboardWidget = 103, + X_Bell = 104, + X_ChangePointerWidget = 105, + X_GetPointerWidget = 106, + X_SetScreenSaver = 107, + X_GetScreenSaver = 108, + X_ChangeHosts = 109, + X_ListHosts = 110, + X_SetAccessWidget = 111, + X_SetCloseDownMode = 112, + X_KillClient = 113, + X_RotateProperties = 114, + X_ForceScreenSaver = 115, + X_SetPointerMapping = 116, + X_GetPointerMapping = 117, + X_SetModifierMapping = 118, + X_GetModifierMapping = 119, + X_NoOperation = 127 + } + + [Flags] + internal enum XIMProperties { + XIMPreeditArea = 0x0001, + XIMPreeditCallbacks = 0x0002, + XIMPreeditPosition = 0x0004, + XIMPreeditNothing = 0x0008, + XIMPreeditNone = 0x0010, + XIMStatusArea = 0x0100, + XIMStatusCallbacks = 0x0200, + XIMStatusNothing = 0x0400, + XIMStatusNone = 0x0800, + } + + [Flags] + internal enum WindowType { + Client = 1, + Whole = 2, + Both = 3 + } + + internal enum XEmbedMessage { + EmbeddedNotify = 0, + WindowActivate = 1, + WindowDeactivate = 2, + RequestFocus = 3, + FocusIn = 4, + FocusOut = 5, + FocusNext = 6, + FocusPrev = 7, + /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */ + ModalityOn = 10, + ModalityOff = 11, + RegisterAccelerator = 12, + UnregisterAccelerator = 13, + ActivateAccelerator = 14 + } + + [StructLayout (LayoutKind.Sequential)] + internal struct XcursorImage + { + private int version; + public int size; /* nominal size for matching */ + public int width; /* actual width */ + public int height; /* actual height */ + public int xhot; /* hot spot x (must be inside image) */ + public int yhot; /* hot spot y (must be inside image) */ + public int delay; /* hot spot y (must be inside image) */ + public IntPtr pixels; /* pointer to pixels */ + + public override string ToString () + { + return string.Format ("XCursorImage (version: {0}, size: {1}, width: {2}, height: {3}, xhot: {4}, yhot: {5}, delay: {6}, pixels: {7}", + version, size, width, height, xhot, yhot, delay, pixels); + } + } ; + + [StructLayout (LayoutKind.Sequential)] + internal struct XcursorImages + { + public int nimage; /* number of images */ + public IntPtr images; /* array of XcursorImage pointers */ + } + + [StructLayout (LayoutKind.Sequential)] + internal struct XIMStyles + { + public ushort count_styles; + public IntPtr supported_styles; + } + + [StructLayout (LayoutKind.Sequential)] + [Serializable] + internal class XPoint + { + public short X; + public short Y; + } + + [StructLayout (LayoutKind.Sequential)] + [Serializable] + internal class XIMCallback + { + public IntPtr client_data; + public XIMProc callback; + [NonSerialized] + GCHandle gch; + + public XIMCallback (IntPtr clientData, XIMProc proc) + { + this.client_data = clientData; + this.gch = GCHandle.Alloc (proc); + this.callback = proc; + } + + ~XIMCallback () + { + gch.Free (); + } + } + + internal enum XIMFeedback + { + Reverse = 1, + Underline = 2, + Highlight = 4, + Primary = 32, + Secondary = 64, + Tertiary = 128, + } + + internal struct XIMFeedbackStruct + { + public byte FeedbackMask; // one or more of XIMFeedback enum + } + + internal struct XIMText + { + public ushort Length; + public IntPtr Feedback; // to XIMFeedbackStruct + public bool EncodingIsWChar; + public IntPtr String; // it could be either char* or wchar_t* + } + + internal struct XIMPreeditDrawCallbackStruct + { + public int Caret; + public int ChangeFirst; + public int ChangeLength; + public IntPtr Text; // to XIMText + } + + internal enum XIMCaretDirection + { + XIMForwardChar, + XIMBackwardChar, + XIMForwardWord, + XIMBackwardWord, + XIMCaretUp, + XIMCaretDown, + XIMNextLine, + XIMPreviousLine, + XIMLineStart, + XIMLineEnd, + XIMAbsolutePosition, + XIMDontChange + } + + internal enum XIMCaretStyle + { + IsInvisible, + IsPrimary, + IsSecondary + } + + internal struct XIMPreeditCaretCallbackStruct + { + public int Position; + public XIMCaretDirection Direction; + public XIMCaretStyle Style; + } + + // only PreeditStartCallback requires return value though. + internal delegate int XIMProc (IntPtr xim, IntPtr clientData, IntPtr callData); + + internal static class XNames + { + public const string XNVaNestedList = "XNVaNestedList"; + public const string XNQueryInputStyle = "queryInputStyle"; + public const string XNClientWindow = "clientWindow"; + public const string XNInputStyle = "inputStyle"; + public const string XNFocusWindow = "focusWindow"; + + // XIMPreeditCallbacks delegate names. + public const string XNPreeditStartCallback = "preeditStartCallback"; + public const string XNPreeditDoneCallback = "preeditDoneCallback"; + public const string XNPreeditDrawCallback = "preeditDrawCallback"; + public const string XNPreeditCaretCallback = "preeditCaretCallback"; + public const string XNPreeditStateNotifyCallback = "preeditStateNotifyCallback"; + public const string XNPreeditAttributes = "preeditAttributes"; + // XIMStatusCallbacks delegate names. + public const string XNStatusStartCallback = "statusStartCallback"; + public const string XNStatusDoneCallback = "statusDoneCallback"; + public const string XNStatusDrawCallback = "statusDrawCallback"; + public const string XNStatusAttributes = "statusAttributes"; + + public const string XNArea = "area"; + public const string XNAreaNeeded = "areaNeeded"; + public const string XNSpotLocation = "spotLocation"; + public const string XNFontSet = "fontSet"; + } + + [StructLayout (LayoutKind.Sequential)] + internal struct XineramaScreenInfo + { + public int screen_number; + public short x_org; + public short y_org; + public short width; + public short height; + } +} diff --git a/source/ShiftUI/Internal/X11ThreadQueue.cs b/source/ShiftUI/Internal/X11ThreadQueue.cs new file mode 100644 index 0000000..b68d74c --- /dev/null +++ b/source/ShiftUI/Internal/X11ThreadQueue.cs @@ -0,0 +1,502 @@ +// 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: +// Jackson Harper ([email protected]) +// Peter Dennis Bartok ([email protected]) +// Chris Toshok ([email protected]) +// + +using System; +using System.Threading; +using System.Collections; + +namespace ShiftUI.X11Internal { + + internal class X11ThreadQueue { + + XEventQueue xqueue; + PaintQueue paint_queue; + ConfigureQueue configure_queue; + ArrayList timer_list; + Thread thread; + bool quit_posted; + bool dispatch_idle; + bool need_dispatch_idle = true; + object lockobj = new object (); + + static readonly int InitialXEventQueueSize = 128; + static readonly int InitialHwndQueueSize = 50; + + public X11ThreadQueue (Thread thread) + { + xqueue = new XEventQueue (InitialXEventQueueSize); + paint_queue = new PaintQueue (InitialHwndQueueSize); + configure_queue = new ConfigureQueue (InitialHwndQueueSize); + timer_list = new ArrayList (); + this.thread = thread; + this.quit_posted = false; + this.dispatch_idle = true; + } + + public int CountUnlocked { + get { return xqueue.Count + paint_queue.Count; } + } + + public Thread Thread { + get { return thread; } + } + + public void EnqueueUnlocked (XEvent xevent) + { + switch (xevent.type) { + case XEventName.KeyPress: + case XEventName.KeyRelease: + case XEventName.ButtonPress: + case XEventName.ButtonRelease: + NeedDispatchIdle = true; + break; + case XEventName.MotionNotify: + if (xqueue.Count > 0) { + XEvent peek = xqueue.Peek (); + if (peek.AnyEvent.type == XEventName.MotionNotify) + return; // we've already got a pending motion notify. + } + + // otherwise fall through and enqueue + // the event. + break; + } + + xqueue.Enqueue (xevent); + // wake up any thread blocking in DequeueUnlocked + Monitor.PulseAll (lockobj); + } + + public void Enqueue (XEvent xevent) + { + lock (lockobj) { + EnqueueUnlocked (xevent); + } + } + + public bool Dequeue (out XEvent xevent) + { + StartOver: + bool got_xevent = false; + + lock (lockobj) { + if (xqueue.Count > 0) { + got_xevent = true; + xevent = xqueue.Dequeue (); + } + else + xevent = new XEvent (); /* not strictly needed, but mcs complains */ + } + + if (got_xevent) { + if (xevent.AnyEvent.type == XEventName.Expose) { +#if spew + Console.Write ("E"); + Console.Out.Flush (); +#endif + X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window); + hwnd.AddExpose (xevent.AnyEvent.window == hwnd.ClientWindow, + xevent.ExposeEvent.x, xevent.ExposeEvent.y, + xevent.ExposeEvent.width, xevent.ExposeEvent.height); + goto StartOver; + } + else if (xevent.AnyEvent.type == XEventName.ConfigureNotify) { +#if spew + Console.Write ("C"); + Console.Out.Flush (); +#endif + X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow (xevent.AnyEvent.window); + hwnd.AddConfigureNotify (xevent); + goto StartOver; + } + else { +#if spew + Console.Write ("X"); + Console.Out.Flush (); +#endif + /* it was an event we can deal with directly, return it */ + return true; + } + } + else { + if (paint_queue.Count > 0) { + xevent = paint_queue.Dequeue (); +#if spew + Console.Write ("e"); + Console.Out.Flush (); +#endif + return true; + } + else if (configure_queue.Count > 0) { + xevent = configure_queue.Dequeue (); +#if spew + Console.Write ("c"); + Console.Out.Flush (); +#endif + return true; + } + } + + if (dispatch_idle && need_dispatch_idle) { + OnIdle (EventArgs.Empty); + need_dispatch_idle = false; + } + + lock (lockobj) { + if (CountUnlocked > 0) + goto StartOver; + + if (Monitor.Wait (lockobj, NextTimeout (), true)) { + // the lock was reaquired before the + // timeout. meaning an event was + // enqueued by X11Display.XEventThread. + goto StartOver; + } + else { + CheckTimers (); + return false; + } + } + } + + public void RemovePaint (Hwnd hwnd) + { + paint_queue.Remove (hwnd); + } + + public void AddPaint (Hwnd hwnd) + { + paint_queue.Enqueue (hwnd); + } + + public void AddConfigure (Hwnd hwnd) + { + configure_queue.Enqueue (hwnd); + } + + public ConfigureQueue Configure { + get { return configure_queue; } + } + + public PaintQueue Paint { + get { return paint_queue; } + } + + public void Lock () + { + Monitor.Enter (lockobj); + } + + public void Unlock () + { + Monitor.Exit (lockobj); + } + + private int NextTimeout () + { + int timeout = Int32.MaxValue; + DateTime now = DateTime.UtcNow; + + foreach (Timer timer in timer_list) { + int next = (int) (timer.Expires - now).TotalMilliseconds; + if (next < 0) + return 0; // Have a timer that has already expired + + if (next < timeout) + timeout = next; + } + + if (timeout < Timer.Minimum) { + timeout = Timer.Minimum; + } + + if (timeout == Int32.MaxValue) + timeout = Timeout.Infinite; + + return timeout; + } + + public void CheckTimers () + { + int count; + DateTime now = DateTime.UtcNow; + + count = timer_list.Count; + + if (count == 0) + return; + + for (int i = 0; i < timer_list.Count; i++) { + Timer timer; + + timer = (Timer) timer_list [i]; + + if (timer.Enabled && timer.Expires <= now) { + timer.Update (now); + timer.FireTick (); + } + } + } + + public void SetTimer (Timer timer) + { + lock (lockobj) { + timer_list.Add (timer); + + // we need to wake up any thread waiting in DequeueUnlocked, + // since it might need to wait for a different amount of time. + Monitor.PulseAll (lockobj); + } + + } + + public void KillTimer (Timer timer) + { + lock (lockobj) { + timer_list.Remove (timer); + + // we need to wake up any thread waiting in DequeueUnlocked, + // since it might need to wait for a different amount of time. + Monitor.PulseAll (lockobj); + } + } + + public event EventHandler Idle; + public void OnIdle (EventArgs e) + { + if (Idle != null) + Idle (thread, e); + } + + public bool NeedDispatchIdle { + get { return need_dispatch_idle; } + set { need_dispatch_idle = value; } + } + + public bool DispatchIdle { + get { return dispatch_idle; } + set { dispatch_idle = value; } + } + + public bool PostQuitState { + get { return quit_posted; } + set { quit_posted = value; } + } + + public abstract class HwndEventQueue { + protected ArrayList hwnds; +#if DebugHwndEventQueue + protected ArrayList stacks; +#endif + public HwndEventQueue (int size) + { + hwnds = new ArrayList (size); +#if DebugHwndEventQueue + stacks = new ArrayList (size); +#endif + } + + public int Count { + get { return hwnds.Count; } + } + + public void Enqueue (Hwnd hwnd) + { + if (hwnds.Contains (hwnd)) { +#if DebugHwndEventQueue + Console.WriteLine ("hwnds can only appear in the queue once."); + Console.WriteLine (Environment.StackTrace); + Console.WriteLine ("originally added here:"); + Console.WriteLine (stacks[hwnds.IndexOf (hwnd)]); +#endif + + return; + } + hwnds.Add(hwnd); +#if DebugHwndEventQueue + stacks.Add(Environment.StackTrace); +#endif + } + + public void Remove(Hwnd hwnd) + { +#if DebugHwndEventQueue + int index = hwnds.IndexOf(hwnd); + if (index != -1) + stacks.RemoveAt(index); +#endif + hwnds.Remove(hwnd); + } + + protected abstract XEvent Peek (); + + public virtual XEvent Dequeue () + { + if (hwnds.Count == 0) + throw new Exception ("Attempt to dequeue empty queue."); + + return Peek (); + } + } + + + public class ConfigureQueue : HwndEventQueue + { + public ConfigureQueue (int size) : base (size) + { + } + + protected override XEvent Peek () + { + X11Hwnd hwnd = (X11Hwnd)hwnds[0]; + + XEvent xevent = new XEvent (); + xevent.AnyEvent.type = XEventName.ConfigureNotify; + + xevent.ConfigureEvent.window = hwnd.ClientWindow; + xevent.ConfigureEvent.x = hwnd.X; + xevent.ConfigureEvent.y = hwnd.Y; + xevent.ConfigureEvent.width = hwnd.Width; + xevent.ConfigureEvent.height = hwnd.Height; + + return xevent; + } + + public override XEvent Dequeue () + { + XEvent xev = base.Dequeue (); + + + hwnds.RemoveAt(0); +#if DebugHwndEventQueue + stacks.RemoveAt(0); +#endif + + return xev; + } + } + + public class PaintQueue : HwndEventQueue + { + public PaintQueue (int size) : base (size) + { + } + + protected override XEvent Peek () + { + X11Hwnd hwnd = (X11Hwnd)hwnds[0]; + + XEvent xevent = new XEvent (); + + xevent.AnyEvent.type = XEventName.Expose; + + if (hwnd.PendingExpose) { + xevent.ExposeEvent.window = hwnd.ClientWindow; + } else { + xevent.ExposeEvent.window = hwnd.WholeWindow; + xevent.ExposeEvent.x = hwnd.nc_invalid.X; + xevent.ExposeEvent.y = hwnd.nc_invalid.Y; + xevent.ExposeEvent.width = hwnd.nc_invalid.Width; + xevent.ExposeEvent.height = hwnd.nc_invalid.Height; + } + + return xevent; + } + + // don't override Dequeue like ConfigureQueue does. + } + + /* a circular queue for holding X events for processing by GetMessage */ + private class XEventQueue { + + XEvent[] xevents; + int head; + int tail; + int size; + + public XEventQueue (int initial_size) + { + if (initial_size % 2 != 0) + throw new Exception ("XEventQueue must be a power of 2 size"); + + xevents = new XEvent [initial_size]; + } + + public int Count { + get { return size; } + } + + public void Enqueue (XEvent xevent) + { + if (size == xevents.Length) + Grow (); + + xevents [tail] = xevent; + tail = (tail + 1) & (xevents.Length - 1); + size++; + } + + public XEvent Dequeue () + { + if (size < 1) + throw new Exception ("Attempt to dequeue empty queue."); + + XEvent res = xevents [head]; + head = (head + 1) & (xevents.Length - 1); + size--; + return res; + } + + public XEvent Peek() + { + if (size < 1) + throw new Exception ("Attempt to peek at empty queue."); + + return xevents[head]; + } + + private void Grow () + { + int newcap = (xevents.Length * 2); + XEvent [] na = new XEvent [newcap]; + + if (head + size > xevents.Length) { + Array.Copy (xevents, head, na, 0, xevents.Length - head); + Array.Copy (xevents, 0, na, xevents.Length - head, head + size - xevents.Length); + } + else { + Array.Copy (xevents, head, na, 0, size); + } + + xevents = na; + head = 0; + tail = head + size; + } + } + } +} + diff --git a/source/ShiftUI/Internal/XEventQueue.cs b/source/ShiftUI/Internal/XEventQueue.cs new file mode 100644 index 0000000..526c14c --- /dev/null +++ b/source/ShiftUI/Internal/XEventQueue.cs @@ -0,0 +1,261 @@ +// 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. +// +// ShiftUI.XEventQueue +// +// Authors: +// Jackson Harper ([email protected]) +// Peter Dennis Bartok ([email protected]) +// + +using System; +using System.Threading; +using System.Collections; + +namespace ShiftUI { + + internal class XEventQueue { + + private XQueue xqueue; + private XQueue lqueue; // Events inserted from threads other then the main X thread + private PaintQueue paint; // Paint-only queue + internal ArrayList timer_list; + private Thread thread; + private bool dispatch_idle; + + private static readonly int InitialXEventSize = 100; + private static readonly int InitialLXEventSize = 10; + private static readonly int InitialPaintSize = 50; + + public XEventQueue (Thread thread) { + xqueue = new XQueue (InitialXEventSize); + lqueue = new XQueue (InitialLXEventSize); + paint = new PaintQueue(InitialPaintSize); + timer_list = new ArrayList (); + this.thread = thread; + this.dispatch_idle = true; + } + + public int Count { + get { + lock (lqueue) { + return xqueue.Count + lqueue.Count; + } + } + } + + public PaintQueue Paint { + get { + return paint; + } + } + + public Thread Thread { + get { + return thread; + } + } + + public void Enqueue (XEvent xevent) + { + if (Thread.CurrentThread != thread) { + Console.WriteLine ("Hwnd.Queue.Enqueue called from a different thread without locking."); + Console.WriteLine (Environment.StackTrace); + } + + xqueue.Enqueue (xevent); + } + + public void EnqueueLocked (XEvent xevent) + { + lock (lqueue) { + lqueue.Enqueue (xevent); + } + } + + public XEvent Dequeue () + { + if (Thread.CurrentThread != thread) { + Console.WriteLine ("Hwnd.Queue.Dequeue called from a different thread without locking."); + Console.WriteLine (Environment.StackTrace); + } + + if (xqueue.Count == 0) { + lock (lqueue) { + return lqueue.Dequeue (); + } + } + return xqueue.Dequeue (); + } + + public XEvent Peek() + { + if (Thread.CurrentThread != thread) { + Console.WriteLine ("Hwnd.Queue.Peek called from a different thread without locking."); + Console.WriteLine (Environment.StackTrace); + } + + if (xqueue.Count == 0) { + lock (lqueue) { + return lqueue.Peek (); + } + } + return xqueue.Peek (); + } + + public bool DispatchIdle { + get { + return dispatch_idle; + } + set { + dispatch_idle = value; + } + } + + public class PaintQueue { + + private ArrayList hwnds; + private XEvent xevent; + + public PaintQueue (int size) { + hwnds = new ArrayList (size); + xevent = new XEvent (); + xevent.AnyEvent.type = XEventName.Expose; + } + + public int Count { + get { + lock (hwnds) { + return hwnds.Count; + } + } + } + + public void Enqueue (Hwnd hwnd) { + lock (hwnds) { + hwnds.Add (hwnd); + } + } + + public void Remove(Hwnd hwnd) { + if (!hwnd.expose_pending && !hwnd.nc_expose_pending) { + lock (hwnds) { + hwnds.Remove (hwnd); + } + } + } + + public XEvent Dequeue () { + Hwnd hwnd; + IEnumerator next; + + lock (hwnds) { + if (hwnds.Count == 0) { + xevent.ExposeEvent.window = IntPtr.Zero; + return xevent; + } + + next = hwnds.GetEnumerator (); + next.MoveNext (); + hwnd = (Hwnd)next.Current; + + // We only remove the event from the queue if we have one expose left since + // a single 'entry in our queue may be for both NC and Client exposed + if (!(hwnd.nc_expose_pending && hwnd.expose_pending)) { + hwnds.Remove (hwnd); + } + if (hwnd.expose_pending) { + xevent.ExposeEvent.window = hwnd.client_window; +#if not + xevent.ExposeEvent.x = hwnd.invalid.X; + xevent.ExposeEvent.y = hwnd.invalid.Y; + xevent.ExposeEvent.width = hwnd.invalid.Width; + xevent.ExposeEvent.height = hwnd.invalid.Height; +#endif + return xevent; + } else { + xevent.ExposeEvent.window = hwnd.whole_window; + xevent.ExposeEvent.x = hwnd.nc_invalid.X; + xevent.ExposeEvent.y = hwnd.nc_invalid.Y; + xevent.ExposeEvent.width = hwnd.nc_invalid.Width; + xevent.ExposeEvent.height = hwnd.nc_invalid.Height; + return xevent; + } + } + } + } + + private class XQueue { + + private XEvent [] xevents; + private int head; + private int tail; + private int size; + + public XQueue (int size) + { + xevents = new XEvent [size]; + } + + public int Count { + get { return size; } + } + + public void Enqueue (XEvent xevent) + { + if (size == xevents.Length) + Grow (); + + xevents [tail] = xevent; + tail = (tail + 1) % xevents.Length; + size++; + } + + public XEvent Dequeue () + { + if (size < 1) + throw new Exception ("Attempt to dequeue empty queue."); + XEvent res = xevents [head]; + head = (head + 1) % xevents.Length; + size--; + return res; + } + + public XEvent Peek() { + if (size < 1) { + throw new Exception ("Attempt to peek at empty queue"); + } + return xevents[head]; + } + + private void Grow () + { + int newcap = (xevents.Length * 2); + XEvent [] na = new XEvent [newcap]; + xevents.CopyTo (na, 0); + xevents = na; + head = 0; + tail = head + size; + } + } + } +} + diff --git a/source/ShiftUI/Internal/Xlib.cs b/source/ShiftUI/Internal/Xlib.cs new file mode 100644 index 0000000..5eebd7e --- /dev/null +++ b/source/ShiftUI/Internal/Xlib.cs @@ -0,0 +1,350 @@ +// 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) 2006 Novell, Inc. (http://www.novell.com) +// +// + +using System; +using System.Text; +using System.Runtime.InteropServices; + +namespace ShiftUI.X11Internal { + + internal class Xlib { + const string libX11 = "X11"; + + [DllImport (libX11)] + public extern static IntPtr XOpenDisplay(IntPtr display); + + [DllImport (libX11)] + public extern static int XCloseDisplay(IntPtr display); + + [DllImport (libX11)] + public extern static IntPtr XSynchronize(IntPtr display, bool onoff); + + [DllImport (libX11)] + public extern static IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, UIntPtr valuemask, ref XSetWindowAttributes attributes); + + [DllImport (libX11)] + public extern static IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, UIntPtr border, UIntPtr background); + + [DllImport (libX11)] + public extern static int XMapWindow(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static int XUnmapWindow(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static int XMapSubindows(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static int XUnmapSubwindows(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static IntPtr XRootWindow(IntPtr display, int screen_number); + + [DllImport (libX11)] + public extern static IntPtr XNextEvent(IntPtr display, ref XEvent xevent); + + [DllImport (libX11)] + public extern static int XConnectionNumber (IntPtr diplay); + + [DllImport (libX11)] + public extern static int XPending (IntPtr diplay); + + [DllImport (libX11)] + public extern static IntPtr XSelectInput(IntPtr display, IntPtr window, IntPtr mask); + + [DllImport (libX11)] + public extern static int XDestroyWindow(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static int XReparentWindow(IntPtr display, IntPtr window, IntPtr parent, int x, int y); + + [DllImport (libX11)] + public extern static int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height); + + [DllImport (libX11)] + public extern static int XResizeWindow(IntPtr display, IntPtr window, int width, int height); + + [DllImport (libX11)] + public extern static int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes); + + [DllImport (libX11)] + public extern static int XFlush(IntPtr display); + + [DllImport (libX11)] + public extern static int XSetWMName(IntPtr display, IntPtr window, ref XTextProperty text_prop); + + [DllImport (libX11)] + public extern static int XStoreName(IntPtr display, IntPtr window, string window_name); + + [DllImport (libX11)] + public extern static int XFetchName(IntPtr display, IntPtr window, ref IntPtr window_name); + + [DllImport (libX11)] + public extern static int XSendEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask, ref XEvent send_event); + + [DllImport (libX11)] + public extern static int XQueryTree(IntPtr display, IntPtr window, out IntPtr root_return, out IntPtr parent_return, out IntPtr children_return, out int nchildren_return); + + [DllImport (libX11)] + public extern static int XFree(IntPtr data); + + [DllImport (libX11)] + public extern static int XRaiseWindow(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static uint XLowerWindow(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static uint XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask, ref XWindowChanges values); + + [DllImport (libX11)] + public extern static IntPtr XInternAtom(IntPtr display, string atom_name, bool only_if_exists); + + [DllImport (libX11)] + public extern static int XInternAtoms(IntPtr display, string[] atom_names, int atom_count, bool only_if_exists, IntPtr[] atoms); + + [DllImport (libX11)] + public extern static int XSetWMProtocols(IntPtr display, IntPtr window, IntPtr[] protocols, int count); + + [DllImport (libX11)] + public extern static int XGrabPointer(IntPtr display, IntPtr window, bool owner_events, EventMask event_mask, GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, IntPtr timestamp); + + [DllImport (libX11)] + public extern static int XUngrabPointer(IntPtr display, IntPtr timestamp); + + [DllImport (libX11)] + public extern static bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons); + + [DllImport (libX11)] + public extern static bool XTranslateCoordinates (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return); + + [DllImport (libX11)] + public extern static bool XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y, out int width, out int height, out int border_width, out int depth); + + [DllImport (libX11)] + public extern static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, out int width, out int height, IntPtr border_width, IntPtr depth); + + [DllImport (libX11)] + public extern static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, IntPtr width, IntPtr height, IntPtr border_width, IntPtr depth); + + [DllImport (libX11)] + public extern static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, IntPtr x, IntPtr y, out int width, out int height, IntPtr border_width, IntPtr depth); + + [DllImport (libX11)] + public extern static uint XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, uint src_width, uint src_height, int dest_x, int dest_y); + + [DllImport (libX11)] + public extern static int XClearWindow(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static int XClearArea(IntPtr display, IntPtr window, int x, int y, int width, int height, bool exposures); + + // Colormaps + [DllImport (libX11)] + public extern static IntPtr XDefaultScreenOfDisplay(IntPtr display); + + [DllImport (libX11)] + public extern static int XScreenNumberOfScreen(IntPtr display, IntPtr Screen); + + [DllImport (libX11)] + public extern static IntPtr XDefaultVisual(IntPtr display, int screen_number); + + [DllImport (libX11)] + public extern static uint XDefaultDepth(IntPtr display, int screen_number); + + [DllImport (libX11)] + public extern static int XDefaultScreen(IntPtr display); + + [DllImport (libX11)] + public extern static IntPtr XDefaultColormap(IntPtr display, int screen_number); + + [DllImport (libX11)] + public extern static int XLookupColor(IntPtr display, IntPtr Colormap, string Coloranem, ref XColor exact_def_color, ref XColor screen_def_color); + + [DllImport (libX11)] + public extern static int XAllocColor(IntPtr display, IntPtr Colormap, ref XColor colorcell_def); + + [DllImport (libX11)] + public extern static int XSetTransientForHint(IntPtr display, IntPtr window, IntPtr prop_window); + + [DllImport (libX11)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref MotifWmHints data, int nelements); + + [DllImport (libX11)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref uint value, int nelements); + + [DllImport (libX11)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref IntPtr value, int nelements); + + [DllImport (libX11)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, uint[] data, int nelements); + + [DllImport (libX11)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, int[] data, int nelements); + + [DllImport (libX11)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr[] data, int nelements); + + [DllImport (libX11)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr atoms, int nelements); + + [DllImport (libX11, CharSet=CharSet.Ansi)] + public extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, string text, int text_length); + + [DllImport (libX11)] + public extern static int XDeleteProperty(IntPtr display, IntPtr window, IntPtr property); + + // Drawing + [DllImport (libX11)] + public extern static IntPtr XCreateGC(IntPtr display, IntPtr window, IntPtr valuemask, ref XGCValues values); + + [DllImport (libX11)] + public extern static int XFreeGC(IntPtr display, IntPtr gc); + + [DllImport (libX11)] + public extern static int XSetFunction(IntPtr display, IntPtr gc, GXFunction function); + + [DllImport (libX11)] + internal extern static int XSetLineAttributes(IntPtr display, IntPtr gc, int line_width, GCLineStyle line_style, GCCapStyle cap_style, GCJoinStyle join_style); + + [DllImport (libX11)] + public extern static int XDrawLine(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int x2, int y2); + + [DllImport (libX11)] + public extern static int XDrawRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height); + + [DllImport (libX11)] + public extern static int XFillRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height); + + [DllImport (libX11)] + public extern static int XSetWindowBackground(IntPtr display, IntPtr window, IntPtr background); + + [DllImport (libX11)] + public extern static int XCopyArea(IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int src_x, int src_y, int width, int height, int dest_x, int dest_y); + + [DllImport (libX11)] + public extern static int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset, IntPtr long_length, bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format, out IntPtr nitems, out IntPtr bytes_after, ref IntPtr prop); + + [DllImport (libX11)] + public extern static int XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time); + + [DllImport (libX11)] + public extern static int XIconifyWindow(IntPtr display, IntPtr window, int screen_number); + + [DllImport (libX11)] + public extern static int XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor); + + [DllImport (libX11)] + public extern static int XUndefineCursor(IntPtr display, IntPtr window); + + [DllImport (libX11)] + public extern static int XFreeCursor(IntPtr display, IntPtr cursor); + + [DllImport (libX11)] + public extern static IntPtr XCreateFontCursor(IntPtr display, CursorFontShape shape); + + [DllImport (libX11)] + public extern static IntPtr XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask, ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot); + + [DllImport (libX11)] + public extern static IntPtr XCreatePixmapFromBitmapData(IntPtr display, IntPtr drawable, byte[] data, int width, int height, IntPtr fg, IntPtr bg, int depth); + + [DllImport (libX11)] + public extern static IntPtr XCreatePixmap(IntPtr display, IntPtr d, int width, int height, int depth); + + [DllImport (libX11)] + public extern static IntPtr XFreePixmap(IntPtr display, IntPtr pixmap); + + [DllImport (libX11)] + public extern static int XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height, out int best_width, out int best_height); + + [DllImport (libX11)] + public extern static IntPtr XWhitePixel(IntPtr display, int screen_no); + + [DllImport (libX11)] + public extern static IntPtr XBlackPixel(IntPtr display, int screen_no); + + [DllImport (libX11)] + public extern static void XGrabServer(IntPtr display); + + [DllImport (libX11)] + public extern static void XUngrabServer(IntPtr display); + + [DllImport (libX11)] + public extern static void XGetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints, out IntPtr supplied_return); + + [DllImport (libX11)] + public extern static void XSetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport (libX11)] + public extern static void XSetZoomHints(IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport (libX11)] + public extern static void XSetWMHints(IntPtr display, IntPtr window, ref XWMHints wmhints); + + [DllImport (libX11)] + public extern static int XGetIconSizes(IntPtr display, IntPtr window, out IntPtr size_list, out int count); + + [DllImport (libX11)] + public extern static IntPtr XSetErrorHandler(XErrorHandler error_handler); + + [DllImport (libX11)] + public extern static IntPtr XGetErrorText(IntPtr display, byte code, StringBuilder buffer, int length); + + [DllImport (libX11)] + public extern static int XInitThreads(); + + [DllImport (libX11)] + public extern static int XConvertSelection(IntPtr display, IntPtr selection, IntPtr target, IntPtr property, IntPtr requestor, IntPtr time); + + [DllImport (libX11)] + public extern static IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection); + + [DllImport (libX11)] + public extern static int XSetSelectionOwner(IntPtr display, IntPtr selection, IntPtr owner, IntPtr time); + + [DllImport (libX11)] + public extern static int XSetPlaneMask(IntPtr display, IntPtr gc, IntPtr mask); + + [DllImport (libX11)] + public extern static int XSetForeground(IntPtr display, IntPtr gc, UIntPtr foreground); + + [DllImport (libX11)] + public extern static int XSetBackground(IntPtr display, IntPtr gc, UIntPtr background); + + [DllImport (libX11)] + public extern static int XBell(IntPtr display, int percent); + + [DllImport (libX11)] + public extern static int XChangeActivePointerGrab (IntPtr display, EventMask event_mask, IntPtr cursor, IntPtr time); + + [DllImport (libX11)] + public extern static bool XFilterEvent(ref XEvent xevent, IntPtr window); + + [DllImport (libX11)] + public extern static void XkbSetDetectableAutoRepeat (IntPtr display, bool detectable, IntPtr supported); + + [DllImport (libX11)] + public extern static void XPeekEvent (IntPtr display, ref XEvent xevent); + } +} diff --git a/source/ShiftUI/Internal/XplatUI.cs b/source/ShiftUI/Internal/XplatUI.cs new file mode 100644 index 0000000..afa3ddb --- /dev/null +++ b/source/ShiftUI/Internal/XplatUI.cs @@ -0,0 +1,1235 @@ +// 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 [email protected] + +// NOT COMPLETE + +// define to log API calls to stdout +#undef DriverDebug +#undef DriverDebugPaint +#undef DriverDebugCreate +#undef DriverDebugDestroy +#undef DriverDebugState + +using System; +using System.Drawing; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace ShiftUI { + internal class XplatUI { + #region Local Variables + static XplatUIDriver driver; + //static String default_class_name; + internal static ArrayList key_filters = new ArrayList (); + #endregion // Local Variables + + public static string DefaultClassName { + get { return GetDefaultClassName(); } + } + + #region Private Methods + internal static string Window (IntPtr handle) + { + return String.Format ("'{0}' ({1:X})", Widget.FromHandle (handle), handle.ToInt32 ()); + } + + [Conditional ("DriverDebug")] + static void DriverDebug (string format, params object [] args) + { + Console.WriteLine (String.Format (format, args)); + } + + #endregion // Private Methods + + #region Subclasses + public class State { + static public Keys ModifierKeys { + get { + return driver.ModifierKeys; + } + } + + static public MouseButtons MouseButtons { + get { + return driver.MouseButtons; + } + } + + static public Point MousePosition { + get { + return driver.MousePosition; + } + } + + } + #endregion // Subclasses + + #region Constructor & Destructor + static XplatUI () + { + // Compose name with current domain id because on Win32 we register class name + // and name must be unique to process. If we load MWF into multiple appdomains + // and try to register same class name we fail. + //default_class_name = "SWFClass" + System.Threading.Thread.GetDomainID ().ToString (); + try { + if (RunningOnUnix) { + //if (Environment.GetEnvironmentVariable ("not_supported_MONO_MWF_USE_NEW_X11_BACKEND") != null) { + // driver=XplatUIX11_new.GetInstance (); + //} else + if (Environment.GetEnvironmentVariable ("MONO_MWF_MAC_FORCE_X11") != null) { + driver = XplatUIX11.GetInstance (); + } else { + IntPtr buf = Marshal.AllocHGlobal (8192); + // This is a hacktastic way of getting sysname from uname () + if (uname (buf) != 0) { + // WTF: We cannot run uname + driver=XplatUIX11.GetInstance (); + } else { + string os = Marshal.PtrToStringAnsi (buf); + if (os == "Darwin") + driver=XplatUICarbon.GetInstance (); + else + driver=XplatUIX11.GetInstance (); + } + Marshal.FreeHGlobal (buf); + } + } else { + driver=XplatUIWin32.GetInstance (); + } + + driver.InitializeDriver (); + + // Initialize things that need to be done after the driver is ready + DataFormats.GetFormat (0); + + // Signal that the Application loop can be run. + // This allows UIA to initialize a11y support for MWF + // before the main loop begins. + Application.FirePreRun (); + } + catch (Exception ex) { + Console.WriteLine ("[ShiftUI/XplatUI] Driver init error: {0}", ex.Message); + } + } + #endregion // Constructor & Destructor + + #region Public Static Properties + + public static bool RunningOnUnix { + get { + int p = (int) Environment.OSVersion.Platform; + + return (p == 4 || p == 6 || p == 128); + } + } + + public static int ActiveWindowTrackingDelay { + get { return driver.ActiveWindowTrackingDelay; } + } + + // Compose name with current domain id because on Win32 we register class name + // and name must be unique to process. If we load MWF into multiple appdomains + // and try to register same class name we fail. + internal static string GetDefaultClassName (Type type) + { + return "SWFClass" + Thread.GetDomainID ().ToString () + "." + type.ToString (); + } + + // Generate a class name using current domain and a random number. + internal static string GetDefaultClassName () + { + return "SWFClass" + Thread.GetDomainID ().ToString () + "." + new Random().Next(0, 9999999).ToString(); + } + + + static public Size Border3DSize { + get { + return driver.Border3DSize; + } + } + + static public Size BorderSize { + get { + return driver.BorderSize; + } + } + + static public Size CaptionButtonSize { + get { + return driver.CaptionButtonSize; + } + } + + static public int CaptionHeight { + get { + return driver.CaptionHeight; + } + } + + public static int CaretBlinkTime { get { return driver.CaretBlinkTime; } } + public static int CaretWidth { get { return driver.CaretWidth; } } + + static public Size CursorSize { + get { + return driver.CursorSize; + } + } + + static public Size DoubleClickSize { + get { + return driver.DoubleClickSize; + } + } + + static public int DoubleClickTime { + get { + return driver.DoubleClickTime; + } + } + + static public bool DragFullWindows { + get { + return driver.DragFullWindows; + } + } + + static public Size DragSize { + get { + return driver.DragSize; + } + } + + static public Size FixedFrameBorderSize { + get { + return driver.FixedFrameBorderSize; + } + } + + public static int FontSmoothingContrast { get { return driver.FontSmoothingContrast; } } + public static int FontSmoothingType { get { return driver.FontSmoothingType; } } + + public static Size FrameBorderSize { + get { + return driver.FrameBorderSize; + } + } + + public static int HorizontalResizeBorderThickness { get { return driver.HorizontalResizeBorderThickness; } } + + static public int HorizontalScrollBarHeight { + get { + return driver.HorizontalScrollBarHeight; + } + } + + static public Size IconSize { + get { + return driver.IconSize; + } + } + + public static bool IsActiveWindowTrackingEnabled { get { return driver.IsActiveWindowTrackingEnabled; } } + public static bool IsComboBoxAnimationEnabled { get { return driver.IsComboBoxAnimationEnabled; } } + public static bool IsDropShadowEnabled { get { return driver.IsDropShadowEnabled; } } + public static bool IsFontSmoothingEnabled { get { return driver.IsFontSmoothingEnabled; } } + public static bool IsHotTrackingEnabled { get { return driver.IsHotTrackingEnabled; } } + public static bool IsIconTitleWrappingEnabled { get { return driver.IsIconTitleWrappingEnabled; } } + public static bool IsKeyboardPreferred { get { return driver.IsKeyboardPreferred; } } + public static bool IsListBoxSmoothScrollingEnabled { get { return driver.IsListBoxSmoothScrollingEnabled; } } + public static bool IsMenuAnimationEnabled { get { return driver.IsMenuAnimationEnabled; } } + public static bool IsMenuFadeEnabled { get { return driver.IsMenuFadeEnabled; } } + public static bool IsMinimizeRestoreAnimationEnabled { get { return driver.IsMinimizeRestoreAnimationEnabled; } } + public static bool IsSelectionFadeEnabled { get { return driver.IsSelectionFadeEnabled; } } + public static bool IsSnapToDefaultEnabled { get { return driver.IsSnapToDefaultEnabled; } } + public static bool IsTitleBarGradientEnabled { get { return driver.IsTitleBarGradientEnabled; } } + public static bool IsToolTipAnimationEnabled { get { return driver.IsToolTipAnimationEnabled; } } + + static public int KeyboardSpeed { + get { + return driver.KeyboardSpeed; + } + } + + static public int KeyboardDelay { + get { + return driver.KeyboardDelay; + } + } + + static public Size MaxWindowTrackSize { + get { + return driver.MaxWindowTrackSize; + } + } + + static public bool MenuAccessKeysUnderlined { + get { + return driver.MenuAccessKeysUnderlined; + } + } + + static public Size MenuBarButtonSize { get { return driver.MenuBarButtonSize; } } + + public static Size MenuButtonSize { + get { + return driver.MenuButtonSize; + } + } + + static public int MenuShowDelay { get { return driver.MenuShowDelay; } } + + static public Size MinimizedWindowSize { + get { + return driver.MinimizedWindowSize; + } + } + + static public Size MinimizedWindowSpacingSize { + get { + return driver.MinimizedWindowSpacingSize; + } + } + + static public Size MinimumWindowSize { + get { + return driver.MinimumWindowSize; + } + } + + static public Size MinimumFixedToolWindowSize { + get { return driver.MinimumFixedToolWindowSize; } + } + + static public Size MinimumSizeableToolWindowSize { + get { return driver.MinimumSizeableToolWindowSize; } + } + + static public Size MinimumNoBorderWindowSize { + get { return driver.MinimumNoBorderWindowSize; } + } + + static public Size MinWindowTrackSize { + get { + return driver.MinWindowTrackSize; + } + } + + public static int MouseSpeed { + get { return driver.MouseSpeed; } + } + + static public Size SmallIconSize { + get { + return driver.SmallIconSize; + } + } + + static public int MenuHeight { + get { + return driver.MenuHeight; + } + } + + static public int MouseButtonCount { + get { + return driver.MouseButtonCount; + } + } + + static public bool MouseButtonsSwapped { + get { + return driver.MouseButtonsSwapped; + } + } + + static public Size MouseHoverSize { + get { + return driver.MouseHoverSize; + } + } + + static public int MouseHoverTime { + get { + return driver.MouseHoverTime; + } + } + + static public int MouseWheelScrollDelta { + get { + return driver.MouseWheelScrollDelta; + } + } + + static public bool MouseWheelPresent { + get { + return driver.MouseWheelPresent; + } + } + + public static LeftRightAlignment PopupMenuAlignment { + get { return driver.PopupMenuAlignment; } + } + + public static PowerStatus PowerStatus { + get { return driver.PowerStatus; } + } + + public static bool RequiresPositiveClientAreaSize { + get { + return driver.RequiresPositiveClientAreaSize; + } + } + + public static int SizingBorderWidth { + get { return driver.SizingBorderWidth; } + } + + public static Size SmallCaptionButtonSize { + get { return driver.SmallCaptionButtonSize; } + } + + public static bool UIEffectsEnabled { + get { return driver.UIEffectsEnabled; } + } + + static public bool UserClipWontExposeParent { + get { + return driver.UserClipWontExposeParent; + } + } + + public static int VerticalResizeBorderThickness { get { return driver.VerticalResizeBorderThickness; } } + + static public int VerticalScrollBarWidth { + get { + return driver.VerticalScrollBarWidth; + } + } + + static public Rectangle VirtualScreen { + get { + return driver.VirtualScreen; + } + } + + static public Rectangle WorkingArea { + get { + return driver.WorkingArea; + } + } + + public static Screen[] AllScreens { + get { + return driver.AllScreens; + } + } + + public static bool ThemesEnabled { + get { + return XplatUI.driver.ThemesEnabled; + } + } + + public static int ToolWindowCaptionHeight { + get { + return driver.ToolWindowCaptionHeight; + } + } + + public static Size ToolWindowCaptionButtonSize { + get { + return driver.ToolWindowCaptionButtonSize; + } + } + #endregion // Public Static Properties + + #region Events + + internal static event EventHandler Idle { + add { + driver.Idle += value; + } + remove { + driver.Idle -= value; + } + } + + #endregion // Events + + #region Public Static Methods + internal static void Activate (IntPtr handle) + { + DriverDebug ("Activate ({0}): Called", Window (handle)); + driver.Activate (handle); + } + + internal static void AudibleAlert (AlertType alert) + { + DriverDebug ("AudibleAlert (): Called"); + driver.AudibleAlert (alert); + } + + internal static void BeginMoveResize (IntPtr handle) + { + driver.BeginMoveResize (handle); + } + + internal static bool CalculateWindowRect (ref Rectangle ClientRect, CreateParams cp, Menu menu, out Rectangle WindowRect) + { + DriverDebug ("CalculateWindowRect ({0}, {1}, {2}): Called", ClientRect, cp, menu); + return driver.CalculateWindowRect (ref ClientRect, cp, menu, out WindowRect); + } + + internal static void CaretVisible (IntPtr handle, bool visible) + { + DriverDebug ("CaretVisible ({0:X}, {1}): Called", handle.ToInt32 (), visible); + driver.CaretVisible (handle, visible); + } + + internal static void CreateCaret (IntPtr handle, int width, int height) + { + DriverDebug ("CreateCaret ({0:X}), {1}, {2}: Called", handle.ToInt32 (), width, height); + driver.CreateCaret (handle, width, height); + } + + internal static IntPtr CreateWindow (CreateParams cp) + { + #if DriverDebug || DriverDebugCreate + IntPtr handle; + + handle = driver.CreateWindow (cp); + + Console.WriteLine ("CreateWindow (): Called, returning {0:X}", handle.ToInt32 ()); + return handle; + #else + return driver.CreateWindow (cp); + #endif + } + + internal static IntPtr CreateWindow (IntPtr Parent, int X, int Y, int Width, int Height) + { + #if DriverDebug || DriverDebugCreate + Console.WriteLine ("CreateWindow (): Called"); + #endif + return driver.CreateWindow (Parent, X, Y, Width, Height); + } + + internal static void ClientToScreen (IntPtr handle, ref int x, ref int y) + { + #if DriverDebug + Console.WriteLine ("ClientToScreen ({0}, {1}, {2}): Called", Window (handle), x, y); + #endif + driver.ClientToScreen (handle, ref x, ref y); + } + + internal static int[] ClipboardAvailableFormats (IntPtr handle) + { + DriverDebug ("ClipboardAvailableTypes ({0:X}): Called", handle.ToInt32 ()); + return driver.ClipboardAvailableFormats (handle); + } + + internal static void ClipboardClose (IntPtr handle) + { + DriverDebug ("ClipboardClose ({0:X}): Called", handle.ToInt32 ()); + driver.ClipboardClose (handle); + } + + internal static int ClipboardGetID (IntPtr handle, string format) + { + DriverDebug ("ClipboardGetID ({0:X}, {1}): Called", handle.ToInt32 (), format); + return driver.ClipboardGetID (handle, format); + } + + internal static IntPtr ClipboardOpen (bool primary_selection) + { + DriverDebug ("ClipboardOpen (): Called"); + return driver.ClipboardOpen (primary_selection); + } + + internal static void ClipboardStore (IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter, bool copy) + { + DriverDebug ("ClipboardStore ({0:X}, {1}, {2}, {3}, {4}): Called", handle.ToInt32 (), obj, type, converter, copy); + driver.ClipboardStore (handle, obj, type, converter, copy); + } + + internal static object ClipboardRetrieve (IntPtr handle, int type, XplatUI.ClipboardToObject converter) + { + DriverDebug ("ClipboardRetrieve ({0:X}, type, {1}): Called", handle.ToInt32 (), converter); + return driver.ClipboardRetrieve (handle, type, converter); + } + + internal static IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) + { + DriverDebug ("DefineCursor (...): Called"); + return driver.DefineCursor (bitmap, mask, cursor_pixel, mask_pixel, xHotSpot, yHotSpot); + } + + internal static IntPtr DefineStdCursor (StdCursor id) + { + return driver.DefineStdCursor (id); + } + + internal static Bitmap DefineStdCursorBitmap (StdCursor id) + { + return driver.DefineStdCursorBitmap (id); + } + + internal static IntPtr DefWndProc (ref Message msg) + { + return driver.DefWndProc (ref msg); + } + + internal static void DestroyCaret (IntPtr handle) + { + DriverDebug ("DestroyCaret ({0:X}): Called", handle.ToInt32 ()); + driver.DestroyCaret (handle); + } + + internal static void DestroyCursor (IntPtr cursor) + { + DriverDebug ("DestroyCursor ({0:X}): Called", cursor.ToInt32 ()); + driver.DestroyCursor (cursor); + } + + internal static void DestroyWindow (IntPtr handle) + { + DriverDebug ("DestroyWindow ({0}): Called", Window (handle)); + driver.DestroyWindow (handle); + } + + internal static IntPtr DispatchMessage (ref MSG msg) + { + return driver.DispatchMessage (ref msg); + } + + internal static void DoEvents () + { + driver.DoEvents (); + } + + internal static void DrawReversibleRectangle (IntPtr handle, Rectangle rect, int line_width) + { + DriverDebug ("DrawReversibleRectangle ({0}, {1}, {2}): Called", Window (handle), rect, line_width); + driver.DrawReversibleRectangle (handle, rect, line_width); + } + + internal static void FillReversibleRectangle (Rectangle rectangle, Color backColor) + { + DriverDebug ("FillReversibleRectangle ({0}, {1}): Called", rectangle, backColor); + driver.FillReversibleRectangle (rectangle, backColor); + } + + internal static void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style) + { + DriverDebug ("DrawReversibleFrame ({0}, {1}, {2}): Called", rectangle, backColor, style); + driver.DrawReversibleFrame (rectangle, backColor, style); + } + + internal static void DrawReversibleLine (Point start, Point end, Color backColor) + { + DriverDebug ("DrawReversibleLine ({0}, {1}, {2}): Called", start, end, backColor); + driver.DrawReversibleLine (start, end, backColor); + } + + internal static void EnableThemes () + { + driver.EnableThemes (); + } + + internal static void EnableWindow (IntPtr handle, bool Enable) + { + DriverDebug ("EnableWindow ({0}, {1}): Called", Window (handle), Enable); + driver.EnableWindow (handle, Enable); + } + + internal static void EndLoop (Thread thread) + { + DriverDebug ("EndLoop ({0:X}): Called", thread.GetHashCode ()); + driver.EndLoop (thread); + } + + internal static IntPtr GetActive () + { + DriverDebug ("GetActive (): Called"); + return driver.GetActive (); + } + + internal static SizeF GetAutoScaleSize (Font font) + { + DriverDebug ("GetAutoScaleSize ({0}): Called", font); + return driver.GetAutoScaleSize (font); + } + + internal static Region GetClipRegion (IntPtr handle) + { + DriverDebug ("GetClipRegion ({0}): Called", Window (handle)); + return driver.GetClipRegion (handle); + } + + internal static void GetCursorInfo (IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y) + { + DriverDebug ("GetCursorInfo ({0}): Called", cursor.ToInt32 ()); + driver.GetCursorInfo (cursor, out width, out height, out hotspot_x, out hotspot_y); + } + + internal static void GetCursorPos (IntPtr handle, out int x, out int y) + { + DriverDebug ("GetCursorPos ({0}): Called", Window (handle)); + driver.GetCursorPos (handle, out x, out y); + } + + internal static void GetDisplaySize (out Size size) + { + DriverDebug ("GetDisplaySize (): Called"); + driver.GetDisplaySize (out size); + } + + internal static IntPtr GetFocus () + { + DriverDebug ("GetFocus (): Called, Result:{0}", Window (driver.GetFocus ())); + return driver.GetFocus (); + } + + internal static bool GetFontMetrics (Graphics g, Font font, out int ascent, out int descent) + { + DriverDebug ("GetFontMetrics (): Called"); + return driver.GetFontMetrics (g, font, out ascent, out descent); + } + + internal static Point GetMenuOrigin (IntPtr handle) + { + DriverDebug ("GetMenuOrigin ({0}): Called", Window (handle)); + return driver.GetMenuOrigin (handle); + } + + internal static bool GetMessage (object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) + { + return driver.GetMessage (queue_id, ref msg, hWnd, wFilterMin, wFilterMax); + } + + internal static IntPtr GetParent (IntPtr handle) + { + DriverDebug ("GetParent ({0}): Called", Window (handle)); + return driver.GetParent (handle); + } + + internal static IntPtr GetPreviousWindow (IntPtr handle) + { + return driver.GetPreviousWindow (handle); + } + + internal static bool GetText (IntPtr handle, out string text) + { + DriverDebug ("GetText ({0}): Called", Window (handle)); + return driver.GetText (handle, out text); + } + + internal static void GetWindowPos (IntPtr handle, bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) + { + DriverDebug ("GetWindowPos ({0}): Called", Window (handle)); + driver.GetWindowPos (handle, is_toplevel, out x, out y, out width, out height, out client_width, out client_height); + } + + /* this method can (and does, on X11) return + * (FormWindowState) (-1), when the state of the window + * cannot be determined (in the X11 case, when the + * window isn't mapped.) Checking for the additional + * return value is less expensive than + * throwing/catching an exception. */ + internal static FormWindowState GetWindowState (IntPtr handle) + { + DriverDebug ("GetWindowState ({0}): Called", Window (handle)); + return driver.GetWindowState (handle); + } + + internal static void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea) + { + DriverDebug ("GrabInfo (): Called"); + driver.GrabInfo (out handle, out GrabConfined, out GrabArea); + } + + internal static void GrabWindow (IntPtr handle, IntPtr ConfineToHwnd) + { + DriverDebug ("GrabWindow ({0}, {1}): Called", Window (handle), Window (ConfineToHwnd)); + driver.GrabWindow (handle, ConfineToHwnd); + } + + internal static void HandleException (Exception e) + { + driver.HandleException (e); + } + + internal static void Invalidate (IntPtr handle, Rectangle rc, bool clear) + { + DriverDebug ("Invalidate ({0}, {1}, {2}): Called", Window (handle), rc, clear); + driver.Invalidate (handle, rc, clear); + } + + internal static void InvalidateNC (IntPtr handle) + { + DriverDebug ("InvalidateNC ({0}): Called", Window (handle)); + driver.InvalidateNC (handle); + } + + + internal static bool IsEnabled (IntPtr handle) + { + #if DriverDebug || DriverDebugState + Console.WriteLine ("IsEnabled ({0}): Called, Result={1}", Window (handle), driver.IsEnabled (handle)); + #endif + return driver.IsEnabled (handle); + } + + internal static bool IsKeyLocked (VirtualKeys key) + { + #if DriverDebug || DriverDebugState + Console.WriteLine ("IsKeyLocked ({0}): Called, Result={1}", key, driver.IsKeyLocked (key)); + #endif + return driver.IsKeyLocked (key); + } + + internal static bool IsVisible (IntPtr handle) + { + #if DriverDebug || DriverDebugState + Console.WriteLine ("IsVisible ({0}): Called, Result={1}", Window (handle), driver.IsVisible (handle)); + #endif + return driver.IsVisible (handle); + } + + internal static void KillTimer (Timer timer) + { + DriverDebug ("KillTimer ({0}): Called", timer); + driver.KillTimer (timer); + } + + internal static void MenuToScreen (IntPtr handle, ref int x, ref int y) + { + DriverDebug ("MenuToScreen ({0}, {1}, {2}): Called", Window (handle), x, y); + driver.MenuToScreen (handle, ref x, ref y); + } + + internal static void OverrideCursor (IntPtr cursor) + { + DriverDebug ("OverrideCursor ({0:X}): Called", cursor.ToInt32 ()); + driver.OverrideCursor (cursor); + } + + internal static void PaintEventEnd (ref Message msg, IntPtr handle, bool client) + { + #if DriverDebug || DriverDebugPaint + Console.WriteLine ("PaintEventEnd ({0}, {1}, {2}): Called from thread {3}", msg, Window (handle), client, Thread.CurrentThread.GetHashCode ()); + #endif + driver.PaintEventEnd (ref msg, handle, client); + } + + internal static PaintEventArgs PaintEventStart (ref Message msg, IntPtr handle, bool client) + { + #if DriverDebug || DriverDebugPaint + Console.WriteLine ("PaintEventStart ({0}, {1}, {2}): Called from thread {3}", msg, Window (handle), client, Thread.CurrentThread.GetHashCode ()); + #endif + return driver.PaintEventStart (ref msg, handle, client); + } + + internal static bool PeekMessage (Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) + { + return driver.PeekMessage (queue_id, ref msg, hWnd, wFilterMin, wFilterMax, flags); + } + + internal static bool PostMessage (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) + { + DriverDebug ("PostMessage ({0}, {1}, {2:X}, {3:X}): Called", Window (hwnd), message, wParam.ToInt32 (), lParam.ToInt32 ()); + return driver.PostMessage (hwnd, message, wParam, lParam); + } + + internal static bool PostMessage (ref MSG msg) + { + DriverDebug ("PostMessage ({0}): Called", msg); + return driver.PostMessage (msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + + internal static void PostQuitMessage (int exitCode) + { + DriverDebug ("PostQuitMessage ({0}): Called", exitCode); + driver.PostQuitMessage (exitCode); + } + + internal static void RaiseIdle (EventArgs e) + { + DriverDebug ("RaiseIdle ({0}): Called", e.ToString ()); + + driver.RaiseIdle (e); + } + + internal static void RequestAdditionalWM_NCMessages (IntPtr handle, bool hover, bool leave) + { + DriverDebug ("RequestAdditionalWM_NCMessages ({0}, {1}, {2}): Called", Window (handle), hover, leave); + driver.RequestAdditionalWM_NCMessages (handle, hover, leave); + } + + internal static void RequestNCRecalc (IntPtr handle) + { + DriverDebug ("RequestNCRecalc ({0}): Called", Window (handle)); + driver.RequestNCRecalc (handle); + } + + internal static void ResetMouseHover (IntPtr handle) + { + DriverDebug ("ResetMouseHover ({0}): Called", Window (handle)); + driver.ResetMouseHover (handle); + } + + internal static void ScreenToClient (IntPtr handle, ref int x, ref int y) + { + DriverDebug ("ScreenToClient ({0}, {1}, {2}): Called", Window (handle), x, y); + driver.ScreenToClient (handle, ref x, ref y); + } + + internal static void ScreenToMenu (IntPtr handle, ref int x, ref int y) + { + DriverDebug ("ScreenToMenu ({0}, {1}, {2}): Called", Window (handle), x, y); + driver.ScreenToMenu (handle, ref x, ref y); + } + + internal static void ScrollWindow (IntPtr handle, Rectangle rectangle, int XAmount, int YAmount, bool with_children) + { + DriverDebug ("ScrollWindow ({0}, {1}, {2}, {3}, {4}): Called", Window (handle), rectangle, XAmount, YAmount, with_children); + driver.ScrollWindow (handle, rectangle, XAmount, YAmount, with_children); + } + + internal static void ScrollWindow (IntPtr handle, int XAmount, int YAmount, bool with_children) + { + DriverDebug ("ScrollWindow ({0}, {1}, {2}, {3}): Called", Window (handle), XAmount, YAmount, with_children); + driver.ScrollWindow (handle, XAmount, YAmount, with_children); + } + + internal static void SendAsyncMethod (AsyncMethodData data) + { + DriverDebug ("SendAsyncMethod ({0}): Called", data); + driver.SendAsyncMethod (data); + } + + internal static int SendInput (IntPtr hwnd, Queue keys) + { + DriverDebug ("SendInput ({0}, {1}): Called", hwnd, keys); + return driver.SendInput (hwnd, keys); + } + + internal static IntPtr SendMessage (IntPtr handle, Msg message, IntPtr wParam, IntPtr lParam) + { + DriverDebug ("SendMessage ({0}, {1}, {2:X}, {3:X}): Called", Window (handle), message, wParam.ToInt32 (), lParam.ToInt32 ()); + return driver.SendMessage (handle, message, wParam, lParam); + } + + internal static void SendMessage (ref Message m) + { + DriverDebug ("SendMessage ({0}): Called", m); + m.Result = driver.SendMessage (m.HWnd, (Msg)m.Msg, m.WParam, m.LParam); + } + + internal static void SetAllowDrop (IntPtr handle, bool value) + { + DriverDebug ("SetAllowDrop ({0}, {1}): Called", handle, value); + driver.SetAllowDrop (handle, value); + } + + internal static void SetBorderStyle (IntPtr handle, FormBorderStyle border_style) + { + DriverDebug ("SetBorderStyle ({0}, {1}): Called", Window (handle), border_style); + driver.SetBorderStyle (handle, border_style); + } + + internal static void SetCaretPos (IntPtr handle, int x, int y) + { + DriverDebug ("SetCaretPos ({0}, {1}, {2}): Called", Window (handle), x, y); + driver.SetCaretPos (handle, x, y); + } + + internal static void SetClipRegion (IntPtr handle, Region region) + { + DriverDebug ("SetClipRegion ({0}, {1}): Called", Window (handle), region); + driver.SetClipRegion (handle, region); + } + + internal static void SetCursor (IntPtr handle, IntPtr cursor) + { + DriverDebug ("SetCursor ({0}, {1:X}): Called", Window (handle), cursor.ToInt32 ()); + driver.SetCursor (handle, cursor); + } + + internal static void SetCursorPos (IntPtr handle, int x, int y) + { + DriverDebug ("SetCursorPos ({0}, {1}, {2}): Called", Window (handle), x, y); + driver.SetCursorPos (handle, x, y); + } + + internal static void SetFocus (IntPtr handle) + { + DriverDebug ("SetFocus ({0}): Called", Window (handle)); + driver.SetFocus (handle); + } + + internal static void SetForegroundWindow (IntPtr handle) + { + DriverDebug ("SetForegroundWindow ({0}): Called", Window (handle)); + driver.SetForegroundWindow (handle); + } + + internal static void SetIcon (IntPtr handle, Icon icon) + { + DriverDebug ("SetIcon ({0}, {1}): Called", Window (handle), icon); + driver.SetIcon (handle, icon); + } + + internal static void SetMenu (IntPtr handle, Menu menu) + { + DriverDebug ("SetMenu ({0}, {1}): Called", Window (handle), menu); + driver.SetMenu (handle, menu); + } + + internal static void SetModal (IntPtr handle, bool Modal) + { + DriverDebug ("SetModal ({0}, {1}): Called", Window (handle), Modal); + driver.SetModal (handle, Modal); + } + + internal static IntPtr SetParent (IntPtr handle, IntPtr hParent) + { + DriverDebug ("SetParent ({0}, {1:X}): Called", Window (handle), Window (hParent)); + return driver.SetParent (handle, hParent); + } + + internal static void SetTimer (Timer timer) + { + DriverDebug ("SetTimer ({0}): Called", timer); + driver.SetTimer (timer); + } + + internal static bool SetTopmost (IntPtr handle, bool Enabled) + { + DriverDebug ("SetTopMost ({0}, {1}): Called", Window (handle), Enabled); + return driver.SetTopmost (handle, Enabled); + } + + internal static bool SetOwner (IntPtr handle, IntPtr hWndOwner) + { + DriverDebug ("SetOwner ({0}, {1}): Called", Window (handle), Window (hWndOwner)); + return driver.SetOwner (handle, hWndOwner); + } + + internal static bool SetVisible (IntPtr handle, bool visible, bool activate) + { + #if DriverDebug || DriverDebugState + Console.WriteLine ("SetVisible ({0}, {1}, {2}): Called", Window (handle), visible, activate); + #endif + return driver.SetVisible (handle, visible, activate); + } + + internal static void SetWindowMinMax (IntPtr handle, Rectangle maximized, Size min, Size max) + { + #if DriverDebug || DriverDebugState + Console.WriteLine ("SetWindowMinMax ({0}, {1}, {2}, {3}): Called", Window (handle), maximized, min, max); + #endif + driver.SetWindowMinMax (handle, maximized, min, max); + } + + internal static void SetWindowPos (IntPtr handle, int x, int y, int width, int height) + { + DriverDebug ("SetWindowPos ({0}, {1}, {2}, {3}, {4}): Called", Window (handle), x, y, width, height); + driver.SetWindowPos (handle, x, y, width, height); + } + + internal static void SetWindowState (IntPtr handle, FormWindowState state) + { + #if DriverDebug || DriverDebugState + Console.WriteLine ("SetWindowState ({0} {1}): Called", Window (handle), state); + #endif + driver.SetWindowState (handle, state); + } + + internal static void SetWindowStyle (IntPtr handle, CreateParams cp) + { + DriverDebug ("SetWindowStyle ({0}): Called", Window (handle)); + driver.SetWindowStyle (handle, cp); + } + + internal static double GetWindowTransparency (IntPtr handle) + { + DriverDebug ("SetWindowTransparency ({0}): Called", Window (handle)); + return driver.GetWindowTransparency (handle); + } + + internal static void SetWindowTransparency (IntPtr handle, double transparency, Color key) + { + DriverDebug ("SetWindowTransparency ({0}): Called", Window (handle)); + driver.SetWindowTransparency (handle, transparency, key); + } + + internal static bool SetZOrder (IntPtr handle, IntPtr AfterhWnd, bool Top, bool Bottom) + { + DriverDebug ("SetZOrder ({0}, {1:X}, {2}, {3}): Called", Window (handle), Window (AfterhWnd), Top, Bottom); + return driver.SetZOrder (handle, AfterhWnd, Top, Bottom); + } + + internal static void ShowCursor (bool show) + { + DriverDebug ("ShowCursor ({0}): Called", show); + driver.ShowCursor (show); + } + + internal static DragDropEffects StartDrag (IntPtr handle, object data, DragDropEffects allowedEffects) + { + DriverDebug ("StartDrag ({0}, {1}, {2}): Called", Window (handle), data, allowedEffects); + return driver.StartDrag (handle, data, allowedEffects); + } + + internal static object StartLoop (Thread thread) + { + DriverDebug ("StartLoop ({0:X}): Called", thread.GetHashCode ()); + return driver.StartLoop (thread); + } + + internal static TransparencySupport SupportsTransparency () + { + DriverDebug ("SupportsTransparency (): Called, result={0}", driver.SupportsTransparency ()); + return driver.SupportsTransparency (); + } + + internal static bool SystrayAdd (IntPtr handle, string tip, Icon icon, out ToolTip tt) + { + DriverDebug ("SystrayAdd ({0}, {1}): Called", Window (handle), tip); + return driver.SystrayAdd (handle, tip, icon, out tt); + } + + internal static void SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt) + { + DriverDebug ("SystrayChange ({0}, {1}): Called", Window (handle), tip); + driver.SystrayChange (handle, tip, icon, ref tt); + } + + internal static void SystrayRemove (IntPtr handle, ref ToolTip tt) + { + DriverDebug ("SystrayRemove ({0}): Called", Window (handle)); + driver.SystrayRemove (handle, ref tt); + } + + internal static void SystrayBalloon (IntPtr handle, int timeout, string title, string text, ToolTipIcon icon) + { + DriverDebug ("SystrayBalloon ({0}, {1}, {2}, {3}, {4}): Called", Window (handle), timeout, title, text, icon); + driver.SystrayBalloon (handle, timeout, title, text, icon); + } + + internal static bool Text (IntPtr handle, string text) + { + DriverDebug ("Text ({0}, {1}): Called", Window (handle), text); + return driver.Text (handle, text); + } + + internal static bool TranslateMessage (ref MSG msg) + { + return driver.TranslateMessage (ref msg); + } + + internal static void UngrabWindow (IntPtr handle) + { + DriverDebug ("UngrabWindow ({0}): Called", Window (handle)); + driver.UngrabWindow (handle); + } + + internal static void UpdateWindow (IntPtr handle) + { + DriverDebug ("UpdateWindow ({0}): Called", Window (handle)); + driver.UpdateWindow (handle); + } + + // double buffering + internal static void CreateOffscreenDrawable (IntPtr handle, + int width, int height, + out object offscreen_drawable) + { + DriverDebug ("CreateOffscreenDrawable ({2}, {0},{1}): Called", width, height, Window (handle)); + driver.CreateOffscreenDrawable (handle, width, height, + out offscreen_drawable); + } + + internal static void DestroyOffscreenDrawable (object offscreen_drawable) + { + DriverDebug ("DestroyOffscreenDrawable (): Called"); + driver.DestroyOffscreenDrawable (offscreen_drawable); + } + + internal static Graphics GetOffscreenGraphics (object offscreen_drawable) + { + DriverDebug ("GetOffscreenGraphics (): Called"); + return driver.GetOffscreenGraphics (offscreen_drawable); + } + + internal static void BlitFromOffscreen (IntPtr dest_handle, + Graphics dest_dc, + object offscreen_drawable, + Graphics offscreen_dc, + Rectangle r) + { + DriverDebug ("BlitFromOffscreen ({0}): Called", Window (dest_handle)); + driver.BlitFromOffscreen (dest_handle, dest_dc, offscreen_drawable, offscreen_dc, r); + } + + + // Santa's little helper + internal static void Version () + { + Console.WriteLine ("Xplat version $Revision: $"); + } + + internal static void AddKeyFilter (IKeyFilter value) + { + lock (key_filters) { + key_filters.Add (value); + } + } + + internal static bool FilterKey (KeyFilterData key) + { + lock (key_filters) { + for (int i = 0; i < key_filters.Count; i++) { + IKeyFilter filter = (IKeyFilter) key_filters[i]; + if (filter.PreFilterKey (key)) + return true; + } + } + return false; + } + #endregion // Public Static Methods + + #region Delegates + public delegate bool ClipboardToObject (int type, IntPtr data, out object obj); + public delegate bool ObjectToClipboard (ref int type, object obj, out byte[] data); + #endregion // Delegates + + [DllImport ("libc")] + static extern int uname (IntPtr buf); + } +} diff --git a/source/ShiftUI/Internal/XplatUICarbon.cs b/source/ShiftUI/Internal/XplatUICarbon.cs new file mode 100644 index 0000000..ec1b53a --- /dev/null +++ b/source/ShiftUI/Internal/XplatUICarbon.cs @@ -0,0 +1,2432 @@ +// 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-2007 Novell, Inc. +// +// Authors: +// Geoff Norton <[email protected]> +// +// + +using System; +using System.Threading; +using System.Drawing; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Runtime.InteropServices; + +using Carbon = ShiftUI.CarbonInternal; + +/// Carbon Version +using ShiftUI; + + +namespace ShiftUI { + internal delegate Rectangle [] HwndDelegate (IntPtr handle); + + internal class XplatUICarbon : XplatUIDriver { + #region Local Variables + // General driver variables + private static XplatUICarbon Instance; + private static int RefCount; + private static bool themes_enabled; + + // Internal members available to the event handler sub-system + internal static IntPtr FocusWindow; + internal static IntPtr ActiveWindow; + internal static IntPtr UnactiveWindow; + internal static IntPtr ReverseWindow; + internal static IntPtr CaretWindow; + + internal static Hwnd MouseHwnd; + + internal static MouseButtons MouseState; + internal static Carbon.Hover Hover; + + internal static HwndDelegate HwndDelegate = new HwndDelegate (GetClippingRectangles); + // Instance members + internal Point mouse_position; + + // Event handlers + internal Carbon.ApplicationHandler ApplicationHandler; + internal Carbon.WidgetHandler WidgetHandler; + internal Carbon.HIObjectHandler HIObjectHandler; + internal Carbon.KeyboardHandler KeyboardHandler; + internal Carbon.MouseHandler MouseHandler; + internal Carbon.WindowHandler WindowHandler; + + // Carbon Specific + internal static GrabStruct Grab; + internal static Carbon.Caret Caret; + private static Carbon.Dnd Dnd; + private static Hashtable WindowMapping; + private static Hashtable HandleMapping; + private static IntPtr FosterParent; + private static IntPtr Subclass; + private static int MenuBarHeight; + internal static ArrayList UtilityWindows; + + // Message loop + private static Queue MessageQueue; + private static bool GetMessageResult; + + private static bool ReverseWindowMapped; + + // Timers + private ArrayList TimerList; + private static bool in_doevents; + + static readonly object instancelock = new object (); + static readonly object queuelock = new object (); + + // Event Handlers + internal override event EventHandler Idle; + #endregion + + #region Constructors + private XplatUICarbon() { + + RefCount = 0; + TimerList = new ArrayList (); + in_doevents = false; + MessageQueue = new Queue (); + + Initialize (); + } + + ~XplatUICarbon() { + // FIXME: Clean up the FosterParent here. + } + #endregion + + #region Singleton specific code + public static XplatUICarbon GetInstance() { + lock (instancelock) { + if (Instance == null) { + Instance = new XplatUICarbon (); + } + RefCount++; + } + return Instance; + } + + public int Reference { + get { + return RefCount; + } + } + #endregion + + #region Internal methods + internal void AddExpose (Hwnd hwnd, bool client, Carbon.HIRect rect) { + AddExpose (hwnd, client, (int) rect.origin.x, (int) rect.origin.y, (int) rect.size.width, (int) rect.size.height); + } + + internal void AddExpose (Hwnd hwnd, bool client, Rectangle rect) { + AddExpose (hwnd, client, (int) rect.X, (int) rect.Y, (int) rect.Width, (int) rect.Height); + } + + internal void FlushQueue () { + CheckTimers (DateTime.UtcNow); + lock (queuelock) { + while (MessageQueue.Count > 0) { + object queueobj = MessageQueue.Dequeue (); + if (queueobj is GCHandle) { + XplatUIDriverSupport.ExecuteClientMessage((GCHandle)queueobj); + } else { + MSG msg = (MSG)queueobj; + NativeWindow.WndProc (msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + } + } + } + + internal static Rectangle [] GetClippingRectangles (IntPtr handle) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) + return null; + if (hwnd.Handle != handle) + return new Rectangle [] {hwnd.ClientRect}; + + return (Rectangle []) hwnd.GetClippingRectangles ().ToArray (typeof (Rectangle)); + } + + internal IntPtr GetMousewParam(int Delta) { + int result = 0; + + if ((MouseState & MouseButtons.Left) != 0) { + result |= (int)MsgButtons.MK_LBUTTON; + } + + if ((MouseState & MouseButtons.Middle) != 0) { + result |= (int)MsgButtons.MK_MBUTTON; + } + + if ((MouseState & MouseButtons.Right) != 0) { + result |= (int)MsgButtons.MK_RBUTTON; + } + + Keys mods = ModifierKeys; + if ((mods & Keys.Widget) != 0) { + result |= (int)MsgButtons.MK_CONTROL; + } + + if ((mods & Keys.Shift) != 0) { + result |= (int)MsgButtons.MK_SHIFT; + } + + result |= Delta << 16; + + return (IntPtr)result; + } + + internal IntPtr HandleToWindow (IntPtr handle) { + if (HandleMapping [handle] != null) + return (IntPtr) HandleMapping [handle]; + return IntPtr.Zero; + } + + internal void Initialize () { + if (Marshal.SizeOf<IntPtr> () == 8){ + Console.Error.WriteLine ("WARNING: The Carbon driver has not been ported to 64bits, and very few parts of Windows.Forms will work properly, or at all"); + } + // Initialize the event handlers + Carbon.EventHandler.Driver = this; + ApplicationHandler = new Carbon.ApplicationHandler (this); + WidgetHandler = new Carbon.WidgetHandler (this); + HIObjectHandler = new Carbon.HIObjectHandler (this); + KeyboardHandler = new Carbon.KeyboardHandler (this); + MouseHandler = new Carbon.MouseHandler (this); + WindowHandler = new Carbon.WindowHandler (this); + + // Initilize the mouse Widgets + Hover.Interval = 500; + Hover.Timer = new Timer (); + Hover.Timer.Enabled = false; + Hover.Timer.Interval = Hover.Interval; + Hover.Timer.Tick += new EventHandler (HoverCallback); + Hover.X = -1; + Hover.Y = -1; + MouseState = MouseButtons.None; + mouse_position = Point.Empty; + + // Initialize the Caret + Caret.Timer = new Timer (); + Caret.Timer.Interval = 500; + Caret.Timer.Tick += new EventHandler (CaretCallback); + + // Initialize the D&D + Dnd = new Carbon.Dnd (); + + // Initialize the Carbon Specific stuff + WindowMapping = new Hashtable (); + HandleMapping = new Hashtable (); + UtilityWindows = new ArrayList (); + + // Initialize the FosterParent + Carbon.Rect rect = new Carbon.Rect (); + SetRect (ref rect, (short)0, (short)0, (short)0, (short)0); + Carbon.ProcessSerialNumber psn = new Carbon.ProcessSerialNumber(); + + GetCurrentProcess( ref psn ); + TransformProcessType (ref psn, 1); + SetFrontProcess (ref psn); + + HIObjectRegisterSubclass (__CFStringMakeConstantString ("com.novell.mwfview"), __CFStringMakeConstantString ("com.apple.hiview"), 0, Carbon.EventHandler.EventHandlerDelegate, (uint)Carbon.EventHandler.HIObjectEvents.Length, Carbon.EventHandler.HIObjectEvents, IntPtr.Zero, ref Subclass); + + Carbon.EventHandler.InstallApplicationHandler (); + + CreateNewWindow (Carbon.WindowClass.kDocumentWindowClass, Carbon.WindowAttributes.kWindowStandardHandlerAttribute | Carbon.WindowAttributes.kWindowCloseBoxAttribute | Carbon.WindowAttributes.kWindowFullZoomAttribute | Carbon.WindowAttributes.kWindowCollapseBoxAttribute | Carbon.WindowAttributes.kWindowResizableAttribute | Carbon.WindowAttributes.kWindowCompositingAttribute, ref rect, ref FosterParent); + + CreateNewWindow (Carbon.WindowClass.kOverlayWindowClass, Carbon.WindowAttributes.kWindowNoUpdatesAttribute | Carbon.WindowAttributes.kWindowNoActivatesAttribute, ref rect, ref ReverseWindow); + CreateNewWindow (Carbon.WindowClass.kOverlayWindowClass, Carbon.WindowAttributes.kWindowNoUpdatesAttribute | Carbon.WindowAttributes.kWindowNoActivatesAttribute, ref rect, ref CaretWindow); + + // Get some values about bar heights + Carbon.Rect structRect = new Carbon.Rect (); + Carbon.Rect contentRect = new Carbon.Rect (); + GetWindowBounds (FosterParent, 32, ref structRect); + GetWindowBounds (FosterParent, 33, ref contentRect); + + MenuBarHeight = GetMBarHeight (); + + // Focus + FocusWindow = IntPtr.Zero; + + // Message loop + GetMessageResult = true; + + ReverseWindowMapped = false; + } + + internal void PerformNCCalc(Hwnd hwnd) { + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + IntPtr ptr; + Rectangle rect; + + rect = new Rectangle (0, 0, hwnd.Width, hwnd.Height); + + ncp = new XplatUIWin32.NCCALCSIZE_PARAMS(); + ptr = Marshal.AllocHGlobal(Marshal.SizeOf(ncp)); + + ncp.rgrc1.left = rect.Left; + ncp.rgrc1.top = rect.Top; + ncp.rgrc1.right = rect.Right; + ncp.rgrc1.bottom = rect.Bottom; + + Marshal.StructureToPtr(ncp, ptr, true); + NativeWindow.WndProc(hwnd.client_window, Msg.WM_NCCALCSIZE, (IntPtr)1, ptr); + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(ptr, typeof(XplatUIWin32.NCCALCSIZE_PARAMS)); + Marshal.FreeHGlobal(ptr); + + + rect = new Rectangle(ncp.rgrc1.left, ncp.rgrc1.top, ncp.rgrc1.right - ncp.rgrc1.left, ncp.rgrc1.bottom - ncp.rgrc1.top); + hwnd.ClientRect = rect; + + rect = TranslateClientRectangleToQuartzClientRectangle (hwnd); + + if (hwnd.visible) { + Carbon.HIRect r = new Carbon.HIRect (rect.X, rect.Y, rect.Width, rect.Height); + HIViewSetFrame (hwnd.client_window, ref r); + } + + AddExpose (hwnd, false, 0, 0, hwnd.Width, hwnd.Height); + } + + internal void ScreenToClient(IntPtr handle, ref Carbon.QDPoint point) { + int x = (int) point.x; + int y = (int) point.y; + + ScreenToClient (handle, ref x, ref y); + + point.x = (short) x; + point.y = (short) y; + } + + internal static Rectangle TranslateClientRectangleToQuartzClientRectangle (Hwnd hwnd) { + return TranslateClientRectangleToQuartzClientRectangle (hwnd, Widget.FromHandle (hwnd.Handle)); + } + + internal static Rectangle TranslateClientRectangleToQuartzClientRectangle (Hwnd hwnd, Widget ctrl) { + /* From XplatUIX11 + * If this is a form with no window manager, X is handling all the border and caption painting + * so remove that from the area (since the area we set of the window here is the part of the window + * we're painting in only) + */ + Rectangle rect = hwnd.ClientRect; + Form form = ctrl as Form; + CreateParams cp = null; + + if (form != null) + cp = form.GetCreateParams (); + + if (form != null && (form.window_manager == null || cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW))) { + Hwnd.Borders borders = Hwnd.GetBorders (cp, null); + Rectangle qrect = rect; + + qrect.Y -= borders.top; + qrect.X -= borders.left; + qrect.Width += borders.left + borders.right; + qrect.Height += borders.top + borders.bottom; + + rect = qrect; + } + + if (rect.Width < 1 || rect.Height < 1) { + rect.Width = 1; + rect.Height = 1; + rect.X = -5; + rect.Y = -5; + } + + return rect; + } + + internal static Size TranslateWindowSizeToQuartzWindowSize (CreateParams cp) { + return TranslateWindowSizeToQuartzWindowSize (cp, new Size (cp.Width, cp.Height)); + } + + internal static Size TranslateWindowSizeToQuartzWindowSize (CreateParams cp, Size size) { + /* From XplatUIX11 + * If this is a form with no window manager, X is handling all the border and caption painting + * so remove that from the area (since the area we set of the window here is the part of the window + * we're painting in only) + */ + Form form = cp.control as Form; + if (form != null && (form.window_manager == null || cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW))) { + Hwnd.Borders borders = Hwnd.GetBorders (cp, null); + Size qsize = size; + + qsize.Width -= borders.left + borders.right; + qsize.Height -= borders.top + borders.bottom; + + size = qsize; + } + + if (size.Height == 0) + size.Height = 1; + if (size.Width == 0) + size.Width = 1; + return size; + } + + internal static Size TranslateQuartzWindowSizeToWindowSize (CreateParams cp, int width, int height) { + /* From XplatUIX11 + * If this is a form with no window manager, X is handling all the border and caption painting + * so remove that from the area (since the area we set of the window here is the part of the window + * we're painting in only) + */ + Size size = new Size (width, height); + Form form = cp.control as Form; + if (form != null && (form.window_manager == null || cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW))) { + Hwnd.Borders borders = Hwnd.GetBorders (cp, null); + Size qsize = size; + + qsize.Width += borders.left + borders.right; + qsize.Height += borders.top + borders.bottom; + + size = qsize; + } + + return size; + } + #endregion + + #region Callbacks + private void CaretCallback (object sender, EventArgs e) { + if (Caret.Paused) { + return; + } + + if (!Caret.On) { + ShowCaret (); + } else { + HideCaret (); + } + } + + private void HoverCallback (object sender, EventArgs e) { + if ((Hover.X == mouse_position.X) && (Hover.Y == mouse_position.Y)) { + MSG msg = new MSG (); + msg.hwnd = Hover.Hwnd; + msg.message = Msg.WM_MOUSEHOVER; + msg.wParam = GetMousewParam (0); + msg.lParam = (IntPtr)((ushort)Hover.X << 16 | (ushort)Hover.X); + EnqueueMessage (msg); + } + } + #endregion + + #region Private Methods + private Point ConvertScreenPointToClient (IntPtr handle, Point point) { + Point converted_point = new Point (); + Carbon.Rect window_bounds = new Carbon.Rect (); + Carbon.CGPoint native_point = new Carbon.CGPoint (); + + GetWindowBounds (HIViewGetWindow (handle), 32, ref window_bounds); + + native_point.x = (point.X - window_bounds.left); + native_point.y = (point.Y - window_bounds.top); + + HIViewConvertPoint (ref native_point, IntPtr.Zero, handle); + + converted_point.X = (int)native_point.x; + converted_point.Y = (int)native_point.y; + + return converted_point; + } + + private Point ConvertClientPointToScreen (IntPtr handle, Point point) { + Point converted_point = new Point (); + Carbon.Rect window_bounds = new Carbon.Rect (); + Carbon.CGPoint native_point = new Carbon.CGPoint (); + + GetWindowBounds (HIViewGetWindow (handle), 32, ref window_bounds); + + native_point.x = point.X; + native_point.y = point.Y; + + HIViewConvertPoint (ref native_point, handle, IntPtr.Zero); + + converted_point.X = (int)(native_point.x + window_bounds.left); + converted_point.Y = (int)(native_point.y + window_bounds.top); + + return converted_point; + } + + private double NextTimeout () { + DateTime now = DateTime.UtcNow; + int timeout = 0x7FFFFFF; + lock (TimerList) { + foreach (Timer timer in TimerList) { + int next = (int) (timer.Expires - now).TotalMilliseconds; + if (next < 0) + return 0; + if (next < timeout) + timeout = next; + } + } + if (timeout < Timer.Minimum) + timeout = Timer.Minimum; + + return (double)((double)timeout/1000); + } + + private void CheckTimers (DateTime now) { + lock (TimerList) { + int count = TimerList.Count; + if (count == 0) + return; + for (int i = 0; i < TimerList.Count; i++) { + Timer timer = (Timer) TimerList [i]; + if (timer.Enabled && timer.Expires <= now) { + // Timer ticks: + // - Before MainForm.OnLoad if DoEvents () is called. + // - After MainForm.OnLoad if not. + // + if (in_doevents || + (Application.MWFThread.Current.Context != null && + Application.MWFThread.Current.Context.MainForm != null && + Application.MWFThread.Current.Context.MainForm.IsLoaded)) { + timer.FireTick (); + timer.Update (now); + } + } + } + } + } + + private void WaitForHwndMessage (Hwnd hwnd, Msg message) { + MSG msg = new MSG (); + + bool done = false; + do { + if (GetMessage(null, ref msg, IntPtr.Zero, 0, 0)) { + if ((Msg)msg.message == Msg.WM_QUIT) { + PostQuitMessage (0); + done = true; + } + else { + if (msg.hwnd == hwnd.Handle) { + if ((Msg)msg.message == message) + break; + else if ((Msg)msg.message == Msg.WM_DESTROY) + done = true; + } + + TranslateMessage (ref msg); + DispatchMessage (ref msg); + } + } + } while (!done); + } + + private void SendParentNotify(IntPtr child, Msg cause, int x, int y) { + Hwnd hwnd; + + if (child == IntPtr.Zero) { + return; + } + + hwnd = Hwnd.GetObjectFromWindow (child); + + if (hwnd == null) { + return; + } + + if (hwnd.Handle == IntPtr.Zero) { + return; + } + + if (ExStyleSet ((int) hwnd.initial_ex_style, WindowExStyles.WS_EX_NOPARENTNOTIFY)) { + return; + } + + if (hwnd.Parent == null) { + return; + } + + if (hwnd.Parent.Handle == IntPtr.Zero) { + return; + } + + if (cause == Msg.WM_CREATE || cause == Msg.WM_DESTROY) { + SendMessage(hwnd.Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), child); + } else { + SendMessage(hwnd.Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), Widget.MakeParam(x, y)); + } + + SendParentNotify (hwnd.Parent.Handle, cause, x, y); + } + + private bool StyleSet (int s, WindowStyles ws) { + return (s & (int)ws) == (int)ws; + } + + private bool ExStyleSet (int ex, WindowExStyles exws) { + return (ex & (int)exws) == (int)exws; + } + + private void DeriveStyles(int Style, int ExStyle, out FormBorderStyle border_style, out bool border_static, out TitleStyle title_style, out int caption_height, out int tool_caption_height) { + + caption_height = 0; + tool_caption_height = 0; + border_static = false; + + if (StyleSet (Style, WindowStyles.WS_CHILD)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) { + border_style = FormBorderStyle.Fixed3D; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (!StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.None; + } else { + border_style = FormBorderStyle.FixedSingle; + } + title_style = TitleStyle.None; + + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + caption_height = 0; + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + title_style = TitleStyle.Tool; + } else { + title_style = TitleStyle.Normal; + } + } + + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_MDICHILD)) { + caption_height = 0; + + if (StyleSet (Style, WindowStyles.WS_OVERLAPPEDWINDOW) || + ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + border_style = (FormBorderStyle) 0xFFFF; + } else { + border_style = FormBorderStyle.None; + } + } + + } else { + title_style = TitleStyle.None; + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + title_style = TitleStyle.Tool; + } else { + title_style = TitleStyle.Normal; + } + } + + border_style = FormBorderStyle.None; + + if (StyleSet (Style, WindowStyles.WS_THICKFRAME)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + border_style = FormBorderStyle.SizableToolWindow; + } else { + border_style = FormBorderStyle.Sizable; + } + } else { + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) { + border_style = FormBorderStyle.Fixed3D; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_DLGMODALFRAME)) { + border_style = FormBorderStyle.FixedDialog; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + border_style = FormBorderStyle.FixedToolWindow; + } else if (StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.FixedSingle; + } + } else { + if (StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.FixedSingle; + } + } + } + } + } + + private void SetHwndStyles(Hwnd hwnd, CreateParams cp) { + DeriveStyles(cp.Style, cp.ExStyle, out hwnd.border_style, out hwnd.border_static, out hwnd.title_style, out hwnd.caption_height, out hwnd.tool_caption_height); + } + + private void ShowCaret () { + if (Caret.On) + return; + Caret.On = true; + ShowWindow (CaretWindow); + Graphics g = Graphics.FromHwnd (HIViewGetRoot (CaretWindow)); + + g.FillRectangle (new SolidBrush (Color.Black), new Rectangle (0, 0, Caret.Width, Caret.Height)); + + g.Dispose (); + } + + private void HideCaret () { + if (!Caret.On) + return; + Caret.On = false; + HideWindow (CaretWindow); + } + + private void AccumulateDestroyedHandles (Widget c, ArrayList list) { + if (c != null) { + Widget[] Widgets = c.Widgets.GetAllWidgets (); + + if (c.IsHandleCreated && !c.IsDisposed) { + Hwnd hwnd = Hwnd.ObjectFromHandle(c.Handle); + + list.Add (hwnd); + CleanupCachedWindows (hwnd); + } + + for (int i = 0; i < Widgets.Length; i ++) { + AccumulateDestroyedHandles (Widgets[i], list); + } + } + + } + + private void CleanupCachedWindows (Hwnd hwnd) { + if (ActiveWindow == hwnd.Handle) { + SendMessage(hwnd.client_window, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); + ActiveWindow = IntPtr.Zero; + } + + if (FocusWindow == hwnd.Handle) { + SendMessage(hwnd.client_window, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero); + FocusWindow = IntPtr.Zero; + } + + if (Grab.Hwnd == hwnd.Handle) { + Grab.Hwnd = IntPtr.Zero; + Grab.Confined = false; + } + + DestroyCaret (hwnd.Handle); + } + + private void AddExpose (Hwnd hwnd, bool client, int x, int y, int width, int height) { + // Don't waste time + if ((hwnd == null) || (x > hwnd.Width) || (y > hwnd.Height) || ((x + width) < 0) || ((y + height) < 0)) { + return; + } + + // Keep the invalid area as small as needed + if ((x + width) > hwnd.width) { + width = hwnd.width - x; + } + + if ((y + height) > hwnd.height) { + height = hwnd.height - y; + } + + if (client) { + hwnd.AddInvalidArea(x, y, width, height); + if (!hwnd.expose_pending && hwnd.visible) { + MSG msg = new MSG (); + msg.message = Msg.WM_PAINT; + msg.hwnd = hwnd.Handle; + EnqueueMessage (msg); + hwnd.expose_pending = true; + } + } else { + hwnd.AddNcInvalidArea (x, y, width, height); + if (!hwnd.nc_expose_pending && hwnd.visible) { + MSG msg = new MSG (); + Region rgn = new Region (hwnd.Invalid); + IntPtr hrgn = rgn.GetHrgn (null); // Graphics object isn't needed + msg.message = Msg.WM_NCPAINT; + msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn; + msg.refobject = rgn; + msg.hwnd = hwnd.Handle; + EnqueueMessage (msg); + hwnd.nc_expose_pending = true; + + } + } + } + #endregion + + #region Public Methods + internal void EnqueueMessage (MSG msg) { + lock (queuelock) { + MessageQueue.Enqueue (msg); + } + } + + internal override void RaiseIdle (EventArgs e) + { + if (Idle != null) + Idle (this, e); + } + + internal override IntPtr InitializeDriver() { + return IntPtr.Zero; + } + + internal override void ShutdownDriver(IntPtr token) { + } + + internal override void EnableThemes() { + themes_enabled = true; + } + + internal override void Activate(IntPtr handle) { + if (ActiveWindow != IntPtr.Zero) { + UnactiveWindow = ActiveWindow; + ActivateWindow (HIViewGetWindow (ActiveWindow), false); + } + ActivateWindow (HIViewGetWindow (handle), true); + ActiveWindow = handle; + } + + internal override void AudibleAlert(AlertType alert) { + AlertSoundPlay (); + } + + internal override void BeginMoveResize (IntPtr handle) { + } + + internal override void CaretVisible (IntPtr hwnd, bool visible) { + if (Caret.Hwnd == hwnd) { + if (visible) { + if (Caret.Visible < 1) { + Caret.Visible++; + Caret.On = false; + if (Caret.Visible == 1) { + ShowCaret (); + Caret.Timer.Start (); + } + } + } else { + Caret.Visible--; + if (Caret.Visible == 0) { + Caret.Timer.Stop (); + HideCaret (); + } + } + } + } + + internal override bool CalculateWindowRect(ref Rectangle ClientRect, CreateParams cp, Menu menu, out Rectangle WindowRect) { + WindowRect = Hwnd.GetWindowRectangle (cp, menu, ClientRect); + return true; + } + + internal override void ClientToScreen(IntPtr handle, ref int x, ref int y) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + Point point = ConvertClientPointToScreen (hwnd.ClientWindow, new Point (x, y)); + + x = point.X; + y = point.Y; + } + + internal override void MenuToScreen(IntPtr handle, ref int x, ref int y) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + Point point = ConvertClientPointToScreen (hwnd.ClientWindow, new Point (x, y)); + + x = point.X; + y = point.Y; + } + + internal override int[] ClipboardAvailableFormats(IntPtr handle) { + ArrayList list = new ArrayList (); + DataFormats.Format f = DataFormats.Format.List; + + while (f != null) { + list.Add (f.Id); + f = f.Next; + } + + return (int [])list.ToArray (typeof (int)); + } + + internal override void ClipboardClose(IntPtr handle) { + } + + //TODO: Map our internal formats to the right os code where we can + internal override int ClipboardGetID(IntPtr handle, string format) { + return (int)__CFStringMakeConstantString (format); + } + + internal override IntPtr ClipboardOpen(bool primary_selection) { + if (primary_selection) + return Carbon.Pasteboard.Primary; + return Carbon.Pasteboard.Application; + } + + internal override object ClipboardRetrieve(IntPtr handle, int type, XplatUI.ClipboardToObject converter) { + return Carbon.Pasteboard.Retrieve (handle, type); + } + + internal override void ClipboardStore(IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter, bool copy) { + Carbon.Pasteboard.Store (handle, obj, type); + } + + internal override void CreateCaret (IntPtr hwnd, int width, int height) { + if (Caret.Hwnd != IntPtr.Zero) + DestroyCaret (Caret.Hwnd); + + Caret.Hwnd = hwnd; + Caret.Width = width; + Caret.Height = height; + Caret.Visible = 0; + Caret.On = false; + } + + internal override IntPtr CreateWindow(CreateParams cp) { + Hwnd hwnd; + Hwnd parent_hwnd = null; + int X; + int Y; + int Width; + int Height; + IntPtr ParentHandle; + IntPtr WindowHandle; + IntPtr WholeWindow; + IntPtr ClientWindow; + IntPtr WholeWindowTracking; + IntPtr ClientWindowTracking; + + hwnd = new Hwnd (); + + X = cp.X; + Y = cp.Y; + Width = cp.Width; + Height = cp.Height; + ParentHandle = IntPtr.Zero; + WindowHandle = IntPtr.Zero; + WholeWindow = IntPtr.Zero; + ClientWindow = IntPtr.Zero; + WholeWindowTracking = IntPtr.Zero; + ClientWindowTracking = IntPtr.Zero; + + if (Width < 1) Width = 1; + if (Height < 1) Height = 1; + + if (cp.Parent != IntPtr.Zero) { + parent_hwnd = Hwnd.ObjectFromHandle (cp.Parent); + ParentHandle = parent_hwnd.client_window; + } else { + if (StyleSet (cp.Style, WindowStyles.WS_CHILD)) { + HIViewFindByID (HIViewGetRoot (FosterParent), new Carbon.HIViewID (Carbon.EventHandler.kEventClassWindow, 1), ref ParentHandle); + } + } + + Point next; + if (cp.control is Form) { + next = Hwnd.GetNextStackedFormLocation (cp, parent_hwnd); + X = next.X; + Y = next.Y; + } + + hwnd.x = X; + hwnd.y = Y; + hwnd.width = Width; + hwnd.height = Height; + hwnd.Parent = Hwnd.ObjectFromHandle (cp.Parent); + hwnd.initial_style = cp.WindowStyle; + hwnd.initial_ex_style = cp.WindowExStyle; + hwnd.visible = false; + + if (StyleSet (cp.Style, WindowStyles.WS_DISABLED)) { + hwnd.enabled = false; + } + + ClientWindow = IntPtr.Zero; + + Size QWindowSize = TranslateWindowSizeToQuartzWindowSize (cp); + Rectangle QClientRect = TranslateClientRectangleToQuartzClientRectangle (hwnd, cp.control); + + SetHwndStyles(hwnd, cp); +/* FIXME */ + if (ParentHandle == IntPtr.Zero) { + IntPtr WindowView = IntPtr.Zero; + IntPtr GrowBox = IntPtr.Zero; + Carbon.WindowClass windowklass = Carbon.WindowClass.kOverlayWindowClass; + Carbon.WindowAttributes attributes = Carbon.WindowAttributes.kWindowCompositingAttribute | Carbon.WindowAttributes.kWindowStandardHandlerAttribute; + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZEBOX)) { + attributes |= Carbon.WindowAttributes.kWindowCollapseBoxAttribute; + } + if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZEBOX)) { + attributes |= Carbon.WindowAttributes.kWindowResizableAttribute | Carbon.WindowAttributes.kWindowHorizontalZoomAttribute | Carbon.WindowAttributes.kWindowVerticalZoomAttribute; + } + if (StyleSet (cp.Style, WindowStyles.WS_SYSMENU)) { + attributes |= Carbon.WindowAttributes.kWindowCloseBoxAttribute; + } + if (StyleSet (cp.Style, WindowStyles.WS_CAPTION)) { + windowklass = Carbon.WindowClass.kDocumentWindowClass; + } + if (hwnd.border_style == FormBorderStyle.FixedToolWindow) { + windowklass = Carbon.WindowClass.kUtilityWindowClass; + } else if (hwnd.border_style == FormBorderStyle.SizableToolWindow) { + attributes |= Carbon.WindowAttributes.kWindowResizableAttribute; + windowklass = Carbon.WindowClass.kUtilityWindowClass; + } + if (windowklass == Carbon.WindowClass.kOverlayWindowClass) { + attributes = Carbon.WindowAttributes.kWindowCompositingAttribute | Carbon.WindowAttributes.kWindowStandardHandlerAttribute; + } + attributes |= Carbon.WindowAttributes.kWindowLiveResizeAttribute; + + Carbon.Rect rect = new Carbon.Rect (); + if (StyleSet (cp.Style, WindowStyles.WS_POPUP)) { + SetRect (ref rect, (short)X, (short)(Y), (short)(X + QWindowSize.Width), (short)(Y + QWindowSize.Height)); + } else { + SetRect (ref rect, (short)X, (short)(Y + MenuBarHeight), (short)(X + QWindowSize.Width), (short)(Y + MenuBarHeight + QWindowSize.Height)); + } + + CreateNewWindow (windowklass, attributes, ref rect, ref WindowHandle); + + Carbon.EventHandler.InstallWindowHandler (WindowHandle); + HIViewFindByID (HIViewGetRoot (WindowHandle), new Carbon.HIViewID (Carbon.EventHandler.kEventClassWindow, 1), ref WindowView); + HIViewFindByID (HIViewGetRoot (WindowHandle), new Carbon.HIViewID (Carbon.EventHandler.kEventClassWindow, 7), ref GrowBox); + HIGrowBoxViewSetTransparent (GrowBox, true); + SetAutomaticWidgetDragTrackingEnabledForWindow (WindowHandle, true); + ParentHandle = WindowView; + } + + HIObjectCreate (__CFStringMakeConstantString ("com.novell.mwfview"), 0, ref WholeWindow); + HIObjectCreate (__CFStringMakeConstantString ("com.novell.mwfview"), 0, ref ClientWindow); + + Carbon.EventHandler.InstallWidgetHandler (WholeWindow); + Carbon.EventHandler.InstallWidgetHandler (ClientWindow); + + // Enable embedding on Widgets + HIViewChangeFeatures (WholeWindow, 1<<1, 0); + HIViewChangeFeatures (ClientWindow, 1<<1, 0); + + HIViewNewTrackingArea (WholeWindow, IntPtr.Zero, (UInt64)WholeWindow, ref WholeWindowTracking); + HIViewNewTrackingArea (ClientWindow, IntPtr.Zero, (UInt64)ClientWindow, ref ClientWindowTracking); + Carbon.HIRect WholeRect; + if (WindowHandle != IntPtr.Zero) { + WholeRect = new Carbon.HIRect (0, 0, QWindowSize.Width, QWindowSize.Height); + } else { + WholeRect = new Carbon.HIRect (X, Y, QWindowSize.Width, QWindowSize.Height); + } + Carbon.HIRect ClientRect = new Carbon.HIRect (QClientRect.X, QClientRect.Y, QClientRect.Width, QClientRect.Height); + HIViewSetFrame (WholeWindow, ref WholeRect); + HIViewSetFrame (ClientWindow, ref ClientRect); + + HIViewAddSubview (ParentHandle, WholeWindow); + HIViewAddSubview (WholeWindow, ClientWindow); + + hwnd.WholeWindow = WholeWindow; + hwnd.ClientWindow = ClientWindow; + + if (WindowHandle != IntPtr.Zero) { + WindowMapping [hwnd.Handle] = WindowHandle; + HandleMapping [WindowHandle] = hwnd.Handle; + if (hwnd.border_style == FormBorderStyle.FixedToolWindow || hwnd.border_style == FormBorderStyle.SizableToolWindow) { + UtilityWindows.Add (WindowHandle); + } + } + + // Allow dnd on Widgets + Dnd.SetAllowDrop (hwnd, true); + + Text (hwnd.Handle, cp.Caption); + + SendMessage (hwnd.Handle, Msg.WM_CREATE, (IntPtr)1, IntPtr.Zero /* XXX unused */); + SendParentNotify (hwnd.Handle, Msg.WM_CREATE, int.MaxValue, int.MaxValue); + + if (StyleSet (cp.Style, WindowStyles.WS_VISIBLE)) { + if (WindowHandle != IntPtr.Zero) { + if (Widget.FromHandle(hwnd.Handle) is Form) { + Form f = Widget.FromHandle(hwnd.Handle) as Form; + if (f.WindowState == FormWindowState.Normal) { + SendMessage(hwnd.Handle, Msg.WM_SHOWWINDOW, (IntPtr)1, IntPtr.Zero); + } + } + ShowWindow (WindowHandle); + WaitForHwndMessage (hwnd, Msg.WM_SHOWWINDOW); + } + HIViewSetVisible (WholeWindow, true); + HIViewSetVisible (ClientWindow, true); + hwnd.visible = true; + if (!(Widget.FromHandle(hwnd.Handle) is Form)) { + SendMessage(hwnd.Handle, Msg.WM_SHOWWINDOW, (IntPtr)1, IntPtr.Zero); + } + } + + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZE)) { + SetWindowState(hwnd.Handle, FormWindowState.Minimized); + } else if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZE)) { + SetWindowState(hwnd.Handle, FormWindowState.Maximized); + } + + return hwnd.Handle; + } + + internal override IntPtr CreateWindow(IntPtr Parent, int X, int Y, int Width, int Height) { + CreateParams create_params = new CreateParams(); + + create_params.Caption = ""; + create_params.X = X; + create_params.Y = Y; + create_params.Width = Width; + create_params.Height = Height; + + create_params.ClassName=XplatUI.GetDefaultClassName (GetType ()); + create_params.ClassStyle = 0; + create_params.ExStyle=0; + create_params.Parent=IntPtr.Zero; + create_params.Param=0; + + return CreateWindow(create_params); + } + + internal override Bitmap DefineStdCursorBitmap (StdCursor id) { + return Carbon.Cursor.DefineStdCursorBitmap (id); + } + + internal override IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) { + return Carbon.Cursor.DefineCursor (bitmap, mask, cursor_pixel, mask_pixel, xHotSpot, yHotSpot); + } + + internal override IntPtr DefineStdCursor (StdCursor id) { + return Carbon.Cursor.DefineStdCursor (id); + } + + internal override IntPtr DefWndProc(ref Message msg) { + Hwnd hwnd = Hwnd.ObjectFromHandle (msg.HWnd); + switch ((Msg)msg.Msg) { + case Msg.WM_IME_COMPOSITION: + string s = KeyboardHandler.ComposedString; + foreach (char c in s) + SendMessage (msg.HWnd, Msg.WM_IME_CHAR, (IntPtr) c, msg.LParam); + break; + case Msg.WM_IME_CHAR: + // On Windows API it sends two WM_CHAR messages for each byte, but + // I wonder if it is worthy to emulate it (also no idea how to + // reconstruct those bytes into chars). + SendMessage (msg.HWnd, Msg.WM_CHAR, msg.WParam, msg.LParam); + return IntPtr.Zero; + case Msg.WM_QUIT: { + if (WindowMapping [hwnd.Handle] != null) + + Exit (); + break; + } + case Msg.WM_PAINT: { + hwnd.expose_pending = false; + break; + } + case Msg.WM_NCPAINT: { + hwnd.nc_expose_pending = false; + break; + } + case Msg.WM_NCCALCSIZE: { + if (msg.WParam == (IntPtr)1) { + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (msg.LParam, typeof (XplatUIWin32.NCCALCSIZE_PARAMS)); + + // Add all the stuff X is supposed to draw. + Widget ctrl = Widget.FromHandle (hwnd.Handle); + if (ctrl != null) { + Hwnd.Borders rect = Hwnd.GetBorders (ctrl.GetCreateParams (), null); + + ncp.rgrc1.top += rect.top; + ncp.rgrc1.bottom -= rect.bottom; + ncp.rgrc1.left += rect.left; + ncp.rgrc1.right -= rect.right; + + Marshal.StructureToPtr (ncp, msg.LParam, true); + } + } + break; + } + case Msg.WM_SETCURSOR: { + // Pass to parent window first + while ((hwnd.parent != null) && (msg.Result == IntPtr.Zero)) { + hwnd = hwnd.parent; + msg.Result = NativeWindow.WndProc(hwnd.Handle, Msg.WM_SETCURSOR, msg.HWnd, msg.LParam); + } + + if (msg.Result == IntPtr.Zero) { + IntPtr handle; + + switch((HitTest)(msg.LParam.ToInt32() & 0xffff)) { + case HitTest.HTBOTTOM: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBORDER: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBOTTOMLEFT: handle = Cursors.SizeNESW.handle; break; + case HitTest.HTBOTTOMRIGHT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTERROR: if ((msg.LParam.ToInt32() >> 16) == (int)Msg.WM_LBUTTONDOWN) { + //FIXME: AudibleAlert(); + } + handle = Cursors.Default.handle; + break; + + case HitTest.HTHELP: handle = Cursors.Help.handle; break; + case HitTest.HTLEFT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTRIGHT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTTOP: handle = Cursors.SizeNS.handle; break; + case HitTest.HTTOPLEFT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTTOPRIGHT: handle = Cursors.SizeNESW.handle; break; + + #if SameAsDefault + case HitTest.HTGROWBOX: + case HitTest.HTSIZE: + case HitTest.HTZOOM: + case HitTest.HTVSCROLL: + case HitTest.HTSYSMENU: + case HitTest.HTREDUCE: + case HitTest.HTNOWHERE: + case HitTest.HTMAXBUTTON: + case HitTest.HTMINBUTTON: + case HitTest.HTMENU: + case HitTest.HSCROLL: + case HitTest.HTBOTTOM: + case HitTest.HTCAPTION: + case HitTest.HTCLIENT: + case HitTest.HTCLOSE: + #endif + default: handle = Cursors.Default.handle; break; + } + SetCursor(msg.HWnd, handle); + } + return (IntPtr)1; + } + } + return IntPtr.Zero; + } + + internal override void DestroyCaret (IntPtr hwnd) { + if (Caret.Hwnd == hwnd) { + if (Caret.Visible == 1) { + Caret.Timer.Stop (); + HideCaret (); + } + Caret.Hwnd = IntPtr.Zero; + Caret.Visible = 0; + Caret.On = false; + } + } + + [MonoTODO] + internal override void DestroyCursor(IntPtr cursor) { + throw new NotImplementedException (); + } + + internal override void DestroyWindow(IntPtr handle) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) { + return; + } + + SendParentNotify (hwnd.Handle, Msg.WM_DESTROY, int.MaxValue, int.MaxValue); + + CleanupCachedWindows (hwnd); + + ArrayList windows = new ArrayList (); + + AccumulateDestroyedHandles (Widget.WidgetNativeWindow.WidgetFromHandle(hwnd.Handle), windows); + + + foreach (Hwnd h in windows) { + SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero); + h.zombie = true; + } + + // TODO: This is crashing swf-messageboxes + /* + if (false && hwnd.whole_window != IntPtr.Zero) + CFRelease (hwnd.whole_window); + if (false && hwnd.client_window != IntPtr.Zero) + CFRelease (hwnd.client_window); + */ + + if (WindowMapping [hwnd.Handle] != null) { + DisposeWindow ((IntPtr)(WindowMapping [hwnd.Handle])); + WindowMapping.Remove (hwnd.Handle); + } + } + + internal override IntPtr DispatchMessage(ref MSG msg) { + return NativeWindow.WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + + internal override void DoEvents() { + MSG msg = new MSG (); + + in_doevents = true; + while (PeekMessage (null, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) { + TranslateMessage (ref msg); + DispatchMessage (ref msg); + } + in_doevents = false; + + } + + internal override void EnableWindow(IntPtr handle, bool Enable) { + //Like X11 we need not do anything here + } + + internal override void EndLoop(Thread thread) { + } + + internal void Exit () { + GetMessageResult = false; + } + + internal override IntPtr GetActive() { + return ActiveWindow; + } + + internal override Region GetClipRegion(IntPtr hwnd) { + return null; + } + + [MonoTODO] + internal override void GetCursorInfo(IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y) { + width = 12; + height = 12; + hotspot_x = 0; + hotspot_y = 0; + } + + internal override void GetDisplaySize(out Size size) { + Carbon.HIRect bounds = CGDisplayBounds (CGMainDisplayID ()); + size = new Size ((int)bounds.size.width, (int)bounds.size.height); + } + + internal override IntPtr GetParent(IntPtr handle) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd != null && hwnd.Parent != null) { + return hwnd.Parent.Handle; + } + return IntPtr.Zero; + } + + internal override IntPtr GetPreviousWindow(IntPtr handle) { + return HIViewGetPreviousView(handle); + } + + internal override void GetCursorPos(IntPtr handle, out int x, out int y) { + Carbon.QDPoint pt = new Carbon.QDPoint (); + GetGlobalMouse (ref pt); + x = pt.x; + y = pt.y; + } + + internal override IntPtr GetFocus() { + return FocusWindow; + } + + + internal override bool GetFontMetrics(Graphics g, Font font, out int ascent, out int descent) { + FontFamily ff = font.FontFamily; + ascent = ff.GetCellAscent (font.Style); + descent = ff.GetCellDescent (font.Style); + return true; + } + + internal override Point GetMenuOrigin(IntPtr handle) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) { + return hwnd.MenuOrigin; + } + return Point.Empty; + } + + internal override bool GetMessage(object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) { + IntPtr evtRef = IntPtr.Zero; + IntPtr target = GetEventDispatcherTarget(); + CheckTimers (DateTime.UtcNow); + ReceiveNextEvent (0, IntPtr.Zero, 0, true, ref evtRef); + if (evtRef != IntPtr.Zero && target != IntPtr.Zero) { + SendEventToEventTarget (evtRef, target); + ReleaseEvent (evtRef); + } + + object queueobj; + loop: + lock (queuelock) { + + if (MessageQueue.Count <= 0) { + if (Idle != null) + Idle (this, EventArgs.Empty); + else if (TimerList.Count == 0) { + ReceiveNextEvent (0, IntPtr.Zero, 0.15, true, ref evtRef); + if (evtRef != IntPtr.Zero && target != IntPtr.Zero) { + SendEventToEventTarget (evtRef, target); + ReleaseEvent (evtRef); + } + } else { + ReceiveNextEvent (0, IntPtr.Zero, NextTimeout (), true, ref evtRef); + if (evtRef != IntPtr.Zero && target != IntPtr.Zero) { + SendEventToEventTarget (evtRef, target); + ReleaseEvent (evtRef); + } + } + msg.hwnd = IntPtr.Zero; + msg.message = Msg.WM_ENTERIDLE; + return GetMessageResult; + } + queueobj = MessageQueue.Dequeue (); + } + if (queueobj is GCHandle) { + XplatUIDriverSupport.ExecuteClientMessage((GCHandle)queueobj); + goto loop; + } else { + msg = (MSG)queueobj; + } + return GetMessageResult; + } + + [MonoTODO] + internal override bool GetText(IntPtr handle, out string text) { + throw new NotImplementedException (); + } + + internal override void GetWindowPos(IntPtr handle, bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) { + x = hwnd.x; + y = hwnd.y; + width = hwnd.width; + height = hwnd.height; + + PerformNCCalc(hwnd); + + client_width = hwnd.ClientRect.Width; + client_height = hwnd.ClientRect.Height; + + return; + } + + // Should we throw an exception or fail silently? + // throw new ArgumentException("Called with an invalid window handle", "handle"); + + x = 0; + y = 0; + width = 0; + height = 0; + client_width = 0; + client_height = 0; + } + + internal override FormWindowState GetWindowState(IntPtr hwnd) { + IntPtr window = HIViewGetWindow (hwnd); + + if (IsWindowCollapsed (window)) + return FormWindowState.Minimized; + if (IsWindowInStandardState (window, IntPtr.Zero, IntPtr.Zero)) + return FormWindowState.Maximized; + + return FormWindowState.Normal; + } + + internal override void GrabInfo(out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea) { + handle = Grab.Hwnd; + GrabConfined = Grab.Confined; + GrabArea = Grab.Area; + } + + internal override void GrabWindow(IntPtr handle, IntPtr confine_to_handle) { + Grab.Hwnd = handle; + Grab.Confined = confine_to_handle != IntPtr.Zero; + /* FIXME: Set the Grab.Area */ + } + + internal override void UngrabWindow(IntPtr hwnd) { + bool was_grabbed = Grab.Hwnd != IntPtr.Zero; + + Grab.Hwnd = IntPtr.Zero; + Grab.Confined = false; + + if (was_grabbed) { + // lparam should be the handle to the window gaining the mouse capture, + // but we dont have that information like X11. + // Also only generate WM_CAPTURECHANGED if the window actually was grabbed. + SendMessage (hwnd, Msg.WM_CAPTURECHANGED, IntPtr.Zero, IntPtr.Zero); + } + } + + internal override void HandleException(Exception e) { + StackTrace st = new StackTrace(e); + Console.WriteLine("Exception '{0}'", e.Message+st.ToString()); + Console.WriteLine("{0}{1}", e.Message, st.ToString()); + } + + internal override void Invalidate (IntPtr handle, Rectangle rc, bool clear) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (clear) { + AddExpose (hwnd, true, hwnd.X, hwnd.Y, hwnd.Width, hwnd.Height); + } else { + AddExpose (hwnd, true, rc.X, rc.Y, rc.Width, rc.Height); + } + } + + internal override void InvalidateNC (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + AddExpose (hwnd, false, 0, 0, hwnd.Width, hwnd.Height); + } + + internal override bool IsEnabled(IntPtr handle) { + return Hwnd.ObjectFromHandle(handle).Enabled; + } + + internal override bool IsVisible(IntPtr handle) { + return Hwnd.ObjectFromHandle(handle).visible; + } + + internal override void KillTimer(Timer timer) { + lock (TimerList) { + TimerList.Remove(timer); + } + } + + + internal override void OverrideCursor(IntPtr cursor) { + } + + internal override PaintEventArgs PaintEventStart(ref Message msg, IntPtr handle, bool client) { + PaintEventArgs paint_event; + Hwnd hwnd; + Hwnd paint_hwnd; + + hwnd = Hwnd.ObjectFromHandle(msg.HWnd); + if (msg.HWnd == handle) { + paint_hwnd = hwnd; + } else { + paint_hwnd = Hwnd.ObjectFromHandle (handle); + } + + if (Caret.Visible == 1) { + Caret.Paused = true; + HideCaret(); + } + + Graphics dc; + + if (client) { + dc = Graphics.FromHwnd (paint_hwnd.client_window); + + Region clip_region = new Region (); + clip_region.MakeEmpty(); + + foreach (Rectangle r in hwnd.ClipRectangles) { + /* Expand the region slightly. + * See bug 464464. + */ + Rectangle r2 = Rectangle.FromLTRB (r.Left, r.Top, r.Right, r.Bottom + 1); + clip_region.Union (r2); + } + + if (hwnd.UserClip != null) { + clip_region.Intersect(hwnd.UserClip); + } + + // FIXME: Clip region is hosed + dc.Clip = clip_region; + paint_event = new PaintEventArgs(dc, hwnd.Invalid); + hwnd.expose_pending = false; + hwnd.ClearInvalidArea(); + + hwnd.drawing_stack.Push (paint_event); + hwnd.drawing_stack.Push (dc); + } else { + dc = Graphics.FromHwnd (paint_hwnd.whole_window); + + if (!hwnd.nc_invalid.IsEmpty) { + // FIXME: Clip region is hosed + dc.SetClip (hwnd.nc_invalid); + paint_event = new PaintEventArgs(dc, hwnd.nc_invalid); + } else { + paint_event = new PaintEventArgs(dc, new Rectangle(0, 0, hwnd.width, hwnd.height)); + } + hwnd.nc_expose_pending = false; + hwnd.ClearNcInvalidArea (); + + hwnd.drawing_stack.Push (paint_event); + hwnd.drawing_stack.Push (dc); + } + + return paint_event; + } + + internal override void PaintEventEnd(ref Message msg, IntPtr handle, bool client) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + // FIXME: Pop is causing invalid stack ops sometimes; race condition? + try { + Graphics dc = (Graphics)hwnd.drawing_stack.Pop(); + dc.Flush (); + dc.Dispose (); + + PaintEventArgs pe = (PaintEventArgs)hwnd.drawing_stack.Pop(); + pe.SetGraphics (null); + pe.Dispose (); + } catch {} + + if (Caret.Visible == 1) { + ShowCaret(); + Caret.Paused = false; + } + } + + internal override bool PeekMessage(Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) { + IntPtr evtRef = IntPtr.Zero; + IntPtr target = GetEventDispatcherTarget(); + CheckTimers (DateTime.UtcNow); + ReceiveNextEvent (0, IntPtr.Zero, 0, true, ref evtRef); + if (evtRef != IntPtr.Zero && target != IntPtr.Zero) { + SendEventToEventTarget (evtRef, target); + ReleaseEvent (evtRef); + } + + lock (queuelock) { + if (MessageQueue.Count <= 0) { + return false; + } else { + object queueobj; + if (flags == (uint)PeekMessageFlags.PM_REMOVE) + queueobj = MessageQueue.Dequeue (); + else + queueobj = MessageQueue.Peek (); + + if (queueobj is GCHandle) { + XplatUIDriverSupport.ExecuteClientMessage((GCHandle)queueobj); + return false; + } + msg = (MSG)queueobj; + return true; + } + } + } + + internal override bool PostMessage (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) { + MSG msg = new MSG(); + msg.hwnd = hwnd; + msg.message = message; + msg.wParam = wParam; + msg.lParam = lParam; + EnqueueMessage (msg); + return true; + } + + internal override void PostQuitMessage(int exitCode) { + PostMessage (FosterParent, Msg.WM_QUIT, IntPtr.Zero, IntPtr.Zero); + } + + internal override void RequestAdditionalWM_NCMessages(IntPtr hwnd, bool hover, bool leave) { + } + + internal override void RequestNCRecalc(IntPtr handle) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) { + return; + } + + PerformNCCalc(hwnd); + SendMessage(handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + InvalidateNC(handle); + } + + [MonoTODO] + internal override void ResetMouseHover(IntPtr handle) { + throw new NotImplementedException(); + } + + internal override void ScreenToClient(IntPtr handle, ref int x, ref int y) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + Point point = ConvertScreenPointToClient (hwnd.ClientWindow, new Point (x, y)); + + x = point.X; + y = point.Y; + } + + internal override void ScreenToMenu(IntPtr handle, ref int x, ref int y) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + Point point = ConvertScreenPointToClient (hwnd.WholeWindow, new Point (x, y)); + + x = point.X; + y = point.Y; + } + + internal override void ScrollWindow(IntPtr handle, Rectangle area, int XAmount, int YAmount, bool clear) { + /* + * This used to use a HIViewScrollRect but this causes issues with the fact that we dont coalesce + * updates properly with our short-circuiting of the window manager. For now we'll do a less + * efficient invalidation of the entire handle which appears to fix the problem + * see bug #381084 + */ + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + Invalidate (handle, new Rectangle (0, 0, hwnd.Width, hwnd.Height), false); + } + + + internal override void ScrollWindow(IntPtr handle, int XAmount, int YAmount, bool clear) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + Invalidate (handle, new Rectangle (0, 0, hwnd.Width, hwnd.Height), false); + } + + [MonoTODO] + internal override void SendAsyncMethod (AsyncMethodData method) { + // Fake async + lock (queuelock) { + MessageQueue.Enqueue (GCHandle.Alloc (method)); + } + } + + [MonoTODO] + internal override IntPtr SendMessage (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) { + return NativeWindow.WndProc(hwnd, message, wParam, lParam); + } + + internal override int SendInput(IntPtr hwnd, Queue keys) { + return 0; + } + + + internal override void SetCaretPos (IntPtr hwnd, int x, int y) { + if (hwnd != IntPtr.Zero && hwnd == Caret.Hwnd) { + Caret.X = x; + Caret.Y = y; + ClientToScreen (hwnd, ref x, ref y); + SizeWindow (new Rectangle (x, y, Caret.Width, Caret.Height), CaretWindow); + Caret.Timer.Stop (); + HideCaret (); + if (Caret.Visible == 1) { + ShowCaret (); + Caret.Timer.Start (); + } + } + } + + internal override void SetClipRegion(IntPtr hwnd, Region region) { + throw new NotImplementedException(); + } + + internal override void SetCursor(IntPtr window, IntPtr cursor) { + Hwnd hwnd = Hwnd.ObjectFromHandle (window); + + hwnd.Cursor = cursor; + } + + internal override void SetCursorPos(IntPtr handle, int x, int y) { + CGDisplayMoveCursorToPoint (CGMainDisplayID (), new Carbon.CGPoint (x, y)); + } + + internal override void SetFocus(IntPtr handle) { + if (FocusWindow != IntPtr.Zero) { + PostMessage(FocusWindow, Msg.WM_KILLFOCUS, handle, IntPtr.Zero); + } + PostMessage(handle, Msg.WM_SETFOCUS, FocusWindow, IntPtr.Zero); + FocusWindow = handle; + } + + internal override void SetIcon(IntPtr handle, Icon icon) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + // FIXME: we need to map the icon for active window switches + if (WindowMapping [hwnd.Handle] != null) { + if (icon == null) { + RestoreApplicationDockTileImage (); + } else { + Bitmap bitmap; + int size; + IntPtr[] data; + int index; + + bitmap = new Bitmap (128, 128); + using (Graphics g = Graphics.FromImage (bitmap)) { + g.DrawImage (icon.ToBitmap (), 0, 0, 128, 128); + } + index = 0; + size = bitmap.Width * bitmap.Height; + data = new IntPtr[size]; + + for (int y = 0; y < bitmap.Height; y++) { + for (int x = 0; x < bitmap.Width; x++) { + int pixel = bitmap.GetPixel (x, y).ToArgb (); + if (BitConverter.IsLittleEndian) { + byte a = (byte) ((pixel >> 24) & 0xFF); + byte r = (byte) ((pixel >> 16) & 0xFF); + byte g = (byte) ((pixel >> 8) & 0xFF); + byte b = (byte) (pixel & 0xFF); + data[index++] = (IntPtr)(a + (r << 8) + (g << 16) + (b << 24)); + } else { + data[index++] = (IntPtr)pixel; + } + } + } + + IntPtr provider = CGDataProviderCreateWithData (IntPtr.Zero, data, size*4, IntPtr.Zero); + IntPtr image = CGImageCreate (128, 128, 8, 32, 4*128, CGColorSpaceCreateDeviceRGB (), 4, provider, IntPtr.Zero, 0, 0); + SetApplicationDockTileImage (image); + } + } + } + + + internal override void SetModal(IntPtr handle, bool Modal) { + IntPtr hWnd = HIViewGetWindow (Hwnd.ObjectFromHandle (handle).WholeWindow); + if (Modal) + BeginAppModalStateForWindow (hWnd); + else + EndAppModalStateForWindow (hWnd); + return; + } + + internal override IntPtr SetParent(IntPtr handle, IntPtr parent) { + IntPtr ParentHandle = IntPtr.Zero; + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + hwnd.Parent = Hwnd.ObjectFromHandle (parent); + if (HIViewGetSuperview (hwnd.whole_window) != IntPtr.Zero) { + HIViewRemoveFromSuperview (hwnd.whole_window); + } + if (hwnd.parent == null) + HIViewFindByID (HIViewGetRoot (FosterParent), new Carbon.HIViewID (Carbon.EventHandler.kEventClassWindow, 1), ref ParentHandle); + HIViewAddSubview (hwnd.parent == null ? ParentHandle : hwnd.Parent.client_window, hwnd.whole_window); + HIViewPlaceInSuperviewAt (hwnd.whole_window, hwnd.X, hwnd.Y); + HIViewAddSubview (hwnd.whole_window, hwnd.client_window); + HIViewPlaceInSuperviewAt (hwnd.client_window, hwnd.ClientRect.X, hwnd.ClientRect.Y); + + return IntPtr.Zero; + } + + internal override void SetTimer (Timer timer) { + lock (TimerList) { + TimerList.Add (timer); + } + } + + internal override bool SetTopmost(IntPtr hWnd, bool Enabled) { + HIViewSetZOrder (hWnd, 1, IntPtr.Zero); + return true; + } + + internal override bool SetOwner(IntPtr hWnd, IntPtr hWndOwner) { + // TODO: Set window owner. + return true; + } + + internal override bool SetVisible(IntPtr handle, bool visible, bool activate) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + object window = WindowMapping [hwnd.Handle]; + if (window != null) + if (visible) + ShowWindow ((IntPtr)window); + else + HideWindow ((IntPtr)window); + + if (visible) + SendMessage(handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + + HIViewSetVisible (hwnd.whole_window, visible); + HIViewSetVisible (hwnd.client_window, visible); + + hwnd.visible = visible; + hwnd.Mapped = true; + return true; + } + + internal override void SetAllowDrop (IntPtr handle, bool value) { + // Like X11 we allow drop on al windows and filter in our handler + } + + internal override DragDropEffects StartDrag (IntPtr handle, object data, DragDropEffects allowed_effects) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) + throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ")."); + + return Dnd.StartDrag (hwnd.client_window, data, allowed_effects); + } + + internal override void SetBorderStyle(IntPtr handle, FormBorderStyle border_style) { + Form form = Widget.FromHandle (handle) as Form; + if (form != null && form.window_manager == null && (border_style == FormBorderStyle.FixedToolWindow || + border_style == FormBorderStyle.SizableToolWindow)) { + form.window_manager = new ToolWindowManager (form); + } + + RequestNCRecalc(handle); + } + + internal override void SetMenu(IntPtr handle, Menu menu) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + hwnd.menu = menu; + + RequestNCRecalc(handle); + } + + internal override void SetWindowMinMax(IntPtr handle, Rectangle maximized, Size min, Size max) { + } + + internal override void SetWindowPos(IntPtr handle, int x, int y, int width, int height) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) { + return; + } + + // Win32 automatically changes negative width/height to 0. + if (width < 0) + width = 0; + if (height < 0) + height = 0; + + // X requires a sanity check for width & height; otherwise it dies + if (hwnd.zero_sized && width > 0 && height > 0) { + if (hwnd.visible) { + HIViewSetVisible(hwnd.WholeWindow, true); + } + hwnd.zero_sized = false; + } + + if ((width < 1) || (height < 1)) { + hwnd.zero_sized = true; + HIViewSetVisible(hwnd.WholeWindow, false); + } + + // Save a server roundtrip (and prevent a feedback loop) + if ((hwnd.x == x) && (hwnd.y == y) && (hwnd.width == width) && (hwnd.height == height)) { + return; + } + + if (!hwnd.zero_sized) { + hwnd.x = x; + hwnd.y = y; + hwnd.width = width; + hwnd.height = height; + SendMessage(hwnd.client_window, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + + Widget ctrl = Widget.FromHandle (handle); + CreateParams cp = ctrl.GetCreateParams (); + Size TranslatedSize = TranslateWindowSizeToQuartzWindowSize (cp, new Size (width, height)); + Carbon.Rect rect = new Carbon.Rect (); + + if (WindowMapping [hwnd.Handle] != null) { + if (StyleSet (cp.Style, WindowStyles.WS_POPUP)) { + SetRect (ref rect, (short)x, (short)y, (short)(x+TranslatedSize.Width), (short)(y+TranslatedSize.Height)); + } else { + SetRect (ref rect, (short)x, (short)(y+MenuBarHeight), (short)(x+TranslatedSize.Width), (short)(y+MenuBarHeight+TranslatedSize.Height)); + } + SetWindowBounds ((IntPtr) WindowMapping [hwnd.Handle], 33, ref rect); + Carbon.HIRect frame_rect = new Carbon.HIRect (0, 0, TranslatedSize.Width, TranslatedSize.Height); + HIViewSetFrame (hwnd.whole_window, ref frame_rect); + SetCaretPos (Caret.Hwnd, Caret.X, Caret.Y); + } else { + Carbon.HIRect frame_rect = new Carbon.HIRect (x, y, TranslatedSize.Width, TranslatedSize.Height); + HIViewSetFrame (hwnd.whole_window, ref frame_rect); + } + PerformNCCalc(hwnd); + } + + hwnd.x = x; + hwnd.y = y; + hwnd.width = width; + hwnd.height = height; + } + + internal override void SetWindowState(IntPtr handle, FormWindowState state) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + IntPtr window = HIViewGetWindow (handle); + + switch (state) { + case FormWindowState.Minimized: { + CollapseWindow (window, true); + break; + } + case FormWindowState.Normal: { + ZoomWindow (window, 7, false); + break; + } + case FormWindowState.Maximized: { + Form form = Widget.FromHandle (hwnd.Handle) as Form; + if (form != null && form.FormBorderStyle == FormBorderStyle.None) { + Carbon.Rect rect = new Carbon.Rect (); + Carbon.HIRect bounds = CGDisplayBounds (CGMainDisplayID ()); + SetRect (ref rect, (short)0, (short)0, (short)bounds.size.width, (short)bounds.size.height); + SetWindowBounds ((IntPtr) WindowMapping [hwnd.Handle], 33, ref rect); + HIViewSetFrame (hwnd.whole_window, ref bounds); + } else { + ZoomWindow (window, 8, false); + } + break; + } + } + } + + internal override void SetWindowStyle(IntPtr handle, CreateParams cp) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + SetHwndStyles(hwnd, cp); + + if (WindowMapping [hwnd.Handle] != null) { + Carbon.WindowAttributes attributes = Carbon.WindowAttributes.kWindowCompositingAttribute | Carbon.WindowAttributes.kWindowStandardHandlerAttribute; + if ((cp.Style & ((int)WindowStyles.WS_MINIMIZEBOX)) != 0) { + attributes |= Carbon.WindowAttributes.kWindowCollapseBoxAttribute; + } + if ((cp.Style & ((int)WindowStyles.WS_MAXIMIZEBOX)) != 0) { + attributes |= Carbon.WindowAttributes.kWindowResizableAttribute | Carbon.WindowAttributes.kWindowHorizontalZoomAttribute | Carbon.WindowAttributes.kWindowVerticalZoomAttribute; + } + if ((cp.Style & ((int)WindowStyles.WS_SYSMENU)) != 0) { + attributes |= Carbon.WindowAttributes.kWindowCloseBoxAttribute; + } + if ((cp.ExStyle & ((int)WindowExStyles.WS_EX_TOOLWINDOW)) != 0) { + attributes = Carbon.WindowAttributes.kWindowStandardHandlerAttribute | Carbon.WindowAttributes.kWindowCompositingAttribute; + } + attributes |= Carbon.WindowAttributes.kWindowLiveResizeAttribute; + + Carbon.WindowAttributes outAttributes = Carbon.WindowAttributes.kWindowNoAttributes; + GetWindowAttributes ((IntPtr)WindowMapping [hwnd.Handle], ref outAttributes); + ChangeWindowAttributes ((IntPtr)WindowMapping [hwnd.Handle], attributes, outAttributes); + } + } + + internal override void SetWindowTransparency(IntPtr handle, double transparency, Color key) { + } + + internal override double GetWindowTransparency(IntPtr handle) + { + return 1.0; + } + + internal override TransparencySupport SupportsTransparency() { + return TransparencySupport.None; + } + + internal override bool SetZOrder(IntPtr handle, IntPtr after_handle, bool Top, bool Bottom) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (Top) { + HIViewSetZOrder (hwnd.whole_window, 2, IntPtr.Zero); + return true; + } else if (!Bottom) { + Hwnd after_hwnd = Hwnd.ObjectFromHandle (after_handle); + HIViewSetZOrder (hwnd.whole_window, 2, (after_handle == IntPtr.Zero ? IntPtr.Zero : after_hwnd.whole_window)); + } else { + HIViewSetZOrder (hwnd.whole_window, 1, IntPtr.Zero); + return true; + } + return false; + } + + internal override void ShowCursor(bool show) { + if (show) + CGDisplayShowCursor (CGMainDisplayID ()); + else + CGDisplayHideCursor (CGMainDisplayID ()); + } + + internal override object StartLoop(Thread thread) { + return new object (); + } + + [MonoTODO] + internal override bool SystrayAdd(IntPtr hwnd, string tip, Icon icon, out ToolTip tt) { + throw new NotImplementedException(); + } + + [MonoTODO] + internal override bool SystrayChange(IntPtr hwnd, string tip, Icon icon, ref ToolTip tt) { + throw new NotImplementedException(); + } + + [MonoTODO] + internal override void SystrayRemove(IntPtr hwnd, ref ToolTip tt) { + throw new NotImplementedException(); + } + + [MonoTODO] + internal override void SystrayBalloon(IntPtr hwnd, int timeout, string title, string text, ToolTipIcon icon) + { + throw new NotImplementedException (); + } + + internal override bool Text(IntPtr handle, string text) { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + if (WindowMapping [hwnd.Handle] != null) { + SetWindowTitleWithCFString ((IntPtr)(WindowMapping [hwnd.Handle]), __CFStringMakeConstantString (text)); + } + SetWidgetTitleWithCFString (hwnd.whole_window, __CFStringMakeConstantString (text)); + SetWidgetTitleWithCFString (hwnd.client_window, __CFStringMakeConstantString (text)); + return true; + } + + internal override void UpdateWindow(IntPtr handle) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (!hwnd.visible || !HIViewIsVisible (handle)) { + return; + } + + SendMessage(handle, Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + } + + internal override bool TranslateMessage(ref MSG msg) { + return Carbon.EventHandler.TranslateMessage (ref msg); + } + + #region Reversible regions + /* + * Quartz has no concept of XOR drawing due to its compositing nature + * We fake this by mapping a overlay window on the first draw and mapping it on the second. + * This has some issues with it because its POSSIBLE for WidgetPaint.DrawReversible* to actually + * reverse two regions at once. We dont do this in MWF, but this behaviour woudn't work. + * We could in theory cache the Rectangle/Color combination to handle this behaviour. + * + * PROBLEMS: This has some flicker / banding + */ + internal void SizeWindow (Rectangle rect, IntPtr window) { + Carbon.Rect qrect = new Carbon.Rect (); + + SetRect (ref qrect, (short)rect.X, (short)rect.Y, (short)(rect.X+rect.Width), (short)(rect.Y+rect.Height)); + + SetWindowBounds (window, 33, ref qrect); + } + + internal override void DrawReversibleLine(Point start, Point end, Color backColor) { +// throw new NotImplementedException(); + } + + internal override void FillReversibleRectangle (Rectangle rectangle, Color backColor) { +// throw new NotImplementedException(); + } + + internal override void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style) { +// throw new NotImplementedException(); + } + + internal override void DrawReversibleRectangle(IntPtr handle, Rectangle rect, int line_width) { + Rectangle size_rect = rect; + int new_x = 0; + int new_y = 0; + + if (ReverseWindowMapped) { + HideWindow (ReverseWindow); + ReverseWindowMapped = false; + } else { + ClientToScreen(handle, ref new_x, ref new_y); + + size_rect.X += new_x; + size_rect.Y += new_y; + + SizeWindow (size_rect, ReverseWindow); + ShowWindow (ReverseWindow); + + rect.X = 0; + rect.Y = 0; + rect.Width -= 1; + rect.Height -= 1; + + Graphics g = Graphics.FromHwnd (HIViewGetRoot (ReverseWindow)); + + for (int i = 0; i < line_width; i++) { + g.DrawRectangle (ThemeEngine.Current.ResPool.GetPen (Color.Black), rect); + rect.X += 1; + rect.Y += 1; + rect.Width -= 1; + rect.Height -= 1; + } + + g.Flush (); + g.Dispose (); + + ReverseWindowMapped = true; + } + } + #endregion + + internal override SizeF GetAutoScaleSize(Font font) { + Graphics g; + float width; + string magic_string = "The quick brown fox jumped over the lazy dog."; + double magic_number = 44.549996948242189; + + g = Graphics.FromImage (new Bitmap (1, 1)); + + width = (float) (g.MeasureString (magic_string, font).Width / magic_number); + return new SizeF(width, font.Height); + } + + internal override Point MousePosition { + get { + return mouse_position; + } + } + #endregion + + #region System information + internal override int KeyboardSpeed { get{ throw new NotImplementedException(); } } + internal override int KeyboardDelay { get{ throw new NotImplementedException(); } } + + internal override int CaptionHeight { + get { + return 19; + } + } + + internal override Size CursorSize { get{ throw new NotImplementedException(); } } + internal override bool DragFullWindows { get{ throw new NotImplementedException(); } } + internal override Size DragSize { + get { + return new Size(4, 4); + } + } + + internal override Size FrameBorderSize { + get { + return new Size (2, 2); + } + } + + internal override Size IconSize { get{ throw new NotImplementedException(); } } + internal override Size MaxWindowTrackSize { get{ throw new NotImplementedException(); } } + internal override bool MenuAccessKeysUnderlined { + get { + return false; + } + } + internal override Size MinimizedWindowSpacingSize { get{ throw new NotImplementedException(); } } + + internal override Size MinimumWindowSize { + get { + return new Size(110, 22); + } + } + + internal override Keys ModifierKeys { + get { + return KeyboardHandler.ModifierKeys; + } + } + internal override Size SmallIconSize { get{ throw new NotImplementedException(); } } + internal override int MouseButtonCount { get{ throw new NotImplementedException(); } } + internal override bool MouseButtonsSwapped { get{ throw new NotImplementedException(); } } + internal override bool MouseWheelPresent { get{ throw new NotImplementedException(); } } + + internal override MouseButtons MouseButtons { + get { + return MouseState; + } + } + + internal override Rectangle VirtualScreen { + get { + return WorkingArea; + } + } + + internal override Rectangle WorkingArea { + get { + Carbon.HIRect bounds = CGDisplayBounds (CGMainDisplayID ()); + return new Rectangle ((int)bounds.origin.x, (int)bounds.origin.y, (int)bounds.size.width, (int)bounds.size.height); + } + } + + [MonoTODO] + internal override Screen[] AllScreens { + get { + return null; + } + } + + internal override bool ThemesEnabled { + get { + return XplatUICarbon.themes_enabled; + } + } + + + #endregion + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewConvertPoint (ref Carbon.CGPoint point, IntPtr pView, IntPtr cView); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewChangeFeatures (IntPtr aView, ulong bitsin, ulong bitsout); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewFindByID (IntPtr rootWnd, Carbon.HIViewID id, ref IntPtr outPtr); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIGrowBoxViewSetTransparent (IntPtr GrowBox, bool transparency); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr HIViewGetRoot (IntPtr hWnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIObjectCreate (IntPtr cfStr, uint what, ref IntPtr hwnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIObjectRegisterSubclass (IntPtr classid, IntPtr superclassid, uint options, Carbon.EventDelegate upp, uint count, Carbon.EventTypeSpec [] list, IntPtr state, ref IntPtr cls); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewPlaceInSuperviewAt (IntPtr view, float x, float y); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewAddSubview (IntPtr parentHnd, IntPtr childHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr HIViewGetPreviousView (IntPtr aView); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr HIViewGetSuperview (IntPtr aView); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewRemoveFromSuperview (IntPtr aView); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewSetVisible (IntPtr vHnd, bool visible); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static bool HIViewIsVisible (IntPtr vHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewGetBounds (IntPtr vHnd, ref Carbon.HIRect r); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewScrollRect (IntPtr vHnd, ref Carbon.HIRect rect, float x, float y); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewSetZOrder (IntPtr hWnd, int cmd, IntPtr oHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewNewTrackingArea (IntPtr inView, IntPtr inShape, UInt64 inID, ref IntPtr outRef); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr HIViewGetWindow (IntPtr aView); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int HIViewSetFrame (IntPtr view_handle, ref Carbon.HIRect bounds); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal extern static int HIViewSetNeedsDisplayInRect (IntPtr view_handle, ref Carbon.HIRect rect, bool needs_display); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void SetRect (ref Carbon.Rect r, short left, short top, short right, short bottom); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int ActivateWindow (IntPtr windowHnd, bool inActivate); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern bool IsWindowActive (IntPtr windowHnd); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + static extern int SetAutomaticWidgetDragTrackingEnabledForWindow (IntPtr window, bool enabled); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr GetEventDispatcherTarget (); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int SendEventToEventTarget (IntPtr evt, IntPtr target); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int ReleaseEvent (IntPtr evt); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int ReceiveNextEvent (uint evtCount, IntPtr evtTypes, double timeout, bool processEvt, ref IntPtr evt); + + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static bool IsWindowCollapsed (IntPtr hWnd); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static bool IsWindowInStandardState (IntPtr hWnd, IntPtr a, IntPtr b); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void CollapseWindow (IntPtr hWnd, bool collapse); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void ZoomWindow (IntPtr hWnd, short partCode, bool front); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetWindowAttributes (IntPtr hWnd, ref Carbon.WindowAttributes outAttributes); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int ChangeWindowAttributes (IntPtr hWnd, Carbon.WindowAttributes inAttributes, Carbon.WindowAttributes outAttributes); + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal extern static int GetGlobalMouse (ref Carbon.QDPoint outData); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int BeginAppModalStateForWindow (IntPtr window); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int EndAppModalStateForWindow (IntPtr window); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int CreateNewWindow (Carbon.WindowClass klass, Carbon.WindowAttributes attributes, ref Carbon.Rect r, ref IntPtr window); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int DisposeWindow (IntPtr wHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal extern static int ShowWindow (IntPtr wHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal extern static int HideWindow (IntPtr wHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal extern static bool IsWindowVisible (IntPtr wHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int SetWindowBounds (IntPtr wHnd, uint reg, ref Carbon.Rect rect); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetWindowBounds (IntPtr wHnd, uint reg, ref Carbon.Rect rect); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int SetWidgetTitleWithCFString (IntPtr hWnd, IntPtr titleCFStr); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int SetWindowTitleWithCFString (IntPtr hWnd, IntPtr titleCFStr); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal extern static IntPtr __CFStringMakeConstantString (string cString); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + internal extern static int CFRelease (IntPtr wHnd); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static short GetMBarHeight (); + + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void AlertSoundPlay (); + + #region Cursor imports + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static Carbon.HIRect CGDisplayBounds (IntPtr displayID); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr CGMainDisplayID (); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void CGDisplayShowCursor (IntPtr display); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void CGDisplayHideCursor (IntPtr display); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void CGDisplayMoveCursorToPoint (IntPtr display, Carbon.CGPoint point); + #endregion + + #region Process imports + [DllImport ("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int GetCurrentProcess (ref Carbon.ProcessSerialNumber psn); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int TransformProcessType (ref Carbon.ProcessSerialNumber psn, uint type); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static int SetFrontProcess (ref Carbon.ProcessSerialNumber psn); + #endregion + + #region Dock tile imports + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr CGColorSpaceCreateDeviceRGB(); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr CGDataProviderCreateWithData (IntPtr info, IntPtr [] data, int size, IntPtr releasefunc); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static IntPtr CGImageCreate (int width, int height, int bitsPerComponent, int bitsPerPixel, int bytesPerRow, IntPtr colorspace, uint bitmapInfo, IntPtr provider, IntPtr decode, int shouldInterpolate, int intent); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void SetApplicationDockTileImage(IntPtr imageRef); + [DllImport("/System/Library/Frameworks/Carbon.framework/Versions/Current/Carbon")] + extern static void RestoreApplicationDockTileImage(); + #endregion + } +} diff --git a/source/ShiftUI/Internal/XplatUICosmos.cs b/source/ShiftUI/Internal/XplatUICosmos.cs new file mode 100644 index 0000000..55f30df --- /dev/null +++ b/source/ShiftUI/Internal/XplatUICosmos.cs @@ -0,0 +1,2916 @@ +/* Cosmos XplatUI driver + * + * Author: Michael VanOverbeek + * Based off: XplatUIWin32 driver (ShiftUI\Internal\XplatUIWin32.cs), copyright info can be found there. + * + * Cosmos is a C# open source managed operating system, capable of basic graphics using either + * VGA or VESA BIOS Extensions, it can do full console IO, has a working FAT driver, and is + * written entirely in C# and their homebrewed high-level assembly language, X#. + * + * ShiftUI cannot access Cosmos APIs directly, so to compensate, the CosmosInterface + * class exists. This is an interface between ShiftUI and Cosmos, and it is used + * to tell the operating system where to draw pixels, what colors to use, and when + * something happens, like a window opening or closing, or an error occurring. + * + * This makes it so that ShiftUI doesn't care what graphics driver you use, as long as it + * supports 32bit RGB and you are capable of converting RGB values thrown by ShiftUI + * to the values that your graphics driver wants. + * + * This also means that your OS can deal with actually drawing windows, much like how + * Win32 and X11 does it. It's sort of a "you be the puppet, ShiftUI'll pull the strings" + * situation. + * + * The Cosmos driver is UNFINISHED, and many Win32 code remains. So, if you can continue + * on, or you want to use Cosmos features when I'm done, simply uncomment this: + */ + + //#define COSMOS + + /* + * and the C# compiler and Visual Studio will compile all of the Cosmos stuff. + * + * If you're planning on using ShiftUI for a Windows application or even Linux/Mac, + * DO NOT UNCOMMENT THAT #DEFINE. YOU WILL BREAK LOTS OF THINGS. There is a lot of + * platform-dependent stuff in Mono Winforms which is what ShiftUI is HEAVILY based + * off of, for example, when the app starts, it detects if you're on Windows, Mac or + * Linux, so that it knows whether to use Win32, X11, or Carbon. Cosmos can't really + * determine what OS it's running on because it IS an OS, so the COSMOS #define is + * used to bypass the platform-dependent stuff and make ShiftUI work specifically on + * Cosmos. + * + * Michael VanOverbeek out. + */ + +#if COSMOS +using ShiftUI; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace ShiftUI.Internal +{ + public struct CosmosScreen + { + public int Width { get; set; } + public int Height { get; set; } + public CosmosGfxDriver Driver { get; set; } + } + + public enum CosmosGfxDriver + { + VGA, + VESA, + VMware, + Dummy + } + + public enum CosmosWindowState + { + Maximized, + Minimized, + Normal, + } + + public enum CosmosWindowHint + { + MESSAGEBOX, + TOPLEVEL, + DEFAULT, + BORDERLESS, + DESKTOP, + TOOL, + } + + public class CosmosWindow + { + public IntPtr Handle { get; set; } + public Hwnd Hwnd { get; set; } + public string Title { get; set; } + public CosmosWindowState WindowState { get; set; } + public CosmosWindowHint Style { get; set; } + } + + /// <summary> + /// Because Cosmos cannot do dynamic loading and therefore + /// can't do P/Invokes, I'll let the operating system developer + /// use this interface as a backbone for letting XplatUI actually + /// interact with their operating system. + /// + /// ShiftOS won't use this, however Memphis will, and it's entire window manager + /// will reside in a class deriving from this class. + /// </summary> + public abstract class CosmosInterface + { + public abstract void SetPixel(int x, int y, Color col); + public abstract Color GetPixel(int x, int y); + public abstract CosmosScreen GetScreenInfo(); + public abstract int MouseX { get; } + public abstract int MouseY { get; } + public abstract bool MouseWheelPressed { get; } + internal List<CosmosWindow> Windows = null; + public abstract void Redraw(); + + + + public bool IsWindow(IntPtr handle) + { + if(Windows == null) + { + Windows = new List<Hwnd>(); + return false; + } + foreach(var win in Windows) + { + if (win.Handle == handle) + return true; + } + return false; + } + + /// <summary> + /// Clears the screen with a specified color. + /// </summary> + /// <param name="c">The color to clear to.</param> + public void Clear(Color c) { + var scr = GetScreenInfo(); + int w = scr.Width; + int h = scr.Height; + DrawRectangle(c, new Rectangle(0, 0, w, h)); + } + + /// <summary> + /// Draws a rectangle with the specified Rectangle and color. + /// </summary> + /// <param name="c">The color to use. (Note that depending on the graphics config of the current OS, color quality may be reduced.</param> + /// <param name="rect">The Rectangle struct containing the X and Y coordinates and the width and height.</param> + public void DrawRectangle(Color c, Rectangle rect) + { + int x = rect.X; + int y = rect.Y; + int max_width = rect.Width; + int max_height = rect.Height; + for(int i = x; i < max_width; i++) + { + for(int i2 = y; i2 < max_height; i2++) + { + if (GetPixel(i, i2) != c) + SetPixel(i, i2, c); + } + } + } + + /// <summary> + /// Draws an unfilled rectangle using the specified Rectangle, line width and color. + /// </summary> + public void DrawUnfilledRectangle(Rectangle rect, int width, Color c) + { + //top & bottom border + for(int x = rect.X; x < rect.Width; x++) + { + //top + for(int y = rect.Y; y < rect.Y + width; y++) + { + if (GetPixel(x, y) != c) + SetPixel(x, y, c); + } + + //bottom + for(int y = rect.Height - width; y < rect.Height; y++) + { + if (GetPixel(x, y) != c) + SetPixel(x, y, c); + } + } + + //In theory that code should work for left & right. + for (int y = rect.Y; y < rect.Height; y++) + { + //left + for (int x = rect.X; x < rect.X + width; x++) + { + if (GetPixel(x, y) != c) + SetPixel(x, y, c); + } + + //right + for (int x = rect.Width - width; x < rect.Width; x++) + { + if (GetPixel(x, y) != c) + SetPixel(x, y, c); + } + } + + } + } + + public class DefaultCosmosInterface : CosmosInterface + { + public const string SUI_ERR_NODRIVER = "[ShiftUI] You cannot use ShiftUI yet. You still need to load in a proper CosmosInterface."; + + public override bool MouseWheelPressed + { + get + { + Console.WriteLine(SUI_ERR_NODRIVER); + } + } + + public override int MouseX + { + get + { + Console.WriteLine(SUI_ERR_NODRIVER); + } + } + + public override int MouseY + { + get + { + Console.WriteLine(SUI_ERR_NODRIVER); + } + } + + public override Color GetPixel(int x, int y) + { + Console.WriteLine(SUI_ERR_NODRIVER); + return Color.Empty; + } + + public override CosmosScreen GetScreenInfo() + { + Console.WriteLine(SUI_ERR_NODRIVER); + var s = new CosmosScreen(); + s.Driver = CosmosGfxDriver.Dummy; + s.Width = 0; + s.Height = 0; + return s; + } + + public override void Redraw() + { + Console.WriteLine(SUI_ERR_NODRIVER); + } + + public override void SetPixel(int x, int y, Color col) + { + Console.WriteLine(SUI_ERR_NODRIVER); + } + } + + internal class XplatUICosmos : XplatUIDriver + { +#region Local Variables + private CosmosInterface cosmos = new DefaultCosmosInterface(); + private static XplatUICosmos instance; + private static int ref_count; + private static IntPtr FosterParentLast; + + internal static MouseButtons mouse_state; + internal static Point mouse_position; + internal static bool grab_confined; + internal static IntPtr grab_hwnd; + internal static Rectangle grab_area; + internal static WndProc wnd_proc; + internal static IntPtr prev_mouse_hwnd; + internal static bool caret_visible; + + internal static bool themes_enabled; + private Hashtable timer_list; + private static Queue message_queue; + private static IntPtr clip_magic = new IntPtr(27051977); + private static int scroll_width; + private static int scroll_height; + private static Hashtable wm_nc_registered; + private static Rectangle clipped_cursor_rect; + private Hashtable registered_classes; + private Hwnd HwndCreating; // the Hwnd we are currently creating (see CreateWindow) + +#endregion // Local Variables + +#region Constructor & Destructor + private XplatUICosmos() + { + // Handle singleton stuff first + ref_count = 0; + + mouse_state = MouseButtons.None; + mouse_position = Point.Empty; + + grab_confined = false; + grab_area = Rectangle.Empty; + + message_queue = new List<CosmosMessage>(); + + themes_enabled = true; + + FosterParentLast = IntPtr.Zero; + + scroll_height = 25; + scroll_width = 25; + + timer_list = new Hashtable(); + registered_classes = new Hashtable(); + } +#endregion // Constructor & Destructor + +#region Private Support Methods + + private IntPtr GetFosterParent() + { + if (!cosmos.IsWindow(FosterParentLast)) + { + FosterParentLast = cosmos.CreateWindow("Foster Parent", BorderStyle.None, 0, 0, 0, 0, IntPtr.Zero); + + if (FosterParentLast == IntPtr.Zero) + { + MessageBox.Show("Could not create foster window, system error \"" + cosmos.GetLastError().ToString() = "\".", "Fatal error."); + } + } + return FosterParentLast; + } + +#region Static Properties + internal override int ActiveWindowTrackingDelay + { + get { return 0; } + } + + internal override int CaretWidth + { + get + { + return Application.CurrentSkin.CaretWidth; + } + } + + internal override int FontSmoothingContrast + { + get + { + return 0; + } + } + + internal override int FontSmoothingType + { + get + { + return 0; + } + } + + internal override int HorizontalResizeBorderThickness + { + get { return 5; } + } + + internal override bool IsActiveWindowTrackingEnabled + { + get { return false; } + } + + internal override bool IsComboBoxAnimationEnabled + { + get { return false; } + } + + internal override bool IsDropShadowEnabled + { + get + { + return false; + } + } + + internal override bool IsFontSmoothingEnabled + { + get { return false; } + } + + internal override bool IsHotTrackingEnabled + { + get { return true; } + } + + internal override bool IsIconTitleWrappingEnabled + { + get { return true; } + } + + internal override bool IsKeyboardPreferred + { + get { return false; } + } + + internal override bool IsListBoxSmoothScrollingEnabled + { + get { return false; } + } + + internal override bool IsMenuAnimationEnabled + { + get { return false; } + } + + internal override bool IsMenuFadeEnabled + { + get { return false; } + } + + internal override bool IsMinimizeRestoreAnimationEnabled + { + get + { + return false; + } + } + + internal override bool IsSelectionFadeEnabled + { + get { return false; } + } + + internal override bool IsSnapToDefaultEnabled + { + get { return false; } + } + + internal override bool IsTitleBarGradientEnabled + { + get { return false; } + } + + internal override bool IsToolTipAnimationEnabled + { + get { return false; } + } + + internal override Size MenuBarButtonSize + { + get + { + return new Size(32, 32); + } + } + + public override Size MenuButtonSize + { + get + { + return new Size(32, 32); + } + } + + internal override int MenuShowDelay + { + get { return 0; } + } + + internal override int MouseSpeed + { + get { return Application.CurrentSkin.MouseSpeed; } + } + + internal override LeftRightAlignment PopupMenuAlignment + { + get { return LeftRightAlignment.Left; } + } + + internal override PowerStatus PowerStatus + { + get + { + //Cosmos doesn't have power management. Sadly. + return new PowerStatus(BatteryChargeStatus.Unknown, "No power status for you.".Length, 0.0, 0, PowerLineStatus.Unknown); + } + } + + internal override int SizingBorderWidth + { + get { return Application.CurrentSkin.SizeBorderWidth; } + } + + internal override Size SmallCaptionButtonSize + { + get + { + return new Size(32, 32); + } + } + + internal override bool UIEffectsEnabled + { + get { return false; } + } + + internal override int VerticalResizeBorderThickness + { + get { return 5; } + } + + internal override void RaiseIdle(EventArgs e) + { + if (Idle != null) + Idle(this, e); + } + + internal override Keys ModifierKeys + { + get + { + short state; + Keys key_state; + + key_state = Keys.None; + + return key_state; + } + } + + internal override MouseButtons MouseButtons + { + get + { + return cosmos.MouseState; + } + } + + internal override Point MousePosition + { + get + { + return new Point(cosmos.MouseX, cosmos.MouseY); + } + } + + internal override Size MouseHoverSize + { + get + { + int width = 4; + int height = 4; + return new Size(width, height); + } + } + + internal override int MouseHoverTime + { + get + { + int time = 500; + return time; + } + } + + internal override int MouseWheelScrollDelta + { + get + { + int delta = 120; + return delta; + } + } + + internal override int HorizontalScrollBarHeight + { + get + { + return scroll_height; + } + } + + internal override bool UserClipWontExposeParent + { + get + { + return false; + } + } + + + internal override int VerticalScrollBarWidth + { + get + { + return scroll_width; + } + } + + internal override int MenuHeight + { + get + { + return 32; + } + } + + internal override Size Border3DSize + { + get + { + return new Size(2, 2); + } + } + + internal override Size BorderSize + { + get + { + return new Size(2, 2); + } + } + + internal override bool DropTarget + { + get + { + return false; + } + + set + { + if (value) + { + //throw new NotImplementedException("Need to figure out D'n'D for Win32"); + } + } + } + + internal override Size CaptionButtonSize + { + get + { + return new Size(64, 32); + } + } + + internal override int CaptionHeight + { + get + { + return 35; + } + } + + internal override Size CursorSize + { + get + { + return new Size(32, 32); + } + } + + internal override bool DragFullWindows + { + get + { + return false; + } + } + + internal override Size DragSize + { + get + { + return new Size(32, 32); + } + } + + internal override Size DoubleClickSize + { + get + { + return new Size(36, 36); //thanks to @InternetUnexploder from the Cosmos Gitter chat, I know the raw value I need to put here :P + } + } + + internal override int DoubleClickTime + { + get + { + return 1000; + } + } + + internal override Size FixedFrameBorderSize + { + get + { + return new Size(2, 2); + } + } + + internal override Size FrameBorderSize + { + get + { + return new Size(4, 4); + } + } + + internal override Size IconSize + { + get + { + return new Size(16, 16); + } + } + + internal override Size MaxWindowTrackSize + { + get + { + return new Size(59, 60); + } + } + + internal override bool MenuAccessKeysUnderlined + { + get + { + return false; + } + } + + internal override Size MinimizedWindowSize + { + get + { + return new Size(0, 0); + } + } + + internal override Size MinimizedWindowSpacingSize + { + get + { + return new Size(0, 0); + } + } + + internal override Size MinimumWindowSize + { + get + { + return new Size(50, 50); + } + } + + internal override Size MinWindowTrackSize + { + get + { + return new Size(50, 50); + } + } + + internal override Size SmallIconSize + { + get + { + return new Size(8, 8); + } + } + + internal override int MouseButtonCount + { + get + { + return 3; + } + } + + internal override bool MouseButtonsSwapped + { + get + { + return false; + } + } + + internal override bool MouseWheelPresent + { + get + { + return cosmos.MouseWheelPressed; + } + } + + internal override Rectangle VirtualScreen + { + get + { + var s = cosmos.GetScreenInfo(); + return new Rectangle(s.X, s.Y, s.Width, s.Height); + } + } + + internal override Rectangle WorkingArea + { + get + { + return VirtualScreen; + } + } + + [MonoTODO] + internal override Screen[] AllScreens + { + get + { + // Can't use this sadly. + return null; + } + } + + internal override bool ThemesEnabled + { + get + { + return true; + } + } + + internal override bool RequiresPositiveClientAreaSize + { + get + { + return false; + } + } + + public override int ToolWindowCaptionHeight + { + get + { + return 35; //Drawing will be done entirely by the developer itself. Do we really care about the tool window size? + } + } + + public override Size ToolWindowCaptionButtonSize + { + get + { + return new Size(32, 32); + } + } +#endregion // Static Properties + +#region Singleton Specific Code + public static XplatUICosmos GetInstance() + { + if (instance == null) + { + instance = new XplatUICosmos(); + } + ref_count++; + return instance; + } + + public int Reference + { + get + { + return ref_count; + } + } +#endregion + +#region Public Static Methods + internal override IntPtr InitializeDriver() + { + cosmos.OnInit(); + return IntPtr.Zero; + } + + internal override void ShutdownDriver(IntPtr token) + { + Console.WriteLine("[ShiftUI] Stopping Cosmos driver..."); + cosmos.OnShutdown(); + } + + + internal void Version() + { + Console.WriteLine($"ShiftUI version {Application.ShiftUIVersionString}, running in the cosmos, on {cosmos.OSName} version {cosmos.OSVersionString}."); + } + + string GetSoundAlias(AlertType alert) + { + return ""; + } + + internal override void AudibleAlert(AlertType alert) + { + //Cosmos cannot do audio. + } + + internal override void BeginMoveResize(IntPtr handle) + { + //If the Mono devs that worked on the Win32 driver didn't know what this did, what + //makes you think I will? + } + + internal override void GetDisplaySize(out Size size) + { + //get screen metrics from OS + var screen = cosmos.GetScreenInfo(); + //push the data out. + size = new Size(screen.Width, screen.Height); + } + + internal override void EnableThemes() + { + //Themes will forever be enabled. + } + + //[MichaelComment(Message = "Yay. I just LOVE messing with Win32 stuff and porting it to Cosmos.")] + internal override IntPtr CreateWindow(CreateParams cp) + { + //I think I'll let this do it's thing it's own way but just change where the new Hwnd gets placed. + if (cosmos.Windows == null) + cosmos.Windows = new List<Hwnd>(); + IntPtr WindowHandle; + IntPtr ParentHandle; + Hwnd hwnd; + + hwnd = new Hwnd(); + + ParentHandle = cp.Parent; + + if ((ParentHandle == IntPtr.Zero) && (cp.Style & (int)(WindowStyles.WS_CHILD)) != 0) + { + // We need to use our foster parent window until this poor child gets it's parent assigned + ParentHandle = GetFosterParent(); + } + + if (((cp.Style & (int)(WindowStyles.WS_CHILD | WindowStyles.WS_POPUP)) == 0) && ((cp.ExStyle & (int)WindowExStyles.WS_EX_APPWINDOW) == 0)) + { + // If we want to be hidden from the taskbar we need to be 'owned' by + // something not on the taskbar. FosterParent is just that + ParentHandle = GetFosterParent(); + } + + Point location; + if (cp.HasWindowManager) + { + location = Hwnd.GetNextStackedFormLocation(cp, Hwnd.ObjectFromHandle(cp.Parent)); + } + else + { + location = new Point(cp.X, cp.Y); + } + + string class_name = RegisterWindowClass(cp.ClassStyle); + HwndCreating = hwnd; + + // We cannot actually send the WS_EX_MDICHILD flag to Windows because we + // are faking MDI, not uses Windows' version. + if ((cp.WindowExStyle & WindowExStyles.WS_EX_MDICHILD) == WindowExStyles.WS_EX_MDICHILD) + cp.WindowExStyle ^= WindowExStyles.WS_EX_MDICHILD; + + WindowHandle = cosmos.Windows.Count + 1; + + HwndCreating = null; + + if (WindowHandle == IntPtr.Zero) + { + MessageBox.Show("An error happened while showing a window... Fascinating.", "Fatal error."); + } + + hwnd.ClientWindow = WindowHandle; + hwnd.Mapped = true; + cosmos.Windows.Add(hwnd); + + return WindowHandle; + } + + internal override IntPtr CreateWindow(IntPtr Parent, int X, int Y, int Width, int Height) + { + CreateParams create_params = new CreateParams(); + + create_params.Caption = ""; + create_params.X = X; + create_params.Y = Y; + create_params.Width = Width; + create_params.Height = Height; + + create_params.ClassName = XplatUI.GetDefaultClassName(GetType()); + create_params.ClassStyle = 0; + create_params.ExStyle = 0; + create_params.Parent = IntPtr.Zero; + create_params.Param = 0; + + return CreateWindow(create_params); + } + + internal override void DestroyWindow(IntPtr handle) + { + Hwnd hwnd; + + foreach(var win in cosmos.Windows) + { + if (win.Handle == handle) + hwnd = win; + } + if (hwnd == null) + MessageBox.Show("An error occurred while destroying a window.", "Fatal error."); + cosmos.Windows.Remove(hwnd); + cosmos.OnDestroyWindow(hwnd); //tell the OS to redraw that area... + hwnd.Dispose(); + return; + } + + internal override void SetWindowMinMax(IntPtr handle, Rectangle maximized, Size min, Size max) + { + // We do nothing, Form has to handle WM_GETMINMAXINFO + } + + + internal override FormWindowState GetWindowState(IntPtr handle) + { + CosmosWindowStyle style; + + style = cosmos.GetWindowState(handle); + switch(style) + { + case CosmosWindowStyle.Maximized: + return FormWindowState.Maximized; + case CosmosWindowStyle.Minimized: + return FormWindowState.Minimized; + } + return FormWindowState.Normal; + } + + internal override void SetWindowState(IntPtr hwnd, FormWindowState state) + { + switch (state) + { + case FormWindowState.Normal: + { + Win32ShowWindow(hwnd, WindowPlacementFlags.SW_RESTORE); + return; + } + + case FormWindowState.Minimized: + { + Win32ShowWindow(hwnd, WindowPlacementFlags.SW_MINIMIZE); + return; + } + + case FormWindowState.Maximized: + { + Win32ShowWindow(hwnd, WindowPlacementFlags.SW_MAXIMIZE); + return; + } + } + } + + internal override void SetWindowStyle(IntPtr handle, CreateParams cp) + { + + Win32SetWindowLong(handle, WindowLong.GWL_STYLE, (uint)cp.Style); + Win32SetWindowLong(handle, WindowLong.GWL_EXSTYLE, (uint)cp.ExStyle); + + // From MSDN: + // Certain window data is cached, so changes you make using SetWindowLong + // will not take effect until you call the SetWindowPos function. Specifically, + // if you change any of the frame styles, you must call SetWindowPos with + // the SWP_FRAMECHANGED flag for the cache to be updated properly. + if (cp.control is Form) + XplatUI.RequestNCRecalc(handle); + } + + internal override double GetWindowTransparency(IntPtr handle) + { + LayeredWindowAttributes lwa; + COLORREF clrRef; + byte alpha; + + if (0 == Win32GetLayeredWindowAttributes(handle, out clrRef, out alpha, out lwa)) + return 1.0; + + return ((double)alpha) / 255.0; + } + + internal override void SetWindowTransparency(IntPtr handle, double transparency, Color key) + { + LayeredWindowAttributes lwa = LayeredWindowAttributes.LWA_ALPHA; + byte opacity = (byte)(transparency * 255); + COLORREF clrRef = new COLORREF(); + if (key != Color.Empty) + { + clrRef.R = key.R; + clrRef.G = key.G; + clrRef.B = key.B; + lwa = (LayeredWindowAttributes)((int)lwa | (int)LayeredWindowAttributes.LWA_COLORKEY); + } + RECT rc; + rc.right = 1000; + rc.bottom = 1000; + Win32SetLayeredWindowAttributes(handle, clrRef, opacity, lwa); + } + + TransparencySupport support; + bool queried_transparency_support; + internal override TransparencySupport SupportsTransparency() + { + if (queried_transparency_support) + return support; + + bool flag; + support = TransparencySupport.None; + + flag = true; + try + { + Win32SetLayeredWindowAttributes(IntPtr.Zero, new COLORREF(), 255, LayeredWindowAttributes.LWA_ALPHA); + } + catch (EntryPointNotFoundException) { flag = false; } + catch { /* swallow everything else */ } + + if (flag) support |= TransparencySupport.Set; + + flag = true; + try + { + LayeredWindowAttributes lwa; + COLORREF clrRef; + byte alpha; + + Win32GetLayeredWindowAttributes(IntPtr.Zero, out clrRef, out alpha, out lwa); + } + catch (EntryPointNotFoundException) { flag = false; } + catch { /* swallow everything else */ } + + if (flag) support |= TransparencySupport.Get; + + queried_transparency_support = true; + return support; + } + + internal override void UpdateWindow(IntPtr handle) + { + Win32UpdateWindow(handle); + } + + internal override PaintEventArgs PaintEventStart(ref Message msg, IntPtr handle, bool client) + { + IntPtr hdc; + PAINTSTRUCT ps; + PaintEventArgs paint_event; + RECT rect; + Rectangle clip_rect; + Hwnd hwnd; + + clip_rect = new Rectangle(); + rect = new RECT(); + ps = new PAINTSTRUCT(); + + hwnd = Hwnd.ObjectFromHandle(msg.HWnd); + + if (client) + { + if (Win32GetUpdateRect(msg.HWnd, ref rect, false)) + { + if (handle != msg.HWnd) + { + // We need to validate the window where the paint message + // was generated, otherwise we'll never stop getting paint + // messages. + Win32GetClientRect(msg.HWnd, out rect); + Win32ValidateRect(msg.HWnd, ref rect); + hdc = Win32GetDC(handle); + } + else + { + hdc = Win32BeginPaint(handle, ref ps); + rect = ps.rcPaint; + } + } + else + { + hdc = Win32GetDC(handle); + } + clip_rect = rect.ToRectangle(); + } + else + { + hdc = Win32GetWindowDC(handle); + + // HACK this in for now + Win32GetWindowRect(handle, out rect); + clip_rect = new Rectangle(0, 0, rect.Width, rect.Height); + } + + // If we called BeginPaint, store the PAINTSTRUCT, + // otherwise store hdc, so that PaintEventEnd can know + // whether to call EndPaint or ReleaseDC. + if (ps.hdc != IntPtr.Zero) + { + hwnd.drawing_stack.Push(ps); + } + else + { + hwnd.drawing_stack.Push(hdc); + } + + Graphics dc = Graphics.FromHdc(hdc); + hwnd.drawing_stack.Push(dc); + + paint_event = new PaintEventArgs(dc, clip_rect); + + return paint_event; + } + + internal override void PaintEventEnd(ref Message m, IntPtr handle, bool client) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(m.HWnd); + + Graphics dc = (Graphics)hwnd.drawing_stack.Pop(); + dc.Dispose(); + + object o = hwnd.drawing_stack.Pop(); + if (o is IntPtr) + { + IntPtr hdc = (IntPtr)o; + Win32ReleaseDC(handle, hdc); + } + else if (o is PAINTSTRUCT) + { + PAINTSTRUCT ps = (PAINTSTRUCT)o; + Win32EndPaint(handle, ref ps); + } + } + + + internal override void SetWindowPos(IntPtr handle, int x, int y, int width, int height) + { + Win32MoveWindow(handle, x, y, width, height, true); + return; + } + + internal override void GetWindowPos(IntPtr handle, bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) + { + IntPtr parent; + RECT rect; + POINT pt; + + Win32GetWindowRect(handle, out rect); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + pt.x = rect.left; + pt.y = rect.top; + + parent = Win32GetAncestor(handle, AncestorType.GA_PARENT); + if (parent != IntPtr.Zero && parent != Win32GetDesktopWindow()) + Win32ScreenToClient(parent, ref pt); + + x = pt.x; + y = pt.y; + + Win32GetClientRect(handle, out rect); + client_width = rect.right - rect.left; + client_height = rect.bottom - rect.top; + return; + } + + internal override void Activate(IntPtr handle) + { + Win32SetActiveWindow(handle); + // delayed timer enabled + lock (timer_list) + { + foreach (Timer t in timer_list.Values) + { + if (t.Enabled && t.window == IntPtr.Zero) + { + t.window = handle; + int id = t.GetHashCode(); + Win32SetTimer(handle, id, (uint)t.Interval, IntPtr.Zero); + } + } + } + } + + internal override void Invalidate(IntPtr handle, Rectangle rc, bool clear) + { + cosmos.Redraw(); //I don't want to make it too hard for devs to deal with this... + } + + + internal override void InvalidateNC(IntPtr handle) + { + cosmos.Redraw(); + } + + private IntPtr InternalWndProc(IntPtr hWnd, Msg msg, IntPtr wParam, IntPtr lParam) + { + if (HwndCreating != null && HwndCreating.ClientWindow == IntPtr.Zero) + HwndCreating.ClientWindow = hWnd; + return NativeWindow.WndProc(hWnd, msg, wParam, lParam); + } + + internal override IntPtr DefWndProc(ref Message msg) + { + msg.Result = Win32DefWindowProc(msg.HWnd, (Msg)msg.Msg, msg.WParam, msg.LParam); + return msg.Result; + } + + internal override void HandleException(Exception e) + { + StackTrace st = new StackTrace(e); + Win32MessageBox(IntPtr.Zero, e.Message + st.ToString(), "Exception", 0); + Console.WriteLine("{0}{1}", e.Message, st.ToString()); + } + + internal override void DoEvents() + { + MSG msg = new MSG(); + + while (GetMessage(ref msg, IntPtr.Zero, 0, 0, false)) + { + Message m = Message.Create(msg.hwnd, (int)msg.message, msg.wParam, msg.lParam); + + if (Application.FilterMessage(ref m)) + continue; + + XplatUI.TranslateMessage(ref msg); + XplatUI.DispatchMessage(ref msg); + } + } + + internal override bool PeekMessage(Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) + { + return Win32PeekMessage(ref msg, hWnd, wFilterMin, wFilterMax, flags); + } + + internal override void PostQuitMessage(int exitCode) + { + Win32PostQuitMessage(exitCode); + } + + internal override void RequestAdditionalWM_NCMessages(IntPtr hwnd, bool hover, bool leave) + { + if (wm_nc_registered == null) + wm_nc_registered = new Hashtable(); + + TMEFlags flags = TMEFlags.TME_NONCLIENT; + if (hover) + flags |= TMEFlags.TME_HOVER; + if (leave) + flags |= TMEFlags.TME_LEAVE; + + if (flags == TMEFlags.TME_NONCLIENT) + { + if (wm_nc_registered.Contains(hwnd)) + { + wm_nc_registered.Remove(hwnd); + } + } + else + { + if (!wm_nc_registered.Contains(hwnd)) + { + wm_nc_registered.Add(hwnd, flags); + } + else + { + wm_nc_registered[hwnd] = flags; + } + } + } + + internal override void RequestNCRecalc(IntPtr handle) + { + Win32SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE); + } + + internal override void ResetMouseHover(IntPtr handle) + { + TRACKMOUSEEVENT tme; + + tme = new TRACKMOUSEEVENT(); + tme.size = Marshal.SizeOf(tme); + tme.hWnd = handle; + tme.dwFlags = TMEFlags.TME_LEAVE | TMEFlags.TME_HOVER; + Win32TrackMouseEvent(ref tme); + } + + + internal override bool GetMessage(Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) + { + return GetMessage(ref msg, hWnd, wFilterMin, wFilterMax, true); + } + + private bool GetMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, bool blocking) + { + bool result; + + msg.refobject = 0; + if (RetrieveMessage(ref msg)) + { + return true; + } + + if (blocking) + { + result = Win32GetMessage(ref msg, hWnd, wFilterMin, wFilterMax); + } + else + { + result = Win32PeekMessage(ref msg, hWnd, wFilterMin, wFilterMax, (uint)PeekMessageFlags.PM_REMOVE); + if (!result) + { + return false; + } + } + + // We need to fake WM_MOUSE_ENTER + switch (msg.message) + { + case Msg.WM_LBUTTONDOWN: + { + mouse_state |= MouseButtons.Left; + break; + } + + case Msg.WM_MBUTTONDOWN: + { + mouse_state |= MouseButtons.Middle; + break; + } + + case Msg.WM_RBUTTONDOWN: + { + mouse_state |= MouseButtons.Right; + break; + } + + case Msg.WM_LBUTTONUP: + { + mouse_state &= ~MouseButtons.Left; + break; + } + + case Msg.WM_MBUTTONUP: + { + mouse_state &= ~MouseButtons.Middle; + break; + } + + case Msg.WM_RBUTTONUP: + { + mouse_state &= ~MouseButtons.Right; + break; + } + + case Msg.WM_ASYNC_MESSAGE: + { + XplatUIDriverSupport.ExecuteClientMessage((GCHandle)msg.lParam); + break; + } + + case Msg.WM_MOUSEMOVE: + { + if (msg.hwnd != prev_mouse_hwnd) + { + TRACKMOUSEEVENT tme; + + mouse_state = Widget.FromParamToMouseButtons((int)msg.lParam.ToInt32()); + + // The current message will be sent out next time around + StoreMessage(ref msg); + + // This is the message we want to send at this point + msg.message = Msg.WM_MOUSE_ENTER; + + prev_mouse_hwnd = msg.hwnd; + + tme = new TRACKMOUSEEVENT(); + tme.size = Marshal.SizeOf(tme); + tme.hWnd = msg.hwnd; + tme.dwFlags = TMEFlags.TME_LEAVE | TMEFlags.TME_HOVER; + Win32TrackMouseEvent(ref tme); + return result; + } + break; + } + + case Msg.WM_NCMOUSEMOVE: + { + if (wm_nc_registered == null || !wm_nc_registered.Contains(msg.hwnd)) + break; + + mouse_state = Widget.FromParamToMouseButtons((int)msg.lParam.ToInt32()); + + TRACKMOUSEEVENT tme; + + tme = new TRACKMOUSEEVENT(); + tme.size = Marshal.SizeOf(tme); + tme.hWnd = msg.hwnd; + tme.dwFlags = (TMEFlags)wm_nc_registered[msg.hwnd]; + Win32TrackMouseEvent(ref tme); + return result; + } + + case Msg.WM_DROPFILES: + { + return Win32DnD.HandleWMDropFiles(ref msg); + } + + case Msg.WM_MOUSELEAVE: + { + prev_mouse_hwnd = IntPtr.Zero; + break; + } + + case Msg.WM_TIMER: + { + Timer timer = (Timer)timer_list[(int)msg.wParam]; + + if (timer != null) + { + timer.FireTick(); + } + break; + } + } + + return result; + } + + internal override bool TranslateMessage(ref MSG msg) + { + return Win32TranslateMessage(ref msg); + } + + internal override IntPtr DispatchMessage(ref MSG msg) + { + return Win32DispatchMessage(ref msg); + } + + internal override bool SetZOrder(IntPtr hWnd, IntPtr AfterhWnd, bool Top, bool Bottom) + { + if (Top) + { + Win32SetWindowPos(hWnd, SetWindowPosZOrder.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + return true; + } + else if (!Bottom) + { + Win32SetWindowPos(hWnd, AfterhWnd, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + } + else + { + Win32SetWindowPos(hWnd, (IntPtr)SetWindowPosZOrder.HWND_BOTTOM, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + return true; + } + return false; + } + + internal override bool SetTopmost(IntPtr hWnd, bool Enabled) + { + if (Enabled) + { + Win32SetWindowPos(hWnd, SetWindowPosZOrder.HWND_TOPMOST, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE); + return true; + } + else + { + Win32SetWindowPos(hWnd, SetWindowPosZOrder.HWND_NOTOPMOST, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE); + return true; + } + } + + internal override bool SetOwner(IntPtr hWnd, IntPtr hWndOwner) + { + Win32SetWindowLong(hWnd, WindowLong.GWL_HWNDPARENT, (uint)hWndOwner); + return true; + } + + internal override bool Text(IntPtr handle, string text) + { + Win32SetWindowText(handle, text); + return true; + } + + internal override bool GetText(IntPtr handle, out string text) + { + StringBuilder sb; + + sb = new StringBuilder(256); + Win32GetWindowText(handle, sb, sb.Capacity); + text = sb.ToString(); + return true; + } + + internal override bool SetVisible(IntPtr handle, bool visible, bool activate) + { + if (visible) + { + Widget c = Widget.FromHandle(handle); + if (c is Form) + { + Form f; + + f = (Form)Widget.FromHandle(handle); + WindowPlacementFlags flags = WindowPlacementFlags.SW_SHOWNORMAL; + switch (f.WindowState) + { + case FormWindowState.Normal: flags = WindowPlacementFlags.SW_SHOWNORMAL; break; + case FormWindowState.Minimized: flags = WindowPlacementFlags.SW_MINIMIZE; break; + case FormWindowState.Maximized: flags = WindowPlacementFlags.SW_MAXIMIZE; break; + } + + if (!f.ActivateOnShow) + flags = WindowPlacementFlags.SW_SHOWNOACTIVATE; + + Win32ShowWindow(handle, flags); + } + else + { + if (c.ActivateOnShow) + Win32ShowWindow(handle, WindowPlacementFlags.SW_SHOWNORMAL); + else + Win32ShowWindow(handle, WindowPlacementFlags.SW_SHOWNOACTIVATE); + } + } + else + { + Win32ShowWindow(handle, WindowPlacementFlags.SW_HIDE); + } + return true; + } + + internal override bool IsEnabled(IntPtr handle) + { + return IsWindowEnabled(handle); + } + + internal override bool IsKeyLocked(VirtualKeys key) + { + return (Win32GetKeyState(key) & 1) == 1; + } + + internal override bool IsVisible(IntPtr handle) + { + return IsWindowVisible(handle); + } + + internal override IntPtr SetParent(IntPtr handle, IntPtr parent) + { + Widget c = Widget.FromHandle(handle); + if (parent == IntPtr.Zero) + { + if (!(c is Form)) + { + Win32ShowWindow(handle, WindowPlacementFlags.SW_HIDE); + } + } + else + { + if (!(c is Form)) + { + SetVisible(handle, c.is_visible, true); + } + } + // The Win32SetParent is lame, it can very well move the window + // ref: http://groups.google.com/group/microsoft.public.vb.winapi/browse_thread/thread/1b82ccc54231ecee/afa82835bfc0422a%23afa82835bfc0422a + // Here we save the position before changing the parent, and if it has changed afterwards restore it. + // Another possibility would be to intercept WM_WINDOWPOSCHANGING and restore the coords there, but this would require plumbing in weird places + // (either inside Widget or add handling to InternalWndProc) + // We also need to remove WS_CHILD if making the window parent-less, and add it if we're parenting it. + RECT rect, rect2; + IntPtr result; + WindowStyles style, new_style; + + Win32GetWindowRect(handle, out rect); + style = (WindowStyles)Win32GetWindowLong(handle, WindowLong.GWL_STYLE); + + if (parent == IntPtr.Zero) + { + new_style = style & ~WindowStyles.WS_CHILD; + result = Win32SetParent(handle, GetFosterParent()); + } + else + { + new_style = style | WindowStyles.WS_CHILD; + result = Win32SetParent(handle, parent); + } + if (style != new_style && c is Form) + { + Win32SetWindowLong(handle, WindowLong.GWL_STYLE, (uint)new_style); + } + Win32GetWindowRect(handle, out rect2); + if (rect.top != rect2.top && rect.left != rect2.left && c is Form) + { + Win32SetWindowPos(handle, IntPtr.Zero, rect.top, rect.left, rect.Width, rect.Height, SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOREDRAW | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOENDSCHANGING | SetWindowPosFlags.SWP_NOACTIVATE); + } + return result; + } + + // If we ever start using this, we should probably replace FosterParent with IntPtr.Zero + internal override IntPtr GetParent(IntPtr handle) + { + return Win32GetParent(handle); + } + + // This is a nop on win32 and x11 + internal override IntPtr GetPreviousWindow(IntPtr handle) + { + return handle; + } + + internal override void GrabWindow(IntPtr hWnd, IntPtr ConfineToHwnd) + { + grab_hwnd = hWnd; + Win32SetCapture(hWnd); + + if (ConfineToHwnd != IntPtr.Zero) + { + RECT window_rect; + Win32GetWindowRect(ConfineToHwnd, out window_rect); + Win32GetClipCursor(out clipped_cursor_rect); + Win32ClipCursor(ref window_rect); + } + } + + internal override void GrabInfo(out IntPtr hWnd, out bool GrabConfined, out Rectangle GrabArea) + { + hWnd = grab_hwnd; + GrabConfined = grab_confined; + GrabArea = grab_area; + } + + internal override void UngrabWindow(IntPtr hWnd) + { + if (!(clipped_cursor_rect.top == 0 && clipped_cursor_rect.bottom == 0 && clipped_cursor_rect.left == 0 && clipped_cursor_rect.right == 0)) + { + Win32ClipCursor(ref clipped_cursor_rect); + clipped_cursor_rect = new RECT(); + } + + Win32ReleaseCapture(); + grab_hwnd = IntPtr.Zero; + } + + internal override bool CalculateWindowRect(ref Rectangle ClientRect, CreateParams cp, Menu menu, out Rectangle WindowRect) + { + RECT rect; + + rect.left = ClientRect.Left; + rect.top = ClientRect.Top; + rect.right = ClientRect.Right; + rect.bottom = ClientRect.Bottom; + + if (!Win32AdjustWindowRectEx(ref rect, cp.Style, menu != null, cp.ExStyle)) + { + WindowRect = new Rectangle(ClientRect.Left, ClientRect.Top, ClientRect.Width, ClientRect.Height); + return false; + } + + WindowRect = new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + return true; + } + + internal override void SetCursor(IntPtr window, IntPtr cursor) + { + Win32SetCursor(cursor); + return; + } + + internal override void ShowCursor(bool show) + { + Win32ShowCursor(show); + } + + internal override void OverrideCursor(IntPtr cursor) + { + Win32SetCursor(cursor); + } + + internal override IntPtr DefineCursor(Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) + { + IntPtr cursor; + Bitmap cursor_bitmap; + Bitmap cursor_mask; + Byte[] cursor_bits; + Byte[] mask_bits; + Color pixel; + int width; + int height; + + // Win32 only allows creation cursors of a certain size + if ((bitmap.Width != Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR)) || (bitmap.Width != Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR))) + { + cursor_bitmap = new Bitmap(bitmap, new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR), Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR))); + cursor_mask = new Bitmap(mask, new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR), Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR))); + } + else + { + cursor_bitmap = bitmap; + cursor_mask = mask; + } + + width = cursor_bitmap.Width; + height = cursor_bitmap.Height; + + cursor_bits = new Byte[(width / 8) * height]; + mask_bits = new Byte[(width / 8) * height]; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + pixel = cursor_bitmap.GetPixel(x, y); + + if (pixel == cursor_pixel) + { + cursor_bits[y * width / 8 + x / 8] |= (byte)(0x80 >> (x % 8)); + } + + pixel = cursor_mask.GetPixel(x, y); + + if (pixel == mask_pixel) + { + mask_bits[y * width / 8 + x / 8] |= (byte)(0x80 >> (x % 8)); + } + } + } + + cursor = Win32CreateCursor(IntPtr.Zero, xHotSpot, yHotSpot, width, height, mask_bits, cursor_bits); + + return cursor; + } + + internal override Bitmap DefineStdCursorBitmap(StdCursor id) + { + // We load the cursor, create a bitmap, draw the cursor onto the bitmap and return the bitmap. + IntPtr cursor = DefineStdCursor(id); + // Windows only have one possible cursor size! + int width = Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR); + int height = Win32GetSystemMetrics(SystemMetrics.SM_CYCURSOR); + Bitmap bmp = new Bitmap(width, height); + Graphics gc = Graphics.FromImage(bmp); + IntPtr hdc = gc.GetHdc(); + Win32DrawIcon(hdc, 0, 0, cursor); + gc.ReleaseHdc(hdc); + gc.Dispose(); + return bmp; + } + + [MonoTODO("Define the missing cursors")] + internal override IntPtr DefineStdCursor(StdCursor id) + { + switch (id) + { + case StdCursor.AppStarting: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_APPSTARTING); + case StdCursor.Arrow: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); + case StdCursor.Cross: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_CROSS); + case StdCursor.Default: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); + case StdCursor.Hand: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_HAND); + case StdCursor.Help: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_HELP); + case StdCursor.HSplit: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.IBeam: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_IBEAM); + case StdCursor.No: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_NO); + case StdCursor.NoMove2D: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.NoMoveHoriz: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.NoMoveVert: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanEast: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanNE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanNorth: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanNW: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanSE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanSouth: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanSW: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanWest: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.SizeAll: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZEALL); + case StdCursor.SizeNESW: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZENESW); + case StdCursor.SizeNS: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZENS); + case StdCursor.SizeNWSE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZENWSE); + case StdCursor.SizeWE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZEWE); + case StdCursor.UpArrow: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_UPARROW); + case StdCursor.VSplit: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.WaitCursor: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_WAIT); + } + throw new NotImplementedException(); + } + + internal override void DestroyCursor(IntPtr cursor) + { + if ((cursor.ToInt32() < (int)LoadCursorType.First) || (cursor.ToInt32() > (int)LoadCursorType.Last)) + { + Win32DestroyCursor(cursor); + } + } + + [MonoTODO] + internal override void GetCursorInfo(IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y) + { + ICONINFO ii = new ICONINFO(); + + if (!Win32GetIconInfo(cursor, out ii)) + throw new Win32Exception(); + + width = 20; + height = 20; + hotspot_x = ii.xHotspot; + hotspot_y = ii.yHotspot; + } + + internal override void SetCursorPos(IntPtr handle, int x, int y) + { + Win32SetCursorPos(x, y); + } + + internal override Region GetClipRegion(IntPtr hwnd) + { + Region region; + + region = new Region(); + + Win32GetWindowRgn(hwnd, region.GetHrgn(Graphics.FromHwnd(hwnd))); + + return region; + } + + internal override void SetClipRegion(IntPtr hwnd, Region region) + { + if (region == null) + Win32SetWindowRgn(hwnd, IntPtr.Zero, true); + else + Win32SetWindowRgn(hwnd, region.GetHrgn(Graphics.FromHwnd(hwnd)), true); + } + + internal override void EnableWindow(IntPtr handle, bool Enable) + { + Win32EnableWindow(handle, Enable); + } + + internal override void EndLoop(System.Threading.Thread thread) + { + // Nothing to do + } + + internal override object StartLoop(System.Threading.Thread thread) + { + return null; + } + + internal override void SetModal(IntPtr handle, bool Modal) + { + // we do nothing on Win32 + } + + internal override void GetCursorPos(IntPtr handle, out int x, out int y) + { + POINT pt; + + Win32GetCursorPos(out pt); + + if (handle != IntPtr.Zero) + { + Win32ScreenToClient(handle, ref pt); + } + + x = pt.x; + y = pt.y; + } + + internal override void ScreenToClient(IntPtr handle, ref int x, ref int y) + { + POINT pnt = new POINT(); + + pnt.x = x; + pnt.y = y; + Win32ScreenToClient(handle, ref pnt); + + x = pnt.x; + y = pnt.y; + } + + internal override void ClientToScreen(IntPtr handle, ref int x, ref int y) + { + POINT pnt = new POINT(); + + pnt.x = x; + pnt.y = y; + + Win32ClientToScreen(handle, ref pnt); + + x = pnt.x; + y = pnt.y; + } + + internal override void ScreenToMenu(IntPtr handle, ref int x, ref int y) + { + RECT rect; + + Win32GetWindowRect(handle, out rect); + x -= rect.left + SystemInformation.FrameBorderSize.Width; + y -= rect.top + SystemInformation.FrameBorderSize.Height; + + WindowStyles style = (WindowStyles)Win32GetWindowLong(handle, WindowLong.GWL_STYLE); + if (CreateParams.IsSet(style, WindowStyles.WS_CAPTION)) + { + y -= ThemeEngine.Current.CaptionHeight; + } + } + + internal override void MenuToScreen(IntPtr handle, ref int x, ref int y) + { + RECT rect; + + Win32GetWindowRect(handle, out rect); + x += rect.left + SystemInformation.FrameBorderSize.Width; + y += rect.top + SystemInformation.FrameBorderSize.Height + ThemeEngine.Current.CaptionHeight; + return; + } + + internal override void SendAsyncMethod(AsyncMethodData method) + { + Win32PostMessage(GetFosterParent(), Msg.WM_ASYNC_MESSAGE, IntPtr.Zero, (IntPtr)GCHandle.Alloc(method)); + } + + internal override void SetTimer(Timer timer) + { + IntPtr FosterParent = GetFosterParent(); + int index; + + index = timer.GetHashCode(); + + lock (timer_list) + { + timer_list[index] = timer; + } + + if (Win32SetTimer(FosterParent, index, (uint)timer.Interval, IntPtr.Zero) != IntPtr.Zero) + timer.window = FosterParent; + else + timer.window = IntPtr.Zero; + } + + internal override void KillTimer(Timer timer) + { + int index; + + index = timer.GetHashCode(); + + Win32KillTimer(timer.window, index); + + lock (timer_list) + { + timer_list.Remove(index); + } + } + + internal override void CreateCaret(IntPtr hwnd, int width, int height) + { + Win32CreateCaret(hwnd, IntPtr.Zero, width, height); + caret_visible = false; + } + + internal override void DestroyCaret(IntPtr hwnd) + { + Win32DestroyCaret(); + } + + internal override void SetCaretPos(IntPtr hwnd, int x, int y) + { + Win32SetCaretPos(x, y); + } + + internal override void CaretVisible(IntPtr hwnd, bool visible) + { + if (visible) + { + if (!caret_visible) + { + Win32ShowCaret(hwnd); + caret_visible = true; + } + } + else + { + if (caret_visible) + { + Win32HideCaret(hwnd); + caret_visible = false; + } + } + } + + internal override IntPtr GetFocus() + { + return Win32GetFocus(); + } + + internal override void SetFocus(IntPtr hwnd) + { + Win32SetFocus(hwnd); + } + + internal override IntPtr GetActive() + { + return Win32GetActiveWindow(); + } + + internal override bool GetFontMetrics(Graphics g, Font font, out int ascent, out int descent) + { + IntPtr dc; + IntPtr prevobj; + TEXTMETRIC tm; + + tm = new TEXTMETRIC(); + + dc = Win32GetDC(IntPtr.Zero); + prevobj = Win32SelectObject(dc, font.ToHfont()); + + if (Win32GetTextMetrics(dc, ref tm) == false) + { + prevobj = Win32SelectObject(dc, prevobj); + Win32DeleteObject(prevobj); + Win32ReleaseDC(IntPtr.Zero, dc); + ascent = 0; + descent = 0; + return false; + } + prevobj = Win32SelectObject(dc, prevobj); + Win32DeleteObject(prevobj); + Win32ReleaseDC(IntPtr.Zero, dc); + + ascent = tm.tmAscent; + descent = tm.tmDescent; + + return true; + } + + internal override void ScrollWindow(IntPtr hwnd, Rectangle rectangle, int XAmount, int YAmount, bool with_children) + { + RECT rect; + + rect = new RECT(); + rect.left = rectangle.X; + rect.top = rectangle.Y; + rect.right = rectangle.Right; + rect.bottom = rectangle.Bottom; + + Win32ScrollWindowEx(hwnd, XAmount, YAmount, IntPtr.Zero, ref rect, IntPtr.Zero, IntPtr.Zero, ScrollWindowExFlags.SW_INVALIDATE | ScrollWindowExFlags.SW_ERASE | (with_children ? ScrollWindowExFlags.SW_SCROLLCHILDREN : ScrollWindowExFlags.SW_NONE)); + Win32UpdateWindow(hwnd); + } + + internal override void ScrollWindow(IntPtr hwnd, int XAmount, int YAmount, bool with_children) + { + Win32ScrollWindowEx(hwnd, XAmount, YAmount, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ScrollWindowExFlags.SW_INVALIDATE | ScrollWindowExFlags.SW_ERASE | (with_children ? ScrollWindowExFlags.SW_SCROLLCHILDREN : ScrollWindowExFlags.SW_NONE)); + } + + internal override bool SystrayAdd(IntPtr hwnd, string tip, Icon icon, out ToolTip tt) + { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hWnd = hwnd; + nid.uID = 1; + nid.uCallbackMessage = (uint)Msg.WM_USER; + nid.uFlags = NotifyIconFlags.NIF_MESSAGE; + + if (tip != null) + { + nid.szTip = tip; + nid.uFlags |= NotifyIconFlags.NIF_TIP; + } + + if (icon != null) + { + nid.hIcon = icon.Handle; + nid.uFlags |= NotifyIconFlags.NIF_ICON; + } + + tt = null; + + return Win32Shell_NotifyIcon(NotifyIconMessage.NIM_ADD, ref nid); + } + + internal override bool SystrayChange(IntPtr hwnd, string tip, Icon icon, ref ToolTip tt) + { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hIcon = icon.Handle; + nid.hWnd = hwnd; + nid.uID = 1; + nid.uCallbackMessage = (uint)Msg.WM_USER; + nid.uFlags = NotifyIconFlags.NIF_MESSAGE; + + if (tip != null) + { + nid.szTip = tip; + nid.uFlags |= NotifyIconFlags.NIF_TIP; + } + + if (icon != null) + { + nid.hIcon = icon.Handle; + nid.uFlags |= NotifyIconFlags.NIF_ICON; + } + + return Win32Shell_NotifyIcon(NotifyIconMessage.NIM_MODIFY, ref nid); + } + + internal override void SystrayRemove(IntPtr hwnd, ref ToolTip tt) + { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hWnd = hwnd; + nid.uID = 1; + nid.uFlags = 0; + + Win32Shell_NotifyIcon(NotifyIconMessage.NIM_DELETE, ref nid); + } + + internal override void SystrayBalloon(IntPtr hwnd, int timeout, string title, string text, ToolTipIcon icon) + { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hWnd = hwnd; + nid.uID = 1; + nid.uFlags = NotifyIconFlags.NIF_INFO; + nid.uTimeoutOrVersion = timeout; + nid.szInfoTitle = title; + nid.szInfo = text; + nid.dwInfoFlags = icon; + + Win32Shell_NotifyIcon(NotifyIconMessage.NIM_MODIFY, ref nid); + } + + internal override void SetBorderStyle(IntPtr handle, FormBorderStyle border_style) + { + // Nothing to do on Win32 + } + + internal override void SetMenu(IntPtr handle, Menu menu) + { + // Trigger WM_NCCALC + Win32SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + } + + internal override Point GetMenuOrigin(IntPtr handle) + { + Form form = Widget.FromHandle(handle) as Form; + + if (form != null) + { + if (form.FormBorderStyle == FormBorderStyle.None) + return Point.Empty; + + int bordersize = (form.Width - form.ClientSize.Width) / 2; + + if (form.FormBorderStyle == FormBorderStyle.FixedToolWindow || form.FormBorderStyle == FormBorderStyle.SizableToolWindow) + return new Point(bordersize, bordersize + SystemInformation.ToolWindowCaptionHeight); + else + return new Point(bordersize, bordersize + SystemInformation.CaptionHeight); + } + + return new Point(SystemInformation.FrameBorderSize.Width, SystemInformation.FrameBorderSize.Height + ThemeEngine.Current.CaptionHeight); + } + + internal override void SetIcon(IntPtr hwnd, Icon icon) + { + Win32SendMessage(hwnd, Msg.WM_SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : icon.Handle); // 1 = large icon (0 would be small) + } + + internal override void ClipboardClose(IntPtr handle) + { + if (handle != clip_magic) + { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + Win32CloseClipboard(); + } + + internal override int ClipboardGetID(IntPtr handle, string format) + { + if (handle != clip_magic) + { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + if (format == "Text") return 1; + else if (format == "Bitmap") return 2; + else if (format == "MetaFilePict") return 3; + else if (format == "SymbolicLink") return 4; + else if (format == "DataInterchangeFormat") return 5; + else if (format == "Tiff") return 6; + else if (format == "OEMText") return 7; + else if (format == "DeviceIndependentBitmap") return 8; + else if (format == "Palette") return 9; + else if (format == "PenData") return 10; + else if (format == "RiffAudio") return 11; + else if (format == "WaveAudio") return 12; + else if (format == "UnicodeText") return 13; + else if (format == "EnhancedMetafile") return 14; + else if (format == "FileDrop") return 15; + else if (format == "Locale") return 16; + + return (int)Win32RegisterClipboardFormat(format); + } + + internal override IntPtr ClipboardOpen(bool primary_selection) + { + // Win32 does not have primary selection + Win32OpenClipboard(GetFosterParent()); + return clip_magic; + } + + internal override int[] ClipboardAvailableFormats(IntPtr handle) + { + uint format; + int[] result; + int count; + + if (handle != clip_magic) + { + return null; + } + + // Count first + count = 0; + format = 0; + do + { + format = Win32EnumClipboardFormats(format); + if (format != 0) + { + count++; + } + } while (format != 0); + + // Now assign + result = new int[count]; + count = 0; + format = 0; + do + { + format = Win32EnumClipboardFormats(format); + if (format != 0) + { + result[count++] = (int)format; + } + } while (format != 0); + + return result; + } + + + internal override object ClipboardRetrieve(IntPtr handle, int type, XplatUI.ClipboardToObject converter) + { + IntPtr hmem; + IntPtr data; + object obj; + + if (handle != clip_magic) + { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + + hmem = Win32GetClipboardData((uint)type); + if (hmem == IntPtr.Zero) + { + return null; + } + + data = Win32GlobalLock(hmem); + if (data == IntPtr.Zero) + { + uint error = Win32GetLastError(); + Console.WriteLine("Error: {0}", error); + return null; + } + + obj = null; + + if (type == DataFormats.GetFormat(DataFormats.Rtf).Id) + { + obj = AnsiToString(data); + } + else switch ((ClipboardFormats)type) + { + case ClipboardFormats.CF_TEXT: + { + obj = AnsiToString(data); + break; + } + + case ClipboardFormats.CF_DIB: + { + obj = DIBtoImage(data); + break; + } + + case ClipboardFormats.CF_UNICODETEXT: + { + obj = UnicodeToString(data); + break; + } + + default: + { + if (converter != null && !converter(type, data, out obj)) + { + obj = null; + } + break; + } + } + Win32GlobalUnlock(hmem); + + return obj; + + } + + internal override void ClipboardStore(IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter, bool copy) + { + byte[] data = null; + + if (handle != clip_magic) + { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + + if (obj == null) + { + // Just clear it + if (!Win32EmptyClipboard()) + throw new ExternalException("Win32EmptyClipboard"); + return; + } + + if (type == -1) + { + if (obj is string) + { + type = (int)ClipboardFormats.CF_UNICODETEXT; + } + else if (obj is Image) + { + type = (int)ClipboardFormats.CF_DIB; + } + } + + if (type == DataFormats.GetFormat(DataFormats.Rtf).Id) + { + data = StringToAnsi((string)obj); + } + else switch ((ClipboardFormats)type) + { + case ClipboardFormats.CF_UNICODETEXT: + { + data = StringToUnicode((string)obj); + break; + } + + case ClipboardFormats.CF_TEXT: + { + data = StringToAnsi((string)obj); + break; + } + + case ClipboardFormats.CF_BITMAP: + case ClipboardFormats.CF_DIB: + { + data = ImageToDIB((Image)obj); + type = (int)ClipboardFormats.CF_DIB; + break; + } + + default: + { + if (converter != null && !converter(ref type, obj, out data)) + { + data = null; // ensure that a failed conversion leaves null. + } + break; + } + } + if (data != null) + { + SetClipboardData((uint)type, data); + } + } + + internal static byte[] StringToUnicode(string text) + { + return Encoding.Unicode.GetBytes(text + "\0"); + } + + internal static byte[] StringToAnsi(string text) + { + // FIXME, follow the behaviour of the previous code using UTF-8, + // but this should be 'ANSI' on Windows, i.e. the current code page. + // Does Encoding.Default work on Windows? + return Encoding.UTF8.GetBytes(text + "\0"); + } + + private void SetClipboardData(uint type, byte[] data) + { + if (data.Length == 0) + // Shouldn't call Win32SetClipboard with NULL, as, from MSDN: + // "This parameter can be NULL, indicating that the window provides data + // in the specified clipboard format (renders the format) upon request." + // and I don't think we support that... + // Note this is unrelated to the fact that passing a null obj to + // ClipboardStore is actually a request to empty the clipboard! + return; + IntPtr hmem = CopyToMoveableMemory(data); + if (hmem == IntPtr.Zero) + // As above, should not call with null. + // (Not that CopyToMoveableMemory should ever return null!) + throw new ExternalException("CopyToMoveableMemory failed."); + if (Win32SetClipboardData(type, hmem) == IntPtr.Zero) + throw new ExternalException("Win32SetClipboardData"); + } + + /// <summary> + /// Creates a memory block with GlobalAlloc(GMEM_MOVEABLE), copies the data + /// into it, and returns the handle to the memory. + /// </summary> + /// - + /// <param name="data">The data. Must not be null or zero-length — + /// see the exception notes.</param> + /// - + /// <returns>The *handle* to the allocated GMEM_MOVEABLE block.</returns> + /// - + /// <exception cref="T:System.ArgumentException">The data was null or zero + /// length. This is disallowed since a zero length allocation can't be made + /// </exception> + /// <exception cref="T:System.ComponentModel.Win32Exception">The allocation, + /// or locking (handle->pointer) failed. + /// Either out of memory or the handle table is full (256 max currently). + /// Note Win32Exception is a subclass of ExternalException so this is OK in + /// the documented Clipboard interface. + /// </exception> + internal static IntPtr CopyToMoveableMemory(byte[] data) + { + if (data == null || data.Length == 0) + // detect this before GlobalAlloc does. + throw new ArgumentException("Can't create a zero length memory block."); + + IntPtr hmem = Win32GlobalAlloc(GAllocFlags.GMEM_MOVEABLE | GAllocFlags.GMEM_DDESHARE, data.Length); + if (hmem == IntPtr.Zero) + throw new Win32Exception(); + IntPtr hmem_ptr = Win32GlobalLock(hmem); + if (hmem_ptr == IntPtr.Zero) // If the allocation was valid this shouldn't occur. + throw new Win32Exception(); + Marshal.Copy(data, 0, hmem_ptr, data.Length); + Win32GlobalUnlock(hmem); + return hmem; + } + + + internal override void SetAllowDrop(IntPtr hwnd, bool allowed) + { + if (allowed) + { + Win32DnD.RegisterDropTarget(hwnd); + } + else + { + Win32DnD.UnregisterDropTarget(hwnd); + } + } + + internal override DragDropEffects StartDrag(IntPtr hwnd, object data, DragDropEffects allowedEffects) + { + return Win32DnD.StartDrag(hwnd, data, allowedEffects); + } + + // XXX this doesn't work at all for FrameStyle.Dashed - it draws like Thick, and in the Thick case + // the corners are drawn incorrectly. + internal override void DrawReversibleFrame(Rectangle rectangle, Color backColor, FrameStyle style) + { + IntPtr hdc; + IntPtr pen; + IntPtr oldpen; + COLORREF clrRef = new COLORREF(); + + // If we want the standard hatch pattern we would + // need to create a brush + + clrRef.R = backColor.R; + clrRef.G = backColor.G; + clrRef.B = backColor.B; + + // Grab a pen + pen = Win32CreatePen(style == FrameStyle.Thick ? PenStyle.PS_SOLID : PenStyle.PS_DASH, + style == FrameStyle.Thick ? 4 : 2, ref clrRef); + + hdc = Win32GetDC(IntPtr.Zero); + Win32SetROP2(hdc, ROP2DrawMode.R2_NOT); + oldpen = Win32SelectObject(hdc, pen); + + Win32MoveToEx(hdc, rectangle.Left, rectangle.Top, IntPtr.Zero); + if ((rectangle.Width > 0) && (rectangle.Height > 0)) + { + Win32LineTo(hdc, rectangle.Right, rectangle.Top); + Win32LineTo(hdc, rectangle.Right, rectangle.Bottom); + Win32LineTo(hdc, rectangle.Left, rectangle.Bottom); + Win32LineTo(hdc, rectangle.Left, rectangle.Top); + } + else + { + if (rectangle.Width > 0) + { + Win32LineTo(hdc, rectangle.Right, rectangle.Top); + } + else + { + Win32LineTo(hdc, rectangle.Left, rectangle.Bottom); + } + } + + Win32SelectObject(hdc, oldpen); + Win32DeleteObject(pen); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override void DrawReversibleLine(Point start, Point end, Color backColor) + { + IntPtr hdc; + IntPtr pen; + IntPtr oldpen; + POINT pt; + COLORREF clrRef = new COLORREF(); + + pt = new POINT(); + pt.x = 0; + pt.y = 0; + Win32ClientToScreen(IntPtr.Zero, ref pt); + + // If we want the standard hatch pattern we would + // need to create a brush + + clrRef.R = backColor.R; + clrRef.G = backColor.G; + clrRef.B = backColor.B; + + // Grab a pen + pen = Win32CreatePen(PenStyle.PS_SOLID, 1, ref clrRef); + + hdc = Win32GetDC(IntPtr.Zero); + Win32SetROP2(hdc, ROP2DrawMode.R2_NOT); + oldpen = Win32SelectObject(hdc, pen); + + Win32MoveToEx(hdc, pt.x + start.X, pt.y + start.Y, IntPtr.Zero); + Win32LineTo(hdc, pt.x + end.X, pt.y + end.Y); + + Win32SelectObject(hdc, oldpen); + Win32DeleteObject(pen); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override void FillReversibleRectangle(Rectangle rectangle, Color backColor) + { + RECT rect; + + rect = new RECT(); + rect.left = rectangle.Left; + rect.top = rectangle.Top; + rect.right = rectangle.Right; + rect.bottom = rectangle.Bottom; + + IntPtr hdc; + IntPtr brush; + IntPtr oldbrush; + COLORREF clrRef = new COLORREF(); + + clrRef.R = backColor.R; + clrRef.G = backColor.G; + clrRef.B = backColor.B; + + // Grab a brush + brush = Win32CreateSolidBrush(clrRef); + + hdc = Win32GetDC(IntPtr.Zero); + oldbrush = Win32SelectObject(hdc, brush); + + Win32PatBlt(hdc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height, PatBltRop.DSTINVERT); + + Win32SelectObject(hdc, oldbrush); + Win32DeleteObject(brush); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override void DrawReversibleRectangle(IntPtr handle, Rectangle rect, int line_width) + { + IntPtr hdc; + IntPtr pen; + IntPtr oldpen; + POINT pt; + + pt = new POINT(); + pt.x = 0; + pt.y = 0; + Win32ClientToScreen(handle, ref pt); + + // If we want the standard hatch pattern we would + // need to create a brush + + // Grab a pen + pen = Win32CreatePen(PenStyle.PS_SOLID, line_width, IntPtr.Zero); + + hdc = Win32GetDC(IntPtr.Zero); + Win32SetROP2(hdc, ROP2DrawMode.R2_NOT); + oldpen = Win32SelectObject(hdc, pen); + + Widget c = Widget.FromHandle(handle); + if (c != null) + { + RECT window_rect; + Win32GetWindowRect(c.Handle, out window_rect); + Region r = new Region(new Rectangle(window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top)); + Win32ExtSelectClipRgn(hdc, r.GetHrgn(Graphics.FromHdc(hdc)), (int)ClipCombineMode.RGN_AND); + } + + Win32MoveToEx(hdc, pt.x + rect.Left, pt.y + rect.Top, IntPtr.Zero); + if ((rect.Width > 0) && (rect.Height > 0)) + { + Win32LineTo(hdc, pt.x + rect.Right, pt.y + rect.Top); + Win32LineTo(hdc, pt.x + rect.Right, pt.y + rect.Bottom); + Win32LineTo(hdc, pt.x + rect.Left, pt.y + rect.Bottom); + Win32LineTo(hdc, pt.x + rect.Left, pt.y + rect.Top); + } + else + { + if (rect.Width > 0) + { + Win32LineTo(hdc, pt.x + rect.Right, pt.y + rect.Top); + } + else + { + Win32LineTo(hdc, pt.x + rect.Left, pt.y + rect.Bottom); + } + } + + Win32SelectObject(hdc, oldpen); + Win32DeleteObject(pen); + if (c != null) + Win32ExtSelectClipRgn(hdc, IntPtr.Zero, (int)ClipCombineMode.RGN_COPY); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override SizeF GetAutoScaleSize(Font font) + { + Graphics g; + float width; + string magic_string = "The quick brown fox jumped over the lazy dog."; + double magic_number = 44.549996948242189; + + g = Graphics.FromHwnd(GetFosterParent()); + + width = (float)(g.MeasureString(magic_string, font).Width / magic_number); + return new SizeF(width, font.Height); + } + + internal override IntPtr SendMessage(IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) + { + return Win32SendMessage(hwnd, message, wParam, lParam); + } + + internal override bool PostMessage(IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) + { + return Win32PostMessage(hwnd, message, wParam, lParam); + } + + internal override int SendInput(IntPtr hwnd, Queue keys) + { + INPUT[] inputs = new INPUT[keys.Count]; + const Int32 INPUT_KEYBOARD = 1; + uint returns = 0; + int i = 0; + while (keys.Count > 0) + { + MSG msg = (MSG)keys.Dequeue(); + + + inputs[i].ki.wScan = 0; + inputs[i].ki.time = 0; + inputs[i].ki.dwFlags = (Int32)(msg.message == Msg.WM_KEYUP ? InputFlags.KEYEVENTF_KEYUP : 0); + inputs[i].ki.wVk = (short)msg.wParam.ToInt32(); + inputs[i].type = INPUT_KEYBOARD; + i++; + } + returns = Win32SendInput((UInt32)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT))); + + return (int)returns; + } + + internal override int KeyboardSpeed + { + get + { + int speed = 0; + Win32SystemParametersInfo(SPIAction.SPI_GETKEYBOARDSPEED, 0, ref speed, 0); + // + // Return values range from 0 to 31 which map to 2.5 to 30 repetitions per second. + // + return speed; + } + } + + internal override int KeyboardDelay + { + get + { + int delay = 1; + Win32SystemParametersInfo(SPIAction.SPI_GETKEYBOARDDELAY, 0, ref delay, 0); + // + // Return values must range from 0 to 4, 0 meaning 250ms, + // and 4 meaning 1000 ms. + // + return delay; + } + } + + private class WinBuffer + { + public IntPtr hdc; + public IntPtr bitmap; + + public WinBuffer(IntPtr hdc, IntPtr bitmap) + { + this.hdc = hdc; + this.bitmap = bitmap; + } + } + + internal override void CreateOffscreenDrawable(IntPtr handle, int width, int height, out object offscreen_drawable) + { + Graphics destG = Graphics.FromHwnd(handle); + IntPtr destHdc = destG.GetHdc(); + + IntPtr srcHdc = Win32CreateCompatibleDC(destHdc); + IntPtr srcBmp = Win32CreateCompatibleBitmap(destHdc, width, height); + Win32SelectObject(srcHdc, srcBmp); + + offscreen_drawable = new WinBuffer(srcHdc, srcBmp); + + destG.ReleaseHdc(destHdc); + } + + internal override Graphics GetOffscreenGraphics(object offscreen_drawable) + { + return Graphics.FromHdc(((WinBuffer)offscreen_drawable).hdc); + } + + internal override void BlitFromOffscreen(IntPtr dest_handle, Graphics dest_dc, object offscreen_drawable, Graphics offscreen_dc, Rectangle r) + { + WinBuffer wb = (WinBuffer)offscreen_drawable; + + IntPtr destHdc = dest_dc.GetHdc(); + Win32BitBlt(destHdc, r.Left, r.Top, r.Width, r.Height, wb.hdc, r.Left, r.Top, TernaryRasterOperations.SRCCOPY); + dest_dc.ReleaseHdc(destHdc); + } + + internal override void DestroyOffscreenDrawable(object offscreen_drawable) + { + WinBuffer wb = (WinBuffer)offscreen_drawable; + + Win32DeleteObject(wb.bitmap); + Win32DeleteDC(wb.hdc); + } + + internal override void SetForegroundWindow(IntPtr handle) + { + Win32SetForegroundWindow(handle); + } + + internal override event EventHandler Idle; +#endregion // Public Static Methods + + } +} +#endregion +#endif
\ No newline at end of file diff --git a/source/ShiftUI/Internal/XplatUIDriver.cs b/source/ShiftUI/Internal/XplatUIDriver.cs new file mode 100644 index 0000000..c3f5e0d --- /dev/null +++ b/source/ShiftUI/Internal/XplatUIDriver.cs @@ -0,0 +1,532 @@ +// 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 [email protected] +// Sebastien Pouliot [email protected] +// + +// COMPLETE + +using System.Drawing; +using System.Threading; +using System.Runtime.InteropServices; +using System; + +namespace ShiftUI { + internal abstract class XplatUIDriver { + internal abstract IntPtr InitializeDriver(); + internal abstract void ShutdownDriver(IntPtr token); + internal delegate IntPtr WndProc(IntPtr hwnd, Msg msg, IntPtr wParam, IntPtr lParam); + + + #region XplatUI Driver Properties + internal virtual int ActiveWindowTrackingDelay { get { return 0; } } + + internal virtual Color ForeColor { + get { + return ThemeEngine.Current.DefaultWindowForeColor; + } + } + + internal virtual Color BackColor { + get { + return ThemeEngine.Current.DefaultWindowBackColor; + } + } + + internal virtual Size Border3DSize { + get { + return new Size (2, 2); + } + } + + internal virtual Size BorderSize { + get { + return new Size (1, 1); + } + } + + internal virtual Size CaptionButtonSize { + get { + return new Size (18, 18); + } + } + + internal virtual int CaretBlinkTime { get { return 530; } } + internal virtual int CaretWidth { get { return 10; } } + + internal virtual Size DoubleClickSize { + get { + return new Size (4, 4); + } + } + + internal virtual int DoubleClickTime { + get { + return 500; + } + } + + internal virtual Size FixedFrameBorderSize { + get { + return new Size (3, 3); + } + } + + internal virtual Font Font { + get { + return ThemeEngine.Current.DefaultFont; + } + } + + internal virtual int FontSmoothingContrast { get { return 1400; } } + internal virtual int FontSmoothingType { get { return 1; } } + internal virtual int HorizontalResizeBorderThickness { get { return 8; } } + internal virtual bool IsActiveWindowTrackingEnabled { get { return false; } } + internal virtual bool IsComboBoxAnimationEnabled { get { return false; } } + internal virtual bool IsDropShadowEnabled { get { return false; } } + internal virtual bool IsFontSmoothingEnabled { get { return true; } } + internal virtual bool IsHotTrackingEnabled { get { return false; } } + internal virtual bool IsIconTitleWrappingEnabled { get { return true; } } + internal virtual bool IsKeyboardPreferred { get { return false; } } + internal virtual bool IsListBoxSmoothScrollingEnabled { get { return true; } } + internal virtual bool IsMenuAnimationEnabled { get { return false; } } + internal virtual bool IsMenuFadeEnabled { get { return true; } } + internal virtual bool IsMinimizeRestoreAnimationEnabled { get { return false; } } + internal virtual bool IsSelectionFadeEnabled { get { return false; } } + internal virtual bool IsSnapToDefaultEnabled { get { return false; } } + internal virtual bool IsTitleBarGradientEnabled { get { return false; } } + internal virtual bool IsToolTipAnimationEnabled { get { return false; } } + internal virtual Size MenuBarButtonSize { get { return new Size (19, 19); } } + public virtual Size MenuButtonSize { + get { + return new Size(18, 18); + } + } + internal virtual int MenuShowDelay { get { return 0; } } + + internal virtual Keys ModifierKeys { + get { + return Keys.None; + } + } + + internal virtual MouseButtons MouseButtons { + get { + return MouseButtons.None; + } + } + + internal virtual Size MouseHoverSize { + get { + return new Size (1, 1); + } + } + + internal virtual int MouseHoverTime { + get { + return 500; + } + } + + internal virtual int MouseSpeed { + get { return 10; } + } + + internal virtual int MouseWheelScrollDelta { + get { + return 120; + } + } + + internal virtual Point MousePosition { + get { + return Point.Empty; + } + } + + internal virtual int MenuHeight { + get { + return 19; + } + } + + internal virtual LeftRightAlignment PopupMenuAlignment { + get { return LeftRightAlignment.Left; } + } + + internal virtual PowerStatus PowerStatus { + get { throw new NotImplementedException ("Has not been implemented yet for this platform."); } + } + + internal virtual int SizingBorderWidth { + get { return 4; } + } + + internal virtual Size SmallCaptionButtonSize { + get { return new Size (15, 15); } + } + + internal virtual bool UIEffectsEnabled { + get { return false; } + } + + internal virtual bool DropTarget { + get { + return false; + } + + set { + } + } + + internal virtual int HorizontalScrollBarHeight { + get { + return 16; + } + } + + internal virtual bool UserClipWontExposeParent { + get { + return true; + } + } + + internal virtual int VerticalResizeBorderThickness { get { return 8; } } + + internal virtual int VerticalScrollBarWidth { + get { + return 16; + } + } + + internal abstract int CaptionHeight { get; } + internal abstract Size CursorSize { get; } + internal abstract bool DragFullWindows { get; } + internal abstract Size DragSize { get; } + internal abstract Size FrameBorderSize { get; } + internal abstract Size IconSize { get; } + internal abstract Size MaxWindowTrackSize { get; } + internal abstract bool MenuAccessKeysUnderlined { get; } + internal virtual Size MinimizedWindowSize { + get { + const int BorderWidth = 3; + return new Size (154 + 2 * BorderWidth, SystemInformation.CaptionHeight + 2 * BorderWidth - 1); + } + } + internal abstract Size MinimizedWindowSpacingSize { get; } + internal abstract Size MinimumWindowSize { get; } + internal virtual Size MinimumFixedToolWindowSize { get { return Size.Empty; } } + internal virtual Size MinimumSizeableToolWindowSize { get { return Size.Empty; } } + internal virtual Size MinimumNoBorderWindowSize { get { return Size.Empty; } } + internal virtual Size MinWindowTrackSize { + get { + return new Size (112, 27); + } + } + internal abstract Size SmallIconSize { get; } + internal abstract int MouseButtonCount { get; } + internal abstract bool MouseButtonsSwapped { get; } + internal abstract bool MouseWheelPresent { get; } + internal abstract Rectangle VirtualScreen { get; } + internal abstract Rectangle WorkingArea { get; } + internal abstract Screen[] AllScreens { get; } + internal abstract bool ThemesEnabled { get; } + + internal virtual bool RequiresPositiveClientAreaSize { + get { + return true; + } + } + + public virtual int ToolWindowCaptionHeight { + get { + return 16; + } + } + + public virtual Size ToolWindowCaptionButtonSize { + get { + return new Size (15, 15); + } + } + #endregion // XplatUI Driver Properties + + internal abstract event EventHandler Idle; + + #region XplatUI Driver Methods + internal abstract void AudibleAlert(AlertType alert); + + internal abstract void BeginMoveResize (IntPtr handle); // init a window manager driven resize event + + internal abstract void EnableThemes(); + + internal abstract void GetDisplaySize(out Size size); + + internal abstract IntPtr CreateWindow(CreateParams cp); + internal abstract IntPtr CreateWindow(IntPtr Parent, int X, int Y, int Width, int Height); + internal abstract void DestroyWindow(IntPtr handle); + + internal abstract FormWindowState GetWindowState(IntPtr handle); + internal abstract void SetWindowState(IntPtr handle, FormWindowState state); + internal abstract void SetWindowMinMax(IntPtr handle, Rectangle maximized, Size min, Size max); + + internal abstract void SetWindowStyle(IntPtr handle, CreateParams cp); + + internal abstract double GetWindowTransparency(IntPtr handle); + internal abstract void SetWindowTransparency(IntPtr handle, double transparency, Color key); + internal abstract TransparencySupport SupportsTransparency(); + + internal virtual void SetAllowDrop (IntPtr handle, bool value) + { + Console.Error.WriteLine ("Drag and Drop is currently " + + "not supported on this platform"); + } + + internal virtual DragDropEffects StartDrag(IntPtr handle, object data, DragDropEffects allowedEffects) { + Console.Error.WriteLine ("Drag and Drop is currently " + + "not supported on this platform"); + return DragDropEffects.None; + } + + internal abstract void SetBorderStyle(IntPtr handle, FormBorderStyle border_style); + internal abstract void SetMenu(IntPtr handle, Menu menu); + + internal abstract bool GetText(IntPtr handle, out string text); + internal abstract bool Text(IntPtr handle, string text); + internal abstract bool SetVisible(IntPtr handle, bool visible, bool activate); + internal abstract bool IsVisible(IntPtr handle); + internal abstract bool IsEnabled(IntPtr handle); + internal virtual bool IsKeyLocked (VirtualKeys key) { return false; } + internal abstract IntPtr SetParent(IntPtr handle, IntPtr parent); + internal abstract IntPtr GetParent(IntPtr handle); + + internal abstract void UpdateWindow(IntPtr handle); + internal abstract PaintEventArgs PaintEventStart (ref Message msg, IntPtr handle, bool client); + internal abstract void PaintEventEnd (ref Message msg, IntPtr handle, bool client); + + internal abstract void SetWindowPos(IntPtr handle, int x, int y, int width, int height); + internal abstract void GetWindowPos(IntPtr handle, bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height); + internal abstract void Activate(IntPtr handle); + internal abstract void EnableWindow(IntPtr handle, bool Enable); + internal abstract void SetModal(IntPtr handle, bool Modal); + internal abstract void Invalidate(IntPtr handle, Rectangle rc, bool clear); + internal abstract void InvalidateNC(IntPtr handle); + internal abstract IntPtr DefWndProc(ref Message msg); + internal abstract void HandleException(Exception e); + internal abstract void DoEvents(); + internal abstract bool PeekMessage(Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags); + internal abstract void PostQuitMessage(int exitCode); + internal abstract bool GetMessage(object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax); + internal abstract bool TranslateMessage(ref MSG msg); + internal abstract IntPtr DispatchMessage(ref MSG msg); + + internal abstract bool SetZOrder(IntPtr hWnd, IntPtr AfterhWnd, bool Top, bool Bottom); + internal abstract bool SetTopmost(IntPtr hWnd, bool Enabled); + internal abstract bool SetOwner(IntPtr hWnd, IntPtr hWndOwner); + + internal abstract bool CalculateWindowRect(ref Rectangle ClientRect, CreateParams cp, Menu menu, out Rectangle WindowRect); + + internal abstract Region GetClipRegion(IntPtr hwnd); + internal abstract void SetClipRegion(IntPtr hwnd, Region region); + + internal abstract void SetCursor(IntPtr hwnd, IntPtr cursor); + internal abstract void ShowCursor(bool show); + internal abstract void OverrideCursor(IntPtr cursor); + internal abstract IntPtr DefineCursor(Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot); + internal abstract IntPtr DefineStdCursor(StdCursor id); + internal abstract Bitmap DefineStdCursorBitmap(StdCursor id); + internal abstract void DestroyCursor(IntPtr cursor); + internal abstract void GetCursorInfo(IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y); + + internal abstract void GetCursorPos(IntPtr hwnd, out int x, out int y); + internal abstract void SetCursorPos(IntPtr hwnd, int x, int y); + + internal abstract void ScreenToClient(IntPtr hwnd, ref int x, ref int y); + internal abstract void ClientToScreen(IntPtr hwnd, ref int x, ref int y); + + internal abstract void GrabWindow(IntPtr hwnd, IntPtr ConfineToHwnd); + internal abstract void GrabInfo(out IntPtr hwnd, out bool GrabConfined, out Rectangle GrabArea); + internal abstract void UngrabWindow(IntPtr hwnd); + + internal abstract void SendAsyncMethod (AsyncMethodData method); + internal abstract void SetTimer (Timer timer); + internal abstract void KillTimer (Timer timer); + + internal abstract void CreateCaret(IntPtr hwnd, int width, int height); + internal abstract void DestroyCaret(IntPtr hwnd); + internal abstract void SetCaretPos(IntPtr hwnd, int x, int y); + internal abstract void CaretVisible(IntPtr hwnd, bool visible); + + internal abstract IntPtr GetFocus(); + internal abstract void SetFocus(IntPtr hwnd); + internal abstract IntPtr GetActive(); + internal abstract IntPtr GetPreviousWindow(IntPtr hwnd); + + internal abstract void ScrollWindow(IntPtr hwnd, Rectangle rectangle, int XAmount, int YAmount, bool with_children); + internal abstract void ScrollWindow(IntPtr hwnd, int XAmount, int YAmount, bool with_children); + + internal abstract bool GetFontMetrics(Graphics g, Font font, out int ascent, out int descent); + + internal abstract bool SystrayAdd(IntPtr hwnd, string tip, Icon icon, out ToolTip tt); + internal abstract bool SystrayChange(IntPtr hwnd, string tip, Icon icon, ref ToolTip tt); + internal abstract void SystrayRemove(IntPtr hwnd, ref ToolTip tt); + internal abstract void SystrayBalloon(IntPtr hwnd, int timeout, string title, string text, ToolTipIcon icon); + + internal abstract Point GetMenuOrigin(IntPtr hwnd); + internal abstract void MenuToScreen(IntPtr hwnd, ref int x, ref int y); + internal abstract void ScreenToMenu(IntPtr hwnd, ref int x, ref int y); + + internal abstract void SetIcon(IntPtr handle, Icon icon); + + internal abstract void ClipboardClose(IntPtr handle); + internal abstract IntPtr ClipboardOpen (bool primary_selection); + internal abstract int ClipboardGetID(IntPtr handle, string format); + internal abstract void ClipboardStore(IntPtr handle, object obj, int id, XplatUI.ObjectToClipboard converter, bool copy); + internal abstract int[] ClipboardAvailableFormats(IntPtr handle); + internal abstract object ClipboardRetrieve(IntPtr handle, int id, XplatUI.ClipboardToObject converter); + + internal abstract void DrawReversibleLine(Point start, Point end, Color backColor); + internal abstract void DrawReversibleRectangle(IntPtr handle, Rectangle rect, int line_width); + internal abstract void FillReversibleRectangle (Rectangle rectangle, Color backColor); + internal abstract void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style); + + internal abstract SizeF GetAutoScaleSize(Font font); + + internal abstract IntPtr SendMessage(IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam); + internal abstract bool PostMessage(IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam); + internal abstract int SendInput(IntPtr hwnd, System.Collections.Queue keys); + + internal abstract object StartLoop(Thread thread); + internal abstract void EndLoop(Thread thread); + + internal abstract void RequestNCRecalc(IntPtr hwnd); + internal abstract void ResetMouseHover(IntPtr hwnd); + internal abstract void RequestAdditionalWM_NCMessages(IntPtr hwnd, bool hover, bool leave); + + internal abstract void RaiseIdle (EventArgs e); + + // System information + internal abstract int KeyboardSpeed { get; } + internal abstract int KeyboardDelay { get; } + + + // Double buffering + internal virtual void CreateOffscreenDrawable (IntPtr handle, + int width, int height, + out object offscreen_drawable) + { + Bitmap bmp = new Bitmap (width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + + offscreen_drawable = bmp; + } + + internal virtual void DestroyOffscreenDrawable (object offscreen_drawable) + { + Bitmap bmp = (Bitmap)offscreen_drawable; + + bmp.Dispose (); + } + + internal virtual Graphics GetOffscreenGraphics (object offscreen_drawable) + { + Bitmap bmp = (Bitmap)offscreen_drawable; + return Graphics.FromImage (bmp); + } + + internal virtual void BlitFromOffscreen (IntPtr dest_handle, + Graphics dest_dc, + object offscreen_drawable, + Graphics offscreen_dc, + Rectangle r) + { + dest_dc.DrawImage ((Bitmap)offscreen_drawable, r, r, GraphicsUnit.Pixel); + } + + internal virtual void SetForegroundWindow (IntPtr handle) + { + } + +#endregion // XplatUI Driver Methods + } + + static class XplatUIDriverSupport { + #region XplatUI Driver Support Methods + internal static void ExecutionCallback (AsyncMethodData data) + { + AsyncMethodResult result = data.Result; + + object ret; + try { + ret = data.Method.DynamicInvoke (data.Args); + } catch (Exception ex) { + if (result != null) { + result.CompleteWithException (ex); + return; + } + + throw; + } + + if (result != null) { + result.Complete (ret); + } + } + + static void ExecutionCallbackInContext (object state) + { + AsyncMethodData data = (AsyncMethodData) state; + + if (data.SyncContext == null) { + ExecutionCallback (data); + return; + } + + var oldContext = SynchronizationContext.Current; + SynchronizationContext.SetSynchronizationContext (data.SyncContext); + + try { + ExecutionCallback (data); + } finally { + SynchronizationContext.SetSynchronizationContext (oldContext); + } + } + + internal static void ExecuteClientMessage (GCHandle gchandle) + { + AsyncMethodData data = (AsyncMethodData) gchandle.Target; + try { + if (data.Context == null) { + ExecutionCallback (data); + } else { + data.SyncContext = SynchronizationContext.Current; + ExecutionContext.Run (data.Context, new ContextCallback (ExecutionCallbackInContext), data); + } + } + finally { + gchandle.Free (); + } + } + + #endregion // XplatUI Driver Support Methods + } +} diff --git a/source/ShiftUI/Internal/XplatUIStructs.cs b/source/ShiftUI/Internal/XplatUIStructs.cs new file mode 100644 index 0000000..f0c3bc5 --- /dev/null +++ b/source/ShiftUI/Internal/XplatUIStructs.cs @@ -0,0 +1,1042 @@ +// 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 [email protected] +// +// *** When you make some changes to this file, dont forget to update Tests/TestHelper class *** + +// NOT COMPLETE + +using System; +using System.Drawing; +using System.Runtime.InteropServices; + +#if PUBLIC_TYPES +namespace Mono.Winforms +#else +namespace ShiftUI +#endif +{ + [Flags] +#if PUBLIC_TYPES + public +#else + internal +#endif + enum WindowStyles : int { + WS_OVERLAPPED = 0x00000000, + WS_POPUP = unchecked((int)0x80000000), + WS_CHILD = 0x40000000, + WS_MINIMIZE = 0x20000000, + WS_VISIBLE = 0x10000000, + WS_DISABLED = 0x08000000, + WS_CLIPSIBLINGS = 0x04000000, + WS_CLIPCHILDREN = 0x02000000, + WS_MAXIMIZE = 0x01000000, + WS_CAPTION = 0x00C00000, // == WS_BORDER | WS_DLGFRAME + WS_BORDER = 0x00800000, + WS_DLGFRAME = 0x00400000, + WS_VSCROLL = 0x00200000, + WS_HSCROLL = 0x00100000, + WS_SYSMENU = 0x00080000, + WS_THICKFRAME = 0x00040000, + WS_GROUP = 0x00020000, + WS_TABSTOP = 0x00010000, + WS_MINIMIZEBOX = 0x00020000, + WS_MAXIMIZEBOX = 0x00010000, + WS_TILED = 0x00000000, + WS_ICONIC = 0x20000000, + WS_SIZEBOX = 0x00040000, + WS_POPUPWINDOW = unchecked((int)0x80880000), + WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, + WS_TILEDWINDOW = WS_OVERLAPPEDWINDOW, + WS_CHILDWINDOW = WS_CHILD, + } + + [Flags] +#if PUBLIC_TYPES + public +#else + internal +#endif + enum WindowExStyles : int { + // Extended Styles + WS_EX_DLGMODALFRAME = 0x00000001, + WS_EX_DRAGDETECT = 0x00000002, + WS_EX_NOPARENTNOTIFY = 0x00000004, + WS_EX_TOPMOST = 0x00000008, + WS_EX_ACCEPTFILES = 0x00000010, + WS_EX_TRANSPARENT = 0x00000020, + + WS_EX_MDICHILD = 0x00000040, + WS_EX_TOOLWINDOW = 0x00000080, + WS_EX_WINDOWEDGE = 0x00000100, + WS_EX_CLIENTEDGE = 0x00000200, + WS_EX_CONTEXTHELP = 0x00000400, + + WS_EX_RIGHT = 0x00001000, + WS_EX_LEFT = 0x00000000, + WS_EX_RTLREADING = 0x00002000, + WS_EX_LTRREADING = 0x00000000, + WS_EX_LEFTSCROLLBAR = 0x00004000, + WS_EX_LAYERED = 0x00080000, + WS_EX_RIGHTSCROLLBAR = 0x00000000, + + WS_EX_WidgetPARENT = 0x00010000, + WS_EX_STATICEDGE = 0x00020000, + WS_EX_APPWINDOW = 0x00040000, + WS_EX_NOINHERITLAYOUT = 0x00100000, + WS_EX_LAYOUTRTL = 0x00400000, + WS_EX_COMPOSITED = 0x02000000, + WS_EX_NOACTIVATE = 0x08000000, + + WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE, + WS_EX_PALETTEWINDOW = WS_EX_WINDOWEDGE | WS_EX_TOOLWINDOW | WS_EX_TOPMOST + } + + [Flags] +#if PUBLIC_TYPES + public +#else + internal +#endif + enum ToolBarStyles : int { + TBSTYLE_TOOLTIPS = 0x100, + TBSTYLE_FLAT = 0x800, + TBSTYLE_LIST = 0x1000, + TBSTYLE_TRANSPARENT = 0x8000 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum Msg { + WM_NULL = 0x0000, + WM_CREATE = 0x0001, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + WM_ACTIVATE = 0x0006, + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + // public const uint WM_SETVISIBLE = 0x0009; + WM_ENABLE = 0x000A, + WM_SETREDRAW = 0x000B, + WM_SETTEXT = 0x000C, + WM_GETTEXT = 0x000D, + WM_GETTEXTLENGTH = 0x000E, + WM_PAINT = 0x000F, + WM_CLOSE = 0x0010, + WM_QUERYENDSESSION = 0x0011, + WM_QUIT = 0x0012, + WM_QUERYOPEN = 0x0013, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_ENDSESSION = 0x0016, + // public const uint WM_SYSTEMERROR = 0x0017; + WM_SHOWWINDOW = 0x0018, + WM_CTLCOLOR = 0x0019, + WM_WININICHANGE = 0x001A, + WM_SETTINGCHANGE = 0x001A, + WM_DEVMODECHANGE = 0x001B, + WM_ACTIVATEAPP = 0x001C, + WM_FONTCHANGE = 0x001D, + WM_TIMECHANGE = 0x001E, + WM_CANCELMODE = 0x001F, + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_CHILDACTIVATE = 0x0022, + WM_QUEUESYNC = 0x0023, + WM_GETMINMAXINFO = 0x0024, + WM_PAINTICON = 0x0026, + WM_ICONERASEBKGND = 0x0027, + WM_NEXTDLGCTL = 0x0028, + // public const uint WM_ALTTABACTIVE = 0x0029; + WM_SPOOLERSTATUS = 0x002A, + WM_DRAWITEM = 0x002B, + WM_MEASUREITEM = 0x002C, + WM_DELETEITEM = 0x002D, + WM_VKEYTOITEM = 0x002E, + WM_CHARTOITEM = 0x002F, + WM_SETFONT = 0x0030, + WM_GETFONT = 0x0031, + WM_SETHOTKEY = 0x0032, + WM_GETHOTKEY = 0x0033, + // public const uint WM_FILESYSCHANGE = 0x0034; + // public const uint WM_ISACTIVEICON = 0x0035; + // public const uint WM_QUERYPARKICON = 0x0036; + WM_QUERYDRAGICON = 0x0037, + WM_COMPAREITEM = 0x0039, + // public const uint WM_TESTING = 0x003a; + // public const uint WM_OTHERWINDOWCREATED = 0x003c; + WM_GETOBJECT = 0x003D, + // public const uint WM_ACTIVATESHELLWINDOW = 0x003e; + WM_COMPACTING = 0x0041, + WM_COMMNOTIFY = 0x0044 , + WM_WINDOWPOSCHANGING = 0x0046, + WM_WINDOWPOSCHANGED = 0x0047, + WM_POWER = 0x0048, + WM_COPYDATA = 0x004A, + WM_CANCELJOURNAL = 0x004B, + WM_NOTIFY = 0x004E, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + WM_TCARD = 0x0052, + WM_HELP = 0x0053, + WM_USERCHANGED = 0x0054, + WM_NOTIFYFORMAT = 0x0055, + WM_CONTEXTMENU = 0x007B, + WM_STYLECHANGING = 0x007C, + WM_STYLECHANGED = 0x007D, + WM_DISPLAYCHANGE = 0x007E, + WM_GETICON = 0x007F, + + // Non-Client messages + WM_SETICON = 0x0080, + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + WM_GETDLGCODE = 0x0087, + WM_SYNCPAINT = 0x0088, + // public const uint WM_SYNCTASK = 0x0089; + WM_NCMOUSEMOVE = 0x00A0, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, + WM_NCLBUTTONDBLCLK = 0x00A3, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCRBUTTONUP = 0x00A5, + WM_NCRBUTTONDBLCLK = 0x00A6, + WM_NCMBUTTONDOWN = 0x00A7, + WM_NCMBUTTONUP = 0x00A8, + WM_NCMBUTTONDBLCLK = 0x00A9, + // public const uint WM_NCXBUTTONDOWN = 0x00ab; + // public const uint WM_NCXBUTTONUP = 0x00ac; + // public const uint WM_NCXBUTTONDBLCLK = 0x00ad; + WM_KEYDOWN = 0x0100, + WM_KEYFIRST = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + WM_DEADCHAR = 0x0103, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, + WM_SYSCHAR = 0x0106, + WM_SYSDEADCHAR = 0x0107, + WM_KEYLAST = 0x0108, + WM_IME_STARTCOMPOSITION = 0x010D, + WM_IME_ENDCOMPOSITION = 0x010E, + WM_IME_COMPOSITION = 0x010F, + WM_IME_KEYLAST = 0x010F, + WM_INITDIALOG = 0x0110, + WM_COMMAND = 0x0111, + WM_SYSCOMMAND = 0x0112, + WM_TIMER = 0x0113, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + // public const uint WM_SYSTIMER = 0x0118; + WM_MENUSELECT = 0x011F, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + WM_MENURBUTTONUP = 0x0122, + WM_MENUDRAG = 0x0123, + WM_MENUGETOBJECT = 0x0124, + WM_UNINITMENUPOPUP = 0x0125, + WM_MENUCOMMAND = 0x0126, + + WM_CHANGEUISTATE = 0x0127, + WM_UPDATEUISTATE = 0x0128, + WM_QUERYUISTATE = 0x0129, + + // public const uint WM_LBTRACKPOINT = 0x0131; + WM_CTLCOLORMSGBOX = 0x0132, + WM_CTLCOLOREDIT = 0x0133, + WM_CTLCOLORLISTBOX = 0x0134, + WM_CTLCOLORBTN = 0x0135, + WM_CTLCOLORDLG = 0x0136, + WM_CTLCOLORSCROLLBAR = 0x0137, + WM_CTLCOLORSTATIC = 0x0138, + WM_MOUSEMOVE = 0x0200, + WM_MOUSEFIRST = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + WM_MOUSEWHEEL = 0x020A, + WM_MOUSELAST = 0x020D, + // public const uint WM_XBUTTONDOWN = 0x020B; + // public const uint WM_XBUTTONUP = 0x020C; + // public const uint WM_XBUTTONDBLCLK = 0x020D; + WM_PARENTNOTIFY = 0x0210, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + WM_NEXTMENU = 0x0213, + WM_SIZING = 0x0214, + WM_CAPTURECHANGED = 0x0215, + WM_MOVING = 0x0216, + // public const uint WM_POWERBROADCAST = 0x0218; + WM_DEVICECHANGE = 0x0219, + WM_MDICREATE = 0x0220, + WM_MDIDESTROY = 0x0221, + WM_MDIACTIVATE = 0x0222, + WM_MDIRESTORE = 0x0223, + WM_MDINEXT = 0x0224, + WM_MDIMAXIMIZE = 0x0225, + WM_MDITILE = 0x0226, + WM_MDICASCADE = 0x0227, + WM_MDIICONARRANGE = 0x0228, + WM_MDIGETACTIVE = 0x0229, + /* D&D messages */ + // public const uint WM_DROPOBJECT = 0x022A; + // public const uint WM_QUERYDROPOBJECT = 0x022B; + // public const uint WM_BEGINDRAG = 0x022C; + // public const uint WM_DRAGLOOP = 0x022D; + // public const uint WM_DRAGSELECT = 0x022E; + // public const uint WM_DRAGMOVE = 0x022F; + WM_MDISETMENU = 0x0230, + WM_ENTERSIZEMOVE = 0x0231, + WM_EXITSIZEMOVE = 0x0232, + WM_DROPFILES = 0x0233, + WM_MDIREFRESHMENU = 0x0234, + WM_IME_SETCONTEXT = 0x0281, + WM_IME_NOTIFY = 0x0282, + WM_IME_Widget = 0x0283, + WM_IME_COMPOSITIONFULL = 0x0284, + WM_IME_SELECT = 0x0285, + WM_IME_CHAR = 0x0286, + WM_IME_REQUEST = 0x0288, + WM_IME_KEYDOWN = 0x0290, + WM_IME_KEYUP = 0x0291, + WM_NCMOUSEHOVER = 0x02A0, + WM_MOUSEHOVER = 0x02A1, + WM_NCMOUSELEAVE = 0x02A2, + WM_MOUSELEAVE = 0x02A3, + WM_CUT = 0x0300, + WM_COPY = 0x0301, + WM_PASTE = 0x0302, + WM_CLEAR = 0x0303, + WM_UNDO = 0x0304, + WM_RENDERFORMAT = 0x0305, + WM_RENDERALLFORMATS = 0x0306, + WM_DESTROYCLIPBOARD = 0x0307, + WM_DRAWCLIPBOARD = 0x0308, + WM_PAINTCLIPBOARD = 0x0309, + WM_VSCROLLCLIPBOARD = 0x030A, + WM_SIZECLIPBOARD = 0x030B, + WM_ASKCBFORMATNAME = 0x030C, + WM_CHANGECBCHAIN = 0x030D, + WM_HSCROLLCLIPBOARD = 0x030E, + WM_QUERYNEWPALETTE = 0x030F, + WM_PALETTEISCHANGING = 0x0310, + WM_PALETTECHANGED = 0x0311, + WM_HOTKEY = 0x0312, + WM_PRINT = 0x0317, + WM_PRINTCLIENT = 0x0318, + WM_HANDHELDFIRST = 0x0358, + WM_HANDHELDLAST = 0x035F, + WM_AFXFIRST = 0x0360, + WM_AFXLAST = 0x037F, + WM_PENWINFIRST = 0x0380, + WM_PENWINLAST = 0x038F, + WM_APP = 0x8000, + WM_USER = 0x0400, + + // Our "private" ones + WM_MOUSE_ENTER = 0x0401, + WM_ASYNC_MESSAGE = 0x0403, + WM_REFLECT = WM_USER + 0x1c00, + WM_CLOSE_INTERNAL = WM_USER + 0x1c01, + + // private messages to support on-the-spot IME editing. + WM_XIM_PREEDITSTART = WM_USER + 0x1d00, + WM_XIM_PREEDITDONE = WM_USER + 0x1d01, + WM_XIM_PREEDITDRAW = WM_USER + 0x1d02, + WM_XIM_PREEDITCARET = WM_USER + 0x1d03, + + // NotifyIcon (Systray) Balloon messages + NIN_BALLOONSHOW = WM_USER + 0x0002, + NIN_BALLOONHIDE = WM_USER + 0x0003, + NIN_BALLOONTIMEOUT = WM_USER + 0x0004, + NIN_BALLOONUSERCLICK = WM_USER + 0x0005 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum MsgButtons { + MK_LBUTTON = 0x0001, + MK_RBUTTON = 0x0002, + MK_SHIFT = 0x0004, + MK_CONTROL = 0x0008, + MK_MBUTTON = 0x0010, + MK_XBUTTON1 = 0x0020, + MK_XBUTTON2 = 0x0040, + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum MsgUIState { + UIS_SET = 1, + UIS_CLEAR = 2, + UIS_INITIALIZE = 3, + UISF_HIDEFOCUS = 0x1, + UISF_HIDEACCEL = 0x2, + UISF_ACTIVE = 0x4 + } + + [StructLayout(LayoutKind.Sequential)] +#if PUBLIC_TYPES + public +#else + internal +#endif + struct POINT { + public int x; + public int y; + + public POINT (int x, int y) + { + this.x = x; + this.y = y; + } + + public Point ToPoint () + { + return new Point (x, y); + } + + public override string ToString () + { + return "Point {" + x.ToString () + ", " + y.ToString () + "}"; + } + } + + [StructLayout(LayoutKind.Sequential)] +#if PUBLIC_TYPES + public +#else + internal +#endif + struct MSG { + internal IntPtr hwnd; + internal Msg message; + internal IntPtr wParam; + internal IntPtr lParam; + internal uint time; + internal POINT pt; + internal object refobject; + + public override string ToString () + { + return String.Format ("msg=0x{0:x} ({1}) hwnd=0x{2:x} wparam=0x{3:x} lparam=0x{4:x} pt={5}", (int) message, message.ToString (), hwnd.ToInt32 (), wParam.ToInt32 (), lParam.ToInt32 (), pt); + } + } + + [Flags] +#if PUBLIC_TYPES + public +#else + internal +#endif + enum TransparencySupport { + None = 0x00, + Get = 0x01, + Set = 0x02, + GetSet = 0x03 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum WindowActiveFlags { + WA_INACTIVE = 0, + WA_ACTIVE = 1, + WA_CLICKACTIVE = 2 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum KeybdEventFlags { + None = 0, + ExtendedKey = 0x0001, + KeyUp = 0x0002 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum VirtualKeys { + VK_LBUTTON = 0x01, + VK_RBUTTON = 0x02, + VK_CANCEL = 0x03, + VK_MBUTTON = 0x04, + VK_XBUTTON1 = 0x05, + VK_XBUTTON2 = 0x06, + VK_BACK = 0x08, + VK_TAB = 0x09, + VK_CLEAR = 0x0C, + VK_RETURN = 0x0D, + VK_SHIFT = 0x10, + VK_CONTROL = 0x11, + VK_MENU = 0x12, + VK_PAUSE = 0x13, + VK_CAPITAL = 0x14, + VK_ESCAPE = 0x1B, + VK_CONVERT = 0x1C, + VK_NONCONVERT = 0x1D, + VK_SPACE = 0x20, + VK_PRIOR = 0x21, + VK_NEXT = 0x22, + VK_END = 0x23, + VK_HOME = 0x24, + VK_LEFT = 0x25, + VK_UP = 0x26, + VK_RIGHT = 0x27, + VK_DOWN = 0x28, + VK_SELECT = 0x29, + VK_PRINT = 0x2A, + VK_EXECUTE = 0x2B, + VK_SNAPSHOT = 0x2C, + VK_INSERT = 0x2D, + VK_DELETE = 0x2E, + VK_HELP = 0x2F, + VK_0 = 0x30, + VK_1 = 0x31, + VK_2 = 0x32, + VK_3 = 0x33, + VK_4 = 0x34, + VK_5 = 0x35, + VK_6 = 0x36, + VK_7 = 0x37, + VK_8 = 0x38, + VK_9 = 0x39, + VK_A = 0x41, + VK_B = 0x42, + VK_C = 0x43, + VK_D = 0x44, + VK_E = 0x45, + VK_F = 0x46, + VK_G = 0x47, + VK_H = 0x48, + VK_I = 0x49, + VK_J = 0x4A, + VK_K = 0x4B, + VK_L = 0x4C, + VK_M = 0x4D, + VK_N = 0x4E, + VK_O = 0x4F, + VK_P = 0x50, + VK_Q = 0x51, + VK_R = 0x52, + VK_S = 0x53, + VK_T = 0x54, + VK_U = 0x55, + VK_V = 0x56, + VK_W = 0x57, + VK_X = 0x58, + VK_Y = 0x59, + VK_Z = 0x5A, + VK_LWIN = 0x5B, + VK_RWIN = 0x5C, + VK_APPS = 0x5D, + VK_NUMPAD0 = 0x60, + VK_NUMPAD1 = 0x61, + VK_NUMPAD2 = 0x62, + VK_NUMPAD3 = 0x63, + VK_NUMPAD4 = 0x64, + VK_NUMPAD5 = 0x65, + VK_NUMPAD6 = 0x66, + VK_NUMPAD7 = 0x67, + VK_NUMPAD8 = 0x68, + VK_NUMPAD9 = 0x69, + VK_MULTIPLY = 0x6A, + VK_ADD = 0x6B, + VK_SEPARATOR = 0x6C, + VK_SUBTRACT = 0x6D, + VK_DECIMAL = 0x6E, + VK_DIVIDE = 0x6F, + VK_F1 = 0x70, + VK_F2 = 0x71, + VK_F3 = 0x72, + VK_F4 = 0x73, + VK_F5 = 0x74, + VK_F6 = 0x75, + VK_F7 = 0x76, + VK_F8 = 0x77, + VK_F9 = 0x78, + VK_F10 = 0x79, + VK_F11 = 0x7A, + VK_F12 = 0x7B, + VK_F13 = 0x7C, + VK_F14 = 0x7D, + VK_F15 = 0x7E, + VK_F16 = 0x7F, + VK_F17 = 0x80, + VK_F18 = 0x81, + VK_F19 = 0x82, + VK_F20 = 0x83, + VK_F21 = 0x84, + VK_F22 = 0x85, + VK_F23 = 0x86, + VK_F24 = 0x87, + VK_NUMLOCK = 0x90, + VK_SCROLL = 0x91, + VK_LSHIFT = 0xA0, + VK_RSHIFT = 0xA1, + VK_LWidget = 0xA2, + VK_RWidget = 0xA3, + VK_LMENU = 0xA4, + VK_RMENU = 0xA5, + VK_OEM_1 = 0xBA, + VK_OEM_PLUS = 0xBB, + VK_OEM_COMMA = 0xBC, + VK_OEM_MINUS = 0xBD, + VK_OEM_PERIOD = 0xBE, + VK_OEM_2 = 0xBF, + VK_OEM_3 = 0xC0, + VK_OEM_4 = 0xDB, + VK_OEM_5 = 0xDC, + VK_OEM_6 = 0xDD, + VK_OEM_7 = 0xDE, + VK_OEM_8 = 0xDF, + VK_OEM_AX = 0xE1, + VK_OEM_102 = 0xE2, + VK_ICO_HELP = 0xE3, + VK_ICO_00 = 0xE4, + VK_PROCESSKEY = 0xE5, + VK_OEM_ATTN = 0xF0, + VK_OEM_COPY = 0xF2, + VK_OEM_AUTO = 0xF3, + VK_OEM_ENLW = 0xF4, + VK_OEM_BACKTAB = 0xF5, + VK_ATTN = 0xF6, + VK_CRSEL = 0xF7, + VK_EXSEL = 0xF8, + VK_EREOF = 0xF9, + VK_PLAY = 0xFA, + VK_ZOOM = 0xFB, + VK_NONAME = 0xFC, + VK_PA1 = 0xFD, + VK_OEM_CLEAR = 0xFE, + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum TtyKeys { + XK_BackSpace = 0xff08, /* Back space, back char */ + XK_Tab = 0xff09, + XK_Linefeed = 0xff0a, /* Linefeed, LF */ + XK_Clear = 0xff0b, + XK_Return = 0xff0d, /* Return, enter */ + XK_Pause = 0xff13, /* Pause, hold */ + XK_Scroll_Lock = 0xff14, + XK_Sys_Req = 0xff15, + XK_Escape = 0xff1b, + XK_Delete = 0xffff /* Delete, rubout */ + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum MiscKeys { + XK_ISO_Lock = 0xfe01, + XK_ISO_Last_Group_Lock = 0xfe0f, + XK_Select = 0xff60, + XK_Print = 0xff61, + XK_Execute = 0xff62, + XK_Insert = 0xff63, + XK_Undo = 0xff65, + XK_Redo = 0xff66, + XK_Menu = 0xff67, + XK_Find = 0xff68, + XK_Cancel = 0xff69, + XK_Help = 0xff6a, + XK_Break = 0xff6b, + XK_Mode_switch = 0xff7e, + XK_script_switch = 0xff7e, + XK_Num_Lock = 0xff7f + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum KeypadKeys { + XK_KP_Space = 0xff80, + XK_KP_Tab = 0xff89, + XK_KP_Enter = 0xff8d, /* Enter */ + XK_KP_F1 = 0xff91, /* PF1, KP_A, ... */ + XK_KP_F2 = 0xff92, + XK_KP_F3 = 0xff93, + XK_KP_F4 = 0xff94, + XK_KP_Home = 0xff95, + XK_KP_Left = 0xff96, + XK_KP_Up = 0xff97, + XK_KP_Right = 0xff98, + XK_KP_Down = 0xff99, + XK_KP_Prior = 0xff9a, + XK_KP_Page_Up = 0xff9a, + XK_KP_Next = 0xff9b, + XK_KP_Page_Down = 0xff9b, + XK_KP_End = 0xff9c, + XK_KP_Begin = 0xff9d, + XK_KP_Insert = 0xff9e, + XK_KP_Delete = 0xff9f, + XK_KP_Equal = 0xffbd, /* Equals */ + XK_KP_Multiply = 0xffaa, + XK_KP_Add = 0xffab, + XK_KP_Separator = 0xffac, /* Separator, often comma */ + XK_KP_Subtract = 0xffad, + XK_KP_Decimal = 0xffae, + XK_KP_Divide = 0xffaf, + + XK_KP_0 = 0xffb0, + XK_KP_1 = 0xffb1, + XK_KP_2 = 0xffb2, + XK_KP_3 = 0xffb3, + XK_KP_4 = 0xffb4, + XK_KP_5 = 0xffb5, + XK_KP_6 = 0xffb6, + XK_KP_7 = 0xffb7, + XK_KP_8 = 0xffb8, + XK_KP_9 = 0xffb9 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum DeadKeys { + XK_dead_grave = 0xfe50, + XK_dead_acute = 0xfe51, + XK_dead_circumflex = 0xfe52, + XK_dead_tilde = 0xfe53, + XK_dead_macron = 0xfe54, + XK_dead_breve = 0xfe55, + XK_dead_abovedot = 0xfe56, + XK_dead_diaeresis = 0xfe57, + XK_dead_abovering = 0xfe58, + XK_dead_doubleacute = 0xfe59, + XK_dead_caron = 0xfe5a, + XK_dead_cedilla = 0xfe5b, + XK_dead_ogonek = 0xfe5c, + XK_dead_iota = 0xfe5d, + XK_dead_voiced_sound = 0xfe5e, + XK_dead_semivoiced_sound = 0xfe5f, + XK_dead_belowdot = 0xfe60, + XK_dead_hook = 0xfe61, + XK_dead_horn = 0xfe62 + + } + + [StructLayout(LayoutKind.Sequential)] +#if PUBLIC_TYPES + public +#else + internal +#endif + struct HELPINFO { + internal uint cbSize; + internal int iContextType; + internal int iCtrlId; + internal IntPtr hItemHandle; + internal uint dwContextId; + internal POINT MousePos; + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum PeekMessageFlags { + PM_NOREMOVE = 0x00000000, + PM_REMOVE = 0x00000001, + PM_NOYIELD = 0x00000002 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum StdCursor { + Default = 0, + AppStarting = 1, + Arrow = 2, + Cross = 3, + Hand = 4, + Help = 5, + HSplit = 6, + IBeam = 7, + No = 8, + NoMove2D = 9, + NoMoveHoriz = 10, + NoMoveVert = 11, + PanEast = 12, + PanNE = 13, + PanNorth = 14, + PanNW = 15, + PanSE = 16, + PanSouth = 17, + PanSW = 18, + PanWest = 19, + SizeAll = 20, + SizeNESW = 21, + SizeNS = 22, + SizeNWSE = 23, + SizeWE = 24, + UpArrow = 25, + VSplit = 26, + WaitCursor = 27 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum HitTest { + HTERROR = -2, + HTTRANSPARENT = -1, + HTNOWHERE = 0, + HTCLIENT = 1, + HTCAPTION = 2, + HTSYSMENU = 3, + HTGROWBOX = 4, + HTSIZE = HTGROWBOX, + HTMENU = 5, + HTHSCROLL = 6, + HTVSCROLL = 7, + HTMINBUTTON = 8, + HTMAXBUTTON = 9, + HTLEFT = 10, + HTRIGHT = 11, + HTTOP = 12, + HTTOPLEFT = 13, + HTTOPRIGHT = 14, + HTBOTTOM = 15, + HTBOTTOMLEFT = 16, + HTBOTTOMRIGHT = 17, + HTBORDER = 18, + HTREDUCE = HTMINBUTTON, + HTZOOM = HTMAXBUTTON, + HTSIZEFIRST = HTLEFT, + HTSIZELAST = HTBOTTOMRIGHT, + HTOBJECT = 19, + HTCLOSE = 20, + HTHELP = 21 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum TitleStyle { + None = 0, + Normal = 1, + Tool = 2 + } + + [StructLayout(LayoutKind.Sequential)] +#if PUBLIC_TYPES + public +#else + internal +#endif + struct BITMAPINFOHEADER { + internal uint biSize; + internal int biWidth; + internal int biHeight; + internal ushort biPlanes; + internal ushort biBitCount; + internal uint biCompression; + internal uint biSizeImage; + internal int biXPelsPerMeter; + internal int biYPelsPerMeter; + internal uint biClrUsed; + internal uint biClrImportant; + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum ClipboardFormats : ushort { + CF_TEXT = 1, + CF_BITMAP = 2, + CF_METAFILEPICT = 3, + CF_SYLK = 4, + CF_DIF = 5, + CF_TIFF = 6, + CF_OEMTEXT = 7, + CF_DIB = 8, + CF_PALETTE = 9, + CF_PENDATA = 10, + CF_RIFF = 11, + CF_WAVE = 12, + CF_UNICODETEXT = 13, + CF_ENHMETAFILE = 14, + CF_HDROP = 15, + CF_LOCALE = 16, + CF_DIBV5 = 17 + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] +#if PUBLIC_TYPES + public +#else + internal +#endif + struct MINMAXINFO { + internal POINT ptReserved; + internal POINT ptMaxSize; + internal POINT ptMaxPosition; + internal POINT ptMinTrackSize; + internal POINT ptMaxTrackSize; + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] +#if PUBLIC_TYPES + public +#else + internal +#endif + struct KeyFilterData { + internal bool Down; + internal int keycode; + internal int keysym; + internal Keys ModifierKeys; + internal String str; + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum ScrollBarCommands { + SB_LINEUP = 0, + SB_LINELEFT = 0, + SB_LINEDOWN = 1, + SB_LINERIGHT = 1, + SB_PAGEUP = 2, + SB_PAGELEFT = 2, + SB_PAGEDOWN = 3, + SB_PAGERIGHT = 3, + SB_THUMBPOSITION = 4, + SB_THUMBTRACK = 5, + SB_TOP = 6, + SB_LEFT = 6, + SB_BOTTOM = 7, + SB_RIGHT = 7, + SB_ENDSCROLL = 8 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum ClipCombineMode { + RGN_AND = 1, + RGN_OR, + RGN_XOR, + RGN_DIFF, + RGN_COPY, + + RGN_MIN = RGN_AND, + RGN_MAX = RGN_COPY + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum SystemCommands { + SC_SIZE = 0xF000, + SC_MOVE = 0xF010, + SC_MINIMIZE = 0xF020, + SC_MAXIMIZE = 0xF030, + SC_NEXTWINDOW = 0xF040, + SC_PREVWINDOW = 0xF050, + SC_CLOSE = 0xF060, + SC_VSCROLL = 0xF070, + SC_HSCROLL = 0xF080, + SC_MOUSEMENU = 0xF090, + SC_KEYMENU = 0xF100, + SC_ARRANGE = 0xF110, + SC_RESTORE = 0xF120, + SC_TASKLIST = 0xF130, + SC_SCREENSAVE = 0xF140, + SC_HOTKEY = 0xF150, + SC_DEFAULT = 0xF160, + SC_MONITORPOWER = 0xF170, + SC_CONTEXTHELP = 0xF180 + } + +#if PUBLIC_TYPES + public +#else + internal +#endif + enum AlertType { + Default = 1, + Error = 2, + Question = 3, + Warning = 4, + Information = 5 + } +} + diff --git a/source/ShiftUI/Internal/XplatUIWin32.cs b/source/ShiftUI/Internal/XplatUIWin32.cs new file mode 100644 index 0000000..a741a46 --- /dev/null +++ b/source/ShiftUI/Internal/XplatUIWin32.cs @@ -0,0 +1,3751 @@ +// 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 [email protected] +// +// + +// NOT COMPLETE + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + + +/// Win32 Version +namespace ShiftUI { + internal class XplatUIWin32 : XplatUIDriver { + #region Local Variables + private static XplatUIWin32 instance; + private static int ref_count; + private static IntPtr FosterParentLast; + + internal static MouseButtons mouse_state; + internal static Point mouse_position; + internal static bool grab_confined; + internal static IntPtr grab_hwnd; + internal static Rectangle grab_area; + internal static WndProc wnd_proc; + internal static IntPtr prev_mouse_hwnd; + internal static bool caret_visible; + + internal static bool themes_enabled; + private Hashtable timer_list; + private static Queue message_queue; + private static IntPtr clip_magic = new IntPtr(27051977); + private static int scroll_width; + private static int scroll_height; + private static Hashtable wm_nc_registered; + private static RECT clipped_cursor_rect; + private Hashtable registered_classes; + private Hwnd HwndCreating; // the Hwnd we are currently creating (see CreateWindow) + + #endregion // Local Variables + + #region Private Structs + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + private struct WNDCLASS { + internal int style; + internal WndProc lpfnWndProc; + internal int cbClsExtra; + internal int cbWndExtra; + internal IntPtr hInstance; + internal IntPtr hIcon; + internal IntPtr hCursor; + internal IntPtr hbrBackground; + [MarshalAs(UnmanagedType.LPWStr)] + internal string lpszMenuName; + [MarshalAs(UnmanagedType.LPWStr)] + internal string lpszClassName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct RECT { + internal int left; + internal int top; + internal int right; + internal int bottom; + + public RECT (int left, int top, int right, int bottom) + { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + #region Instance Properties + public int Height { get { return bottom - top; } } + public int Width { get { return right - left; } } + public Size Size { get { return new Size (Width, Height); } } + public Point Location { get { return new Point (left, top); } } + #endregion + + #region Instance Methods + public Rectangle ToRectangle () + { + return Rectangle.FromLTRB (left, top, right, bottom); + } + + public static RECT FromRectangle (Rectangle rectangle) + { + return new RECT (rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom); + } + + public override int GetHashCode () + { + return left ^ ((top << 13) | (top >> 0x13)) + ^ ((Width << 0x1a) | (Width >> 6)) + ^ ((Height << 7) | (Height >> 0x19)); + } + + public override string ToString () + { + return String.Format("RECT left={0}, top={1}, right={2}, bottom={3}, width={4}, height={5}", left, top, right, bottom, right-left, bottom-top); + } + #endregion + + #region Operator overloads + public static implicit operator Rectangle (RECT rect) + { + return Rectangle.FromLTRB (rect.left, rect.top, rect.right, rect.bottom); + } + + public static implicit operator RECT (Rectangle rect) + { + return new RECT (rect.Left, rect.Top, rect.Right, rect.Bottom); + } + #endregion + } + + internal enum SPIAction { + SPI_GETACTIVEWINDOWTRACKING = 0x1000, + SPI_GETACTIVEWNDTRKTIMEOUT = 0x2002, + SPI_GETANIMATION = 0x0048, + SPI_GETCARETWIDTH = 0x2006, + SPI_GETCOMBOBOXANIMATION = 0x1004, + SPI_GETDRAGFULLWINDOWS = 0x0026, + SPI_GETDROPSHADOW = 0x1024, + SPI_GETFONTSMOOTHING = 0x004A, + SPI_GETFONTSMOOTHINGCONTRAST = 0x200C, + SPI_GETFONTSMOOTHINGTYPE = 0x200A, + SPI_GETGRADIENTCAPTIONS = 0x1008, + SPI_GETHOTTRACKING = 0x100E, + SPI_GETICONTITLEWRAP = 0x0019, + SPI_GETKEYBOARDSPEED = 0x000A, + SPI_GETKEYBOARDDELAY = 0x0016, + SPI_GETKEYBOARDCUES = 0x100A, + SPI_GETKEYBOARDPREF = 0x0044, + SPI_GETLISTBOXSMOOTHSCROLLING = 0x1006, + SPI_GETMENUANIMATION = 0x1002, + SPI_GETMENUDROPALIGNMENT = 0x001B, + SPI_GETMENUFADE = 0x1012, + SPI_GETMENUSHOWDELAY = 0x006A, + SPI_GETMOUSESPEED = 0x0070, + SPI_GETSELECTIONFADE = 0x1014, + SPI_GETSNAPTODEFBUTTON = 0x005F, + SPI_GETTOOLTIPANIMATION = 0x1016, + SPI_GETWORKAREA = 0x0030, + SPI_GETMOUSEHOVERWIDTH = 0x0062, + SPI_GETMOUSEHOVERHEIGHT = 0x0064, + SPI_GETMOUSEHOVERTIME = 0x0066, + SPI_GETUIEFFECTS = 0x103E, + SPI_GETWHEELSCROLLLINES = 0x0068 + } + + internal enum WindowPlacementFlags { + SW_HIDE = 0, + SW_SHOWNORMAL = 1, + SW_NORMAL = 1, + SW_SHOWMINIMIZED = 2, + SW_SHOWMAXIMIZED = 3, + SW_MAXIMIZE = 3, + SW_SHOWNOACTIVATE = 4, + SW_SHOW = 5, + SW_MINIMIZE = 6, + SW_SHOWMINNOACTIVE = 7, + SW_SHOWNA = 8, + SW_RESTORE = 9, + SW_SHOWDEFAULT = 10, + SW_FORCEMINIMIZE = 11, + SW_MAX = 11 + } + + [StructLayout(LayoutKind.Sequential)] + private struct WINDOWPLACEMENT { + internal uint length; + internal uint flags; + internal WindowPlacementFlags showCmd; + internal POINT ptMinPosition; + internal POINT ptMaxPosition; + internal RECT rcNormalPosition; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct NCCALCSIZE_PARAMS { + internal RECT rgrc1; + internal RECT rgrc2; + internal RECT rgrc3; + internal IntPtr lppos; + } + + [Flags] + private enum TMEFlags { + TME_HOVER = 0x00000001, + TME_LEAVE = 0x00000002, + TME_NONCLIENT = 0x00000010, + TME_QUERY = unchecked((int)0x40000000), + TME_CANCEL = unchecked((int)0x80000000) + } + + [StructLayout(LayoutKind.Sequential)] + private struct TRACKMOUSEEVENT { + internal int size; + internal TMEFlags dwFlags; + internal IntPtr hWnd; + internal int dwHoverTime; + } + + [StructLayout(LayoutKind.Sequential)] + private struct PAINTSTRUCT { + internal IntPtr hdc; + internal int fErase; + internal RECT rcPaint; + internal int fRestore; + internal int fIncUpdate; + internal int Reserved1; + internal int Reserved2; + internal int Reserved3; + internal int Reserved4; + internal int Reserved5; + internal int Reserved6; + internal int Reserved7; + internal int Reserved8; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct KEYBDINPUT { + internal short wVk; + internal short wScan; + internal Int32 dwFlags; + internal Int32 time; + internal UIntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MOUSEINPUT { + internal Int32 dx; + internal Int32 dy; + internal Int32 mouseData; + internal Int32 dwFlags; + internal Int32 time; + internal UIntPtr dwExtraInfo; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct HARDWAREINPUT { + internal Int32 uMsg; + internal short wParamL; + internal short wParamH; + } + + [StructLayout (LayoutKind.Sequential)] + internal struct ICONINFO { + internal bool fIcon; + internal Int32 xHotspot; + internal Int32 yHotspot; + internal IntPtr hbmMask; + internal IntPtr hbmColor; + } + + [StructLayout(LayoutKind.Explicit)] + internal struct INPUT { + [FieldOffset(0)] + internal Int32 type; + + [FieldOffset(4)] + internal MOUSEINPUT mi; + + [FieldOffset(4)] + internal KEYBDINPUT ki; + + [FieldOffset(4)] + internal HARDWAREINPUT hi; + } + + [StructLayout (LayoutKind.Sequential)] + public struct ANIMATIONINFO { + internal uint cbSize; + internal int iMinAnimate; + } + + internal enum InputFlags { + KEYEVENTF_EXTENDEDKEY = 0x0001, + KEYEVENTF_KEYUP = 0x0002, + KEYEVENTF_SCANCODE = 0x0003, + KEYEVENTF_UNICODE = 0x0004, + } + + internal enum ClassStyle { + CS_VREDRAW = 0x00000001, + CS_HREDRAW = 0x00000002, + CS_KEYCVTWINDOW = 0x00000004, + CS_DBLCLKS = 0x00000008, + CS_OWNDC = 0x00000020, + CS_CLASSDC = 0x00000040, + CS_PARENTDC = 0x00000080, + CS_NOKEYCVT = 0x00000100, + CS_NOCLOSE = 0x00000200, + CS_SAVEBITS = 0x00000800, + CS_BYTEALIGNCLIENT = 0x00001000, + CS_BYTEALIGNWINDOW = 0x00002000, + CS_GLOBALCLASS = 0x00004000, + CS_IME = 0x00010000, + // Windows XP+ + CS_DROPSHADOW = 0x00020000 + } + + internal enum SetWindowPosZOrder { + HWND_TOP = 0, + HWND_BOTTOM = 1, + HWND_TOPMOST = -1, + HWND_NOTOPMOST = -2 + } + + [Flags] + internal enum SetWindowPosFlags { + SWP_ASYNCWINDOWPOS = 0x4000, + SWP_DEFERERASE = 0x2000, + SWP_DRAWFRAME = 0x0020, + SWP_FRAMECHANGED = 0x0020, + SWP_HIDEWINDOW = 0x0080, + SWP_NOACTIVATE = 0x0010, + SWP_NOCOPYBITS = 0x0100, + SWP_NOMOVE = 0x0002, + SWP_NOOWNERZORDER = 0x0200, + SWP_NOREDRAW = 0x0008, + SWP_NOREPOSITION = 0x0200, + SWP_NOENDSCHANGING = 0x0400, + SWP_NOSIZE = 0x0001, + SWP_NOZORDER = 0x0004, + SWP_SHOWWINDOW = 0x0040 + } + + internal enum GetSysColorIndex { + COLOR_SCROLLBAR = 0, + COLOR_BACKGROUND = 1, + COLOR_ACTIVECAPTION = 2, + COLOR_INACTIVECAPTION = 3, + COLOR_MENU = 4, + COLOR_WINDOW = 5, + COLOR_WINDOWFRAME = 6, + COLOR_MENUTEXT = 7, + COLOR_WINDOWTEXT = 8, + COLOR_CAPTIONTEXT = 9, + COLOR_ACTIVEBORDER = 10, + COLOR_INACTIVEBORDER = 11, + COLOR_APPWORKSPACE = 12, + COLOR_HIGHLIGHT = 13, + COLOR_HIGHLIGHTTEXT = 14, + COLOR_BTNFACE = 15, + COLOR_BTNSHADOW = 16, + COLOR_GRAYTEXT = 17, + COLOR_BTNTEXT = 18, + COLOR_INACTIVECAPTIONTEXT = 19, + COLOR_BTNHIGHLIGHT = 20, + COLOR_3DDKSHADOW = 21, + COLOR_3DLIGHT = 22, + COLOR_INFOTEXT = 23, + COLOR_INFOBK = 24, + + COLOR_HOTLIGHT = 26, + COLOR_GRADIENTACTIVECAPTION = 27, + COLOR_GRADIENTINACTIVECAPTION = 28, + COLOR_MENUHIGHLIGHT = 29, + COLOR_MENUBAR = 30, + + COLOR_DESKTOP = 1, + COLOR_3DFACE = 16, + COLOR_3DSHADOW = 16, + COLOR_3DHIGHLIGHT = 20, + COLOR_3DHILIGHT = 20, + COLOR_BTNHILIGHT = 20, + COLOR_MAXVALUE = 24,/* Maximum value */ + } + + private enum LoadCursorType { + First = 32512, + IDC_ARROW = 32512, + IDC_IBEAM = 32513, + IDC_WAIT = 32514, + IDC_CROSS = 32515, + IDC_UPARROW = 32516, + IDC_SIZE = 32640, + IDC_ICON = 32641, + IDC_SIZENWSE = 32642, + IDC_SIZENESW = 32643, + IDC_SIZEWE = 32644, + IDC_SIZENS = 32645, + IDC_SIZEALL = 32646, + IDC_NO = 32648, + IDC_HAND = 32649, + IDC_APPSTARTING = 32650, + IDC_HELP = 32651, + Last = 32651 + } + + private enum AncestorType { + GA_PARENT = 1, + GA_ROOT = 2, + GA_ROOTOWNER = 3 + } + + [Flags] + private enum WindowLong { + GWL_WNDPROC = -4, + GWL_HINSTANCE = -6, + GWL_HWNDPARENT = -8, + GWL_STYLE = -16, + GWL_EXSTYLE = -20, + GWL_USERDATA = -21, + GWL_ID = -12 + } + + [Flags] + private enum LogBrushStyle { + BS_SOLID = 0, + BS_NULL = 1, + BS_HATCHED = 2, + BS_PATTERN = 3, + BS_INDEXED = 4, + BS_DIBPATTERN = 5, + BS_DIBPATTERNPT = 6, + BS_PATTERN8X8 = 7, + BS_DIBPATTERN8X8 = 8, + BS_MONOPATTERN = 9 + } + + [Flags] + private enum LogBrushHatch { + HS_HORIZONTAL = 0, /* ----- */ + HS_VERTICAL = 1, /* ||||| */ + HS_FDIAGONAL = 2, /* \\\\\ */ + HS_BDIAGONAL = 3, /* ///// */ + HS_CROSS = 4, /* +++++ */ + HS_DIAGCROSS = 5, /* xxxxx */ + } + + internal struct COLORREF { + internal byte R; + internal byte G; + internal byte B; + internal byte A; + } + + [StructLayout(LayoutKind.Sequential)] + private struct LOGBRUSH { + internal LogBrushStyle lbStyle; + internal COLORREF lbColor; + internal LogBrushHatch lbHatch; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct TEXTMETRIC { + internal int tmHeight; + internal int tmAscent; + internal int tmDescent; + internal int tmInternalLeading; + internal int tmExternalLeading; + internal int tmAveCharWidth; + internal int tmMaxCharWidth; + internal int tmWeight; + internal int tmOverhang; + internal int tmDigitizedAspectX; + internal int tmDigitizedAspectY; + internal short tmFirstChar; + internal short tmLastChar; + internal short tmDefaultChar; + internal short tmBreakChar; + internal byte tmItalic; + internal byte tmUnderlined; + internal byte tmStruckOut; + internal byte tmPitchAndFamily; + internal byte tmCharSet; + } + + public enum TernaryRasterOperations : uint + { + SRCCOPY = 0x00CC0020, + SRCPAINT = 0x00EE0086, + SRCAND = 0x008800C6, + SRCINVERT = 0x00660046, + SRCERASE = 0x00440328, + NOTSRCCOPY = 0x00330008, + NOTSRCERASE = 0x001100A6, + MERGECOPY = 0x00C000CA, + MERGEPAINT = 0x00BB0226, + PATCOPY = 0x00F00021, + PATPAINT = 0x00FB0A09, + PATINVERT = 0x005A0049, + DSTINVERT = 0x00550009, + BLACKNESS = 0x00000042, + WHITENESS = 0x00FF0062 + } + + [Flags] + private enum ScrollWindowExFlags { + SW_NONE = 0x0000, + SW_SCROLLCHILDREN = 0x0001, + SW_INVALIDATE = 0x0002, + SW_ERASE = 0x0004, + SW_SMOOTHSCROLL = 0x0010 + } + + internal enum SystemMetrics { + SM_CXSCREEN = 0, + SM_CYSCREEN = 1, + SM_CXVSCROLL = 2, + SM_CYHSCROLL = 3, + SM_CYCAPTION = 4, + SM_CXBORDER = 5, + SM_CYBORDER = 6, + SM_CXDLGFRAME = 7, + SM_CYDLGFRAME = 8, + SM_CYVTHUMB = 9, + SM_CXHTHUMB = 10, + SM_CXICON = 11, + SM_CYICON = 12, + SM_CXCURSOR = 13, + SM_CYCURSOR = 14, + SM_CYMENU = 15, + SM_CXFULLSCREEN = 16, + SM_CYFULLSCREEN = 17, + SM_CYKANJIWINDOW = 18, + SM_MOUSEPRESENT = 19, + SM_CYVSCROLL = 20, + SM_CXHSCROLL = 21, + SM_DEBUG = 22, + SM_SWAPBUTTON = 23, + SM_RESERVED1 = 24, + SM_RESERVED2 = 25, + SM_RESERVED3 = 26, + SM_RESERVED4 = 27, + SM_CXMIN = 28, + SM_CYMIN = 29, + SM_CXSIZE = 30, + SM_CYSIZE = 31, + SM_CXFRAME = 32, + SM_CYFRAME = 33, + SM_CXMINTRACK = 34, + SM_CYMINTRACK = 35, + SM_CXDOUBLECLK = 36, + SM_CYDOUBLECLK = 37, + SM_CXICONSPACING = 38, + SM_CYICONSPACING = 39, + SM_MENUDROPALIGNMENT = 40, + SM_PENWINDOWS = 41, + SM_DBCSENABLED = 42, + SM_CMOUSEBUTTONS = 43, + SM_CXFIXEDFRAME = SM_CXDLGFRAME, + SM_CYFIXEDFRAME = SM_CYDLGFRAME, + SM_CXSIZEFRAME = SM_CXFRAME, + SM_CYSIZEFRAME = SM_CYFRAME, + SM_SECURE = 44, + SM_CXEDGE = 45, + SM_CYEDGE = 46, + SM_CXMINSPACING = 47, + SM_CYMINSPACING = 48, + SM_CXSMICON = 49, + SM_CYSMICON = 50, + SM_CYSMCAPTION = 51, + SM_CXSMSIZE = 52, + SM_CYSMSIZE = 53, + SM_CXMENUSIZE = 54, + SM_CYMENUSIZE = 55, + SM_ARRANGE = 56, + SM_CXMINIMIZED = 57, + SM_CYMINIMIZED = 58, + SM_CXMAXTRACK = 59, + SM_CYMAXTRACK = 60, + SM_CXMAXIMIZED = 61, + SM_CYMAXIMIZED = 62, + SM_NETWORK = 63, + SM_CLEANBOOT = 67, + SM_CXDRAG = 68, + SM_CYDRAG = 69, + SM_SHOWSOUNDS = 70, + SM_CXMENUCHECK = 71, + SM_CYMENUCHECK = 72, + SM_SLOWMACHINE = 73, + SM_MIDEASTENABLED = 74, + SM_MOUSEWHEELPRESENT = 75, + SM_XVIRTUALSCREEN = 76, + SM_YVIRTUALSCREEN = 77, + SM_CXVIRTUALSCREEN = 78, + SM_CYVIRTUALSCREEN = 79, + SM_CMONITORS = 80, + SM_SAMEDISPLAYFORMAT = 81, + SM_IMMENABLED = 82, + SM_CXFOCUSBORDER = 83, + SM_CYFOCUSBORDER = 84, + SM_TABLETPC = 86, + SM_MEDIACENTER = 87, + SM_CMETRICS = 88 + } + + // We'll only support _WIN32_IE < 0x0500 for now + internal enum NotifyIconMessage { + NIM_ADD = 0x00000000, + NIM_MODIFY = 0x00000001, + NIM_DELETE = 0x00000002, + } + + [Flags] + internal enum NotifyIconFlags { + NIF_MESSAGE = 0x00000001, + NIF_ICON = 0x00000002, + NIF_TIP = 0x00000004, + NIF_STATE = 0x00000008, + NIF_INFO = 0x00000010 + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + internal struct NOTIFYICONDATA { + internal uint cbSize; + internal IntPtr hWnd; + internal uint uID; + internal NotifyIconFlags uFlags; + internal uint uCallbackMessage; + internal IntPtr hIcon; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=128)] + internal string szTip; + internal int dwState; + internal int dwStateMask; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=256)] + internal string szInfo; + internal int uTimeoutOrVersion; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst=64)] + internal string szInfoTitle; + internal ToolTipIcon dwInfoFlags; + } + + [Flags] + internal enum DCExFlags { + DCX_WINDOW = 0x00000001, + DCX_CACHE = 0x00000002, + DCX_NORESETATTRS = 0x00000004, + DCX_CLIPCHILDREN = 0x00000008, + DCX_CLIPSIBLINGS = 0x00000010, + DCX_PARENTCLIP = 0x00000020, + DCX_EXCLUDERGN = 0x00000040, + DCX_INTERSECTRGN = 0x00000080, + DCX_EXCLUDEUPDATE = 0x00000100, + DCX_INTERSECTUPDATE = 0x00000200, + DCX_LOCKWINDOWUPDATE = 0x00000400, + DCX_USESTYLE = 0x00010000, + DCX_VALIDATE = 0x00200000 + } + + [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] + internal struct CLIENTCREATESTRUCT { + internal IntPtr hWindowMenu; + internal uint idFirstChild; + } + + private enum ClassLong : int { + GCL_MENUNAME = -8, + GCL_HBRBACKGROUND = -10, + GCL_HCURSOR = -12, + GCL_HICON = -14, + GCL_HMODULE = -16, + GCL_CBWNDEXTRA = -18, + GCL_CBCLSEXTRA = -20, + GCL_WNDPROC = -24, + GCL_STYLE = -26, + GCW_ATOM = -32, + GCL_HICONSM = -34 + } + + [Flags] + internal enum GAllocFlags : uint { + GMEM_FIXED = 0x0000, + GMEM_MOVEABLE = 0x0002, + GMEM_NOCOMPACT = 0x0010, + GMEM_NODISCARD = 0x0020, + GMEM_ZEROINIT = 0x0040, + GMEM_MODIFY = 0x0080, + GMEM_DISCARDABLE = 0x0100, + GMEM_NOT_BANKED = 0x1000, + GMEM_SHARE = 0x2000, + GMEM_DDESHARE = 0x2000, + GMEM_NOTIFY = 0x4000, + GMEM_LOWER = GMEM_NOT_BANKED, + GMEM_VALID_FLAGS = 0x7F72, + GMEM_INVALID_HANDLE = 0x8000, + GHND = (GMEM_MOVEABLE | GMEM_ZEROINIT), + GPTR = (GMEM_FIXED | GMEM_ZEROINIT) + } + + internal enum ROP2DrawMode : int { + R2_BLACK = 1, + R2_NOTMERGEPEN = 2, + R2_MASKNOTPEN = 3, + R2_NOTCOPYPEN = 4, + R2_MASKPENNOT = 5, + R2_NOT = 6, + R2_XORPEN = 7, + R2_NOTMASKPEN = 8, + R2_MASKPEN = 9, + R2_NOTXORPEN = 10, + R2_NOP = 11, + R2_MERGENOTPEN = 12, + R2_COPYPEN = 13, + R2_MERGEPENNOT = 14, + R2_MERGEPEN = 15, + R2_WHITE = 16, + R2_LAST = 16 + } + + internal enum PenStyle : int { + PS_SOLID = 0, + PS_DASH = 1, + PS_DOT = 2, + PS_DASHDOT = 3, + PS_DASHDOTDOT = 4, + PS_NULL = 5, + PS_INSIDEFRAME = 6, + PS_USERSTYLE = 7, + PS_ALTERNATE = 8 + } + + internal enum PatBltRop : int { + PATCOPY = 0xf00021, + PATINVERT = 0x5a0049, + DSTINVERT = 0x550009, + BLACKNESS = 0x000042, + WHITENESS = 0xff0062, + } + + internal enum StockObject : int { + WHITE_BRUSH = 0, + LTGRAY_BRUSH = 1, + GRAY_BRUSH = 2, + DKGRAY_BRUSH = 3, + BLACK_BRUSH = 4, + NULL_BRUSH = 5, + HOLLOW_BRUSH = NULL_BRUSH, + WHITE_PEN = 6, + BLACK_PEN = 7, + NULL_PEN = 8, + OEM_FIXED_FONT = 10, + ANSI_FIXED_FONT = 11, + ANSI_VAR_FONT = 12, + SYSTEM_FONT = 13, + DEVICE_DEFAULT_FONT = 14, + DEFAULT_PALETTE = 15, + SYSTEM_FIXED_FONT = 16 + } + + internal enum HatchStyle : int { + HS_HORIZONTAL = 0, + HS_VERTICAL = 1, + HS_FDIAGONAL = 2, + HS_BDIAGONAL = 3, + HS_CROSS = 4, + HS_DIAGCROSS = 5 + } + + [Flags] + internal enum SndFlags : int { + SND_SYNC = 0x0000, + SND_ASYNC = 0x0001, + SND_NODEFAULT = 0x0002, + SND_MEMORY = 0x0004, + SND_LOOP = 0x0008, + SND_NOSTOP = 0x0010, + SND_NOWAIT = 0x00002000, + SND_ALIAS = 0x00010000, + SND_ALIAS_ID = 0x00110000, + SND_FILENAME = 0x00020000, + SND_RESOURCE = 0x00040004, + SND_PURGE = 0x0040, + SND_APPLICATION = 0x0080, + } + + [Flags] + internal enum LayeredWindowAttributes : int { + LWA_COLORKEY = 0x1, + LWA_ALPHA = 0x2, + } + + public enum ACLineStatus : byte { + Offline = 0, + Online = 1, + Unknown = 255 + } + + public enum BatteryFlag : byte { + High = 1, + Low = 2, + Critical = 4, + Charging = 8, + NoSystemBattery = 128, + Unknown = 255 + } + + [StructLayout (LayoutKind.Sequential)] + public class SYSTEMPOWERSTATUS { + public ACLineStatus _ACLineStatus; + public BatteryFlag _BatteryFlag; + public Byte _BatteryLifePercent; + public Byte _Reserved1; + public Int32 _BatteryLifeTime; + public Int32 _BatteryFullLifeTime; + } + #endregion + + #region Constructor & Destructor + private XplatUIWin32() { + // Handle singleton stuff first + ref_count=0; + + mouse_state = MouseButtons.None; + mouse_position = Point.Empty; + + grab_confined = false; + grab_area = Rectangle.Empty; + + message_queue = new Queue(); + + themes_enabled = false; + + wnd_proc = new WndProc(InternalWndProc); + + FosterParentLast = IntPtr.Zero; + + scroll_height = Win32GetSystemMetrics(SystemMetrics.SM_CYHSCROLL); + scroll_width = Win32GetSystemMetrics(SystemMetrics.SM_CXVSCROLL); + + timer_list = new Hashtable (); + registered_classes = new Hashtable (); + } + #endregion // Constructor & Destructor + + #region Private Support Methods + + private IntPtr GetFosterParent() + { + if (!IsWindow(FosterParentLast)) + { + FosterParentLast=Win32CreateWindow(WindowExStyles.WS_EX_TOOLWINDOW, "static", "Foster Parent Window", WindowStyles.WS_OVERLAPPEDWINDOW, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + if (FosterParentLast==IntPtr.Zero) { + Win32MessageBox(IntPtr.Zero, "Could not create foster window, win32 error " + Win32GetLastError().ToString(), "Oops", 0); + } + } + return FosterParentLast; + } + + private string RegisterWindowClass (int classStyle) + { + string class_name; + + lock (registered_classes) { + class_name = (string)registered_classes[classStyle]; + + if (class_name != null) + return class_name; + + class_name = string.Format ("ShiftUI.Form.Thread={0}.GUID={1}", System.Threading.Thread.GetDomainID ().ToString (), Guid.NewGuid().ToString()); + + WNDCLASS wndClass; + + wndClass.style = classStyle; + wndClass.lpfnWndProc = wnd_proc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hbrBackground = (IntPtr)(GetSysColorIndex.COLOR_WINDOW + 1); + wndClass.hCursor = Win32LoadCursor (IntPtr.Zero, LoadCursorType.IDC_ARROW); + wndClass.hIcon = IntPtr.Zero; + wndClass.hInstance = IntPtr.Zero; + wndClass.lpszClassName = class_name; + wndClass.lpszMenuName = ""; + + bool result = Win32RegisterClass (ref wndClass); + + if (result == false) + Win32MessageBox (IntPtr.Zero, "Could not register the window class, win32 error " + Win32GetLastError ().ToString (), "Oops", 0); + + registered_classes[classStyle] = class_name; + } + + return class_name; + } + + private static bool RetrieveMessage(ref MSG msg) { + MSG message; + + if (message_queue.Count == 0) { + return false; + } + + message = (MSG)message_queue.Dequeue(); + msg = message; + + return true; + } + + private static bool StoreMessage(ref MSG msg) { + MSG message = new MSG(); + + message = msg; + message_queue.Enqueue(message); + + return true; + } + + internal static String AnsiToString(IntPtr ansi_data) { + return (string)Marshal.PtrToStringAnsi(ansi_data); + } + + internal static String UnicodeToString(IntPtr unicode_data) { + return (string)Marshal.PtrToStringUni(unicode_data); + } + + internal static Image DIBtoImage(IntPtr dib_data) { + BITMAPINFOHEADER bmi; + int ncolors; + int imagesize; + //int palettesize; + Bitmap bmp; + BitmapData bits; + ColorPalette pal; + int[] palette; + byte[] imagebits; + int bytesPerLine; + + bmi = (BITMAPINFOHEADER)Marshal.PtrToStructure(dib_data, typeof(BITMAPINFOHEADER)); + + ncolors = (int)bmi.biClrUsed; + if (ncolors == 0) { + if (bmi.biBitCount < 24) { + ncolors = (int)(1 << bmi.biBitCount); + } + } + //palettesize = ncolors * 4; + + imagesize = (int)bmi.biSizeImage; + if (imagesize == 0) { + imagesize = (int)(((((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3) * bmi.biHeight); + } + + switch(bmi.biBitCount) { + case 1: { // Monochrome + bmp = new Bitmap(bmi.biWidth, bmi.biHeight, PixelFormat.Format1bppIndexed); + palette = new int[2]; + break; + } + + case 4: { // 4bpp + bmp = new Bitmap(bmi.biWidth, bmi.biHeight, PixelFormat.Format4bppIndexed); + palette = new int[16]; + break; + } + + case 8: { // 8bpp + bmp = new Bitmap(bmi.biWidth, bmi.biHeight, PixelFormat.Format8bppIndexed); + palette = new int[256]; + break; + } + + case 24: + case 32: { // 32bpp + bmp = new Bitmap(bmi.biWidth, bmi.biHeight, PixelFormat.Format32bppArgb); + palette = new int[0]; + break; + } + + default: { + throw new Exception("Unexpected number of bits:" + bmi.biBitCount.ToString()); + } + } + + if (bmi.biBitCount < 24) { + pal = bmp.Palette; // Managed palette + Marshal.Copy((IntPtr)((int)dib_data + Marshal.SizeOf(typeof(BITMAPINFOHEADER))), palette, 0, palette.Length); + + for (int i = 0; i < ncolors; i++) { + pal.Entries[i] = Color.FromArgb(palette[i] | unchecked((int)0xff000000)); + } + bmp.Palette = pal; + } + + bytesPerLine = (int)((((bmi.biWidth * bmi.biBitCount) + 31) & ~31) >> 3); + bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); + + imagebits = new byte[bytesPerLine]; + + for (int y = 0; y < bmi.biHeight; y++) { + // Copy from source to managed + Marshal.Copy((IntPtr)((int)dib_data + Marshal.SizeOf(typeof(BITMAPINFOHEADER)) + palette.Length * 4 + bytesPerLine * y), imagebits, 0, bytesPerLine); + + // Copy from managed to dest + Marshal.Copy(imagebits, 0, (IntPtr)((int)bits.Scan0 + bits.Stride * (bmi.biHeight - 1 - y)), imagebits.Length); + } + + bmp.UnlockBits(bits); + + return bmp; + } + + internal static byte[] ImageToDIB(Image image) { + MemoryStream ms; + byte[] buffer; + byte[] retbuf; + + ms = new MemoryStream(); + image.Save(ms, ImageFormat.Bmp); + buffer = ms.GetBuffer(); + + // Filter out the file header + retbuf = new byte[buffer.Length]; + Array.Copy(buffer, 14, retbuf, 0, buffer.Length - 14); + return retbuf; + } + + internal static IntPtr DupGlobalMem(IntPtr mem) { + IntPtr dup; + IntPtr dup_ptr; + IntPtr mem_ptr; + uint len; + + len = Win32GlobalSize(mem); + mem_ptr = Win32GlobalLock(mem); + + dup = Win32GlobalAlloc(GAllocFlags.GMEM_MOVEABLE, (int)len); + dup_ptr = Win32GlobalLock(dup); + + Win32CopyMemory(dup_ptr, mem_ptr, (int)len); + + Win32GlobalUnlock(mem); + Win32GlobalUnlock(dup); + + return dup; + } + + private int GetSystemParametersInfoInt (SPIAction spi) + { + int value = 0; + + Win32SystemParametersInfo (spi, 0, ref value, 0); + + return value; + } + + private bool GetSystemParametersInfoBool (SPIAction spi) + { + bool value = false; + + Win32SystemParametersInfo (spi, 0, ref value, 0); + + return value; + } + #endregion // Private Support Methods + + #region Static Properties + internal override int ActiveWindowTrackingDelay { + get { return GetSystemParametersInfoInt (SPIAction.SPI_GETACTIVEWNDTRKTIMEOUT); } + } + + internal override int CaretWidth { + get { + // Supported on 2k, XP, 2k3 + + if (Environment.OSVersion.Version.Major < 5) + throw new NotSupportedException (); + + return GetSystemParametersInfoInt (SPIAction.SPI_GETCARETWIDTH); + } + } + + internal override int FontSmoothingContrast { + get { + // Supported on XP, 2k3 + + if (Environment.OSVersion.Version.Major < 5 || (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)) + throw new NotSupportedException (); + + return GetSystemParametersInfoInt (SPIAction.SPI_GETFONTSMOOTHINGCONTRAST); + } + } + + internal override int FontSmoothingType { + get { + // Supported on XP, 2k3 + + if (Environment.OSVersion.Version.Major < 5 || (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)) + throw new NotSupportedException (); + + return GetSystemParametersInfoInt (SPIAction.SPI_GETFONTSMOOTHINGTYPE); + } + } + + internal override int HorizontalResizeBorderThickness { + get { return Win32GetSystemMetrics (SystemMetrics.SM_CXSIZEFRAME); } + } + + internal override bool IsActiveWindowTrackingEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETACTIVEWINDOWTRACKING); } + } + + internal override bool IsComboBoxAnimationEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETCOMBOBOXANIMATION); } + } + + internal override bool IsDropShadowEnabled { + get { + // Supported on XP, 2k3 + + if (Environment.OSVersion.Version.Major < 5 || (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)) + throw new NotSupportedException (); + + return GetSystemParametersInfoBool (SPIAction.SPI_GETDROPSHADOW); + } + } + + internal override bool IsFontSmoothingEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETFONTSMOOTHING); } + } + + internal override bool IsHotTrackingEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETHOTTRACKING); } + } + + internal override bool IsIconTitleWrappingEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETICONTITLEWRAP); } + } + + internal override bool IsKeyboardPreferred { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETKEYBOARDPREF); } + } + + internal override bool IsListBoxSmoothScrollingEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETLISTBOXSMOOTHSCROLLING); } + } + + internal override bool IsMenuAnimationEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETMENUANIMATION); } + } + + internal override bool IsMenuFadeEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETMENUFADE); } + } + + internal override bool IsMinimizeRestoreAnimationEnabled { + get { + ANIMATIONINFO ai = new ANIMATIONINFO (); + ai.cbSize = (uint)Marshal.SizeOf (ai); + + Win32SystemParametersInfo (SPIAction.SPI_GETANIMATION, 0, ref ai, 0); + return ai.iMinAnimate == 0 ? false : true; + } + } + + internal override bool IsSelectionFadeEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETSELECTIONFADE); } + } + + internal override bool IsSnapToDefaultEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETSNAPTODEFBUTTON); } + } + + internal override bool IsTitleBarGradientEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETGRADIENTCAPTIONS); } + } + + internal override bool IsToolTipAnimationEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETTOOLTIPANIMATION); } + } + + internal override Size MenuBarButtonSize { + get { + return new Size (Win32GetSystemMetrics (SystemMetrics.SM_CXMENUSIZE), + Win32GetSystemMetrics (SystemMetrics.SM_CYMENUSIZE)); + } + } + + public override Size MenuButtonSize { + get { + return new Size ( + Win32GetSystemMetrics (SystemMetrics.SM_CXMENUSIZE), + Win32GetSystemMetrics (SystemMetrics.SM_CYMENUSIZE)); + } + } + + internal override int MenuShowDelay { + get { return GetSystemParametersInfoInt (SPIAction.SPI_GETMENUSHOWDELAY); } + } + + internal override int MouseSpeed { + get { return GetSystemParametersInfoInt (SPIAction.SPI_GETMOUSESPEED); } + } + + internal override LeftRightAlignment PopupMenuAlignment { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETMENUDROPALIGNMENT) == true ? LeftRightAlignment.Left : LeftRightAlignment.Right; } + } + + internal override PowerStatus PowerStatus { + get { + SYSTEMPOWERSTATUS p = new SYSTEMPOWERSTATUS (); + + Win32GetSystemPowerStatus (p); + + PowerStatus ps = new PowerStatus ((BatteryChargeStatus)p._BatteryFlag, p._BatteryFullLifeTime, (float)p._BatteryLifePercent / 255f, p._BatteryLifeTime, (PowerLineStatus)p._ACLineStatus); + + return ps; + } + } + + internal override int SizingBorderWidth { + get { return Win32GetSystemMetrics (SystemMetrics.SM_CXSIZEFRAME); } + } + + internal override Size SmallCaptionButtonSize { + get { + return new Size (Win32GetSystemMetrics (SystemMetrics.SM_CXSMSIZE), + Win32GetSystemMetrics (SystemMetrics.SM_CYSMSIZE)); + } + } + + internal override bool UIEffectsEnabled { + get { return GetSystemParametersInfoBool (SPIAction.SPI_GETUIEFFECTS); } + } + + internal override int VerticalResizeBorderThickness { + get { return Win32GetSystemMetrics (SystemMetrics.SM_CYSIZEFRAME); } + } + + internal override void RaiseIdle (EventArgs e) + { + if (Idle != null) + Idle (this, e); + } + + internal override Keys ModifierKeys { + get { + short state; + Keys key_state; + + key_state = Keys.None; + + state = Win32GetKeyState(VirtualKeys.VK_SHIFT); + if ((state & 0x8000) != 0) { + key_state |= Keys.Shift; + } + state = Win32GetKeyState(VirtualKeys.VK_CONTROL); + if ((state & 0x8000) != 0) { + key_state |= Keys.Widget; + } + + state = Win32GetKeyState(VirtualKeys.VK_MENU); + if ((state & 0x8000) != 0) { + key_state |= Keys.Alt; + } + + return key_state; + } + } + + internal override MouseButtons MouseButtons { + get { + return mouse_state; + } + } + + internal override Point MousePosition { + get { + return mouse_position; + } + } + + internal override Size MouseHoverSize { + get { + int width = 4; + int height = 4; + + Win32SystemParametersInfo(SPIAction.SPI_GETMOUSEHOVERWIDTH, 0, ref width, 0); + Win32SystemParametersInfo(SPIAction.SPI_GETMOUSEHOVERWIDTH, 0, ref height, 0); + return new Size(width, height); + } + } + + internal override int MouseHoverTime { + get { + int time = 500; + + Win32SystemParametersInfo(SPIAction.SPI_GETMOUSEHOVERTIME, 0, ref time, 0); + return time; + } + } + + internal override int MouseWheelScrollDelta { + get { + int delta = 120; + Win32SystemParametersInfo(SPIAction.SPI_GETWHEELSCROLLLINES, 0, ref delta, 0); + return delta; + } + } + + internal override int HorizontalScrollBarHeight { + get { + return scroll_height; + } + } + + internal override bool UserClipWontExposeParent { + get { + return false; + } + } + + + internal override int VerticalScrollBarWidth { + get { + return scroll_width; + } + } + + internal override int MenuHeight { + get { + return Win32GetSystemMetrics(SystemMetrics.SM_CYMENU); + } + } + + internal override Size Border3DSize { + get { + return new Size (Win32GetSystemMetrics (SystemMetrics.SM_CXEDGE), + Win32GetSystemMetrics (SystemMetrics.SM_CYEDGE)); + } + } + + internal override Size BorderSize { + get { + return new Size (Win32GetSystemMetrics (SystemMetrics.SM_CXBORDER), + Win32GetSystemMetrics (SystemMetrics.SM_CYBORDER)); + } + } + + internal override bool DropTarget { + get { + return false; + } + + set { + if (value) { + //throw new NotImplementedException("Need to figure out D'n'D for Win32"); + } + } + } + + internal override Size CaptionButtonSize { + get { + return new Size (Win32GetSystemMetrics (SystemMetrics.SM_CXSIZE), + Win32GetSystemMetrics (SystemMetrics.SM_CYSIZE)); + } + } + + internal override int CaptionHeight { + get { + return Win32GetSystemMetrics(SystemMetrics.SM_CYCAPTION); + } + } + + internal override Size CursorSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR), Win32GetSystemMetrics(SystemMetrics.SM_CYCURSOR)); + } + } + + internal override bool DragFullWindows { + get { + int full = 0; + Win32SystemParametersInfo (SPIAction.SPI_GETDRAGFULLWINDOWS, 0, ref full, 0); + return (full != 0); + } + } + + internal override Size DragSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXDRAG), Win32GetSystemMetrics(SystemMetrics.SM_CYDRAG)); + } + } + + internal override Size DoubleClickSize { + get { + return new Size (Win32GetSystemMetrics (SystemMetrics.SM_CXDOUBLECLK), + Win32GetSystemMetrics (SystemMetrics.SM_CYDOUBLECLK)); + } + } + + internal override int DoubleClickTime { + get { + return Win32GetDoubleClickTime (); + } + } + + internal override Size FixedFrameBorderSize { + get { + return new Size (Win32GetSystemMetrics (SystemMetrics.SM_CXFIXEDFRAME), + Win32GetSystemMetrics (SystemMetrics.SM_CYFIXEDFRAME)); + } + } + + internal override Size FrameBorderSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXFRAME), Win32GetSystemMetrics(SystemMetrics.SM_CYFRAME)); + } + } + + internal override Size IconSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXICON), Win32GetSystemMetrics(SystemMetrics.SM_CYICON)); + } + } + + internal override Size MaxWindowTrackSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXMAXTRACK), Win32GetSystemMetrics(SystemMetrics.SM_CYMAXTRACK)); + } + } + + internal override bool MenuAccessKeysUnderlined { + get { + int underlined = 0; + Win32SystemParametersInfo (SPIAction.SPI_GETKEYBOARDCUES, 0, ref underlined, 0); + return (underlined != 0); + } + } + + internal override Size MinimizedWindowSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXMINIMIZED), Win32GetSystemMetrics(SystemMetrics.SM_CYMINIMIZED)); + } + } + + internal override Size MinimizedWindowSpacingSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXMINSPACING), Win32GetSystemMetrics(SystemMetrics.SM_CYMINSPACING)); + } + } + + internal override Size MinimumWindowSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXMIN), Win32GetSystemMetrics(SystemMetrics.SM_CYMIN)); + } + } + + internal override Size MinWindowTrackSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXMINTRACK), Win32GetSystemMetrics(SystemMetrics.SM_CYMINTRACK)); + } + } + + internal override Size SmallIconSize { + get { + return new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXSMICON), Win32GetSystemMetrics(SystemMetrics.SM_CYSMICON)); + } + } + + internal override int MouseButtonCount { + get { + return Win32GetSystemMetrics(SystemMetrics.SM_CMOUSEBUTTONS); + } + } + + internal override bool MouseButtonsSwapped { + get { + return Win32GetSystemMetrics(SystemMetrics.SM_SWAPBUTTON) != 0; + } + } + + internal override bool MouseWheelPresent { + get { + return Win32GetSystemMetrics(SystemMetrics.SM_MOUSEWHEELPRESENT) != 0; + } + } + + internal override Rectangle VirtualScreen { + get { + return new Rectangle( Win32GetSystemMetrics(SystemMetrics.SM_XVIRTUALSCREEN), Win32GetSystemMetrics(SystemMetrics.SM_YVIRTUALSCREEN), + Win32GetSystemMetrics(SystemMetrics.SM_CXVIRTUALSCREEN), Win32GetSystemMetrics(SystemMetrics.SM_CYVIRTUALSCREEN)); + } + } + + internal override Rectangle WorkingArea { + get { + RECT rect; + + rect = new RECT(); + Win32SystemParametersInfo(SPIAction.SPI_GETWORKAREA, 0, ref rect, 0); + return new Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); + //return new Rectangle(0, 0, Win32GetSystemMetrics(SystemMetrics.SM.SM_CXSCREEN), Win32GetSystemMetrics(SystemMetrics.SM_CYSCREEN)); + } + } + + [MonoTODO] + internal override Screen[] AllScreens { + get { + // To support multiples, we need to use GetMonitorInfo API on Win32 + return null; + } + } + + internal override bool ThemesEnabled { + get { + return XplatUIWin32.themes_enabled; + } + } + + internal override bool RequiresPositiveClientAreaSize { + get { + return false; + } + } + + public override int ToolWindowCaptionHeight { + get { + return Win32GetSystemMetrics (SystemMetrics.SM_CYSMCAPTION); + } + } + + public override Size ToolWindowCaptionButtonSize { + get { + return new Size ( + Win32GetSystemMetrics (SystemMetrics.SM_CXSMSIZE), + Win32GetSystemMetrics (SystemMetrics.SM_CYSMSIZE)); + } + } + #endregion // Static Properties + + #region Singleton Specific Code + public static XplatUIWin32 GetInstance() { + if (instance==null) { + instance=new XplatUIWin32(); + } + ref_count++; + return instance; + } + + public int Reference { + get { + return ref_count; + } + } + #endregion + + #region Public Static Methods + internal override IntPtr InitializeDriver() { + return IntPtr.Zero; + } + + internal override void ShutdownDriver(IntPtr token) { + Console.WriteLine("XplatUIWin32 ShutdownDriver called"); + } + + + internal void Version() { + Console.WriteLine("Xplat version $revision: $"); + } + + string GetSoundAlias (AlertType alert) + { + switch (alert) { + case AlertType.Error: + return "SystemHand"; + case AlertType.Question: + return "SystemQuestion"; + case AlertType.Warning: + return "SystemExclamation"; + case AlertType.Information: + return "SystemAsterisk"; + default: + return "SystemDefault"; + } + } + + internal override void AudibleAlert(AlertType alert) { + Win32PlaySound(GetSoundAlias (alert), IntPtr.Zero, SndFlags.SND_ALIAS_ID | SndFlags.SND_ASYNC | SndFlags.SND_NOSTOP | SndFlags.SND_NOWAIT); + } + + internal override void BeginMoveResize (IntPtr handle) { + } + + internal override void GetDisplaySize(out Size size) { + RECT rect; + + Win32GetWindowRect(Win32GetDesktopWindow(), out rect); + + size = new Size(rect.right - rect.left, rect.bottom - rect.top); + } + + internal override void EnableThemes() { + themes_enabled=true; + } + + internal override IntPtr CreateWindow(CreateParams cp) { + IntPtr WindowHandle; + IntPtr ParentHandle; + Hwnd hwnd; + + hwnd = new Hwnd(); + + ParentHandle=cp.Parent; + + if ((ParentHandle==IntPtr.Zero) && (cp.Style & (int)(WindowStyles.WS_CHILD))!=0) { + // We need to use our foster parent window until this poor child gets it's parent assigned + ParentHandle = GetFosterParent(); + } + + if ( ((cp.Style & (int)(WindowStyles.WS_CHILD | WindowStyles.WS_POPUP))==0) && ((cp.ExStyle & (int)WindowExStyles.WS_EX_APPWINDOW) == 0)) { + // If we want to be hidden from the taskbar we need to be 'owned' by + // something not on the taskbar. FosterParent is just that + ParentHandle = GetFosterParent(); + } + + Point location; + if (cp.HasWindowManager) { + location = Hwnd.GetNextStackedFormLocation (cp, Hwnd.ObjectFromHandle (cp.Parent)); + } else { + location = new Point (cp.X, cp.Y); + } + + string class_name = RegisterWindowClass (cp.ClassStyle); + HwndCreating = hwnd; + + // We cannot actually send the WS_EX_MDICHILD flag to Windows because we + // are faking MDI, not uses Windows' version. + if ((cp.WindowExStyle & WindowExStyles.WS_EX_MDICHILD) == WindowExStyles.WS_EX_MDICHILD) + cp.WindowExStyle ^= WindowExStyles.WS_EX_MDICHILD; + + WindowHandle = Win32CreateWindow (cp.WindowExStyle, class_name, cp.Caption, cp.WindowStyle, location.X, location.Y, cp.Width, cp.Height, ParentHandle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + + HwndCreating = null; + + if (WindowHandle==IntPtr.Zero) { + int error = Marshal.GetLastWin32Error (); + + Win32MessageBox(IntPtr.Zero, "Error : " + error.ToString(), "Failed to create window, class '"+cp.ClassName+"'", 0); + } + + hwnd.ClientWindow = WindowHandle; + hwnd.Mapped = true; + Win32SetWindowLong(WindowHandle, WindowLong.GWL_USERDATA, (uint)ThemeEngine.Current.DefaultControlBackColor.ToArgb()); + + return WindowHandle; + } + + internal override IntPtr CreateWindow(IntPtr Parent, int X, int Y, int Width, int Height) { + CreateParams create_params = new CreateParams(); + + create_params.Caption = ""; + create_params.X = X; + create_params.Y = Y; + create_params.Width = Width; + create_params.Height = Height; + + create_params.ClassName=XplatUI.GetDefaultClassName (GetType ()); + create_params.ClassStyle = 0; + create_params.ExStyle=0; + create_params.Parent=IntPtr.Zero; + create_params.Param=0; + + return CreateWindow(create_params); + } + + internal override void DestroyWindow(IntPtr handle) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + Win32DestroyWindow(handle); + hwnd.Dispose(); + return; + } + + internal override void SetWindowMinMax(IntPtr handle, Rectangle maximized, Size min, Size max) { + // We do nothing, Form has to handle WM_GETMINMAXINFO + } + + + internal override FormWindowState GetWindowState(IntPtr handle) { + uint style; + + style = Win32GetWindowLong(handle, WindowLong.GWL_STYLE); + if ((style & (uint)WindowStyles.WS_MAXIMIZE) != 0) { + return FormWindowState.Maximized; + } else if ((style & (uint)WindowStyles.WS_MINIMIZE) != 0) { + return FormWindowState.Minimized; + } + return FormWindowState.Normal; + } + + internal override void SetWindowState(IntPtr hwnd, FormWindowState state) { + switch(state) { + case FormWindowState.Normal: { + Win32ShowWindow(hwnd, WindowPlacementFlags.SW_RESTORE); + return; + } + + case FormWindowState.Minimized: { + Win32ShowWindow(hwnd, WindowPlacementFlags.SW_MINIMIZE); + return; + } + + case FormWindowState.Maximized: { + Win32ShowWindow(hwnd, WindowPlacementFlags.SW_MAXIMIZE); + return; + } + } + } + + internal override void SetWindowStyle(IntPtr handle, CreateParams cp) { + + Win32SetWindowLong(handle, WindowLong.GWL_STYLE, (uint)cp.Style); + Win32SetWindowLong(handle, WindowLong.GWL_EXSTYLE, (uint)cp.ExStyle); + + // From MSDN: + // Certain window data is cached, so changes you make using SetWindowLong + // will not take effect until you call the SetWindowPos function. Specifically, + // if you change any of the frame styles, you must call SetWindowPos with + // the SWP_FRAMECHANGED flag for the cache to be updated properly. + if (cp.control is Form) + XplatUI.RequestNCRecalc (handle); + } + + internal override double GetWindowTransparency(IntPtr handle) + { + LayeredWindowAttributes lwa; + COLORREF clrRef; + byte alpha; + + if (0 == Win32GetLayeredWindowAttributes (handle, out clrRef, out alpha, out lwa)) + return 1.0; + + return ((double)alpha) / 255.0; + } + + internal override void SetWindowTransparency(IntPtr handle, double transparency, Color key) { + LayeredWindowAttributes lwa = LayeredWindowAttributes.LWA_ALPHA; + byte opacity = (byte)(transparency*255); + COLORREF clrRef = new COLORREF(); + if (key != Color.Empty) { + clrRef.R = key.R; + clrRef.G = key.G; + clrRef.B = key.B; + lwa = (LayeredWindowAttributes)( (int)lwa | (int)LayeredWindowAttributes.LWA_COLORKEY ); + } + RECT rc; + rc.right = 1000; + rc.bottom = 1000; + Win32SetLayeredWindowAttributes(handle, clrRef, opacity, lwa); + } + + TransparencySupport support; + bool queried_transparency_support; + internal override TransparencySupport SupportsTransparency() { + if (queried_transparency_support) + return support; + + bool flag; + support = TransparencySupport.None; + + flag = true; + try { + Win32SetLayeredWindowAttributes (IntPtr.Zero, new COLORREF (), 255, LayeredWindowAttributes.LWA_ALPHA); + } + catch (EntryPointNotFoundException) { flag = false; } + catch { /* swallow everything else */ } + + if (flag) support |= TransparencySupport.Set; + + flag = true; + try { + LayeredWindowAttributes lwa; + COLORREF clrRef; + byte alpha; + + Win32GetLayeredWindowAttributes (IntPtr.Zero, out clrRef, out alpha, out lwa); + } + catch (EntryPointNotFoundException) { flag = false; } + catch { /* swallow everything else */ } + + if (flag) support |= TransparencySupport.Get; + + queried_transparency_support = true; + return support; + } + + internal override void UpdateWindow(IntPtr handle) { + Win32UpdateWindow(handle); + } + + internal override PaintEventArgs PaintEventStart(ref Message msg, IntPtr handle, bool client) { + IntPtr hdc; + PAINTSTRUCT ps; + PaintEventArgs paint_event; + RECT rect; + Rectangle clip_rect; + Hwnd hwnd; + + clip_rect = new Rectangle(); + rect = new RECT(); + ps = new PAINTSTRUCT(); + + hwnd = Hwnd.ObjectFromHandle(msg.HWnd); + + if (client) { + if (Win32GetUpdateRect(msg.HWnd, ref rect, false)) { + if (handle != msg.HWnd) { + // We need to validate the window where the paint message + // was generated, otherwise we'll never stop getting paint + // messages. + Win32GetClientRect (msg.HWnd, out rect); + Win32ValidateRect (msg.HWnd, ref rect); + hdc = Win32GetDC (handle); + } else { + hdc = Win32BeginPaint (handle, ref ps); + rect = ps.rcPaint; + } + } else { + hdc = Win32GetDC(handle); + } + clip_rect = rect.ToRectangle (); + } else { + hdc = Win32GetWindowDC (handle); + + // HACK this in for now + Win32GetWindowRect (handle, out rect); + clip_rect = new Rectangle (0, 0, rect.Width, rect.Height); + } + + // If we called BeginPaint, store the PAINTSTRUCT, + // otherwise store hdc, so that PaintEventEnd can know + // whether to call EndPaint or ReleaseDC. + if (ps.hdc != IntPtr.Zero) { + hwnd.drawing_stack.Push (ps); + } else { + hwnd.drawing_stack.Push (hdc); + } + + Graphics dc = Graphics.FromHdc(hdc); + hwnd.drawing_stack.Push (dc); + + paint_event = new PaintEventArgs(dc, clip_rect); + + return paint_event; + } + + internal override void PaintEventEnd(ref Message m, IntPtr handle, bool client) { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(m.HWnd); + + Graphics dc = (Graphics)hwnd.drawing_stack.Pop(); + dc.Dispose (); + + object o = hwnd.drawing_stack.Pop(); + if (o is IntPtr) { + IntPtr hdc = (IntPtr) o; + Win32ReleaseDC (handle, hdc); + } else if (o is PAINTSTRUCT) { + PAINTSTRUCT ps = (PAINTSTRUCT) o; + Win32EndPaint (handle, ref ps); + } + } + + + internal override void SetWindowPos(IntPtr handle, int x, int y, int width, int height) { + Win32MoveWindow(handle, x, y, width, height, true); + return; + } + + internal override void GetWindowPos(IntPtr handle, bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) { + IntPtr parent; + RECT rect; + POINT pt; + + Win32GetWindowRect(handle, out rect); + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + pt.x=rect.left; + pt.y=rect.top; + + parent = Win32GetAncestor (handle, AncestorType.GA_PARENT); + if (parent != IntPtr.Zero && parent != Win32GetDesktopWindow ()) + Win32ScreenToClient(parent, ref pt); + + x = pt.x; + y = pt.y; + + Win32GetClientRect(handle, out rect); + client_width = rect.right - rect.left; + client_height = rect.bottom - rect.top; + return; + } + + internal override void Activate(IntPtr handle) { + Win32SetActiveWindow(handle); + // delayed timer enabled + lock (timer_list) { + foreach (Timer t in timer_list.Values) { + if (t.Enabled && t.window == IntPtr.Zero) { + t.window = handle; + int id = t.GetHashCode (); + Win32SetTimer(handle, id, (uint)t.Interval, IntPtr.Zero); + } + } + } + } + + internal override void Invalidate(IntPtr handle, Rectangle rc, bool clear) { + RECT rect; + + rect.left=rc.Left; + rect.top=rc.Top; + rect.right=rc.Right; + rect.bottom=rc.Bottom; + Win32InvalidateRect(handle, ref rect, clear); + } + + + internal override void InvalidateNC (IntPtr handle) + { + // found this gem at + // http://www.dotnet247.com/247reference/msgs/58/292037.aspx + Win32SetWindowPos(handle, IntPtr.Zero, + 0, 0, 0, 0, + SetWindowPosFlags.SWP_NOMOVE | + SetWindowPosFlags.SWP_NOSIZE | + SetWindowPosFlags.SWP_NOZORDER | + SetWindowPosFlags.SWP_NOACTIVATE | + SetWindowPosFlags.SWP_DRAWFRAME); + } + + private IntPtr InternalWndProc (IntPtr hWnd, Msg msg, IntPtr wParam, IntPtr lParam) + { + if (HwndCreating != null && HwndCreating.ClientWindow == IntPtr.Zero) + HwndCreating.ClientWindow = hWnd; + return NativeWindow.WndProc (hWnd, msg, wParam, lParam); + } + + internal override IntPtr DefWndProc(ref Message msg) { + msg.Result=Win32DefWindowProc(msg.HWnd, (Msg)msg.Msg, msg.WParam, msg.LParam); + return msg.Result; + } + + internal override void HandleException(Exception e) { + StackTrace st = new StackTrace(e); + Win32MessageBox(IntPtr.Zero, e.Message+st.ToString(), "Exception", 0); + Console.WriteLine("{0}{1}", e.Message, st.ToString()); + } + + internal override void DoEvents() { + MSG msg = new MSG(); + + while (GetMessage(ref msg, IntPtr.Zero, 0, 0, false)) { + Message m = Message.Create (msg.hwnd, (int)msg.message, msg.wParam, msg.lParam); + + if (Application.FilterMessage (ref m)) + continue; + + XplatUI.TranslateMessage(ref msg); + XplatUI.DispatchMessage(ref msg); + } + } + + internal override bool PeekMessage(Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) { + return Win32PeekMessage(ref msg, hWnd, wFilterMin, wFilterMax, flags); + } + + internal override void PostQuitMessage(int exitCode) { + Win32PostQuitMessage(exitCode); + } + + internal override void RequestAdditionalWM_NCMessages(IntPtr hwnd, bool hover, bool leave) + { + if (wm_nc_registered == null) + wm_nc_registered = new Hashtable (); + + TMEFlags flags = TMEFlags.TME_NONCLIENT; + if (hover) + flags |= TMEFlags.TME_HOVER; + if (leave) + flags |= TMEFlags.TME_LEAVE; + + if (flags == TMEFlags.TME_NONCLIENT) { + if (wm_nc_registered.Contains (hwnd)) { + wm_nc_registered.Remove (hwnd); + } + } else { + if (!wm_nc_registered.Contains (hwnd)) { + wm_nc_registered.Add (hwnd, flags); + } else { + wm_nc_registered [hwnd] = flags; + } + } + } + + internal override void RequestNCRecalc(IntPtr handle) { + Win32SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOACTIVATE); + } + + internal override void ResetMouseHover(IntPtr handle) { + TRACKMOUSEEVENT tme; + + tme = new TRACKMOUSEEVENT(); + tme.size = Marshal.SizeOf(tme); + tme.hWnd = handle; + tme.dwFlags = TMEFlags.TME_LEAVE | TMEFlags.TME_HOVER; + Win32TrackMouseEvent(ref tme); + } + + + internal override bool GetMessage(Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax) { + return GetMessage(ref msg, hWnd, wFilterMin, wFilterMax, true); + } + + private bool GetMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, bool blocking) { + bool result; + + msg.refobject = 0; + if (RetrieveMessage(ref msg)) { + return true; + } + + if (blocking) { + result = Win32GetMessage(ref msg, hWnd, wFilterMin, wFilterMax); + } else { + result = Win32PeekMessage(ref msg, hWnd, wFilterMin, wFilterMax, (uint)PeekMessageFlags.PM_REMOVE); + if (!result) { + return false; + } + } + + // We need to fake WM_MOUSE_ENTER + switch (msg.message) { + case Msg.WM_LBUTTONDOWN: { + mouse_state |= MouseButtons.Left; + break; + } + + case Msg.WM_MBUTTONDOWN: { + mouse_state |= MouseButtons.Middle; + break; + } + + case Msg.WM_RBUTTONDOWN: { + mouse_state |= MouseButtons.Right; + break; + } + + case Msg.WM_LBUTTONUP: { + mouse_state &= ~MouseButtons.Left; + break; + } + + case Msg.WM_MBUTTONUP: { + mouse_state &= ~MouseButtons.Middle; + break; + } + + case Msg.WM_RBUTTONUP: { + mouse_state &= ~MouseButtons.Right; + break; + } + + case Msg.WM_ASYNC_MESSAGE: { + XplatUIDriverSupport.ExecuteClientMessage((GCHandle)msg.lParam); + break; + } + + case Msg.WM_MOUSEMOVE: { + if (msg.hwnd != prev_mouse_hwnd) { + TRACKMOUSEEVENT tme; + + mouse_state = Widget.FromParamToMouseButtons ((int)msg.lParam.ToInt32()); + + // The current message will be sent out next time around + StoreMessage(ref msg); + + // This is the message we want to send at this point + msg.message = Msg.WM_MOUSE_ENTER; + + prev_mouse_hwnd = msg.hwnd; + + tme = new TRACKMOUSEEVENT(); + tme.size = Marshal.SizeOf(tme); + tme.hWnd = msg.hwnd; + tme.dwFlags = TMEFlags.TME_LEAVE | TMEFlags.TME_HOVER; + Win32TrackMouseEvent(ref tme); + return result; + } + break; + } + + case Msg.WM_NCMOUSEMOVE: { + if (wm_nc_registered == null || !wm_nc_registered.Contains (msg.hwnd)) + break; + + mouse_state = Widget.FromParamToMouseButtons ((int)msg.lParam.ToInt32 ()); + + TRACKMOUSEEVENT tme; + + tme = new TRACKMOUSEEVENT (); + tme.size = Marshal.SizeOf(tme); + tme.hWnd = msg.hwnd; + tme.dwFlags = (TMEFlags)wm_nc_registered[msg.hwnd]; + Win32TrackMouseEvent (ref tme); + return result; + } + + case Msg.WM_DROPFILES: { + return Win32DnD.HandleWMDropFiles(ref msg); + } + + case Msg.WM_MOUSELEAVE: { + prev_mouse_hwnd = IntPtr.Zero; + break; + } + + case Msg.WM_TIMER: { + Timer timer=(Timer)timer_list[(int)msg.wParam]; + + if (timer != null) { + timer.FireTick(); + } + break; + } + } + + return result; + } + + internal override bool TranslateMessage(ref MSG msg) { + return Win32TranslateMessage(ref msg); + } + + internal override IntPtr DispatchMessage(ref MSG msg) { + return Win32DispatchMessage(ref msg); + } + + internal override bool SetZOrder(IntPtr hWnd, IntPtr AfterhWnd, bool Top, bool Bottom) { + if (Top) { + Win32SetWindowPos(hWnd, SetWindowPosZOrder.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + return true; + } else if (!Bottom) { + Win32SetWindowPos(hWnd, AfterhWnd, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + } else { + Win32SetWindowPos(hWnd, (IntPtr)SetWindowPosZOrder.HWND_BOTTOM, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + return true; + } + return false; + } + + internal override bool SetTopmost(IntPtr hWnd, bool Enabled) { + if (Enabled) { + Win32SetWindowPos(hWnd, SetWindowPosZOrder.HWND_TOPMOST, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE); + return true; + } else { + Win32SetWindowPos(hWnd, SetWindowPosZOrder.HWND_NOTOPMOST, 0, 0, 0, 0, SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOACTIVATE); + return true; + } + } + + internal override bool SetOwner(IntPtr hWnd, IntPtr hWndOwner) { + Win32SetWindowLong(hWnd, WindowLong.GWL_HWNDPARENT, (uint) hWndOwner); + return true; + } + + internal override bool Text(IntPtr handle, string text) { + Win32SetWindowText(handle, text); + return true; + } + + internal override bool GetText(IntPtr handle, out string text) { + StringBuilder sb; + + sb = new StringBuilder(256); + Win32GetWindowText(handle, sb, sb.Capacity); + text = sb.ToString(); + return true; + } + + internal override bool SetVisible (IntPtr handle, bool visible, bool activate) + { + if (visible) { + Widget c = Widget.FromHandle (handle); + if (c is Form) { + Form f; + + f = (Form)Widget.FromHandle (handle); + WindowPlacementFlags flags = WindowPlacementFlags.SW_SHOWNORMAL; + switch (f.WindowState) { + case FormWindowState.Normal: flags = WindowPlacementFlags.SW_SHOWNORMAL; break; + case FormWindowState.Minimized: flags = WindowPlacementFlags.SW_MINIMIZE; break; + case FormWindowState.Maximized: flags = WindowPlacementFlags.SW_MAXIMIZE; break; + } + + if (!f.ActivateOnShow) + flags = WindowPlacementFlags.SW_SHOWNOACTIVATE; + + Win32ShowWindow (handle, flags); + } + else { + if (c.ActivateOnShow) + Win32ShowWindow (handle, WindowPlacementFlags.SW_SHOWNORMAL); + else + Win32ShowWindow (handle, WindowPlacementFlags.SW_SHOWNOACTIVATE); + } + } + else { + Win32ShowWindow (handle, WindowPlacementFlags.SW_HIDE); + } + return true; + } + + internal override bool IsEnabled(IntPtr handle) { + return IsWindowEnabled (handle); + } + + internal override bool IsKeyLocked (VirtualKeys key) + { + return (Win32GetKeyState (key) & 1) == 1; + } + + internal override bool IsVisible(IntPtr handle) { + return IsWindowVisible (handle); + } + + internal override IntPtr SetParent(IntPtr handle, IntPtr parent) { + Widget c = Widget.FromHandle (handle); + if (parent == IntPtr.Zero) { + if (!(c is Form)) { + Win32ShowWindow(handle, WindowPlacementFlags.SW_HIDE); + } + } else { + if (!(c is Form)) { + SetVisible (handle, c.is_visible, true); + } + } + // The Win32SetParent is lame, it can very well move the window + // ref: http://groups.google.com/group/microsoft.public.vb.winapi/browse_thread/thread/1b82ccc54231ecee/afa82835bfc0422a%23afa82835bfc0422a + // Here we save the position before changing the parent, and if it has changed afterwards restore it. + // Another possibility would be to intercept WM_WINDOWPOSCHANGING and restore the coords there, but this would require plumbing in weird places + // (either inside Widget or add handling to InternalWndProc) + // We also need to remove WS_CHILD if making the window parent-less, and add it if we're parenting it. + RECT rect, rect2; + IntPtr result; + WindowStyles style, new_style; + + Win32GetWindowRect (handle, out rect); + style = (WindowStyles) Win32GetWindowLong (handle, WindowLong.GWL_STYLE); + + if (parent == IntPtr.Zero) { + new_style = style & ~WindowStyles.WS_CHILD; + result = Win32SetParent (handle, GetFosterParent()); + } else { + new_style = style | WindowStyles.WS_CHILD; + result = Win32SetParent (handle, parent); + } + if (style != new_style && c is Form) { + Win32SetWindowLong (handle, WindowLong.GWL_STYLE, (uint) new_style); + } + Win32GetWindowRect (handle, out rect2); + if (rect.top != rect2.top && rect.left != rect2.left && c is Form) { + Win32SetWindowPos (handle, IntPtr.Zero, rect.top, rect.left, rect.Width, rect.Height, SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOREDRAW | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOENDSCHANGING | SetWindowPosFlags.SWP_NOACTIVATE); + } + return result; + } + + // If we ever start using this, we should probably replace FosterParent with IntPtr.Zero + internal override IntPtr GetParent(IntPtr handle) { + return Win32GetParent(handle); + } + + // This is a nop on win32 and x11 + internal override IntPtr GetPreviousWindow(IntPtr handle) { + return handle; + } + + internal override void GrabWindow(IntPtr hWnd, IntPtr ConfineToHwnd) { + grab_hwnd = hWnd; + Win32SetCapture(hWnd); + + if (ConfineToHwnd != IntPtr.Zero) { + RECT window_rect; + Win32GetWindowRect (ConfineToHwnd, out window_rect); + Win32GetClipCursor (out clipped_cursor_rect); + Win32ClipCursor (ref window_rect); + } + } + + internal override void GrabInfo(out IntPtr hWnd, out bool GrabConfined, out Rectangle GrabArea) { + hWnd = grab_hwnd; + GrabConfined = grab_confined; + GrabArea = grab_area; + } + + internal override void UngrabWindow(IntPtr hWnd) { + if (!(clipped_cursor_rect.top == 0 && clipped_cursor_rect.bottom == 0 && clipped_cursor_rect.left == 0 && clipped_cursor_rect.right == 0)) { + Win32ClipCursor (ref clipped_cursor_rect); + clipped_cursor_rect = new RECT (); + } + + Win32ReleaseCapture(); + grab_hwnd = IntPtr.Zero; + } + + internal override bool CalculateWindowRect(ref Rectangle ClientRect, CreateParams cp, Menu menu, out Rectangle WindowRect) { + RECT rect; + + rect.left=ClientRect.Left; + rect.top=ClientRect.Top; + rect.right=ClientRect.Right; + rect.bottom=ClientRect.Bottom; + + if (!Win32AdjustWindowRectEx(ref rect, cp.Style, menu != null, cp.ExStyle)) { + WindowRect = new Rectangle(ClientRect.Left, ClientRect.Top, ClientRect.Width, ClientRect.Height); + return false; + } + + WindowRect = new Rectangle(rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top); + return true; + } + + internal override void SetCursor(IntPtr window, IntPtr cursor) { + Win32SetCursor(cursor); + return; + } + + internal override void ShowCursor(bool show) { + Win32ShowCursor(show); + } + + internal override void OverrideCursor(IntPtr cursor) { + Win32SetCursor(cursor); + } + + internal override IntPtr DefineCursor(Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) { + IntPtr cursor; + Bitmap cursor_bitmap; + Bitmap cursor_mask; + Byte[] cursor_bits; + Byte[] mask_bits; + Color pixel; + int width; + int height; + + // Win32 only allows creation cursors of a certain size + if ((bitmap.Width != Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR)) || (bitmap.Width != Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR))) { + cursor_bitmap = new Bitmap(bitmap, new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR), Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR))); + cursor_mask = new Bitmap(mask, new Size(Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR), Win32GetSystemMetrics(SystemMetrics.SM_CXCURSOR))); + } else { + cursor_bitmap = bitmap; + cursor_mask = mask; + } + + width = cursor_bitmap.Width; + height = cursor_bitmap.Height; + + cursor_bits = new Byte[(width / 8) * height]; + mask_bits = new Byte[(width / 8) * height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + pixel = cursor_bitmap.GetPixel(x, y); + + if (pixel == cursor_pixel) { + cursor_bits[y * width / 8 + x / 8] |= (byte)(0x80 >> (x % 8)); + } + + pixel = cursor_mask.GetPixel(x, y); + + if (pixel == mask_pixel) { + mask_bits[y * width / 8 + x / 8] |= (byte)(0x80 >> (x % 8)); + } + } + } + + cursor = Win32CreateCursor(IntPtr.Zero, xHotSpot, yHotSpot, width, height, mask_bits, cursor_bits); + + return cursor; + } + + internal override Bitmap DefineStdCursorBitmap (StdCursor id) + { + // We load the cursor, create a bitmap, draw the cursor onto the bitmap and return the bitmap. + IntPtr cursor = DefineStdCursor (id); + // Windows only have one possible cursor size! + int width = Win32GetSystemMetrics (SystemMetrics.SM_CXCURSOR); + int height = Win32GetSystemMetrics (SystemMetrics.SM_CYCURSOR); + Bitmap bmp = new Bitmap (width, height); + Graphics gc = Graphics.FromImage (bmp); + IntPtr hdc = gc.GetHdc (); + Win32DrawIcon (hdc, 0, 0, cursor); + gc.ReleaseHdc (hdc); + gc.Dispose (); + return bmp; + } + + [MonoTODO("Define the missing cursors")] + internal override IntPtr DefineStdCursor(StdCursor id) { + switch(id) { + case StdCursor.AppStarting: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_APPSTARTING); + case StdCursor.Arrow: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); + case StdCursor.Cross: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_CROSS); + case StdCursor.Default: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); + case StdCursor.Hand: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_HAND); + case StdCursor.Help: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_HELP); + case StdCursor.HSplit: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.IBeam: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_IBEAM); + case StdCursor.No: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_NO); + case StdCursor.NoMove2D: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.NoMoveHoriz: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.NoMoveVert: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanEast: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanNE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanNorth: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanNW: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanSE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanSouth: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanSW: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.PanWest: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.SizeAll: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZEALL); + case StdCursor.SizeNESW: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZENESW); + case StdCursor.SizeNS: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZENS); + case StdCursor.SizeNWSE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZENWSE); + case StdCursor.SizeWE: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_SIZEWE); + case StdCursor.UpArrow: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_UPARROW); + case StdCursor.VSplit: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_ARROW); // FIXME + case StdCursor.WaitCursor: return Win32LoadCursor(IntPtr.Zero, LoadCursorType.IDC_WAIT); + } + throw new NotImplementedException (); + } + + internal override void DestroyCursor(IntPtr cursor) { + if ((cursor.ToInt32() < (int)LoadCursorType.First) || (cursor.ToInt32() > (int)LoadCursorType.Last)) { + Win32DestroyCursor(cursor); + } + } + + [MonoTODO] + internal override void GetCursorInfo(IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y) { + ICONINFO ii = new ICONINFO (); + + if (!Win32GetIconInfo (cursor, out ii)) + throw new Win32Exception (); + + width = 20; + height = 20; + hotspot_x = ii.xHotspot; + hotspot_y = ii.yHotspot; + } + + internal override void SetCursorPos(IntPtr handle, int x, int y) { + Win32SetCursorPos(x, y); + } + + internal override Region GetClipRegion(IntPtr hwnd) { + Region region; + + region = new Region(); + + Win32GetWindowRgn(hwnd, region.GetHrgn(Graphics.FromHwnd(hwnd))); + + return region; + } + + internal override void SetClipRegion(IntPtr hwnd, Region region) { + if (region == null) + Win32SetWindowRgn (hwnd, IntPtr.Zero, true); + else + Win32SetWindowRgn(hwnd, region.GetHrgn(Graphics.FromHwnd(hwnd)), true); + } + + internal override void EnableWindow(IntPtr handle, bool Enable) { + Win32EnableWindow(handle, Enable); + } + + internal override void EndLoop(System.Threading.Thread thread) { + // Nothing to do + } + + internal override object StartLoop(System.Threading.Thread thread) { + return null; + } + + internal override void SetModal(IntPtr handle, bool Modal) { + // we do nothing on Win32 + } + + internal override void GetCursorPos(IntPtr handle, out int x, out int y) { + POINT pt; + + Win32GetCursorPos(out pt); + + if (handle!=IntPtr.Zero) { + Win32ScreenToClient(handle, ref pt); + } + + x=pt.x; + y=pt.y; + } + + internal override void ScreenToClient(IntPtr handle, ref int x, ref int y) + { + POINT pnt = new POINT(); + + pnt.x = x; + pnt.y = y; + Win32ScreenToClient (handle, ref pnt); + + x = pnt.x; + y = pnt.y; + } + + internal override void ClientToScreen(IntPtr handle, ref int x, ref int y) { + POINT pnt = new POINT(); + + pnt.x = x; + pnt.y = y; + + Win32ClientToScreen(handle, ref pnt); + + x = pnt.x; + y = pnt.y; + } + + internal override void ScreenToMenu(IntPtr handle, ref int x, ref int y) { + RECT rect; + + Win32GetWindowRect(handle, out rect); + x -= rect.left + SystemInformation.FrameBorderSize.Width; + y -= rect.top + SystemInformation.FrameBorderSize.Height; + + WindowStyles style = (WindowStyles) Win32GetWindowLong (handle, WindowLong.GWL_STYLE); + if (CreateParams.IsSet (style, WindowStyles.WS_CAPTION)) { + y -= ThemeEngine.Current.CaptionHeight; + } + } + + internal override void MenuToScreen(IntPtr handle, ref int x, ref int y) { + RECT rect; + + Win32GetWindowRect(handle, out rect); + x += rect.left + SystemInformation.FrameBorderSize.Width; + y += rect.top + SystemInformation.FrameBorderSize.Height + ThemeEngine.Current.CaptionHeight; + return; + } + + internal override void SendAsyncMethod (AsyncMethodData method) + { + Win32PostMessage(GetFosterParent(), Msg.WM_ASYNC_MESSAGE, IntPtr.Zero, (IntPtr)GCHandle.Alloc (method)); + } + + internal override void SetTimer (Timer timer) + { + IntPtr FosterParent=GetFosterParent(); + int index; + + index = timer.GetHashCode(); + + lock (timer_list) { + timer_list[index]=timer; + } + + if (Win32SetTimer(FosterParent, index, (uint)timer.Interval, IntPtr.Zero) != IntPtr.Zero) + timer.window = FosterParent; + else + timer.window = IntPtr.Zero; + } + + internal override void KillTimer (Timer timer) + { + int index; + + index = timer.GetHashCode(); + + Win32KillTimer(timer.window, index); + + lock (timer_list) { + timer_list.Remove(index); + } + } + + internal override void CreateCaret(IntPtr hwnd, int width, int height) { + Win32CreateCaret(hwnd, IntPtr.Zero, width, height); + caret_visible = false; + } + + internal override void DestroyCaret(IntPtr hwnd) { + Win32DestroyCaret(); + } + + internal override void SetCaretPos(IntPtr hwnd, int x, int y) { + Win32SetCaretPos(x, y); + } + + internal override void CaretVisible(IntPtr hwnd, bool visible) { + if (visible) { + if (!caret_visible) { + Win32ShowCaret(hwnd); + caret_visible = true; + } + } else { + if (caret_visible) { + Win32HideCaret(hwnd); + caret_visible = false; + } + } + } + + internal override IntPtr GetFocus() { + return Win32GetFocus(); + } + + internal override void SetFocus(IntPtr hwnd) { + Win32SetFocus(hwnd); + } + + internal override IntPtr GetActive() { + return Win32GetActiveWindow(); + } + + internal override bool GetFontMetrics(Graphics g, Font font, out int ascent, out int descent) { + IntPtr dc; + IntPtr prevobj; + TEXTMETRIC tm; + + tm = new TEXTMETRIC(); + + dc = Win32GetDC (IntPtr.Zero); + prevobj = Win32SelectObject (dc, font.ToHfont ()); + + if (Win32GetTextMetrics (dc, ref tm) == false) { + prevobj = Win32SelectObject (dc, prevobj); + Win32DeleteObject (prevobj); + Win32ReleaseDC (IntPtr.Zero, dc); + ascent = 0; + descent = 0; + return false; + } + prevobj = Win32SelectObject (dc, prevobj); + Win32DeleteObject (prevobj); + Win32ReleaseDC (IntPtr.Zero, dc); + + ascent = tm.tmAscent; + descent = tm.tmDescent; + + return true; + } + + internal override void ScrollWindow(IntPtr hwnd, Rectangle rectangle, int XAmount, int YAmount, bool with_children) { + RECT rect; + + rect = new RECT(); + rect.left = rectangle.X; + rect.top = rectangle.Y; + rect.right = rectangle.Right; + rect.bottom = rectangle.Bottom; + + Win32ScrollWindowEx(hwnd, XAmount, YAmount, IntPtr.Zero, ref rect, IntPtr.Zero, IntPtr.Zero, ScrollWindowExFlags.SW_INVALIDATE | ScrollWindowExFlags.SW_ERASE | (with_children ? ScrollWindowExFlags.SW_SCROLLCHILDREN : ScrollWindowExFlags.SW_NONE)); + Win32UpdateWindow(hwnd); + } + + internal override void ScrollWindow(IntPtr hwnd, int XAmount, int YAmount, bool with_children) { + Win32ScrollWindowEx(hwnd, XAmount, YAmount, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, ScrollWindowExFlags.SW_INVALIDATE | ScrollWindowExFlags.SW_ERASE | (with_children ? ScrollWindowExFlags.SW_SCROLLCHILDREN : ScrollWindowExFlags.SW_NONE)); + } + + internal override bool SystrayAdd(IntPtr hwnd, string tip, Icon icon, out ToolTip tt) { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hWnd = hwnd; + nid.uID = 1; + nid.uCallbackMessage = (uint)Msg.WM_USER; + nid.uFlags = NotifyIconFlags.NIF_MESSAGE; + + if (tip != null) { + nid.szTip = tip; + nid.uFlags |= NotifyIconFlags.NIF_TIP; + } + + if (icon != null) { + nid.hIcon = icon.Handle; + nid.uFlags |= NotifyIconFlags.NIF_ICON; + } + + tt = null; + + return Win32Shell_NotifyIcon(NotifyIconMessage.NIM_ADD, ref nid); + } + + internal override bool SystrayChange(IntPtr hwnd, string tip, Icon icon, ref ToolTip tt) { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hIcon = icon.Handle; + nid.hWnd = hwnd; + nid.uID = 1; + nid.uCallbackMessage = (uint)Msg.WM_USER; + nid.uFlags = NotifyIconFlags.NIF_MESSAGE; + + if (tip != null) { + nid.szTip = tip; + nid.uFlags |= NotifyIconFlags.NIF_TIP; + } + + if (icon != null) { + nid.hIcon = icon.Handle; + nid.uFlags |= NotifyIconFlags.NIF_ICON; + } + + return Win32Shell_NotifyIcon(NotifyIconMessage.NIM_MODIFY, ref nid); + } + + internal override void SystrayRemove(IntPtr hwnd, ref ToolTip tt) { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hWnd = hwnd; + nid.uID = 1; + nid.uFlags = 0; + + Win32Shell_NotifyIcon(NotifyIconMessage.NIM_DELETE, ref nid); + } + + internal override void SystrayBalloon(IntPtr hwnd, int timeout, string title, string text, ToolTipIcon icon) + { + NOTIFYICONDATA nid; + + nid = new NOTIFYICONDATA(); + + nid.cbSize = (uint)Marshal.SizeOf(nid); + nid.hWnd = hwnd; + nid.uID = 1; + nid.uFlags = NotifyIconFlags.NIF_INFO; + nid.uTimeoutOrVersion = timeout; + nid.szInfoTitle = title; + nid.szInfo = text; + nid.dwInfoFlags = icon; + + Win32Shell_NotifyIcon(NotifyIconMessage.NIM_MODIFY, ref nid); + } + + internal override void SetBorderStyle(IntPtr handle, FormBorderStyle border_style) { + // Nothing to do on Win32 + } + + internal override void SetMenu(IntPtr handle, Menu menu) { + // Trigger WM_NCCALC + Win32SetWindowPos(handle, IntPtr.Zero, 0, 0, 0, 0, SetWindowPosFlags.SWP_FRAMECHANGED | SetWindowPosFlags.SWP_NOMOVE | SetWindowPosFlags.SWP_NOSIZE); + } + + internal override Point GetMenuOrigin(IntPtr handle) { + Form form = Widget.FromHandle (handle) as Form; + + if (form != null) { + if (form.FormBorderStyle == FormBorderStyle.None) + return Point.Empty; + + int bordersize = (form.Width - form.ClientSize.Width) / 2; + + if (form.FormBorderStyle == FormBorderStyle.FixedToolWindow || form.FormBorderStyle == FormBorderStyle.SizableToolWindow) + return new Point (bordersize, bordersize + SystemInformation.ToolWindowCaptionHeight); + else + return new Point (bordersize, bordersize + SystemInformation.CaptionHeight); + } + + return new Point(SystemInformation.FrameBorderSize.Width, SystemInformation.FrameBorderSize.Height + ThemeEngine.Current.CaptionHeight); + } + + internal override void SetIcon(IntPtr hwnd, Icon icon) { + Win32SendMessage(hwnd, Msg.WM_SETICON, (IntPtr)1, icon == null ? IntPtr.Zero : icon.Handle); // 1 = large icon (0 would be small) + } + + internal override void ClipboardClose(IntPtr handle) { + if (handle != clip_magic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + Win32CloseClipboard(); + } + + internal override int ClipboardGetID(IntPtr handle, string format) { + if (handle != clip_magic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + if (format == "Text" ) return 1; + else if (format == "Bitmap" ) return 2; + else if (format == "MetaFilePict" ) return 3; + else if (format == "SymbolicLink" ) return 4; + else if (format == "DataInterchangeFormat" ) return 5; + else if (format == "Tiff" ) return 6; + else if (format == "OEMText" ) return 7; + else if (format == "DeviceIndependentBitmap" ) return 8; + else if (format == "Palette" ) return 9; + else if (format == "PenData" ) return 10; + else if (format == "RiffAudio" ) return 11; + else if (format == "WaveAudio" ) return 12; + else if (format == "UnicodeText" ) return 13; + else if (format == "EnhancedMetafile" ) return 14; + else if (format == "FileDrop" ) return 15; + else if (format == "Locale" ) return 16; + + return (int)Win32RegisterClipboardFormat(format); + } + + internal override IntPtr ClipboardOpen(bool primary_selection) { + // Win32 does not have primary selection + Win32OpenClipboard(GetFosterParent()); + return clip_magic; + } + + internal override int[] ClipboardAvailableFormats(IntPtr handle) { + uint format; + int[] result; + int count; + + if (handle != clip_magic) { + return null; + } + + // Count first + count = 0; + format = 0; + do { + format = Win32EnumClipboardFormats(format); + if (format != 0) { + count++; + } + } while (format != 0); + + // Now assign + result = new int[count]; + count = 0; + format = 0; + do { + format = Win32EnumClipboardFormats(format); + if (format != 0) { + result[count++] = (int)format; + } + } while (format != 0); + + return result; + } + + + internal override object ClipboardRetrieve(IntPtr handle, int type, XplatUI.ClipboardToObject converter) { + IntPtr hmem; + IntPtr data; + object obj; + + if (handle != clip_magic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + + hmem = Win32GetClipboardData((uint)type); + if (hmem == IntPtr.Zero) { + return null; + } + + data = Win32GlobalLock(hmem); + if (data == IntPtr.Zero) { + uint error = Win32GetLastError(); + Console.WriteLine("Error: {0}", error); + return null; + } + + obj = null; + + if (type == DataFormats.GetFormat(DataFormats.Rtf).Id) { + obj = AnsiToString(data); + } else switch ((ClipboardFormats)type) { + case ClipboardFormats.CF_TEXT: { + obj = AnsiToString(data); + break; + } + + case ClipboardFormats.CF_DIB: { + obj = DIBtoImage(data); + break; + } + + case ClipboardFormats.CF_UNICODETEXT: { + obj = UnicodeToString(data); + break; + } + + default: { + if (converter != null && !converter(type, data, out obj)) { + obj = null; + } + break; + } + } + Win32GlobalUnlock(hmem); + + return obj; + + } + + internal override void ClipboardStore(IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter, bool copy) + { + byte[] data = null; + + if (handle != clip_magic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + + if (obj == null) { + // Just clear it + if (!Win32EmptyClipboard()) + throw new ExternalException("Win32EmptyClipboard"); + return; + } + + if (type == -1) { + if (obj is string) { + type = (int)ClipboardFormats.CF_UNICODETEXT; + } else if (obj is Image) { + type = (int)ClipboardFormats.CF_DIB; + } + } + + if (type == DataFormats.GetFormat(DataFormats.Rtf).Id) { + data = StringToAnsi ((string)obj); + } else switch((ClipboardFormats)type) { + case ClipboardFormats.CF_UNICODETEXT: { + data = StringToUnicode ((string)obj); + break; + } + + case ClipboardFormats.CF_TEXT: { + data = StringToAnsi ((string)obj); + break; + } + + case ClipboardFormats.CF_BITMAP: + case ClipboardFormats.CF_DIB: { + data = ImageToDIB ((Image)obj); + type = (int)ClipboardFormats.CF_DIB; + break; + } + + default: { + if (converter != null && !converter(ref type, obj, out data)) { + data = null; // ensure that a failed conversion leaves null. + } + break; + } + } + if (data != null) { + SetClipboardData ((uint)type, data); + } + } + + internal static byte[] StringToUnicode (string text) + { + return Encoding.Unicode.GetBytes (text + "\0"); + } + + internal static byte[] StringToAnsi (string text) + { + // FIXME, follow the behaviour of the previous code using UTF-8, + // but this should be 'ANSI' on Windows, i.e. the current code page. + // Does Encoding.Default work on Windows? + return Encoding.UTF8.GetBytes (text + "\0"); + } + + private void SetClipboardData (uint type, byte[] data) + { + if (data.Length == 0) + // Shouldn't call Win32SetClipboard with NULL, as, from MSDN: + // "This parameter can be NULL, indicating that the window provides data + // in the specified clipboard format (renders the format) upon request." + // and I don't think we support that... + // Note this is unrelated to the fact that passing a null obj to + // ClipboardStore is actually a request to empty the clipboard! + return; + IntPtr hmem = CopyToMoveableMemory (data); + if (hmem == IntPtr.Zero) + // As above, should not call with null. + // (Not that CopyToMoveableMemory should ever return null!) + throw new ExternalException ("CopyToMoveableMemory failed."); + if (Win32SetClipboardData (type, hmem) == IntPtr.Zero) + throw new ExternalException ("Win32SetClipboardData"); + } + + /// <summary> + /// Creates a memory block with GlobalAlloc(GMEM_MOVEABLE), copies the data + /// into it, and returns the handle to the memory. + /// </summary> + /// - + /// <param name="data">The data. Must not be null or zero-length — + /// see the exception notes.</param> + /// - + /// <returns>The *handle* to the allocated GMEM_MOVEABLE block.</returns> + /// - + /// <exception cref="T:System.ArgumentException">The data was null or zero + /// length. This is disallowed since a zero length allocation can't be made + /// </exception> + /// <exception cref="T:System.ComponentModel.Win32Exception">The allocation, + /// or locking (handle->pointer) failed. + /// Either out of memory or the handle table is full (256 max currently). + /// Note Win32Exception is a subclass of ExternalException so this is OK in + /// the documented Clipboard interface. + /// </exception> + internal static IntPtr CopyToMoveableMemory (byte[] data) + { + if (data == null || data.Length == 0) + // detect this before GlobalAlloc does. + throw new ArgumentException ("Can't create a zero length memory block."); + + IntPtr hmem = Win32GlobalAlloc (GAllocFlags.GMEM_MOVEABLE | GAllocFlags.GMEM_DDESHARE, data.Length); + if (hmem == IntPtr.Zero) + throw new Win32Exception (); + IntPtr hmem_ptr = Win32GlobalLock (hmem); + if (hmem_ptr == IntPtr.Zero) // If the allocation was valid this shouldn't occur. + throw new Win32Exception (); + Marshal.Copy (data, 0, hmem_ptr, data.Length); + Win32GlobalUnlock (hmem); + return hmem; + } + + + internal override void SetAllowDrop(IntPtr hwnd, bool allowed) { + if (allowed) { + Win32DnD.RegisterDropTarget(hwnd); + } else { + Win32DnD.UnregisterDropTarget(hwnd); + } + } + + internal override DragDropEffects StartDrag(IntPtr hwnd, object data, DragDropEffects allowedEffects) { + return Win32DnD.StartDrag(hwnd, data, allowedEffects); + } + + // XXX this doesn't work at all for FrameStyle.Dashed - it draws like Thick, and in the Thick case + // the corners are drawn incorrectly. + internal override void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style) { + IntPtr hdc; + IntPtr pen; + IntPtr oldpen; + COLORREF clrRef = new COLORREF(); + + // If we want the standard hatch pattern we would + // need to create a brush + + clrRef.R = backColor.R; + clrRef.G = backColor.G; + clrRef.B = backColor.B; + + // Grab a pen + pen = Win32CreatePen (style == FrameStyle.Thick ? PenStyle.PS_SOLID : PenStyle.PS_DASH, + style == FrameStyle.Thick ? 4 : 2, ref clrRef); + + hdc = Win32GetDC(IntPtr.Zero); + Win32SetROP2(hdc, ROP2DrawMode.R2_NOT); + oldpen = Win32SelectObject(hdc, pen); + + Win32MoveToEx(hdc, rectangle.Left, rectangle.Top, IntPtr.Zero); + if ((rectangle.Width > 0) && (rectangle.Height > 0)) { + Win32LineTo(hdc, rectangle.Right, rectangle.Top); + Win32LineTo(hdc, rectangle.Right, rectangle.Bottom); + Win32LineTo(hdc, rectangle.Left, rectangle.Bottom); + Win32LineTo(hdc, rectangle.Left, rectangle.Top); + } else { + if (rectangle.Width > 0) { + Win32LineTo(hdc, rectangle.Right, rectangle.Top); + } else { + Win32LineTo(hdc, rectangle.Left, rectangle.Bottom); + } + } + + Win32SelectObject(hdc, oldpen); + Win32DeleteObject(pen); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override void DrawReversibleLine(Point start, Point end, Color backColor) { + IntPtr hdc; + IntPtr pen; + IntPtr oldpen; + POINT pt; + COLORREF clrRef = new COLORREF(); + + pt = new POINT(); + pt.x = 0; + pt.y = 0; + Win32ClientToScreen(IntPtr.Zero, ref pt); + + // If we want the standard hatch pattern we would + // need to create a brush + + clrRef.R = backColor.R; + clrRef.G = backColor.G; + clrRef.B = backColor.B; + + // Grab a pen + pen = Win32CreatePen(PenStyle.PS_SOLID, 1, ref clrRef); + + hdc = Win32GetDC(IntPtr.Zero); + Win32SetROP2(hdc, ROP2DrawMode.R2_NOT); + oldpen = Win32SelectObject(hdc, pen); + + Win32MoveToEx(hdc, pt.x + start.X, pt.y + start.Y, IntPtr.Zero); + Win32LineTo(hdc, pt.x + end.X, pt.y + end.Y); + + Win32SelectObject(hdc, oldpen); + Win32DeleteObject(pen); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override void FillReversibleRectangle (Rectangle rectangle, Color backColor) + { + RECT rect; + + rect = new RECT(); + rect.left = rectangle.Left; + rect.top = rectangle.Top; + rect.right = rectangle.Right; + rect.bottom = rectangle.Bottom; + + IntPtr hdc; + IntPtr brush; + IntPtr oldbrush; + COLORREF clrRef = new COLORREF(); + + clrRef.R = backColor.R; + clrRef.G = backColor.G; + clrRef.B = backColor.B; + + // Grab a brush + brush = Win32CreateSolidBrush (clrRef); + + hdc = Win32GetDC(IntPtr.Zero); + oldbrush = Win32SelectObject(hdc, brush); + + Win32PatBlt (hdc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height, PatBltRop.DSTINVERT); + + Win32SelectObject(hdc, oldbrush); + Win32DeleteObject(brush); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override void DrawReversibleRectangle(IntPtr handle, Rectangle rect, int line_width) { + IntPtr hdc; + IntPtr pen; + IntPtr oldpen; + POINT pt; + + pt = new POINT(); + pt.x = 0; + pt.y = 0; + Win32ClientToScreen(handle, ref pt); + + // If we want the standard hatch pattern we would + // need to create a brush + + // Grab a pen + pen = Win32CreatePen(PenStyle.PS_SOLID, line_width, IntPtr.Zero); + + hdc = Win32GetDC(IntPtr.Zero); + Win32SetROP2(hdc, ROP2DrawMode.R2_NOT); + oldpen = Win32SelectObject(hdc, pen); + + Widget c = Widget.FromHandle (handle); + if (c != null) { + RECT window_rect; + Win32GetWindowRect (c.Handle, out window_rect); + Region r = new Region (new Rectangle(window_rect.left, window_rect.top, window_rect.right - window_rect.left, window_rect.bottom - window_rect.top)); + Win32ExtSelectClipRgn(hdc, r.GetHrgn (Graphics.FromHdc (hdc)), (int) ClipCombineMode.RGN_AND); + } + + Win32MoveToEx(hdc, pt.x + rect.Left, pt.y + rect.Top, IntPtr.Zero); + if ((rect.Width > 0) && (rect.Height > 0)) { + Win32LineTo(hdc, pt.x + rect.Right, pt.y + rect.Top); + Win32LineTo(hdc, pt.x + rect.Right, pt.y + rect.Bottom); + Win32LineTo(hdc, pt.x + rect.Left, pt.y + rect.Bottom); + Win32LineTo(hdc, pt.x + rect.Left, pt.y + rect.Top); + } else { + if (rect.Width > 0) { + Win32LineTo(hdc, pt.x + rect.Right, pt.y + rect.Top); + } else { + Win32LineTo(hdc, pt.x + rect.Left, pt.y + rect.Bottom); + } + } + + Win32SelectObject(hdc, oldpen); + Win32DeleteObject(pen); + if (c != null) + Win32ExtSelectClipRgn(hdc, IntPtr.Zero, (int) ClipCombineMode.RGN_COPY); + + Win32ReleaseDC(IntPtr.Zero, hdc); + } + + internal override SizeF GetAutoScaleSize(Font font) { + Graphics g; + float width; + string magic_string = "The quick brown fox jumped over the lazy dog."; + double magic_number = 44.549996948242189; + + g = Graphics.FromHwnd(GetFosterParent()); + + width = (float) (g.MeasureString (magic_string, font).Width / magic_number); + return new SizeF(width, font.Height); + } + + internal override IntPtr SendMessage (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) { + return Win32SendMessage(hwnd, message, wParam, lParam); + } + + internal override bool PostMessage (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) { + return Win32PostMessage(hwnd, message, wParam, lParam); + } + + internal override int SendInput (IntPtr hwnd, Queue keys) { + INPUT[] inputs = new INPUT[keys.Count]; + const Int32 INPUT_KEYBOARD = 1; + uint returns = 0; + int i = 0; + while (keys.Count > 0) { + MSG msg = (MSG)keys.Dequeue(); + + + inputs[i].ki.wScan = 0; + inputs[i].ki.time = 0; + inputs[i].ki.dwFlags = (Int32)(msg.message == Msg.WM_KEYUP ? InputFlags.KEYEVENTF_KEYUP : 0); + inputs[i].ki.wVk = (short)msg.wParam.ToInt32(); + inputs[i].type = INPUT_KEYBOARD; + i++; + } + returns = Win32SendInput((UInt32)inputs.Length, inputs, Marshal.SizeOf(typeof(INPUT))); + + return (int) returns; + } + + internal override int KeyboardSpeed { + get { + int speed = 0; + Win32SystemParametersInfo(SPIAction.SPI_GETKEYBOARDSPEED, 0, ref speed, 0); + // + // Return values range from 0 to 31 which map to 2.5 to 30 repetitions per second. + // + return speed; + } + } + + internal override int KeyboardDelay { + get { + int delay = 1; + Win32SystemParametersInfo(SPIAction.SPI_GETKEYBOARDDELAY, 0, ref delay, 0); + // + // Return values must range from 0 to 4, 0 meaning 250ms, + // and 4 meaning 1000 ms. + // + return delay; + } + } + + private class WinBuffer + { + public IntPtr hdc; + public IntPtr bitmap; + + public WinBuffer (IntPtr hdc, IntPtr bitmap) + { + this.hdc = hdc; + this.bitmap = bitmap; + } + } + + internal override void CreateOffscreenDrawable (IntPtr handle, int width, int height, out object offscreen_drawable) + { + Graphics destG = Graphics.FromHwnd (handle); + IntPtr destHdc = destG.GetHdc (); + + IntPtr srcHdc = Win32CreateCompatibleDC (destHdc); + IntPtr srcBmp = Win32CreateCompatibleBitmap (destHdc, width, height); + Win32SelectObject (srcHdc, srcBmp); + + offscreen_drawable = new WinBuffer (srcHdc, srcBmp); + + destG.ReleaseHdc (destHdc); + } + + internal override Graphics GetOffscreenGraphics (object offscreen_drawable) + { + return Graphics.FromHdc (((WinBuffer)offscreen_drawable).hdc); + } + + internal override void BlitFromOffscreen (IntPtr dest_handle, Graphics dest_dc, object offscreen_drawable, Graphics offscreen_dc, Rectangle r) + { + WinBuffer wb = (WinBuffer)offscreen_drawable; + + IntPtr destHdc = dest_dc.GetHdc (); + Win32BitBlt (destHdc, r.Left, r.Top, r.Width, r.Height, wb.hdc, r.Left, r.Top, TernaryRasterOperations.SRCCOPY); + dest_dc.ReleaseHdc (destHdc); + } + + internal override void DestroyOffscreenDrawable (object offscreen_drawable) + { + WinBuffer wb = (WinBuffer)offscreen_drawable; + + Win32DeleteObject (wb.bitmap); + Win32DeleteDC (wb.hdc); + } + + internal override void SetForegroundWindow (IntPtr handle) + { + Win32SetForegroundWindow(handle); + } + + internal override event EventHandler Idle; + #endregion // Public Static Methods + + #region Win32 Imports + [DllImport ("kernel32.dll", EntryPoint="GetLastError", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32GetLastError(); + + [DllImport ("user32.dll", EntryPoint="CreateWindowExW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32CreateWindow(WindowExStyles dwExStyle, string lpClassName, string lpWindowName, WindowStyles dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lParam); + + [DllImport ("user32.dll", EntryPoint="DestroyWindow", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32DestroyWindow(IntPtr hWnd); + + [DllImport ("user32.dll", EntryPoint="PeekMessageW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32PeekMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags); + + [DllImport ("user32.dll", EntryPoint="GetMessageW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32GetMessage(ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax); + + [DllImport ("user32.dll", EntryPoint="TranslateMessage", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32TranslateMessage(ref MSG msg); + + [DllImport ("user32.dll", EntryPoint="DispatchMessageW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32DispatchMessage(ref MSG msg); + + [DllImport ("user32.dll", EntryPoint="MoveWindow", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32MoveWindow(IntPtr hWnd, int x, int y, int width, int height, bool repaint); + + [DllImport ("user32.dll", EntryPoint="SetWindowPos", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, SetWindowPosFlags Flags); + + [DllImport ("user32.dll", EntryPoint="SetWindowPos", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32SetWindowPos(IntPtr hWnd, SetWindowPosZOrder pos, int x, int y, int cx, int cy, SetWindowPosFlags Flags); + + [DllImport ("user32.dll", EntryPoint="SetWindowTextW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32SetWindowText(IntPtr hWnd, string lpString); + + [DllImport ("user32.dll", EntryPoint="GetWindowTextW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport ("user32.dll", EntryPoint="SetParent", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32SetParent(IntPtr hWnd, IntPtr hParent); + + [DllImport ("user32.dll", EntryPoint="RegisterClassW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32RegisterClass(ref WNDCLASS wndClass); + + [DllImport ("user32.dll", EntryPoint="LoadCursorW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32LoadCursor(IntPtr hInstance, LoadCursorType type); + + [DllImport ("user32.dll", EntryPoint="ShowCursor", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32ShowCursor(bool bShow); + + [DllImport ("user32.dll", EntryPoint="SetCursor", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32SetCursor(IntPtr hCursor); + + [DllImport ("user32.dll", EntryPoint="CreateCursor", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32CreateCursor(IntPtr hInstance, int xHotSpot, int yHotSpot, int nWidth, int nHeight, Byte[] pvANDPlane, Byte[] pvORPlane); + + [DllImport ("user32.dll", EntryPoint="DestroyCursor", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32DestroyCursor(IntPtr hCursor); + + [DllImport ("user32.dll", EntryPoint = "DrawIcon", CallingConvention = CallingConvention.StdCall)] + private extern static bool Win32DrawIcon (IntPtr hDC, int X, int Y, IntPtr hIcon); + + [DllImport ("user32.dll", EntryPoint="DefWindowProcW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32DefWindowProc(IntPtr hWnd, Msg Msg, IntPtr wParam, IntPtr lParam); + + //[DllImport ("user32.dll", EntryPoint="DefDlgProcW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + //private extern static IntPtr Win32DefDlgProc(IntPtr hWnd, Msg Msg, IntPtr wParam, IntPtr lParam); + + [DllImport ("user32.dll", EntryPoint="PostQuitMessage", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32PostQuitMessage(int nExitCode); + + [DllImport ("user32.dll", EntryPoint="UpdateWindow", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32UpdateWindow(IntPtr hWnd); + + [DllImport ("user32.dll", EntryPoint="GetUpdateRect", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32GetUpdateRect(IntPtr hWnd, ref RECT rect, bool erase); + + [DllImport ("user32.dll", EntryPoint="BeginPaint", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32BeginPaint(IntPtr hWnd, ref PAINTSTRUCT ps); + + [DllImport ("user32.dll", EntryPoint = "ValidateRect", CallingConvention = CallingConvention.StdCall)] + private extern static IntPtr Win32ValidateRect (IntPtr hWnd, ref RECT rect); + + [DllImport ("user32.dll", EntryPoint="EndPaint", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32EndPaint(IntPtr hWnd, ref PAINTSTRUCT ps); + + [DllImport ("user32.dll", EntryPoint="GetDC", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetDC(IntPtr hWnd); + + [DllImport ("user32.dll", EntryPoint="GetWindowDC", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetWindowDC(IntPtr hWnd); + + //[DllImport ("user32.dll", EntryPoint="GetDCEx", CallingConvention=CallingConvention.StdCall)] + //private extern static IntPtr Win32GetDCEx(IntPtr hWnd, IntPtr hRgn, DCExFlags flags); + + [DllImport ("user32.dll", EntryPoint="ReleaseDC", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32ReleaseDC(IntPtr hWnd, IntPtr hDC); + + [DllImport ("user32.dll", EntryPoint="MessageBoxW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32MessageBox(IntPtr hParent, string pText, string pCaption, uint uType); + + [DllImport ("user32.dll", EntryPoint="InvalidateRect", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32InvalidateRect(IntPtr hWnd, ref RECT lpRect, bool bErase); + + //[DllImport ("user32.dll", EntryPoint="InvalidateRect", CallingConvention=CallingConvention.StdCall)] + //private extern static IntPtr Win32InvalidateRect(IntPtr hWnd, IntPtr lpRect, bool bErase); + + [DllImport ("user32.dll", EntryPoint="SetCapture", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32SetCapture(IntPtr hWnd); + + [DllImport ("user32.dll", EntryPoint="ReleaseCapture", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32ReleaseCapture(); + + [DllImport ("user32.dll", EntryPoint="GetWindowRect", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetWindowRect(IntPtr hWnd, out RECT rect); + + [DllImport ("user32.dll", EntryPoint="GetClientRect", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetClientRect(IntPtr hWnd, out RECT rect); + + [DllImport ("user32.dll", EntryPoint="ScreenToClient", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32ScreenToClient(IntPtr hWnd, ref POINT pt); + + [DllImport ("user32.dll", EntryPoint="ClientToScreen", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32ClientToScreen(IntPtr hWnd, ref POINT pt); + + // This function returns the parent OR THE OWNER! + // Use GetAncestor to only get the parent. + [DllImport ("user32.dll", EntryPoint="GetParent", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetParent(IntPtr hWnd); + + [DllImport ("user32.dll", EntryPoint = "GetAncestor", CallingConvention = CallingConvention.StdCall)] + private extern static IntPtr Win32GetAncestor (IntPtr hWnd, AncestorType flags); + + [DllImport ("user32.dll", EntryPoint="SetActiveWindow", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32SetActiveWindow(IntPtr hWnd); + + [DllImport ("user32.dll", EntryPoint="AdjustWindowRectEx", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32AdjustWindowRectEx(ref RECT lpRect, int dwStyle, bool bMenu, int dwExStyle); + + [DllImport ("user32.dll", EntryPoint="GetCursorPos", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32GetCursorPos(out POINT lpPoint); + + [DllImport ("user32.dll", EntryPoint="SetCursorPos", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32SetCursorPos(int x, int y); + + //[DllImport ("user32.dll", EntryPoint="GetWindowPlacement", CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + [DllImport ("user32.dll", EntryPoint="TrackMouseEvent", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32TrackMouseEvent(ref TRACKMOUSEEVENT tme); + + //[DllImport ("gdi32.dll", EntryPoint="CreateBrushIndirect", CallingConvention=CallingConvention.StdCall)] + //private extern static IntPtr Win32CreateBrushIndirect(ref LOGBRUSH lb); + + [DllImport ("gdi32.dll", EntryPoint="CreateSolidBrush", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32CreateSolidBrush(COLORREF clrRef); + + [DllImport ("gdi32.dll", EntryPoint="PatBlt", CallingConvention=CallingConvention.StdCall)] + private extern static int Win32PatBlt(IntPtr hdc, int nXLeft, int nYLeft, int nWidth, int nHeight, PatBltRop dwRop); + + [DllImport ("user32.dll", EntryPoint="SetWindowLong", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32SetWindowLong(IntPtr hwnd, WindowLong index, uint value); + + [DllImport ("user32.dll", EntryPoint="GetWindowLong", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32GetWindowLong(IntPtr hwnd, WindowLong index); + + [DllImport ("user32.dll", EntryPoint="SetLayeredWindowAttributes", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32SetLayeredWindowAttributes (IntPtr hwnd, COLORREF crKey, byte bAlpha, LayeredWindowAttributes dwFlags); + + [DllImport ("user32.dll", EntryPoint="GetLayeredWindowAttributes", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32GetLayeredWindowAttributes (IntPtr hwnd, out COLORREF pcrKey, out byte pbAlpha, out LayeredWindowAttributes pwdFlags); + + [DllImport ("gdi32.dll", EntryPoint="DeleteObject", CallingConvention=CallingConvention.StdCall)] + public extern static bool Win32DeleteObject(IntPtr o); + + [DllImport ("user32.dll", EntryPoint="GetKeyState", CallingConvention=CallingConvention.StdCall)] + private extern static short Win32GetKeyState(VirtualKeys nVirtKey); + + [DllImport ("user32.dll", EntryPoint="GetDesktopWindow", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetDesktopWindow(); + + [DllImport ("user32.dll", EntryPoint="SetTimer", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32SetTimer(IntPtr hwnd, int nIDEvent, uint uElapse, IntPtr timerProc); + + [DllImport ("user32.dll", EntryPoint="KillTimer", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32KillTimer(IntPtr hwnd, int nIDEvent); + + [DllImport ("user32.dll", EntryPoint="ShowWindow", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32ShowWindow(IntPtr hwnd, WindowPlacementFlags nCmdShow); + + [DllImport ("user32.dll", EntryPoint="EnableWindow", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32EnableWindow(IntPtr hwnd, bool Enabled); + + [DllImport ("user32.dll", EntryPoint="SetFocus", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32SetFocus(IntPtr hwnd); + + [DllImport ("user32.dll", EntryPoint="GetFocus", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32GetFocus(); + + [DllImport ("user32.dll", EntryPoint="CreateCaret", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32CreateCaret(IntPtr hwnd, IntPtr hBitmap, int nWidth, int nHeight); + + [DllImport ("user32.dll", EntryPoint="DestroyCaret", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32DestroyCaret(); + + [DllImport ("user32.dll", EntryPoint="ShowCaret", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32ShowCaret(IntPtr hwnd); + + [DllImport ("user32.dll", EntryPoint="HideCaret", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32HideCaret(IntPtr hwnd); + + [DllImport ("user32.dll", EntryPoint="SetCaretPos", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32SetCaretPos(int X, int Y); + + //[DllImport ("user32.dll", EntryPoint="GetCaretBlinkTime", CallingConvention=CallingConvention.StdCall)] + //private extern static uint Win32GetCaretBlinkTime(); + + [DllImport ("gdi32.dll", EntryPoint="GetTextMetricsW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32GetTextMetrics(IntPtr hdc, ref TEXTMETRIC tm); + + [DllImport ("gdi32.dll", EntryPoint="SelectObject", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32SelectObject(IntPtr hdc, IntPtr hgdiobject); + + //[DllImport ("user32.dll", EntryPoint="ScrollWindowEx", CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32ScrollWindowEx(IntPtr hwnd, int dx, int dy, ref RECT prcScroll, ref RECT prcClip, IntPtr hrgnUpdate, out RECT prcUpdate, ScrollWindowExFlags flags); + + //[DllImport ("user32.dll", EntryPoint="ScrollWindowEx", CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32ScrollWindowEx(IntPtr hwnd, int dx, int dy, IntPtr prcScroll, ref RECT prcClip, IntPtr hrgnUpdate, out RECT prcUpdate, ScrollWindowExFlags flags); + + //[DllImport ("user32.dll", EntryPoint="ScrollWindowEx", CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32ScrollWindowEx(IntPtr hwnd, int dx, int dy, ref RECT prcScroll, IntPtr prcClip, IntPtr hrgnUpdate, out RECT prcUpdate, ScrollWindowExFlags flags); + + [DllImport ("user32.dll", EntryPoint="ScrollWindowEx", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32ScrollWindowEx(IntPtr hwnd, int dx, int dy, IntPtr prcScroll, ref RECT prcClip, IntPtr hrgnUpdate, IntPtr prcUpdate, ScrollWindowExFlags flags); + + //[DllImport ("user32.dll", EntryPoint="ScrollWindowEx", CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32ScrollWindowEx(IntPtr hwnd, int dx, int dy, ref RECT prcScroll, IntPtr prcClip, IntPtr hrgnUpdate, IntPtr prcUpdate, ScrollWindowExFlags flags); + + //[DllImport ("user32.dll", EntryPoint="ScrollWindowEx", CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32ScrollWindowEx(IntPtr hwnd, int dx, int dy, ref RECT prcScroll, ref RECT prcClip, IntPtr hrgnUpdate, IntPtr prcUpdate, ScrollWindowExFlags flags); + + [DllImport ("user32.dll", EntryPoint="ScrollWindowEx", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32ScrollWindowEx(IntPtr hwnd, int dx, int dy, IntPtr prcScroll, IntPtr prcClip, IntPtr hrgnUpdate, IntPtr prcUpdate, ScrollWindowExFlags flags); + + [DllImport ("user32.dll", EntryPoint="GetActiveWindow", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetActiveWindow(); + + [DllImport ("user32.dll", EntryPoint="GetSystemMetrics", CallingConvention=CallingConvention.StdCall)] + private extern static int Win32GetSystemMetrics(SystemMetrics nIndex); + + [DllImport ("shell32.dll", EntryPoint="Shell_NotifyIconW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32Shell_NotifyIcon(NotifyIconMessage dwMessage, ref NOTIFYICONDATA lpData); + + [DllImport ("gdi32.dll", EntryPoint="CreateRectRgn", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32CreateRectRgn(int nLeftRect, int nTopRect, int nRightRect, int nBottomRect); + + [DllImport ("user32.dll", EntryPoint="IsWindowEnabled", CallingConvention=CallingConvention.StdCall)] + private extern static bool IsWindowEnabled(IntPtr hwnd); + + [DllImport ("user32.dll", EntryPoint="IsWindowVisible", CallingConvention=CallingConvention.StdCall)] + private extern static bool IsWindowVisible(IntPtr hwnd); + + [DllImport ("user32.dll", EntryPoint="IsWindow", CallingConvention=CallingConvention.StdCall)] + private extern static bool IsWindow(IntPtr hwnd); + + //[DllImport ("user32.dll", EntryPoint="SetClassLong", CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32SetClassLong(IntPtr hwnd, ClassLong nIndex, IntPtr dwNewLong); + + [DllImport ("user32.dll", EntryPoint="SendMessageW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32SendMessage(IntPtr hwnd, Msg msg, IntPtr wParam, IntPtr lParam); + + [DllImport ("user32.dll", EntryPoint="PostMessageW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32PostMessage(IntPtr hwnd, Msg msg, IntPtr wParam, IntPtr lParam); + + [DllImport ("user32.dll", EntryPoint="SendInput", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static UInt32 Win32SendInput(UInt32 nInputs, [MarshalAs(UnmanagedType.LPArray)] INPUT[] inputs, Int32 cbSize); + + [DllImport ("user32.dll", EntryPoint="SystemParametersInfoW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32SystemParametersInfo(SPIAction uiAction, uint uiParam, ref RECT rect, uint fWinIni); + + //[DllImport ("user32.dll", EntryPoint="SystemParametersInfoW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + //private extern static bool Win32SystemParametersInfo(SPIAction uiAction, uint uiParam, ref uint value, uint fWinIni); + + [DllImport ("user32.dll", EntryPoint = "SystemParametersInfoW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] + private extern static bool Win32SystemParametersInfo (SPIAction uiAction, uint uiParam, ref int value, uint fWinIni); + + [DllImport ("user32.dll", EntryPoint = "SystemParametersInfoW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] + private extern static bool Win32SystemParametersInfo (SPIAction uiAction, uint uiParam, ref bool value, uint fWinIni); + + [DllImport ("user32.dll", EntryPoint = "SystemParametersInfoW", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] + private extern static bool Win32SystemParametersInfo (SPIAction uiAction, uint uiParam, ref ANIMATIONINFO value, uint fWinIni); + + [DllImport ("user32.dll", EntryPoint="OpenClipboard", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32OpenClipboard(IntPtr hwnd); + + [DllImport ("user32.dll", EntryPoint="EmptyClipboard", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32EmptyClipboard(); + + [DllImport ("user32.dll", EntryPoint="RegisterClipboardFormatW", CharSet=CharSet.Unicode, CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32RegisterClipboardFormat(string format); + + [DllImport ("user32.dll", EntryPoint="CloseClipboard", CallingConvention=CallingConvention.StdCall)] + private extern static bool Win32CloseClipboard(); + + [DllImport ("user32.dll", EntryPoint="EnumClipboardFormats", CallingConvention=CallingConvention.StdCall)] + private extern static uint Win32EnumClipboardFormats(uint format); + + [DllImport ("user32.dll", EntryPoint="GetClipboardData", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32GetClipboardData(uint format); + + [DllImport ("user32.dll", EntryPoint="SetClipboardData", CallingConvention=CallingConvention.StdCall)] + private extern static IntPtr Win32SetClipboardData(uint format, IntPtr handle); + + [DllImport ("kernel32.dll", EntryPoint="GlobalAlloc", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32GlobalAlloc(GAllocFlags Flags, int dwBytes); + + [DllImport ("kernel32.dll", EntryPoint="CopyMemory", CallingConvention=CallingConvention.StdCall)] + internal extern static void Win32CopyMemory(IntPtr Destination, IntPtr Source, int length); + + [DllImport ("kernel32.dll", EntryPoint="GlobalFree", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32GlobalFree(IntPtr hMem); + + [DllImport ("kernel32.dll", EntryPoint="GlobalSize", CallingConvention=CallingConvention.StdCall)] + internal extern static uint Win32GlobalSize(IntPtr hMem); + + [DllImport ("kernel32.dll", EntryPoint="GlobalLock", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32GlobalLock(IntPtr hMem); + + [DllImport ("kernel32.dll", EntryPoint="GlobalUnlock", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32GlobalUnlock(IntPtr hMem); + + [DllImport ("gdi32.dll", EntryPoint="SetROP2", CallingConvention=CallingConvention.StdCall)] + internal extern static int Win32SetROP2(IntPtr hdc, ROP2DrawMode fnDrawMode); + + [DllImport ("gdi32.dll", EntryPoint="MoveToEx", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32MoveToEx(IntPtr hdc, int x, int y, ref POINT lpPoint); + + [DllImport ("gdi32.dll", EntryPoint="MoveToEx", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32MoveToEx(IntPtr hdc, int x, int y, IntPtr lpPoint); + + [DllImport ("gdi32.dll", EntryPoint="LineTo", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32LineTo(IntPtr hdc, int x, int y); + + [DllImport ("gdi32.dll", EntryPoint="CreatePen", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32CreatePen(PenStyle fnPenStyle, int nWidth, ref COLORREF color); + + [DllImport ("gdi32.dll", EntryPoint="CreatePen", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32CreatePen(PenStyle fnPenStyle, int nWidth, IntPtr color); + + [DllImport ("gdi32.dll", EntryPoint="GetStockObject", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32GetStockObject(StockObject fnObject); + + [DllImport ("gdi32.dll", EntryPoint="CreateHatchBrush", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32CreateHatchBrush(HatchStyle fnStyle, IntPtr color); + + [DllImport ("gdi32.dll", EntryPoint="CreateHatchBrush", CallingConvention=CallingConvention.StdCall)] + internal extern static IntPtr Win32CreateHatchBrush(HatchStyle fnStyle, ref COLORREF color); + + [DllImport("gdi32.dll", EntryPoint = "ExcludeClipRect", CallingConvention = CallingConvention.StdCall)] + internal extern static int Win32ExcludeClipRect (IntPtr hdc, int left, int top, int right, int bottom); + + [DllImport ("gdi32.dll", EntryPoint="ExtSelectClipRgn", CallingConvention=CallingConvention.StdCall)] + internal extern static int Win32ExtSelectClipRgn(IntPtr hdc, IntPtr hrgn, int mode); + + [DllImport ("winmm.dll", EntryPoint="PlaySoundW", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode)] + internal extern static IntPtr Win32PlaySound(string pszSound, IntPtr hmod, SndFlags fdwSound); + + [DllImport ("user32.dll", EntryPoint="GetDoubleClickTime", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode)] + private extern static int Win32GetDoubleClickTime (); + + [DllImport ("user32.dll", EntryPoint="SetWindowRgn", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode)] + internal extern static int Win32SetWindowRgn(IntPtr hWnd, IntPtr hRgn, bool redraw); + + [DllImport ("user32.dll", EntryPoint="GetWindowRgn", CallingConvention=CallingConvention.StdCall, CharSet=CharSet.Unicode)] + internal extern static IntPtr Win32GetWindowRgn(IntPtr hWnd, IntPtr hRgn); + + [DllImport ("user32.dll", EntryPoint="ClipCursor", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32ClipCursor (ref RECT lpRect); + + [DllImport ("user32.dll", EntryPoint="GetClipCursor", CallingConvention=CallingConvention.StdCall)] + internal extern static bool Win32GetClipCursor (out RECT lpRect); + + [DllImport ("gdi32.dll", EntryPoint="BitBlt", CallingConvention=CallingConvention.StdCall)] + internal static extern bool Win32BitBlt (IntPtr hObject, int nXDest, int nYDest, int nWidth, + int nHeight, IntPtr hObjSource, int nXSrc, int nYSrc, TernaryRasterOperations dwRop); + + [DllImport ("gdi32.dll", EntryPoint="CreateCompatibleDC", CallingConvention=CallingConvention.StdCall, ExactSpelling = true, SetLastError = true)] + internal static extern IntPtr Win32CreateCompatibleDC (IntPtr hdc); + + [DllImport ("gdi32.dll", EntryPoint="DeleteDC", CallingConvention=CallingConvention.StdCall, ExactSpelling = true, SetLastError = true)] + internal static extern bool Win32DeleteDC (IntPtr hdc); + + [DllImport ("gdi32.dll", EntryPoint="CreateCompatibleBitmap", CallingConvention=CallingConvention.StdCall)] + internal static extern IntPtr Win32CreateCompatibleBitmap (IntPtr hdc, int nWidth, int nHeight); + + [DllImport ("kernel32.dll", EntryPoint = "GetSystemPowerStatus", CallingConvention = CallingConvention.StdCall)] + internal static extern Boolean Win32GetSystemPowerStatus (SYSTEMPOWERSTATUS sps); + + [DllImport ("user32.dll", EntryPoint = "GetIconInfo", CallingConvention = CallingConvention.StdCall)] + internal static extern bool Win32GetIconInfo (IntPtr hIcon, out ICONINFO piconinfo); + + [DllImport ("user32.dll", EntryPoint="SetForegroundWindow", CallingConvention=CallingConvention.StdCall)] + extern static bool Win32SetForegroundWindow(IntPtr hWnd); + #endregion + } +} diff --git a/source/ShiftUI/Internal/XplatUIX11-new.cs b/source/ShiftUI/Internal/XplatUIX11-new.cs new file mode 100644 index 0000000..3795406 --- /dev/null +++ b/source/ShiftUI/Internal/XplatUIX11-new.cs @@ -0,0 +1,1079 @@ +// 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 [email protected] +// Chris Toshok [email protected] +// +// + +// NOTE: +// This driver understands the following environment variables: (Set the var to enable feature) +// +// MONO_XEXCEPTIONS = throw an exception when a X11 error is encountered; +// by default a message is displayed but execution continues +// +// MONO_XSYNC = perform all X11 commands synchronous; this is slower but +// helps in debugging errors +// + +// NOT COMPLETE + +// define to log Window handles and relationships to stdout +#undef DriverDebug + +// Extra detailed debug +#undef DriverDebugExtra +#undef DriverDebugParent +#undef DriverDebugCreate +#undef DriverDebugDestroy +#undef DriverDebugThreads +#undef DriverDebugXEmbed + +using System; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using ShiftUI; + +/// X11 Version +namespace ShiftUI.X11Internal { + internal class XplatUIX11_new : XplatUIX11 { + + + #region Local Variables + // General + static volatile XplatUIX11_new Instance; + static readonly object lockobj = new object (); + static int RefCount; + static bool themes_enabled; + + static Hashtable MessageQueues; // Holds our thread-specific X11ThreadQueues + + X11Display display; + + #endregion // Local Variables + #region Constructors + private XplatUIX11_new() { + // Handle singleton stuff first + RefCount = 0; + + // Now regular initialization + MessageQueues = Hashtable.Synchronized (new Hashtable(7)); + if (Xlib.XInitThreads() == 0) { + Console.WriteLine ("Failed XInitThreads. The X11 event loop will not function properly"); + } + } + + private void InitializeDisplay () + { + display = new X11Display (Xlib.XOpenDisplay(IntPtr.Zero)); + + Graphics.FromHdcInternal (display.Handle); + } + + ~XplatUIX11_new() { + // Remove our display handle from S.D + Graphics.FromHdcInternal (IntPtr.Zero); + } + + #endregion // Constructors + + #region Singleton Specific Code + public static XplatUIX11_new GetInstance() { + lock (lockobj) { + if (Instance == null) { + Instance = new XplatUIX11_new (); + + Instance.InitializeDisplay (); + } + RefCount++; + } + return Instance; + } + + public int Reference { + get { + return RefCount; + } + } + #endregion + + #region Internal Methods + internal static void Where() { + Console.WriteLine("Here: {0}\n", GetInstance().display.WhereString()); + } + + #endregion // Internal Methods + + #region Private Methods + + internal X11ThreadQueue ThreadQueue (Thread thread) + { + X11ThreadQueue queue; + + queue = (X11ThreadQueue)MessageQueues[thread]; + if (queue == null) { + queue = new X11ThreadQueue(thread); + MessageQueues[thread] = queue; + } + + return queue; + } + #endregion // Private Methods + + + #region Public Properties + internal override int CaptionHeight { + get { return 19; } + } + + internal override Size CursorSize { + get { return display.CursorSize; } + } + + internal override bool DragFullWindows { + get { return true; } + } + + internal override Size DragSize { + get { return new Size(4, 4); } + } + + internal override Size FrameBorderSize { + get { return new Size (4, 4); } + } + + internal override Size IconSize { + get { return display.IconSize; } + } + + internal override int KeyboardSpeed { + get { return display.KeyboardSpeed; } + } + + internal override int KeyboardDelay { + get { return display.KeyboardSpeed; } + } + + internal override Size MaxWindowTrackSize { + get { return new Size (WorkingArea.Width, WorkingArea.Height); } + } + + internal override bool MenuAccessKeysUnderlined { + get { + return false; + } + } + + internal override Size MinimizedWindowSpacingSize { + get { return new Size(1, 1); } + } + + internal override Size MinimumWindowSize { + get { return new Size(1, 1); } + } + + internal override Keys ModifierKeys { + get { return display.ModifierKeys; } + } + + internal override Size SmallIconSize { + get { return display.SmallIconSize; } + } + + internal override int MouseButtonCount { + get { return 3; /* FIXME - should detect this somehow.. */} + } + + internal override bool MouseButtonsSwapped { + get { return false; /*FIXME - how to detect? */} + } + + internal override Size MouseHoverSize { + get { return new Size (1, 1); } + } + + internal override int MouseHoverTime { + get { return display.MouseHoverTime; } + } + + internal override bool MouseWheelPresent { + get { return true; /* FIXME - how to detect? */ } + } + + internal override Rectangle VirtualScreen { + get { return display.VirtualScreen; } + } + + internal override Rectangle WorkingArea { + get { return display.WorkingArea; } + } + + internal override bool ThemesEnabled { + get { return XplatUIX11_new.themes_enabled; } + } + + + #endregion // Public properties + + #region Public Static Methods + internal override void RaiseIdle (EventArgs e) + { + X11ThreadQueue queue = ThreadQueue (Thread.CurrentThread); + queue.OnIdle (e); + } + + internal override IntPtr InitializeDriver () + { + lock (this) { + if (display == null) + display = new X11Display (Xlib.XOpenDisplay(IntPtr.Zero)); + } + return IntPtr.Zero; + } + + internal override void ShutdownDriver(IntPtr token) + { + lock (this) { + if (display != null) { + display.Close (); + display = null; + } + } + } + + internal override void EnableThemes() + { + themes_enabled = true; + } + + internal override void Activate (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.Activate (); + } + + internal override void AudibleAlert(AlertType atype) + { + display.AudibleAlert (); + } + + + internal override void CaretVisible (IntPtr handle, bool visible) + { + display.CaretVisible (handle, visible); + } + + // XXX this implementation should probably be shared between all non-win32 backends + internal override bool CalculateWindowRect (ref Rectangle ClientRect, CreateParams cp, Menu menu, out Rectangle WindowRect) + { + WindowRect = Hwnd.GetWindowRectangle (cp, menu, ClientRect); + return true; + } + + internal override void ClientToScreen (IntPtr handle, ref int x, ref int y) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.ClientToScreen (ref x, ref y); + } + + internal override void CreateCaret (IntPtr handle, int width, int height) + { + display.CreateCaret (handle, width, height); + } + + internal override IntPtr CreateWindow (CreateParams cp) + { + X11Hwnd hwnd = new X11Hwnd (display); + + hwnd.CreateWindow (cp); + + return hwnd.Handle; + } + + internal override IntPtr CreateWindow (IntPtr Parent, int X, int Y, int Width, int Height) + { + CreateParams create_params = new CreateParams(); + + create_params.Caption = ""; + create_params.X = X; + create_params.Y = Y; + create_params.Width = Width; + create_params.Height = Height; + + create_params.ClassName = XplatUI.DefaultClassName; + create_params.ClassStyle = 0; + create_params.ExStyle = 0; + create_params.Parent = IntPtr.Zero; + create_params.Param = 0; + + return CreateWindow (create_params); + } + + internal override IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) + { + return display.DefineCursor (bitmap, mask, cursor_pixel, mask_pixel, xHotSpot, yHotSpot); + } + internal override Bitmap DefineStdCursorBitmap (StdCursor id) + { + return display.DefineStdCursorBitmap (id); + } + internal override IntPtr DefineStdCursor (StdCursor id) + { + return display.DefineStdCursor (id); + } + + internal override IntPtr DefWndProc(ref Message msg) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(msg.HWnd); + + if (hwnd == null) + return IntPtr.Zero; + + return hwnd.DefWndProc (ref msg); + } + + internal override void DestroyCaret (IntPtr handle) + { + display.DestroyCaret (handle); + } + + internal override void DestroyCursor(IntPtr cursor) + { + display.DestroyCursor (cursor); + } + + internal override void DestroyWindow (IntPtr handle) { + X11Hwnd hwnd; + + hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) { +#if DriverDebug || DriverDebugDestroy + Console.WriteLine("window {0:X} already destroyed", handle.ToInt32()); +#endif + return; + } + +#if DriverDebug || DriverDebugDestroy + Console.WriteLine("Destroying window {0}", XplatUI.Window(hwnd.ClientWindow)); +#endif + + display.DestroyWindow (hwnd); + } + + internal override IntPtr DispatchMessage(ref MSG msg) + { + return display.DispatchMessage (ref msg); + } + + internal override void DrawReversibleLine (Point start, Point end, Color backColor) + { + display.DrawReversibleLine (start, end, backColor); + } + + internal override void FillReversibleRectangle (Rectangle rectangle, Color backColor) + { + display.FillReversibleRectangle (rectangle, backColor); + } + + internal override void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style) + { + display.DrawReversibleFrame (rectangle, backColor, style); + } + + internal override void DrawReversibleRectangle (IntPtr handle, Rectangle rect, int line_width) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.DrawReversibleRectangle (rect, line_width); + } + + internal override void DoEvents () + { + X11ThreadQueue queue = ThreadQueue (Thread.CurrentThread); + display.DoEvents (queue); + } + + internal override void EnableWindow (IntPtr handle, bool Enable) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd != null) + hwnd.Enabled = Enable; + } + + internal override void EndLoop (Thread thread) + { + // This is where we one day will shut down the loop for the thread + } + + + internal override IntPtr GetActive() + { + X11Hwnd hwnd = display.GetActiveWindow (); + + return (hwnd == null) ? IntPtr.Zero : hwnd.Handle; + } + + internal override Region GetClipRegion (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + return (hwnd == null) ? null : hwnd.GetClipRegion (); + } + + internal override void GetCursorInfo(IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y) + { + width = 20; + height = 20; + hotspot_x = 0; + hotspot_y = 0; + } + + internal override void GetDisplaySize(out Size size) + { + display.GetDisplaySize (out size); + } + + internal override SizeF GetAutoScaleSize (Font font) + { + return display.GetAutoScaleSize (font); + } + + // XXX this should be someplace shareable by all non-win32 backends.. like in Hwnd itself. + // maybe a Hwnd.ParentHandle property + internal override IntPtr GetParent (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd != null && hwnd.parent != null) { + return hwnd.parent.Handle; + } + return IntPtr.Zero; + } + + // This is a nop on win32 and x11 + internal override IntPtr GetPreviousWindow(IntPtr handle) { + return handle; + } + + internal override void GetCursorPos (IntPtr handle, out int x, out int y) + { + display.GetCursorPos ((X11Hwnd)Hwnd.ObjectFromHandle(handle), + out x, out y); + } + + internal override IntPtr GetFocus() + { + return display.GetFocus (); + } + + // XXX this should be shared amongst non-win32 backends + internal override bool GetFontMetrics (Graphics g, Font font, out int ascent, out int descent) + { + FontFamily ff = font.FontFamily; + ascent = ff.GetCellAscent (font.Style); + descent = ff.GetCellDescent (font.Style); + return true; + } + + + // XXX this should be shared amongst non-win32 backends + internal override Point GetMenuOrigin (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + return hwnd.MenuOrigin; + + return Point.Empty; + } + + internal override bool GetMessage (object queue_id, ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax) + { + return display.GetMessage (queue_id, ref msg, handle, wFilterMin, wFilterMax); + } + + internal override bool GetText (IntPtr handle, out string text) + { + X11Hwnd hwnd = (X11Hwnd) Hwnd.ObjectFromHandle(handle); + + text = ""; + return hwnd != null && hwnd.GetText (out text); + } + + internal override void GetWindowPos (IntPtr handle, bool is_toplevel, + out int x, out int y, + out int width, out int height, + out int client_width, out int client_height) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) { + hwnd.GetPosition (is_toplevel, out x, out y, out width, out height, out client_width, out client_height); + } + else { + // Should we throw an exception or fail silently? + // throw new ArgumentException("Called with an invalid window handle", "handle"); + + x = 0; + y = 0; + width = 0; + height = 0; + client_width = 0; + client_height = 0; + } + } + + internal override FormWindowState GetWindowState (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) + return FormWindowState.Normal; // XXX should we throw an exception here? probably + + return hwnd.GetWindowState (); + } + + internal override void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea) + { + display.GrabInfo (out handle, out GrabConfined, out GrabArea); + } + + internal override void GrabWindow (IntPtr handle, IntPtr confine_to_handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + X11Hwnd confine_to_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(confine_to_handle); + + display.GrabWindow (hwnd, confine_to_hwnd); + } + + internal override void UngrabWindow (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + display.UngrabWindow (hwnd); + } + + internal override void HandleException(Exception e) { + StackTrace st = new StackTrace(e, true); + Console.WriteLine("Exception '{0}'", e.Message+st.ToString()); + Console.WriteLine("{0}{1}", e.Message, st.ToString()); + } + + internal override void Invalidate (IntPtr handle, Rectangle rc, bool clear) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + hwnd.Invalidate (rc, clear); + } + + internal override void InvalidateNC (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + hwnd.InvalidateNC (); + } + + internal override bool IsEnabled(IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle); + + return hwnd != null && hwnd.Enabled; + } + + internal override bool IsVisible(IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle (handle); + + return hwnd != null && hwnd.Visible; + } + + internal override void KillTimer (Timer timer) + { + X11ThreadQueue queue = (X11ThreadQueue) MessageQueues [timer.thread]; + + if (queue == null) { + // This isn't really an error, MS doesn't start the timer if + // it has no assosciated queue + return; + } + queue.KillTimer (timer); + } + + internal override void MenuToScreen (IntPtr handle, ref int x, ref int y) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.MenuToScreen (ref x, ref y); + } + + internal override void OverrideCursor (IntPtr cursor) + { + display.OverrideCursor = cursor; + } + + internal override PaintEventArgs PaintEventStart (ref Message m, IntPtr handle, bool client) + { + return display.PaintEventStart (ref m, handle, client); + } + + internal override void PaintEventEnd (ref Message m, IntPtr handle, bool client) + { + display.PaintEventEnd (ref m, handle, client); + } + + + internal override bool PeekMessage (object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) + { + return display.PeekMessage (queue_id, ref msg, hWnd, wFilterMin, wFilterMax, flags); + } + + internal override bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam) + { + return display.PostMessage (handle, message, wparam, lparam); + } + + internal override void PostQuitMessage(int exitCode) + { + display.PostMessage (display.FosterParent.Handle, Msg.WM_QUIT, IntPtr.Zero, IntPtr.Zero); + display.Flush (); + } + + [MonoTODO] + internal override void RequestAdditionalWM_NCMessages (IntPtr handle, bool hover, bool leave) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.RequestAdditionalWM_NCMessages (hover, leave); + } + + internal override void RequestNCRecalc (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.RequestNCRecalc (); + } + + internal override void ResetMouseHover (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + display.ResetMouseHover (hwnd); + } + + internal override void ScreenToClient(IntPtr handle, ref int x, ref int y) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.ScreenToClient (ref x, ref y); + } + + internal override void ScreenToMenu (IntPtr handle, ref int x, ref int y) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.ScreenToMenu (ref x, ref y); + } + + internal override void ScrollWindow (IntPtr handle, Rectangle area, int XAmount, int YAmount, bool with_children) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.ScrollWindow (area, XAmount, YAmount, with_children); + } + + internal override void ScrollWindow(IntPtr handle, int XAmount, int YAmount, bool with_children) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.GetObjectFromWindow(handle); + + if (hwnd != null) { + Rectangle rect; + + rect = hwnd.ClientRect; + rect.X = 0; + rect.Y = 0; + hwnd.ScrollWindow (rect, XAmount, YAmount, with_children); + } + } + + internal override void SendAsyncMethod (AsyncMethodData method) + { + display.SendAsyncMethod (method); + } + + // XXX this is likely shareable amongst other backends + internal override IntPtr SendMessage (IntPtr handle, Msg message, IntPtr wParam, IntPtr lParam) + { + return display.SendMessage (handle, message, wParam, lParam); + } + + internal override int SendInput(IntPtr handle, Queue keys) { + return display.SendInput(handle, keys); + } + + + internal override void SetAllowDrop (IntPtr handle, bool value) + { + // We allow drop on all windows + } + + internal override DragDropEffects StartDrag (IntPtr handle, object data, + DragDropEffects allowed_effects) + { + return display.StartDrag (handle, data, allowed_effects); + } + + internal override void SetBorderStyle (IntPtr handle, FormBorderStyle border_style) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.SetBorderStyle (border_style); + } + + internal override void SetCaretPos (IntPtr handle, int x, int y) + { + display.SetCaretPos (handle, x, y); + } + + internal override void SetClipRegion (IntPtr handle, Region region) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.SetClipRegion (region); + } + + internal override void SetCursor (IntPtr handle, IntPtr cursor) + { + display.SetCursor (handle, cursor); + } + + internal override void SetCursorPos (IntPtr handle, int x, int y) + { + if (handle == IntPtr.Zero) { + display.SetCursorPos (x, y); + } + else { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + hwnd.SetCursorPos (x, y); + } + } + + internal override void SetFocus (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + display.SetFocus (hwnd); + } + + internal override void SetIcon(IntPtr handle, Icon icon) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle);; + + if (hwnd != null) + hwnd.SetIcon (icon); + } + + internal override void SetMenu(IntPtr handle, Menu menu) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + hwnd.SetMenu (menu); + } + + internal override void SetModal(IntPtr handle, bool Modal) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + display.SetModal (hwnd, Modal); + } + + internal override IntPtr SetParent(IntPtr handle, IntPtr parent) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + X11Hwnd parent_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(parent); + + if (hwnd != null) + hwnd.SetParent (parent_hwnd); + + return IntPtr.Zero; + } + + internal override void SetTimer (Timer timer) + { + X11ThreadQueue queue = (X11ThreadQueue) MessageQueues [timer.thread]; + + if (queue == null) { + // This isn't really an error, MS doesn't start the timer if + // it has no assosciated queue + return; + } + queue.SetTimer (timer); + } + + internal override bool SetTopmost(IntPtr handle, bool enabled) + { + X11Hwnd hwnd = (X11Hwnd) Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) + return false; + + return hwnd.SetTopmost (enabled); + } + + internal override bool SetOwner(IntPtr handle, IntPtr handle_owner) + { + X11Hwnd hwnd; + + hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) + return false; + + X11Hwnd hwnd_owner = (X11Hwnd)Hwnd.ObjectFromHandle(handle_owner); + + return hwnd.SetOwner (hwnd_owner); + } + + internal override bool SetVisible (IntPtr handle, bool visible, bool activate) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + return hwnd != null && hwnd.SetVisible (visible, activate); + } + + internal override void SetWindowMinMax (IntPtr handle, Rectangle maximized, Size min, Size max) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) + return; + + hwnd.SetMinMax (maximized, min, max); + } + + internal override void SetWindowPos (IntPtr handle, int x, int y, int width, int height) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.SetPosition (x, y, width, height); + } + + internal override void SetWindowState (IntPtr handle, FormWindowState state) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.SetWindowState (state); + } + + internal override void SetWindowStyle (IntPtr handle, CreateParams cp) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) { + hwnd.SetHwndStyles (cp); + hwnd.SetWMStyles (cp); + } + } + + internal override double GetWindowTransparency (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + return hwnd.GetWindowTransparency (); + else + return 0.0; + } + + internal override void SetWindowTransparency (IntPtr handle, double transparency, Color key) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.SetWindowTransparency (transparency, key); + } + + internal override bool SetZOrder (IntPtr handle, IntPtr after_handle, bool top, bool bottom) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd == null || !hwnd.mapped) + return false; + + X11Hwnd after_hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(after_handle); + + return hwnd.SetZOrder (after_hwnd, top, bottom); + } + + internal override void ShowCursor (bool show) + { + display.ShowCursor (show); + } + + internal override object StartLoop(Thread thread) + { + return (object) ThreadQueue(thread); + } + + internal override TransparencySupport SupportsTransparency() + { + return display.SupportsTransparency (); + } + + internal override bool SystrayAdd (IntPtr handle, string tip, Icon icon, out ToolTip tt) + { + return display.SystrayAdd (handle, tip, icon, out tt); + } + + internal override bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt) + { + return display.SystrayChange (handle, tip, icon, ref tt); + } + + internal override void SystrayRemove (IntPtr handle, ref ToolTip tt) + { + display.SystrayRemove (handle, ref tt); + } + + NotifyIcon.BalloonWindow balloon_window; + + internal override void SystrayBalloon(IntPtr handle, int timeout, string title, string text, ToolTipIcon icon) + { + Widget control = Widget.FromHandle(handle); + + if (control == null) + return; + + if (balloon_window != null) { + balloon_window.Close (); + balloon_window.Dispose (); + } + + balloon_window = new NotifyIcon.BalloonWindow (handle); + balloon_window.Title = title; + balloon_window.Text = text; + balloon_window.Timeout = timeout; + balloon_window.Show (); + + SendMessage(handle, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONSHOW); + } + + internal override bool Text (IntPtr handle, string text) + { + X11Hwnd hwnd = (X11Hwnd) Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.Text = text; + + return true; + } + + internal override bool TranslateMessage (ref MSG msg) + { + return display.TranslateMessage (ref msg); + } + + internal override void UpdateWindow (IntPtr handle) + { + X11Hwnd hwnd = (X11Hwnd)Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) + hwnd.Update (); + } + + internal override void CreateOffscreenDrawable (IntPtr handle, + int width, int height, + out object offscreen_drawable) + { + display.CreateOffscreenDrawable (handle, width, height, + out offscreen_drawable); + } + + internal override void DestroyOffscreenDrawable (object offscreen_drawable) + { + display.DestroyOffscreenDrawable (offscreen_drawable); + } + + internal override Graphics GetOffscreenGraphics (object offscreen_drawable) + { + return display.GetOffscreenGraphics (offscreen_drawable); + } + + internal override void BlitFromOffscreen (IntPtr dest_handle, + Graphics dest_dc, + object offscreen_drawable, + Graphics offscreen_dc, + Rectangle r) + { + display.BlitFromOffscreen (dest_handle, dest_dc, offscreen_drawable, offscreen_dc, r); + } + + #endregion // Public Static Methods + + #region Events + internal override event EventHandler Idle { + add { + Console.WriteLine ("adding idle handler for thread {0}", Thread.CurrentThread.GetHashCode()); + X11ThreadQueue queue = ThreadQueue(Thread.CurrentThread); + queue.Idle += value; + } + remove { + X11ThreadQueue queue = ThreadQueue(Thread.CurrentThread); + queue.Idle += value; + } + } + #endregion // Events + } +} diff --git a/source/ShiftUI/Internal/XplatUIX11.cs b/source/ShiftUI/Internal/XplatUIX11.cs new file mode 100644 index 0000000..fd68bab --- /dev/null +++ b/source/ShiftUI/Internal/XplatUIX11.cs @@ -0,0 +1,7687 @@ +// 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 [email protected] +// +// + +// NOTE: +// This driver understands the following environment variables: (Set the var to enable feature) +// +// MONO_XEXCEPTIONS = throw an exception when a X11 error is encountered; +// by default a message is displayed but execution continues +// +// MONO_XSYNC = perform all X11 commands synchronous; this is slower but +// helps in debugging errors +// + +// NOT COMPLETE + +// define to log Window handles and relationships to stdout +#undef DriverDebug + +// Extra detailed debug +#undef DriverDebugExtra +#undef DriverDebugParent +#undef DriverDebugCreate +#undef DriverDebugDestroy +#undef DriverDebugThreads +#undef DriverDebugXEmbed + +//#define TRACE +//#define DEBUG + +using System; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading; + +// Only do the poll when building with mono for now +#if __MonoCS__ +using Mono.Unix.Native; +#endif + +/// X11 Version +namespace ShiftUI { + internal class XplatUIX11 : XplatUIDriver { + #region Local Variables + // General + static volatile XplatUIX11 Instance; + static int RefCount; + static object XlibLock; // Our locking object + static bool themes_enabled; + + // General X11 + static IntPtr DisplayHandle; // X11 handle to display + static int ScreenNo; // Screen number used + static IntPtr DefaultColormap; // Colormap for screen + static IntPtr CustomVisual; // Visual for window creation + static IntPtr CustomColormap; // Colormap for window creation + static IntPtr RootWindow; // Handle of the root window for the screen/display + static IntPtr FosterParent; // Container to hold child windows until their parent exists + static XErrorHandler ErrorHandler; // Error handler delegate + static bool ErrorExceptions; // Throw exceptions on X errors + int render_major_opcode; + int render_first_event; + int render_first_error; + + // Clipboard + static IntPtr ClipMagic; + static ClipboardData Clipboard; // Our clipboard + + // Communication + static IntPtr PostAtom; // PostMessage atom + static IntPtr AsyncAtom; // Support for async messages + + // Message Loop + static Hashtable MessageQueues; // Holds our thread-specific XEventQueues + static ArrayList unattached_timer_list; // holds timers that are enabled but not attached to a window. + #if __MonoCS__ // + static Pollfd[] pollfds; // For watching the X11 socket + static bool wake_waiting; + static object wake_waiting_lock = new object (); + #endif // + static X11Keyboard Keyboard; // + static X11Dnd Dnd; + static Socket listen; // + static Socket wake; // + static Socket wake_receive; // + static byte[] network_buffer; // + static bool detectable_key_auto_repeat; + + // Focus tracking + static IntPtr ActiveWindow; // Handle of the active window + static IntPtr FocusWindow; // Handle of the window with keyboard focus (if any) + + // Modality support + static Stack ModalWindows; // Stack of our modal windows + + // Systray + static IntPtr SystrayMgrWindow; // Handle of the Systray Manager window + + // Cursors + static IntPtr LastCursorWindow; // The last window we set the cursor on + static IntPtr LastCursorHandle; // The handle that was last set on LastCursorWindow + static IntPtr OverrideCursorHandle; // The cursor that is set to override any other cursors + + // Caret + static CaretStruct Caret; // + + // Last window containing the pointer + static IntPtr LastPointerWindow; // The last window containing the pointer + + // Our atoms + static IntPtr WM_PROTOCOLS; + static IntPtr WM_DELETE_WINDOW; + static IntPtr WM_TAKE_FOCUS; + //static IntPtr _NET_SUPPORTED; + //static IntPtr _NET_CLIENT_LIST; + //static IntPtr _NET_NUMBER_OF_DESKTOPS; + static IntPtr _NET_DESKTOP_GEOMETRY; + //static IntPtr _NET_DESKTOP_VIEWPORT; + static IntPtr _NET_CURRENT_DESKTOP; + //static IntPtr _NET_DESKTOP_NAMES; + static IntPtr _NET_ACTIVE_WINDOW; + static IntPtr _NET_WORKAREA; + //static IntPtr _NET_SUPPORTING_WM_CHECK; + //static IntPtr _NET_VIRTUAL_ROOTS; + //static IntPtr _NET_DESKTOP_LAYOUT; + //static IntPtr _NET_SHOWING_DESKTOP; + //static IntPtr _NET_CLOSE_WINDOW; + //static IntPtr _NET_MOVERESIZE_WINDOW; + static IntPtr _NET_WM_MOVERESIZE; + //static IntPtr _NET_RESTACK_WINDOW; + //static IntPtr _NET_REQUEST_FRAME_EXTENTS; + static IntPtr _NET_WM_NAME; + //static IntPtr _NET_WM_VISIBLE_NAME; + //static IntPtr _NET_WM_ICON_NAME; + //static IntPtr _NET_WM_VISIBLE_ICON_NAME; + //static IntPtr _NET_WM_DESKTOP; + static IntPtr _NET_WM_WINDOW_TYPE; + static IntPtr _NET_WM_STATE; + //static IntPtr _NET_WM_ALLOWED_ACTIONS; + //static IntPtr _NET_WM_STRUT; + //static IntPtr _NET_WM_STRUT_PARTIAL; + //static IntPtr _NET_WM_ICON_GEOMETRY; + static IntPtr _NET_WM_ICON; + //static IntPtr _NET_WM_PID; + //static IntPtr _NET_WM_HANDLED_ICONS; + static IntPtr _NET_WM_USER_TIME; + static IntPtr _NET_FRAME_EXTENTS; + //static IntPtr _NET_WM_PING; + //static IntPtr _NET_WM_SYNC_REQUEST; + static IntPtr _NET_SYSTEM_TRAY_S; + //static IntPtr _NET_SYSTEM_TRAY_ORIENTATION; + static IntPtr _NET_SYSTEM_TRAY_OPCODE; + static IntPtr _NET_WM_STATE_MAXIMIZED_HORZ; + static IntPtr _NET_WM_STATE_MAXIMIZED_VERT; + static IntPtr _XEMBED; + static IntPtr _XEMBED_INFO; + static IntPtr _MOTIF_WM_HINTS; + static IntPtr _NET_WM_STATE_SKIP_TASKBAR; + static IntPtr _NET_WM_STATE_ABOVE; + static IntPtr _NET_WM_STATE_MODAL; + static IntPtr _NET_WM_STATE_HIDDEN; + static IntPtr _NET_WM_CONTEXT_HELP; + static IntPtr _NET_WM_WINDOW_OPACITY; + //static IntPtr _NET_WM_WINDOW_TYPE_DESKTOP; + //static IntPtr _NET_WM_WINDOW_TYPE_DOCK; + //static IntPtr _NET_WM_WINDOW_TYPE_TOOLBAR; + //static IntPtr _NET_WM_WINDOW_TYPE_MENU; + static IntPtr _NET_WM_WINDOW_TYPE_UTILITY; + //static IntPtr _NET_WM_WINDOW_TYPE_SPLASH; + // static IntPtr _NET_WM_WINDOW_TYPE_DIALOG; + static IntPtr _NET_WM_WINDOW_TYPE_NORMAL; + static IntPtr CLIPBOARD; + static IntPtr PRIMARY; + //static IntPtr DIB; + static IntPtr OEMTEXT; + static IntPtr UTF8_STRING; + static IntPtr UTF16_STRING; + static IntPtr RICHTEXTFORMAT; + static IntPtr TARGETS; + + // mouse hover message generation + static HoverStruct HoverState; // + + // double click message generation + static ClickStruct ClickPending; // + + // Support for mouse grab + static GrabStruct Grab; // + + // State + Point mouse_position; // Last position of mouse, in screen coords + internal static MouseButtons MouseState; // Last state of mouse buttons + internal static bool in_doevents; + // 'Constants' + static int DoubleClickInterval; // msec; max interval between clicks to count as double click + + const EventMask SelectInputMask = (EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask | + EventMask.KeyPressMask | + EventMask.KeyReleaseMask | + EventMask.EnterWindowMask | + EventMask.LeaveWindowMask | + EventMask.ExposureMask | + EventMask.FocusChangeMask | + EventMask.PointerMotionMask | + EventMask.PointerMotionHintMask | + EventMask.SubstructureNotifyMask); + + static readonly object lockobj = new object (); + + // messages WaitForHwndMwssage is waiting on + static Hashtable messageHold; + + #endregion // Local Variables + #region Constructors + public XplatUIX11() + { + // Handle singleton stuff first + RefCount = 0; + in_doevents = false; + + // Now regular initialization + XlibLock = new object (); + X11Keyboard.XlibLock = XlibLock; + MessageQueues = Hashtable.Synchronized (new Hashtable(7)); + unattached_timer_list = ArrayList.Synchronized (new ArrayList (3)); + messageHold = Hashtable.Synchronized (new Hashtable(3)); + Clipboard = new ClipboardData (); + XInitThreads(); + + ErrorExceptions = false; + + // X11 Initialization + SetDisplay(XOpenDisplay(IntPtr.Zero)); + X11DesktopColors.Initialize(); + + + // Disable keyboard autorepeat + try { + XkbSetDetectableAutoRepeat (DisplayHandle, true, IntPtr.Zero); + detectable_key_auto_repeat = true; + } catch { + Console.Error.WriteLine ("Could not disable keyboard auto repeat, will attempt to disable manually."); + detectable_key_auto_repeat = false; + } + + // Handle any upcoming errors; we re-set it here, X11DesktopColor stuff might have stolen it (gtk does) + ErrorHandler = new XErrorHandler(HandleError); + XSetErrorHandler(ErrorHandler); + } + + ~XplatUIX11() { + // Remove our display handle from S.D + Graphics.FromHdcInternal (IntPtr.Zero); + } + + #endregion // Constructors + + #region Singleton Specific Code + public static XplatUIX11 GetInstance() { + lock (lockobj) { + if (Instance == null) { + Instance=new XplatUIX11(); + } + RefCount++; + } + return Instance; + } + + public int Reference { + get { + return RefCount; + } + } + #endregion + + #region Internal Properties + internal static IntPtr Display { + get { + return DisplayHandle; + } + + set { + XplatUIX11.GetInstance().SetDisplay(value); + } + } + + internal static int Screen { + get { + return ScreenNo; + } + + set { + ScreenNo = value; + } + } + + internal static IntPtr RootWindowHandle { + get { + return RootWindow; + } + + set { + RootWindow = value; + } + } + + internal static IntPtr Visual { + get { + return CustomVisual; + } + + set { + CustomVisual = value; + } + } + + internal static IntPtr ColorMap { + get { + return CustomColormap; + } + + set { + CustomColormap = value; + } + } + +#if DEBUG_shana + internal static IntPtr DefaultColorMap { + get { + return DefaultColormap; + } + } +#endif + #endregion + + #region XExceptionClass + internal class XException : ApplicationException { + IntPtr Display; + IntPtr ResourceID; + IntPtr Serial; + XRequest RequestCode; + byte ErrorCode; + byte MinorCode; + + public XException(IntPtr Display, IntPtr ResourceID, IntPtr Serial, byte ErrorCode, XRequest RequestCode, byte MinorCode) { + this.Display = Display; + this.ResourceID = ResourceID; + this.Serial = Serial; + this.RequestCode = RequestCode; + this.ErrorCode = ErrorCode; + this.MinorCode = MinorCode; + } + + public override string Message { + get { + return GetMessage(Display, ResourceID, Serial, ErrorCode, RequestCode, MinorCode); + } + } + + public static string GetMessage(IntPtr Display, IntPtr ResourceID, IntPtr Serial, byte ErrorCode, XRequest RequestCode, byte MinorCode) { + StringBuilder sb; + string x_error_text; + string error; + string hwnd_text; + string Widget_text; + Hwnd hwnd; + Widget c; + + sb = new StringBuilder(160); + XGetErrorText(Display, ErrorCode, sb, sb.Capacity); + x_error_text = sb.ToString(); + hwnd = Hwnd.ObjectFromHandle(ResourceID); + if (hwnd != null) { + hwnd_text = hwnd.ToString(); + c = Widget.FromHandle(hwnd.Handle); + if (c != null) { + Widget_text = c.ToString(); + } else { + Widget_text = String.Format("<handle {0:X} non-existant>", hwnd.Handle.ToInt32()); + } + } else { + hwnd_text = "<null>"; + Widget_text = "<null>"; + } + + + error = String.Format("\n Error: {0}\n Request: {1:D} ({2})\n Resource ID: 0x{3:X}\n Serial: {4}\n Hwnd: {5}\n Widget: {6}", x_error_text, RequestCode, MinorCode, ResourceID.ToInt32(), Serial, hwnd_text, Widget_text); + return error; + } + } + #endregion // XExceptionClass + + #region Internal Methods + internal void SetDisplay(IntPtr display_handle) + { + if (display_handle != IntPtr.Zero) { + Hwnd hwnd; + + if ((DisplayHandle != IntPtr.Zero) && (FosterParent != IntPtr.Zero)) { + hwnd = Hwnd.ObjectFromHandle(FosterParent); + XDestroyWindow(DisplayHandle, FosterParent); + hwnd.Dispose(); + } + + if (DisplayHandle != IntPtr.Zero) { + XCloseDisplay(DisplayHandle); + } + + DisplayHandle=display_handle; + + // We need to tell System.Drawing our DisplayHandle. FromHdcInternal has + // been hacked to do this for us. + Graphics.FromHdcInternal (DisplayHandle); + + // query for the render extension so + // we can ignore the spurious + // BadPicture errors that are + // generated by cairo/render. + XQueryExtension (DisplayHandle, "RENDER", + ref render_major_opcode, ref render_first_event, ref render_first_error); + + // Debugging support + if (Environment.GetEnvironmentVariable ("MONO_XSYNC") != null) { + XSynchronize(DisplayHandle, true); + } + + if (Environment.GetEnvironmentVariable ("MONO_XEXCEPTIONS") != null) { + ErrorExceptions = true; + } + + // Generic X11 setup + ScreenNo = XDefaultScreen(DisplayHandle); + RootWindow = XRootWindow(DisplayHandle, ScreenNo); + DefaultColormap = XDefaultColormap(DisplayHandle, ScreenNo); + + // Create the foster parent + // it is important that border_width is kept in synch with the other XCreateWindow calls + FosterParent=XCreateSimpleWindow(DisplayHandle, RootWindow, 0, 0, 1, 1, 0, UIntPtr.Zero, UIntPtr.Zero); + if (FosterParent==IntPtr.Zero) { + Console.WriteLine("XplatUIX11 Constructor failed to create FosterParent"); + } + + DebugHelper.WriteLine ("FosterParent created 0x{0:x}", FosterParent.ToInt32()); + + hwnd = new Hwnd(); + hwnd.Queue = ThreadQueue(Thread.CurrentThread); + hwnd.WholeWindow = FosterParent; + hwnd.ClientWindow = FosterParent; + + // Create a HWND for RootWIndow as well, so our queue doesn't eat the events + hwnd = new Hwnd(); + hwnd.Queue = ThreadQueue(Thread.CurrentThread); + hwnd.whole_window = RootWindow; + hwnd.ClientWindow = RootWindow; + + // For sleeping on the X11 socket + listen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); + IPEndPoint ep = new IPEndPoint(IPAddress.Loopback, 0); + listen.Bind(ep); + listen.Listen(1); + + // To wake up when a timer is ready + network_buffer = new byte[10]; + + wake = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); + wake.Connect(listen.LocalEndPoint); + + // Make this non-blocking, so it doesn't + // deadlock if too many wakes are sent + // before the wake_receive end is polled + wake.Blocking = false; + + wake_receive = listen.Accept(); + + #if __MonoCS__ + pollfds = new Pollfd [2]; + pollfds [0] = new Pollfd (); + pollfds [0].fd = XConnectionNumber (DisplayHandle); + pollfds [0].events = PollEvents.POLLIN; + + pollfds [1] = new Pollfd (); + pollfds [1].fd = wake_receive.Handle.ToInt32 (); + pollfds [1].events = PollEvents.POLLIN; + #endif + + Keyboard = new X11Keyboard(DisplayHandle, FosterParent); + Dnd = new X11Dnd (DisplayHandle, Keyboard); + + DoubleClickInterval = 500; + + HoverState.Interval = 500; + HoverState.Timer = new Timer(); + HoverState.Timer.Enabled = false; + HoverState.Timer.Interval = HoverState.Interval; + HoverState.Timer.Tick += new EventHandler(MouseHover); + HoverState.Size = new Size(4, 4); + HoverState.X = -1; + HoverState.Y = -1; + + ActiveWindow = IntPtr.Zero; + FocusWindow = IntPtr.Zero; + ModalWindows = new Stack(3); + + MouseState = MouseButtons.None; + mouse_position = new Point(0, 0); + + Caret.Timer = new Timer(); + Caret.Timer.Interval = 500; // FIXME - where should this number come from? + Caret.Timer.Tick += new EventHandler(CaretCallback); + + SetupAtoms(); + + // Grab atom changes off the root window to catch certain WM events + XSelectInput(DisplayHandle, RootWindow, new IntPtr ((int) (EventMask.PropertyChangeMask | Keyboard.KeyEventMask))); + + // Handle any upcoming errors + ErrorHandler = new XErrorHandler(HandleError); + XSetErrorHandler(ErrorHandler); + } else { + throw new ArgumentNullException("Display", "Could not open display (X-Server required. Check your DISPLAY environment variable)"); + } + } + #endregion // Internal Methods + + #region Methods + [Conditional ("DriverDebug")] + static void DriverDebug (string format, params object [] args) + { + Console.WriteLine (String.Format (format, args)); + } + + int unixtime() { + TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); + + return (int) t.TotalSeconds; + } + + static void SetupAtoms() { + // make sure this array stays in sync with the statements below + string [] atom_names = new string[] { + "WM_PROTOCOLS", + "WM_DELETE_WINDOW", + "WM_TAKE_FOCUS", + //"_NET_SUPPORTED", + //"_NET_CLIENT_LIST", + //"_NET_NUMBER_OF_DESKTOPS", + "_NET_DESKTOP_GEOMETRY", + //"_NET_DESKTOP_VIEWPORT", + "_NET_CURRENT_DESKTOP", + //"_NET_DESKTOP_NAMES", + "_NET_ACTIVE_WINDOW", + "_NET_WORKAREA", + //"_NET_SUPPORTING_WM_CHECK", + //"_NET_VIRTUAL_ROOTS", + //"_NET_DESKTOP_LAYOUT", + //"_NET_SHOWING_DESKTOP", + //"_NET_CLOSE_WINDOW", + //"_NET_MOVERESIZE_WINDOW", + "_NET_WM_MOVERESIZE", + //"_NET_RESTACK_WINDOW", + //"_NET_REQUEST_FRAME_EXTENTS", + "_NET_WM_NAME", + //"_NET_WM_VISIBLE_NAME", + //"_NET_WM_ICON_NAME", + //"_NET_WM_VISIBLE_ICON_NAME", + //"_NET_WM_DESKTOP", + "_NET_WM_WINDOW_TYPE", + "_NET_WM_STATE", + //"_NET_WM_ALLOWED_ACTIONS", + //"_NET_WM_STRUT", + //"_NET_WM_STRUT_PARTIAL", + //"_NET_WM_ICON_GEOMETRY", + "_NET_WM_ICON", + //"_NET_WM_PID", + //"_NET_WM_HANDLED_ICONS", + "_NET_WM_USER_TIME", + "_NET_FRAME_EXTENTS", + //"_NET_WM_PING", + //"_NET_WM_SYNC_REQUEST", + "_NET_SYSTEM_TRAY_OPCODE", + //"_NET_SYSTEM_TRAY_ORIENTATION", + "_NET_WM_STATE_MAXIMIZED_HORZ", + "_NET_WM_STATE_MAXIMIZED_VERT", + "_NET_WM_STATE_HIDDEN", + "_XEMBED", + "_XEMBED_INFO", + "_MOTIF_WM_HINTS", + "_NET_WM_STATE_SKIP_TASKBAR", + "_NET_WM_STATE_ABOVE", + "_NET_WM_STATE_MODAL", + "_NET_WM_CONTEXT_HELP", + "_NET_WM_WINDOW_OPACITY", + //"_NET_WM_WINDOW_TYPE_DESKTOP", + //"_NET_WM_WINDOW_TYPE_DOCK", + //"_NET_WM_WINDOW_TYPE_TOOLBAR", + //"_NET_WM_WINDOW_TYPE_MENU", + "_NET_WM_WINDOW_TYPE_UTILITY", + // "_NET_WM_WINDOW_TYPE_DIALOG", + //"_NET_WM_WINDOW_TYPE_SPLASH", + "_NET_WM_WINDOW_TYPE_NORMAL", + "CLIPBOARD", + "PRIMARY", + "COMPOUND_TEXT", + "UTF8_STRING", + "UTF16_STRING", + "RICHTEXTFORMAT", + "TARGETS", + "_SWF_AsyncAtom", + "_SWF_PostMessageAtom", + "_SWF_HoverAtom" }; + + IntPtr[] atoms = new IntPtr [atom_names.Length];; + + XInternAtoms (DisplayHandle, atom_names, atom_names.Length, false, atoms); + + int off = 0; + WM_PROTOCOLS = atoms [off++]; + WM_DELETE_WINDOW = atoms [off++]; + WM_TAKE_FOCUS = atoms [off++]; + //_NET_SUPPORTED = atoms [off++]; + //_NET_CLIENT_LIST = atoms [off++]; + //_NET_NUMBER_OF_DESKTOPS = atoms [off++]; + _NET_DESKTOP_GEOMETRY = atoms [off++]; + //_NET_DESKTOP_VIEWPORT = atoms [off++]; + _NET_CURRENT_DESKTOP = atoms [off++]; + //_NET_DESKTOP_NAMES = atoms [off++]; + _NET_ACTIVE_WINDOW = atoms [off++]; + _NET_WORKAREA = atoms [off++]; + //_NET_SUPPORTING_WM_CHECK = atoms [off++]; + //_NET_VIRTUAL_ROOTS = atoms [off++]; + //_NET_DESKTOP_LAYOUT = atoms [off++]; + //_NET_SHOWING_DESKTOP = atoms [off++]; + //_NET_CLOSE_WINDOW = atoms [off++]; + //_NET_MOVERESIZE_WINDOW = atoms [off++]; + _NET_WM_MOVERESIZE = atoms [off++]; + //_NET_RESTACK_WINDOW = atoms [off++]; + //_NET_REQUEST_FRAME_EXTENTS = atoms [off++]; + _NET_WM_NAME = atoms [off++]; + //_NET_WM_VISIBLE_NAME = atoms [off++]; + //_NET_WM_ICON_NAME = atoms [off++]; + //_NET_WM_VISIBLE_ICON_NAME = atoms [off++]; + //_NET_WM_DESKTOP = atoms [off++]; + _NET_WM_WINDOW_TYPE = atoms [off++]; + _NET_WM_STATE = atoms [off++]; + //_NET_WM_ALLOWED_ACTIONS = atoms [off++]; + //_NET_WM_STRUT = atoms [off++]; + //_NET_WM_STRUT_PARTIAL = atoms [off++]; + //_NET_WM_ICON_GEOMETRY = atoms [off++]; + _NET_WM_ICON = atoms [off++]; + //_NET_WM_PID = atoms [off++]; + //_NET_WM_HANDLED_ICONS = atoms [off++]; + _NET_WM_USER_TIME = atoms [off++]; + _NET_FRAME_EXTENTS = atoms [off++]; + //_NET_WM_PING = atoms [off++]; + //_NET_WM_SYNC_REQUEST = atoms [off++]; + _NET_SYSTEM_TRAY_OPCODE = atoms [off++]; + //_NET_SYSTEM_TRAY_ORIENTATION = atoms [off++]; + _NET_WM_STATE_MAXIMIZED_HORZ = atoms [off++]; + _NET_WM_STATE_MAXIMIZED_VERT = atoms [off++]; + _NET_WM_STATE_HIDDEN = atoms [off++]; + _XEMBED = atoms [off++]; + _XEMBED_INFO = atoms [off++]; + _MOTIF_WM_HINTS = atoms [off++]; + _NET_WM_STATE_SKIP_TASKBAR = atoms [off++]; + _NET_WM_STATE_ABOVE = atoms [off++]; + _NET_WM_STATE_MODAL = atoms [off++]; + _NET_WM_CONTEXT_HELP = atoms [off++]; + _NET_WM_WINDOW_OPACITY = atoms [off++]; + //_NET_WM_WINDOW_TYPE_DESKTOP = atoms [off++]; + //_NET_WM_WINDOW_TYPE_DOCK = atoms [off++]; + //_NET_WM_WINDOW_TYPE_TOOLBAR = atoms [off++]; + //_NET_WM_WINDOW_TYPE_MENU = atoms [off++]; + _NET_WM_WINDOW_TYPE_UTILITY = atoms [off++]; + // _NET_WM_WINDOW_TYPE_DIALOG = atoms [off++]; + //_NET_WM_WINDOW_TYPE_SPLASH = atoms [off++]; + _NET_WM_WINDOW_TYPE_NORMAL = atoms [off++]; + CLIPBOARD = atoms [off++]; + PRIMARY = atoms [off++]; + OEMTEXT = atoms [off++]; + UTF8_STRING = atoms [off++]; + UTF16_STRING = atoms [off++]; + RICHTEXTFORMAT = atoms [off++]; + TARGETS = atoms [off++]; + AsyncAtom = atoms [off++]; + PostAtom = atoms [off++]; + HoverState.Atom = atoms [off++]; + + //DIB = (IntPtr)Atom.XA_PIXMAP; + _NET_SYSTEM_TRAY_S = XInternAtom (DisplayHandle, "_NET_SYSTEM_TRAY_S" + ScreenNo.ToString(), false); + } + + void GetSystrayManagerWindow() { + XGrabServer(DisplayHandle); + SystrayMgrWindow = XGetSelectionOwner(DisplayHandle, _NET_SYSTEM_TRAY_S); + XUngrabServer(DisplayHandle); + XFlush(DisplayHandle); + } + + void SendNetWMMessage(IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2) { + SendNetWMMessage (window, message_type, l0, l1, l2, IntPtr.Zero); + } + + void SendNetWMMessage(IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2, IntPtr l3) { + XEvent xev; + + xev = new XEvent(); + xev.ClientMessageEvent.type = XEventName.ClientMessage; + xev.ClientMessageEvent.send_event = true; + xev.ClientMessageEvent.window = window; + xev.ClientMessageEvent.message_type = message_type; + xev.ClientMessageEvent.format = 32; + xev.ClientMessageEvent.ptr1 = l0; + xev.ClientMessageEvent.ptr2 = l1; + xev.ClientMessageEvent.ptr3 = l2; + xev.ClientMessageEvent.ptr4 = l3; + XSendEvent(DisplayHandle, RootWindow, false, new IntPtr ((int) (EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask)), ref xev); + } + + void SendNetClientMessage(IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2) { + XEvent xev; + + xev = new XEvent(); + xev.ClientMessageEvent.type = XEventName.ClientMessage; + xev.ClientMessageEvent.send_event = true; + xev.ClientMessageEvent.window = window; + xev.ClientMessageEvent.message_type = message_type; + xev.ClientMessageEvent.format = 32; + xev.ClientMessageEvent.ptr1 = l0; + xev.ClientMessageEvent.ptr2 = l1; + xev.ClientMessageEvent.ptr3 = l2; + XSendEvent(DisplayHandle, window, false, new IntPtr ((int)EventMask.NoEventMask), ref xev); + } + + // For WM_LBUTTONDOWN, WM_MBUTTONDOWN, WM_RBUTTONDOWN, WM_XBUTTONDOWN + // WM_CREATE and WM_DESTROY causes + void SendParentNotify(IntPtr child, Msg cause, int x, int y) + { + Hwnd hwnd; + + if (child == IntPtr.Zero) { + return; + } + + hwnd = Hwnd.GetObjectFromWindow (child); + + if (hwnd == null) { + return; + } + + if (hwnd.Handle == IntPtr.Zero) { + return; + } + + if (ExStyleSet ((int) hwnd.initial_ex_style, WindowExStyles.WS_EX_NOPARENTNOTIFY)) { + return; + } + + if (hwnd.Parent == null) { + return; + } + + if (hwnd.Parent.Handle == IntPtr.Zero) { + return; + } + + if (cause == Msg.WM_CREATE || cause == Msg.WM_DESTROY) { + SendMessage(hwnd.Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), child); + } else { + SendMessage(hwnd.Parent.Handle, Msg.WM_PARENTNOTIFY, Widget.MakeParam((int)cause, 0), Widget.MakeParam(x, y)); + } + + SendParentNotify (hwnd.Parent.Handle, cause, x, y); + } + + bool StyleSet (int s, WindowStyles ws) + { + return (s & (int)ws) == (int)ws; + } + + bool ExStyleSet (int ex, WindowExStyles exws) + { + return (ex & (int)exws) == (int)exws; + } + + internal static Rectangle TranslateClientRectangleToXClientRectangle (Hwnd hwnd) + { + return TranslateClientRectangleToXClientRectangle (hwnd, Widget.FromHandle (hwnd.Handle)); + } + + internal static Rectangle TranslateClientRectangleToXClientRectangle (Hwnd hwnd, Widget ctrl) + { + /* + * If this is a form with no window manager, X is handling all the border and caption painting + * so remove that from the area (since the area we set of the window here is the part of the window + * we're painting in only) + */ + Rectangle rect = hwnd.ClientRect; + Form form = ctrl as Form; + CreateParams cp = null; + + if (form != null) + cp = form.GetCreateParams (); + + if (form != null && (form.window_manager == null && !cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW))) { + Hwnd.Borders borders = Hwnd.GetBorders (cp, null); + Rectangle xrect = rect; + + xrect.Y -= borders.top; + xrect.X -= borders.left; + xrect.Width += borders.left + borders.right; + xrect.Height += borders.top + borders.bottom; + + rect = xrect; + } + + if (rect.Width < 1 || rect.Height < 1) { + rect.Width = 1; + rect.Height = 1; + rect.X = -5; + rect.Y = -5; + } + + return rect; + } + + internal static Size TranslateWindowSizeToXWindowSize (CreateParams cp) + { + return TranslateWindowSizeToXWindowSize (cp, new Size (cp.Width, cp.Height)); + } + + internal static Size TranslateWindowSizeToXWindowSize (CreateParams cp, Size size) + { + /* + * If this is a form with no window manager, X is handling all the border and caption painting + * so remove that from the area (since the area we set of the window here is the part of the window + * we're painting in only) + */ + Form form = cp.control as Form; + if (form != null && (form.window_manager == null && !cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW))) { + Hwnd.Borders borders = Hwnd.GetBorders (cp, null); + Size xrect = size; + + xrect.Width -= borders.left + borders.right; + xrect.Height -= borders.top + borders.bottom; + + size = xrect; + } + if (size.Height == 0) + size.Height = 1; + if (size.Width == 0) + size.Width = 1; + return size; + } + + internal static Size TranslateXWindowSizeToWindowSize (CreateParams cp, int xWidth, int xHeight) + { + /* + * If this is a form with no window manager, X is handling all the border and caption painting + * so remove that from the area (since the area we set of the window here is the part of the window + * we're painting in only) + */ + Size rect = new Size (xWidth, xHeight); + Form form = cp.control as Form; + if (form != null && (form.window_manager == null && !cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW))) { + Hwnd.Borders borders = Hwnd.GetBorders (cp, null); + Size xrect = rect; + + xrect.Width += borders.left + borders.right; + xrect.Height += borders.top + borders.bottom; + + rect = xrect; + } + return rect; + } + + internal static Point GetTopLevelWindowLocation (Hwnd hwnd) + { + IntPtr dummy; + int x, y; + Hwnd.Borders frame; + + XTranslateCoordinates (DisplayHandle, hwnd.whole_window, RootWindow, 0, 0, out x, out y, out dummy); + frame = FrameExtents (hwnd.whole_window); + + x -= frame.left; + y -= frame.top; + + return new Point (x, y); + } + + void DeriveStyles(int Style, int ExStyle, out FormBorderStyle border_style, out bool border_static, out TitleStyle title_style, out int caption_height, out int tool_caption_height) { + + caption_height = 0; + tool_caption_height = 19; + border_static = false; + + if (StyleSet (Style, WindowStyles.WS_CHILD)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) { + border_style = FormBorderStyle.Fixed3D; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (!StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.None; + } else { + border_style = FormBorderStyle.FixedSingle; + } + title_style = TitleStyle.None; + + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + caption_height = 19; + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + title_style = TitleStyle.Tool; + } else { + title_style = TitleStyle.Normal; + } + } + + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_MDICHILD)) { + caption_height = 19; + + if (StyleSet (Style, WindowStyles.WS_OVERLAPPEDWINDOW) || + ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + border_style = (FormBorderStyle) 0xFFFF; + } else { + border_style = FormBorderStyle.None; + } + } + + } else { + title_style = TitleStyle.None; + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + title_style = TitleStyle.Tool; + } else { + title_style = TitleStyle.Normal; + } + } + + border_style = FormBorderStyle.None; + + if (StyleSet (Style, WindowStyles.WS_THICKFRAME)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + border_style = FormBorderStyle.SizableToolWindow; + } else { + border_style = FormBorderStyle.Sizable; + } + } else { + if (StyleSet (Style, WindowStyles.WS_CAPTION)) { + if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_CLIENTEDGE)) { + border_style = FormBorderStyle.Fixed3D; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_STATICEDGE)) { + border_style = FormBorderStyle.Fixed3D; + border_static = true; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_DLGMODALFRAME)) { + border_style = FormBorderStyle.FixedDialog; + } else if (ExStyleSet (ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + border_style = FormBorderStyle.FixedToolWindow; + } else if (StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.FixedSingle; + } + } else { + if (StyleSet (Style, WindowStyles.WS_BORDER)) { + border_style = FormBorderStyle.FixedSingle; + } + } + } + } + } + + void SetHwndStyles(Hwnd hwnd, CreateParams cp) { + DeriveStyles(cp.Style, cp.ExStyle, out hwnd.border_style, out hwnd.border_static, out hwnd.title_style, out hwnd.caption_height, out hwnd.tool_caption_height); + } + + void SetWMStyles(Hwnd hwnd, CreateParams cp) { + MotifWmHints mwmHints; + MotifFunctions functions; + MotifDecorations decorations; + int[] atoms; + int atom_count; + Rectangle client_rect; + Form form; + IntPtr window_type; + bool hide_from_taskbar; + IntPtr transient_for_parent; + + // Windows we manage ourselves don't need WM window styles. + if (cp.HasWindowManager && !cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) { + return; + } + + atoms = new int[8]; + mwmHints = new MotifWmHints(); + functions = 0; + decorations = 0; + window_type = _NET_WM_WINDOW_TYPE_NORMAL; + transient_for_parent = IntPtr.Zero; + + mwmHints.flags = (IntPtr)(MotifFlags.Functions | MotifFlags.Decorations); + mwmHints.functions = (IntPtr)0; + mwmHints.decorations = (IntPtr)0; + + form = cp.control as Form; + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + /* tool windows get no window manager + decorations. + */ + + /* just because the window doesn't get any decorations doesn't + mean we should disable the functions. for instance, without + MotifFunctions.Maximize, changing the windowstate to Maximized + is ignored by metacity. */ + functions |= MotifFunctions.Move | MotifFunctions.Resize | MotifFunctions.Minimize | MotifFunctions.Maximize; + } else if (form != null && form.FormBorderStyle == FormBorderStyle.None) { + /* allow borderless window to be maximized */ + functions |= MotifFunctions.All | MotifFunctions.Resize; + } else { + if (StyleSet (cp.Style, WindowStyles.WS_CAPTION)) { + functions |= MotifFunctions.Move; + decorations |= MotifDecorations.Title | MotifDecorations.Menu; + } + + if (StyleSet (cp.Style, WindowStyles.WS_THICKFRAME)) { + functions |= MotifFunctions.Move | MotifFunctions.Resize; + decorations |= MotifDecorations.Border | MotifDecorations.ResizeH; + } + + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZEBOX)) { + functions |= MotifFunctions.Minimize; + decorations |= MotifDecorations.Minimize; + } + + if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZEBOX)) { + functions |= MotifFunctions.Maximize; + decorations |= MotifDecorations.Maximize; + } + + if (StyleSet (cp.Style, WindowStyles.WS_SIZEBOX)) { + functions |= MotifFunctions.Resize; + decorations |= MotifDecorations.ResizeH; + } + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_DLGMODALFRAME)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_BORDER)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_DLGFRAME)) { + decorations |= MotifDecorations.Border; + } + + if (StyleSet (cp.Style, WindowStyles.WS_SYSMENU)) { + functions |= MotifFunctions.Close; + } + else { + functions &= ~(MotifFunctions.Maximize | MotifFunctions.Minimize | MotifFunctions.Close); + decorations &= ~(MotifDecorations.Menu | MotifDecorations.Maximize | MotifDecorations.Minimize); + if (cp.Caption == "") { + functions &= ~MotifFunctions.Move; + decorations &= ~(MotifDecorations.Title | MotifDecorations.ResizeH); + } + } + } + + if ((functions & MotifFunctions.Resize) == 0) { + hwnd.fixed_size = true; + Rectangle fixed_rectangle = new Rectangle (cp.X, cp.Y, cp.Width, cp.Height); + SetWindowMinMax(hwnd.Handle, fixed_rectangle, fixed_rectangle.Size, fixed_rectangle.Size, cp); + } else { + hwnd.fixed_size = false; + } + + mwmHints.functions = (IntPtr)functions; + mwmHints.decorations = (IntPtr)decorations; + + DriverDebug ("SetWMStyles ({0}, {1}) functions = {2}, decorations = {3}", hwnd, cp, functions, decorations); + + if (cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) { + // needed! map toolwindows to _NET_WM_WINDOW_TYPE_UTILITY to make newer metacity versions happy + // and get those windows in front of their parents + window_type = _NET_WM_WINDOW_TYPE_UTILITY; + } else { + window_type = _NET_WM_WINDOW_TYPE_NORMAL; + } + + if (!cp.IsSet (WindowExStyles.WS_EX_APPWINDOW)) { + hide_from_taskbar = true; + } else if (cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW) && form != null && form.Parent != null && !form.ShowInTaskbar) { + hide_from_taskbar = true; + } else { + hide_from_taskbar = false; + } + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + if (form != null && !hwnd.reparented) { + if (form.Owner != null && form.Owner.Handle != IntPtr.Zero) { + Hwnd owner_hwnd = Hwnd.ObjectFromHandle (form.Owner.Handle); + if (owner_hwnd != null) + transient_for_parent = owner_hwnd.whole_window; + } + } + } + if (StyleSet (cp.Style, WindowStyles.WS_POPUP) && (hwnd.parent != null) && (hwnd.parent.whole_window != IntPtr.Zero)) { + transient_for_parent = hwnd.parent.whole_window; + } + + FormWindowState current_state = GetWindowState (hwnd.Handle); + if (current_state == (FormWindowState)(-1)) + current_state = FormWindowState.Normal; + + client_rect = TranslateClientRectangleToXClientRectangle (hwnd); + + lock (XlibLock) { + atom_count = 0; + + atoms [0] = window_type.ToInt32 (); + XChangeProperty (DisplayHandle, hwnd.whole_window, _NET_WM_WINDOW_TYPE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1); + + XChangeProperty(DisplayHandle, hwnd.whole_window, _MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32, PropertyMode.Replace, ref mwmHints, 5); + + if (transient_for_parent != IntPtr.Zero) { + XSetTransientForHint (DisplayHandle, hwnd.whole_window, transient_for_parent); + } + + MoveResizeWindow(DisplayHandle, hwnd.client_window, client_rect.X, client_rect.Y, client_rect.Width, client_rect.Height); + + if (hide_from_taskbar) { + /* this line keeps the window from showing up in gnome's taskbar */ + atoms[atom_count++] = _NET_WM_STATE_SKIP_TASKBAR.ToInt32(); + } + /* we need to add these atoms in the + * event we're maximized, since we're + * replacing the existing + * _NET_WM_STATE here. If we don't + * add them, future calls to + * GetWindowState will return Normal + * for a window which is maximized. */ + if (current_state == FormWindowState.Maximized) { + atoms[atom_count++] = _NET_WM_STATE_MAXIMIZED_HORZ.ToInt32(); + atoms[atom_count++] = _NET_WM_STATE_MAXIMIZED_VERT.ToInt32(); + } + + if (form != null && form.Modal) { + atoms[atom_count++] = _NET_WM_STATE_MODAL.ToInt32 (); + } + + XChangeProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, atom_count); + + atom_count = 0; + IntPtr[] atom_ptrs = new IntPtr[2]; + atom_ptrs[atom_count++] = WM_DELETE_WINDOW; + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_CONTEXTHELP)) { + atom_ptrs[atom_count++] = _NET_WM_CONTEXT_HELP; + } + + XSetWMProtocols(DisplayHandle, hwnd.whole_window, atom_ptrs, atom_count); + } + } + + void SetIcon(Hwnd hwnd, Icon icon) + { + if (icon == null) { + // XXX + + // This really needs to do whatever it + // takes to remove the window manager + // menu, not just delete the ICON + // property. This will cause metacity + // to use the "no icon set" icon, and + // we'll still have an icon. + XDeleteProperty (DisplayHandle, hwnd.whole_window, _NET_WM_ICON); + } + else { + Bitmap bitmap; + int size; + IntPtr[] data; + int index; + + bitmap = icon.ToBitmap(); + index = 0; + size = bitmap.Width * bitmap.Height + 2; + data = new IntPtr[size]; + + data[index++] = (IntPtr)bitmap.Width; + data[index++] = (IntPtr)bitmap.Height; + + for (int y = 0; y < bitmap.Height; y++) { + for (int x = 0; x < bitmap.Width; x++) { + data[index++] = (IntPtr)bitmap.GetPixel (x, y).ToArgb (); + } + } + + XChangeProperty (DisplayHandle, hwnd.whole_window, + _NET_WM_ICON, (IntPtr)Atom.XA_CARDINAL, 32, + PropertyMode.Replace, data, size); + } + } + + void WakeupMain () { + try { + wake.Send (new byte [] { 0xFF }); + } catch (SocketException ex) { + if (ex.SocketErrorCode != SocketError.WouldBlock) { + throw; + } + } + } + + XEventQueue ThreadQueue(Thread thread) { + XEventQueue queue; + + queue = (XEventQueue)MessageQueues[thread]; + if (queue == null) { + queue = new XEventQueue(thread); + MessageQueues[thread] = queue; + } + + return queue; + } + + void TranslatePropertyToClipboard(IntPtr property) { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + Clipboard.Item = null; + + XGetWindowProperty(DisplayHandle, FosterParent, property, IntPtr.Zero, new IntPtr (0x7fffffff), true, (IntPtr)Atom.AnyPropertyType, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if ((long)nitems > 0) { + if (property == (IntPtr)Atom.XA_STRING) { + // Xamarin-5116: PtrToStringAnsi expects to get UTF-8, but we might have + // Latin-1 instead, in which case it will return null. + var s = Marshal.PtrToStringAnsi (prop); + if (string.IsNullOrEmpty (s)) { + var sb = new StringBuilder (); + for (int i = 0; i < (int)nitems; i++) { + var b = Marshal.ReadByte (prop, i); + sb.Append ((char)b); + } + s = sb.ToString (); + } + // Some X managers/apps pass unicode chars as escaped strings, so + // we may need to unescape them. + Clipboard.Item = UnescapeUnicodeFromAnsi (s); + } else if (property == (IntPtr)Atom.XA_BITMAP) { + // FIXME - convert bitmap to image + } else if (property == (IntPtr)Atom.XA_PIXMAP) { + // FIXME - convert pixmap to image + } else if (property == OEMTEXT) { + Clipboard.Item = UnescapeUnicodeFromAnsi (Marshal.PtrToStringAnsi(prop)); + } else if (property == UTF8_STRING) { + byte [] buffer = new byte [(int)nitems]; + for (int i = 0; i < (int)nitems; i++) + buffer [i] = Marshal.ReadByte (prop, i); + Clipboard.Item = Encoding.UTF8.GetString (buffer); + } else if (property == UTF16_STRING) { + byte [] buffer = new byte [(int)nitems]; + for (int i = 0; i < (int)nitems; i++) + buffer [i] = Marshal.ReadByte (prop, i); + Clipboard.Item = Encoding.Unicode.GetString (buffer); + } else if (property == RICHTEXTFORMAT) + Clipboard.Item = Marshal.PtrToStringAnsi(prop); + else if (DataFormats.ContainsFormat (property.ToInt32 ())) { + if (DataFormats.GetFormat (property.ToInt32 ()).is_serializable) { + MemoryStream memory_stream = new MemoryStream ((int)nitems); + for (int i = 0; i < (int)nitems; i++) + memory_stream.WriteByte (Marshal.ReadByte (prop, i)); + + memory_stream.Position = 0; + BinaryFormatter formatter = new BinaryFormatter (); + Clipboard.Item = formatter.Deserialize (memory_stream); + memory_stream.Close (); + } + } + + XFree(prop); + } + } + + string UnescapeUnicodeFromAnsi (string value) + { + if (value == null || value.IndexOf ("\\u") == -1) + return value; + + StringBuilder sb = new StringBuilder (value.Length); + int start, pos; + + start = pos = 0; + while (start < value.Length) { + pos = value.IndexOf ("\\u", start); + if (pos == -1) + break; + + sb.Append (value, start, pos - start); + pos += 2; + start = pos; + + int length = 0; + while (pos < value.Length && length < 4) { + if (!ValidHexDigit (value [pos])) + break; + length++; + pos++; + } + + int res; + if (!Int32.TryParse (value.Substring (start, length), System.Globalization.NumberStyles.HexNumber, + null, out res)) + return value; // Error, return the unescaped original value. + + sb.Append ((char)res); + start = pos; + } + + // Append any remaining data. + if (start < value.Length) + sb.Append (value, start, value.Length - start); + + return sb.ToString (); + } + + private static bool ValidHexDigit (char e) + { + return Char.IsDigit (e) || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f'); + } + + void AddExpose (Hwnd hwnd, bool client, int x, int y, int width, int height) { + // Don't waste time + if ((hwnd == null) || (x > hwnd.Width) || (y > hwnd.Height) || ((x + width) < 0) || ((y + height) < 0)) { + return; + } + + // Keep the invalid area as small as needed + if ((x + width) > hwnd.width) { + width = hwnd.width - x; + } + + if ((y + height) > hwnd.height) { + height = hwnd.height - y; + } + + if (client) { + hwnd.AddInvalidArea(x, y, width, height); + if (!hwnd.expose_pending) { + if (!hwnd.nc_expose_pending) { + hwnd.Queue.Paint.Enqueue(hwnd); + } + hwnd.expose_pending = true; + } + } else { + hwnd.AddNcInvalidArea (x, y, width, height); + + if (!hwnd.nc_expose_pending) { + if (!hwnd.expose_pending) { + hwnd.Queue.Paint.Enqueue(hwnd); + } + hwnd.nc_expose_pending = true; + } + } + } + + static Hwnd.Borders FrameExtents (IntPtr window) + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + Hwnd.Borders rect = new Hwnd.Borders (); + + XGetWindowProperty (DisplayHandle, window, _NET_FRAME_EXTENTS, IntPtr.Zero, new IntPtr (16), false, (IntPtr)Atom.XA_CARDINAL, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if (prop != IntPtr.Zero) { + if (nitems.ToInt32 () == 4) { + rect.left = Marshal.ReadInt32 (prop, 0); + rect.right = Marshal.ReadInt32 (prop, IntPtr.Size); + rect.top = Marshal.ReadInt32 (prop, 2 * IntPtr.Size); + rect.bottom = Marshal.ReadInt32 (prop, 3 * IntPtr.Size); + } + XFree (prop); + } + + return rect; + } + + void AddConfigureNotify (XEvent xevent) { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow(xevent.ConfigureEvent.window); + + // Don't waste time + if (hwnd == null || hwnd.zombie) { + return; + } + if ((xevent.ConfigureEvent.window == hwnd.whole_window)/* && (xevent.ConfigureEvent.window == xevent.ConfigureEvent.xevent)*/) { + if (hwnd.parent == null) { + // The location given by the event is not reliable between different wm's, + // so use an alternative way of getting it. + Point location = GetTopLevelWindowLocation (hwnd); + hwnd.x = location.X; + hwnd.y = location.Y; + } + + // XXX this sucks. this isn't thread safe + Widget ctrl = Widget.FromHandle (hwnd.Handle); + Size TranslatedSize; + if (ctrl != null) { + TranslatedSize = TranslateXWindowSizeToWindowSize (ctrl.GetCreateParams (), xevent.ConfigureEvent.width, xevent.ConfigureEvent.height); + } else { + TranslatedSize = new Size (xevent.ConfigureEvent.width, xevent.ConfigureEvent.height); + } + hwnd.width = TranslatedSize.Width; + hwnd.height = TranslatedSize.Height; + hwnd.ClientRect = Rectangle.Empty; + + DriverDebug ("AddConfigureNotify (hwnd.Handle = {1}, final hwnd.rect = {0}, reported rect={2})", + new Rectangle (hwnd.x, hwnd.y, hwnd.width, hwnd.height), hwnd.Handle, + new Rectangle (xevent.ConfigureEvent.x, xevent.ConfigureEvent.y, xevent.ConfigureEvent.width, xevent.ConfigureEvent.width)); + lock (hwnd.configure_lock) { + if (!hwnd.configure_pending) { + hwnd.Queue.EnqueueLocked (xevent); + hwnd.configure_pending = true; + } + } + } + // We drop configure events for Client windows + } + + void ShowCaret() { + if ((Caret.gc == IntPtr.Zero) || Caret.On) { + return; + } + Caret.On = true; + + lock (XlibLock) { + XDrawLine(DisplayHandle, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + } + + void HideCaret() { + if ((Caret.gc == IntPtr.Zero) || !Caret.On) { + return; + } + Caret.On = false; + + lock (XlibLock) { + XDrawLine(DisplayHandle, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + } + + int NextTimeout (ArrayList timers, DateTime now) { + int timeout = 0; + + foreach (Timer timer in timers) { + int next = (int) (timer.Expires - now).TotalMilliseconds; + if (next < 0) { + return 0; // Have a timer that has already expired + } + + if (next < timeout) { + timeout = next; + } + } + if (timeout < Timer.Minimum) { + timeout = Timer.Minimum; + } + + if (timeout > 1000) + timeout = 1000; + return timeout; + } + + void CheckTimers (ArrayList timers, DateTime now) { + int count; + + count = timers.Count; + + if (count == 0) + return; + + for (int i = 0; i < timers.Count; i++) { + Timer timer; + + timer = (Timer) timers [i]; + + if (timer.Enabled && timer.Expires <= now && !timer.Busy) { + // Timer ticks: + // - Before MainForm.OnLoad if DoEvents () is called. + // - After MainForm.OnLoad if not. + // + if (in_doevents || + (Application.MWFThread.Current.Context != null && + (Application.MWFThread.Current.Context.MainForm == null || + Application.MWFThread.Current.Context.MainForm.IsLoaded))) { + timer.Busy = true; + timer.Update (now); + timer.FireTick (); + timer.Busy = false; + } + } + } + } + + void WaitForHwndMessage (Hwnd hwnd, Msg message) { + WaitForHwndMessage (hwnd, message, false); + + } + + void WaitForHwndMessage (Hwnd hwnd, Msg message, bool process) { + MSG msg = new MSG (); + XEventQueue queue; + + queue = ThreadQueue(Thread.CurrentThread); + + queue.DispatchIdle = false; + + bool done = false; + string key = hwnd.Handle + ":" + message; + if (!messageHold.ContainsKey (key)) + messageHold.Add (key, 1); + else + messageHold[key] = ((int)messageHold[key]) + 1; + + + do { + + DebugHelper.WriteLine ("Waiting for message " + message + " on hwnd " + String.Format("0x{0:x}", hwnd.Handle.ToInt32 ())); + DebugHelper.Indent (); + + if (PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) { + if ((Msg)msg.message == Msg.WM_QUIT) { + PostQuitMessage (0); + done = true; + } + else { + + DebugHelper.WriteLine ("PeekMessage got " + msg); + + if (msg.hwnd == hwnd.Handle) { + if ((Msg)msg.message == message) { + if (process) { + TranslateMessage (ref msg); + DispatchMessage (ref msg); + } + break; + } + else if ((Msg)msg.message == Msg.WM_DESTROY) + done = true; + } + + TranslateMessage (ref msg); + DispatchMessage (ref msg); + } + } + + done = !messageHold.ContainsKey (key) || ((int)messageHold[key] < 1) || done; + } while (!done); + + messageHold.Remove (key); + + DebugHelper.Unindent (); + DebugHelper.WriteLine ("Finished waiting for " + key); + + queue.DispatchIdle = true; + + } + + void MapWindow(Hwnd hwnd, WindowType windows) { + if (!hwnd.mapped) { + Form f = Widget.FromHandle(hwnd.Handle) as Form; + if (f != null) { + if (f.WindowState == FormWindowState.Normal) { + f.waiting_showwindow = true; + SendMessage(hwnd.Handle, Msg.WM_SHOWWINDOW, (IntPtr)1, IntPtr.Zero); + } + } + + // it's possible that our Hwnd is no + // longer valid after making that + // SendMessage call, so check here. + if (hwnd.zombie) + return; + + if (hwnd.topmost) { + // Most window managers will respect the _NET_WM_STATE property. + // If not, use XMapRaised to map the window at the top level as + // a last ditch effort. + if ((windows & WindowType.Whole) != 0) { + XMapRaised(DisplayHandle, hwnd.whole_window); + } + if ((windows & WindowType.Client) != 0) { + XMapRaised(DisplayHandle, hwnd.client_window); + } + } else { + if ((windows & WindowType.Whole) != 0) { + XMapWindow(DisplayHandle, hwnd.whole_window); + } + if ((windows & WindowType.Client) != 0) { + XMapWindow(DisplayHandle, hwnd.client_window); + } + } + + hwnd.mapped = true; + + if (f != null) { + if (f.waiting_showwindow) { + WaitForHwndMessage (hwnd, Msg.WM_SHOWWINDOW); + CreateParams cp = f.GetCreateParams(); + if (!ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_MDICHILD) && + !StyleSet (cp.Style, WindowStyles.WS_CHILD)) { + WaitForHwndMessage (hwnd, Msg.WM_ACTIVATE, true); + } + } + } + } + } + + void UnmapWindow(Hwnd hwnd, WindowType windows) { + if (hwnd.mapped) { + Form f = null; + if (Widget.FromHandle(hwnd.Handle) is Form) { + f = Widget.FromHandle(hwnd.Handle) as Form; + if (f.WindowState == FormWindowState.Normal) { + f.waiting_showwindow = true; + SendMessage(hwnd.Handle, Msg.WM_SHOWWINDOW, IntPtr.Zero, IntPtr.Zero); + } + } + + // it's possible that our Hwnd is no + // longer valid after making that + // SendMessage call, so check here. + // FIXME: it is likely wrong, as it has already sent WM_SHOWWINDOW + if (hwnd.zombie) + return; + + if ((windows & WindowType.Client) != 0) { + XUnmapWindow(DisplayHandle, hwnd.client_window); + } + if ((windows & WindowType.Whole) != 0) { + XUnmapWindow(DisplayHandle, hwnd.whole_window); + } + + hwnd.mapped = false; + + if (f != null) { + if (f.waiting_showwindow) { + WaitForHwndMessage (hwnd, Msg.WM_SHOWWINDOW); + CreateParams cp = f.GetCreateParams(); + if (!ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_MDICHILD) && + !StyleSet (cp.Style, WindowStyles.WS_CHILD)) { + WaitForHwndMessage (hwnd, Msg.WM_ACTIVATE, true); + } + } + } + } + } + + void UpdateMessageQueue (XEventQueue queue) { + UpdateMessageQueue(queue, true); + } + + void UpdateMessageQueue (XEventQueue queue, bool allowIdle) { + DateTime now; + int pending; + Hwnd hwnd; + + now = DateTime.UtcNow; + + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + + if (pending == 0 && allowIdle) { + if ((queue == null || queue.DispatchIdle) && Idle != null) { + Idle (this, EventArgs.Empty); + } + + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + } + + if (pending == 0) { + int timeout = 0; + + if (queue != null) { + if (queue.Paint.Count > 0) + return; + + timeout = NextTimeout (queue.timer_list, now); + } + + if (timeout > 0) { + #if __MonoCS__ + int length = pollfds.Length - 1; + lock (wake_waiting_lock) { + if (wake_waiting == false) { + length ++; + wake_waiting = true; + } + } + + Syscall.poll (pollfds, (uint)length, timeout); + // Clean out buffer, so we're not busy-looping on the same data + if (length == pollfds.Length) { + if (pollfds[1].revents != 0) + wake_receive.Receive(network_buffer, 0, 1, SocketFlags.None); + lock (wake_waiting_lock) { + wake_waiting = false; + } + } + #endif + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + } + } + + if (queue != null) + CheckTimers (queue.timer_list, now); + + while (true) { + XEvent xevent = new XEvent (); + + lock (XlibLock) { + if (XPending (DisplayHandle) == 0) + break; + + XNextEvent (DisplayHandle, ref xevent); + + if (xevent.AnyEvent.type == XEventName.KeyPress || + xevent.AnyEvent.type == XEventName.KeyRelease) { + // PreFilter() handles "shift key state updates. + Keyboard.PreFilter (xevent); + if (XFilterEvent (ref xevent, Keyboard.ClientWindow)) { + // probably here we could raise WM_IME_KEYDOWN and + // WM_IME_KEYUP, but I'm not sure it is worthy. + continue; + } + } + else if (XFilterEvent (ref xevent, IntPtr.Zero)) + continue; + } + + hwnd = Hwnd.GetObjectFromWindow(xevent.AnyEvent.window); + if (hwnd == null) + continue; + + DebugHelper.WriteLine ("UpdateMessageQueue got Event: " + xevent.ToString ()); + + switch (xevent.type) { + case XEventName.Expose: + AddExpose (hwnd, xevent.ExposeEvent.window == hwnd.ClientWindow, xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); + break; + + case XEventName.SelectionClear: { + // Should we do something? + break; + } + + case XEventName.SelectionRequest: { + if (Dnd.HandleSelectionRequestEvent (ref xevent)) + break; + XEvent sel_event; + + sel_event = new XEvent(); + sel_event.SelectionEvent.type = XEventName.SelectionNotify; + sel_event.SelectionEvent.send_event = true; + sel_event.SelectionEvent.display = DisplayHandle; + sel_event.SelectionEvent.selection = xevent.SelectionRequestEvent.selection; + sel_event.SelectionEvent.target = xevent.SelectionRequestEvent.target; + sel_event.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor; + sel_event.SelectionEvent.time = xevent.SelectionRequestEvent.time; + sel_event.SelectionEvent.property = IntPtr.Zero; + + IntPtr format_atom = xevent.SelectionRequestEvent.target; + + // Seems that some apps support asking for supported types + if (format_atom == TARGETS) { + int[] atoms; + int atom_count; + + atoms = new int[5]; + atom_count = 0; + + if (Clipboard.IsSourceText) { + atoms[atom_count++] = (int)Atom.XA_STRING; + atoms[atom_count++] = (int)OEMTEXT; + atoms[atom_count++] = (int)UTF8_STRING; + atoms[atom_count++] = (int)UTF16_STRING; + atoms[atom_count++] = (int)RICHTEXTFORMAT; + } else if (Clipboard.IsSourceImage) { + atoms[atom_count++] = (int)Atom.XA_PIXMAP; + atoms[atom_count++] = (int)Atom.XA_BITMAP; + } else { + // FIXME - handle other types + } + + XChangeProperty(DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, + (IntPtr)xevent.SelectionRequestEvent.target, 32, PropertyMode.Replace, atoms, atom_count); + sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; + } else if (format_atom == (IntPtr)RICHTEXTFORMAT) { + string rtf_text = Clipboard.GetRtfText (); + if (rtf_text != null) { + // The RTF spec mentions that ascii is enough to contain it + Byte [] bytes = Encoding.ASCII.GetBytes (rtf_text); + int buflen = bytes.Length; + IntPtr buffer = Marshal.AllocHGlobal (buflen); + + for (int i = 0; i < buflen; i++) + Marshal.WriteByte (buffer, i, bytes[i]); + + XChangeProperty(DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, + (IntPtr)xevent.SelectionRequestEvent.target, 8, PropertyMode.Replace, buffer, buflen); + sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; + Marshal.FreeHGlobal(buffer); + } + } else if (Clipboard.IsSourceText && + (format_atom == (IntPtr)Atom.XA_STRING + || format_atom == OEMTEXT + || format_atom == UTF16_STRING + || format_atom == UTF8_STRING)) { + IntPtr buffer = IntPtr.Zero; + int buflen; + Encoding encoding = null; + + buflen = 0; + + // Select an encoding depending on the target + IntPtr target_atom = xevent.SelectionRequestEvent.target; + if (target_atom == (IntPtr)Atom.XA_STRING || target_atom == OEMTEXT) + // FIXME - EOMTEXT should encode into ISO2022 + encoding = Encoding.ASCII; + else if (target_atom == UTF16_STRING) + encoding = Encoding.Unicode; + else if (target_atom == UTF8_STRING) + encoding = Encoding.UTF8; + + Byte [] bytes; + + bytes = encoding.GetBytes (Clipboard.GetPlainText ()); + buffer = Marshal.AllocHGlobal (bytes.Length); + buflen = bytes.Length; + + for (int i = 0; i < buflen; i++) + Marshal.WriteByte (buffer, i, bytes [i]); + + if (buffer != IntPtr.Zero) { + XChangeProperty(DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, (IntPtr)xevent.SelectionRequestEvent.target, 8, PropertyMode.Replace, buffer, buflen); + sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; + Marshal.FreeHGlobal(buffer); + } + } else if (Clipboard.GetSource (format_atom.ToInt32 ()) != null) { // check if we have an available value of this format + if (DataFormats.GetFormat (format_atom.ToInt32 ()).is_serializable) { + object serializable = Clipboard.GetSource (format_atom.ToInt32 ()); + + BinaryFormatter formatter = new BinaryFormatter (); + MemoryStream memory_stream = new MemoryStream (); + formatter.Serialize (memory_stream, serializable); + + int buflen = (int)memory_stream.Length; + IntPtr buffer = Marshal.AllocHGlobal (buflen); + memory_stream.Position = 0; + for (int i = 0; i < buflen; i++) + Marshal.WriteByte (buffer, i, (byte)memory_stream.ReadByte ()); + memory_stream.Close (); + + XChangeProperty (DisplayHandle, xevent.SelectionRequestEvent.requestor, (IntPtr)xevent.SelectionRequestEvent.property, (IntPtr)xevent.SelectionRequestEvent.target, + 8, PropertyMode.Replace, buffer, buflen); + sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; + Marshal.FreeHGlobal (buffer); + } + + } else if (Clipboard.IsSourceImage) { + if (xevent.SelectionEvent.target == (IntPtr)Atom.XA_PIXMAP) { + // FIXME - convert image and store as property + } else if (xevent.SelectionEvent.target == (IntPtr)Atom.XA_PIXMAP) { + // FIXME - convert image and store as property + } + } + + XSendEvent(DisplayHandle, xevent.SelectionRequestEvent.requestor, false, new IntPtr ((int)EventMask.NoEventMask), ref sel_event); + break; + } + + case XEventName.SelectionNotify: { + if (Clipboard.Enumerating) { + Clipboard.Enumerating = false; + if (xevent.SelectionEvent.property != IntPtr.Zero) { + XDeleteProperty(DisplayHandle, FosterParent, (IntPtr)xevent.SelectionEvent.property); + if (!Clipboard.Formats.Contains(xevent.SelectionEvent.property)) { + Clipboard.Formats.Add(xevent.SelectionEvent.property); + DriverDebug("Got supported clipboard atom format: {0}", xevent.SelectionEvent.property); + } + } + } else if (Clipboard.Retrieving) { + Clipboard.Retrieving = false; + if (xevent.SelectionEvent.property != IntPtr.Zero) { + TranslatePropertyToClipboard(xevent.SelectionEvent.property); + } else { + Clipboard.ClearSources (); + Clipboard.Item = null; + } + } else { + Dnd.HandleSelectionNotifyEvent (ref xevent); + } + break; + } + + case XEventName.KeyRelease: + if (!detectable_key_auto_repeat && XPending (DisplayHandle) != 0) { + XEvent nextevent = new XEvent (); + + XPeekEvent (DisplayHandle, ref nextevent); + + if (nextevent.type == XEventName.KeyPress && + nextevent.KeyEvent.keycode == xevent.KeyEvent.keycode && + nextevent.KeyEvent.time == xevent.KeyEvent.time) { + continue; + } + } + goto case XEventName.KeyPress; + + case XEventName.MotionNotify: { + XEvent peek; + + /* we can't do motion compression across threads, so just punt if we don't match up */ + if (Thread.CurrentThread == hwnd.Queue.Thread && hwnd.Queue.Count > 0) { + peek = hwnd.Queue.Peek(); + if (peek.AnyEvent.type == XEventName.MotionNotify) { + continue; + } + } + goto case XEventName.KeyPress; + } + + case XEventName.KeyPress: + hwnd.Queue.EnqueueLocked (xevent); + /* Process KeyPresses immediately. Otherwise multiple Compose messages as a result of a + * single physical keypress are not processed correctly */ + return; + case XEventName.ButtonPress: + case XEventName.ButtonRelease: + case XEventName.EnterNotify: + case XEventName.LeaveNotify: + case XEventName.CreateNotify: + case XEventName.DestroyNotify: + case XEventName.FocusIn: + case XEventName.FocusOut: + case XEventName.ClientMessage: + case XEventName.ReparentNotify: + case XEventName.MapNotify: + case XEventName.UnmapNotify: + hwnd.Queue.EnqueueLocked (xevent); + break; + + case XEventName.ConfigureNotify: + AddConfigureNotify(xevent); + break; + + case XEventName.PropertyNotify: + DriverDebug ("UpdateMessageQueue (), got Event: {0}", xevent.ToString ()); + if (xevent.PropertyEvent.atom == _NET_ACTIVE_WINDOW) { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr prev_active; + + prev_active = ActiveWindow; + XGetWindowProperty(DisplayHandle, RootWindow, _NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false, (IntPtr)Atom.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if (((long)nitems > 0) && (prop != IntPtr.Zero)) { + ActiveWindow = Hwnd.GetHandleFromWindow((IntPtr)Marshal.ReadInt32(prop)); + XFree(prop); + + DebugHelper.WriteLine ("PropertyNotify: _NET_ACTIVE_WINDOW: previous = 0x{0:x}, new = 0x{1:x}", prev_active.ToInt32 (), ActiveWindow.ToInt32 ()); + + if (prev_active != ActiveWindow) { + if (prev_active != IntPtr.Zero) { + PostMessage(prev_active, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); + } + if (ActiveWindow != IntPtr.Zero) { + PostMessage(ActiveWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_ACTIVE, IntPtr.Zero); + } + } + if (ModalWindows.Count == 0) { + break; + } else { + // Modality Handling + // + // If there is a modal window on the stack and the new active + // window is MWF window, but not the modal one and not a non-modal + // child of the modal one, switch back to the modal window. + // + // To identify if a non-modal form is child of a modal form + // we match their ApplicationContexts, which will be the same. + // This is because each modal form runs the loop with a + // new ApplicationContext, which is inherited by the non-modal + // forms. + + Form activeForm = Widget.FromHandle (ActiveWindow) as Form; + if (activeForm != null) { + Form modalForm = Widget.FromHandle ((IntPtr)ModalWindows.Peek()) as Form; + if (ActiveWindow != (IntPtr)ModalWindows.Peek() && + (modalForm == null || activeForm.context == modalForm.context)) { + Activate((IntPtr)ModalWindows.Peek()); + } + } + break; + } + } + } + else if (xevent.PropertyEvent.atom == _NET_WM_STATE) { + // invalidate our cache - we'll query again the next time someone does GetWindowState. + hwnd.cached_window_state = (FormWindowState)(-1); + PostMessage (hwnd.Handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + } + break; + + } + } + } + + IntPtr GetMousewParam(int Delta) { + int result = 0; + + if ((MouseState & MouseButtons.Left) != 0) { + result |= (int)MsgButtons.MK_LBUTTON; + } + + if ((MouseState & MouseButtons.Middle) != 0) { + result |= (int)MsgButtons.MK_MBUTTON; + } + + if ((MouseState & MouseButtons.Right) != 0) { + result |= (int)MsgButtons.MK_RBUTTON; + } + + Keys mods = ModifierKeys; + if ((mods & Keys.Widget) != 0) { + result |= (int)MsgButtons.MK_CONTROL; + } + + if ((mods & Keys.Shift) != 0) { + result |= (int)MsgButtons.MK_SHIFT; + } + + result |= Delta << 16; + + return (IntPtr)result; + } + IntPtr XGetParent(IntPtr handle) { + IntPtr Root; + IntPtr Parent; + IntPtr Children; + int ChildCount; + + lock (XlibLock) { + XQueryTree(DisplayHandle, handle, out Root, out Parent, out Children, out ChildCount); + } + + if (Children!=IntPtr.Zero) { + lock (XlibLock) { + XFree(Children); + } + } + return Parent; + } + + int HandleError (IntPtr display, ref XErrorEvent error_event) + { + // we need to workaround a problem with the + // ordering of destruction of Drawables and + // Pictures that exists between cairo and + // RENDER on the server. + if (error_event.request_code == (XRequest)render_major_opcode + && error_event.minor_code == 7 /* X_RenderFreePicture from render.h */ + && error_event.error_code == render_first_error + 1 /* BadPicture from render.h */) { + return 0; + } + + if (ErrorExceptions) { + XUngrabPointer (display, IntPtr.Zero); + throw new XException (error_event.display, error_event.resourceid, + error_event.serial, error_event.error_code, + error_event.request_code, error_event.minor_code); + } else { + Console.WriteLine("X11 Error encountered: {0}{1}\n", + XException.GetMessage (error_event.display, error_event.resourceid, + error_event.serial, error_event.error_code, + error_event.request_code, error_event.minor_code), + Environment.StackTrace); + } + return 0; + } + + void AccumulateDestroyedHandles (Widget c, ArrayList list) + { + DebugHelper.Enter (); + if (c != null) { + + Widget[] Widgets = c.Widgets.GetAllWidgets (); + + DebugHelper.WriteLine ("Checking Widget:0x{0:x}", c.IsHandleCreated ? c.Handle.ToInt32() : 0); + + if (c.IsHandleCreated && !c.IsDisposed) { + Hwnd hwnd = Hwnd.ObjectFromHandle(c.Handle); + + DriverDebug (" + adding {0} to the list of zombie windows", XplatUI.Window (hwnd.Handle)); + DriverDebug (" + parent X window is {0:X}", XGetParent (hwnd.whole_window).ToInt32()); + + list.Add (hwnd); + CleanupCachedWindows (hwnd); + } + + for (int i = 0; i < Widgets.Length; i ++) { + AccumulateDestroyedHandles (Widgets[i], list); + } + } + DebugHelper.Leave (); + } + + void CleanupCachedWindows (Hwnd hwnd) + { + if (ActiveWindow == hwnd.Handle) { + SendMessage(hwnd.client_window, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); + ActiveWindow = IntPtr.Zero; + } + + if (FocusWindow == hwnd.Handle) { + SendMessage(hwnd.client_window, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero); + FocusWindow = IntPtr.Zero; + } + + if (Grab.Hwnd == hwnd.Handle) { + Grab.Hwnd = IntPtr.Zero; + Grab.Confined = false; + } + + DestroyCaret (hwnd.Handle); + } + + void PerformNCCalc(Hwnd hwnd) { + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + IntPtr ptr; + Rectangle rect; + + rect = new Rectangle (0, 0, hwnd.Width, hwnd.Height); + + ncp = new XplatUIWin32.NCCALCSIZE_PARAMS(); + ptr = Marshal.AllocHGlobal(Marshal.SizeOf(ncp)); + + ncp.rgrc1.left = rect.Left; + ncp.rgrc1.top = rect.Top; + ncp.rgrc1.right = rect.Right; + ncp.rgrc1.bottom = rect.Bottom; + + Marshal.StructureToPtr(ncp, ptr, true); + NativeWindow.WndProc(hwnd.client_window, Msg.WM_NCCALCSIZE, (IntPtr)1, ptr); + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure(ptr, typeof(XplatUIWin32.NCCALCSIZE_PARAMS)); + Marshal.FreeHGlobal(ptr); + + + rect = new Rectangle(ncp.rgrc1.left, ncp.rgrc1.top, ncp.rgrc1.right - ncp.rgrc1.left, ncp.rgrc1.bottom - ncp.rgrc1.top); + hwnd.ClientRect = rect; + + rect = TranslateClientRectangleToXClientRectangle (hwnd); + + if (hwnd.visible) { + MoveResizeWindow (DisplayHandle, hwnd.client_window, rect.X, rect.Y, rect.Width, rect.Height); + } + + AddExpose (hwnd, hwnd.WholeWindow == hwnd.ClientWindow, 0, 0, hwnd.Width, hwnd.Height); + } + #endregion // Methods + + #region Callbacks + void MouseHover(object sender, EventArgs e) { + XEvent xevent; + Hwnd hwnd; + + HoverState.Timer.Enabled = false; + + if (HoverState.Window != IntPtr.Zero) { + hwnd = Hwnd.GetObjectFromWindow(HoverState.Window); + if (hwnd != null) { + xevent = new XEvent (); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = DisplayHandle; + xevent.ClientMessageEvent.window = HoverState.Window; + xevent.ClientMessageEvent.message_type = HoverState.Atom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = (IntPtr) (HoverState.Y << 16 | HoverState.X); + + hwnd.Queue.EnqueueLocked (xevent); + + WakeupMain (); + } + } + } + + void CaretCallback(object sender, EventArgs e) { + if (Caret.Paused) { + return; + } + Caret.On = !Caret.On; + + XDrawLine(DisplayHandle, Caret.Hwnd, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + #endregion // Callbacks + + #region Public Properties + + internal override int CaptionHeight { + get { + return 19; + } + } + + internal override Size CursorSize { + get { + int x; + int y; + + if (XQueryBestCursor(DisplayHandle, RootWindow, 32, 32, out x, out y) != 0) { + return new Size(x, y); + } else { + return new Size(16, 16); + } + } + } + + internal override bool DragFullWindows { + get { + return true; + } + } + + internal override Size DragSize { + get { + return new Size(4, 4); + } + } + + internal override Size FrameBorderSize { + get { + return new Size (4, 4); + } + } + + internal override Size IconSize { + get { + IntPtr list; + XIconSize size; + int count; + + if (XGetIconSizes(DisplayHandle, RootWindow, out list, out count) != 0) { + long current; + int largest; + + current = (long)list; + largest = 0; + + size = new XIconSize(); + + for (int i = 0; i < count; i++) { + size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType()); + current += Marshal.SizeOf(size); + + // Look for our preferred size + if (size.min_width == 32) { + XFree(list); + return new Size(32, 32); + } + + if (size.max_width == 32) { + XFree(list); + return new Size(32, 32); + } + + if (size.min_width < 32 && size.max_width > 32) { + int x; + + // check if we can fit one + x = size.min_width; + while (x < size.max_width) { + x += size.width_inc; + if (x == 32) { + XFree(list); + return new Size(32, 32); + } + } + } + + if (largest < size.max_width) { + largest = size.max_width; + } + } + + // We didn't find a match or we wouldn't be here + return new Size(largest, largest); + + } else { + return new Size(32, 32); + } + } + } + + internal override int KeyboardSpeed { + get{ + // + // A lot harder: need to do: + // XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 1 + // XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 0x080517a8 + // XkbGetWidgets(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58) = 0 + // + // And from that we can tell the repetition rate + // + // Notice, the values must map to: + // [0, 31] which maps to 2.5 to 30 repetitions per second. + // + return 0; + } + } + + internal override int KeyboardDelay { + get { + // + // Return values must range from 0 to 4, 0 meaning 250ms, + // and 4 meaning 1000 ms. + // + return 1; // ie, 500 ms + } + } + + internal override Size MaxWindowTrackSize { + get { + return new Size (WorkingArea.Width, WorkingArea.Height); + } + } + + internal override bool MenuAccessKeysUnderlined { + get { + return false; + } + } + + internal override Size MinimizedWindowSpacingSize { + get { + return new Size(1, 1); + } + } + + internal override Size MinimumWindowSize { + get { + return new Size(110, 22); + } + } + + internal override Size MinimumFixedToolWindowSize { + get { return new Size (27, 22); } + } + + internal override Size MinimumSizeableToolWindowSize { + get { return new Size (37, 22); } + } + + internal override Size MinimumNoBorderWindowSize { + get { return new Size (2, 2); } + } + + internal override Keys ModifierKeys { + get { + return Keyboard.ModifierKeys; + } + } + + internal override Size SmallIconSize { + get { + IntPtr list; + XIconSize size; + int count; + + if (XGetIconSizes(DisplayHandle, RootWindow, out list, out count) != 0) { + long current; + int smallest; + + current = (long)list; + smallest = 0; + + size = new XIconSize(); + + for (int i = 0; i < count; i++) { + size = (XIconSize)Marshal.PtrToStructure((IntPtr)current, size.GetType()); + current += Marshal.SizeOf(size); + + // Look for our preferred size + if (size.min_width == 16) { + XFree(list); + return new Size(16, 16); + } + + if (size.max_width == 16) { + XFree(list); + return new Size(16, 16); + } + + if (size.min_width < 16 && size.max_width > 16) { + int x; + + // check if we can fit one + x = size.min_width; + while (x < size.max_width) { + x += size.width_inc; + if (x == 16) { + XFree(list); + return new Size(16, 16); + } + } + } + + if (smallest == 0 || smallest > size.min_width) { + smallest = size.min_width; + } + } + + // We didn't find a match or we wouldn't be here + return new Size(smallest, smallest); + + } else { + return new Size(16, 16); + } + } + } + + internal override int MouseButtonCount { + get { + return 3; + } + } + + internal override bool MouseButtonsSwapped { + get { + return false; // FIXME - how to detect? + } + } + + internal override Point MousePosition { + get { + return mouse_position; + } + } + + internal override Size MouseHoverSize { + get { + return new Size (1, 1); + } + } + + internal override int MouseHoverTime { + get { + return HoverState.Interval; + } + } + + + + internal override bool MouseWheelPresent { + get { + return true; // FIXME - how to detect? + } + } + + internal override MouseButtons MouseButtons { + get { + return MouseState; + } + } + + internal override Rectangle VirtualScreen { + get { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + int width; + int height; + + XGetWindowProperty(DisplayHandle, RootWindow, _NET_DESKTOP_GEOMETRY, IntPtr.Zero, new IntPtr (256), false, (IntPtr)Atom.XA_CARDINAL, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if ((long)nitems < 2) + goto failsafe; + + width = Marshal.ReadIntPtr(prop, 0).ToInt32(); + height = Marshal.ReadIntPtr(prop, IntPtr.Size).ToInt32(); + + XFree(prop); + + return new Rectangle(0, 0, width, height); + + failsafe: + XWindowAttributes attributes=new XWindowAttributes(); + + lock (XlibLock) { + XGetWindowAttributes(DisplayHandle, XRootWindow(DisplayHandle, 0), ref attributes); + } + + return new Rectangle(0, 0, attributes.width, attributes.height); + } + } + + internal override Rectangle WorkingArea { + get { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + int width; + int height; + int current_desktop; + int x; + int y; + + XGetWindowProperty(DisplayHandle, RootWindow, _NET_CURRENT_DESKTOP, IntPtr.Zero, new IntPtr(1), false, (IntPtr)Atom.XA_CARDINAL, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if ((long)nitems < 1) { + goto failsafe; + } + + current_desktop = Marshal.ReadIntPtr(prop, 0).ToInt32(); + XFree(prop); + + XGetWindowProperty(DisplayHandle, RootWindow, _NET_WORKAREA, IntPtr.Zero, new IntPtr (256), false, (IntPtr)Atom.XA_CARDINAL, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if ((long)nitems < 4 * (current_desktop + 1)) { + goto failsafe; + } + + x = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop).ToInt32(); + y = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size).ToInt32(); + width = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 2).ToInt32(); + height = Marshal.ReadIntPtr(prop, IntPtr.Size * 4 * current_desktop + IntPtr.Size * 3).ToInt32(); + XFree(prop); + + return new Rectangle(x, y, width, height); + + failsafe: + XWindowAttributes attributes=new XWindowAttributes(); + + lock (XlibLock) { + XGetWindowAttributes(DisplayHandle, XRootWindow(DisplayHandle, 0), ref attributes); + } + + return new Rectangle(0, 0, attributes.width, attributes.height); + } + } + + internal override Screen[] AllScreens { + get { + if (!XineramaIsActive (DisplayHandle)) + return null; + int nScreens; + IntPtr xineramaScreens = XineramaQueryScreens (DisplayHandle, out nScreens); + var screens = new Screen [nScreens]; + IntPtr current = xineramaScreens; + for (int i = 0; i < nScreens; i++) { + var screen = (XineramaScreenInfo)Marshal.PtrToStructure (current, + typeof (XineramaScreenInfo)); + var screenRect = new Rectangle (screen.x_org, screen.y_org, screen.width, + screen.height); + var name = string.Format ("Display {0}", screen.screen_number); + screens [i] = new Screen (i == 0, name, screenRect, screenRect); + current = (IntPtr)( (ulong)current + (ulong)Marshal.SizeOf(typeof (XineramaScreenInfo))); + } + XFree (xineramaScreens); + return screens; + } + } + + internal override bool ThemesEnabled { + get { + return XplatUIX11.themes_enabled; + } + } + + + #endregion // Public properties + + #region Public Static Methods + internal override void RaiseIdle (EventArgs e) + { + if (Idle != null) + Idle (this, e); + } + + internal override IntPtr InitializeDriver() + { + lock (this) { + if (DisplayHandle==IntPtr.Zero) { + SetDisplay(XOpenDisplay(IntPtr.Zero)); + } + } + return IntPtr.Zero; + } + + internal override void ShutdownDriver(IntPtr token) + { + lock (this) { + if (DisplayHandle!=IntPtr.Zero) { + XCloseDisplay(DisplayHandle); + DisplayHandle=IntPtr.Zero; + } + } + } + + internal override void EnableThemes() + { + themes_enabled = true; + } + + + internal override void Activate(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) { + lock (XlibLock) { + if (true /* the window manager supports NET_ACTIVE_WINDOW */) { + SendNetWMMessage(hwnd.whole_window, _NET_ACTIVE_WINDOW, (IntPtr)1, IntPtr.Zero, IntPtr.Zero); + XEventQueue q = null; + lock (unattached_timer_list) { + foreach (Timer t in unattached_timer_list) { + if (q == null) + q= (XEventQueue) MessageQueues [Thread.CurrentThread]; + t.thread = q.Thread; + q.timer_list.Add (t); + } + unattached_timer_list.Clear (); + } + } +// else { +// XRaiseWindow(DisplayHandle, handle); +// } + } + } + } + + internal override void AudibleAlert(AlertType alert) + { + XBell(DisplayHandle, 0); + return; + } + + + internal override void CaretVisible(IntPtr handle, bool visible) + { + if (Caret.Hwnd == handle) { + if (visible) { + if (!Caret.Visible) { + Caret.Visible = true; + ShowCaret(); + Caret.Timer.Start(); + } + } else { + Caret.Visible = false; + Caret.Timer.Stop(); + HideCaret(); + } + } + } + + internal override bool CalculateWindowRect(ref Rectangle ClientRect, CreateParams cp, Menu menu, out Rectangle WindowRect) + { + WindowRect = Hwnd.GetWindowRectangle (cp, menu, ClientRect); + return true; + } + + internal override void ClientToScreen(IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + lock (XlibLock) { + XTranslateCoordinates(handle, hwnd.client_window, RootWindow, x, y, out dest_x_return, out dest_y_return, out child); + } + + x = dest_x_return; + y = dest_y_return; + } + + internal override int[] ClipboardAvailableFormats(IntPtr handle) + { + DataFormats.Format f; + int[] result; + + f = DataFormats.Format.List; + + if (XGetSelectionOwner(DisplayHandle, CLIPBOARD) == IntPtr.Zero) { + return null; + } + + Clipboard.Formats = new ArrayList(); + + while (f != null) { + XConvertSelection(DisplayHandle, CLIPBOARD, (IntPtr)f.Id, (IntPtr)f.Id, FosterParent, IntPtr.Zero); + + var timeToWaitForSelectionFormats = TimeSpan.FromSeconds(4); + var startTime = DateTime.Now; + Clipboard.Enumerating = true; + while (Clipboard.Enumerating) { + UpdateMessageQueue(null, false); + + if (DateTime.Now - startTime > timeToWaitForSelectionFormats) + break; + } + f = f.Next; + } + + result = new int[Clipboard.Formats.Count]; + + for (int i = 0; i < Clipboard.Formats.Count; i++) { + result[i] = ((IntPtr)Clipboard.Formats[i]).ToInt32 (); + } + + Clipboard.Formats = null; + return result; + } + + internal override void ClipboardClose(IntPtr handle) + { + if (handle != ClipMagic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + return; + } + + internal override int ClipboardGetID(IntPtr handle, string format) + { + if (handle != ClipMagic) { + throw new ArgumentException("handle is not a valid clipboard handle"); + } + + if (format == "Text" ) return (int)Atom.XA_STRING; + else if (format == "Bitmap" ) return (int)Atom.XA_BITMAP; + //else if (format == "MetaFilePict" ) return 3; + //else if (format == "SymbolicLink" ) return 4; + //else if (format == "DataInterchangeFormat" ) return 5; + //else if (format == "Tiff" ) return 6; + else if (format == "OEMText" ) return OEMTEXT.ToInt32(); + else if (format == "DeviceIndependentBitmap" ) return (int)Atom.XA_PIXMAP; + else if (format == "Palette" ) return (int)Atom.XA_COLORMAP; // Useless + //else if (format == "PenData" ) return 10; + //else if (format == "RiffAudio" ) return 11; + //else if (format == "WaveAudio" ) return 12; + else if (format == "UnicodeText" ) return UTF16_STRING.ToInt32(); + //else if (format == "EnhancedMetafile" ) return 14; + //else if (format == "FileDrop" ) return 15; + //else if (format == "Locale" ) return 16; + else if (format == "Rich Text Format") return RICHTEXTFORMAT.ToInt32 (); + + return XInternAtom(DisplayHandle, format, false).ToInt32(); + } + + internal override IntPtr ClipboardOpen(bool primary_selection) + { + if (!primary_selection) + ClipMagic = CLIPBOARD; + else + ClipMagic = PRIMARY; + return ClipMagic; + } + + internal override object ClipboardRetrieve(IntPtr handle, int type, XplatUI.ClipboardToObject converter) + { + XConvertSelection(DisplayHandle, handle, (IntPtr)type, (IntPtr)type, FosterParent, IntPtr.Zero); + + Clipboard.Retrieving = true; + while (Clipboard.Retrieving) { + UpdateMessageQueue(null, false); + } + + return Clipboard.Item; + } + + internal override void ClipboardStore (IntPtr handle, object obj, int type, XplatUI.ObjectToClipboard converter, bool copy) + { + Clipboard.Converter = converter; + + if (obj != null) { + Clipboard.AddSource (type, obj); + XSetSelectionOwner (DisplayHandle, CLIPBOARD, FosterParent, IntPtr.Zero); + + if (copy) { + try { + var clipboardAtom = gdk_atom_intern ("CLIPBOARD", true); + var clipboard = gtk_clipboard_get (clipboardAtom); + if (clipboard != null) { + // for now we only store text + var text = Clipboard.GetRtfText (); + if (string.IsNullOrEmpty (text)) + text = Clipboard.GetPlainText (); + if (!string.IsNullOrEmpty (text)) { + gtk_clipboard_set_text (clipboard, text, text.Length); + gtk_clipboard_store (clipboard); + } + } + } catch { + // ignore any errors - most likely because gtk isn't installed? + } + } + } else { + // Clearing the selection + Clipboard.ClearSources (); + XSetSelectionOwner (DisplayHandle, CLIPBOARD, IntPtr.Zero, IntPtr.Zero); + } + } + + internal override void CreateCaret (IntPtr handle, int width, int height) + { + XGCValues gc_values; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (Caret.Hwnd != IntPtr.Zero) { + DestroyCaret(Caret.Hwnd); + } + + Caret.Hwnd = handle; + Caret.Window = hwnd.client_window; + Caret.Width = width; + Caret.Height = height; + Caret.Visible = false; + Caret.On = false; + + gc_values = new XGCValues(); + gc_values.line_width = width; + + Caret.gc = XCreateGC(DisplayHandle, Caret.Window, new IntPtr ((int)GCFunction.GCLineWidth), ref gc_values); + if (Caret.gc == IntPtr.Zero) { + Caret.Hwnd = IntPtr.Zero; + return; + } + + XSetFunction(DisplayHandle, Caret.gc, GXFunction.GXinvert); + } + + internal override IntPtr CreateWindow (CreateParams cp) + { + XSetWindowAttributes Attributes; + Hwnd hwnd; + Hwnd parent_hwnd = null; + int X; + int Y; + int Width; + int Height; + IntPtr ParentHandle; + IntPtr WholeWindow; + IntPtr ClientWindow; + SetWindowValuemask ValueMask; + + hwnd = new Hwnd(); + + Attributes = new XSetWindowAttributes(); + X = cp.X; + Y = cp.Y; + Width = cp.Width; + Height = cp.Height; + + if (Width<1) Width=1; + if (Height<1) Height=1; + + if (cp.Parent != IntPtr.Zero) { + parent_hwnd = Hwnd.ObjectFromHandle(cp.Parent); + ParentHandle = parent_hwnd.client_window; + } else { + if (StyleSet (cp.Style, WindowStyles.WS_CHILD)) { + // We need to use our foster parent window until this poor child gets it's parent assigned + ParentHandle=FosterParent; + } else { + ParentHandle=RootWindow; + } + } + + // Set the default location location for forms. + Point next; + if (cp.control is Form) { + next = Hwnd.GetNextStackedFormLocation (cp, parent_hwnd); + X = next.X; + Y = next.Y; + } + ValueMask = SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; + + Attributes.bit_gravity = Gravity.NorthWestGravity; + Attributes.win_gravity = Gravity.NorthWestGravity; + + // Save what's under the toolwindow + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOOLWINDOW)) { + Attributes.save_under = true; + ValueMask |= SetWindowValuemask.SaveUnder; + } + + + // If we're a popup without caption we override the WM + if (StyleSet (cp.Style, WindowStyles.WS_POPUP) && !StyleSet (cp.Style, WindowStyles.WS_CAPTION)) { + Attributes.override_redirect = true; + ValueMask |= SetWindowValuemask.OverrideRedirect; + } + + hwnd.x = X; + hwnd.y = Y; + hwnd.width = Width; + hwnd.height = Height; + hwnd.parent = Hwnd.ObjectFromHandle(cp.Parent); + hwnd.initial_style = cp.WindowStyle; + hwnd.initial_ex_style = cp.WindowExStyle; + + if (StyleSet (cp.Style, WindowStyles.WS_DISABLED)) { + hwnd.enabled = false; + } + + ClientWindow = IntPtr.Zero; + + Size XWindowSize = TranslateWindowSizeToXWindowSize (cp); + Rectangle XClientRect = TranslateClientRectangleToXClientRectangle (hwnd, cp.control); + + lock (XlibLock) { + WholeWindow = XCreateWindow(DisplayHandle, ParentHandle, X, Y, XWindowSize.Width, XWindowSize.Height, 0, (int)CreateWindowArgs.CopyFromParent, (int)CreateWindowArgs.InputOutput, IntPtr.Zero, new UIntPtr ((uint)ValueMask), ref Attributes); + if (WholeWindow != IntPtr.Zero) { + ValueMask &= ~(SetWindowValuemask.OverrideRedirect | SetWindowValuemask.SaveUnder); + + if (CustomVisual != IntPtr.Zero && CustomColormap != IntPtr.Zero) { + ValueMask = SetWindowValuemask.ColorMap; + Attributes.colormap = CustomColormap; + } + ClientWindow = XCreateWindow(DisplayHandle, WholeWindow, XClientRect.X, XClientRect.Y, XClientRect.Width, XClientRect.Height, 0, (int)CreateWindowArgs.CopyFromParent, (int)CreateWindowArgs.InputOutput, CustomVisual, new UIntPtr ((uint)ValueMask), ref Attributes); + } + } + + if ((WholeWindow == IntPtr.Zero) || (ClientWindow == IntPtr.Zero)) { + throw new Exception("Could not create X11 windows"); + } + + hwnd.Queue = ThreadQueue(Thread.CurrentThread); + hwnd.WholeWindow = WholeWindow; + hwnd.ClientWindow = ClientWindow; + + DriverDebug("Created window {0:X} / {1:X} parent {2:X}, Style {3}, ExStyle {4}", ClientWindow.ToInt32(), WholeWindow.ToInt32(), hwnd.parent != null ? hwnd.parent.Handle.ToInt32() : 0, (WindowStyles)cp.Style, (WindowExStyles)cp.ExStyle); + + if (!StyleSet (cp.Style, WindowStyles.WS_CHILD)) { + if ((X != unchecked((int)0x80000000)) && (Y != unchecked((int)0x80000000))) { + XSizeHints hints; + + hints = new XSizeHints(); + hints.x = X; + hints.y = Y; + hints.flags = (IntPtr)(XSizeHintsFlags.USPosition | XSizeHintsFlags.PPosition); + XSetWMNormalHints(DisplayHandle, WholeWindow, ref hints); + } + } + + lock (XlibLock) { + XSelectInput(DisplayHandle, hwnd.whole_window, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask | EventMask.PropertyChangeMask | Keyboard.KeyEventMask))); + if (hwnd.whole_window != hwnd.client_window) + XSelectInput(DisplayHandle, hwnd.client_window, new IntPtr ((int)(SelectInputMask | EventMask.StructureNotifyMask | Keyboard.KeyEventMask))); + } + + if (ExStyleSet (cp.ExStyle, WindowExStyles.WS_EX_TOPMOST)) + SetTopmost(hwnd.whole_window, true); + + SetWMStyles(hwnd, cp); + + // set the group leader + XWMHints wm_hints = new XWMHints (); + + wm_hints.flags = (IntPtr)(XWMHintsFlags.InputHint | XWMHintsFlags.StateHint | XWMHintsFlags.WindowGroupHint); + wm_hints.input = !StyleSet (cp.Style, WindowStyles.WS_DISABLED); + wm_hints.initial_state = StyleSet (cp.Style, WindowStyles.WS_MINIMIZE) ? XInitialState.IconicState : XInitialState.NormalState; + + if (ParentHandle != RootWindow) { + wm_hints.window_group = hwnd.whole_window; + } else { + wm_hints.window_group = ParentHandle; + } + + lock (XlibLock) { + XSetWMHints(DisplayHandle, hwnd.whole_window, ref wm_hints ); + } + + if (StyleSet (cp.Style, WindowStyles.WS_MINIMIZE)) { + SetWindowState(hwnd.Handle, FormWindowState.Minimized); + } else if (StyleSet (cp.Style, WindowStyles.WS_MAXIMIZE)) { + SetWindowState(hwnd.Handle, FormWindowState.Maximized); + } + + // for now make all windows dnd enabled + Dnd.SetAllowDrop (hwnd, true); + + // Set caption/window title + Text(hwnd.Handle, cp.Caption); + + SendMessage (hwnd.Handle, Msg.WM_CREATE, (IntPtr)1, IntPtr.Zero /* XXX unused */); + SendParentNotify (hwnd.Handle, Msg.WM_CREATE, int.MaxValue, int.MaxValue); + + if (StyleSet (cp.Style, WindowStyles.WS_VISIBLE)) { + hwnd.visible = true; + MapWindow(hwnd, WindowType.Both); + if (!(Widget.FromHandle(hwnd.Handle) is Form)) + SendMessage(hwnd.Handle, Msg.WM_SHOWWINDOW, (IntPtr)1, IntPtr.Zero); + } + + return hwnd.Handle; + } + + internal override IntPtr CreateWindow(IntPtr Parent, int X, int Y, int Width, int Height) + { + CreateParams create_params = new CreateParams(); + + create_params.Caption = ""; + create_params.X = X; + create_params.Y = Y; + create_params.Width = Width; + create_params.Height = Height; + + create_params.ClassName=XplatUI.GetDefaultClassName (GetType ()); + create_params.ClassStyle = 0; + create_params.ExStyle=0; + create_params.Parent=IntPtr.Zero; + create_params.Param=0; + + return CreateWindow(create_params); + } + + internal override IntPtr DefineCursor(Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) + { + IntPtr cursor; + Bitmap cursor_bitmap; + Bitmap cursor_mask; + Byte[] cursor_bits; + Byte[] mask_bits; + Color c_pixel; + Color m_pixel; + int width; + int height; + IntPtr cursor_pixmap; + IntPtr mask_pixmap; + XColor fg; + XColor bg; + bool and; + bool xor; + + if (XQueryBestCursor(DisplayHandle, RootWindow, bitmap.Width, bitmap.Height, out width, out height) == 0) { + return IntPtr.Zero; + } + + // Win32 only allows creation cursors of a certain size + if ((bitmap.Width != width) || (bitmap.Width != height)) { + cursor_bitmap = new Bitmap(bitmap, new Size(width, height)); + cursor_mask = new Bitmap(mask, new Size(width, height)); + } else { + cursor_bitmap = bitmap; + cursor_mask = mask; + } + + width = cursor_bitmap.Width; + height = cursor_bitmap.Height; + + cursor_bits = new Byte[(width / 8) * height]; + mask_bits = new Byte[(width / 8) * height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + c_pixel = cursor_bitmap.GetPixel(x, y); + m_pixel = cursor_mask.GetPixel(x, y); + + and = c_pixel == cursor_pixel; + xor = m_pixel == mask_pixel; + + if (!and && !xor) { + // Black + // cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); // The bit already is 0 + mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); + } else if (and && !xor) { + // White + cursor_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); + mask_bits[y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); +#if notneeded + } else if (and && !xor) { + // Screen + } else if (and && xor) { + // Inverse Screen + + // X11 doesn't know the 'reverse screen' concept, so we'll treat them the same + // we want both to be 0 so nothing to be done + //cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); + //mask_bits[y * width / 8 + x / 8] |= (byte)(01 << (x % 8)); +#endif + } + } + } + + cursor_pixmap = XCreatePixmapFromBitmapData(DisplayHandle, RootWindow, cursor_bits, width, height, (IntPtr)1, (IntPtr)0, 1); + mask_pixmap = XCreatePixmapFromBitmapData(DisplayHandle, RootWindow, mask_bits, width, height, (IntPtr)1, (IntPtr)0, 1); + fg = new XColor(); + bg = new XColor(); + + fg.pixel = XWhitePixel(DisplayHandle, ScreenNo); + fg.red = (ushort)65535; + fg.green = (ushort)65535; + fg.blue = (ushort)65535; + + bg.pixel = XBlackPixel(DisplayHandle, ScreenNo); + + cursor = XCreatePixmapCursor(DisplayHandle, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot); + + XFreePixmap(DisplayHandle, cursor_pixmap); + XFreePixmap(DisplayHandle, mask_pixmap); + + return cursor; + } + + internal override Bitmap DefineStdCursorBitmap (StdCursor id) + { + CursorFontShape shape; + string name; + IntPtr theme; + int size; + Bitmap bmp = null; + + try { + shape = StdCursorToFontShape (id); + name = shape.ToString ().Replace ("XC_", string.Empty); + size = XcursorGetDefaultSize (DisplayHandle); + theme = XcursorGetTheme (DisplayHandle); + IntPtr images_ptr = XcursorLibraryLoadImages (name, theme, size); + DriverDebug ("DefineStdCursorBitmap, id={0}, #id={1}, name{2}, size={3}, theme: {4}, images_ptr={5}", id, (int) id, name, size, Marshal.PtrToStringAnsi (theme), images_ptr); + + if (images_ptr == IntPtr.Zero) { + return null; + } + + XcursorImages images = (XcursorImages) Marshal.PtrToStructure (images_ptr, typeof (XcursorImages)); + DriverDebug ("DefineStdCursorBitmap, cursor has {0} images", images.nimage); + + if (images.nimage > 0) { + // We only care about the first image. + XcursorImage image = (XcursorImage)Marshal.PtrToStructure (Marshal.ReadIntPtr (images.images), typeof (XcursorImage)); + + DriverDebug ("DefineStdCursorBitmap, loaded image <size={0}, height={1}, width={2}, xhot={3}, yhot={4}, pixels={5}", image.size, image.height, image.width, image.xhot, image.yhot, image.pixels); + // A sanity check + if (image.width <= short.MaxValue && image.height <= short.MaxValue) { + int [] pixels = new int [image.width * image.height]; + Marshal.Copy (image.pixels, pixels, 0, pixels.Length); + bmp = new Bitmap (image.width, image.height); + for (int w = 0; w < image.width; w++) { + for (int h = 0; h < image.height; h++) { + bmp.SetPixel (w, h, Color.FromArgb (pixels [h * image.width + w])); + } + } + } + } + + XcursorImagesDestroy (images_ptr); + + } catch (DllNotFoundException ex) { + Console.WriteLine ("Could not load libXcursor: " + ex.Message + " (" + ex.GetType ().Name + ")"); + return null; + } + + return bmp; + } + + + internal override IntPtr DefineStdCursor(StdCursor id) + { + CursorFontShape shape; + IntPtr cursor; + + shape = StdCursorToFontShape (id); + + lock (XlibLock) { + cursor = XCreateFontCursor(DisplayHandle, shape); + } + return cursor; + } + + internal static CursorFontShape StdCursorToFontShape (StdCursor id) + { + CursorFontShape shape; + // FIXME - define missing shapes + + switch (id) { + case StdCursor.AppStarting: { + shape = CursorFontShape.XC_watch; + break; + } + + case StdCursor.Arrow: { + shape = CursorFontShape.XC_top_left_arrow; + break; + } + + case StdCursor.Cross: { + shape = CursorFontShape.XC_crosshair; + break; + } + + case StdCursor.Default: { + shape = CursorFontShape.XC_top_left_arrow; + break; + } + + case StdCursor.Hand: { + shape = CursorFontShape.XC_hand1; + break; + } + + case StdCursor.Help: { + shape = CursorFontShape.XC_question_arrow; + break; + } + + case StdCursor.HSplit: { + shape = CursorFontShape.XC_sb_v_double_arrow; + break; + } + + case StdCursor.IBeam: { + shape = CursorFontShape.XC_xterm; + break; + } + + case StdCursor.No: { + shape = CursorFontShape.XC_circle; + break; + } + + case StdCursor.NoMove2D: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.NoMoveHoriz: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.NoMoveVert: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanEast: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanNE: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanNorth: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanNW: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanSE: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanSouth: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanSW: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanWest: { + shape = CursorFontShape.XC_sizing; + break; + } + + case StdCursor.SizeAll: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.SizeNESW: { + shape = CursorFontShape.XC_top_right_corner; + break; + } + + case StdCursor.SizeNS: { + shape = CursorFontShape.XC_sb_v_double_arrow; + break; + } + + case StdCursor.SizeNWSE: { + shape = CursorFontShape.XC_top_left_corner; + break; + } + + case StdCursor.SizeWE: { + shape = CursorFontShape.XC_sb_h_double_arrow; + break; + } + + case StdCursor.UpArrow: { + shape = CursorFontShape.XC_center_ptr; + break; + } + + case StdCursor.VSplit: { + shape = CursorFontShape.XC_sb_h_double_arrow; + break; + } + + case StdCursor.WaitCursor: { + shape = CursorFontShape.XC_watch; + break; + } + + default: { + shape = (CursorFontShape) 0; + break; + } + } + + return shape; + } + + internal override IntPtr DefWndProc(ref Message msg) + { + switch ((Msg)msg.Msg) { + + case Msg.WM_IME_COMPOSITION: + string s = Keyboard.GetCompositionString (); + foreach (char c in s) + SendMessage (msg.HWnd, Msg.WM_IME_CHAR, (IntPtr) c, msg.LParam); + return IntPtr.Zero; + + case Msg.WM_IME_CHAR: + // On Windows API it sends two WM_CHAR messages for each byte, but + // I wonder if it is worthy to emulate it (also no idea how to + // reconstruct those bytes into chars). + SendMessage (msg.HWnd, Msg.WM_CHAR, msg.WParam, msg.LParam); + return IntPtr.Zero; + + case Msg.WM_PAINT: { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow(msg.HWnd); + if (hwnd != null) { + hwnd.expose_pending = false; + } + + return IntPtr.Zero; + } + + case Msg.WM_NCPAINT: { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow(msg.HWnd); + if (hwnd != null) { + hwnd.nc_expose_pending = false; + } + + return IntPtr.Zero; + } + + case Msg.WM_NCCALCSIZE: { + Hwnd hwnd; + + if (msg.WParam == (IntPtr)1) { + hwnd = Hwnd.GetObjectFromWindow (msg.HWnd); + + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (msg.LParam, typeof (XplatUIWin32.NCCALCSIZE_PARAMS)); + + // Add all the stuff X is supposed to draw. + Widget ctrl = Widget.FromHandle (hwnd.Handle); + + if (ctrl != null) { + Hwnd.Borders rect = Hwnd.GetBorders (ctrl.GetCreateParams (), null); + + ncp.rgrc1.top += rect.top; + ncp.rgrc1.bottom -= rect.bottom; + ncp.rgrc1.left += rect.left; + ncp.rgrc1.right -= rect.right; + + Marshal.StructureToPtr (ncp, msg.LParam, true); + } + } + + return IntPtr.Zero; + } + + case Msg.WM_CONTEXTMENU: { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow(msg.HWnd); + + if ((hwnd != null) && (hwnd.parent != null)) { + SendMessage(hwnd.parent.client_window, Msg.WM_CONTEXTMENU, msg.WParam, msg.LParam); + } + return IntPtr.Zero; + } + + case Msg.WM_MOUSEWHEEL: { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow(msg.HWnd); + + if ((hwnd != null) && (hwnd.parent != null)) { + SendMessage(hwnd.parent.client_window, Msg.WM_MOUSEWHEEL, msg.WParam, msg.LParam); + if (msg.Result == IntPtr.Zero) { + return IntPtr.Zero; + } + } + return IntPtr.Zero; + } + + case Msg.WM_SETCURSOR: { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow(msg.HWnd); + if (hwnd == null) + break; // not sure how this happens, but it does + + // Pass to parent window first + while ((hwnd.parent != null) && (msg.Result == IntPtr.Zero)) { + hwnd = hwnd.parent; + msg.Result = NativeWindow.WndProc(hwnd.Handle, Msg.WM_SETCURSOR, msg.HWnd, msg.LParam); + } + + if (msg.Result == IntPtr.Zero) { + IntPtr handle; + + switch((HitTest)(msg.LParam.ToInt32() & 0xffff)) { + case HitTest.HTBOTTOM: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBORDER: handle = Cursors.SizeNS.handle; break; + case HitTest.HTBOTTOMLEFT: handle = Cursors.SizeNESW.handle; break; + case HitTest.HTBOTTOMRIGHT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTERROR: if ((msg.LParam.ToInt32() >> 16) == (int)Msg.WM_LBUTTONDOWN) { + AudibleAlert(AlertType.Default); + } + handle = Cursors.Default.handle; + break; + + case HitTest.HTHELP: handle = Cursors.Help.handle; break; + case HitTest.HTLEFT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTRIGHT: handle = Cursors.SizeWE.handle; break; + case HitTest.HTTOP: handle = Cursors.SizeNS.handle; break; + case HitTest.HTTOPLEFT: handle = Cursors.SizeNWSE.handle; break; + case HitTest.HTTOPRIGHT: handle = Cursors.SizeNESW.handle; break; + + #if SameAsDefault + case HitTest.HTGROWBOX: + case HitTest.HTSIZE: + case HitTest.HTZOOM: + case HitTest.HTVSCROLL: + case HitTest.HTSYSMENU: + case HitTest.HTREDUCE: + case HitTest.HTNOWHERE: + case HitTest.HTMAXBUTTON: + case HitTest.HTMINBUTTON: + case HitTest.HTMENU: + case HitTest.HSCROLL: + case HitTest.HTBOTTOM: + case HitTest.HTCAPTION: + case HitTest.HTCLIENT: + case HitTest.HTCLOSE: + #endif + default: handle = Cursors.Default.handle; break; + } + SetCursor(msg.HWnd, handle); + } + return (IntPtr)1; + } + } + return IntPtr.Zero; + } + + internal override void DestroyCaret(IntPtr handle) + { + if (Caret.Hwnd == handle) { + if (Caret.Visible) { + HideCaret (); + Caret.Timer.Stop(); + } + if (Caret.gc != IntPtr.Zero) { + XFreeGC(DisplayHandle, Caret.gc); + Caret.gc = IntPtr.Zero; + } + Caret.Hwnd = IntPtr.Zero; + Caret.Visible = false; + Caret.On = false; + } + } + + internal override void DestroyCursor(IntPtr cursor) + { + lock (XlibLock) { + XFreeCursor(DisplayHandle, cursor); + } + } + + internal override void DestroyWindow(IntPtr handle) + { + Hwnd hwnd; + hwnd = Hwnd.ObjectFromHandle(handle); + + // The window should never ever be a zombie here, since we should + // wait until it's completely dead before returning from + // "destroying" calls, but just in case.... + if (hwnd == null || hwnd.zombie) { + DriverDebug ("window {0:X} already destroyed", handle.ToInt32()); + return; + } + + DriverDebug ("Destroying window {0}", XplatUI.Window(hwnd.client_window)); + + SendParentNotify (hwnd.Handle, Msg.WM_DESTROY, int.MaxValue, int.MaxValue); + + CleanupCachedWindows (hwnd); + + ArrayList windows = new ArrayList (); + + AccumulateDestroyedHandles (Widget.WidgetNativeWindow.WidgetFromHandle(hwnd.Handle), windows); + + + foreach (Hwnd h in windows) { + SendMessage (h.Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero); + h.zombie = true; + } + + lock (XlibLock) { + if (hwnd.whole_window != IntPtr.Zero) { + DriverDebug ("XDestroyWindow (whole_window = {0:X})", hwnd.whole_window.ToInt32()); + Keyboard.DestroyICForWindow (hwnd.whole_window); + XDestroyWindow(DisplayHandle, hwnd.whole_window); + } + else if (hwnd.client_window != IntPtr.Zero) { + DriverDebug ("XDestroyWindow (client_window = {0:X})", hwnd.client_window.ToInt32()); + Keyboard.DestroyICForWindow (hwnd.client_window); + XDestroyWindow(DisplayHandle, hwnd.client_window); + } + + } + } + + internal override IntPtr DispatchMessage(ref MSG msg) + { + return NativeWindow.WndProc(msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + + IntPtr GetReversibleScreenGC (Color backColor) + { + XGCValues gc_values; + IntPtr gc; + uint pixel; + + XColor xcolor = new XColor(); + xcolor.red = (ushort)(backColor.R * 257); + xcolor.green = (ushort)(backColor.G * 257); + xcolor.blue = (ushort)(backColor.B * 257); + XAllocColor(DisplayHandle, DefaultColormap, ref xcolor); + pixel = (uint)xcolor.pixel.ToInt32(); + + + gc_values = new XGCValues(); + + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + gc_values.foreground = (IntPtr)pixel; + + gc = XCreateGC(DisplayHandle, RootWindow, new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCForeground)), ref gc_values); + XSetForeground(DisplayHandle, gc, (UIntPtr)pixel); + XSetFunction(DisplayHandle, gc, GXFunction.GXxor); + + return gc; + } + + IntPtr GetReversibleWidgetGC (Widget Widget, int line_width) + { + XGCValues gc_values; + IntPtr gc; + + gc_values = new XGCValues(); + + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + gc_values.line_width = line_width; + gc_values.foreground = XBlackPixel(DisplayHandle, ScreenNo); + + // This logic will give us true rubber bands: (libsx, SANE_XOR) + //mask = foreground ^ background; + //XSetForeground(DisplayHandle, gc, 0xffffffff); + //XSetBackground(DisplayHandle, gc, background); + //XSetFunction(DisplayHandle, gc, GXxor); + //XSetPlaneMask(DisplayHandle, gc, mask); + + + gc = XCreateGC(DisplayHandle, Widget.Handle, new IntPtr ((int) (GCFunction.GCSubwindowMode | GCFunction.GCLineWidth | GCFunction.GCForeground)), ref gc_values); + uint foreground; + uint background; + + XColor xcolor = new XColor(); + + xcolor.red = (ushort)(Widget.ForeColor.R * 257); + xcolor.green = (ushort)(Widget.ForeColor.G * 257); + xcolor.blue = (ushort)(Widget.ForeColor.B * 257); + XAllocColor(DisplayHandle, DefaultColormap, ref xcolor); + foreground = (uint)xcolor.pixel.ToInt32(); + + xcolor.red = (ushort)(Widget.BackColor.R * 257); + xcolor.green = (ushort)(Widget.BackColor.G * 257); + xcolor.blue = (ushort)(Widget.BackColor.B * 257); + XAllocColor(DisplayHandle, DefaultColormap, ref xcolor); + background = (uint)xcolor.pixel.ToInt32(); + + uint mask = foreground ^ background; + + XSetForeground(DisplayHandle, gc, (UIntPtr)0xffffffff); + XSetBackground(DisplayHandle, gc, (UIntPtr)background); + XSetFunction(DisplayHandle, gc, GXFunction.GXxor); + XSetPlaneMask(DisplayHandle, gc, (IntPtr)mask); + + return gc; + } + + internal override void DrawReversibleLine(Point start, Point end, Color backColor) + { + if (backColor.GetBrightness() < 0.5) + backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B); + + IntPtr gc = GetReversibleScreenGC (backColor); + + XDrawLine (DisplayHandle, RootWindow, gc, start.X, start.Y, end.X, end.Y); + + XFreeGC(DisplayHandle, gc); + } + + internal override void DrawReversibleFrame (Rectangle rectangle, Color backColor, FrameStyle style) + { + if (backColor.GetBrightness() < 0.5) + backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B); + + IntPtr gc = GetReversibleScreenGC (backColor); + + if (rectangle.Width < 0) { + rectangle.X += rectangle.Width; + rectangle.Width = -rectangle.Width; + } + if (rectangle.Height < 0) { + rectangle.Y += rectangle.Height; + rectangle.Height = -rectangle.Height; + } + + int line_width = 1; + GCLineStyle line_style = GCLineStyle.LineSolid; + GCCapStyle cap_style = GCCapStyle.CapButt; + GCJoinStyle join_style = GCJoinStyle.JoinMiter; + + switch (style) { + case FrameStyle.Dashed: + line_style = GCLineStyle.LineOnOffDash; + break; + case FrameStyle.Thick: + line_width = 2; + break; + } + + XSetLineAttributes (DisplayHandle, gc, line_width, line_style, cap_style, join_style); + + XDrawRectangle(DisplayHandle, RootWindow, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height); + + XFreeGC(DisplayHandle, gc); + } + + internal override void FillReversibleRectangle (Rectangle rectangle, Color backColor) + { + if (backColor.GetBrightness() < 0.5) + backColor = Color.FromArgb(255 - backColor.R, 255 - backColor.G, 255 - backColor.B); + + IntPtr gc = GetReversibleScreenGC (backColor); + + if (rectangle.Width < 0) { + rectangle.X += rectangle.Width; + rectangle.Width = -rectangle.Width; + } + if (rectangle.Height < 0) { + rectangle.Y += rectangle.Height; + rectangle.Height = -rectangle.Height; + } + XFillRectangle(DisplayHandle, RootWindow, gc, rectangle.Left, rectangle.Top, rectangle.Width, rectangle.Height); + + XFreeGC(DisplayHandle, gc); + } + + internal override void DrawReversibleRectangle(IntPtr handle, Rectangle rect, int line_width) + { + IntPtr gc; + Widget Widget = Widget.FromHandle(handle); + + gc = GetReversibleWidgetGC (Widget, line_width); + + if ((rect.Width > 0) && (rect.Height > 0)) { + XDrawRectangle(DisplayHandle, Widget.Handle, gc, rect.Left, rect.Top, rect.Width, rect.Height); + } else { + if (rect.Width > 0) { + XDrawLine(DisplayHandle, Widget.Handle, gc, rect.X, rect.Y, rect.Right, rect.Y); + } else { + XDrawLine(DisplayHandle, Widget.Handle, gc, rect.X, rect.Y, rect.X, rect.Bottom); + } + } + XFreeGC(DisplayHandle, gc); + } + + internal override void DoEvents() + { + DebugHelper.Enter (); + + MSG msg = new MSG (); + XEventQueue queue; + + if (OverrideCursorHandle != IntPtr.Zero) { + OverrideCursorHandle = IntPtr.Zero; + } + + queue = ThreadQueue(Thread.CurrentThread); + + queue.DispatchIdle = false; + in_doevents = true; + + while (PeekMessage(queue, ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) { + Message m = Message.Create (msg.hwnd, (int)msg.message, msg.wParam, msg.lParam); + + if (Application.FilterMessage (ref m)) + continue; + + TranslateMessage (ref msg); + DispatchMessage (ref msg); + + string key = msg.hwnd + ":" + msg.message; + if (messageHold[key] != null) { + messageHold[key] = ((int)messageHold[key]) - 1; + DebugHelper.WriteLine ("Got " + msg + " for " + key); + } + } + + in_doevents = false; + queue.DispatchIdle = true; + + DebugHelper.Leave (); + } + + internal override void EnableWindow(IntPtr handle, bool Enable) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd != null) { + hwnd.Enabled = Enable; + } + } + + internal override void EndLoop(Thread thread) + { + // This is where we one day will shut down the loop for the thread + } + + internal override IntPtr GetActive() + { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr active = IntPtr.Zero; + + XGetWindowProperty(DisplayHandle, RootWindow, _NET_ACTIVE_WINDOW, IntPtr.Zero, new IntPtr (1), false, (IntPtr)Atom.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if (((long)nitems > 0) && (prop != IntPtr.Zero)) { + active = (IntPtr)Marshal.ReadInt32(prop); + XFree(prop); + } else { + // The window manager does not support _NET_ACTIVE_WINDOW. Fall back to XGetInputFocus. + IntPtr revert_to = IntPtr.Zero; + XGetInputFocus(DisplayHandle, out active, out revert_to); + } + + if (active != IntPtr.Zero) { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow(active); + if (hwnd != null) { + active = hwnd.Handle; + } else { + active = IntPtr.Zero; + } + } + return active; + } + + internal override Region GetClipRegion(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd != null) { + return hwnd.UserClip; + } + + return null; + } + + internal override void GetCursorInfo(IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y) + { + width = 20; + height = 20; + hotspot_x = 0; + hotspot_y = 0; + } + + internal override void GetDisplaySize(out Size size) + { + XWindowAttributes attributes=new XWindowAttributes(); + + lock (XlibLock) { + // FIXME - use _NET_WM messages instead? + XGetWindowAttributes(DisplayHandle, XRootWindow(DisplayHandle, 0), ref attributes); + } + + size = new Size(attributes.width, attributes.height); + } + + internal override SizeF GetAutoScaleSize(Font font) + { + Graphics g; + float width; + string magic_string = "The quick brown fox jumped over the lazy dog."; + double magic_number = 44.549996948242189; + + g = Graphics.FromHwnd(FosterParent); + + width = (float) (g.MeasureString (magic_string, font).Width / magic_number); + return new SizeF(width, font.Height); + } + + internal override IntPtr GetParent(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd != null && hwnd.parent != null) { + return hwnd.parent.Handle; + } + return IntPtr.Zero; + } + + // This is a nop on win32 and x11 + internal override IntPtr GetPreviousWindow(IntPtr handle) + { + return handle; + } + + internal override void GetCursorPos(IntPtr handle, out int x, out int y) + { + IntPtr use_handle; + IntPtr root; + IntPtr child; + int root_x; + int root_y; + int win_x; + int win_y; + int keys_buttons; + + if (handle != IntPtr.Zero) { + use_handle = Hwnd.ObjectFromHandle(handle).client_window; + } else { + use_handle = RootWindow; + } + + lock (XlibLock) { + QueryPointer (DisplayHandle, use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons); + } + + if (handle != IntPtr.Zero) { + x = win_x; + y = win_y; + } else { + x = root_x; + y = root_y; + } + } + + internal override IntPtr GetFocus() + { + return FocusWindow; + } + + + internal override bool GetFontMetrics(Graphics g, Font font, out int ascent, out int descent) + { + FontFamily ff = font.FontFamily; + ascent = ff.GetCellAscent (font.Style); + descent = ff.GetCellDescent (font.Style); + return true; + } + + internal override Point GetMenuOrigin(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) { + return hwnd.MenuOrigin; + } + return Point.Empty; + } + + [MonoTODO("Implement filtering")] + internal override bool GetMessage(Object queue_id, ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax) + { + XEvent xevent; + bool client; + Hwnd hwnd; + + ProcessNextMessage: + + if (((XEventQueue)queue_id).Count > 0) { + xevent = (XEvent) ((XEventQueue)queue_id).Dequeue (); + } else { + UpdateMessageQueue ((XEventQueue)queue_id); + + if (((XEventQueue)queue_id).Count > 0) { + xevent = (XEvent) ((XEventQueue)queue_id).Dequeue (); + } else if (((XEventQueue)queue_id).Paint.Count > 0) { + xevent = ((XEventQueue)queue_id).Paint.Dequeue(); + } else { + msg.hwnd= IntPtr.Zero; + msg.message = Msg.WM_ENTERIDLE; + return true; + } + } + + hwnd = Hwnd.GetObjectFromWindow(xevent.AnyEvent.window); + +#if DriverDebugDestroy + if (hwnd != null) + if (hwnd.zombie) + Console.WriteLine ( "GetMessage zombie, got Event: " + xevent.ToString () + " for 0x{0:x}", hwnd.Handle.ToInt32()); + else + Console.WriteLine ( "GetMessage, got Event: " + xevent.ToString () + " for 0x{0:x}", hwnd.Handle.ToInt32()); +#endif + // Handle messages for windows that are already or are about to be destroyed. + + // we need a special block for this because unless we remove the hwnd from the paint + // queue it will always stay there (since we don't handle the expose), and we'll + // effectively loop infinitely trying to repaint a non-existant window. + if (hwnd != null && hwnd.zombie && xevent.type == XEventName.Expose) { + hwnd.expose_pending = hwnd.nc_expose_pending = false; + hwnd.Queue.Paint.Remove (hwnd); + goto ProcessNextMessage; + } + + // We need to make sure we only allow DestroyNotify events through for zombie + // hwnds, since much of the event handling code makes requests using the hwnd's + // client_window, and that'll result in BadWindow errors if there's some lag + // between the XDestroyWindow call and the DestroyNotify event. + if (hwnd == null || hwnd.zombie && xevent.AnyEvent.type != XEventName.ClientMessage) { + DriverDebug("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}", xevent.type, xevent.AnyEvent.window.ToInt32()); + goto ProcessNextMessage; + } + + + // If we get here, that means the window is no more but there are Client Messages + // to be processed, probably a Posted message (for instance, an WM_ACTIVATE message) + // We don't want anything else to run but the ClientMessage block, so reset all hwnd + // properties that might cause other processing to occur. + if (hwnd.zombie) { + hwnd.resizing_or_moving = false; + } + + if (hwnd.client_window == xevent.AnyEvent.window) { + client = true; + //Console.WriteLine("Client message {1}, sending to window {0:X}", msg.hwnd.ToInt32(), xevent.type); + } else { + client = false; + //Console.WriteLine("Non-Client message, sending to window {0:X}", msg.hwnd.ToInt32()); + } + + msg.hwnd = hwnd.Handle; + + // Windows sends WM_ENTERSIZEMOVE when a form resize/move operation starts and WM_EXITSIZEMOVE + // when it is done. The problem in X11 is that there is no concept of start-end of a moving/sizing. + // Configure events ("this window has resized/moved") are sent for each step of the resize. We send a + // WM_ENTERSIZEMOVE when we get the first Configure event. The problem is the WM_EXITSIZEMOVE. + // + // - There is no way for us to know which is the last Configure event. We can't traverse the events + // queue, because the next configure event might not be pending yet. + // - We can't get ButtonPress/Release events for the window decorations, because they are not part + // of the window(s) we manage. + // - We can't rely on the mouse state to change to "up" before the last Configure event. It doesn't. + // + // We are almost 100% guaranteed to get another event (e.g Expose or other), but we can't know for sure + // which, so we have here to check if the mouse buttons state is "up" and send the WM_EXITSIZEMOVE + // + if (hwnd.resizing_or_moving) { + int root_x, root_y, win_x, win_y, keys_buttons; + IntPtr root, child; + XQueryPointer (DisplayHandle, hwnd.Handle, out root, out child, out root_x, out root_y, + out win_x, out win_y, out keys_buttons); + if ((keys_buttons & (int)MouseKeyMasks.Button1Mask) == 0 && + (keys_buttons & (int)MouseKeyMasks.Button2Mask) == 0 && + (keys_buttons & (int)MouseKeyMasks.Button3Mask) == 0) { + hwnd.resizing_or_moving = false; + SendMessage (hwnd.Handle, Msg.WM_EXITSIZEMOVE, IntPtr.Zero, IntPtr.Zero); + } + } + + // + // If you add a new event to this switch make sure to add it in + // UpdateMessage also unless it is not coming through the X event system. + // + switch(xevent.type) { + case XEventName.KeyPress: { + Keyboard.KeyEvent (FocusWindow, xevent, ref msg); + + // F1 key special case - WM_HELP sending + if (msg.wParam == (IntPtr)VirtualKeys.VK_F1 || msg.wParam == (IntPtr)VirtualKeys.VK_HELP) { + // Send wM_HELP and then return it as a keypress message in + // case it needs to be preproccessed. + HELPINFO helpInfo = new HELPINFO (); + GetCursorPos (IntPtr.Zero, out helpInfo.MousePos.x, out helpInfo.MousePos.y); + IntPtr helpInfoPtr = Marshal.AllocHGlobal (Marshal.SizeOf (helpInfo)); + Marshal.StructureToPtr (helpInfo, helpInfoPtr, true); + NativeWindow.WndProc (FocusWindow, Msg.WM_HELP, IntPtr.Zero, helpInfoPtr); + Marshal.FreeHGlobal (helpInfoPtr); + } + break; + } + + case XEventName.KeyRelease: { + Keyboard.KeyEvent (FocusWindow, xevent, ref msg); + break; + } + + case XEventName.ButtonPress: { + switch(xevent.ButtonEvent.button) { + case 1: { + MouseState |= MouseButtons.Left; + if (client) { + msg.message = Msg.WM_LBUTTONDOWN; + msg.wParam = GetMousewParam (0); + } else { + msg.message = Msg.WM_NCLBUTTONDOWN; + msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y); + MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + break; + } + + case 2: { + MouseState |= MouseButtons.Middle; + if (client) { + msg.message = Msg.WM_MBUTTONDOWN; + msg.wParam = GetMousewParam (0); + } else { + msg.message = Msg.WM_NCMBUTTONDOWN; + msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y); + MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + break; + } + + case 3: { + MouseState |= MouseButtons.Right; + if (client) { + msg.message = Msg.WM_RBUTTONDOWN; + msg.wParam = GetMousewParam (0); + } else { + msg.message = Msg.WM_NCRBUTTONDOWN; + msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y); + MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + break; + } + + case 4: { + msg.hwnd = FocusWindow; + msg.message=Msg.WM_MOUSEWHEEL; + msg.wParam=GetMousewParam(120); + break; + } + + case 5: { + msg.hwnd = FocusWindow; + msg.message=Msg.WM_MOUSEWHEEL; + msg.wParam=GetMousewParam(-120); + break; + } + + } + + msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x); + mouse_position.X = xevent.ButtonEvent.x; + mouse_position.Y = xevent.ButtonEvent.y; + + if (!hwnd.Enabled) { + IntPtr dummy; + + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates(DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow, xevent.ButtonEvent.x, xevent.ButtonEvent.y, out xevent.ButtonEvent.x, out xevent.ButtonEvent.y, out dummy); + msg.lParam = (IntPtr)(mouse_position.Y << 16 | mouse_position.X); + } + + if (Grab.Hwnd != IntPtr.Zero) { + msg.hwnd = Grab.Hwnd; + } + + if (ClickPending.Pending && ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) && (msg.wParam == ClickPending.wParam) && (msg.lParam == ClickPending.lParam) && (msg.message == ClickPending.Message))) { + // Looks like a genuine double click, clicked twice on the same spot with the same keys + switch(xevent.ButtonEvent.button) { + case 1: { + msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK; + break; + } + + case 2: { + msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK; + break; + } + + case 3: { + msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK; + break; + } + } + ClickPending.Pending = false; + } else { + ClickPending.Pending = true; + ClickPending.Hwnd = msg.hwnd; + ClickPending.Message = msg.message; + ClickPending.wParam = msg.wParam; + ClickPending.lParam = msg.lParam; + ClickPending.Time = (long)xevent.ButtonEvent.time; + } + + if (msg.message == Msg.WM_LBUTTONDOWN || msg.message == Msg.WM_MBUTTONDOWN || msg.message == Msg.WM_RBUTTONDOWN) { + SendParentNotify(msg.hwnd, msg.message, mouse_position.X, mouse_position.Y); + } + + break; + } + + case XEventName.ButtonRelease: { + switch(xevent.ButtonEvent.button) { + case 1: { + if (client) { + msg.message = Msg.WM_LBUTTONUP; + } else { + msg.message = Msg.WM_NCLBUTTONUP; + msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y); + MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + MouseState &= ~MouseButtons.Left; + msg.wParam = GetMousewParam (0); + break; + } + + case 2: { + if (client) { + msg.message = Msg.WM_MBUTTONUP; + } else { + msg.message = Msg.WM_NCMBUTTONUP; + msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y); + MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + MouseState &= ~MouseButtons.Middle; + msg.wParam = GetMousewParam (0); + break; + } + + case 3: { + if (client) { + msg.message = Msg.WM_RBUTTONUP; + } else { + msg.message = Msg.WM_NCRBUTTONUP; + msg.wParam = (IntPtr) NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y); + MenuToScreen (xevent.AnyEvent.window, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + MouseState &= ~MouseButtons.Right; + msg.wParam = GetMousewParam (0); + break; + } + + case 4: { + goto ProcessNextMessage; + } + + case 5: { + goto ProcessNextMessage; + } + } + + if (!hwnd.Enabled) { + IntPtr dummy; + + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates(DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow, xevent.ButtonEvent.x, xevent.ButtonEvent.y, out xevent.ButtonEvent.x, out xevent.ButtonEvent.y, out dummy); + msg.lParam = (IntPtr)(mouse_position.Y << 16 | mouse_position.X); + } + + if (Grab.Hwnd != IntPtr.Zero) { + msg.hwnd = Grab.Hwnd; + } + + msg.lParam=(IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x); + mouse_position.X = xevent.ButtonEvent.x; + mouse_position.Y = xevent.ButtonEvent.y; + + // Win32 splurts MouseMove events all over the place, regardless of whether the mouse is actually moving or + // not, especially after mousedown and mouseup. To support apps relying on mousemove events between and after + // mouse clicks to repaint or whatever, we generate a mousemove event here. *sigh* + if (msg.message == Msg.WM_LBUTTONUP || msg.message == Msg.WM_MBUTTONUP || msg.message == Msg.WM_RBUTTONUP) { + XEvent motionEvent = new XEvent (); + motionEvent.type = XEventName.MotionNotify; + motionEvent.MotionEvent.display = DisplayHandle; + motionEvent.MotionEvent.window = xevent.ButtonEvent.window; + motionEvent.MotionEvent.x = xevent.ButtonEvent.x; + motionEvent.MotionEvent.y = xevent.ButtonEvent.y; + hwnd.Queue.EnqueueLocked (motionEvent); + } + break; + } + + case XEventName.MotionNotify: { + if (client) { + DriverDebug("GetMessage(): Window {0:X} MotionNotify x={1} y={2}", + client ? hwnd.client_window.ToInt32() : hwnd.whole_window.ToInt32(), + xevent.MotionEvent.x, xevent.MotionEvent.y); + + if (Grab.Hwnd != IntPtr.Zero) { + msg.hwnd = Grab.Hwnd; + } else { + if (hwnd.Enabled) { + NativeWindow.WndProc(msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT); + } + } + + if (xevent.MotionEvent.is_hint != 0) + { + IntPtr root, child; + int mask; + XQueryPointer (DisplayHandle, xevent.AnyEvent.window, + out root, out child, + out xevent.MotionEvent.x_root, + out xevent.MotionEvent.y_root, + out xevent.MotionEvent.x, + out xevent.MotionEvent.y, out mask); + } + + msg.message = Msg.WM_MOUSEMOVE; + msg.wParam = GetMousewParam(0); + msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF); + + if (!hwnd.Enabled) { + IntPtr dummy; + + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates(DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow, xevent.MotionEvent.x, xevent.MotionEvent.y, out xevent.MotionEvent.x, out xevent.MotionEvent.y, out dummy); + msg.lParam = (IntPtr)(mouse_position.Y << 16 | mouse_position.X); + } + + mouse_position.X = xevent.MotionEvent.x; + mouse_position.Y = xevent.MotionEvent.y; + + if ((HoverState.Timer.Enabled) && + (((mouse_position.X + HoverState.Size.Width) < HoverState.X) || + ((mouse_position.X - HoverState.Size.Width) > HoverState.X) || + ((mouse_position.Y + HoverState.Size.Height) < HoverState.Y) || + ((mouse_position.Y - HoverState.Size.Height) > HoverState.Y))) { + HoverState.Timer.Stop(); + HoverState.Timer.Start(); + HoverState.X = mouse_position.X; + HoverState.Y = mouse_position.Y; + } + + break; + } else { + HitTest ht; + IntPtr dummy; + + DriverDebug("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}", + client ? hwnd.client_window.ToInt32() : hwnd.whole_window.ToInt32(), + xevent.MotionEvent.x, xevent.MotionEvent.y); + msg.message = Msg.WM_NCMOUSEMOVE; + + if (!hwnd.Enabled) { + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates(DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle(msg.hwnd).ClientWindow, xevent.MotionEvent.x, xevent.MotionEvent.y, out xevent.MotionEvent.x, out xevent.MotionEvent.y, out dummy); + msg.lParam = (IntPtr)(mouse_position.Y << 16 | mouse_position.X); + } + + ht = NCHitTest (hwnd, xevent.MotionEvent.x, xevent.MotionEvent.y); + NativeWindow.WndProc(hwnd.client_window, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)ht); + + mouse_position.X = xevent.MotionEvent.x; + mouse_position.Y = xevent.MotionEvent.y; + } + + break; + } + + case XEventName.EnterNotify: { + if (!hwnd.Enabled) { + goto ProcessNextMessage; + } + if (xevent.CrossingEvent.mode == NotifyMode.NotifyGrab || xevent.AnyEvent.window != hwnd.client_window) { + goto ProcessNextMessage; + } + if (xevent.CrossingEvent.mode == NotifyMode.NotifyUngrab) { // Pseudo motion caused by grabbing + if (LastPointerWindow == xevent.AnyEvent.window) + goto ProcessNextMessage; + + if (LastPointerWindow != IntPtr.Zero) { + Point enter_loc = new Point (xevent.ButtonEvent.x, xevent.ButtonEvent.y); + + // We need this due to EnterNotify being fired on all the parent Widgets + // of the Widget being grabbed, and obviously in that scenario we are not + // actuallty entering them + Widget ctrl = Widget.FromHandle (hwnd.client_window); + foreach (Widget child_Widget in ctrl.Widgets.GetAllWidgets ()) + if (child_Widget.Bounds.Contains (enter_loc)) + goto ProcessNextMessage; + + // A MouseLeave/LeaveNotify event is sent to the previous window + // until the mouse is ungrabbed, not when actually leaving its bounds + int x = xevent.CrossingEvent.x_root; + int y = xevent.CrossingEvent.y_root; + ScreenToClient (LastPointerWindow, ref x, ref y); + + XEvent leaveEvent = new XEvent (); + leaveEvent.type = XEventName.LeaveNotify; + leaveEvent.CrossingEvent.display = DisplayHandle; + leaveEvent.CrossingEvent.window = LastPointerWindow; + leaveEvent.CrossingEvent.x = x; + leaveEvent.CrossingEvent.y = y; + leaveEvent.CrossingEvent.mode = NotifyMode.NotifyNormal; + Hwnd last_pointer_hwnd = Hwnd.ObjectFromHandle (LastPointerWindow); + last_pointer_hwnd.Queue.EnqueueLocked (leaveEvent); + } + } + + LastPointerWindow = xevent.AnyEvent.window; + + msg.message = Msg.WM_MOUSE_ENTER; + HoverState.X = xevent.CrossingEvent.x; + HoverState.Y = xevent.CrossingEvent.y; + HoverState.Timer.Enabled = true; + HoverState.Window = xevent.CrossingEvent.window; + + // Win32 sends a WM_MOUSEMOVE after mouse enter + XEvent motionEvent = new XEvent (); + motionEvent.type = XEventName.MotionNotify; + motionEvent.MotionEvent.display = DisplayHandle; + motionEvent.MotionEvent.window = xevent.ButtonEvent.window; + motionEvent.MotionEvent.x = xevent.ButtonEvent.x; + motionEvent.MotionEvent.y = xevent.ButtonEvent.y; + hwnd.Queue.EnqueueLocked (motionEvent); + break; + } + + case XEventName.LeaveNotify: { + if (xevent.CrossingEvent.mode == NotifyMode.NotifyUngrab) { + WindowUngrabbed (hwnd.Handle); + goto ProcessNextMessage; + } + if (!hwnd.Enabled) { + goto ProcessNextMessage; + } + if ((xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) || (xevent.CrossingEvent.window != hwnd.client_window)) { + goto ProcessNextMessage; + } + // If a grab is taking place, ignore it - we handle it in EnterNotify + if (Grab.Hwnd != IntPtr.Zero) + goto ProcessNextMessage; + + // Reset the cursor explicitly on X11. + // X11 remembers the last set cursor for the window and in cases where + // the Widget won't get a WM_SETCURSOR X11 will restore the last + // known cursor, which we don't want. + // + SetCursor (hwnd.client_window, IntPtr.Zero); + + msg.message=Msg.WM_MOUSELEAVE; + HoverState.Timer.Enabled = false; + HoverState.Window = IntPtr.Zero; + break; + } + + #if later + case XEventName.CreateNotify: { + if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { + msg.message = WM_CREATE; + // Set up CreateStruct + } else { + goto ProcessNextMessage; + } + break; + } + #endif + + + case XEventName.ReparentNotify: { + if (hwnd.parent == null) { // Toplevel + if ((xevent.ReparentEvent.parent != IntPtr.Zero) && (xevent.ReparentEvent.window == hwnd.whole_window)) { + hwnd.Reparented = true; + + // The location given by the event is not reliable between different wm's, + // so use an alternative way of getting it. + Point location = GetTopLevelWindowLocation (hwnd); + hwnd.X = location.X; + hwnd.Y = location.Y; + + if (hwnd.opacity != 0xffffffff) { + IntPtr opacity; + + opacity = (IntPtr)(Int32)hwnd.opacity; + XChangeProperty(DisplayHandle, XGetParent(hwnd.whole_window), _NET_WM_WINDOW_OPACITY, (IntPtr)Atom.XA_CARDINAL, 32, PropertyMode.Replace, ref opacity, 1); + } + SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, msg.wParam, msg.lParam); + goto ProcessNextMessage; + } else { + hwnd.Reparented = false; + goto ProcessNextMessage; + } + } + goto ProcessNextMessage; + } + + case XEventName.ConfigureNotify: { + if (!client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas + DriverDebug("GetMessage(): Window {0:X} ConfigureNotify x={1} y={2} width={3} height={4}", + hwnd.client_window.ToInt32(), xevent.ConfigureEvent.x, + xevent.ConfigureEvent.y, xevent.ConfigureEvent.width, xevent.ConfigureEvent.height); + + lock (hwnd.configure_lock) { + Form form = Widget.FromHandle (hwnd.client_window) as Form; + if (form != null && !hwnd.resizing_or_moving) { + if (hwnd.x != form.Bounds.X || hwnd.y != form.Bounds.Y) { + SendMessage (form.Handle, Msg.WM_SYSCOMMAND, (IntPtr)SystemCommands.SC_MOVE, IntPtr.Zero); + hwnd.resizing_or_moving = true; + } else if (hwnd.width != form.Bounds.Width || hwnd.height != form.Bounds.Height) { + SendMessage (form.Handle, Msg.WM_SYSCOMMAND, (IntPtr)SystemCommands.SC_SIZE, IntPtr.Zero); + hwnd.resizing_or_moving = true; + } + if (hwnd.resizing_or_moving) + SendMessage (form.Handle, Msg.WM_ENTERSIZEMOVE, IntPtr.Zero, IntPtr.Zero); + } + + SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + hwnd.configure_pending = false; + + // We need to adjust our client window to track the resize of whole_window + if (hwnd.whole_window != hwnd.client_window) + PerformNCCalc(hwnd); + } + } + goto ProcessNextMessage; + } + + case XEventName.FocusIn: { + // We received focus. We use X11 focus only to know if the app window does or does not have focus + // We do not track the actual focussed window via it. Instead, this is done via FocusWindow internally + // Receiving focus means we've gotten activated and therefore we need to let the actual FocusWindow know + // about it having focus again + if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear) { + goto ProcessNextMessage; + } + + + if (FocusWindow == IntPtr.Zero) { + Widget c = Widget.FromHandle (hwnd.client_window); + + if (c == null) + goto ProcessNextMessage; + Form form = c.FindForm (); + if (form == null) + goto ProcessNextMessage; + + if (ActiveWindow != form.Handle) { + ActiveWindow = form.Handle; + SendMessage (ActiveWindow, Msg.WM_ACTIVATE, (IntPtr) WindowActiveFlags.WA_ACTIVE, IntPtr.Zero); + } + goto ProcessNextMessage; + } + Keyboard.FocusIn (FocusWindow); + SendMessage(FocusWindow, Msg.WM_SETFOCUS, IntPtr.Zero, IntPtr.Zero); + goto ProcessNextMessage; + } + + case XEventName.FocusOut: { + // Se the comment for our FocusIn handler + if (xevent.FocusChangeEvent.detail != NotifyDetail.NotifyNonlinear) { + goto ProcessNextMessage; + } + + while (Keyboard.ResetKeyState(FocusWindow, ref msg)) { + SendMessage(FocusWindow, msg.message, msg.wParam, msg.lParam); + } + + Keyboard.FocusOut(hwnd.client_window); + SendMessage(FocusWindow, Msg.WM_KILLFOCUS, IntPtr.Zero, IntPtr.Zero); + goto ProcessNextMessage; + } + + // We are already firing WM_SHOWWINDOW messages in the proper places, but I'm leaving this code + // in case we break a scenario not taken into account in the tests + case XEventName.MapNotify: { + /*if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas + hwnd.mapped = true; + msg.message = Msg.WM_SHOWWINDOW; + msg.wParam = (IntPtr) 1; + // XXX we're missing the lParam.. + break; + }*/ + goto ProcessNextMessage; + } + + case XEventName.UnmapNotify: { + /*if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas + hwnd.mapped = false; + msg.message = Msg.WM_SHOWWINDOW; + msg.wParam = (IntPtr) 0; + // XXX we're missing the lParam.. + break; + }*/ + goto ProcessNextMessage; + } + + case XEventName.Expose: { + if (!hwnd.Mapped) { + if (client) { + hwnd.expose_pending = false; + } else { + hwnd.nc_expose_pending = false; + } + goto ProcessNextMessage; + } + + if (client) { + if (!hwnd.expose_pending) { + goto ProcessNextMessage; + } + } else { + if (!hwnd.nc_expose_pending) { + goto ProcessNextMessage; + } + + switch (hwnd.border_style) { + case FormBorderStyle.Fixed3D: { + Graphics g; + + g = Graphics.FromHwnd(hwnd.whole_window); + if (hwnd.border_static) + WidgetPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), Border3DStyle.SunkenOuter); + else + WidgetPaint.DrawBorder3D(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), Border3DStyle.Sunken); + g.Dispose(); + break; + } + + case FormBorderStyle.FixedSingle: { + Graphics g; + + g = Graphics.FromHwnd(hwnd.whole_window); + WidgetPaint.DrawBorder(g, new Rectangle(0, 0, hwnd.Width, hwnd.Height), Color.Black, ButtonBorderStyle.Solid); + g.Dispose(); + break; + } + } + DriverDebug("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}", + hwnd.client_window.ToInt32(), xevent.ExposeEvent.x, xevent.ExposeEvent.y, + xevent.ExposeEvent.width, xevent.ExposeEvent.height); + + Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); + Region region = new Region (rect); + IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed + msg.message = Msg.WM_NCPAINT; + msg.wParam = hrgn == IntPtr.Zero ? (IntPtr)1 : hrgn; + msg.refobject = region; + break; + } + DriverDebug("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}", + hwnd.client_window.ToInt32(), xevent.ExposeEvent.x, xevent.ExposeEvent.y, + xevent.ExposeEvent.width, xevent.ExposeEvent.height); + if (Caret.Visible == true) { + Caret.Paused = true; + HideCaret(); + } + + if (Caret.Visible == true) { + ShowCaret(); + Caret.Paused = false; + } + msg.message = Msg.WM_PAINT; + break; + } + + case XEventName.DestroyNotify: { + + // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children + hwnd = Hwnd.ObjectFromHandle(xevent.DestroyWindowEvent.window); + + // We may get multiple for the same window, act only one the first (when Hwnd still knows about it) + if ((hwnd != null) && (hwnd.client_window == xevent.DestroyWindowEvent.window)) { + CleanupCachedWindows (hwnd); + + DriverDebug("Received X11 Destroy Notification for {0}", XplatUI.Window(hwnd.client_window)); + + msg.hwnd = hwnd.client_window; + msg.message=Msg.WM_DESTROY; + hwnd.Dispose(); + } else { + goto ProcessNextMessage; + } + + break; + } + + case XEventName.ClientMessage: { + if (Dnd.HandleClientMessage (ref xevent)) { + goto ProcessNextMessage; + } + + if (xevent.ClientMessageEvent.message_type == AsyncAtom) { + XplatUIDriverSupport.ExecuteClientMessage((GCHandle)xevent.ClientMessageEvent.ptr1); + goto ProcessNextMessage; + } + + if (xevent.ClientMessageEvent.message_type == HoverState.Atom) { + msg.message = Msg.WM_MOUSEHOVER; + msg.wParam = GetMousewParam(0); + msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1); + return true; + } + + if (xevent.ClientMessageEvent.message_type == (IntPtr)PostAtom) { + DebugHelper.Indent (); + DebugHelper.WriteLine (String.Format ("Posted message:" + (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 () + " for 0x{0:x}", xevent.ClientMessageEvent.ptr1.ToInt32 ())); + DebugHelper.Unindent (); + msg.hwnd = xevent.ClientMessageEvent.ptr1; + msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 (); + msg.wParam = xevent.ClientMessageEvent.ptr3; + msg.lParam = xevent.ClientMessageEvent.ptr4; + if (msg.message == (Msg)Msg.WM_QUIT) + return false; + else + return true; + } + + if (xevent.ClientMessageEvent.message_type == _XEMBED) { +#if DriverDebugXEmbed + Console.WriteLine("GOT EMBED MESSAGE {0:X}, detail {1:X}", xevent.ClientMessageEvent.ptr2.ToInt32(), xevent.ClientMessageEvent.ptr3.ToInt32()); +#endif + + if (xevent.ClientMessageEvent.ptr2.ToInt32() == (int)XEmbedMessage.EmbeddedNotify) { + XSizeHints hints = new XSizeHints(); + IntPtr dummy; + + XGetWMNormalHints(DisplayHandle, hwnd.whole_window, ref hints, out dummy); + + hwnd.width = hints.max_width; + hwnd.height = hints.max_height; + hwnd.ClientRect = Rectangle.Empty; + SendMessage(msg.hwnd, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + } + } + + if (xevent.ClientMessageEvent.message_type == WM_PROTOCOLS) { + if (xevent.ClientMessageEvent.ptr1 == WM_DELETE_WINDOW) { + SendMessage (msg.hwnd, Msg.WM_SYSCOMMAND, (IntPtr)SystemCommands.SC_CLOSE, IntPtr.Zero); + msg.message = Msg.WM_CLOSE; + return true; + } + + // We should not get this, but I'll leave the code in case we need it in the future + if (xevent.ClientMessageEvent.ptr1 == WM_TAKE_FOCUS) { + goto ProcessNextMessage; + } + } + goto ProcessNextMessage; + } + + default: { + goto ProcessNextMessage; + } + } + + return true; + } + + HitTest NCHitTest (Hwnd hwnd, int x, int y) + { + // The hit test is sent in screen coordinates + IntPtr dummy; + int screen_x, screen_y; + XTranslateCoordinates (DisplayHandle, hwnd.WholeWindow, RootWindow, x, y, out screen_x, out screen_y, out dummy); + return (HitTest) NativeWindow.WndProc (hwnd.client_window, Msg.WM_NCHITTEST, IntPtr.Zero, + (IntPtr) (screen_y << 16 | screen_x & 0xFFFF)); + } + + // Our very basic implementation of MoveResize - we can extend it later + // *if* needed + internal override void BeginMoveResize (IntPtr handle) + { + // We *need* to ungrab the pointer in the current display + XplatUI.UngrabWindow (Grab.Hwnd); + + int x_root, y_root; + GetCursorPos (IntPtr.Zero, out x_root, out y_root); + + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + SendNetWMMessage (hwnd.whole_window, _NET_WM_MOVERESIZE, (IntPtr) x_root, (IntPtr) y_root, + (IntPtr) NetWmMoveResize._NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT, + (IntPtr) 1); // left button + } + + internal override bool GetText(IntPtr handle, out string text) + { + + lock (XlibLock) { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + + XGetWindowProperty(DisplayHandle, handle, + _NET_WM_NAME, IntPtr.Zero, new IntPtr (1), false, + UTF8_STRING, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if ((long)nitems > 0 && prop != IntPtr.Zero) { + text = Marshal.PtrToStringUni (prop, (int)nitems); + XFree (prop); + return true; + } + else { + // fallback on the non-_NET property + IntPtr textptr; + + textptr = IntPtr.Zero; + + XFetchName(DisplayHandle, Hwnd.ObjectFromHandle(handle).whole_window, ref textptr); + if (textptr != IntPtr.Zero) { + text = Marshal.PtrToStringAnsi(textptr); + XFree(textptr); + return true; + } else { + text = ""; + return false; + } + } + } + } + + internal override void GetWindowPos(IntPtr handle, bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd != null) { + x = hwnd.x; + y = hwnd.y; + width = hwnd.width; + height = hwnd.height; + + PerformNCCalc(hwnd); + + client_width = hwnd.ClientRect.Width; + client_height = hwnd.ClientRect.Height; + + return; + } + + // Should we throw an exception or fail silently? + // throw new ArgumentException("Called with an invalid window handle", "handle"); + + x = 0; + y = 0; + width = 0; + height = 0; + client_width = 0; + client_height = 0; + } + + internal override FormWindowState GetWindowState(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd.cached_window_state == (FormWindowState)(-1)) + hwnd.cached_window_state = UpdateWindowState (handle); + + return hwnd.cached_window_state; + } + + FormWindowState UpdateWindowState (IntPtr handle) { + IntPtr actual_atom; + int actual_format; + IntPtr nitems; + IntPtr bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr atom; + int maximized; + bool minimized; + XWindowAttributes attributes; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + maximized = 0; + minimized = false; + XGetWindowProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE, IntPtr.Zero, new IntPtr (256), false, (IntPtr)Atom.XA_ATOM, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if (((long)nitems > 0) && (prop != IntPtr.Zero)) { + for (int i = 0; i < (long)nitems; i++) { + atom = (IntPtr)Marshal.ReadInt32(prop, i * 4); + if ((atom == _NET_WM_STATE_MAXIMIZED_HORZ) || (atom == _NET_WM_STATE_MAXIMIZED_VERT)) { + maximized++; + } else if (atom == _NET_WM_STATE_HIDDEN) { + minimized = true; + } + } + XFree(prop); + } + + if (minimized) { + return FormWindowState.Minimized; + } else if (maximized == 2) { + return FormWindowState.Maximized; + } + + attributes = new XWindowAttributes(); + XGetWindowAttributes(DisplayHandle, hwnd.client_window, ref attributes); + if (attributes.map_state == MapState.IsUnmapped) { + return (FormWindowState)(-1); + } + + + return FormWindowState.Normal; + } + + internal override void GrabInfo(out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea) + { + handle = Grab.Hwnd; + GrabConfined = Grab.Confined; + GrabArea = Grab.Area; + } + + internal override void GrabWindow(IntPtr handle, IntPtr confine_to_handle) + { + Hwnd hwnd; + IntPtr confine_to_window; + + confine_to_window = IntPtr.Zero; + + if (confine_to_handle != IntPtr.Zero) { + XWindowAttributes attributes = new XWindowAttributes(); + + hwnd = Hwnd.ObjectFromHandle(confine_to_handle); + + lock (XlibLock) { + XGetWindowAttributes(DisplayHandle, hwnd.client_window, ref attributes); + } + Grab.Area.X = attributes.x; + Grab.Area.Y = attributes.y; + Grab.Area.Width = attributes.width; + Grab.Area.Height = attributes.height; + Grab.Confined = true; + confine_to_window = hwnd.client_window; + } + + Grab.Hwnd = handle; + + hwnd = Hwnd.ObjectFromHandle(handle); + + lock (XlibLock) { + XGrabPointer(DisplayHandle, hwnd.client_window, false, + EventMask.ButtonPressMask | EventMask.ButtonMotionMask | + EventMask.ButtonReleaseMask | EventMask.PointerMotionMask | + EventMask.PointerMotionHintMask | EventMask.LeaveWindowMask, + GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_to_window, IntPtr.Zero, IntPtr.Zero); + } + } + + internal override void UngrabWindow(IntPtr hwnd) + { + lock (XlibLock) { + XUngrabPointer(DisplayHandle, IntPtr.Zero); + XFlush(DisplayHandle); + } + WindowUngrabbed (hwnd); + } + + void WindowUngrabbed (IntPtr hwnd) { + bool was_grabbed = Grab.Hwnd != IntPtr.Zero; + + Grab.Hwnd = IntPtr.Zero; + Grab.Confined = false; + + if (was_grabbed) { + // lparam should be the handle to the window gaining the mouse capture, + // but X doesn't seem to give us that information. + // Also only generate WM_CAPTURECHANGED if the window actually was grabbed. + // X will send a NotifyUngrab, but since it comes late sometimes we're + // calling WindowUngrabbed directly from UngrabWindow in order to send + // this WM right away. + SendMessage (hwnd, Msg.WM_CAPTURECHANGED, IntPtr.Zero, IntPtr.Zero); + } + } + + internal override void HandleException(Exception e) + { + StackTrace st = new StackTrace(e, true); + Console.WriteLine("Exception '{0}'", e.Message+st.ToString()); + Console.WriteLine("{0}{1}", e.Message, st.ToString()); + } + + internal override void Invalidate(IntPtr handle, Rectangle rc, bool clear) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (clear) { + AddExpose (hwnd, true, hwnd.X, hwnd.Y, hwnd.Width, hwnd.Height); + } else { + AddExpose (hwnd, true, rc.X, rc.Y, rc.Width, rc.Height); + } + } + + internal override void InvalidateNC (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + AddExpose (hwnd, hwnd.WholeWindow == hwnd.ClientWindow, 0, 0, hwnd.Width, hwnd.Height); + } + + internal override bool IsEnabled(IntPtr handle) + { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + return (hwnd != null && hwnd.Enabled); + } + + internal override bool IsVisible(IntPtr handle) + { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + return (hwnd != null && hwnd.visible); + } + + internal override void KillTimer(Timer timer) + { + XEventQueue queue = (XEventQueue) MessageQueues [timer.thread]; + + if (queue == null) { + // This isn't really an error, MS doesn't start the timer if + // it has no assosciated queue. In this case, remove the timer + // from the list of unattached timers (if it was enabled). + lock (unattached_timer_list) { + if (unattached_timer_list.Contains (timer)) + unattached_timer_list.Remove (timer); + } + return; + } + queue.timer_list.Remove (timer); + } + + internal override void MenuToScreen(IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + lock (XlibLock) { + XTranslateCoordinates(DisplayHandle, hwnd.whole_window, RootWindow, x, y, out dest_x_return, out dest_y_return, out child); + } + + x = dest_x_return; + y = dest_y_return; + } + + internal override void OverrideCursor(IntPtr cursor) + { + if (Grab.Hwnd != IntPtr.Zero) { + XChangeActivePointerGrab (DisplayHandle, + EventMask.ButtonMotionMask | + EventMask.PointerMotionMask | + EventMask.PointerMotionHintMask | + EventMask.ButtonPressMask | + EventMask.ButtonReleaseMask, + cursor, IntPtr.Zero); + return; + } + + OverrideCursorHandle = cursor; + } + + internal override PaintEventArgs PaintEventStart(ref Message msg, IntPtr handle, bool client) + { + PaintEventArgs paint_event; + Hwnd hwnd; + Hwnd paint_hwnd; + + // + // handle (and paint_hwnd) refers to the window that is should be painted. + // msg.HWnd (and hwnd) refers to the window that got the paint message. + // + + hwnd = Hwnd.ObjectFromHandle(msg.HWnd); + if (msg.HWnd == handle) { + paint_hwnd = hwnd; + } else { + paint_hwnd = Hwnd.ObjectFromHandle (handle); + } + + if (Caret.Visible == true) { + Caret.Paused = true; + HideCaret(); + } + + Graphics dc; + + if (client) { + dc = Graphics.FromHwnd (paint_hwnd.client_window); + + Region clip_region = new Region (); + clip_region.MakeEmpty(); + + foreach (Rectangle r in hwnd.ClipRectangles) { + /* Expand the region slightly. + * See bug 464464. + */ + Rectangle r2 = Rectangle.FromLTRB (r.Left, r.Top, r.Right, r.Bottom + 1); + clip_region.Union (r2); + } + + if (hwnd.UserClip != null) { + clip_region.Intersect(hwnd.UserClip); + } + + dc.Clip = clip_region; + paint_event = new PaintEventArgs(dc, hwnd.Invalid); + hwnd.expose_pending = false; + + hwnd.ClearInvalidArea(); + + hwnd.drawing_stack.Push (paint_event); + hwnd.drawing_stack.Push (dc); + + return paint_event; + } else { + dc = Graphics.FromHwnd (paint_hwnd.whole_window); + + if (!hwnd.nc_invalid.IsEmpty) { + dc.SetClip (hwnd.nc_invalid); + paint_event = new PaintEventArgs(dc, hwnd.nc_invalid); + } else { + paint_event = new PaintEventArgs(dc, new Rectangle(0, 0, hwnd.width, hwnd.height)); + } + hwnd.nc_expose_pending = false; + + hwnd.ClearNcInvalidArea (); + + hwnd.drawing_stack.Push (paint_event); + hwnd.drawing_stack.Push (dc); + + return paint_event; + } + } + + internal override void PaintEventEnd(ref Message msg, IntPtr handle, bool client) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (msg.HWnd); + + Graphics dc = (Graphics)hwnd.drawing_stack.Pop (); + dc.Flush(); + dc.Dispose(); + + PaintEventArgs pe = (PaintEventArgs)hwnd.drawing_stack.Pop(); + pe.SetGraphics (null); + pe.Dispose (); + + if (Caret.Visible == true) { + ShowCaret(); + Caret.Paused = false; + } + } + + [MonoTODO("Implement filtering and PM_NOREMOVE")] + internal override bool PeekMessage(Object queue_id, ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) + { + XEventQueue queue = (XEventQueue) queue_id; + bool pending; + + if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) { + throw new NotImplementedException("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag + } + + pending = false; + if (queue.Count > 0) { + pending = true; + } else { + // Only call UpdateMessageQueue if real events are pending + // otherwise we go to sleep on the socket + if (XPending(DisplayHandle) != 0) { + UpdateMessageQueue((XEventQueue)queue_id); + pending = true; + } else if (((XEventQueue)queue_id).Paint.Count > 0) { + pending = true; + } + } + + CheckTimers(queue.timer_list, DateTime.UtcNow); + + if (!pending) { + return false; + } + return GetMessage(queue_id, ref msg, hWnd, wFilterMin, wFilterMax); + } + + internal override bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam) + { + XEvent xevent = new XEvent (); + Hwnd hwnd = Hwnd.ObjectFromHandle(handle); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = DisplayHandle; + + if (hwnd != null) { + xevent.ClientMessageEvent.window = hwnd.whole_window; + } else { + xevent.ClientMessageEvent.window = IntPtr.Zero; + } + + xevent.ClientMessageEvent.message_type = (IntPtr) PostAtom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = handle; + xevent.ClientMessageEvent.ptr2 = (IntPtr) message; + xevent.ClientMessageEvent.ptr3 = wparam; + xevent.ClientMessageEvent.ptr4 = lparam; + + if (hwnd != null) + hwnd.Queue.EnqueueLocked (xevent); + else + ThreadQueue(Thread.CurrentThread).EnqueueLocked (xevent); + + return true; + } + + internal override void PostQuitMessage(int exitCode) + { + ApplicationContext ctx = Application.MWFThread.Current.Context; + Form f = ctx != null ? ctx.MainForm : null; + if (f != null) + PostMessage (Application.MWFThread.Current.Context.MainForm.window.Handle, Msg.WM_QUIT, IntPtr.Zero, IntPtr.Zero); + else + PostMessage (FosterParent, Msg.WM_QUIT, IntPtr.Zero, IntPtr.Zero); + XFlush(DisplayHandle); + } + + internal override void RequestAdditionalWM_NCMessages(IntPtr hwnd, bool hover, bool leave) + { + // TODO + } + + internal override void RequestNCRecalc(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) { + return; + } + + PerformNCCalc(hwnd); + SendMessage(handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + InvalidateNC(handle); + } + + internal override void ResetMouseHover(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd == null) { + return; + } + + HoverState.Timer.Enabled = true; + HoverState.X = mouse_position.X; + HoverState.Y = mouse_position.Y; + HoverState.Window = handle; + } + + + internal override void ScreenToClient(IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + lock (XlibLock) { + XTranslateCoordinates (DisplayHandle, RootWindow, hwnd.client_window, x, y, out dest_x_return, out dest_y_return, out child); + } + + x = dest_x_return; + y = dest_y_return; + } + + internal override void ScreenToMenu(IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + lock (XlibLock) { + XTranslateCoordinates (DisplayHandle, RootWindow, hwnd.whole_window, x, y, out dest_x_return, out dest_y_return, out child); + } + + Form form = Widget.FromHandle (handle) as Form; + if (form != null && form.window_manager != null) { + dest_y_return -= form.window_manager.TitleBarHeight; + } + + x = dest_x_return; + y = dest_y_return; + } + + bool GraphicsExposePredicate (IntPtr display, ref XEvent xevent, IntPtr arg) + { + return (xevent.type == XEventName.GraphicsExpose || xevent.type == XEventName.NoExpose) && + arg == xevent.GraphicsExposeEvent.drawable; + } + + delegate bool EventPredicate (IntPtr display, ref XEvent xevent, IntPtr arg); + + void ProcessGraphicsExpose (Hwnd hwnd) + { + XEvent xevent = new XEvent (); + IntPtr handle = Hwnd.HandleFromObject (hwnd); + EventPredicate predicate = GraphicsExposePredicate; + + for (;;) { + XIfEvent (Display, ref xevent, predicate, handle); + if (xevent.type != XEventName.GraphicsExpose) + break; + + AddExpose (hwnd, xevent.ExposeEvent.window == hwnd.ClientWindow, xevent.GraphicsExposeEvent.x, xevent.GraphicsExposeEvent.y, + xevent.GraphicsExposeEvent.width, xevent.GraphicsExposeEvent.height); + + if (xevent.GraphicsExposeEvent.count == 0) + break; + } + } + + internal override void ScrollWindow(IntPtr handle, Rectangle area, int XAmount, int YAmount, bool with_children) + { + Hwnd hwnd; + IntPtr gc; + XGCValues gc_values; + + hwnd = Hwnd.ObjectFromHandle(handle); + + Rectangle r = Rectangle.Intersect (hwnd.Invalid, area); + if (!r.IsEmpty) { + /* We have an invalid area in the window we're scrolling. + Adjust our stored invalid rectangle to to match the scrolled amount */ + + r.X += XAmount; + r.Y += YAmount; + + if (r.X < 0) { + r.Width += r.X; + r.X =0; + } + + if (r.Y < 0) { + r.Height += r.Y; + r.Y =0; + } + + if (area.Contains (hwnd.Invalid)) + hwnd.ClearInvalidArea (); + hwnd.AddInvalidArea(r); + } + + gc_values = new XGCValues(); + + if (with_children) { + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + } + + gc = XCreateGC(DisplayHandle, hwnd.client_window, IntPtr.Zero, ref gc_values); + + Rectangle visible_rect = GetTotalVisibleArea (hwnd.client_window); + visible_rect.Intersect (area); + + Rectangle dest_rect = visible_rect; + dest_rect.Y += YAmount; + dest_rect.X += XAmount; + dest_rect.Intersect (area); + + Point src = new Point (dest_rect.X - XAmount, dest_rect.Y - YAmount); + XCopyArea (DisplayHandle, hwnd.client_window, hwnd.client_window, gc, src.X, src.Y, + dest_rect.Width, dest_rect.Height, dest_rect.X, dest_rect.Y); + + Rectangle dirty_area = GetDirtyArea (area, dest_rect, XAmount, YAmount); + AddExpose (hwnd, true, dirty_area.X, dirty_area.Y, dirty_area.Width, dirty_area.Height); + + ProcessGraphicsExpose (hwnd); + + XFreeGC(DisplayHandle, gc); + } + + internal override void ScrollWindow(IntPtr handle, int XAmount, int YAmount, bool with_children) + { + Hwnd hwnd; + Rectangle rect; + + hwnd = Hwnd.GetObjectFromWindow(handle); + + rect = hwnd.ClientRect; + rect.X = 0; + rect.Y = 0; + ScrollWindow(handle, rect, XAmount, YAmount, with_children); + } + + Rectangle GetDirtyArea (Rectangle total_area, Rectangle valid_area, int XAmount, int YAmount) + { + Rectangle dirty_area = total_area; + + if (YAmount > 0) + dirty_area.Height -= valid_area.Height; + else if (YAmount < 0) { + dirty_area.Height -= valid_area.Height; + dirty_area.Y += valid_area.Height; + } + + if (XAmount > 0) + dirty_area.Width -= valid_area.Width; + else if (XAmount < 0) { + dirty_area.Width -= valid_area.Width; + dirty_area.X += valid_area.Width; + } + + return dirty_area; + } + + Rectangle GetTotalVisibleArea (IntPtr handle) + { + Widget c = Widget.FromHandle (handle); + + Rectangle visible_area = c.ClientRectangle; + visible_area.Location = c.PointToScreen (Point.Empty); + + for (Widget parent = c.Parent; parent != null; parent = parent.Parent) { + if (!parent.IsHandleCreated || !parent.Visible) + return visible_area; // Non visible, not need to finish computations + + Rectangle r = parent.ClientRectangle; + r.Location = parent.PointToScreen (Point.Empty); + + visible_area.Intersect (r); + } + + visible_area.Location = c.PointToClient (visible_area.Location); + return visible_area; + } + + internal override void SendAsyncMethod (AsyncMethodData method) + { + Hwnd hwnd; + XEvent xevent = new XEvent (); + + hwnd = Hwnd.ObjectFromHandle(method.Handle); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = DisplayHandle; + xevent.ClientMessageEvent.window = method.Handle; + xevent.ClientMessageEvent.message_type = (IntPtr)AsyncAtom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = (IntPtr) GCHandle.Alloc (method); + + hwnd.Queue.EnqueueLocked (xevent); + + WakeupMain (); + } + + delegate IntPtr WndProcDelegate (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam); + + internal override IntPtr SendMessage (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) + { + Hwnd h; + h = Hwnd.ObjectFromHandle(hwnd); + + if (h != null && h.queue != ThreadQueue (Thread.CurrentThread)) { + AsyncMethodResult result; + AsyncMethodData data; + + result = new AsyncMethodResult (); + data = new AsyncMethodData (); + + data.Handle = hwnd; + data.Method = new WndProcDelegate (NativeWindow.WndProc); + data.Args = new object[] { hwnd, message, wParam, lParam }; + data.Result = result; + + SendAsyncMethod (data); + DriverDebug("Sending {0} message across.", message); + + return IntPtr.Zero; + } + string key = hwnd + ":" + message; + if (messageHold[key] != null) + messageHold[key] = ((int)messageHold[key]) - 1; + return NativeWindow.WndProc(hwnd, message, wParam, lParam); + } + + internal override int SendInput(IntPtr handle, Queue keys) + { + if (handle == IntPtr.Zero) + return 0; + + int count = keys.Count; + Hwnd hwnd = Hwnd.ObjectFromHandle(handle); + + while (keys.Count > 0) { + + MSG msg = (MSG)keys.Dequeue(); + + XEvent xevent = new XEvent (); + + xevent.type = (msg.message == Msg.WM_KEYUP ? XEventName.KeyRelease : XEventName.KeyPress); + xevent.KeyEvent.display = DisplayHandle; + + if (hwnd != null) { + xevent.KeyEvent.window = hwnd.whole_window; + } else { + xevent.KeyEvent.window = IntPtr.Zero; + } + + xevent.KeyEvent.keycode = Keyboard.ToKeycode((int)msg.wParam); + + hwnd.Queue.EnqueueLocked (xevent); + } + return count; + } + + internal override void SetAllowDrop (IntPtr handle, bool value) + { + // We allow drop on all windows + } + + internal override DragDropEffects StartDrag (IntPtr handle, object data, + DragDropEffects allowed_effects) + { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) + throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ")."); + + return Dnd.StartDrag (hwnd.client_window, data, allowed_effects); + } + + internal override void SetBorderStyle(IntPtr handle, FormBorderStyle border_style) + { + Form form = Widget.FromHandle (handle) as Form; + if (form != null && form.window_manager == null) { + CreateParams cp = form.GetCreateParams (); + if (border_style == FormBorderStyle.FixedToolWindow || + border_style == FormBorderStyle.SizableToolWindow || + cp.IsSet (WindowExStyles.WS_EX_TOOLWINDOW)) { + form.window_manager = new ToolWindowManager (form); + } + } + + RequestNCRecalc(handle); + } + + internal override void SetCaretPos(IntPtr handle, int x, int y) + { + if (Caret.Hwnd == handle) { + Caret.Timer.Stop(); + HideCaret(); + + Caret.X = x; + Caret.Y = y; + + Keyboard.SetCaretPos (Caret, handle, x, y); + + if (Caret.Visible == true) { + ShowCaret(); + Caret.Timer.Start(); + } + } + } + + internal override void SetClipRegion(IntPtr handle, Region region) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd == null) { + return; + } + + hwnd.UserClip = region; + } + + internal override void SetCursor(IntPtr handle, IntPtr cursor) + { + Hwnd hwnd; + + if (OverrideCursorHandle == IntPtr.Zero) { + if ((LastCursorWindow == handle) && (LastCursorHandle == cursor)) { + return; + } + + LastCursorHandle = cursor; + LastCursorWindow = handle; + + hwnd = Hwnd.ObjectFromHandle(handle); + lock (XlibLock) { + if (cursor != IntPtr.Zero) { + XDefineCursor(DisplayHandle, hwnd.whole_window, cursor); + } else { + XUndefineCursor(DisplayHandle, hwnd.whole_window); + } + XFlush(DisplayHandle); + } + return; + } + + hwnd = Hwnd.ObjectFromHandle(handle); + lock (XlibLock) { + XDefineCursor(DisplayHandle, hwnd.whole_window, OverrideCursorHandle); + } + } + + void QueryPointer (IntPtr display, IntPtr w, out IntPtr root, out IntPtr child, + out int root_x, out int root_y, out int child_x, out int child_y, + out int mask) + { + /* this code was written with the help of + glance at gdk. I never would have realized we + needed a loop in order to traverse down in the + hierarchy. I would have assumed you'd get the + most deeply nested child and have to do + XQueryTree to move back up the hierarchy.. + stupid me, of course. */ + IntPtr c; + + XGrabServer (display); + + XQueryPointer(display, w, out root, out c, + out root_x, out root_y, out child_x, out child_y, + out mask); + + if (root != w) + c = root; + + IntPtr child_last = IntPtr.Zero; + while (c != IntPtr.Zero) { + child_last = c; + XQueryPointer(display, c, out root, out c, + out root_x, out root_y, out child_x, out child_y, + out mask); + } + XUngrabServer (display); + XFlush (display); + + child = child_last; + } + + internal override void SetCursorPos(IntPtr handle, int x, int y) + { + if (handle == IntPtr.Zero) { + lock (XlibLock) { + IntPtr root, child; + int root_x, root_y, child_x, child_y, mask; + + /* we need to do a + * QueryPointer before warping + * because if the warp is on + * the RootWindow, the x/y are + * relative to the current + * mouse position + */ + QueryPointer (DisplayHandle, RootWindow, + out root, + out child, + out root_x, out root_y, + out child_x, out child_y, + out mask); + + XWarpPointer(DisplayHandle, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x - root_x, y - root_y); + + XFlush (DisplayHandle); + + /* then we need to a + * QueryPointer after warping + * to manually generate a + * motion event for the window + * we move into. + */ + QueryPointer (DisplayHandle, RootWindow, + out root, + out child, + out root_x, out root_y, + out child_x, out child_y, + out mask); + + Hwnd child_hwnd = Hwnd.ObjectFromHandle(child); + if (child_hwnd == null) { + return; + } + + XEvent xevent = new XEvent (); + + xevent.type = XEventName.MotionNotify; + xevent.MotionEvent.display = DisplayHandle; + xevent.MotionEvent.window = child_hwnd.client_window; + xevent.MotionEvent.root = RootWindow; + xevent.MotionEvent.x = child_x; + xevent.MotionEvent.y = child_y; + xevent.MotionEvent.x_root = root_x; + xevent.MotionEvent.y_root = root_y; + xevent.MotionEvent.state = mask; + + child_hwnd.Queue.EnqueueLocked (xevent); + } + } else { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + lock (XlibLock) { + XWarpPointer(DisplayHandle, IntPtr.Zero, hwnd.client_window, 0, 0, 0, 0, x, y); + } + } + } + + internal override void SetFocus(IntPtr handle) + { + Hwnd hwnd; + IntPtr prev_focus_window; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd.client_window == FocusWindow) { + return; + } + + // Win32 doesn't do anything if disabled + if (!hwnd.enabled) + return; + + prev_focus_window = FocusWindow; + FocusWindow = hwnd.client_window; + + if (prev_focus_window != IntPtr.Zero) { + SendMessage(prev_focus_window, Msg.WM_KILLFOCUS, FocusWindow, IntPtr.Zero); + } + Keyboard.FocusIn (FocusWindow); + SendMessage(FocusWindow, Msg.WM_SETFOCUS, prev_focus_window, IntPtr.Zero); + + //XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).client_window, RevertTo.None, IntPtr.Zero); + } + + internal override void SetIcon(IntPtr handle, Icon icon) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd != null) { + SetIcon(hwnd, icon); + } + } + + internal override void SetMenu(IntPtr handle, Menu menu) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + hwnd.menu = menu; + + RequestNCRecalc(handle); + } + + internal override void SetModal(IntPtr handle, bool Modal) + { + if (Modal) { + ModalWindows.Push(handle); + } else { + if (ModalWindows.Contains(handle)) { + ModalWindows.Pop(); + } + if (ModalWindows.Count > 0) { + Activate((IntPtr)ModalWindows.Peek()); + } + } + + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + Widget ctrl = Widget.FromHandle (handle); + SetWMStyles (hwnd, ctrl.GetCreateParams ()); + } + + internal override IntPtr SetParent(IntPtr handle, IntPtr parent) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + hwnd.parent = Hwnd.ObjectFromHandle(parent); + + lock (XlibLock) { + DriverDebug("Parent for window {0} = {1}", XplatUI.Window(hwnd.Handle), XplatUI.Window(hwnd.parent != null ? hwnd.parent.Handle : IntPtr.Zero)); + XReparentWindow(DisplayHandle, hwnd.whole_window, hwnd.parent == null ? FosterParent : hwnd.parent.client_window, hwnd.x, hwnd.y); + } + + return IntPtr.Zero; + } + + internal override void SetTimer (Timer timer) + { + XEventQueue queue = (XEventQueue) MessageQueues [timer.thread]; + + if (queue == null) { + // This isn't really an error, MS doesn't start the timer if + // it has no assosciated queue at this stage (it will be + // enabled when a window is activated). + unattached_timer_list.Add (timer); + return; + } + queue.timer_list.Add (timer); + WakeupMain (); + } + + internal override bool SetTopmost(IntPtr handle, bool enabled) + { + + Hwnd hwnd = Hwnd.ObjectFromHandle(handle); + hwnd.topmost = enabled; + + if (enabled) { + lock (XlibLock) { + if (hwnd.Mapped) { + SendNetWMMessage(hwnd.WholeWindow, _NET_WM_STATE, (IntPtr) NetWmStateRequest._NET_WM_STATE_ADD, _NET_WM_STATE_ABOVE, IntPtr.Zero); + } else { + int[] atoms = new int[8]; + atoms[0] = _NET_WM_STATE_ABOVE.ToInt32(); + XChangeProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1); + } + } + } else { + lock (XlibLock) { + if (hwnd.Mapped) + SendNetWMMessage(hwnd.WholeWindow, _NET_WM_STATE, (IntPtr) NetWmStateRequest._NET_WM_STATE_REMOVE, _NET_WM_STATE_ABOVE, IntPtr.Zero); + else + XDeleteProperty(DisplayHandle, hwnd.whole_window, _NET_WM_STATE); + } + } + return true; + } + + internal override bool SetOwner(IntPtr handle, IntPtr handle_owner) + { + Hwnd hwnd; + Hwnd hwnd_owner; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (handle_owner != IntPtr.Zero) { + hwnd_owner = Hwnd.ObjectFromHandle(handle_owner); + lock (XlibLock) { + int[] atoms; + + atoms = new int[8]; + + atoms[0] = _NET_WM_WINDOW_TYPE_NORMAL.ToInt32(); + XChangeProperty(DisplayHandle, hwnd.whole_window, _NET_WM_WINDOW_TYPE, (IntPtr)Atom.XA_ATOM, 32, PropertyMode.Replace, atoms, 1); + + if (hwnd_owner != null) { + XSetTransientForHint(DisplayHandle, hwnd.whole_window, hwnd_owner.whole_window); + } else { + XSetTransientForHint(DisplayHandle, hwnd.whole_window, RootWindow); + } + } + } else { + lock (XlibLock) { + XDeleteProperty(DisplayHandle, hwnd.whole_window, (IntPtr)Atom.XA_WM_TRANSIENT_FOR); + } + } + return true; + } + + internal override bool SetVisible (IntPtr handle, bool visible, bool activate) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + hwnd.visible = visible; + + lock (XlibLock) { + if (visible) { + MapWindow(hwnd, WindowType.Both); + + if (Widget.FromHandle(handle) is Form) { + FormWindowState s; + + s = ((Form)Widget.FromHandle(handle)).WindowState; + + switch(s) { + case FormWindowState.Minimized: SetWindowState(handle, FormWindowState.Minimized); break; + case FormWindowState.Maximized: SetWindowState(handle, FormWindowState.Maximized); break; + } + } + + SendMessage(handle, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + } + else { + UnmapWindow(hwnd, WindowType.Both); + } + } + return true; + } + + internal override void SetWindowMinMax(IntPtr handle, Rectangle maximized, Size min, Size max) + { + Widget ctrl = Widget.FromHandle (handle); + SetWindowMinMax (handle, maximized, min, max, ctrl != null ? ctrl.GetCreateParams () : null); + } + + internal void SetWindowMinMax (IntPtr handle, Rectangle maximized, Size min, Size max, CreateParams cp) + { + Hwnd hwnd; + XSizeHints hints; + IntPtr dummy; + + hwnd = Hwnd.ObjectFromHandle(handle); + if (hwnd == null) { + return; + } + + min.Width = Math.Max (min.Width, SystemInformation.MinimumWindowSize.Width); + min.Height = Math.Max (min.Height, SystemInformation.MinimumWindowSize.Height); + + hints = new XSizeHints(); + + XGetWMNormalHints(DisplayHandle, hwnd.whole_window, ref hints, out dummy); + if ((min != Size.Empty) && (min.Width > 0) && (min.Height > 0)) { + if (cp != null) + min = TranslateWindowSizeToXWindowSize (cp, min); + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMinSize); + hints.min_width = min.Width; + hints.min_height = min.Height; + } + + if ((max != Size.Empty) && (max.Width > 0) && (max.Height > 0)) { + if (cp != null) + max = TranslateWindowSizeToXWindowSize (cp, max); + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMaxSize); + hints.max_width = max.Width; + hints.max_height = max.Height; + } + + if (hints.flags != IntPtr.Zero) { + // The Metacity team has decided that they won't care about this when clicking the maximize icon, + // they will maximize the window to fill the screen/parent no matter what. + // http://bugzilla.ximian.com/show_bug.cgi?id=80021 + XSetWMNormalHints(DisplayHandle, hwnd.whole_window, ref hints); + } + + if ((maximized != Rectangle.Empty) && (maximized.Width > 0) && (maximized.Height > 0)) { + if (cp != null) + maximized.Size = TranslateWindowSizeToXWindowSize (cp); + hints.flags = (IntPtr)XSizeHintsFlags.PPosition; + hints.x = maximized.X; + hints.y = maximized.Y; + hints.width = maximized.Width; + hints.height = maximized.Height; + + // Metacity does not seem to follow this constraint for maximized (zoomed) windows + XSetZoomHints(DisplayHandle, hwnd.whole_window, ref hints); + } + } + + + internal override void SetWindowPos(IntPtr handle, int x, int y, int width, int height) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) { + return; + } + + // Win32 automatically changes negative width/height to 0. + if (width < 0) + width = 0; + if (height < 0) + height = 0; + + // X requires a sanity check for width & height; otherwise it dies + if (hwnd.zero_sized && width > 0 && height > 0) { + if (hwnd.visible) { + MapWindow(hwnd, WindowType.Whole); + } + hwnd.zero_sized = false; + } + + if ((width < 1) || (height < 1)) { + hwnd.zero_sized = true; + UnmapWindow(hwnd, WindowType.Whole); + } + + // Save a server roundtrip (and prevent a feedback loop) + if ((hwnd.x == x) && (hwnd.y == y) && + (hwnd.width == width) && (hwnd.height == height)) { + return; + } + + if (!hwnd.zero_sized) { + //Hack? + hwnd.x = x; + hwnd.y = y; + hwnd.width = width; + hwnd.height = height; + SendMessage(hwnd.client_window, Msg.WM_WINDOWPOSCHANGED, IntPtr.Zero, IntPtr.Zero); + + if (hwnd.fixed_size) { + SetWindowMinMax(handle, Rectangle.Empty, new Size(width, height), new Size(width, height)); + } + + lock (XlibLock) { + Widget ctrl = Widget.FromHandle (handle); + Size TranslatedSize = TranslateWindowSizeToXWindowSize (ctrl.GetCreateParams (), new Size (width, height)); + MoveResizeWindow (DisplayHandle, hwnd.whole_window, x, y, TranslatedSize.Width, TranslatedSize.Height); + PerformNCCalc(hwnd); + } + } + + // Update our position/size immediately, so + // that future calls to SetWindowPos aren't + // kept from calling XMoveResizeWindow (by the + // "Save a server roundtrip" block above). + hwnd.x = x; + hwnd.y = y; + hwnd.width = width; + hwnd.height = height; + hwnd.ClientRect = Rectangle.Empty; + } + + internal override void SetWindowState(IntPtr handle, FormWindowState state) + { + FormWindowState current_state; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + current_state = GetWindowState(handle); + + if (current_state == state) { + return; + } + + switch(state) { + case FormWindowState.Normal: { + lock (XlibLock) { + if (current_state == FormWindowState.Minimized) { + MapWindow(hwnd, WindowType.Both); + } else if (current_state == FormWindowState.Maximized) { + SendNetWMMessage(hwnd.whole_window, _NET_WM_STATE, (IntPtr)2 /* toggle */, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT); + } + } + Activate(handle); + return; + } + + case FormWindowState.Minimized: { + lock (XlibLock) { + if (current_state == FormWindowState.Maximized) { + SendNetWMMessage(hwnd.whole_window, _NET_WM_STATE, (IntPtr)2 /* toggle */, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT); + } + XIconifyWindow(DisplayHandle, hwnd.whole_window, ScreenNo); + } + return; + } + + case FormWindowState.Maximized: { + lock (XlibLock) { + if (current_state == FormWindowState.Minimized) { + MapWindow(hwnd, WindowType.Both); + } + + SendNetWMMessage(hwnd.whole_window, _NET_WM_STATE, (IntPtr)1 /* Add */, _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT); + } + Activate(handle); + return; + } + } + } + + internal override void SetWindowStyle(IntPtr handle, CreateParams cp) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + SetHwndStyles(hwnd, cp); + SetWMStyles(hwnd, cp); + } + + internal override double GetWindowTransparency(IntPtr handle) + { + return 1.0; + } + + internal override void SetWindowTransparency(IntPtr handle, double transparency, Color key) + { + Hwnd hwnd; + IntPtr opacity; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (hwnd == null) { + return; + } + + hwnd.opacity = (uint)(0xffffffff * transparency); + opacity = (IntPtr)hwnd.opacity; + + if (transparency >= 1.0) { + XDeleteProperty (DisplayHandle, hwnd.whole_window, _NET_WM_WINDOW_OPACITY); + } else { + XChangeProperty (DisplayHandle, hwnd.whole_window, _NET_WM_WINDOW_OPACITY, (IntPtr)Atom.XA_CARDINAL, 32, PropertyMode.Replace, ref opacity, 1); + } + } + + internal override bool SetZOrder(IntPtr handle, IntPtr after_handle, bool top, bool bottom) + { + Hwnd hwnd = Hwnd.ObjectFromHandle(handle); + + if (!hwnd.mapped) { + return false; + } + + if (top) { + lock (XlibLock) { + XRaiseWindow(DisplayHandle, hwnd.whole_window); + } + return true; + } else if (!bottom) { + Hwnd after_hwnd = null; + + if (after_handle != IntPtr.Zero) { + after_hwnd = Hwnd.ObjectFromHandle(after_handle); + } + + XWindowChanges values = new XWindowChanges(); + + if (after_hwnd == null) { + // Work around metacity 'issues' + int[] atoms; + + atoms = new int[2]; + atoms[0] = unixtime(); + XChangeProperty(DisplayHandle, hwnd.whole_window, _NET_WM_USER_TIME, (IntPtr)Atom.XA_CARDINAL, 32, PropertyMode.Replace, atoms, 1); + + XRaiseWindow(DisplayHandle, hwnd.whole_window); + SendNetWMMessage(hwnd.whole_window, _NET_ACTIVE_WINDOW, (IntPtr)1, IntPtr.Zero, IntPtr.Zero); + return true; + //throw new ArgumentNullException("after_handle", "Need sibling to adjust z-order"); + } + + values.sibling = after_hwnd.whole_window; + values.stack_mode = StackMode.Below; + + lock (XlibLock) { + XConfigureWindow(DisplayHandle, hwnd.whole_window, ChangeWindowFlags.CWStackMode | ChangeWindowFlags.CWSibling, ref values); + } + } else { + // Bottom + lock (XlibLock) { + XLowerWindow(DisplayHandle, hwnd.whole_window); + } + return true; + } + return false; + } + + internal override void ShowCursor(bool show) + { + ; // FIXME - X11 doesn't 'hide' the cursor. we could create an empty cursor + } + + internal override object StartLoop(Thread thread) + { + XEventQueue q = ThreadQueue(thread); + return q; + } + + internal override TransparencySupport SupportsTransparency() + { + // We need to check if the x compositing manager is running + return TransparencySupport.Set; + } + + internal override bool SystrayAdd(IntPtr handle, string tip, Icon icon, out ToolTip tt) + { + GetSystrayManagerWindow(); + + if (SystrayMgrWindow != IntPtr.Zero) { + XSizeHints size_hints; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + DriverDebug("Adding Systray Whole:{0:X}, Client:{1:X}", hwnd.whole_window.ToInt32(), hwnd.client_window.ToInt32()); + + // Oh boy. + if (hwnd.client_window != hwnd.whole_window) { + Keyboard.DestroyICForWindow (hwnd.client_window); + XDestroyWindow(DisplayHandle, hwnd.client_window); + hwnd.client_window = hwnd.whole_window; + } + + /* by virtue of the way the tests are ordered when determining if it's PAINT + or NCPAINT, client_window == whole_window will always be PAINT. So, if we're + waiting on an nc_expose, drop it and remove the hwnd from the list (unless + there's a pending expose). */ + if (hwnd.nc_expose_pending) { + hwnd.nc_expose_pending = false; + if (!hwnd.expose_pending) + hwnd.Queue.Paint.Remove (hwnd); + } + + // We are going to be directly mapped by the system tray, so mark as mapped + // so we can later properly unmap it. + hwnd.mapped = true; + + size_hints = new XSizeHints(); + + size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize); + + size_hints.min_width = 24; + size_hints.min_height = 24; + size_hints.max_width = 24; + size_hints.max_height = 24; + size_hints.base_width = 24; + size_hints.base_height = 24; + + XSetWMNormalHints(DisplayHandle, hwnd.whole_window, ref size_hints); + + int[] atoms = new int[2]; + atoms [0] = 1; // Version 1 + atoms [1] = 1; // we want to be mapped + + // This line cost me 3 days... + XChangeProperty(DisplayHandle, hwnd.whole_window, _XEMBED_INFO, _XEMBED_INFO, 32, PropertyMode.Replace, atoms, 2); + + // Need to pick some reasonable defaults + tt = new ToolTip(); + tt.AutomaticDelay = 350; + tt.InitialDelay = 250; + tt.ReshowDelay = 250; + tt.ShowAlways = true; + + if ((tip != null) && (tip != string.Empty)) { + tt.SetToolTip(Widget.FromHandle(handle), tip); + tt.Active = true; + } else { + tt.Active = false; + } + + SendNetClientMessage(SystrayMgrWindow, _NET_SYSTEM_TRAY_OPCODE, IntPtr.Zero, (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK, hwnd.whole_window); + + return true; + } + tt = null; + return false; + } + + internal override bool SystrayChange(IntPtr handle, string tip, Icon icon, ref ToolTip tt) + { + Widget Widget; + + Widget = Widget.FromHandle(handle); + if (Widget != null && tt != null) { + tt.SetToolTip(Widget, tip); + tt.Active = true; + SendMessage(handle, Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + return true; + } else { + return false; + } + } + + internal override void SystrayRemove(IntPtr handle, ref ToolTip tt) + { + + SetVisible (handle, false, false); + + // The caller can now re-dock it later... + if (tt != null) { + tt.Dispose(); + tt = null; + } + // Close any balloon window *we* fired. + ThemeEngine.Current.HideBalloonWindow (handle); + } + + internal override void SystrayBalloon(IntPtr handle, int timeout, string title, string text, ToolTipIcon icon) + { + ThemeEngine.Current.ShowBalloonWindow (handle, timeout, title, text, icon); + SendMessage(handle, Msg.WM_USER, IntPtr.Zero, (IntPtr) Msg.NIN_BALLOONSHOW); + } + + internal override bool Text(IntPtr handle, string text) +{ + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + lock (XlibLock) { + XChangeProperty(DisplayHandle, hwnd.whole_window, _NET_WM_NAME, UTF8_STRING, 8, + PropertyMode.Replace, text, Encoding.UTF8.GetByteCount (text)); + + // XXX this has problems with UTF8. + // we need to either use the actual + // text if it's latin-1, or convert it + // to compound text if it's in a + // different charset. + XStoreName(DisplayHandle, Hwnd.ObjectFromHandle(handle).whole_window, text); + } + return true; + } + + internal override bool TranslateMessage(ref MSG msg) + { + return Keyboard.TranslateMessage (ref msg); + } + + internal override void UpdateWindow(IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle(handle); + + if (!hwnd.visible || !hwnd.expose_pending || !hwnd.Mapped) { + return; + } + + SendMessage(handle, Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + hwnd.Queue.Paint.Remove(hwnd); + } + + internal override void CreateOffscreenDrawable (IntPtr handle, + int width, int height, + out object offscreen_drawable) + { + IntPtr root_out; + int x_out, y_out, width_out, height_out, border_width_out, depth_out; + + XGetGeometry (DisplayHandle, handle, + out root_out, + out x_out, out y_out, + out width_out, out height_out, + out border_width_out, out depth_out); + + IntPtr pixmap = XCreatePixmap (DisplayHandle, handle, width, height, depth_out); + + offscreen_drawable = pixmap; + + } + + internal override void DestroyOffscreenDrawable (object offscreen_drawable) + { + XFreePixmap (DisplayHandle, (IntPtr)offscreen_drawable); + } + + internal override Graphics GetOffscreenGraphics (object offscreen_drawable) + { + return Graphics.FromHwnd ((IntPtr) offscreen_drawable); + } + + internal override void BlitFromOffscreen (IntPtr dest_handle, + Graphics dest_dc, + object offscreen_drawable, + Graphics offscreen_dc, + Rectangle r) + { + XGCValues gc_values; + IntPtr gc; + + gc_values = new XGCValues(); + + gc = XCreateGC (DisplayHandle, dest_handle, IntPtr.Zero, ref gc_values); + + XCopyArea (DisplayHandle, (IntPtr)offscreen_drawable, dest_handle, + gc, r.X, r.Y, r.Width, r.Height, r.X, r.Y); + + XFreeGC (DisplayHandle, gc); + } + + #endregion // Public Static Methods + + #region Events + internal override event EventHandler Idle; + #endregion // Events + + +#if TRACE && false + +#region Xcursor imports + [DllImport ("libXcursor", EntryPoint = "XcursorLibraryLoadCursor")] + internal extern static IntPtr XcursorLibraryLoadCursor (IntPtr display, [MarshalAs (UnmanagedType.LPStr)] string name); + + [DllImport ("libXcursor", EntryPoint = "XcursorLibraryLoadImages")] + internal extern static IntPtr XcursorLibraryLoadImages ([MarshalAs (UnmanagedType.LPStr)] string file, IntPtr theme, int size); + + [DllImport ("libXcursor", EntryPoint = "XcursorImagesDestroy")] + internal extern static void XcursorImagesDestroy (IntPtr images); + + [DllImport ("libXcursor", EntryPoint = "XcursorGetDefaultSize")] + internal extern static int XcursorGetDefaultSize (IntPtr display); + + [DllImport ("libXcursor", EntryPoint = "XcursorImageLoadCursor")] + internal extern static IntPtr XcursorImageLoadCursor (IntPtr display, IntPtr image); + + [DllImport ("libXcursor", EntryPoint = "XcursorGetTheme")] + internal extern static IntPtr XcursorGetTheme (IntPtr display); +#endregion +#region X11 Imports + [DllImport ("libX11", EntryPoint="XOpenDisplay")] + internal extern static IntPtr XOpenDisplay(IntPtr display); + [DllImport ("libX11", EntryPoint="XCloseDisplay")] + internal extern static int XCloseDisplay(IntPtr display); + [DllImport ("libX11", EntryPoint="XSynchronize")] + internal extern static IntPtr XSynchronize(IntPtr display, bool onoff); + + [DllImport ("libX11", EntryPoint="XCreateWindow")] + internal extern static IntPtr _XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, UIntPtr valuemask, ref XSetWindowAttributes attributes); + internal static IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, UIntPtr valuemask, ref XSetWindowAttributes attributes) + { + DebugHelper.TraceWriteLine ("XCreateWindow"); + return _XCreateWindow(display, parent, x, y, width, height, + border_width, depth, xclass, visual, valuemask, ref attributes); + } + [DllImport ("libX11", EntryPoint="XCreateSimpleWindow")] + internal extern static IntPtr _XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, UIntPtr border, UIntPtr background); + internal static IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, UIntPtr border, UIntPtr background) + { + DebugHelper.TraceWriteLine ("XCreateSimpleWindow"); + return _XCreateSimpleWindow(display, parent, x, y, width, height, border_width, border, background); + } + [DllImport ("libX11", EntryPoint="XMapWindow")] + internal extern static int _XMapWindow(IntPtr display, IntPtr window); + internal static int XMapWindow(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XMapWindow"); + return _XMapWindow(display, window); + } + [DllImport ("libX11", EntryPoint="XMapRaised")] + internal extern static int _XMapRaised(IntPtr display, IntPtr window); + internal static int XMapRaised(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XMapRaised"); + return _XMapRaised(display, window); + } + [DllImport ("libX11", EntryPoint="XUnmapWindow")] + internal extern static int _XUnmapWindow(IntPtr display, IntPtr window); + internal static int XUnmapWindow(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XUnmapWindow"); + return _XUnmapWindow(display, window); + } + [DllImport ("libX11", EntryPoint="XMapSubwindows")] + internal extern static int _XMapSubindows(IntPtr display, IntPtr window); + internal static int XMapSubindows(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XMapSubindows"); + return _XMapSubindows(display, window); + } + [DllImport ("libX11", EntryPoint="XUnmapSubwindows")] + internal extern static int _XUnmapSubwindows(IntPtr display, IntPtr window); + internal static int XUnmapSubwindows(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XUnmapSubwindows"); + return _XUnmapSubwindows(display, window); + } + [DllImport ("libX11", EntryPoint="XRootWindow")] + internal extern static IntPtr _XRootWindow(IntPtr display, int screen_number); + internal static IntPtr XRootWindow(IntPtr display, int screen_number) + { + DebugHelper.TraceWriteLine ("XRootWindow"); + return _XRootWindow(display, screen_number); + } + [DllImport ("libX11", EntryPoint="XNextEvent")] + internal extern static IntPtr _XNextEvent(IntPtr display, ref XEvent xevent); + internal static IntPtr XNextEvent(IntPtr display, ref XEvent xevent) + { + DebugHelper.TraceWriteLine ("XNextEvent"); + return _XNextEvent(display, ref xevent); + } + [DllImport ("libX11", EntryPoint="XConnectionNumber")] + internal extern static int _XConnectionNumber (IntPtr display); + internal static int XConnectionNumber (IntPtr display) + { + DebugHelper.TraceWriteLine ("XConnectionNumber"); + return _XConnectionNumber (display); + } + [DllImport ("libX11", EntryPoint="XPending")] + internal extern static int _XPending (IntPtr display); + internal static int XPending (IntPtr display) + { + DebugHelper.TraceWriteLine ("XPending"); + DebugHelper.DumpCallers (3); + return _XPending (display); + } + [DllImport ("libX11", EntryPoint="XSelectInput")] + internal extern static IntPtr _XSelectInput(IntPtr display, IntPtr window, IntPtr mask); + internal static IntPtr XSelectInput(IntPtr display, IntPtr window, IntPtr mask) + { + DebugHelper.TraceWriteLine ("XSelectInput"); + return _XSelectInput(display, window, mask); + } + + [DllImport ("libX11", EntryPoint="XDestroyWindow")] + internal extern static int _XDestroyWindow(IntPtr display, IntPtr window); + internal static int XDestroyWindow(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XDestroyWindow 0x{0:x}", window.ToInt32()); + return _XDestroyWindow(display, window); + } + + [DllImport ("libX11", EntryPoint="XReparentWindow")] + internal extern static int _XReparentWindow(IntPtr display, IntPtr window, IntPtr parent, int x, int y); + internal static int XReparentWindow(IntPtr display, IntPtr window, IntPtr parent, int x, int y) + { + DebugHelper.TraceWriteLine ("XReparentWindow"); + return _XReparentWindow(display, window, parent, x, y); + } + + [DllImport ("libX11", EntryPoint="XMoveResizeWindow")] + extern static int _XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height); + static int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height) { + DebugHelper.TraceWriteLine ("XMoveResizeWindow"); + return _XMoveResizeWindow(display, window, x, y, width, height); + } + + internal static int MoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height) + { + int ret = XMoveResizeWindow (display, window, x, y, width, height); + Keyboard.MoveCurrentCaretPos (); + return ret; + } + + [DllImport ("libX11", EntryPoint="XResizeWindow")] + internal extern static int _XResizeWindow(IntPtr display, IntPtr window, int width, int height); + internal static int XResizeWindow(IntPtr display, IntPtr window, int width, int height) + { + DebugHelper.TraceWriteLine ("XResizeWindow"); + return _XResizeWindow(display, window, width, height); + } + + [DllImport ("libX11", EntryPoint="XGetWindowAttributes")] + internal extern static int _XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes); + internal static int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes) + { + DebugHelper.TraceWriteLine ("XGetWindowAttributes"); + return _XGetWindowAttributes(display, window, ref attributes); + } + + [DllImport ("libX11", EntryPoint="XFlush")] + internal extern static int _XFlush(IntPtr display); + internal static int XFlush(IntPtr display) + { + DebugHelper.TraceWriteLine ("XFlush"); + return _XFlush(display); + } + + [DllImport ("libX11", EntryPoint="XSetWMName")] + internal extern static int _XSetWMName(IntPtr display, IntPtr window, ref XTextProperty text_prop); + internal static int XSetWMName(IntPtr display, IntPtr window, ref XTextProperty text_prop) + { + DebugHelper.TraceWriteLine ("XSetWMName"); + return _XSetWMName(display, window, ref text_prop); + } + + [DllImport ("libX11", EntryPoint="XStoreName")] + internal extern static int _XStoreName(IntPtr display, IntPtr window, string window_name); + internal static int XStoreName(IntPtr display, IntPtr window, string window_name) + { + DebugHelper.TraceWriteLine ("XStoreName"); + return _XStoreName(display, window, window_name); + } + + [DllImport ("libX11", EntryPoint="XFetchName")] + internal extern static int _XFetchName(IntPtr display, IntPtr window, ref IntPtr window_name); + internal static int XFetchName(IntPtr display, IntPtr window, ref IntPtr window_name) + { + DebugHelper.TraceWriteLine ("XFetchName"); + return _XFetchName(display, window, ref window_name); + } + + [DllImport ("libX11", EntryPoint="XSendEvent")] + internal extern static int _XSendEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask, ref XEvent send_event); + internal static int XSendEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask, ref XEvent send_event) + { + DebugHelper.TraceWriteLine ("XSendEvent"); + return _XSendEvent(display, window, propagate, event_mask, ref send_event); + } + + [DllImport ("libX11", EntryPoint="XQueryTree")] + internal extern static int _XQueryTree(IntPtr display, IntPtr window, out IntPtr root_return, out IntPtr parent_return, out IntPtr children_return, out int nchildren_return); + internal static int XQueryTree(IntPtr display, IntPtr window, out IntPtr root_return, out IntPtr parent_return, out IntPtr children_return, out int nchildren_return) + { + DebugHelper.TraceWriteLine ("XQueryTree"); + return _XQueryTree(display, window, out root_return, out parent_return, out children_return, out nchildren_return); + } + + [DllImport ("libX11", EntryPoint="XFree")] + internal extern static int _XFree(IntPtr data); + internal static int XFree(IntPtr data) + { + DebugHelper.TraceWriteLine ("XFree"); + return _XFree(data); + } + + [DllImport ("libX11", EntryPoint="XRaiseWindow")] + internal extern static int _XRaiseWindow(IntPtr display, IntPtr window); + internal static int XRaiseWindow(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XRaiseWindow"); + return _XRaiseWindow(display, window); + } + + [DllImport ("libX11", EntryPoint="XLowerWindow")] + internal extern static uint _XLowerWindow(IntPtr display, IntPtr window); + internal static uint XLowerWindow(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XLowerWindow"); + return _XLowerWindow(display, window); + } + + [DllImport ("libX11", EntryPoint="XConfigureWindow")] + internal extern static uint _XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask, ref XWindowChanges values); + internal static uint XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask, ref XWindowChanges values) + { + DebugHelper.TraceWriteLine ("XConfigureWindow"); + return _XConfigureWindow(display, window, value_mask, ref values); + } + + [DllImport ("libX11", EntryPoint="XInternAtom")] + internal extern static IntPtr _XInternAtom(IntPtr display, string atom_name, bool only_if_exists); + internal static IntPtr XInternAtom(IntPtr display, string atom_name, bool only_if_exists) + { + DebugHelper.TraceWriteLine ("XInternAtom"); + return _XInternAtom(display, atom_name, only_if_exists); + } + + [DllImport ("libX11", EntryPoint="XInternAtoms")] + internal extern static int _XInternAtoms(IntPtr display, string[] atom_names, int atom_count, bool only_if_exists, IntPtr[] atoms); + internal static int XInternAtoms(IntPtr display, string[] atom_names, int atom_count, bool only_if_exists, IntPtr[] atoms) + { + DebugHelper.TraceWriteLine ("XInternAtoms"); + return _XInternAtoms(display, atom_names, atom_count, only_if_exists, atoms); + } + + [DllImport ("libX11", EntryPoint="XSetWMProtocols")] + internal extern static int _XSetWMProtocols(IntPtr display, IntPtr window, IntPtr[] protocols, int count); + internal static int XSetWMProtocols(IntPtr display, IntPtr window, IntPtr[] protocols, int count) + { + DebugHelper.TraceWriteLine ("XSetWMProtocols"); + return _XSetWMProtocols(display, window, protocols, count); + } + + [DllImport ("libX11", EntryPoint="XGrabPointer")] + internal extern static int _XGrabPointer(IntPtr display, IntPtr window, bool owner_events, EventMask event_mask, GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, IntPtr timestamp); + internal static int XGrabPointer(IntPtr display, IntPtr window, bool owner_events, EventMask event_mask, GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, IntPtr timestamp) + { + DebugHelper.TraceWriteLine ("XGrabPointer"); + return _XGrabPointer(display, window, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, timestamp); + } + + [DllImport ("libX11", EntryPoint="XUngrabPointer")] + internal extern static int _XUngrabPointer(IntPtr display, IntPtr timestamp); + internal static int XUngrabPointer(IntPtr display, IntPtr timestamp) + { + DebugHelper.TraceWriteLine ("XUngrabPointer"); + return _XUngrabPointer(display, timestamp); + } + + [DllImport ("libX11", EntryPoint="XQueryPointer")] + internal extern static bool _XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons); + internal static bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons) + { + DebugHelper.TraceWriteLine ("XQueryPointer"); + return _XQueryPointer(display, window, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons); + } + + [DllImport ("libX11", EntryPoint="XTranslateCoordinates")] + internal extern static bool _XTranslateCoordinates (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return); + internal static bool XTranslateCoordinates (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return) + { + DebugHelper.TraceWriteLine ("XTranslateCoordinates"); + return _XTranslateCoordinates (display, src_w, dest_w, src_x, src_y, out intdest_x_return, out dest_y_return, out child_return); + } + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool _XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y, out int width, out int height, out int border_width, out int depth); + internal static bool XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y, out int width, out int height, out int border_width, out int depth) + { + DebugHelper.TraceWriteLine ("XGetGeometry"); + return _XGetGeometry(display, window, out root, out x, out y, out width, out height, out border_width, out depth); + } + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool _XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, out int width, out int height, IntPtr border_width, IntPtr depth); + internal static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, out int width, out int height, IntPtr border_width, IntPtr depth) + { + DebugHelper.TraceWriteLine ("XGetGeometry"); + return _XGetGeometry(display, window, root, out x, out y, out width, out height, border_width, depth); + } + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool _XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, IntPtr width, IntPtr height, IntPtr border_width, IntPtr depth); + internal static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, IntPtr width, IntPtr height, IntPtr border_width, IntPtr depth) + { + DebugHelper.TraceWriteLine ("XGetGeometry"); + return _XGetGeometry(display, window, root, out x, out y, width, height, border_width, depth); + } + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool _XGetGeometry(IntPtr display, IntPtr window, IntPtr root, IntPtr x, IntPtr y, out int width, out int height, IntPtr border_width, IntPtr depth); + internal static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, IntPtr x, IntPtr y, out int width, out int height, IntPtr border_width, IntPtr depth) + { + DebugHelper.TraceWriteLine ("XGetGeometry"); + return _XGetGeometry(display, window, root, x, y, out width, out height, border_width, depth); + } + + [DllImport ("libX11", EntryPoint="XWarpPointer")] + internal extern static uint _XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, uint src_width, uint src_height, int dest_x, int dest_y); + internal static uint XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, uint src_width, uint src_height, int dest_x, int dest_y) + { + DebugHelper.TraceWriteLine ("XWarpPointer"); + return _XWarpPointer(display, src_w, dest_w, src_x, src_y, src_width, src_height, dest_x, dest_y); + } + + [DllImport ("libX11", EntryPoint="XClearWindow")] + internal extern static int _XClearWindow(IntPtr display, IntPtr window); + internal static int XClearWindow(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XClearWindow"); + return _XClearWindow(display, window); + } + + [DllImport ("libX11", EntryPoint="XClearArea")] + internal extern static int _XClearArea(IntPtr display, IntPtr window, int x, int y, int width, int height, bool exposures); + internal static int XClearArea(IntPtr display, IntPtr window, int x, int y, int width, int height, bool exposures) + { + DebugHelper.TraceWriteLine ("XClearArea"); + return _XClearArea(display, window, x, y, width, height, exposures); + } + + // Colormaps + [DllImport ("libX11", EntryPoint="XDefaultScreenOfDisplay")] + internal extern static IntPtr _XDefaultScreenOfDisplay(IntPtr display); + internal static IntPtr XDefaultScreenOfDisplay(IntPtr display) + { + DebugHelper.TraceWriteLine ("XDefaultScreenOfDisplay"); + return _XDefaultScreenOfDisplay(display); + } + + [DllImport ("libX11", EntryPoint="XScreenNumberOfScreen")] + internal extern static int _XScreenNumberOfScreen(IntPtr display, IntPtr Screen); + internal static int XDefaultScreenOfDisplay(IntPtr display, IntPtr Screen) + { + DebugHelper.TraceWriteLine ("XDefaultScreenOfDisplay"); + return _XScreenNumberOfScreen(display, Screen); + } + + [DllImport ("libX11", EntryPoint="XDefaultVisual")] + internal extern static IntPtr _XDefaultVisual(IntPtr display, int screen_number); + internal static IntPtr XDefaultScreenOfDisplay(IntPtr display, int screen_number) + { + DebugHelper.TraceWriteLine ("XDefaultScreenOfDisplay"); + return _XDefaultVisual(display, screen_number); + } + + [DllImport ("libX11", EntryPoint="XDefaultDepth")] + internal extern static uint _XDefaultDepth(IntPtr display, int screen_number); + internal static uint XDefaultDepth(IntPtr display, int screen_number) + { + DebugHelper.TraceWriteLine ("XDefaultDepth"); + return _XDefaultDepth(display, screen_number); + } + + [DllImport ("libX11", EntryPoint="XDefaultScreen")] + internal extern static int _XDefaultScreen(IntPtr display); + internal static int XDefaultScreen(IntPtr display) + { + DebugHelper.TraceWriteLine ("XDefaultScreen"); + return _XDefaultScreen(display); + } + + [DllImport ("libX11", EntryPoint="XDefaultColormap")] + internal extern static IntPtr _XDefaultColormap(IntPtr display, int screen_number); + internal static IntPtr XDefaultColormap(IntPtr display, int screen_number) + { + DebugHelper.TraceWriteLine ("XDefaultColormap"); + return _XDefaultColormap(display, screen_number); + } + + [DllImport ("libX11", EntryPoint="XLookupColor")] + internal extern static int _XLookupColor(IntPtr display, IntPtr Colormap, string Coloranem, ref XColor exact_def_color, ref XColor screen_def_color); + internal static int XLookupColor(IntPtr display, IntPtr Colormap, string Coloranem, ref XColor exact_def_color, ref XColor screen_def_color) + { + DebugHelper.TraceWriteLine ("XLookupColor"); + return _XLookupColor(display, Colormap, Coloranem, ref exact_def_color, ref screen_def_color); + } + + [DllImport ("libX11", EntryPoint="XAllocColor")] + internal extern static int _XAllocColor(IntPtr display, IntPtr Colormap, ref XColor colorcell_def); + internal static int XAllocColor(IntPtr display, IntPtr Colormap, ref XColor colorcell_def) + { + DebugHelper.TraceWriteLine ("XAllocColor"); + return _XAllocColor(display, Colormap, ref colorcell_def); + } + + [DllImport ("libX11", EntryPoint="XSetTransientForHint")] + internal extern static int _XSetTransientForHint(IntPtr display, IntPtr window, IntPtr prop_window); + internal static int XSetTransientForHint(IntPtr display, IntPtr window, IntPtr prop_window) + { + DebugHelper.TraceWriteLine ("XSetTransientForHint"); + return _XSetTransientForHint(display, window, prop_window); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref MotifWmHints data, int nelements); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref MotifWmHints data, int nelements) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, ref data, nelements); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref uint value, int nelements); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref uint value, int nelements) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, ref value, nelements); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref IntPtr value, int nelements); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref IntPtr value, int nelements) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, ref value, nelements); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, uint[] data, int nelements); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, uint[] data, int nelements) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, data, nelements); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, int[] data, int nelements); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, int[] data, int nelements) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, data, nelements); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr[] data, int nelements); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr[] data, int nelements) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, data, nelements); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr atoms, int nelements); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr atoms, int nelements) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, atoms, nelements); + } + + [DllImport ("libX11", EntryPoint="XChangeProperty", CharSet=CharSet.Ansi)] + internal extern static int _XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, string text, int text_length); + internal static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, string text, int text_length) + { + DebugHelper.TraceWriteLine ("XChangeProperty"); + return _XChangeProperty(display, window, property, type, format, mode, text, text_length); + } + + [DllImport ("libX11", EntryPoint="XDeleteProperty")] + internal extern static int _XDeleteProperty(IntPtr display, IntPtr window, IntPtr property); + internal static int XDeleteProperty(IntPtr display, IntPtr window, IntPtr property) + { + DebugHelper.TraceWriteLine ("XDeleteProperty"); + return _XDeleteProperty(display, window, property); + } + + // Drawing + [DllImport ("libX11", EntryPoint="XCreateGC")] + internal extern static IntPtr _XCreateGC(IntPtr display, IntPtr window, IntPtr valuemask, ref XGCValues values); + internal static IntPtr XCreateGC(IntPtr display, IntPtr window, IntPtr valuemask, ref XGCValues values) + { + DebugHelper.TraceWriteLine ("XCreateGC"); + return _XCreateGC(display, window, valuemask, ref values); + } + + [DllImport ("libX11", EntryPoint="XFreeGC")] + internal extern static int _XFreeGC(IntPtr display, IntPtr gc); + internal static int XFreeGC(IntPtr display, IntPtr gc) + { + DebugHelper.TraceWriteLine ("XFreeGC"); + return _XFreeGC(display, gc); + } + + [DllImport ("libX11", EntryPoint="XSetFunction")] + internal extern static int _XSetFunction(IntPtr display, IntPtr gc, GXFunction function); + internal static int XSetFunction(IntPtr display, IntPtr gc, GXFunction function) + { + DebugHelper.TraceWriteLine ("XSetFunction"); + return _XSetFunction(display, gc, function); + } + + [DllImport ("libX11", EntryPoint="XSetLineAttributes")] + internal extern static int _XSetLineAttributes(IntPtr display, IntPtr gc, int line_width, GCLineStyle line_style, GCCapStyle cap_style, GCJoinStyle join_style); + internal static int XSetLineAttributes(IntPtr display, IntPtr gc, int line_width, GCLineStyle line_style, GCCapStyle cap_style, GCJoinStyle join_style) + { + DebugHelper.TraceWriteLine ("XSetLineAttributes"); + return _XSetLineAttributes(display, gc, line_width, line_style, cap_style, join_style); + } + + [DllImport ("libX11", EntryPoint="XDrawLine")] + internal extern static int _XDrawLine(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int x2, int y2); + internal static int XDrawLine(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int x2, int y2) + { + DebugHelper.TraceWriteLine ("XDrawLine"); + return _XDrawLine(display, drawable, gc, x1, y1, x2, y2); + } + + [DllImport ("libX11", EntryPoint="XDrawRectangle")] + internal extern static int _XDrawRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height); + internal static int XDrawRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height) + { + DebugHelper.TraceWriteLine ("XDrawRectangle"); + return _XDrawRectangle(display, drawable, gc, x1, y1, width, height); + } + + [DllImport ("libX11", EntryPoint="XFillRectangle")] + internal extern static int _XFillRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height); + internal static int XFillRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height) + { + DebugHelper.TraceWriteLine ("XFillRectangle"); + return _XFillRectangle(display, drawable, gc, x1, y1, width, height); + } + + [DllImport ("libX11", EntryPoint="XSetWindowBackground")] + internal extern static int _XSetWindowBackground(IntPtr display, IntPtr window, IntPtr background); + internal static int XSetWindowBackground(IntPtr display, IntPtr window, IntPtr background) + { + DebugHelper.TraceWriteLine ("XSetWindowBackground"); + return _XSetWindowBackground(display, window, background); + } + + [DllImport ("libX11", EntryPoint="XCopyArea")] + internal extern static int _XCopyArea(IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int src_x, int src_y, int width, int height, int dest_x, int dest_y); + internal static int XCopyArea(IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int src_x, int src_y, int width, int height, int dest_x, int dest_y) + { + DebugHelper.TraceWriteLine ("XCopyArea"); + return _XCopyArea(display, src, dest, gc, src_x, src_y, width, height, dest_x, dest_y); + } + + [DllImport ("libX11", EntryPoint="XGetWindowProperty")] + internal extern static int _XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset, IntPtr long_length, bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format, out IntPtr nitems, out IntPtr bytes_after, ref IntPtr prop); + internal static int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset, IntPtr long_length, bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format, out IntPtr nitems, out IntPtr bytes_after, ref IntPtr prop) + { + DebugHelper.TraceWriteLine ("XGetWindowProperty"); + return _XGetWindowProperty(display, window, atom, long_offset, long_length, delete, req_type, out actual_type, out actual_format, out nitems, out bytes_after, ref prop); + } + + [DllImport ("libX11", EntryPoint="XSetInputFocus")] + internal extern static int _XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time); + internal static int XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time) + { + DebugHelper.TraceWriteLine ("XSetInputFocus"); + return _XSetInputFocus(display, window, revert_to, time); + } + + [DllImport ("libX11", EntryPoint="XIconifyWindow")] + internal extern static int _XIconifyWindow(IntPtr display, IntPtr window, int screen_number); + internal static int XIconifyWindow(IntPtr display, IntPtr window, int screen_number) + { + DebugHelper.TraceWriteLine ("XIconifyWindow"); + return _XIconifyWindow(display, window, screen_number); + } + + [DllImport ("libX11", EntryPoint="XDefineCursor")] + internal extern static int _XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor); + internal static int XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor) + { + DebugHelper.TraceWriteLine ("XDefineCursor"); + return _XDefineCursor(display, window, cursor); + } + + [DllImport ("libX11", EntryPoint="XUndefineCursor")] + internal extern static int _XUndefineCursor(IntPtr display, IntPtr window); + internal static int XUndefineCursor(IntPtr display, IntPtr window) + { + DebugHelper.TraceWriteLine ("XUndefineCursor"); + return _XUndefineCursor(display, window); + } + + [DllImport ("libX11", EntryPoint="XFreeCursor")] + internal extern static int _XFreeCursor(IntPtr display, IntPtr cursor); + internal static int XFreeCursor(IntPtr display, IntPtr cursor) + { + DebugHelper.TraceWriteLine ("XFreeCursor"); + return _XFreeCursor(display, cursor); + } + + [DllImport ("libX11", EntryPoint="XCreateFontCursor")] + internal extern static IntPtr _XCreateFontCursor(IntPtr display, CursorFontShape shape); + internal static IntPtr XCreateFontCursor(IntPtr display, CursorFontShape shape) + { + DebugHelper.TraceWriteLine ("XCreateFontCursor"); + return _XCreateFontCursor(display, shape); + } + + [DllImport ("libX11", EntryPoint="XCreatePixmapCursor")] + internal extern static IntPtr _XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask, ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot); + internal static IntPtr XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask, ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot) + { + DebugHelper.TraceWriteLine ("XCreatePixmapCursor"); + return _XCreatePixmapCursor(display, source, mask, ref foreground_color, ref background_color, x_hot, y_hot); + } + + [DllImport ("libX11", EntryPoint="XCreatePixmapFromBitmapData")] + internal extern static IntPtr _XCreatePixmapFromBitmapData(IntPtr display, IntPtr drawable, byte[] data, int width, int height, IntPtr fg, IntPtr bg, int depth); + internal static IntPtr XCreatePixmapFromBitmapData(IntPtr display, IntPtr drawable, byte[] data, int width, int height, IntPtr fg, IntPtr bg, int depth) + { + DebugHelper.TraceWriteLine ("XCreatePixmapFromBitmapData"); + return _XCreatePixmapFromBitmapData(display, drawable, data, width, height, fg, bg, depth); + } + + [DllImport ("libX11", EntryPoint="XCreatePixmap")] + internal extern static IntPtr _XCreatePixmap(IntPtr display, IntPtr d, int width, int height, int depth); + internal static IntPtr XCreatePixmap(IntPtr display, IntPtr d, int width, int height, int depth) + { + DebugHelper.TraceWriteLine ("XCreatePixmap"); + return _XCreatePixmap(display, d, width, height, depth); + } + + [DllImport ("libX11", EntryPoint="XFreePixmap")] + internal extern static IntPtr _XFreePixmap(IntPtr display, IntPtr pixmap); + internal static IntPtr XFreePixmap(IntPtr display, IntPtr pixmap) + { + DebugHelper.TraceWriteLine ("XFreePixmap"); + return _XFreePixmap(display, pixmap); + } + + [DllImport ("libX11", EntryPoint="XQueryBestCursor")] + internal extern static int _XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height, out int best_width, out int best_height); + internal static int XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height, out int best_width, out int best_height) + { + DebugHelper.TraceWriteLine ("XQueryBestCursor"); + return _XQueryBestCursor(display, drawable, width, height, out best_width, out best_height); + } + + [DllImport ("libX11", EntryPoint="XQueryExtension")] + internal extern static int _XQueryExtension(IntPtr display, string extension_name, ref int major, ref int first_event, ref int first_error); + internal static int XQueryExtension(IntPtr display, string extension_name, ref int major, ref int first_event, ref int first_error) + { + DebugHelper.TraceWriteLine ("XQueryExtension"); + return _XQueryExtension(display, extension_name, ref major, ref first_event, ref first_error); + } + + [DllImport ("libX11", EntryPoint="XWhitePixel")] + internal extern static IntPtr _XWhitePixel(IntPtr display, int screen_no); + internal static IntPtr XWhitePixel(IntPtr display, int screen_no) + { + DebugHelper.TraceWriteLine ("XWhitePixel"); + return _XWhitePixel(display, screen_no); + } + + [DllImport ("libX11", EntryPoint="XBlackPixel")] + internal extern static IntPtr _XBlackPixel(IntPtr display, int screen_no); + internal static IntPtr XBlackPixel(IntPtr display, int screen_no) + { + DebugHelper.TraceWriteLine ("XBlackPixel"); + return _XBlackPixel(display, screen_no); + } + + [DllImport ("libX11", EntryPoint="XGrabServer")] + internal extern static void _XGrabServer(IntPtr display); + internal static void XGrabServer(IntPtr display) + { + DebugHelper.TraceWriteLine ("XGrabServer"); + _XGrabServer(display); + } + + [DllImport ("libX11", EntryPoint="XUngrabServer")] + internal extern static void _XUngrabServer(IntPtr display); + internal static void XUngrabServer(IntPtr display) + { + DebugHelper.TraceWriteLine ("XUngrabServer"); + _XUngrabServer(display); + } + + [DllImport ("libX11", EntryPoint="XGetWMNormalHints")] + internal extern static void _XGetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints, out IntPtr supplied_return); + internal static void XGetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints, out IntPtr supplied_return) + { + DebugHelper.TraceWriteLine ("XGetWMNormalHints"); + _XGetWMNormalHints(display, window, ref hints, out supplied_return); + } + + [DllImport ("libX11", EntryPoint="XSetWMNormalHints")] + internal extern static void _XSetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints); + internal static void XSetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints) + { + DebugHelper.TraceWriteLine ("XSetWMNormalHints"); + _XSetWMNormalHints(display, window, ref hints); + } + + [DllImport ("libX11", EntryPoint="XSetZoomHints")] + internal extern static void _XSetZoomHints(IntPtr display, IntPtr window, ref XSizeHints hints); + internal static void XSetZoomHints(IntPtr display, IntPtr window, ref XSizeHints hints) + { + DebugHelper.TraceWriteLine ("XSetZoomHints"); + _XSetZoomHints(display, window, ref hints); + } + + [DllImport ("libX11", EntryPoint="XSetWMHints")] + internal extern static void _XSetWMHints(IntPtr display, IntPtr window, ref XWMHints wmhints); + internal static void XSetWMHints(IntPtr display, IntPtr window, ref XWMHints wmhints) + { + DebugHelper.TraceWriteLine ("XSetWMHints"); + _XSetWMHints(display, window, ref wmhints); + } + + [DllImport ("libX11", EntryPoint="XGetIconSizes")] + internal extern static int _XGetIconSizes(IntPtr display, IntPtr window, out IntPtr size_list, out int count); + internal static int XGetIconSizes(IntPtr display, IntPtr window, out IntPtr size_list, out int count) + { + DebugHelper.TraceWriteLine ("XGetIconSizes"); + return _XGetIconSizes(display, window, out size_list, out count); + } + + [DllImport ("libX11", EntryPoint="XSetErrorHandler")] + internal extern static IntPtr _XSetErrorHandler(XErrorHandler error_handler); + internal static IntPtr XSetErrorHandler(XErrorHandler error_handler) + { + DebugHelper.TraceWriteLine ("XSetErrorHandler"); + return _XSetErrorHandler(error_handler); + } + + [DllImport ("libX11", EntryPoint="XGetErrorText")] + internal extern static IntPtr _XGetErrorText(IntPtr display, byte code, StringBuilder buffer, int length); + internal static IntPtr XGetErrorText(IntPtr display, byte code, StringBuilder buffer, int length) + { + DebugHelper.TraceWriteLine ("XGetErrorText"); + return _XGetErrorText(display, code, buffer, length); + } + + [DllImport ("libX11", EntryPoint="XInitThreads")] + internal extern static int _XInitThreads(); + internal static int XInitThreads() + { + DebugHelper.TraceWriteLine ("XInitThreads"); + return _XInitThreads(); + } + + [DllImport ("libX11", EntryPoint="XConvertSelection")] + internal extern static int _XConvertSelection(IntPtr display, IntPtr selection, IntPtr target, IntPtr property, IntPtr requestor, IntPtr time); + internal static int XConvertSelection(IntPtr display, IntPtr selection, IntPtr target, IntPtr property, IntPtr requestor, IntPtr time) + { + DebugHelper.TraceWriteLine ("XConvertSelection"); + return _XConvertSelection(display, selection, target, property, requestor, time); + } + + [DllImport ("libX11", EntryPoint="XGetSelectionOwner")] + internal extern static IntPtr _XGetSelectionOwner(IntPtr display, IntPtr selection); + internal static IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection) + { + DebugHelper.TraceWriteLine ("XGetSelectionOwner"); + return _XGetSelectionOwner(display, selection); + } + + [DllImport ("libX11", EntryPoint="XSetSelectionOwner")] + internal extern static int _XSetSelectionOwner(IntPtr display, IntPtr selection, IntPtr owner, IntPtr time); + internal static int XSetSelectionOwner(IntPtr display, IntPtr selection, IntPtr owner, IntPtr time) + { + DebugHelper.TraceWriteLine ("XSetSelectionOwner"); + return _XSetSelectionOwner(display, selection, owner, time); + } + + [DllImport ("libX11", EntryPoint="XSetPlaneMask")] + internal extern static int _XSetPlaneMask(IntPtr display, IntPtr gc, IntPtr mask); + internal static int XSetPlaneMask(IntPtr display, IntPtr gc, IntPtr mask) + { + DebugHelper.TraceWriteLine ("XSetPlaneMask"); + return _XSetPlaneMask(display, gc, mask); + } + + [DllImport ("libX11", EntryPoint="XSetForeground")] + internal extern static int _XSetForeground(IntPtr display, IntPtr gc, UIntPtr foreground); + internal static int XSetForeground(IntPtr display, IntPtr gc, UIntPtr foreground) + { + DebugHelper.TraceWriteLine ("XSetForeground"); + return _XSetForeground(display, gc, foreground); + } + + [DllImport ("libX11", EntryPoint="XSetBackground")] + internal extern static int _XSetBackground(IntPtr display, IntPtr gc, UIntPtr background); + internal static int XSetBackground(IntPtr display, IntPtr gc, UIntPtr background) + { + DebugHelper.TraceWriteLine ("XSetBackground"); + return _XSetBackground(display, gc, background); + } + + [DllImport ("libX11", EntryPoint="XBell")] + internal extern static int _XBell(IntPtr display, int percent); + internal static int XBell(IntPtr display, int percent) + { + DebugHelper.TraceWriteLine ("XBell"); + return _XBell(display, percent); + } + + [DllImport ("libX11", EntryPoint="XChangeActivePointerGrab")] + internal extern static int _XChangeActivePointerGrab (IntPtr display, EventMask event_mask, IntPtr cursor, IntPtr time); + internal static int XChangeActivePointerGrab (IntPtr display, EventMask event_mask, IntPtr cursor, IntPtr time) + { + DebugHelper.TraceWriteLine ("XChangeActivePointerGrab"); + return _XChangeActivePointerGrab (display, event_mask, cursor, time); + } + + [DllImport ("libX11", EntryPoint="XFilterEvent")] + internal extern static bool _XFilterEvent(ref XEvent xevent, IntPtr window); + internal static bool XFilterEvent(ref XEvent xevent, IntPtr window) + { + DebugHelper.TraceWriteLine ("XFilterEvent"); + return _XFilterEvent(ref xevent, window); + } + + [DllImport ("libX11", EntryPoint="XkbSetDetectableAutoRepeat")] + internal extern static void _XkbSetDetectableAutoRepeat (IntPtr display, bool detectable, IntPtr supported); + internal static void XkbSetDetectableAutoRepeat (IntPtr display, bool detectable, IntPtr supported) + { + DebugHelper.TraceWriteLine ("XkbSetDetectableAutoRepeat"); + _XkbSetDetectableAutoRepeat (display, detectable, supported); + } + + [DllImport ("libX11", EntryPoint="XPeekEvent")] + internal extern static void _XPeekEvent (IntPtr display, ref XEvent xevent); + internal static void XPeekEvent (IntPtr display, ref XEvent xevent) + { + DebugHelper.TraceWriteLine ("XPeekEvent"); + _XPeekEvent (display, ref xevent); + } + + [DllImport ("libX11", EntryPoint="XIfEvent")] + internal extern static void _XIfEvent (IntPtr display, ref XEvent xevent, Delegate event_predicate, IntPtr arg); + internal static void XIfEvent (IntPtr display, ref XEvent xevent, Delegate event_predicate, IntPtr arg) + { + DebugHelper.TraceWriteLine ("XIfEvent"); + _XIfEvent (display, ref xevent, event_predicate, arg); + } +#endregion + +#region Xinerama imports + [DllImport ("libXinerama", EntryPoint="XineramaQueryScreens")] + extern static IntPtr _XineramaQueryScreens (IntPtr display, out int number); + internal static IntPtr XineramaQueryScreens (IntPtr display, out int number) + { + DebugHelper.TraceWriteLine ("XineramaQueryScreens"); + return _XineramaQueryScreens (display, out number); + } + + [DllImport ("libXinerama", EntryPoint="XineramaIsActive")] + extern static bool _XineramaIsActive (IntPtr display); + static bool XineramaNotInstalled; + + internal static bool XineramaIsActive (IntPtr display) + { + DebugHelper.TraceWriteLine ("XineramaIsActive"); + + if (XineramaNotInstalled) + return false; + try { + return _XineramaIsActive (display); + } catch (DllNotFoundException) { + // Xinerama isn't installed + XineramaNotInstalled = true; + return false; + } + } +#endregion + +#else //no TRACE defined + +#region Xcursor imports + [DllImport ("libXcursor", EntryPoint = "XcursorLibraryLoadCursor")] + internal extern static IntPtr XcursorLibraryLoadCursor (IntPtr display, [MarshalAs (UnmanagedType.LPStr)] string name); + + [DllImport ("libXcursor", EntryPoint = "XcursorLibraryLoadImages")] + internal extern static IntPtr XcursorLibraryLoadImages ([MarshalAs (UnmanagedType.LPStr)] string file, IntPtr theme, int size); + + [DllImport ("libXcursor", EntryPoint = "XcursorImagesDestroy")] + internal extern static void XcursorImagesDestroy (IntPtr images); + + [DllImport ("libXcursor", EntryPoint = "XcursorGetDefaultSize")] + internal extern static int XcursorGetDefaultSize (IntPtr display); + + [DllImport ("libXcursor", EntryPoint = "XcursorImageLoadCursor")] + internal extern static IntPtr XcursorImageLoadCursor (IntPtr display, IntPtr image); + + [DllImport ("libXcursor", EntryPoint = "XcursorGetTheme")] + internal extern static IntPtr XcursorGetTheme (IntPtr display); +#endregion + #region X11 Imports + [DllImport ("libX11", EntryPoint="XOpenDisplay")] + internal extern static IntPtr XOpenDisplay(IntPtr display); + [DllImport ("libX11", EntryPoint="XCloseDisplay")] + internal extern static int XCloseDisplay(IntPtr display); + [DllImport ("libX11", EntryPoint="XSynchronize")] + internal extern static IntPtr XSynchronize(IntPtr display, bool onoff); + + [DllImport ("libX11", EntryPoint="XCreateWindow")] + internal extern static IntPtr XCreateWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, UIntPtr valuemask, ref XSetWindowAttributes attributes); + + [DllImport ("libX11", EntryPoint="XCreateSimpleWindow")] + internal extern static IntPtr XCreateSimpleWindow(IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, UIntPtr border, UIntPtr background); + + [DllImport ("libX11", EntryPoint="XMapWindow")] + internal extern static int XMapWindow(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XMapRaised")] + internal extern static int XMapRaised(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XUnmapWindow")] + internal extern static int XUnmapWindow(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XMapSubwindows")] + internal extern static int XMapSubindows(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XUnmapSubwindows")] + internal extern static int XUnmapSubwindows(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XRootWindow")] + internal extern static IntPtr XRootWindow(IntPtr display, int screen_number); + + [DllImport ("libX11", EntryPoint="XNextEvent")] + internal extern static IntPtr XNextEvent(IntPtr display, ref XEvent xevent); + + [DllImport ("libX11", EntryPoint="XConnectionNumber")] + internal extern static int XConnectionNumber (IntPtr display); + + [DllImport ("libX11", EntryPoint="XPending")] + internal extern static int XPending (IntPtr display); + + [DllImport ("libX11", EntryPoint="XSelectInput")] + internal extern static IntPtr XSelectInput(IntPtr display, IntPtr window, IntPtr mask); + + [DllImport ("libX11", EntryPoint="XDestroyWindow")] + internal extern static int XDestroyWindow(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XReparentWindow")] + internal extern static int XReparentWindow(IntPtr display, IntPtr window, IntPtr parent, int x, int y); + + [DllImport ("libX11", EntryPoint="XMoveResizeWindow")] + extern static int XMoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height); + internal static int MoveResizeWindow(IntPtr display, IntPtr window, int x, int y, int width, int height) + { + int ret = XMoveResizeWindow (display, window, x, y, width, height); + Keyboard.MoveCurrentCaretPos (); + return ret; + } + + [DllImport ("libX11", EntryPoint="XResizeWindow")] + internal extern static int XResizeWindow(IntPtr display, IntPtr window, int width, int height); + + [DllImport ("libX11", EntryPoint="XGetWindowAttributes")] + internal extern static int XGetWindowAttributes(IntPtr display, IntPtr window, ref XWindowAttributes attributes); + + [DllImport ("libX11", EntryPoint="XFlush")] + internal extern static int XFlush(IntPtr display); + + [DllImport ("libX11", EntryPoint="XSetWMName")] + internal extern static int XSetWMName(IntPtr display, IntPtr window, ref XTextProperty text_prop); + + [DllImport ("libX11", EntryPoint="XStoreName")] + internal extern static int XStoreName(IntPtr display, IntPtr window, string window_name); + + [DllImport ("libX11", EntryPoint="XFetchName")] + internal extern static int XFetchName(IntPtr display, IntPtr window, ref IntPtr window_name); + + [DllImport ("libX11", EntryPoint="XSendEvent")] + internal extern static int XSendEvent(IntPtr display, IntPtr window, bool propagate, IntPtr event_mask, ref XEvent send_event); + + [DllImport ("libX11", EntryPoint="XQueryTree")] + internal extern static int XQueryTree(IntPtr display, IntPtr window, out IntPtr root_return, out IntPtr parent_return, out IntPtr children_return, out int nchildren_return); + + [DllImport ("libX11", EntryPoint="XFree")] + internal extern static int XFree(IntPtr data); + + [DllImport ("libX11", EntryPoint="XRaiseWindow")] + internal extern static int XRaiseWindow(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XLowerWindow")] + internal extern static uint XLowerWindow(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XConfigureWindow")] + internal extern static uint XConfigureWindow(IntPtr display, IntPtr window, ChangeWindowFlags value_mask, ref XWindowChanges values); + + [DllImport ("libX11", EntryPoint="XInternAtom")] + internal extern static IntPtr XInternAtom(IntPtr display, string atom_name, bool only_if_exists); + + [DllImport ("libX11", EntryPoint="XInternAtoms")] + internal extern static int XInternAtoms(IntPtr display, string[] atom_names, int atom_count, bool only_if_exists, IntPtr[] atoms); + + [DllImport ("libX11", EntryPoint="XSetWMProtocols")] + internal extern static int XSetWMProtocols(IntPtr display, IntPtr window, IntPtr[] protocols, int count); + + [DllImport ("libX11", EntryPoint="XGrabPointer")] + internal extern static int XGrabPointer(IntPtr display, IntPtr window, bool owner_events, EventMask event_mask, GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, IntPtr cursor, IntPtr timestamp); + + [DllImport ("libX11", EntryPoint="XUngrabPointer")] + internal extern static int XUngrabPointer(IntPtr display, IntPtr timestamp); + + [DllImport ("libX11", EntryPoint="XQueryPointer")] + internal extern static bool XQueryPointer(IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons); + + [DllImport ("libX11", EntryPoint="XTranslateCoordinates")] + internal extern static bool XTranslateCoordinates (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return); + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool XGetGeometry(IntPtr display, IntPtr window, out IntPtr root, out int x, out int y, out int width, out int height, out int border_width, out int depth); + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, out int width, out int height, IntPtr border_width, IntPtr depth); + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, out int x, out int y, IntPtr width, IntPtr height, IntPtr border_width, IntPtr depth); + + [DllImport ("libX11", EntryPoint="XGetGeometry")] + internal extern static bool XGetGeometry(IntPtr display, IntPtr window, IntPtr root, IntPtr x, IntPtr y, out int width, out int height, IntPtr border_width, IntPtr depth); + + [DllImport ("libX11", EntryPoint="XWarpPointer")] + internal extern static uint XWarpPointer(IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, uint src_width, uint src_height, int dest_x, int dest_y); + + [DllImport ("libX11", EntryPoint="XClearWindow")] + internal extern static int XClearWindow(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XClearArea")] + internal extern static int XClearArea(IntPtr display, IntPtr window, int x, int y, int width, int height, bool exposures); + + // Colormaps + [DllImport ("libX11", EntryPoint="XDefaultScreenOfDisplay")] + internal extern static IntPtr XDefaultScreenOfDisplay(IntPtr display); + + [DllImport ("libX11", EntryPoint="XScreenNumberOfScreen")] + internal extern static int XScreenNumberOfScreen(IntPtr display, IntPtr Screen); + + [DllImport ("libX11", EntryPoint="XDefaultVisual")] + internal extern static IntPtr XDefaultVisual(IntPtr display, int screen_number); + + [DllImport ("libX11", EntryPoint="XDefaultDepth")] + internal extern static uint XDefaultDepth(IntPtr display, int screen_number); + + [DllImport ("libX11", EntryPoint="XDefaultScreen")] + internal extern static int XDefaultScreen(IntPtr display); + + [DllImport ("libX11", EntryPoint="XDefaultColormap")] + internal extern static IntPtr XDefaultColormap(IntPtr display, int screen_number); + + [DllImport ("libX11", EntryPoint="XLookupColor")] + internal extern static int XLookupColor(IntPtr display, IntPtr Colormap, string Coloranem, ref XColor exact_def_color, ref XColor screen_def_color); + + [DllImport ("libX11", EntryPoint="XAllocColor")] + internal extern static int XAllocColor(IntPtr display, IntPtr Colormap, ref XColor colorcell_def); + + [DllImport ("libX11", EntryPoint="XSetTransientForHint")] + internal extern static int XSetTransientForHint(IntPtr display, IntPtr window, IntPtr prop_window); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref MotifWmHints data, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref uint value, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, ref IntPtr value, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, uint[] data, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, int[] data, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr[] data, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, IntPtr atoms, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty", CharSet=CharSet.Ansi)] + internal extern static int XChangeProperty(IntPtr display, IntPtr window, IntPtr property, IntPtr type, int format, PropertyMode mode, string text, int text_length); + + [DllImport ("libX11", EntryPoint="XDeleteProperty")] + internal extern static int XDeleteProperty(IntPtr display, IntPtr window, IntPtr property); + + // Drawing + [DllImport ("libX11", EntryPoint="XCreateGC")] + internal extern static IntPtr XCreateGC(IntPtr display, IntPtr window, IntPtr valuemask, ref XGCValues values); + + [DllImport ("libX11", EntryPoint="XFreeGC")] + internal extern static int XFreeGC(IntPtr display, IntPtr gc); + + [DllImport ("libX11", EntryPoint="XSetFunction")] + internal extern static int XSetFunction(IntPtr display, IntPtr gc, GXFunction function); + + [DllImport ("libX11", EntryPoint="XSetLineAttributes")] + internal extern static int XSetLineAttributes(IntPtr display, IntPtr gc, int line_width, GCLineStyle line_style, GCCapStyle cap_style, GCJoinStyle join_style); + + [DllImport ("libX11", EntryPoint="XDrawLine")] + internal extern static int XDrawLine(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int x2, int y2); + + [DllImport ("libX11", EntryPoint="XDrawRectangle")] + internal extern static int XDrawRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height); + + [DllImport ("libX11", EntryPoint="XFillRectangle")] + internal extern static int XFillRectangle(IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height); + + [DllImport ("libX11", EntryPoint="XSetWindowBackground")] + internal extern static int XSetWindowBackground(IntPtr display, IntPtr window, IntPtr background); + + [DllImport ("libX11", EntryPoint="XCopyArea")] + internal extern static int XCopyArea(IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int src_x, int src_y, int width, int height, int dest_x, int dest_y); + + [DllImport ("libX11", EntryPoint="XGetWindowProperty")] + internal extern static int XGetWindowProperty(IntPtr display, IntPtr window, IntPtr atom, IntPtr long_offset, IntPtr long_length, bool delete, IntPtr req_type, out IntPtr actual_type, out int actual_format, out IntPtr nitems, out IntPtr bytes_after, ref IntPtr prop); + + [DllImport ("libX11", EntryPoint="XSetInputFocus")] + internal extern static int XSetInputFocus(IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time); + + [DllImport ("libX11", EntryPoint="XIconifyWindow")] + internal extern static int XIconifyWindow(IntPtr display, IntPtr window, int screen_number); + + [DllImport ("libX11", EntryPoint="XDefineCursor")] + internal extern static int XDefineCursor(IntPtr display, IntPtr window, IntPtr cursor); + + [DllImport ("libX11", EntryPoint="XUndefineCursor")] + internal extern static int XUndefineCursor(IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XFreeCursor")] + internal extern static int XFreeCursor(IntPtr display, IntPtr cursor); + + [DllImport ("libX11", EntryPoint="XCreateFontCursor")] + internal extern static IntPtr XCreateFontCursor(IntPtr display, CursorFontShape shape); + + [DllImport ("libX11", EntryPoint="XCreatePixmapCursor")] + internal extern static IntPtr XCreatePixmapCursor(IntPtr display, IntPtr source, IntPtr mask, ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot); + + [DllImport ("libX11", EntryPoint="XCreatePixmapFromBitmapData")] + internal extern static IntPtr XCreatePixmapFromBitmapData(IntPtr display, IntPtr drawable, byte[] data, int width, int height, IntPtr fg, IntPtr bg, int depth); + + [DllImport ("libX11", EntryPoint="XCreatePixmap")] + internal extern static IntPtr XCreatePixmap(IntPtr display, IntPtr d, int width, int height, int depth); + + [DllImport ("libX11", EntryPoint="XFreePixmap")] + internal extern static IntPtr XFreePixmap(IntPtr display, IntPtr pixmap); + + [DllImport ("libX11", EntryPoint="XQueryBestCursor")] + internal extern static int XQueryBestCursor(IntPtr display, IntPtr drawable, int width, int height, out int best_width, out int best_height); + + [DllImport ("libX11", EntryPoint="XQueryExtension")] + internal extern static int XQueryExtension(IntPtr display, string extension_name, ref int major, ref int first_event, ref int first_error); + + [DllImport ("libX11", EntryPoint="XWhitePixel")] + internal extern static IntPtr XWhitePixel(IntPtr display, int screen_no); + + [DllImport ("libX11", EntryPoint="XBlackPixel")] + internal extern static IntPtr XBlackPixel(IntPtr display, int screen_no); + + [DllImport ("libX11", EntryPoint="XGrabServer")] + internal extern static void XGrabServer(IntPtr display); + + [DllImport ("libX11", EntryPoint="XUngrabServer")] + internal extern static void XUngrabServer(IntPtr display); + + [DllImport ("libX11", EntryPoint="XGetWMNormalHints")] + internal extern static void XGetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints, out IntPtr supplied_return); + + [DllImport ("libX11", EntryPoint="XSetWMNormalHints")] + internal extern static void XSetWMNormalHints(IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport ("libX11", EntryPoint="XSetZoomHints")] + internal extern static void XSetZoomHints(IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport ("libX11", EntryPoint="XSetWMHints")] + internal extern static void XSetWMHints(IntPtr display, IntPtr window, ref XWMHints wmhints); + + [DllImport ("libX11", EntryPoint="XGetIconSizes")] + internal extern static int XGetIconSizes(IntPtr display, IntPtr window, out IntPtr size_list, out int count); + + [DllImport ("libX11", EntryPoint="XSetErrorHandler")] + internal extern static IntPtr XSetErrorHandler(XErrorHandler error_handler); + + [DllImport ("libX11", EntryPoint="XGetErrorText")] + internal extern static IntPtr XGetErrorText(IntPtr display, byte code, StringBuilder buffer, int length); + + [DllImport ("libX11", EntryPoint="XInitThreads")] + internal extern static int XInitThreads(); + + [DllImport ("libX11", EntryPoint="XConvertSelection")] + internal extern static int XConvertSelection(IntPtr display, IntPtr selection, IntPtr target, IntPtr property, IntPtr requestor, IntPtr time); + + [DllImport ("libX11", EntryPoint="XGetSelectionOwner")] + internal extern static IntPtr XGetSelectionOwner(IntPtr display, IntPtr selection); + + [DllImport ("libX11", EntryPoint="XSetSelectionOwner")] + internal extern static int XSetSelectionOwner(IntPtr display, IntPtr selection, IntPtr owner, IntPtr time); + + [DllImport ("libX11", EntryPoint="XSetPlaneMask")] + internal extern static int XSetPlaneMask(IntPtr display, IntPtr gc, IntPtr mask); + + [DllImport ("libX11", EntryPoint="XSetForeground")] + internal extern static int XSetForeground(IntPtr display, IntPtr gc, UIntPtr foreground); + + [DllImport ("libX11", EntryPoint="XSetBackground")] + internal extern static int XSetBackground(IntPtr display, IntPtr gc, UIntPtr background); + + [DllImport ("libX11", EntryPoint="XBell")] + internal extern static int XBell(IntPtr display, int percent); + + [DllImport ("libX11", EntryPoint="XChangeActivePointerGrab")] + internal extern static int XChangeActivePointerGrab (IntPtr display, EventMask event_mask, IntPtr cursor, IntPtr time); + + [DllImport ("libX11", EntryPoint="XFilterEvent")] + internal extern static bool XFilterEvent(ref XEvent xevent, IntPtr window); + + [DllImport ("libX11", EntryPoint="XkbSetDetectableAutoRepeat")] + internal extern static void XkbSetDetectableAutoRepeat (IntPtr display, bool detectable, IntPtr supported); + + [DllImport ("libX11", EntryPoint="XPeekEvent")] + internal extern static void XPeekEvent (IntPtr display, ref XEvent xevent); + + [DllImport ("libX11", EntryPoint="XIfEvent")] + internal extern static void XIfEvent (IntPtr display, ref XEvent xevent, Delegate event_predicate, IntPtr arg); + + [DllImport ("libX11", EntryPoint="XGetInputFocus")] + internal extern static void XGetInputFocus (IntPtr display, out IntPtr focus, out IntPtr revert_to); + #endregion +#region Gtk/Gdk imports + [DllImport("libgdk-x11-2.0")] + internal extern static IntPtr gdk_atom_intern (string atomName, bool onlyIfExists); + + [DllImport("libgtk-x11-2.0")] + internal extern static IntPtr gtk_clipboard_get (IntPtr atom); + + [DllImport("libgtk-x11-2.0")] + internal extern static void gtk_clipboard_store (IntPtr clipboard); + + [DllImport("libgtk-x11-2.0")] + internal extern static void gtk_clipboard_set_text (IntPtr clipboard, string text, int len); +#endregion + + +#region Xinerama imports + [DllImport ("libXinerama")] + internal extern static IntPtr XineramaQueryScreens (IntPtr display, out int number); + + [DllImport ("libXinerama", EntryPoint = "XineramaIsActive")] + extern static bool _XineramaIsActive (IntPtr display); + static bool XineramaNotInstalled; + + internal static bool XineramaIsActive (IntPtr display) + { + if (XineramaNotInstalled) + return false; + try { + return _XineramaIsActive (display); + } catch (DllNotFoundException) { + // Xinerama isn't installed + XineramaNotInstalled = true; + return false; + } + } +#endregion + +#endif + } +} diff --git a/source/ShiftUI/Internal/XplatUIX11GTK.cs b/source/ShiftUI/Internal/XplatUIX11GTK.cs new file mode 100644 index 0000000..03ea185 --- /dev/null +++ b/source/ShiftUI/Internal/XplatUIX11GTK.cs @@ -0,0 +1,4634 @@ +// 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 [email protected] +// Alexander Olk [email protected] +// +// + +// NOTE: +// This driver understands the following environment variables: (Set the var to enable feature) +// +// MONO_XEXCEPTIONS = throw an exception when a X11 error is encountered; +// by default a message is displayed but execution continues +// +// MONO_XSYNC = perform all X11 commands synchronous; this is slower but +// helps in debugging errors +// + +// NOT COMPLETE - WORK IN PROGRESS + +// One feature of the driver is, that PaintEventstart returns a graphics context created from a offscreen drawable (pixmap) + +// define to log Window handles and relationships to stdout +#undef DriverDebug + +// Extra detailed debug +#undef DriverDebugExtra + +using System; +using System.ComponentModel; +using System.Collections; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +// Only do the poll when building with mono for now +#if __MonoCS__ +using Mono.Unix.Native; +#endif + +/// X11 Version +namespace ShiftUI { + internal class XplatUIX11GTK : XplatUIDriver { + + internal enum GdkWindowClass { + GDK_INPUT_OUTPUT, + GDK_INPUT_ONLY + } + + internal enum GdkWindowType { + GDK_WINDOW_ROOT, + GDK_WINDOW_TOPLEVEL, + GDK_WINDOW_CHILD, + GDK_WINDOW_DIALOG, + GDK_WINDOW_TEMP, + GDK_WINDOW_FOREIGN + } + + internal enum GdkWindowHints { + GDK_HINT_POS = 1 << 0, + GDK_HINT_MIN_SIZE = 1 << 1, + GDK_HINT_MAX_SIZE = 1 << 2, + GDK_HINT_BASE_SIZE = 1 << 3, + GDK_HINT_ASPECT = 1 << 4, + GDK_HINT_RESIZE_INC = 1 << 5, + GDK_HINT_WIN_GRAVITY = 1 << 6, + GDK_HINT_USER_POS = 1 << 7, + GDK_HINT_USER_SIZE = 1 << 8 + } + + internal enum GdkGravity { + GDK_GRAVITY_NORTH_WEST = 1, + GDK_GRAVITY_NORTH, + GDK_GRAVITY_NORTH_EAST, + GDK_GRAVITY_WEST, + GDK_GRAVITY_CENTER, + GDK_GRAVITY_EAST, + GDK_GRAVITY_SOUTH_WEST, + GDK_GRAVITY_SOUTH, + GDK_GRAVITY_SOUTH_EAST, + GDK_GRAVITY_STATIC + } + + internal enum GdkWindowEdge { + GDK_WINDOW_EDGE_NORTH_WEST, + GDK_WINDOW_EDGE_NORTH, + GDK_WINDOW_EDGE_NORTH_EAST, + GDK_WINDOW_EDGE_WEST, + GDK_WINDOW_EDGE_EAST, + GDK_WINDOW_EDGE_SOUTH_WEST, + GDK_WINDOW_EDGE_SOUTH, + GDK_WINDOW_EDGE_SOUTH_EAST + } + + internal enum GdkWindowTypeHint { + GDK_WINDOW_TYPE_HINT_NORMAL, + GDK_WINDOW_TYPE_HINT_DIALOG, + GDK_WINDOW_TYPE_HINT_MENU, + GDK_WINDOW_TYPE_HINT_TOOLBAR, + GDK_WINDOW_TYPE_HINT_SPLASHSCREEN, + GDK_WINDOW_TYPE_HINT_UTILITY, + GDK_WINDOW_TYPE_HINT_DOCK, + GDK_WINDOW_TYPE_HINT_DESKTOP + } + + internal enum GdkWindowAttributesType { + GDK_WA_TITLE = 1 << 1, + GDK_WA_X = 1 << 2, + GDK_WA_Y = 1 << 3, + GDK_WA_CURSOR = 1 << 4, + GDK_WA_COLORMAP = 1 << 5, + GDK_WA_VISUAL = 1 << 6, + GDK_WA_WMCLASS = 1 << 7, + GDK_WA_NOREDIR = 1 << 8 + } + + internal enum GdkEventMask { + GDK_EXPOSURE_MASK = 1 << 1, + GDK_POINTER_MOTION_MASK = 1 << 2, + GDK_POINTER_MOTION_HINT_MASK = 1 << 3, + GDK_BUTTON_MOTION_MASK = 1 << 4, + GDK_BUTTON1_MOTION_MASK = 1 << 5, + GDK_BUTTON2_MOTION_MASK = 1 << 6, + GDK_BUTTON3_MOTION_MASK = 1 << 7, + GDK_BUTTON_PRESS_MASK = 1 << 8, + GDK_BUTTON_RELEASE_MASK = 1 << 9, + GDK_KEY_PRESS_MASK = 1 << 10, + GDK_KEY_RELEASE_MASK = 1 << 11, + GDK_ENTER_NOTIFY_MASK = 1 << 12, + GDK_LEAVE_NOTIFY_MASK = 1 << 13, + GDK_FOCUS_CHANGE_MASK = 1 << 14, + GDK_STRUCTURE_MASK = 1 << 15, + GDK_PROPERTY_CHANGE_MASK = 1 << 16, + GDK_VISIBILITY_NOTIFY_MASK = 1 << 17, + GDK_PROXIMITY_IN_MASK = 1 << 18, + GDK_PROXIMITY_OUT_MASK = 1 << 19, + GDK_SUBSTRUCTURE_MASK = 1 << 20, + GDK_SCROLL_MASK = 1 << 21, + GDK_ALL_EVENTS_MASK = 0x3FFFFE + } + + internal enum GdkEventType { + GDK_NOTHING = -1, + GDK_DELETE = 0, + GDK_DESTROY = 1, + GDK_EXPOSE = 2, + GDK_MOTION_NOTIFY = 3, + GDK_BUTTON_PRESS = 4, + GDK_2BUTTON_PRESS = 5, + GDK_3BUTTON_PRESS = 6, + GDK_BUTTON_RELEASE = 7, + GDK_KEY_PRESS = 8, + GDK_KEY_RELEASE = 9, + GDK_ENTER_NOTIFY = 10, + GDK_LEAVE_NOTIFY = 11, + GDK_FOCUS_CHANGE = 12, + GDK_CONFIGURE = 13, + GDK_MAP = 14, + GDK_UNMAP = 15, + GDK_PROPERTY_NOTIFY = 16, + GDK_SELECTION_CLEAR = 17, + GDK_SELECTION_REQUEST = 18, + GDK_SELECTION_NOTIFY = 19, + GDK_PROXIMITY_IN = 20, + GDK_PROXIMITY_OUT = 21, + GDK_DRAG_ENTER = 22, + GDK_DRAG_LEAVE = 23, + GDK_DRAG_MOTION = 24, + GDK_DRAG_STATUS = 25, + GDK_DROP_START = 26, + GDK_DROP_FINISHED = 27, + GDK_CLIENT_EVENT = 28, + GDK_VISIBILITY_NOTIFY = 29, + GDK_NO_EXPOSE = 30, + GDK_SCROLL = 31, + GDK_WINDOW_STATE = 32, + GDK_SETTING = 33, + GDK_OWNER_CHANGE = 34, + GDK_GRAB_BROKEN = 35 + } + + internal enum GdkWMDecoration { + GDK_DECOR_ALL = 1 << 0, + GDK_DECOR_BORDER = 1 << 1, + GDK_DECOR_RESIZEH = 1 << 2, + GDK_DECOR_TITLE = 1 << 3, + GDK_DECOR_MENU = 1 << 4, + GDK_DECOR_MINIMIZE = 1 << 5, + GDK_DECOR_MAXIMIZE = 1 << 6 + } + + internal enum GdkWMFunction { + GDK_FUNC_ALL = 1 << 0, + GDK_FUNC_RESIZE = 1 << 1, + GDK_FUNC_MOVE = 1 << 2, + GDK_FUNC_MINIMIZE = 1 << 3, + GDK_FUNC_MAXIMIZE = 1 << 4, + GDK_FUNC_CLOSE = 1 << 5 + } + + internal enum GdkCursorType { + GDK_X_CURSOR = 0, + GDK_ARROW = 2, + GDK_BASED_ARROW_DOWN = 4, + GDK_BASED_ARROW_UP = 6, + GDK_BOAT = 8, + GDK_BOGOSITY = 10, + GDK_BOTTOM_LEFT_CORNER = 12, + GDK_BOTTOM_RIGHT_CORNER = 14, + GDK_BOTTOM_SIDE = 16, + GDK_BOTTOM_TEE = 18, + GDK_BOX_SPIRAL = 20, + GDK_CENTER_PTR = 22, + GDK_CIRCLE = 24, + GDK_CLOCK = 26, + GDK_COFFEE_MUG = 28, + GDK_CROSS = 30, + GDK_CROSS_REVERSE = 32, + GDK_CROSSHAIR = 34, + GDK_DIAMOND_CROSS = 36, + GDK_DOT = 38, + GDK_DOTBOX = 40, + GDK_DOUBLE_ARROW = 42, + GDK_DRAFT_LARGE = 44, + GDK_DRAFT_SMALL = 46, + GDK_DRAPED_BOX = 48, + GDK_EXCHANGE = 50, + GDK_FLEUR = 52, + GDK_GOBBLER = 54, + GDK_GUMBY = 56, + GDK_HAND1 = 58, + GDK_HAND2 = 60, + GDK_HEART = 62, + GDK_ICON = 64, + GDK_IRON_CROSS = 66, + GDK_LEFT_PTR = 68, + GDK_LEFT_SIDE = 70, + GDK_LEFT_TEE = 72, + GDK_LEFTBUTTON = 74, + GDK_LL_ANGLE = 76, + GDK_LR_ANGLE = 78, + GDK_MAN = 80, + GDK_MIDDLEBUTTON = 82, + GDK_MOUSE = 84, + GDK_PENCIL = 86, + GDK_PIRATE = 88, + GDK_PLUS = 90, + GDK_QUESTION_ARROW = 92, + GDK_RIGHT_PTR = 94, + GDK_RIGHT_SIDE = 96, + GDK_RIGHT_TEE = 98, + GDK_RIGHTBUTTON = 100, + GDK_RTL_LOGO = 102, + GDK_SAILBOAT = 104, + GDK_SB_DOWN_ARROW = 106, + GDK_SB_H_DOUBLE_ARROW = 108, + GDK_SB_LEFT_ARROW = 110, + GDK_SB_RIGHT_ARROW = 112, + GDK_SB_UP_ARROW = 114, + GDK_SB_V_DOUBLE_ARROW = 116, + GDK_SHUTTLE = 118, + GDK_SIZING = 120, + GDK_SPIDER = 122, + GDK_SPRAYCAN = 124, + GDK_STAR = 126, + GDK_TARGET = 128, + GDK_TCROSS = 130, + GDK_TOP_LEFT_ARROW = 132, + GDK_TOP_LEFT_CORNER = 134, + GDK_TOP_RIGHT_CORNER = 136, + GDK_TOP_SIDE = 138, + GDK_TOP_TEE = 140, + GDK_TREK = 142, + GDK_UL_ANGLE = 144, + GDK_UMBRELLA = 146, + GDK_UR_ANGLE = 148, + GDK_WATCH = 150, + GDK_XTERM = 152, + GDK_LAST_CURSOR, + GDK_CURSOR_IS_PIXMAP = -1 + } + + internal enum GdkPropMode { + GDK_PROP_MODE_REPLACE, + GDK_PROP_MODE_PREPEND, + GDK_PROP_MODE_APPEND + } + + [StructLayout (LayoutKind.Sequential)] + internal struct GdkGeometry { + internal int min_width; + internal int min_height; + internal int max_width; + internal int max_height; + internal int base_width; + internal int base_height; + internal int width_inc; + internal int height_inc; + internal double min_aspect; + internal double max_aspect; + internal GdkGravity win_gravity; + } + + [StructLayout (LayoutKind.Sequential)] + internal struct GdkWindowAttr { + internal string title; + internal int event_mask; + internal int x, y; + internal int width; + internal int height; + internal GdkWindowClass wclass; + internal IntPtr visual; + internal IntPtr colormap; + internal GdkWindowType window_type; + internal IntPtr cursor; + internal string wmclass_name; + internal string wmclass_class; + internal bool override_redirect; + } + + #region Local Variables + // General + static volatile XplatUIX11GTK Instance; + private static int RefCount; + private static object XlibLock; // Our locking object + + // General X11 + private static IntPtr DisplayHandle; // X11 handle to display + private static IntPtr GdkDisplayHandle; // gdk handle to display + private static int ScreenNo; // Screen number used + private static IntPtr GdkScreen; + private static IntPtr DefaultColormap; // Colormap for screen + private static IntPtr GdkDefaultColormap; // Gdk Colormap for screen + private static IntPtr CustomVisual; // Visual for window creation + private static IntPtr GdkCustomVisual; + private static IntPtr CustomColormap; // Colormap for window creation + private static IntPtr GdkCustomColormap; + private static int VisualBestDepth; + private static IntPtr RootWindow; // Handle of the root window for the screen/display + private static IntPtr GdkRootWindow; // Gdk handle of the root window for the screen/display + private static IntPtr FosterParent; // Container to hold child windows until their parent exists + private static IntPtr GdkFosterParent; // Container to hold child windows until their parent exists + private static XErrorHandler ErrorHandler; // Error handler delegate + private static bool ErrorExceptions; // Throw exceptions on X errors + private static bool PostQuitState; // True if we've got an pending exit + + // Clipboard + private static IntPtr ClipMagic = new IntPtr(27051977); + + // Communication + private static int PostAtom; // PostMessage atom + private static int AsyncAtom; // Support for async messages + + // Message Loop + private static XEventQueue MessageQueue; // Holds our queued up events + #if __MonoCS__ // + private static Pollfd[] pollfds; // For watching the X11 socket + #endif // + private static X11Keyboard Keyboard; // + private static X11Dnd Dnd; + private static Socket listen; // + private static Socket wake; // + private static Socket wake_receive; // + private static byte[] network_buffer; // + + + // Focus tracking + private static IntPtr ActiveWindow; // Handle of the active window + private static IntPtr FocusWindow; // Handle of the window with keyboard focus (if any) + + // Modality support + private static Stack ModalWindows; // Stack of our modal windows + + // Systray + private static IntPtr SystrayMgrWindow; // Handle of the Systray Manager window + + // Cursors + private static IntPtr LastCursorWindow; // The last window we set the cursor on + private static IntPtr LastCursorHandle; // The handle that was last set on LastCursorWindow + private static IntPtr OverrideCursorHandle; // The cursor that is set to override any other cursors + + // Caret + private static CaretStruct Caret; // + + // Support for Window Styles + private static int[] NetAtoms; // All atoms we know + + // mouse hover message generation + private static HoverStruct HoverState; // + + // double click message generation + private static ClickStruct ClickPending; // + + // Support for mouse grab + private static GrabStruct Grab; // + + // State + private static Point MousePosition; // Last position of mouse, in screen coords + internal static MouseButtons MouseState; // Last state of mouse buttons + + // Timers + private static ArrayList TimerList; // Holds SWF.Timers + + // 'Constants' + private static int DoubleClickInterval; // msec; max interval between clicks to count as double click + + const GdkEventMask GdkSelectInputMask = GdkEventMask.GDK_BUTTON_PRESS_MASK | + GdkEventMask.GDK_BUTTON_RELEASE_MASK | + GdkEventMask.GDK_KEY_PRESS_MASK | + GdkEventMask.GDK_KEY_RELEASE_MASK | + GdkEventMask.GDK_ENTER_NOTIFY_MASK | + GdkEventMask.GDK_LEAVE_NOTIFY_MASK | + GdkEventMask.GDK_EXPOSURE_MASK | + GdkEventMask.GDK_FOCUS_CHANGE_MASK | + GdkEventMask.GDK_POINTER_MOTION_MASK | + GdkEventMask.GDK_VISIBILITY_NOTIFY_MASK | + GdkEventMask.GDK_SUBSTRUCTURE_MASK | + GdkEventMask.GDK_STRUCTURE_MASK; + + static readonly object lockobj = new object (); + + static Hashtable backing_store = new Hashtable (5); + + #endregion // Local Variables + #region Constructors + private XplatUIX11GTK () + { + Console.WriteLine ("XplatUIX11GTK ctor..."); + // Handle singleton stuff first + RefCount = 0; + + // init gdk + gdk_init_check (IntPtr.Zero, IntPtr.Zero); + + // Now regular initialization + XlibLock = new object (); + MessageQueue = new XEventQueue (); + TimerList = new ArrayList (); + XInitThreads (); + + ErrorExceptions = false; + + // X11 Initialization + SetDisplay (gdk_x11_display_get_xdisplay (gdk_display_get_default ())); + X11DesktopColors.Initialize (); + + // Handle any upcoming errors; we re-set it here, X11DesktopColor stuff might have stolen it (gtk does) + ErrorHandler = new XErrorHandler (HandleError); + XSetErrorHandler (ErrorHandler); + } + #endregion // Constructors + + #region Singleton Specific Code + public static XplatUIX11GTK GetInstance () + { + lock (lockobj) { + if (Instance == null) { + Instance = new XplatUIX11GTK (); + } + RefCount++; + } + return Instance; + } + + public int Reference { + get { + return RefCount; + } + } + #endregion + + #region Internal Properties + internal static IntPtr Display { + get { + return DisplayHandle; + } + + set { + XplatUIX11GTK.GetInstance ().SetDisplay (value); + } + } + + internal static int Screen { + get { + return ScreenNo; + } + + set { + ScreenNo = value; + } + } + + internal static IntPtr RootWindowHandle { + get { + return RootWindow; + } + + set { + RootWindow = value; + } + } + + internal static IntPtr Visual { + get { + return CustomVisual; + } + + set { + CustomVisual = value; + } + } + + internal static IntPtr ColorMap { + get { + return CustomColormap; + } + + set { + CustomColormap = value; + } + } + #endregion + + #region XExceptionClass + internal class XException : ApplicationException { + IntPtr Display; + IntPtr ResourceID; + IntPtr Serial; + XRequest RequestCode; + byte ErrorCode; + byte MinorCode; + + public XException (IntPtr Display, IntPtr ResourceID, IntPtr Serial, byte ErrorCode, XRequest RequestCode, byte MinorCode) + { + this.Display = Display; + this.ResourceID = ResourceID; + this.Serial = Serial; + this.RequestCode = RequestCode; + this.ErrorCode = ErrorCode; + this.MinorCode = MinorCode; + } + + public override string Message { + get { + return GetMessage (Display, ResourceID, Serial, ErrorCode, RequestCode, MinorCode); + } + } + + public static string GetMessage (IntPtr Display, IntPtr ResourceID, IntPtr Serial, byte ErrorCode, XRequest RequestCode, byte MinorCode) + { + StringBuilder sb; + string x_error_text; + string error; + + sb = new StringBuilder (160); + XGetErrorText (Display, ErrorCode, sb, sb.Capacity); + x_error_text = sb.ToString (); + + error = String.Format ("\n Error: {0}\n Request: {1:D} ({2})\n Resource ID: 0x{3:x}\n Serial: {4}", x_error_text, RequestCode, RequestCode, ResourceID.ToInt32 (), Serial); + return error; + } + } + #endregion // XExceptionClass + + #region Internal Methods + // native X display handle + internal void SetDisplay (IntPtr display_handle) + { + if (display_handle != IntPtr.Zero) { + Hwnd hwnd; + + if ((GdkDisplayHandle != IntPtr.Zero) && (GdkFosterParent != IntPtr.Zero)) { + hwnd = Hwnd.ObjectFromHandle (gdk_x11_drawable_get_xid (GdkFosterParent)); + gdk_window_destroy (GdkFosterParent); + hwnd.Dispose (); + } + + if (GdkDisplayHandle != IntPtr.Zero) { + gdk_display_close (GdkDisplayHandle); + } + + DisplayHandle = display_handle; + GdkDisplayHandle = gdk_x11_lookup_xdisplay (display_handle); + + // We need to tell System.Drawing our DisplayHandle. FromHdcInternal has + // been hacked to do this for us. + Graphics.FromHdcInternal (DisplayHandle); + + // Debugging support + if (Environment.GetEnvironmentVariable ("MONO_XSYNC") != null) { + XSynchronize (DisplayHandle, true); + } + + if (Environment.GetEnvironmentVariable ("MONO_XEXCEPTIONS") != null) { + ErrorExceptions = true; + } + + // Generic X11 setup + GdkScreen = gdk_screen_get_default (); + // or gdk_x11_get_default_screen + ScreenNo = gdk_screen_get_number (GdkScreen); + GdkRootWindow = gdk_get_default_root_window (); + RootWindow = gdk_x11_drawable_get_xid (GdkRootWindow); + GdkDefaultColormap = gdk_colormap_get_system (); + DefaultColormap = gdk_x11_colormap_get_xcolormap (GdkDefaultColormap); + + VisualBestDepth = gdk_visual_get_best_depth (); + //Console.WriteLine (VisualBestDepth); + + // Create the foster parent + FosterParent = XCreateSimpleWindow (DisplayHandle, RootWindow, 0, 0, 1, 1, 4, 0, 0); + GdkFosterParent = gdk_window_foreign_new (FosterParent); + + if (GdkFosterParent == IntPtr.Zero) { + Console.WriteLine ("XplatUIX11GTK Constructor failed to create FosterParent"); + } + + hwnd = new Hwnd (); + + hwnd.WholeWindow = FosterParent; + hwnd.ClientWindow = FosterParent; + + // For sleeping on the X11 socket + listen = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); + IPEndPoint ep = new IPEndPoint (IPAddress.Loopback, 0); + listen.Bind (ep); + listen.Listen (1); + + // To wake up when a timer is ready + network_buffer = new byte [10]; + + wake = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); + wake.Connect (listen.LocalEndPoint); + wake_receive = listen.Accept (); + + #if __MonoCS__ + pollfds = new Pollfd [2]; + pollfds [0] = new Pollfd (); + pollfds [0].fd = XConnectionNumber (DisplayHandle); + pollfds [0].events = PollEvents.POLLIN; + + pollfds [1] = new Pollfd (); + pollfds [1].fd = wake_receive.Handle.ToInt32 (); + pollfds [1].events = PollEvents.POLLIN; + #endif + + Keyboard = new X11Keyboard (DisplayHandle); + Dnd = new X11Dnd (DisplayHandle); + + PostQuitState = false; + + DoubleClickInterval = 500; + + HoverState.Interval = 500; + HoverState.Timer = new Timer (); + HoverState.Timer.Enabled = false; + HoverState.Timer.Interval = HoverState.Interval; + HoverState.Timer.Tick += new EventHandler (MouseHover); + HoverState.X = -1; + HoverState.Y = -1; + + ActiveWindow = IntPtr.Zero; + FocusWindow = IntPtr.Zero; + ModalWindows = new Stack (3); + + MouseState = MouseButtons.None; + MousePosition = new Point (0, 0); + + Caret.Timer = new Timer (); + Caret.Timer.Interval = 500; // FIXME - where should this number come from? + Caret.Timer.Tick += new EventHandler (CaretCallback); + + SetupAtoms (); + + // Grab atom changes off the root window to catch certain WM events + gdk_window_set_events (GdkRootWindow, (int)GdkEventMask.GDK_PROPERTY_CHANGE_MASK); + + // Handle any upcoming errors + ErrorHandler = new XErrorHandler (HandleError); + XSetErrorHandler (ErrorHandler); + } else { + throw new ArgumentNullException ("Display", "Could not open display (X-Server required. Check your DISPLAY environment variable)"); + } + } + #endregion // Internal Methods + + #region Private Methods + private static void SetupAtoms () + { + NetAtoms = new int [(int)NA.LAST_NET_ATOM]; + + NetAtoms [(int)NA.WM_PROTOCOLS] = XInternAtom (DisplayHandle, "WM_PROTOCOLS", false); + NetAtoms [(int)NA.WM_DELETE_WINDOW] = XInternAtom (DisplayHandle, "WM_DELETE_WINDOW", false); + NetAtoms [(int)NA.WM_TAKE_FOCUS] = XInternAtom (DisplayHandle, "WM_TAKE_FOCUS", false); + + NetAtoms [(int)NA._NET_SUPPORTED] = XInternAtom (DisplayHandle, "_NET_SUPPORTED", false); + NetAtoms [(int)NA._NET_CLIENT_LIST] = XInternAtom (DisplayHandle, "_NET_CLIENT_LIST", false); + NetAtoms [(int)NA._NET_NUMBER_OF_DESKTOPS] = XInternAtom (DisplayHandle, "_NET_NUMBER_OF_DESKTOPS", false); + NetAtoms [(int)NA._NET_DESKTOP_GEOMETRY] = XInternAtom (DisplayHandle, "_NET_DESKTOP_GEOMETRY", false); + NetAtoms [(int)NA._NET_DESKTOP_VIEWPORT] = XInternAtom (DisplayHandle, "_NET_DESKTOP_VIEWPORT", false); + NetAtoms [(int)NA._NET_CURRENT_DESKTOP] = XInternAtom (DisplayHandle, "_NET_CURRENT_DESKTOP", false); + NetAtoms [(int)NA._NET_DESKTOP_NAMES] = XInternAtom (DisplayHandle, "_NET_DESKTOP_NAMES", false); + NetAtoms [(int)NA._NET_ACTIVE_WINDOW] = XInternAtom (DisplayHandle, "_NET_ACTIVE_WINDOW", false); + NetAtoms [(int)NA._NET_WORKAREA] = XInternAtom (DisplayHandle, "_NET_WORKAREA", false); + NetAtoms [(int)NA._NET_SUPPORTING_WM_CHECK] = XInternAtom (DisplayHandle, "_NET_SUPPORTING_WM_CHECK", false); + NetAtoms [(int)NA._NET_VIRTUAL_ROOTS] = XInternAtom (DisplayHandle, "_NET_VIRTUAL_ROOTS", false); + NetAtoms [(int)NA._NET_DESKTOP_LAYOUT] = XInternAtom (DisplayHandle, "_NET_DESKTOP_LAYOUT", false); + NetAtoms [(int)NA._NET_SHOWING_DESKTOP] = XInternAtom (DisplayHandle, "_NET_SHOWING_DESKTOP", false); + + NetAtoms [(int)NA._NET_CLOSE_WINDOW] = XInternAtom (DisplayHandle, "_NET_CLOSE_WINDOW", false); + NetAtoms [(int)NA._NET_MOVERESIZE_WINDOW] = XInternAtom (DisplayHandle, "_NET_MOVERESIZE_WINDOW", false); + NetAtoms [(int)NA._NET_WM_MOVERESIZE] = XInternAtom (DisplayHandle, "_NET_WM_MOVERESIZE", false); + NetAtoms [(int)NA._NET_RESTACK_WINDOW] = XInternAtom (DisplayHandle, "_NET_RESTACK_WINDOW", false); + NetAtoms [(int)NA._NET_REQUEST_FRAME_EXTENTS] = XInternAtom (DisplayHandle, "_NET_REQUEST_FRAME_EXTENTS", false); + + NetAtoms [(int)NA._NET_WM_NAME] = XInternAtom (DisplayHandle, "_NET_WM_NAME", false); + NetAtoms [(int)NA._NET_WM_VISIBLE_NAME] = XInternAtom (DisplayHandle, "_NET_WM_VISIBLE_NAME", false); + NetAtoms [(int)NA._NET_WM_ICON_NAME] = XInternAtom (DisplayHandle, "_NET_WM_ICON_NAME", false); + NetAtoms [(int)NA._NET_WM_VISIBLE_ICON_NAME] = XInternAtom (DisplayHandle, "_NET_WM_VISIBLE_ICON_NAME", false); + NetAtoms [(int)NA._NET_WM_DESKTOP] = XInternAtom (DisplayHandle, "_NET_WM_DESKTOP", false); + NetAtoms [(int)NA._NET_WM_WINDOW_TYPE] = XInternAtom (DisplayHandle, "_NET_WM_WINDOW_TYPE", false); + NetAtoms [(int)NA._NET_WM_STATE] = XInternAtom (DisplayHandle, "_NET_WM_STATE", false); + NetAtoms [(int)NA._NET_WM_ALLOWED_ACTIONS] = XInternAtom (DisplayHandle, "_NET_WM_ALLOWED_ACTIONS", false); + NetAtoms [(int)NA._NET_WM_STRUT] = XInternAtom (DisplayHandle, "_NET_WM_STRUT", false); + NetAtoms [(int)NA._NET_WM_STRUT_PARTIAL] = XInternAtom (DisplayHandle, "_NET_WM_STRUT_PARTIAL", false); + NetAtoms [(int)NA._NET_WM_ICON_GEOMETRY] = XInternAtom (DisplayHandle, "_NET_WM_ICON_GEOMETRY", false); + NetAtoms [(int)NA._NET_WM_ICON] = XInternAtom (DisplayHandle, "_NET_WM_ICON", false); + NetAtoms [(int)NA._NET_WM_PID] = XInternAtom (DisplayHandle, "_NET_WM_PID", false); + NetAtoms [(int)NA._NET_WM_HANDLED_ICONS] = XInternAtom (DisplayHandle, "_NET_WM_HANDLED_ICONS", false); + NetAtoms [(int)NA._NET_WM_USER_TIME] = XInternAtom (DisplayHandle, "_NET_WM_USER_TIME", false); + NetAtoms [(int)NA._NET_FRAME_EXTENTS] = XInternAtom (DisplayHandle, "_NET_FRAME_EXTENTS", false); + + NetAtoms [(int)NA._NET_WM_PING] = XInternAtom (DisplayHandle, "_NET_WM_PING", false); + NetAtoms [(int)NA._NET_WM_SYNC_REQUEST] = XInternAtom (DisplayHandle, "_NET_WM_SYNC_REQUEST", false); + + NetAtoms [(int)NA._NET_SYSTEM_TRAY_S] = XInternAtom (DisplayHandle, "_NET_SYSTEM_TRAY_S" + ScreenNo.ToString (), false); + NetAtoms [(int)NA._NET_SYSTEM_TRAY_OPCODE] = XInternAtom (DisplayHandle, "_NET_SYSTEM_TRAY_OPCODE", false); + NetAtoms [(int)NA._NET_SYSTEM_TRAY_ORIENTATION] = XInternAtom (DisplayHandle, "_NET_SYSTEM_TRAY_ORIENTATION", false); + + NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_HORZ] = XInternAtom (DisplayHandle, "_NET_WM_STATE_MAXIMIZED_HORZ", false); + NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_VERT] = XInternAtom (DisplayHandle, "_NET_WM_STATE_MAXIMIZED_VERT", false); + NetAtoms [(int)NA._NET_WM_STATE_HIDDEN] = XInternAtom (DisplayHandle, "_NET_WM_STATE_HIDDEN", false); + + NetAtoms [(int)NA._XEMBED] = XInternAtom (DisplayHandle, "_XEMBED", false); + NetAtoms [(int)NA._XEMBED_INFO] = XInternAtom (DisplayHandle, "_XEMBED_INFO", false); + + NetAtoms [(int)NA._MOTIF_WM_HINTS] = XInternAtom (DisplayHandle, "_MOTIF_WM_HINTS", false); + + NetAtoms [(int)NA._NET_WM_STATE_NO_TASKBAR] = XInternAtom (DisplayHandle, "_NET_WM_STATE_NO_TASKBAR", false); + NetAtoms [(int)NA._NET_WM_STATE_ABOVE] = XInternAtom (DisplayHandle, "_NET_WM_STATE_ABOVE", false); + NetAtoms [(int)NA._NET_WM_STATE_MODAL] = XInternAtom (DisplayHandle, "_NET_WM_STATE_MODAL", false); + NetAtoms [(int)NA._NET_WM_CONTEXT_HELP] = XInternAtom (DisplayHandle, "_NET_WM_CONTEXT_HELP", false); + NetAtoms [(int)NA._NET_WM_WINDOW_OPACITY] = XInternAtom (DisplayHandle, "_NET_WM_WINDOW_OPACITY", false); + + // Clipboard support + NetAtoms [(int)NA.CLIPBOARD] = XInternAtom (DisplayHandle, "CLIPBOARD", false); + NetAtoms [(int)NA.DIB] = (int)Atom.XA_PIXMAP; + NetAtoms [(int)NA.OEMTEXT] = XInternAtom (DisplayHandle, "COMPOUND_TEXT", false); + NetAtoms [(int)NA.UNICODETEXT] = XInternAtom (DisplayHandle, "UTF8_STRING", false); + NetAtoms [(int)NA.TARGETS] = XInternAtom (DisplayHandle, "TARGETS", false); + + // Special Atoms + AsyncAtom = XInternAtom (DisplayHandle, "_SWF_AsyncAtom", false); + PostAtom = XInternAtom (DisplayHandle, "_SWF_PostMessageAtom", false); + HoverState.Atom = XInternAtom (DisplayHandle, "_SWF_HoverAtom", false); + } + + private void GetSystrayManagerWindow () + { + gdk_x11_grab_server (); + SystrayMgrWindow = XGetSelectionOwner (DisplayHandle, NetAtoms [(int)NA._NET_SYSTEM_TRAY_S]); + gdk_x11_ungrab_server (); + gdk_display_flush (GdkDisplayHandle); + } + + private void SendNetWMMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2) + { + XEvent xev; + + xev = new XEvent (); + xev.ClientMessageEvent.type = XEventName.ClientMessage; + xev.ClientMessageEvent.send_event = true; + xev.ClientMessageEvent.window = window; + xev.ClientMessageEvent.message_type = message_type; + xev.ClientMessageEvent.format = 32; + xev.ClientMessageEvent.ptr1 = l0; + xev.ClientMessageEvent.ptr2 = l1; + xev.ClientMessageEvent.ptr3 = l2; + XSendEvent (DisplayHandle, RootWindow, false, EventMask.SubstructureRedirectMask | EventMask.SubstructureNotifyMask, ref xev); + } + + private void SendNetClientMessage (IntPtr window, IntPtr message_type, IntPtr l0, IntPtr l1, IntPtr l2) + { + XEvent xev; + + xev = new XEvent (); + xev.ClientMessageEvent.type = XEventName.ClientMessage; + xev.ClientMessageEvent.send_event = true; + xev.ClientMessageEvent.window = window; + xev.ClientMessageEvent.message_type = message_type; + xev.ClientMessageEvent.format = 32; + xev.ClientMessageEvent.ptr1 = l0; + xev.ClientMessageEvent.ptr2 = l1; + xev.ClientMessageEvent.ptr3 = l2; + XSendEvent (DisplayHandle, window, false, EventMask.NoEventMask, ref xev); + } + + private void DeriveStyles (IntPtr handle, int Style, int ExStyle, out FormBorderStyle border_style, out TitleStyle title_style, out int caption_height, out int tool_caption_height) + { + + // Only MDI windows get caption_heights + caption_height = 0; + tool_caption_height = 19; + + if ((Style & (int) WindowStyles.WS_CHILD) != 0) { + if ((Style & (int) WindowStyles.WS_BORDER) == 0) { + border_style = FormBorderStyle.None; + } else if ((ExStyle & (int) WindowStyles.WS_EX_CLIENTEDGE) != 0) { + border_style = FormBorderStyle.Fixed3D; + } else { + border_style = FormBorderStyle.FixedSingle; + } + title_style = TitleStyle.None; + } else { + bool is_mdi = false; + + if ((ExStyle & (int) WindowStyles.WS_EX_MDICHILD) != 0) { + caption_height = 26; + is_mdi = true; + } + + title_style = TitleStyle.None; + if ((Style & (int)WindowStyles.WS_CAPTION) != 0) { + if ((ExStyle & (int)WindowStyles.WS_EX_TOOLWINDOW) != 0) { + title_style = TitleStyle.Tool; + } else { + title_style = TitleStyle.Normal; + } + } + + if (!is_mdi) { + border_style = FormBorderStyle.None; + + if ((Style & (int)WindowStyles.WS_THICKFRAME) != 0) { + if ((ExStyle & (int)WindowStyles.WS_EX_TOOLWINDOW) != 0) { + border_style = FormBorderStyle.SizableToolWindow; + } else { + border_style = FormBorderStyle.Sizable; + } + } else { + if ((ExStyle & (int)WindowStyles.WS_EX_CLIENTEDGE) != 0) { + border_style = FormBorderStyle.Fixed3D; + } else if ((ExStyle & (int)WindowStyles.WS_EX_DLGMODALFRAME) != 0) { + border_style = FormBorderStyle.FixedDialog; + } else if ((ExStyle & (int)WindowStyles.WS_EX_TOOLWINDOW) != 0) { + border_style = FormBorderStyle.FixedToolWindow; + } else if ((Style & (int)WindowStyles.WS_BORDER) != 0) { + border_style = FormBorderStyle.Sizable; + } else { + border_style = FormBorderStyle.None; + } + } + } else { + if ((Style & (int) WindowStyles.WS_OVERLAPPEDWINDOW) != 0 || + (ExStyle & (int) WindowStyles.WS_EX_TOOLWINDOW) != 0) { + border_style = (FormBorderStyle) 0xFFFF; + } else { + border_style = FormBorderStyle.None; + } + } + } + } + + private void SetHwndStyles (Hwnd hwnd, CreateParams cp) + { + DeriveStyles (hwnd.Handle, cp.Style, cp.ExStyle, out hwnd.border_style, out hwnd.title_style, out hwnd.caption_height, out hwnd.tool_caption_height); + } + + private void SetWMStyles (Hwnd hwnd, CreateParams cp) + { + GdkWMDecoration decorations = GdkWMDecoration.GDK_DECOR_ALL; + + if ((cp.Style & (int)WindowStyles.WS_CAPTION) != 0) { + decorations |= GdkWMDecoration.GDK_DECOR_TITLE | GdkWMDecoration.GDK_DECOR_MENU; + } + + if ((cp.Style & ((int)WindowStyles.WS_THICKFRAME)) != 0) { + decorations |= GdkWMDecoration.GDK_DECOR_BORDER | GdkWMDecoration.GDK_DECOR_RESIZEH; + } + if ((cp.Style & ((int)WindowStyles.WS_MINIMIZEBOX)) != 0) { + decorations |= GdkWMDecoration.GDK_DECOR_MINIMIZE; + } + + if ((cp.Style & ((int)WindowStyles.WS_MAXIMIZEBOX)) != 0) { + decorations |= GdkWMDecoration.GDK_DECOR_MAXIMIZE; + } + + // is this needed ? most window managers do not even honour any MotifFunctions... +// if ((cp.Style & ((int)WindowStyles.WS_SYSMENU)) != 0) { +// functions |= MotifFunctions.Close; +// } + + if ((cp.ExStyle & ((int)WindowStyles.WS_EX_DLGMODALFRAME)) != 0) { + decorations |= GdkWMDecoration.GDK_DECOR_BORDER; + } + + if ((cp.Style & ((int)WindowStyles.WS_DLGFRAME)) != 0) { + decorations |= GdkWMDecoration.GDK_DECOR_BORDER; + } + + if ((cp.Style & ((int)WindowStyles.WS_BORDER)) != 0) { + decorations |= GdkWMDecoration.GDK_DECOR_BORDER; + } + + if ((cp.ExStyle & ((int)WindowStyles.WS_EX_TOOLWINDOW)) != 0) { + decorations = 0; + } + + gdk_window_set_decorations (gdk_window_foreign_new (hwnd.whole_window), (int)decorations); + } + + private void SetIcon (Hwnd hwnd, Icon icon) + { + Bitmap bitmap; + int size; + uint[] data; + int index; + + bitmap = icon.ToBitmap (); + index = 0; + size = bitmap.Width * bitmap.Height + 2; + data = new uint [size]; + + data [index++] = (uint)bitmap.Width; + data [index++] = (uint)bitmap.Height; + + for (int y = 0; y < bitmap.Height; y++) { + for (int x = 0; x < bitmap.Width; x++) { + data [index++] = (uint)bitmap.GetPixel (x, y).ToArgb (); + } + } + XChangeProperty (DisplayHandle, hwnd.whole_window, NetAtoms [(int)NA._NET_WM_ICON], Atom.XA_CARDINAL, 32, PropertyMode.Replace, data, size); + } + + private IntPtr ImageToPixmap (Image image) + { + return IntPtr.Zero; + } + + private void WakeupMain () + { + wake.Send (new byte [] { 0xFF }); + } + + private void TranslatePropertyToClipboard (int property) + { + Atom actual_atom; + int actual_format; + int nitems; + int bytes_after; + IntPtr prop = IntPtr.Zero; + + Clipboard.Item = null; + + XGetWindowProperty (DisplayHandle, FosterParent, property, 0, 0x7fffffff, true, Atom.AnyPropertyType, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + + if (nitems > 0) { + if (property == (int)Atom.XA_STRING) { + Clipboard.Item = Marshal.PtrToStringAnsi (prop); + } else if (property == (int)Atom.XA_BITMAP) { + // FIXME - convert bitmap to image + } else if (property == (int)Atom.XA_PIXMAP) { + // FIXME - convert pixmap to image + } else if (property == NetAtoms [(int)NA.OEMTEXT]) { + Clipboard.Item = Marshal.PtrToStringAnsi (prop); + } else if (property == NetAtoms [(int)NA.UNICODETEXT]) { + Clipboard.Item = Marshal.PtrToStringAnsi (prop); + } + + XFree (prop); + } + } + + private void AddExpose (XEvent xevent) + { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow (xevent.AnyEvent.window); + + // Don't waste time + if (hwnd == null) { + return; + } + + if (xevent.AnyEvent.window == hwnd.client_window) { + hwnd.AddInvalidArea (xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); + if (!hwnd.expose_pending) { + MessageQueue.Enqueue (xevent); + hwnd.expose_pending = true; + } + } else { + if (!hwnd.nc_expose_pending) { + MessageQueue.Enqueue (xevent); + hwnd.nc_expose_pending = true; + } + } + } + + private void InvalidateWholeWindow (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + InvalidateWholeWindow (handle, new Rectangle (0, 0, hwnd.Width, hwnd.Height)); + } + + private void InvalidateWholeWindow (IntPtr handle, Rectangle rectangle) + { + Hwnd hwnd; + XEvent xevent; + + hwnd = Hwnd.ObjectFromHandle (handle); + + + xevent = new XEvent (); + xevent.type = XEventName.Expose; + xevent.ExposeEvent.display = DisplayHandle; + xevent.ExposeEvent.window = hwnd.whole_window; + + xevent.ExposeEvent.x = rectangle.X; + xevent.ExposeEvent.y = rectangle.Y; + xevent.ExposeEvent.width = rectangle.Width; + xevent.ExposeEvent.height = rectangle.Height; + + AddExpose (xevent); + } + + private void WholeToScreen (IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + lock (XlibLock) { + gdk_window_get_origin (gdk_window_lookup (hwnd.whole_window), out dest_x_return, out dest_y_return); + } + + x = dest_x_return; + y = dest_y_return; + } + + private void AddConfigureNotify (XEvent xevent) + { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow (xevent.ConfigureEvent.window); + + // Don't waste time + if (hwnd == null) { + return; + } + + if (xevent.ConfigureEvent.window == hwnd.whole_window) { + if (!hwnd.reparented) { + hwnd.x = xevent.ConfigureEvent.x; + hwnd.y = xevent.ConfigureEvent.y; + } else { + int dummy_int; + + gdk_window_get_geometry (gdk_window_lookup (hwnd.whole_window), out hwnd.x, out hwnd.y, out dummy_int, out dummy_int, out dummy_int); + } + + hwnd.width = xevent.ConfigureEvent.width; + hwnd.height = xevent.ConfigureEvent.height; + + if (!hwnd.configure_pending) { + MessageQueue.Enqueue (xevent); + hwnd.configure_pending = true; + } + } + // We drop configure events for Client windows + } + + private void ShowCaret () + { + if ((Caret.gc == IntPtr.Zero) || Caret.On) { + return; + } + Caret.On = true; + + // gdk_gc_set_foreground + // gdk_draw_line + lock (XlibLock) { + XDrawLine (DisplayHandle, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + } + + private void HideCaret () + { + if ((Caret.gc == IntPtr.Zero) || !Caret.On) { + return; + } + Caret.On = false; + + // gdk_gc_set_foreground + // gdk_draw_text_wc + lock (XlibLock) { + XDrawLine (DisplayHandle, Caret.Window, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + } + + private int NextTimeout (DateTime now) + { + int timeout = Int32.MaxValue; + lock (TimerList) { + foreach (Timer timer in TimerList) { + int next = (int) (timer.Expires - now).TotalMilliseconds; + if (next < 0) { + return 0; // Have a timer that has already expired + } + + if (next < timeout) { + timeout = next; + } + } + } + if (timeout < Timer.Minimum) { + timeout = Timer.Minimum; + } + + return timeout; + } + + private void CheckTimers (DateTime now) + { + lock (TimerList) { + int count; + + count = TimerList.Count; + + if (count == 0) { + return; + } + + for (int i = 0; i < TimerList.Count; i++) { + Timer timer; + + timer = (Timer) TimerList [i]; + + if (timer.Enabled && timer.Expires <= now) { + timer.Update (now); + timer.FireTick (); + } + } + } + } + + private void UpdateMessageQueue () + { + DateTime now; + int pending; + + now = DateTime.Now; + + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + + if (pending == 0) { + if (Idle != null) { + Idle (this, EventArgs.Empty); + } + + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + } + + if (pending == 0) { + int timeout; + + timeout = NextTimeout (now); + if (timeout > 0) { + #if __MonoCS__ + Syscall.poll (pollfds, (uint) pollfds.Length, timeout); + // Clean out buffer, so we're not busy-looping on the same data + if (pollfds[1].revents != 0) { + wake_receive.Receive(network_buffer, 0, 1, SocketFlags.None); + } + #endif + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + } + } + + CheckTimers (now); + + if (pending == 0) { + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + } + + while (pending > 0) { + XEvent xevent = new XEvent (); + + lock (XlibLock) { + XNextEvent (DisplayHandle, ref xevent); + } +//Console.WriteLine("Got x event {0}", xevent); + switch (xevent.type) { + case XEventName.Expose: + AddExpose (xevent); + break; + + case XEventName.SelectionClear: { + // Should we do something? + break; + } + + case XEventName.SelectionRequest: { + if (Dnd.HandleSelectionRequestEvent (ref xevent)) + break; + XEvent sel_event; + + sel_event = new XEvent (); + sel_event.SelectionEvent.type = XEventName.SelectionNotify; + sel_event.SelectionEvent.send_event = true; + sel_event.SelectionEvent.display = DisplayHandle; + sel_event.SelectionEvent.selection = xevent.SelectionRequestEvent.selection; + sel_event.SelectionEvent.target = xevent.SelectionRequestEvent.target; + sel_event.SelectionEvent.requestor = xevent.SelectionRequestEvent.requestor; + sel_event.SelectionEvent.time = xevent.SelectionRequestEvent.time; + sel_event.SelectionEvent.property = 0; + + // Seems that some apps support asking for supported types + if (xevent.SelectionEvent.target == NetAtoms [(int)NA.TARGETS]) { + uint[] atoms; + int atom_count; + + atoms = new uint [5]; + atom_count = 0; + + if (Clipboard.Item is String) { + atoms [atom_count++] = (uint)Atom.XA_STRING; + atoms [atom_count++] = (uint)NetAtoms [(int)NA.OEMTEXT]; + atoms [atom_count++] = (uint)NetAtoms [(int)NA.UNICODETEXT]; + } else if (Clipboard.Item is Image) { + atoms [atom_count++] = (uint)Atom.XA_PIXMAP; + atoms [atom_count++] = (uint)Atom.XA_BITMAP; + } else { + // FIXME - handle other types + } + + XChangeProperty (DisplayHandle, xevent.SelectionEvent.requestor, xevent.SelectionRequestEvent.property, xevent.SelectionRequestEvent.target, 32, PropertyMode.Replace, atoms, atom_count); + } else if (Clipboard.Item is string) { + IntPtr buffer; + int buflen; + + buflen = 0; + + if (xevent.SelectionRequestEvent.target == (int)Atom.XA_STRING) { + Byte[] bytes; + + bytes = new ASCIIEncoding ().GetBytes ((string)Clipboard.Item); + buffer = Marshal.AllocHGlobal (bytes.Length); + buflen = bytes.Length; + + for (int i = 0; i < buflen; i++) { + Marshal.WriteByte (buffer, i, bytes [i]); + } + } else if (xevent.SelectionRequestEvent.target == NetAtoms [(int)NA.OEMTEXT]) { + // FIXME - this should encode into ISO2022 + buffer = Marshal.StringToHGlobalAnsi ((string)Clipboard.Item); + while (Marshal.ReadByte (buffer, buflen) != 0) { + buflen++; + } + } else if (xevent.SelectionRequestEvent.target == NetAtoms [(int)NA.UNICODETEXT]) { + buffer = Marshal.StringToHGlobalAnsi ((string)Clipboard.Item); + while (Marshal.ReadByte (buffer, buflen) != 0) { + buflen++; + } + } else { + buffer = IntPtr.Zero; + } + + if (buffer != IntPtr.Zero) { + XChangeProperty (DisplayHandle, xevent.SelectionRequestEvent.requestor, xevent.SelectionRequestEvent.property, xevent.SelectionRequestEvent.target, 8, PropertyMode.Replace, buffer, buflen); + sel_event.SelectionEvent.property = xevent.SelectionRequestEvent.property; + Marshal.FreeHGlobal (buffer); + } + } else if (Clipboard.Item is Image) { + if (xevent.SelectionEvent.target == (int)Atom.XA_PIXMAP) { + // FIXME - convert image and store as property + } else if (xevent.SelectionEvent.target == (int)Atom.XA_PIXMAP) { + // FIXME - convert image and store as property + } + } + + XSendEvent (DisplayHandle, xevent.SelectionRequestEvent.requestor, false, EventMask.NoEventMask, ref sel_event); + break; + } + + case XEventName.SelectionNotify: { + if (Clipboard.Enumerating) { + Clipboard.Enumerating = false; + if (xevent.SelectionEvent.property != 0) { + XDeleteProperty (DisplayHandle, FosterParent, xevent.SelectionEvent.property); + if (!Clipboard.Formats.Contains (xevent.SelectionEvent.property)) { + Clipboard.Formats.Add (xevent.SelectionEvent.property); + #if DriverDebugExtra + Console.WriteLine("Got supported clipboard atom format: {0}", xevent.SelectionEvent.property); + #endif + } + } + } else if (Clipboard.Retrieving) { + Clipboard.Retrieving = false; + if (xevent.SelectionEvent.property != 0) { + TranslatePropertyToClipboard (xevent.SelectionEvent.property); + } else { + Clipboard.Item = null; + } + } else { + Dnd.HandleSelectionNotifyEvent (ref xevent); + } + break; + } + + case XEventName.KeyPress: + case XEventName.KeyRelease: + case XEventName.ButtonPress: + case XEventName.ButtonRelease: + case XEventName.MotionNotify: + case XEventName.EnterNotify: + case XEventName.LeaveNotify: + case XEventName.CreateNotify: + case XEventName.DestroyNotify: + case XEventName.FocusIn: + case XEventName.FocusOut: + case XEventName.ClientMessage: + case XEventName.ReparentNotify: + MessageQueue.Enqueue (xevent); + break; + + case XEventName.ConfigureNotify: + AddConfigureNotify (xevent); + break; + + case XEventName.PropertyNotify: + if (xevent.PropertyEvent.atom == NetAtoms [(int)NA._NET_ACTIVE_WINDOW]) { + Atom actual_atom; + int actual_format; + int nitems; + int bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr prev_active;; + + prev_active = ActiveWindow; + XGetWindowProperty (DisplayHandle, RootWindow, NetAtoms [(int)NA._NET_ACTIVE_WINDOW], 0, 1, false, Atom.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if ((nitems > 0) && (prop != IntPtr.Zero)) { + ActiveWindow = Hwnd.GetHandleFromWindow ((IntPtr)Marshal.ReadInt32 (prop)); + XFree (prop); + + if (prev_active != ActiveWindow) { + if (prev_active != IntPtr.Zero) { + PostMessage (prev_active, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_INACTIVE, IntPtr.Zero); + } + if (ActiveWindow != IntPtr.Zero) { + PostMessage (ActiveWindow, Msg.WM_ACTIVATE, (IntPtr)WindowActiveFlags.WA_ACTIVE, IntPtr.Zero); + } + } + if (ModalWindows.Count == 0) { + break; + } else { + // Modality handling, if we are modal and the new active window is one + // of ours but not the modal one, switch back to the modal window + + if (NativeWindow.FromHandle (ActiveWindow) != null) { + if (ActiveWindow != (IntPtr)ModalWindows.Peek ()) { + Activate ((IntPtr)ModalWindows.Peek ()); + } + } + break; + } + } + } + break; + + } + + lock (XlibLock) { + pending = XPending (DisplayHandle); + } + } + } + + private IntPtr GetMousewParam (int Delta) + { + int result = 0; + + if ((MouseState & MouseButtons.Left) != 0) { + result |= (int)MsgButtons.MK_LBUTTON; + } + + if ((MouseState & MouseButtons.Middle) != 0) { + result |= (int)MsgButtons.MK_MBUTTON; + } + + if ((MouseState & MouseButtons.Right) != 0) { + result |= (int)MsgButtons.MK_RBUTTON; + } + + Keys mods = ModifierKeys; + if ((mods & Keys.Control) != 0) { + result |= (int)MsgButtons.MK_CONTROL; + } + + if ((mods & Keys.Shift) != 0) { + result |= (int)MsgButtons.MK_SHIFT; + } + + result |= Delta << 16; + + return (IntPtr)result; + } + private IntPtr XGetParent (IntPtr handle) + { + return gdk_x11_drawable_get_xid (gdk_window_get_parent (gdk_window_lookup (handle))); + } + + private int HandleError (IntPtr display, ref XErrorEvent error_event) + { + if (ErrorExceptions) { + throw new XException (error_event.display, error_event.resourceid, error_event.serial, error_event.error_code, error_event.request_code, error_event.minor_code); + } else { + Console.WriteLine ("X11 Error encountered: {0}{1}\n", XException.GetMessage (error_event.display, error_event.resourceid, error_event.serial, error_event.error_code, error_event.request_code, error_event.minor_code), Environment.StackTrace); + } + return 0; + } + + private void DestroyChildWindow (ShiftUI.Widget c) + { + Hwnd hwnd; + int i; + ShiftUI.Widget[] Widgets; + + if (c != null) { + Widgets = c.Widgets.GetAllWidgets (); + + for (i = 0; i < Widgets.Length; i++) { + if (Widgets [i].IsHandleCreated) { + hwnd = Hwnd.ObjectFromHandle (Widgets [i].Handle); + SendMessage (Widgets [i].Handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero); + hwnd.Dispose (); + } + DestroyChildWindow (Widgets [i]); + } + } + } + + #endregion // Private Methods + + #region Callbacks + private void MouseHover (object sender, EventArgs e) + { + if ((HoverState.X == MousePosition.X) && (HoverState.Y == MousePosition.Y)) { + XEvent xevent; + + HoverState.Timer.Enabled = false; + + if (HoverState.Window != IntPtr.Zero) { + xevent = new XEvent (); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = DisplayHandle; + xevent.ClientMessageEvent.window = (IntPtr)HoverState.Window; + xevent.ClientMessageEvent.message_type = (IntPtr)HoverState.Atom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = (IntPtr) (HoverState.Y << 16 | HoverState.X); + + MessageQueue.EnqueueLocked (xevent); + + WakeupMain (); + } + } + } + + private void CaretCallback (object sender, EventArgs e) + { + if (Caret.Paused) { + return; + } + Caret.On = !Caret.On; + + XDrawLine (DisplayHandle, Caret.Hwnd, Caret.gc, Caret.X, Caret.Y, Caret.X, Caret.Y + Caret.Height); + } + #endregion // Callbacks + + #region Public Properties + + internal int Caption { + get { + return 25; + } + } + + internal override Size CursorSize { + get { + uint x; + uint y; + gdk_display_get_maximal_cursor_size (GdkDisplayHandle, out x, out y); + + return new Size ((int)x, (int)y); + } + } + + internal override bool DragFullWindows { + get { + return true; + } + } + + internal override Size DragSize { + get { + return new Size (4, 4); + } + } + + internal override Size FrameBorderSize { + get { + throw new NotImplementedException (); + } + } + + internal override Size IconSize { + get { + IntPtr list; + XIconSize size; + int count; + + if (XGetIconSizes (DisplayHandle, RootWindow, out list, out count) != 0) { + long current; + int largest; + + current = (long)list; + largest = 0; + + size = new XIconSize (); + + for (int i = 0; i < count; i++) { + size = (XIconSize)Marshal.PtrToStructure ((IntPtr)current, size.GetType ()); + current += Marshal.SizeOf (size); + + // Look for our preferred size + if (size.min_width == 32) { + XFree (list); + return new Size (32, 32); + } + + if (size.max_width == 32) { + XFree (list); + return new Size (32, 32); + } + + if (size.min_width < 32 && size.max_width > 32) { + int x; + + // check if we can fit one + x = size.min_width; + while (x < size.max_width) { + x += size.width_inc; + if (x == 32) { + XFree (list); + return new Size (32, 32); + } + } + } + + if (largest < size.max_width) { + largest = size.max_width; + } + } + + // We didn't find a match or we wouldn't be here + return new Size (largest, largest); + + } else { + return new Size (32, 32); + } + } + } + + internal override int KeyboardSpeed { + get { + // + // A lot harder: need to do: + // XkbQueryExtension(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 1 + // XkbAllocKeyboard(0x08051008, 0xbfffdf4c, 0xbfffdf50, 0xbfffdf54, 0xbfffdf58) = 0x080517a8 + // XkbGetWidgets(0x08051008, 1, 0x080517a8, 0xbfffdf54, 0xbfffdf58) = 0 + // + // And from that we can tell the repetition rate + // + // Notice, the values must map to: + // [0, 31] which maps to 2.5 to 30 repetitions per second. + // + return 0; + } + } + + internal override int KeyboardDelay { + get { + // + // Return values must range from 0 to 4, 0 meaning 250ms, + // and 4 meaning 1000 ms. + // + return 1; // ie, 500 ms + } + } + + internal override Size MaxWindowTrackSize { + get { + return new Size (WorkingArea.Width, WorkingArea.Height); + } + } + + internal override Size MinimizedWindowSize { + get { + return new Size (1, 1); + } + } + + internal override Size MinimizedWindowSpacingSize { + get { + return new Size (1, 1); + } + } + + internal override Size MinimumWindowSize { + get { + return new Size (1, 1); + } + } + + internal override Size MinWindowTrackSize { + get { + return new Size (1, 1); + } + } + + internal override Keys ModifierKeys { + get { + return Keyboard.ModifierKeys; + } + } + + internal override Size SmallIconSize { + get { + IntPtr list; + XIconSize size; + int count; + + if (XGetIconSizes (DisplayHandle, RootWindow, out list, out count) != 0) { + long current; + int smallest; + + current = (long)list; + smallest = 0; + + size = new XIconSize (); + + for (int i = 0; i < count; i++) { + size = (XIconSize)Marshal.PtrToStructure ((IntPtr)current, size.GetType ()); + current += Marshal.SizeOf (size); + + // Look for our preferred size + if (size.min_width == 16) { + XFree (list); + return new Size (16, 16); + } + + if (size.max_width == 16) { + XFree (list); + return new Size (16, 16); + } + + if (size.min_width < 16 && size.max_width > 16) { + int x; + + // check if we can fit one + x = size.min_width; + while (x < size.max_width) { + x += size.width_inc; + if (x == 16) { + XFree (list); + return new Size (16, 16); + } + } + } + + if (smallest == 0 || smallest > size.min_width) { + smallest = size.min_width; + } + } + + // We didn't find a match or we wouldn't be here + return new Size (smallest, smallest); + + } else { + return new Size (16, 16); + } + } + } + + internal override int MouseButtonCount { + get { + return 3; + } + } + + internal override bool MouseButtonsSwapped { + get { + return false; // FIXME - how to detect? + } + } + + internal override bool MouseWheelPresent { + get { + return true; // FIXME - how to detect? + } + } + + internal override Rectangle VirtualScreen { + get { + return WorkingArea; + } + } + + internal override Rectangle WorkingArea { + get { + Atom actual_atom; + int actual_format; + int nitems; + int bytes_after; + IntPtr prop = IntPtr.Zero; + int width; + int height; + + XGetWindowProperty (DisplayHandle, RootWindow, NetAtoms [(int)NA._NET_DESKTOP_GEOMETRY], 0, 256, false, Atom.XA_CARDINAL, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if ((nitems == 2) && (prop != IntPtr.Zero)) { + width = Marshal.ReadInt32 (prop, 0); + height = Marshal.ReadInt32 (prop, 4); + + XFree (prop); + return new Rectangle (0, 0, width, height); + } else { + XWindowAttributes attributes=new XWindowAttributes (); + + lock (XlibLock) { + XGetWindowAttributes (DisplayHandle, XRootWindow (DisplayHandle, ScreenNo), ref attributes); + } + + return new Rectangle (0, 0, attributes.width, attributes.height); + } + } + } + #endregion // Public properties + + #region Public Static Methods + internal override IntPtr InitializeDriver () + { + lock (this) { + if (GdkDisplayHandle == IntPtr.Zero) { + SetDisplay (gdk_x11_display_get_xdisplay (gdk_display_get_default ())); + } + } + return IntPtr.Zero; + } + + internal override void ShutdownDriver (IntPtr token) + { + lock (this) { + if (GdkDisplayHandle != IntPtr.Zero) { + gdk_display_close (GdkDisplayHandle); + DisplayHandle = IntPtr.Zero; + GdkDisplayHandle = IntPtr.Zero; + } + } + } + + internal override void EnableThemes () + { + ThemesEnabled = true; + } + + + internal override void Activate (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd != null) lock (XlibLock) { + SendNetWMMessage (hwnd.whole_window, (IntPtr)NetAtoms [(int)NA._NET_ACTIVE_WINDOW], IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); + //XRaiseWindow(DisplayHandle, handle); + } + return; + } + + internal override void AudibleAlert (AlertType atype) + { + gdk_display_beep (gdk_x11_lookup_xdisplay (DisplayHandle)); + return; + } + + + internal override void CaretVisible (IntPtr handle, bool visible) + { + // Visible is cumulative; two hides require two shows before the caret is visible again + if (Caret.Hwnd == handle) { + if (visible) { + if (Caret.Visible < 1) { + Caret.Visible++; + Caret.On = false; + if (Caret.Visible == 1) { + ShowCaret (); + Caret.Timer.Start (); + } + } + } else { + Caret.Visible--; + if (Caret.Visible == 0) { + Caret.Timer.Stop (); + HideCaret (); + } + } + } + } + + internal bool CalculateWindowRect (IntPtr handle, ref Rectangle ClientRect, int Style, int ExStyle, Menu menu, out Rectangle WindowRect) + { + FormBorderStyle border_style; + TitleStyle title_style; + int caption_height; + int tool_caption_height; + + DeriveStyles (handle, Style, ExStyle, out border_style, out title_style, + out caption_height, out tool_caption_height); + + WindowRect = Hwnd.GetWindowRectangle (border_style, menu, title_style, + caption_height, tool_caption_height, + ClientRect); + + return true; + } + + internal override void ClientToScreen (IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + lock (XlibLock) { + XTranslateCoordinates (DisplayHandle, hwnd.client_window, RootWindow, x, y, out dest_x_return, out dest_y_return, out child); + } + + x = dest_x_return; + y = dest_y_return; + } + + + internal override void CreateCaret (IntPtr handle, int width, int height) + { + XGCValues gc_values; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (Caret.Hwnd != IntPtr.Zero) { + DestroyCaret (Caret.Hwnd); + } + + Caret.Hwnd = handle; + Caret.Window = hwnd.client_window; + Caret.Width = width; + Caret.Height = height; + Caret.Visible = 0; + Caret.On = false; + + gc_values = new XGCValues (); + gc_values.line_width = width; + + Caret.gc = XCreateGC (DisplayHandle, Caret.Window, GCFunction.GCLineWidth, ref gc_values); + if (Caret.gc == IntPtr.Zero) { + Caret.Hwnd = IntPtr.Zero; + return; + } + + XSetFunction (DisplayHandle, Caret.gc, GXFunction.GXinvert); + } + + internal override IntPtr CreateWindow (CreateParams cp) + { + GdkWindowAttr gdk_window_attributes; + GdkWindowAttributesType attributes_mask = 0; + Hwnd hwnd; + int X; + int Y; + int Width; + int Height; + IntPtr GdkParentHandle; + IntPtr GdkWholeWindow; + IntPtr GdkClientWindow; + Rectangle ClientRect; + GdkWindowType gdk_window_type; + + + hwnd = new Hwnd (); + + gdk_window_attributes = new GdkWindowAttr (); + + X = cp.X; + Y = cp.Y; + Width = cp.Width; + Height = cp.Height; + + if (Width < 1) Width = 1; + if (Height < 1) Height = 1; + + gdk_window_type = GdkWindowType.GDK_WINDOW_CHILD; + + if (cp.Parent != IntPtr.Zero) { + GdkParentHandle = gdk_window_lookup (Hwnd.ObjectFromHandle (cp.Parent).client_window); + } else { + if ((cp.Style & (int)WindowStyles.WS_CHILD) != 0) { + // We need to use our foster parent window until this poor child gets it's parent assigned + GdkParentHandle = GdkFosterParent; + } else if ((cp.Style & (int)WindowStyles.WS_POPUP) != 0) { + GdkParentHandle = GdkRootWindow; + } else { + // Default position on screen, if window manager doesn't place us somewhere else + if (X < 1) X = 50; + if (Y < 1) Y = 50; + GdkParentHandle = GdkRootWindow; + } + } + +// ValueMask = SetWindowValuemask.BitGravity | SetWindowValuemask.WinGravity; + +// Attributes.bit_gravity = Gravity.NorthWestGravity; +// Attributes.win_gravity = Gravity.NorthWestGravity; + + // FIXME: does gdk need that ? + // Save what's under the toolwindow + if ((cp.ExStyle & (int)WindowStyles.WS_EX_TOOLWINDOW) != 0) { +// Attributes.save_under = true; +// ValueMask |= SetWindowValuemask.SaveUnder; + } + + // If we're a popup without caption we override the WM + if ((cp.Style & ((int)WindowStyles.WS_POPUP)) != 0) { + if ((cp.Style & (int)WindowStyles.WS_CAPTION) == 0) { + gdk_window_attributes.override_redirect = true; + attributes_mask |= GdkWindowAttributesType.GDK_WA_NOREDIR; + } + } + + hwnd.x = X; + hwnd.y = Y; + hwnd.width = Width; + hwnd.height = Height; + hwnd.parent = Hwnd.ObjectFromHandle (cp.Parent); + + if ((cp.Style & ((int)WindowStyles.WS_DISABLED)) != 0) { + hwnd.enabled = false; + } + + ClientRect = hwnd.ClientRect; + GdkClientWindow = IntPtr.Zero; + + gdk_window_attributes.x = X; + gdk_window_attributes.y = Y; + gdk_window_attributes.width = Width; + gdk_window_attributes.height = Height; + gdk_window_attributes.window_type = gdk_window_type; + + attributes_mask |= GdkWindowAttributesType.GDK_WA_X | GdkWindowAttributesType.GDK_WA_Y; + + gdk_window_attributes.wclass = GdkWindowClass.GDK_INPUT_OUTPUT; + + lock (XlibLock) { + GdkWholeWindow = gdk_window_new (GdkParentHandle, ref gdk_window_attributes, (int)attributes_mask); + + if (GdkWholeWindow != IntPtr.Zero) { + attributes_mask &= ~GdkWindowAttributesType.GDK_WA_NOREDIR; + + if (GdkCustomVisual != IntPtr.Zero && GdkCustomColormap != IntPtr.Zero) { + attributes_mask |= GdkWindowAttributesType.GDK_WA_COLORMAP | GdkWindowAttributesType.GDK_WA_VISUAL; + gdk_window_attributes.colormap = GdkCustomColormap; + gdk_window_attributes.visual = GdkCustomVisual; + } + + gdk_window_attributes.x = ClientRect.X; + gdk_window_attributes.y = ClientRect.Y; + gdk_window_attributes.width = ClientRect.Width; + gdk_window_attributes.height = ClientRect.Height; + + GdkClientWindow = gdk_window_new (GdkWholeWindow, ref gdk_window_attributes, (int)attributes_mask); + } + } + + if ((GdkWholeWindow == IntPtr.Zero) || (GdkClientWindow == IntPtr.Zero)) { + throw new Exception ("Could not create X11 Gdk windows"); + } + + hwnd.WholeWindow = gdk_x11_drawable_get_xid (GdkWholeWindow); + hwnd.ClientWindow = gdk_x11_drawable_get_xid (GdkClientWindow); + + #if DriverDebug + Console.WriteLine("Created window {0:X} / {1:X} parent {2:X}", ClientWindow.ToInt32(), WholeWindow.ToInt32(), hwnd.parent != null ? hwnd.parent.Handle.ToInt32() : 0); + #endif + + lock (XlibLock) { + gdk_window_set_events (GdkWholeWindow, (int)GdkSelectInputMask); + gdk_window_set_events (GdkClientWindow, (int)GdkSelectInputMask); + + if ((cp.Style & (int)WindowStyles.WS_VISIBLE) != 0) { + gdk_window_show (GdkWholeWindow); + gdk_window_show (GdkClientWindow); + hwnd.visible = true; + } + } + + SetWMStyles (hwnd, cp); + + if ((cp.Style & (int)WindowStyles.WS_MINIMIZE) != 0) { + SetWindowState (hwnd.Handle, FormWindowState.Minimized); + } else if ((cp.Style & (int)WindowStyles.WS_MAXIMIZE) != 0) { + SetWindowState (hwnd.Handle, FormWindowState.Maximized); + } + + // for now make all windows dnd enabled + Dnd.SetAllowDrop (hwnd, true); + + // Set caption/window title + Text (hwnd.Handle, cp.Caption); + + return hwnd.Handle; + } + + internal override IntPtr CreateWindow (IntPtr Parent, int X, int Y, int Width, int Height) + { + CreateParams create_params = new CreateParams (); + + create_params.Caption = ""; + create_params.X = X; + create_params.Y = Y; + create_params.Width = Width; + create_params.Height = Height; + + create_params.ClassName = XplatUI.DefaultClassName; + create_params.ClassStyle = 0; + create_params.ExStyle = 0; + create_params.Parent = IntPtr.Zero; + create_params.Param = 0; + + return CreateWindow (create_params); + } + + internal override IntPtr DefineCursor (Bitmap bitmap, Bitmap mask, Color cursor_pixel, Color mask_pixel, int xHotSpot, int yHotSpot) + { + IntPtr cursor; + Bitmap cursor_bitmap; + Bitmap cursor_mask; + Byte[] cursor_bits; + Byte[] mask_bits; + Color c_pixel; + Color m_pixel; + int width; + int height; + IntPtr cursor_pixmap; + IntPtr mask_pixmap; + XColor fg; + XColor bg; + bool and; + bool xor; + + Size cursor_size = CursorSize; + width = cursor_size.Width; + height = cursor_size.Height; + + // Win32 only allows creation cursors of a certain size + if ((bitmap.Width != width) || (bitmap.Width != height)) { + cursor_bitmap = new Bitmap (bitmap, new Size (width, height)); + cursor_mask = new Bitmap (mask, new Size (width, height)); + } else { + cursor_bitmap = bitmap; + cursor_mask = mask; + } + + width = cursor_bitmap.Width; + height = cursor_bitmap.Height; + + cursor_bits = new Byte [(width / 8) * height]; + mask_bits = new Byte [(width / 8) * height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + c_pixel = cursor_bitmap.GetPixel (x, y); + m_pixel = cursor_mask.GetPixel (x, y); + + and = c_pixel == cursor_pixel; + xor = m_pixel == mask_pixel; + + if (!and && !xor) { + // Black + // cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); // The bit already is 0 + mask_bits [y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); + } else if (and && !xor) { + // White + cursor_bits [y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); + mask_bits [y * width / 8 + x / 8] |= (byte)(1 << (x % 8)); + #if notneeded + } else if (and && !xor) { + // Screen + } else if (and && xor) { + // Inverse Screen + + // X11 doesn't know the 'reverse screen' concept, so we'll treat them the same + // we want both to be 0 so nothing to be done + //cursor_bits[y * width / 8 + x / 8] &= (byte)~((1 << (x % 8))); + //mask_bits[y * width / 8 + x / 8] |= (byte)(01 << (x % 8)); + #endif + } + } + } + + cursor_pixmap = XCreatePixmapFromBitmapData (DisplayHandle, RootWindow, cursor_bits, width, height, (IntPtr)1, (IntPtr)0, 1); + mask_pixmap = XCreatePixmapFromBitmapData (DisplayHandle, RootWindow, mask_bits, width, height, (IntPtr)1, (IntPtr)0, 1); + fg = new XColor (); + bg = new XColor (); + + fg.pixel = XWhitePixel (DisplayHandle, ScreenNo); + fg.red = (ushort)65535; + fg.green = (ushort)65535; + fg.blue = (ushort)65535; + + bg.pixel = XBlackPixel (DisplayHandle, ScreenNo); + + cursor = XCreatePixmapCursor (DisplayHandle, cursor_pixmap, mask_pixmap, ref fg, ref bg, xHotSpot, yHotSpot); + + XFreePixmap (DisplayHandle, cursor_pixmap); + XFreePixmap (DisplayHandle, mask_pixmap); + + return cursor; + } + + internal override IntPtr DefineStdCursor (StdCursor id) + { + CursorFontShape shape; + IntPtr cursor; + + // FIXME - define missing shapes + + switch (id) { + case StdCursor.AppStarting: { + shape = CursorFontShape.XC_watch; + break; + } + + case StdCursor.Arrow: { + return IntPtr.Zero; + } + + case StdCursor.Cross: { + shape = CursorFontShape.XC_crosshair; + break; + } + + case StdCursor.Default: { + return IntPtr.Zero; + } + + case StdCursor.Hand: { + shape = CursorFontShape.XC_hand1; + break; + } + + case StdCursor.Help: { + shape = CursorFontShape.XC_question_arrow; + break; + } + + case StdCursor.HSplit: { + shape = CursorFontShape.XC_sb_v_double_arrow; + break; + } + + case StdCursor.IBeam: { + shape = CursorFontShape.XC_xterm; + break; + } + + case StdCursor.No: { + shape = CursorFontShape.XC_circle; + break; + } + + case StdCursor.NoMove2D: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.NoMoveHoriz: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.NoMoveVert: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanEast: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanNE: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanNorth: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanNW: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanSE: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanSouth: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanSW: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.PanWest: { + shape = CursorFontShape.XC_sizing; + break; + } + + case StdCursor.SizeAll: { + shape = CursorFontShape.XC_fleur; + break; + } + + case StdCursor.SizeNESW: { + shape = CursorFontShape.XC_top_right_corner; + break; + } + + case StdCursor.SizeNS: { + shape = CursorFontShape.XC_sb_v_double_arrow; + break; + } + + case StdCursor.SizeNWSE: { + shape = CursorFontShape.XC_top_left_corner; + break; + } + + case StdCursor.SizeWE: { + shape = CursorFontShape.XC_sb_h_double_arrow; + break; + } + + case StdCursor.UpArrow: { + shape = CursorFontShape.XC_center_ptr; + break; + } + + case StdCursor.VSplit: { + shape = CursorFontShape.XC_sb_h_double_arrow; + break; + } + + case StdCursor.WaitCursor: { + shape = CursorFontShape.XC_watch; + break; + } + + default: { + return IntPtr.Zero; + } + } + + lock (XlibLock) { + cursor = XCreateFontCursor (DisplayHandle, shape); + } + return cursor; + } + + internal override IntPtr DefWndProc (ref Message msg) + { + return IntPtr.Zero; + } + + internal override void DestroyCaret (IntPtr handle) + { + if (Caret.Hwnd == handle) { + if (Caret.Visible == 1) { + Caret.Timer.Stop (); + HideCaret (); + } + if (Caret.gc != IntPtr.Zero) { + XFreeGC (DisplayHandle, Caret.gc); + Caret.gc = IntPtr.Zero; + } + Caret.Hwnd = IntPtr.Zero; + Caret.Visible = 0; + Caret.On = false; + } + } + + internal override void DestroyCursor (IntPtr cursor) + { + lock (XlibLock) { + XFreeCursor (DisplayHandle, cursor); + } + } + + internal override void DestroyWindow (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) { + #if DriverDebug + Console.WriteLine("window {0:X} already destroyed", handle.ToInt32()); + #endif + return; + } + + #if DriverDebug + Console.WriteLine("Destroying window {0:X}", handle.ToInt32()); + #endif + + // Make sure if the caret is in the window, that we destroy the caret, too + if (Caret.Hwnd == hwnd.client_window) { + DestroyCaret (handle); + } + + // Mark our children as gone as well + DestroyChildWindow (ShiftUI.Widget.ShiftUI.WidgetNativeWindow.ShiftUI.WidgetFromHandle (handle)); + + // Send destroy message + SendMessage (handle, Msg.WM_DESTROY, IntPtr.Zero, IntPtr.Zero); + + lock (XlibLock) { + if (hwnd.client_window != IntPtr.Zero) { + gdk_window_destroy (gdk_window_lookup (hwnd.client_window)); + } + + if ((hwnd.whole_window != IntPtr.Zero) && (hwnd.whole_window != hwnd.client_window)) { + gdk_window_destroy (gdk_window_lookup (hwnd.whole_window)); + } + } + hwnd.Dispose (); + } + + internal override IntPtr DispatchMessage (ref MSG msg) + { + return NativeWindow.WndProc (msg.hwnd, msg.message, msg.wParam, msg.lParam); + } + + internal override void DrawReversibleRectangle (IntPtr handle, Rectangle rect, int line_width) + { + Hwnd hwnd; + XGCValues gc_values; + IntPtr gc; + + hwnd = Hwnd.ObjectFromHandle (handle); + + gc_values = new XGCValues (); + + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + gc_values.line_width = line_width; + gc_values.foreground = XBlackPixel (DisplayHandle, ScreenNo); + + // This logic will give us true rubber bands: (libsx, SANE_XOR) + //mask = foreground ^ background; + //XSetForeground(DisplayHandle, gc, 0xffffffff); + //XSetBackground(DisplayHandle, gc, background); + //XSetFunction(DisplayHandle, gc, GXxor); + //XSetPlaneMask(DisplayHandle, gc, mask); + + + gc = XCreateGC (DisplayHandle, hwnd.client_window, GCFunction.GCSubwindowMode | GCFunction.GCLineWidth | GCFunction.GCForeground, ref gc_values); + uint foreground; + uint background; + + ShiftUI.Widget Widget; + Widget = ShiftUI.Widget.FromHandle (handle); + + XColor xcolor = new XColor (); + + xcolor.red = (ushort)(ShiftUI.Widget.ForeColor.R * 257); + xcolor.green = (ushort)(ShiftUI.Widget.ForeColor.G * 257); + xcolor.blue = (ushort)(ShiftUI.Widget.ForeColor.B * 257); + XAllocColor (DisplayHandle, DefaultColormap, ref xcolor); + foreground = (uint)xcolor.pixel.ToInt32 (); + + xcolor.red = (ushort)(ShiftUI.Widget.BackColor.R * 257); + xcolor.green = (ushort)(ShiftUI.Widget.BackColor.G * 257); + xcolor.blue = (ushort)(ShiftUI.Widget.BackColor.B * 257); + XAllocColor (DisplayHandle, DefaultColormap, ref xcolor); + background = (uint)xcolor.pixel.ToInt32 (); + + uint mask = foreground ^ background; + + XSetForeground (DisplayHandle, gc, 0xffffffff); + XSetBackground (DisplayHandle, gc, background); + XSetFunction (DisplayHandle, gc, GXFunction.GXxor); + XSetPlaneMask (DisplayHandle, gc, mask); + + if ((rect.Width > 0) && (rect.Height > 0)) { + XDrawRectangle (DisplayHandle, hwnd.client_window, gc, rect.Left, rect.Top, rect.Width, rect.Height); + } else { + if (rect.Width > 0) { + XDrawLine (DisplayHandle, hwnd.client_window, gc, rect.X, rect.Y, rect.Right, rect.Y); + } else { + XDrawLine (DisplayHandle, hwnd.client_window, gc, rect.X, rect.Y, rect.X, rect.Bottom); + } + } + XFreeGC (DisplayHandle, gc); + } + + internal override void DoEvents () + { + MSG msg = new MSG (); + + if (OverrideCursorHandle != IntPtr.Zero) { + OverrideCursorHandle = IntPtr.Zero; + } + + while (PeekMessage (ref msg, IntPtr.Zero, 0, 0, (uint)PeekMessageFlags.PM_REMOVE)) { + TranslateMessage (ref msg); + DispatchMessage (ref msg); + } + } + + internal override void EnableWindow (IntPtr handle, bool Enable) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + if (hwnd != null) { + hwnd.Enabled = Enable; + } + } + + internal override void EndLoop (Thread thread) + { + // This is where we one day will shut down the loop for the thread + } + + + internal override IntPtr GetActive () + { + Atom actual_atom; + int actual_format; + int nitems; + int bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr active = IntPtr.Zero; + + XGetWindowProperty (DisplayHandle, RootWindow, NetAtoms [(int)NA._NET_ACTIVE_WINDOW], 0, 1, false, Atom.XA_WINDOW, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if ((nitems > 0) && (prop != IntPtr.Zero)) { + active = (IntPtr)Marshal.ReadInt32 (prop); + XFree (prop); + } + + if (active != IntPtr.Zero) { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow (active); + if (hwnd != null) { + active = hwnd.Handle; + } else { + active = IntPtr.Zero; + } + } + return active; + } + + internal override void GetCursorInfo (IntPtr cursor, out int width, out int height, out int hotspot_x, out int hotspot_y) + { + throw new NotImplementedException (); + } + + internal override void GetDisplaySize (out Size size) + { + XWindowAttributes attributes=new XWindowAttributes (); + + lock (XlibLock) { + // FIXME - use _NET_WM messages instead? + XGetWindowAttributes (DisplayHandle, XRootWindow (DisplayHandle, ScreenNo), ref attributes); + } + + size = new Size (attributes.width, attributes.height); + } + + internal override SizeF GetAutoScaleSize (Font font) + { + Graphics g; + float width; + string magic_string = "The quick brown fox jumped over the lazy dog."; + double magic_number = 44.549996948242189; + + g = Graphics.FromHwnd (FosterParent); + + width = (float) (g.MeasureString (magic_string, font).Width / magic_number); + return new SizeF (width, font.Height); + } + + internal override IntPtr GetParent (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + if (hwnd != null && hwnd.parent != null) { + return hwnd.parent.Handle; + } + return IntPtr.Zero; + } + + internal override void GetCursorPos (IntPtr handle, out int x, out int y) + { + IntPtr use_handle; + IntPtr root; + IntPtr child; + int root_x; + int root_y; + int win_x; + int win_y; + int keys_buttons; + + if (handle != IntPtr.Zero) { + use_handle = Hwnd.ObjectFromHandle (handle).client_window; + } else { + use_handle = RootWindow; + } + + lock (XlibLock) { + XQueryPointer (DisplayHandle, use_handle, out root, out child, out root_x, out root_y, out win_x, out win_y, out keys_buttons); + } + + if (handle != IntPtr.Zero) { + x = win_x; + y = win_y; + } else { + x = root_x; + y = root_y; + } + } + + internal override bool GetFontMetrics (Graphics g, Font font, out int ascent, out int descent) + { + return GetFontMetrics (g.GetHdc (), font.ToHfont (), out ascent, out descent); + } + + internal override Point GetMenuOrigin (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd != null) { + return hwnd.MenuOrigin; + } + return Point.Empty; + } + + internal bool GetMessage (ref MSG msg, IntPtr handle, int wFilterMin, int wFilterMax) + { + XEvent xevent; + bool client; + Hwnd hwnd; + + ProcessNextMessage: + + if (MessageQueue.Count > 0) { + xevent = (XEvent) MessageQueue.Dequeue (); + } else { + UpdateMessageQueue (); + + if (MessageQueue.Count > 0) { + xevent = (XEvent) MessageQueue.Dequeue (); + } else { + if (!PostQuitState) { + msg.hwnd = IntPtr.Zero; + msg.message = Msg.WM_ENTERIDLE; + return true; + } + + // We reset ourselves so GetMessage can be called again + PostQuitState = false; + + return false; + } + } + + // FIXME - handle filtering + + hwnd = Hwnd.GetObjectFromWindow (xevent.AnyEvent.window); + + // Handle messages for windows that are already or are about to be destroyed + if (hwnd == null) { + #if DriverDebug + Console.WriteLine("GetMessage(): Got message {0} for non-existent or already destroyed window {1:X}", xevent.type, xevent.AnyEvent.window.ToInt32()); + #endif + goto ProcessNextMessage; + } + + if (hwnd.client_window == xevent.AnyEvent.window) { + client = true; + //Console.WriteLine("Client message, sending to window {0:X}", msg.hwnd.ToInt32()); + } else { + client = false; + //Console.WriteLine("Non-Client message, sending to window {0:X}", msg.hwnd.ToInt32()); + } + + msg.hwnd = hwnd.Handle; + + // + // If you add a new event to this switch make sure to add it in + // UpdateMessage also unless it is not coming through the X event system. + // + switch (xevent.type) { + case XEventName.KeyPress: { + Keyboard.KeyEvent (FocusWindow, xevent, ref msg); + break; + } + + case XEventName.KeyRelease: { + Keyboard.KeyEvent (FocusWindow, xevent, ref msg); + break; + } + + case XEventName.ButtonPress: { + switch (xevent.ButtonEvent.button) { + case 1: { + MouseState |= MouseButtons.Left; + if (client) { + msg.message = Msg.WM_LBUTTONDOWN; + } else { + msg.message = Msg.WM_NCLBUTTONDOWN; + WholeToScreen (msg.hwnd, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + // TODO: For WM_NCLBUTTONDOWN wParam specifies a hit-test value not the virtual keys down + msg.wParam = GetMousewParam (0); + break; + } + + case 2: { + MouseState |= MouseButtons.Middle; + if (client) { + msg.message = Msg.WM_MBUTTONDOWN; + } else { + msg.message = Msg.WM_NCMBUTTONDOWN; + WholeToScreen (msg.hwnd, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + msg.wParam = GetMousewParam (0); + break; + } + + case 3: { + MouseState |= MouseButtons.Right; + if (client) { + msg.message = Msg.WM_RBUTTONDOWN; + } else { + msg.message = Msg.WM_NCRBUTTONDOWN; + WholeToScreen (msg.hwnd, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + msg.wParam = GetMousewParam (0); + break; + } + + case 4: { + msg.message = Msg.WM_MOUSEWHEEL; + msg.wParam = GetMousewParam (120); + break; + } + + case 5: { + msg.message = Msg.WM_MOUSEWHEEL; + msg.wParam = GetMousewParam (-120); + break; + } + + } + + msg.lParam = (IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x); + MousePosition.X = xevent.ButtonEvent.x; + MousePosition.Y = xevent.ButtonEvent.y; + + if (!hwnd.Enabled) { + IntPtr dummy; + + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates (DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle (msg.hwnd).ClientWindow, xevent.ButtonEvent.x, xevent.ButtonEvent.y, out xevent.ButtonEvent.x, out xevent.ButtonEvent.y, out dummy); + msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X); + } + + if (Grab.Hwnd != IntPtr.Zero) { + msg.hwnd = Grab.Hwnd; + } + + if (!ClickPending.Pending) { + ClickPending.Pending = true; + ClickPending.Hwnd = msg.hwnd; + ClickPending.Message = msg.message; + ClickPending.wParam = msg.wParam; + ClickPending.lParam = msg.lParam; + ClickPending.Time = (long)xevent.ButtonEvent.time; + } else { + if ((((long)xevent.ButtonEvent.time - ClickPending.Time) < DoubleClickInterval) && (msg.wParam == ClickPending.wParam) && (msg.lParam == ClickPending.lParam) && (msg.message == ClickPending.Message)) { + // Looks like a genuine double click, clicked twice on the same spot with the same keys + switch (xevent.ButtonEvent.button) { + case 1: { + msg.message = client ? Msg.WM_LBUTTONDBLCLK : Msg.WM_NCLBUTTONDBLCLK; + break; + } + + case 2: { + msg.message = client ? Msg.WM_MBUTTONDBLCLK : Msg.WM_NCMBUTTONDBLCLK; + break; + } + + case 3: { + msg.message = client ? Msg.WM_RBUTTONDBLCLK : Msg.WM_NCRBUTTONDBLCLK; + break; + } + } + } + ClickPending.Pending = false; + } + + break; + } + + case XEventName.ButtonRelease: { + Dnd.HandleButtonRelease (ref xevent); + switch (xevent.ButtonEvent.button) { + case 1: { + if (client) { + msg.message = Msg.WM_LBUTTONUP; + } else { + msg.message = Msg.WM_NCLBUTTONUP; + WholeToScreen (msg.hwnd, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + msg.wParam = GetMousewParam (0); + MouseState &= ~MouseButtons.Left; + break; + } + + case 2: { + if (client) { + msg.message = Msg.WM_MBUTTONUP; + } else { + msg.message = Msg.WM_NCMBUTTONUP; + WholeToScreen (msg.hwnd, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + msg.wParam = GetMousewParam (0); + MouseState &= ~MouseButtons.Middle; + break; + } + + case 3: { + if (client) { + msg.message = Msg.WM_RBUTTONUP; + } else { + msg.message = Msg.WM_NCRBUTTONUP; + WholeToScreen (msg.hwnd, ref xevent.ButtonEvent.x, ref xevent.ButtonEvent.y); + } + msg.wParam = GetMousewParam (0); + MouseState &= ~MouseButtons.Right; + break; + } + + case 4: { + goto ProcessNextMessage; + } + + case 5: { + goto ProcessNextMessage; + } + } + + if (!hwnd.Enabled) { + IntPtr dummy; + + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates (DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle (msg.hwnd).ClientWindow, xevent.ButtonEvent.x, xevent.ButtonEvent.y, out xevent.ButtonEvent.x, out xevent.ButtonEvent.y, out dummy); + msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X); + } + + if (Grab.Hwnd != IntPtr.Zero) { + msg.hwnd = Grab.Hwnd; + } + + msg.lParam = (IntPtr) (xevent.ButtonEvent.y << 16 | xevent.ButtonEvent.x); + MousePosition.X = xevent.ButtonEvent.x; + MousePosition.Y = xevent.ButtonEvent.y; + break; + } + + case XEventName.MotionNotify: { + if (client) { + #if DriverDebugExtra + Console.WriteLine("GetMessage(): Window {0:X} MotionNotify x={1} y={2}", client ? hwnd.client_window.ToInt32() : hwnd.whole_window.ToInt32(), xevent.MotionEvent.x, xevent.MotionEvent.y); + #endif + + if (Dnd.HandleMotionNotify (ref xevent)) + goto ProcessNextMessage; + if (Grab.Hwnd != IntPtr.Zero) { + msg.hwnd = Grab.Hwnd; + } else { + NativeWindow.WndProc (msg.hwnd, Msg.WM_SETCURSOR, msg.hwnd, (IntPtr)HitTest.HTCLIENT); + } + + msg.message = Msg.WM_MOUSEMOVE; + msg.wParam = GetMousewParam (0); + msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF); + + if (!hwnd.Enabled) { + IntPtr dummy; + + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates (DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle (msg.hwnd).ClientWindow, xevent.MotionEvent.x, xevent.MotionEvent.y, out xevent.MotionEvent.x, out xevent.MotionEvent.y, out dummy); + msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X); + } + + HoverState.X = MousePosition.X = xevent.MotionEvent.x; + HoverState.Y = MousePosition.Y = xevent.MotionEvent.y; + + break; + } else { + #if DriverDebugExtra + Console.WriteLine("GetMessage(): non-client area {0:X} MotionNotify x={1} y={2}", client ? hwnd.client_window.ToInt32() : hwnd.whole_window.ToInt32(), xevent.MotionEvent.x, xevent.MotionEvent.y); + #endif + msg.message = Msg.WM_NCMOUSEMOVE; + msg.lParam = (IntPtr) (xevent.MotionEvent.y << 16 | xevent.MotionEvent.x & 0xFFFF); + + if (!hwnd.Enabled) { + IntPtr dummy; + + msg.hwnd = hwnd.EnabledHwnd; + XTranslateCoordinates (DisplayHandle, xevent.AnyEvent.window, Hwnd.ObjectFromHandle (msg.hwnd).ClientWindow, xevent.MotionEvent.x, xevent.MotionEvent.y, out xevent.MotionEvent.x, out xevent.MotionEvent.y, out dummy); + msg.lParam = (IntPtr)(MousePosition.Y << 16 | MousePosition.X); + } + + #if notyet + // Not sure we need this... + HitTest ht; + ht = NativeWindow.WndProc(hwnd.client_window, Msg.WM_NCHITTEST, IntPtr.Zero, msg.lParam); + #endif + + MousePosition.X = xevent.MotionEvent.x; + MousePosition.Y = xevent.MotionEvent.y; + } + + break; + } + + case XEventName.EnterNotify: { + if (!hwnd.Enabled) { + goto ProcessNextMessage; + } + if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) { + goto ProcessNextMessage; + } + msg.message = Msg.WM_MOUSE_ENTER; + HoverState.Timer.Enabled = true; + HoverState.Window = xevent.CrossingEvent.window; + break; + } + + case XEventName.LeaveNotify: { + if (!hwnd.Enabled) { + goto ProcessNextMessage; + } + if (xevent.CrossingEvent.mode != NotifyMode.NotifyNormal) { + goto ProcessNextMessage; + } + msg.message = Msg.WM_MOUSELEAVE; + HoverState.Timer.Enabled = false; + HoverState.Window = IntPtr.Zero; + break; + } + + #if later + case XEventName.CreateNotify: { + if (client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { + msg.message = WM_CREATE; + // Set up CreateStruct + } else { + goto ProcessNextMessage; + } + break; + } + #endif + + + case XEventName.ReparentNotify: { + if (hwnd.parent == null) { // Toplevel + if (xevent.ReparentEvent.parent != IntPtr.Zero) { + // We need to adjust x/y + int dummy_int; + + hwnd.Reparented = true; + + gdk_window_get_geometry (gdk_window_lookup (hwnd.whole_window), out hwnd.x, out hwnd.y, out dummy_int, out dummy_int, out dummy_int); + msg.message = Msg.WM_WINDOWPOSCHANGED; + if (hwnd.opacity != 0xffffffff) { + uint opacity; + + opacity = hwnd.opacity; + XChangeProperty (DisplayHandle, XGetParent (hwnd.whole_window), NetAtoms [(int)NA._NET_WM_WINDOW_OPACITY], Atom.XA_CARDINAL, 32, PropertyMode.Replace, ref opacity, 1); + } + } else { + hwnd.Reparented = false; + goto ProcessNextMessage; + } + } + break; + } + + case XEventName.ConfigureNotify: { + if (!client && (xevent.ConfigureEvent.xevent == xevent.ConfigureEvent.window)) { // Ignore events for children (SubstructureNotify) and client areas + XplatUIWin32.NCCALCSIZE_PARAMS ncp; + IntPtr ptr; + Rectangle rect; + + #if DriverDebugExtra + Console.WriteLine("GetMessage(): Window {0:X} ConfigureNotify x={1} y={2} width={3} height={4}", hwnd.client_window.ToInt32(), xevent.ConfigureEvent.x, xevent.ConfigureEvent.y, xevent.ConfigureEvent.width, xevent.ConfigureEvent.height); + #endif + msg.message = Msg.WM_WINDOWPOSCHANGED; + hwnd.configure_pending = false; + + // We need to adjust our client window to track the resize of whole_window + rect = hwnd.DefaultClientRect; + + ncp = new XplatUIWin32.NCCALCSIZE_PARAMS (); + ptr = Marshal.AllocHGlobal (Marshal.SizeOf (ncp)); + + ncp.rgrc1.left = rect.Left; + ncp.rgrc1.top = rect.Top; + ncp.rgrc1.right = rect.Right; + ncp.rgrc1.bottom = rect.Bottom; + + Marshal.StructureToPtr (ncp, ptr, true); + NativeWindow.WndProc (hwnd.client_window, Msg.WM_NCCALCSIZE, (IntPtr)1, ptr); + ncp = (XplatUIWin32.NCCALCSIZE_PARAMS)Marshal.PtrToStructure (ptr, typeof(XplatUIWin32.NCCALCSIZE_PARAMS)); + Marshal.FreeHGlobal (ptr); + + // FIXME - debug this with Menus, need to set hwnd.ClientRect + + rect = new Rectangle (ncp.rgrc1.left, ncp.rgrc1.top, ncp.rgrc1.right - ncp.rgrc1.left, ncp.rgrc1.bottom - ncp.rgrc1.top); + //Console.WriteLine("CreateOffscreenbuffer..."); +// CreateOffscreenBuffer (ref hwnd.client_offscreen, rect.Width, rect.Height); + + XMoveResizeWindow (DisplayHandle, hwnd.client_window, rect.X, rect.Y, rect.Width, rect.Height); + } else { + goto ProcessNextMessage; + } + + msg.lParam = IntPtr.Zero; // FIXME - Generate LPWINDOWPOS structure and pass on + break; + } + + case XEventName.FocusIn: { + if (!hwnd.Enabled) { + goto ProcessNextMessage; + } + msg.message = Msg.WM_SETFOCUS; + msg.wParam = IntPtr.Zero; + break; + } + + case XEventName.FocusOut: { + if (!hwnd.Enabled) { + goto ProcessNextMessage; + } + msg.message = Msg.WM_KILLFOCUS; + msg.wParam = IntPtr.Zero; + break; + } + + case XEventName.Expose: { + if (PostQuitState) { + goto ProcessNextMessage; + } + + + if (client) { + if (!hwnd.expose_pending) { + goto ProcessNextMessage; + } + } else { + if (!hwnd.nc_expose_pending) { + goto ProcessNextMessage; + } + + switch (hwnd.border_style) { + case FormBorderStyle.Fixed3D: { + Graphics g; + + g = Graphics.FromHwnd (hwnd.whole_window); + ShiftUI.WidgetPaint.DrawBorder3D (g, new Rectangle (0, 0, hwnd.Width, hwnd.Height)); + g.Dispose (); + break; + } + + case FormBorderStyle.FixedSingle: { + Graphics g; + + g = Graphics.FromHwnd (hwnd.whole_window); + ShiftUI.WidgetPaint.DrawBorder (g, new Rectangle (0, 0, hwnd.Width, hwnd.Height), Color.Black, ButtonBorderStyle.Solid); + g.Dispose (); + break; + } + } + #if DriverDebugExtra + Console.WriteLine("GetMessage(): Window {0:X} Exposed non-client area {1},{2} {3}x{4}", hwnd.client_window.ToInt32(), xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); + #endif + + Rectangle rect = new Rectangle (xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); + Region region = new Region (rect); + IntPtr hrgn = region.GetHrgn (null); // Graphics object isn't needed + msg.message = Msg.WM_NCPAINT; + msg.wParam = hrgn; + hwnd.nc_expose_pending = false; + break; + } + #if DriverDebugExtra + Console.WriteLine("GetMessage(): Window {0:X} Exposed area {1},{2} {3}x{4}", hwnd.client_window.ToInt32(), xevent.ExposeEvent.x, xevent.ExposeEvent.y, xevent.ExposeEvent.width, xevent.ExposeEvent.height); + #endif + if (Caret.Visible == 1) { + Caret.Paused = true; + HideCaret (); + } + + if (Caret.Visible == 1) { + ShowCaret (); + Caret.Paused = false; + } + msg.message = Msg.WM_PAINT; + break; + } + + case XEventName.DestroyNotify: { + + // This is a bit tricky, we don't receive our own DestroyNotify, we only get those for our children + hwnd = Hwnd.ObjectFromHandle (xevent.DestroyWindowEvent.window); + + // We may get multiple for the same window, act only one the first (when Hwnd still knows about it) + if (hwnd.client_window == xevent.DestroyWindowEvent.window) { + msg.hwnd = hwnd.client_window; + msg.message = Msg.WM_DESTROY; + hwnd.Dispose (); + + #if DriverDebug + Console.WriteLine("Got DestroyNotify on Window {0:X}", msg.hwnd.ToInt32()); + #endif + } else { + goto ProcessNextMessage; + } + + break; + } + + case XEventName.ClientMessage: { + if (Dnd.HandleClientMessage (ref xevent)) { + goto ProcessNextMessage; + } + + if (xevent.ClientMessageEvent.message_type == (IntPtr)AsyncAtom) { + XplatUIDriverSupport.ExecuteClientMessage ((GCHandle)xevent.ClientMessageEvent.ptr1); + break; + } + + if (xevent.ClientMessageEvent.message_type == (IntPtr)HoverState.Atom) { + msg.message = Msg.WM_MOUSEHOVER; + msg.wParam = GetMousewParam (0); + msg.lParam = (IntPtr) (xevent.ClientMessageEvent.ptr1); + break; + } + + if (xevent.ClientMessageEvent.message_type == (IntPtr)PostAtom) { + msg.hwnd = xevent.ClientMessageEvent.ptr1; + msg.message = (Msg) xevent.ClientMessageEvent.ptr2.ToInt32 (); + msg.wParam = xevent.ClientMessageEvent.ptr3; + msg.lParam = xevent.ClientMessageEvent.ptr4; + break; + } + + #if dontcare + if (xevent.ClientMessageEvent.message_type == (IntPtr)NetAtoms[(int)NA._XEMBED]) { + Console.WriteLine("GOT EMBED MESSAGE {0:X}", xevent.ClientMessageEvent.ptr2.ToInt32()); + break; + } + #endif + + if (xevent.ClientMessageEvent.message_type == (IntPtr)NetAtoms [(int)NA.WM_PROTOCOLS]) { + if (xevent.ClientMessageEvent.ptr1 == (IntPtr)NetAtoms [(int)NA.WM_DELETE_WINDOW]) { + msg.message = Msg.WM_CLOSE; + Graphics.FromHdcInternal (IntPtr.Zero); + break; + } + + // We should not get this, but I'll leave the code in case we need it in the future + if (xevent.ClientMessageEvent.ptr1 == (IntPtr)NetAtoms [(int)NA.WM_TAKE_FOCUS]) { + goto ProcessNextMessage; + } + } + break; + } + + case XEventName.TimerNotify: { + xevent.TimerNotifyEvent.handler (this, EventArgs.Empty); + break; + } + + default: { + goto ProcessNextMessage; + } + } + + return true; + } + + internal override bool GetText (IntPtr handle, out string text) + { + IntPtr textptr; + + textptr = IntPtr.Zero; + + lock (XlibLock) { + // FIXME - use _NET properties + XFetchName (DisplayHandle, Hwnd.ObjectFromHandle (handle).whole_window, ref textptr); + } + if (textptr != IntPtr.Zero) { + text = Marshal.PtrToStringAnsi (textptr); + XFree (textptr); + return true; + } else { + text = ""; + return false; + } + } + + internal override void GetWindowPos (IntPtr handle, bool is_toplevel, out int x, out int y, out int width, out int height, out int client_width, out int client_height) + { + Hwnd hwnd; + Rectangle rect; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd != null) { + x = hwnd.x; + y = hwnd.y; + width = hwnd.width; + height = hwnd.height; + + rect = Hwnd.GetClientRectangle (hwnd.border_style, hwnd.menu, hwnd.title_style, hwnd.caption_height, hwnd.tool_caption_height, width, height); + + client_width = rect.Width; + client_height = rect.Height; + + return; + } + + // Should we throw an exception or fail silently? + // throw new ArgumentException("Called with an invalid window handle", "handle"); + + x = 0; + y = 0; + width = 0; + height = 0; + client_width = 0; + client_height = 0; + } + + internal override FormWindowState GetWindowState (IntPtr handle) + { + Atom actual_atom; + int actual_format; + int nitems; + int bytes_after; + IntPtr prop = IntPtr.Zero; + IntPtr atom; + int maximized; + bool minimized; + XWindowAttributes attributes; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + maximized = 0; + minimized = false; + XGetWindowProperty (DisplayHandle, hwnd.whole_window, NetAtoms [(int)NA._NET_WM_STATE], 0, 256, false, Atom.XA_ATOM, out actual_atom, out actual_format, out nitems, out bytes_after, ref prop); + if ((nitems > 0) && (prop != IntPtr.Zero)) { + for (int i = 0; i < nitems; i++) { + atom = (IntPtr)Marshal.ReadInt32 (prop, i * 4); + if ((atom == (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_HORZ]) || (atom == (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_VERT])) { + maximized++; + } else if (atom == (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_HIDDEN]) { + minimized = true; + } + } + XFree (prop); + } + + if (minimized) { + return FormWindowState.Minimized; + } else if (maximized == 2) { + return FormWindowState.Maximized; + } + + attributes = new XWindowAttributes (); + XGetWindowAttributes (DisplayHandle, handle, ref attributes); + if (attributes.map_state == MapState.IsUnmapped) { + throw new NotSupportedException ("Cannot retrieve the state of an unmapped window"); + } + + + return FormWindowState.Normal; + } + + internal override void GrabInfo (out IntPtr handle, out bool GrabConfined, out Rectangle GrabArea) + { + handle = Grab.Hwnd; + GrabConfined = Grab.Confined; + GrabArea = Grab.Area; + } + + internal override void GrabWindow (IntPtr handle, IntPtr confine_to_handle) + { + Hwnd hwnd; + IntPtr confine_to_window; + + confine_to_window = IntPtr.Zero; + + if (confine_to_handle != IntPtr.Zero) { + XWindowAttributes attributes = new XWindowAttributes (); + + hwnd = Hwnd.ObjectFromHandle (confine_to_handle); + + lock (XlibLock) { + XGetWindowAttributes (DisplayHandle, hwnd.client_window, ref attributes); + } + Grab.Area.X = attributes.x; + Grab.Area.Y = attributes.y; + Grab.Area.Width = attributes.width; + Grab.Area.Height = attributes.height; + Grab.Confined = true; + confine_to_window = hwnd.client_window; + } + + Grab.Hwnd = handle; + + hwnd = Hwnd.ObjectFromHandle (handle); + + lock (XlibLock) { + XGrabPointer (DisplayHandle, hwnd.client_window, false, + EventMask.ButtonPressMask | EventMask.ButtonMotionMask | + EventMask.ButtonReleaseMask | EventMask.PointerMotionMask, + GrabMode.GrabModeAsync, GrabMode.GrabModeAsync, confine_to_window, 0, 0); + } + } + + internal override void UngrabWindow (IntPtr hwnd) + { + lock (XlibLock) { + XUngrabPointer (DisplayHandle, 0); + XFlush (DisplayHandle); + } + Grab.Hwnd = IntPtr.Zero; + Grab.Confined = false; + } + + internal override void HandleException (Exception e) + { + StackTrace st = new StackTrace (e, true); + Console.WriteLine ("Exception '{0}'", e.Message + st.ToString ()); + Console.WriteLine ("{0}{1}", e.Message, st.ToString ()); + } + + internal override void Invalidate (IntPtr handle, Rectangle rc, bool clear) + { + Hwnd hwnd; + XEvent xevent; + + hwnd = Hwnd.ObjectFromHandle (handle); + + + xevent = new XEvent (); + xevent.type = XEventName.Expose; + xevent.ExposeEvent.display = DisplayHandle; + xevent.ExposeEvent.window = hwnd.client_window; + + if (clear) { + xevent.ExposeEvent.x = hwnd.X; + xevent.ExposeEvent.y = hwnd.Y; + xevent.ExposeEvent.width = hwnd.Width; + xevent.ExposeEvent.height = hwnd.Height; + } else { + xevent.ExposeEvent.x = rc.X; + xevent.ExposeEvent.y = rc.Y; + xevent.ExposeEvent.width = rc.Width; + xevent.ExposeEvent.height = rc.Height; + } + + AddExpose (xevent); + } + + internal override bool IsEnabled (IntPtr handle) + { + return Hwnd.ObjectFromHandle (handle).Enabled; + } + + internal override bool IsVisible (IntPtr handle) + { + return Hwnd.ObjectFromHandle (handle).visible; + } + + internal override void KillTimer (Timer timer) + { + lock (TimerList) { + TimerList.Remove (timer); + } + } + + internal override void MenuToScreen (IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + lock (XlibLock) { + XTranslateCoordinates (DisplayHandle, hwnd.whole_window, RootWindow, x, y, out dest_x_return, out dest_y_return, out child); + } + + x = dest_x_return; + y = dest_y_return; + } + + internal override void OverrideCursor (IntPtr cursor) + { + OverrideCursorHandle = cursor; + } + + internal PaintEventArgs PaintEventStart (IntPtr handle, bool client) + { + PaintEventArgs paint_event; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (Caret.Visible == 1) { + Caret.Paused = true; + HideCaret (); + } + + if (client) { + // handle backing store + IntPtr gdk_window = gdk_window_lookup (hwnd.client_window); + IntPtr gdk_pixmap = NewPixmap (gdk_window, hwnd.ClientRect.Width, hwnd.ClientRect.Height); + + backing_store [gdk_window] = gdk_pixmap; + + hwnd.client_dc = Graphics.FromHwnd (gdk_x11_drawable_get_xid (gdk_pixmap)); + hwnd.client_dc.SetClip (hwnd.invalid); + paint_event = new PaintEventArgs (hwnd.client_dc, hwnd.invalid); + hwnd.expose_pending = false; + + return paint_event; + } else { + hwnd.client_dc = Graphics.FromHwnd (hwnd.whole_window); + paint_event = new PaintEventArgs (hwnd.client_dc, new Rectangle (0, 0, hwnd.width, hwnd.height)); + hwnd.nc_expose_pending = false; + + return paint_event; + } + } + + internal void PaintEventEnd (IntPtr handle, bool client) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + hwnd.client_dc.Flush (); + + if (client) { + // clients are already drawn to a backing store pixmap + IntPtr gdk_window = gdk_window_lookup (hwnd.client_window); + IntPtr gdk_pixmap = (IntPtr)backing_store [gdk_window]; + + BlitOffscreenPixmap (gdk_pixmap, gdk_window, hwnd.Invalid); + + g_object_unref (gdk_pixmap); + backing_store.Remove (gdk_pixmap); + + hwnd.ClearInvalidArea (); + } + + hwnd.client_dc.Dispose (); + hwnd.client_dc = null; + + if (Caret.Visible == 1) { + ShowCaret (); + Caret.Paused = false; + } + } + + internal bool PeekMessage (ref MSG msg, IntPtr hWnd, int wFilterMin, int wFilterMax, uint flags) + { + bool pending; + + // FIXME - imlement filtering + + if ((flags & (uint)PeekMessageFlags.PM_REMOVE) == 0) { + throw new NotImplementedException ("PeekMessage PM_NOREMOVE is not implemented yet"); // FIXME - Implement PM_NOREMOVE flag + } + + pending = false; + if (MessageQueue.Count > 0) { + pending = true; + } else { + // Only call UpdateMessageQueue if real events are pending + // otherwise we go to sleep on the socket + if (XPending (DisplayHandle) != 0) { + UpdateMessageQueue (); + pending = true; + } + } + + CheckTimers (DateTime.Now); + + if (!pending) { + return false; + } + return GetMessage (ref msg, hWnd, wFilterMin, wFilterMax); + } + + // FIXME - I think this should just enqueue directly + internal override bool PostMessage (IntPtr handle, Msg message, IntPtr wparam, IntPtr lparam) + { + XEvent xevent = new XEvent (); + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = DisplayHandle; + + if (hwnd != null) { + xevent.ClientMessageEvent.window = hwnd.whole_window; + } else { + xevent.ClientMessageEvent.window = IntPtr.Zero; + } + + xevent.ClientMessageEvent.message_type = (IntPtr) PostAtom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = handle; + xevent.ClientMessageEvent.ptr2 = (IntPtr) message; + xevent.ClientMessageEvent.ptr3 = wparam; + xevent.ClientMessageEvent.ptr4 = lparam; + + MessageQueue.Enqueue (xevent); + + return true; + } + + internal override void PostQuitMessage (int exitCode) + { + XFlush (DisplayHandle); + PostQuitState = true; + + // Remove our display handle from S.D + Graphics.FromHdcInternal (IntPtr.Zero); + } + + internal override void ScreenToClient (IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + lock (XlibLock) { + XTranslateCoordinates (DisplayHandle, RootWindow, hwnd.client_window, x, y, out dest_x_return, out dest_y_return, out child); + } + + x = dest_x_return; + y = dest_y_return; + } + + internal override void ScreenToMenu (IntPtr handle, ref int x, ref int y) + { + int dest_x_return; + int dest_y_return; + IntPtr child; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + lock (XlibLock) { + XTranslateCoordinates (DisplayHandle, RootWindow, hwnd.whole_window, x, y, out dest_x_return, out dest_y_return, out child); + } + + x = dest_x_return; + y = dest_y_return; + } + + internal override void ScrollWindow (IntPtr handle, Rectangle area, int XAmount, int YAmount, bool with_children) + { + Hwnd hwnd; + IntPtr gc; + XGCValues gc_values; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd.invalid != Rectangle.Empty) { + // BIG FAT WARNING. This only works with how we use this function right now + // where we basically still scroll the whole window, but work around areas + // that are covered by our children + + hwnd.invalid.X += XAmount; + hwnd.invalid.Y += YAmount; + + if (hwnd.invalid.X < 0) { + hwnd.invalid.Width += hwnd.invalid.X; + hwnd.invalid.X = 0; + } + + if (hwnd.invalid.Y < 0) { + hwnd.invalid.Height += hwnd.invalid.Y; + hwnd.invalid.Y = 0; + } + } + + gc_values = new XGCValues (); + + if (with_children) { + gc_values.subwindow_mode = GCSubwindowMode.IncludeInferiors; + } + + gc = XCreateGC (DisplayHandle, hwnd.client_window, 0, ref gc_values); + + XCopyArea (DisplayHandle, hwnd.client_window, hwnd.client_window, gc, area.X - XAmount, area.Y - YAmount, area.Width, area.Height, area.X, area.Y); + + // Generate an expose for the area exposed by the horizontal scroll + if (XAmount > 0) { + hwnd.AddInvalidArea (area.X, area.Y, XAmount, area.Height); + } else if (XAmount < 0) { + hwnd.AddInvalidArea (XAmount + area.X + area.Width, area.Y, -XAmount, area.Height); + } + + // Generate an expose for the area exposed by the vertical scroll + if (YAmount > 0) { + hwnd.AddInvalidArea (area.X, area.Y, area.Width, YAmount); + } else if (YAmount < 0) { + hwnd.AddInvalidArea (area.X, YAmount + area.Y + area.Height, area.Width, -YAmount); + } + XFreeGC (DisplayHandle, gc); + + UpdateWindow (handle); + } + + internal override void ScrollWindow (IntPtr handle, int XAmount, int YAmount, bool with_children) + { + Hwnd hwnd; + + hwnd = Hwnd.GetObjectFromWindow (handle); + + ScrollWindow (handle, hwnd.ClientRect, XAmount, YAmount, with_children); + } + + internal override void SendAsyncMethod (AsyncMethodData method) + { + XEvent xevent = new XEvent (); + + xevent.type = XEventName.ClientMessage; + xevent.ClientMessageEvent.display = DisplayHandle; + xevent.ClientMessageEvent.window = FosterParent; + xevent.ClientMessageEvent.message_type = (IntPtr)AsyncAtom; + xevent.ClientMessageEvent.format = 32; + xevent.ClientMessageEvent.ptr1 = (IntPtr) GCHandle.Alloc (method); + + MessageQueue.EnqueueLocked (xevent); + + WakeupMain (); + } + + internal override IntPtr SendMessage (IntPtr hwnd, Msg message, IntPtr wParam, IntPtr lParam) + { + return NativeWindow.WndProc (hwnd, message, wParam, lParam); + } + + internal override void SetAllowDrop (IntPtr handle, bool value) + { + // We allow drop on all windows + } + + internal override DragDropEffects StartDrag (IntPtr handle, object data, + DragDropEffects allowed_effects) + { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) + throw new ArgumentException ("Attempt to begin drag from invalid window handle (" + handle.ToInt32 () + ")."); + + return Dnd.StartDrag (hwnd.client_window, data, allowed_effects); + } + + internal override void SetBorderStyle (IntPtr handle, FormBorderStyle border_style) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + hwnd.border_style = border_style; + + XMoveResizeWindow (DisplayHandle, hwnd.client_window, hwnd.ClientRect.X, hwnd.ClientRect.Y, hwnd.ClientRect.Width, hwnd.ClientRect.Height); + + InvalidateWholeWindow (handle); + } + + internal override void SetCaretPos (IntPtr handle, int x, int y) + { + if (Caret.Hwnd == handle) { + Caret.Timer.Stop (); + HideCaret (); + + Caret.X = x; + Caret.Y = y; + + if (Caret.Visible == 1) { + ShowCaret (); + Caret.Timer.Start (); + } + } + } + + internal override void SetCursor (IntPtr handle, IntPtr cursor) + { + Hwnd hwnd; + + if (OverrideCursorHandle == IntPtr.Zero) { + if ((LastCursorWindow == handle) && (LastCursorHandle == cursor)) { + return; + } + + LastCursorHandle = cursor; + LastCursorWindow = handle; + + hwnd = Hwnd.ObjectFromHandle (handle); + lock (XlibLock) { + if (cursor != IntPtr.Zero) { + XDefineCursor (DisplayHandle, hwnd.whole_window, cursor); + } else { + XUndefineCursor (DisplayHandle, hwnd.whole_window); + } + } + return; + } + + hwnd = Hwnd.ObjectFromHandle (handle); + lock (XlibLock) { + XDefineCursor (DisplayHandle, hwnd.whole_window, OverrideCursorHandle); + } + } + + internal override void SetCursorPos (IntPtr handle, int x, int y) + { + if (handle == IntPtr.Zero) { + lock (XlibLock) { + XWarpPointer (DisplayHandle, IntPtr.Zero, IntPtr.Zero, 0, 0, 0, 0, x, y); + } + return; + } else { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + lock (XlibLock) { + XWarpPointer (DisplayHandle, IntPtr.Zero, hwnd.client_window, 0, 0, 0, 0, x, y); + } + return; + } + } + + internal override void SetFocus (IntPtr handle) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (FocusWindow != IntPtr.Zero) { + PostMessage (FocusWindow, Msg.WM_KILLFOCUS, hwnd.client_window, IntPtr.Zero); + } + PostMessage (hwnd.client_window, Msg.WM_SETFOCUS, FocusWindow, IntPtr.Zero); + FocusWindow = hwnd.client_window; + + //XSetInputFocus(DisplayHandle, Hwnd.ObjectFromHandle(handle).client_window, RevertTo.None, IntPtr.Zero); + } + + internal override void SetIcon (IntPtr handle, Icon icon) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + if (hwnd != null) { + SetIcon (hwnd, icon); + } + } + + internal override void SetMenu (IntPtr handle, Menu menu) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + hwnd.menu = menu; + + // FIXME - do we need to trigger some resize? + } + + internal override void SetModal (IntPtr handle, bool Modal) + { + if (Modal) { + ModalWindows.Push (handle); + } else { + if (ModalWindows.Contains (handle)) { + ModalWindows.Pop (); + } + if (ModalWindows.Count > 0) { + Activate ((IntPtr)ModalWindows.Peek ()); + } + } + } + + internal override IntPtr SetParent (IntPtr handle, IntPtr parent) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + hwnd.parent = Hwnd.ObjectFromHandle (parent); + + lock (XlibLock) { + #if DriverDebug + Console.WriteLine("Parent for window {0:X} / {1:X} = {2:X} (Handle:{3:X})", hwnd.ClientWindow.ToInt32(), hwnd.WholeWindow.ToInt32(), hwnd.parent != null ? hwnd.parent.Handle.ToInt32() : 0, parent.ToInt32()); + #endif + XReparentWindow (DisplayHandle, hwnd.whole_window, hwnd.parent.client_window, hwnd.x, hwnd.y); + } + + return IntPtr.Zero; + } + + internal override void SetTimer (Timer timer) + { + lock (TimerList) { + TimerList.Add (timer); + } + WakeupMain (); + } + + internal bool SetTopmost (IntPtr handle, IntPtr handle_owner, bool enabled) + { + Hwnd hwnd; + Hwnd hwnd_owner; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (handle_owner != IntPtr.Zero) { + hwnd_owner = Hwnd.ObjectFromHandle (handle_owner); + } else { + hwnd_owner = null; + } + + if (enabled) { + lock (XlibLock) { + if (hwnd_owner != null) { + XSetTransientForHint (DisplayHandle, hwnd.whole_window, hwnd_owner.whole_window); + } else { + XSetTransientForHint (DisplayHandle, hwnd.whole_window, FosterParent); + } + } + } else { + lock (XlibLock) { + XDeleteProperty (DisplayHandle, hwnd.whole_window, (int)Atom.XA_WM_TRANSIENT_FOR); + } + } + return true; + } + + internal bool SetVisible (IntPtr handle, bool visible) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + hwnd.visible = visible; + + lock (XlibLock) { + if (visible) { + if (ShiftUI.Widget.FromHandle (handle) is Form) { + FormWindowState s; + + s = ((Form)ShiftUI.Widget.FromHandle (handle)).WindowState; + + XMapWindow (DisplayHandle, hwnd.whole_window); + XMapWindow (DisplayHandle, hwnd.client_window); + + switch (s) { + case FormWindowState.Minimized: SetWindowState (handle, FormWindowState.Minimized); break; + case FormWindowState.Maximized: SetWindowState (handle, FormWindowState.Maximized); break; + } + } else { + XMapWindow (DisplayHandle, hwnd.whole_window); + XMapWindow (DisplayHandle, hwnd.client_window); + } + } else { + XUnmapWindow (DisplayHandle, hwnd.whole_window); + } + } + return true; + } + + internal override void SetWindowMinMax (IntPtr handle, Rectangle maximized, Size min, Size max) + { + Hwnd hwnd; + XSizeHints hints; + + hwnd = Hwnd.ObjectFromHandle (handle); + if (hwnd == null) { + return; + } + + hints = new XSizeHints (); + + if (min != Size.Empty) { + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMinSize); + hints.min_width = min.Width; + hints.min_height = min.Height; + } + + if (max != Size.Empty) { + hints.flags = (IntPtr)((int)hints.flags | (int)XSizeHintsFlags.PMaxSize); + hints.max_width = max.Width; + hints.max_height = max.Height; + } + + XSetWMNormalHints (DisplayHandle, hwnd.whole_window, ref hints); + + if (maximized != Rectangle.Empty) { + hints.flags = (IntPtr)XSizeHintsFlags.PPosition; + hints.x = maximized.X; + hints.y = maximized.Y; + hints.width = maximized.Width; + hints.height = maximized.Height; + + // Metacity does not seem to follow this constraint for maximized (zoomed) windows + XSetZoomHints (DisplayHandle, hwnd.whole_window, ref hints); + } + } + + + internal override void SetWindowPos (IntPtr handle, int x, int y, int width, int height) + { + Hwnd hwnd; + Rectangle client_rect; + + hwnd = Hwnd.ObjectFromHandle (handle); + + // X requires a sanity check for width & height; otherwise it dies + if (hwnd.zero_sized && width > 0 && height > 0) { + if (hwnd.visible) { + XMapWindow (DisplayHandle, hwnd.whole_window); + } + hwnd.zero_sized = false; + } + + if (width < 1) { + hwnd.zero_sized = true; + XUnmapWindow (DisplayHandle, hwnd.whole_window); + } + + if (height < 1) { + hwnd.zero_sized = true; + XUnmapWindow (DisplayHandle, hwnd.whole_window); + } + + client_rect = Hwnd.GetClientRectangle (hwnd.border_style, hwnd.menu, hwnd.title_style, hwnd.caption_height, hwnd.tool_caption_height, width, height); + + // Save a server roundtrip (and prevent a feedback loop) + if ((hwnd.x == x) && (hwnd.y == y) && + (hwnd.width == width) && (hwnd.height == height) && + (hwnd.ClientRect == client_rect)) { + return; + } + + if (!hwnd.zero_sized) { + lock (XlibLock) { + XMoveResizeWindow (DisplayHandle, hwnd.whole_window, x, y, width, height); + XMoveResizeWindow (DisplayHandle, hwnd.client_window, client_rect.X, client_rect.Y, client_rect.Width, client_rect.Height); + } + } + + // Prevent an old queued ConfigureNotify from setting our width with outdated data, set it now + hwnd.width = width; + hwnd.height = height; + } + + internal override void SetWindowState (IntPtr handle, FormWindowState state) + { + FormWindowState current_state; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + current_state = GetWindowState (handle); + + if (current_state == state) { + return; + } + + switch (state) { + case FormWindowState.Normal: { + lock (XlibLock) { + if (current_state == FormWindowState.Minimized) { + XMapWindow (DisplayHandle, hwnd.whole_window); + XMapWindow (DisplayHandle, hwnd.client_window); + } else if (current_state == FormWindowState.Maximized) { + SendNetWMMessage (hwnd.whole_window, (IntPtr)(uint)NetAtoms [(int)NA._NET_WM_STATE], (IntPtr)2 /* toggle */, (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_HORZ], (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_VERT]); + } + } + Activate (handle); + return; + } + + case FormWindowState.Minimized: { + lock (XlibLock) { + if (current_state == FormWindowState.Maximized) { + SendNetWMMessage (hwnd.whole_window, (IntPtr)NetAtoms [(int)NA._NET_WM_STATE], (IntPtr)2 /* toggle */, (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_HORZ], (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_VERT]); + } + XIconifyWindow (DisplayHandle, hwnd.whole_window, ScreenNo); + } + return; + } + + case FormWindowState.Maximized: { + lock (XlibLock) { + if (current_state == FormWindowState.Minimized) { + XMapWindow (DisplayHandle, hwnd.whole_window); + XMapWindow (DisplayHandle, hwnd.client_window); + } + + SendNetWMMessage (hwnd.whole_window, (IntPtr)NetAtoms [(int)NA._NET_WM_STATE], (IntPtr)1 /* Add */, (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_HORZ], (IntPtr)NetAtoms [(int)NA._NET_WM_STATE_MAXIMIZED_VERT]); + } + Activate (handle); + return; + } + } + } + + internal override void SetWindowStyle (IntPtr handle, CreateParams cp) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + SetHwndStyles (hwnd, cp); + SetWMStyles (hwnd, cp); + } + + internal override void SetWindowTransparency (IntPtr handle, double transparency, Color key) + { + Hwnd hwnd; + uint opacity; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (hwnd == null) { + return; + } + + hwnd.opacity = (uint)(0xffffffff * transparency); + opacity = hwnd.opacity; + + if (hwnd.reparented) { + XChangeProperty (DisplayHandle, XGetParent (hwnd.whole_window), NetAtoms [(int)NA._NET_WM_WINDOW_OPACITY], Atom.XA_CARDINAL, 32, PropertyMode.Replace, ref opacity, 1); + } + } + + internal override bool SetZOrder (IntPtr handle, IntPtr after_handle, bool top, bool bottom) + { + Hwnd hwnd = Hwnd.ObjectFromHandle (handle); + + if (top) { + lock (XlibLock) { + XRaiseWindow (DisplayHandle, hwnd.whole_window); + } + return true; + } else if (!bottom) { + Hwnd after_hwnd = null; + + if (after_handle != IntPtr.Zero) { + after_hwnd = Hwnd.ObjectFromHandle (after_handle); + } + + XWindowChanges values = new XWindowChanges (); + + if (after_hwnd == null) { + throw new ArgumentNullException ("after_handle", "Need sibling to adjust z-order"); + } + values.sibling = after_hwnd.whole_window; + values.stack_mode = StackMode.Below; + + lock (XlibLock) { + XConfigureWindow (DisplayHandle, hwnd.whole_window, ChangeWindowFlags.CWStackMode | ChangeWindowFlags.CWSibling, ref values); + } + } else { + // Bottom + lock (XlibLock) { + XLowerWindow (DisplayHandle, hwnd.whole_window); + } + return true; + } + return false; + } + + internal override void ShowCursor (bool show) + { + ; // FIXME - X11 doesn't 'hide' the cursor. we could create an empty cursor + } + + internal override object StartLoop (Thread thread) + { + // Future place for prepping a new queue for this specific thread + return thread; + } + + internal override TransparencySupport SupportsTransparency () + { + // We need to check if the x compositing manager is running + return TransparencySupport.Get; + } + + internal override bool SystrayAdd (IntPtr handle, string tip, Icon icon, out ToolTip tt) + { + GetSystrayManagerWindow (); + + if (SystrayMgrWindow != IntPtr.Zero) { + uint[] atoms; + XSizeHints size_hints; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + #if DriverDebug + Console.WriteLine("Adding Systray Whole:{0:X}, Client:{1:X}", hwnd.whole_window.ToInt32(), hwnd.client_window.ToInt32()); + #endif + + XUnmapWindow (DisplayHandle, hwnd.whole_window); + XUnmapWindow (DisplayHandle, hwnd.client_window); + + // Oh boy. + gdk_window_destroy (gdk_window_lookup (hwnd.client_window)); + hwnd.client_window = hwnd.whole_window; + + size_hints = new XSizeHints (); + + size_hints.flags = (IntPtr)(XSizeHintsFlags.PMinSize | XSizeHintsFlags.PMaxSize | XSizeHintsFlags.PBaseSize); + size_hints.min_width = icon.Width; + size_hints.min_height = icon.Height; + + size_hints.max_width = icon.Width; + size_hints.max_height = icon.Height; + + size_hints.base_width = icon.Width; + size_hints.base_height = icon.Height; + XSetWMNormalHints (DisplayHandle, hwnd.whole_window, ref size_hints); + + atoms = new uint [2]; + atoms [0] = 1; // Version 1 + atoms [1] = 0; // We're not mapped + + // This line cost me 3 days... + XChangeProperty (DisplayHandle, hwnd.whole_window, NetAtoms [(int)NA._XEMBED_INFO], NetAtoms [(int)NA._XEMBED_INFO], 32, PropertyMode.Replace, atoms, 2); + + // Need to pick some reasonable defaults + tt = new ToolTip (); + tt.AutomaticDelay = 100; + tt.InitialDelay = 250; + tt.ReshowDelay = 250; + tt.ShowAlways = true; + + if ((tip != null) && (tip != string.Empty)) { + tt.SetToolTip (ShiftUI.Widget.FromHandle (handle), tip); + tt.Active = true; + } else { + tt.Active = false; + } + + // Make sure the window exists + XSync (DisplayHandle, hwnd.whole_window); + + SendNetClientMessage (SystrayMgrWindow, (IntPtr)NetAtoms [(int)NA._NET_SYSTEM_TRAY_OPCODE], IntPtr.Zero, (IntPtr)SystrayRequest.SYSTEM_TRAY_REQUEST_DOCK, hwnd.whole_window); + return true; + } + + tt = null; + return false; + } + + internal override bool SystrayChange (IntPtr handle, string tip, Icon icon, ref ToolTip tt) + { + ShiftUI.Widget Widget; + + Widget = ShiftUI.Widget.FromHandle (handle); + if (Widget != null && tt != null) { + tt.SetToolTip (Widget, tip); + tt.Active = true; + return true; + } else { + return false; + } + } + + internal override void SystrayRemove (IntPtr handle, ref ToolTip tt) + { + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + XUnmapWindow (DisplayHandle, hwnd.whole_window); + SetParent (hwnd.whole_window, FosterParent); + + // The caller can now re-dock it later... + if (tt != null) { + tt.Dispose (); + tt = null; + } + } + + internal override bool Text (IntPtr handle, string text) + { + lock (XlibLock) { + gdk_window_set_title (gdk_window_lookup (Hwnd.ObjectFromHandle (handle).whole_window), text); + } + return true; + } + + internal override bool TranslateMessage (ref MSG msg) + { + return Keyboard.TranslateMessage (ref msg); + } + + internal override void UpdateWindow (IntPtr handle) + { + XEvent xevent; + Hwnd hwnd; + + hwnd = Hwnd.ObjectFromHandle (handle); + + if (!hwnd.visible || hwnd.expose_pending) { + return; + } + + #if not + SendMessage(handle, Msg.WM_PAINT, IntPtr.Zero, IntPtr.Zero); + #else + xevent = new XEvent (); + xevent.type = XEventName.Expose; + xevent.ExposeEvent.display = DisplayHandle; + xevent.ExposeEvent.window = hwnd.client_window; + + MessageQueue.Enqueue (xevent); + hwnd.expose_pending = true; + #endif + } + + internal static IntPtr NewPixmap (IntPtr gdk_window, int width, int height) + { + return gdk_pixmap_new (gdk_window, width, height, 24); // FIXME: instead of 24, get the correct display depth + } + + internal static void BlitOffscreenPixmap (IntPtr gdk_pixmap, IntPtr dest_drawable, Rectangle area) + { + IntPtr gdk_gc = gdk_gc_new (gdk_pixmap); + + gdk_draw_drawable (dest_drawable, gdk_gc, gdk_pixmap, area.X, area.Y, area.X, area.Y, area.Width, area.Height); + + g_object_unref (gdk_gc); + } + #endregion // Public Static Methods + + #region Events + internal override event EventHandler Idle; + #endregion // Events + + #region X11 Imports + [DllImport ("libX11", EntryPoint="XSynchronize")] + internal extern static IntPtr XSynchronize (IntPtr display, bool onoff); + + [DllImport ("libX11", EntryPoint="XCreateWindow")] + internal extern static IntPtr XCreateWindow (IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int depth, int xclass, IntPtr visual, SetWindowValuemask valuemask, ref XSetWindowAttributes attributes); + [DllImport ("libX11", EntryPoint="XCreateSimpleWindow")] + internal extern static IntPtr XCreateSimpleWindow (IntPtr display, IntPtr parent, int x, int y, int width, int height, int border_width, int border, int background); + [DllImport ("libX11", EntryPoint="XMapWindow")] + internal extern static int XMapWindow (IntPtr display, IntPtr window); + [DllImport ("libX11", EntryPoint="XUnmapWindow")] + internal extern static int XUnmapWindow (IntPtr display, IntPtr window); + [DllImport ("libX11", EntryPoint="XMapSubwindows")] + internal extern static int XMapSubindows (IntPtr display, IntPtr window); + [DllImport ("libX11", EntryPoint="XUnmapSubwindows")] + internal extern static int XUnmapSubwindows (IntPtr display, IntPtr window); + [DllImport ("libX11", EntryPoint="XRootWindow")] + internal extern static IntPtr XRootWindow (IntPtr display, int screen_number); + [DllImport ("libX11", EntryPoint="XNextEvent")] + internal extern static IntPtr XNextEvent (IntPtr display, ref XEvent xevent); + [DllImport ("libX11")] + internal extern static int XConnectionNumber (IntPtr diplay); + [DllImport ("libX11")] + internal extern static int XPending (IntPtr diplay); + [DllImport ("libX11")] + internal extern static bool XCheckWindowEvent (IntPtr display, IntPtr window, EventMask mask, ref XEvent xevent); + [DllImport ("libX11")] + internal extern static bool XCheckMaskEvent (IntPtr display, EventMask mask, ref XEvent xevent); + [DllImport ("libX11", EntryPoint="XSelectInput")] + internal extern static IntPtr XSelectInput (IntPtr display, IntPtr window, EventMask mask); + + [DllImport ("libX11", EntryPoint="XReparentWindow")] + internal extern static int XReparentWindow (IntPtr display, IntPtr window, IntPtr parent, int x, int y); + [DllImport ("libX11", EntryPoint="XMoveResizeWindow")] + internal extern static int XMoveResizeWindow (IntPtr display, IntPtr window, int x, int y, int width, int height); + + [DllImport ("libX11", EntryPoint="XResizeWindow")] + internal extern static int XResizeWindow (IntPtr display, IntPtr window, int width, int height); + + [DllImport ("libX11", EntryPoint="XGetWindowAttributes")] + internal extern static int XGetWindowAttributes (IntPtr display, IntPtr window, ref XWindowAttributes attributes); + + [DllImport ("libX11", EntryPoint="XFlush")] + internal extern static int XFlush (IntPtr display); + + [DllImport ("libX11", EntryPoint="XSetWMName")] + internal extern static int XSetWMName (IntPtr display, IntPtr window, ref XTextProperty text_prop); + + [DllImport ("libX11", EntryPoint="XStoreName")] + internal extern static int XStoreName (IntPtr display, IntPtr window, string window_name); + + [DllImport ("libX11", EntryPoint="XFetchName")] + internal extern static int XFetchName (IntPtr display, IntPtr window, ref IntPtr window_name); + + [DllImport ("libX11", EntryPoint="XSendEvent")] + internal extern static int XSendEvent (IntPtr display, IntPtr window, bool propagate, EventMask event_mask, ref XEvent send_event); + + [DllImport ("libX11", EntryPoint="XQueryTree")] + internal extern static int XQueryTree (IntPtr display, IntPtr window, out IntPtr root_return, out IntPtr parent_return, out IntPtr children_return, out int nchildren_return); + + [DllImport ("libX11", EntryPoint="XFree")] + internal extern static int XFree (IntPtr data); + + [DllImport ("libX11", EntryPoint="XRaiseWindow")] + internal extern static int XRaiseWindow (IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XLowerWindow")] + internal extern static uint XLowerWindow (IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XConfigureWindow")] + internal extern static uint XConfigureWindow (IntPtr display, IntPtr window, ChangeWindowFlags value_mask, ref XWindowChanges values); + + [DllImport ("libX11", EntryPoint="XInternAtom")] + internal extern static int XInternAtom (IntPtr display, string atom_name, bool only_if_exists); + + [DllImport ("libX11", EntryPoint="XSetWMProtocols")] + internal extern static int XSetWMProtocols (IntPtr display, IntPtr window, uint[] protocols, int count); + + [DllImport ("libX11", EntryPoint="XGrabPointer")] + internal extern static int XGrabPointer (IntPtr display, IntPtr window, bool owner_events, EventMask event_mask, GrabMode pointer_mode, GrabMode keyboard_mode, IntPtr confine_to, uint cursor, uint timestamp); + + [DllImport ("libX11", EntryPoint="XUngrabPointer")] + internal extern static int XUngrabPointer (IntPtr display, uint timestamp); + + [DllImport ("libX11", EntryPoint="XQueryPointer")] + internal extern static bool XQueryPointer (IntPtr display, IntPtr window, out IntPtr root, out IntPtr child, out int root_x, out int root_y, out int win_x, out int win_y, out int keys_buttons); + + [DllImport ("libX11", EntryPoint="XTranslateCoordinates")] + internal extern static bool XTranslateCoordinates (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, out int intdest_x_return, out int dest_y_return, out IntPtr child_return); + + [DllImport ("libX11", EntryPoint="XWarpPointer")] + internal extern static uint XWarpPointer (IntPtr display, IntPtr src_w, IntPtr dest_w, int src_x, int src_y, uint src_width, uint src_height, int dest_x, int dest_y); + + [DllImport ("libX11", EntryPoint="XClearWindow")] + internal extern static int XClearWindow (IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XClearArea")] + internal extern static int XClearArea (IntPtr display, IntPtr window, int x, int y, int width, int height, bool exposures); + + // Colormaps + [DllImport ("libX11", EntryPoint="XDefaultScreenOfDisplay")] + internal extern static IntPtr XDefaultScreenOfDisplay (IntPtr display); + + [DllImport ("libX11", EntryPoint="XScreenNumberOfScreen")] + internal extern static int XScreenNumberOfScreen (IntPtr display, IntPtr Screen); + + [DllImport ("libX11", EntryPoint="XDefaultVisual")] + internal extern static IntPtr XDefaultVisual (IntPtr display, int screen_number); + + [DllImport ("libX11", EntryPoint="XDefaultDepth")] + internal extern static uint XDefaultDepth (IntPtr display, int screen_number); + + [DllImport ("libX11", EntryPoint="XDefaultColormap")] + internal extern static IntPtr XDefaultColormap (IntPtr display, int screen_number); + + [DllImport ("libX11", EntryPoint="XLookupColor")] + internal extern static int XLookupColor (IntPtr display, IntPtr Colormap, string Coloranem, ref XColor exact_def_color, ref XColor screen_def_color); + + [DllImport ("libX11", EntryPoint="XAllocColor")] + internal extern static int XAllocColor (IntPtr display, IntPtr Colormap, ref XColor colorcell_def); + + [DllImport ("libX11", EntryPoint="XSetTransientForHint")] + internal extern static int XSetTransientForHint (IntPtr display, IntPtr window, IntPtr prop_window); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty (IntPtr display, IntPtr window, int property, int type, int format, PropertyMode mode, ref MotifWmHints data, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty (IntPtr display, IntPtr window, int property, Atom format, int type, PropertyMode mode, uint[] atoms, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty (IntPtr display, IntPtr window, int property, Atom format, int type, PropertyMode mode, ref uint value, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty (IntPtr display, IntPtr window, int property, int format, int type, PropertyMode mode, uint[] atoms, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty (IntPtr display, IntPtr window, int property, int format, int type, PropertyMode mode, IntPtr atoms, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty")] + internal extern static int XChangeProperty (IntPtr display, IntPtr window, int property, Atom format, int type, PropertyMode mode, IntPtr atoms, int nelements); + + [DllImport ("libX11", EntryPoint="XChangeProperty", CharSet=CharSet.Ansi)] + internal extern static int XChangeProperty (IntPtr display, IntPtr window, int property, int type, int format, PropertyMode mode, string text, int text_length); + + [DllImport ("libX11", EntryPoint="XDeleteProperty")] + internal extern static int XDeleteProperty (IntPtr display, IntPtr window, int property); + + // Drawing + [DllImport ("libX11", EntryPoint="XCreateGC")] + internal extern static IntPtr XCreateGC (IntPtr display, IntPtr window, GCFunction valuemask, ref XGCValues values); + + [DllImport ("libX11", EntryPoint="XFreeGC")] + internal extern static int XFreeGC (IntPtr display, IntPtr gc); + + [DllImport ("libX11", EntryPoint="XSetFunction")] + internal extern static int XSetFunction (IntPtr display, IntPtr gc, GXFunction function); + + [DllImport ("libX11", EntryPoint="XDrawLine")] + internal extern static int XDrawLine (IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int x2, int y2); + + [DllImport ("libX11", EntryPoint="XDrawRectangle")] + internal extern static int XDrawRectangle (IntPtr display, IntPtr drawable, IntPtr gc, int x1, int y1, int width, int height); + + [DllImport ("libX11", EntryPoint="XSetWindowBackground")] + internal extern static int XSetWindowBackground (IntPtr display, IntPtr window, IntPtr background); + + [DllImport ("libX11", EntryPoint="XCopyArea")] + internal extern static int XCopyArea (IntPtr display, IntPtr src, IntPtr dest, IntPtr gc, int src_x, int src_y, int width, int height, int dest_x, int dest_y); + + [DllImport ("libX11", EntryPoint="XGetAtomName")] + internal extern static string XGetAtomName (IntPtr display, int atom); + + [DllImport ("libX11", EntryPoint="XGetWindowProperty")] + internal extern static int XGetWindowProperty (IntPtr display, IntPtr window, int atom, int long_offset, int long_length, bool delete, Atom req_type, out Atom actual_type, out int actual_format, out int nitems, out int bytes_after, ref IntPtr prop); + + [DllImport ("libX11", EntryPoint="XSetInputFocus")] + internal extern static int XSetInputFocus (IntPtr display, IntPtr window, RevertTo revert_to, IntPtr time); + + [DllImport ("libX11", EntryPoint="XIconifyWindow")] + internal extern static int XIconifyWindow (IntPtr display, IntPtr window, int screen_number); + + [DllImport ("libX11", EntryPoint="XDefineCursor")] + internal extern static int XDefineCursor (IntPtr display, IntPtr window, IntPtr cursor); + + [DllImport ("libX11", EntryPoint="XUndefineCursor")] + internal extern static int XUndefineCursor (IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XFreeCursor")] + internal extern static int XFreeCursor (IntPtr display, IntPtr cursor); + + [DllImport ("libX11", EntryPoint="XCreateFontCursor")] + internal extern static IntPtr XCreateFontCursor (IntPtr display, CursorFontShape shape); + + [DllImport ("libX11", EntryPoint="XCreatePixmapCursor")] + internal extern static IntPtr XCreatePixmapCursor (IntPtr display, IntPtr source, IntPtr mask, ref XColor foreground_color, ref XColor background_color, int x_hot, int y_hot); + + [DllImport ("libX11", EntryPoint="XCreatePixmapFromBitmapData")] + internal extern static IntPtr XCreatePixmapFromBitmapData (IntPtr display, IntPtr drawable, byte[] data, int width, int height, IntPtr fg, IntPtr bg, int depth); + + [DllImport ("libX11", EntryPoint="XFreePixmap")] + internal extern static IntPtr XFreePixmap (IntPtr display, IntPtr pixmap); + + [DllImport ("libX11", EntryPoint="XWhitePixel")] + internal extern static IntPtr XWhitePixel (IntPtr display, int screen_no); + + [DllImport ("libX11", EntryPoint="XBlackPixel")] + internal extern static IntPtr XBlackPixel (IntPtr display, int screen_no); + + [DllImport ("libX11", EntryPoint="XGrabServer")] + internal extern static void XGrabServer (IntPtr display); + + [DllImport ("libX11", EntryPoint="XUngrabServer")] + internal extern static void XUngrabServer (IntPtr display); + + [DllImport ("libX11", EntryPoint="XSetWMNormalHints")] + internal extern static void XSetWMNormalHints (IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport ("libX11", EntryPoint="XSetZoomHints")] + internal extern static void XSetZoomHints (IntPtr display, IntPtr window, ref XSizeHints hints); + + [DllImport ("libX11", EntryPoint="XSetWMHints")] + internal extern static void XSetWMHints (IntPtr display, IntPtr window, ref XWMHints wmhints); + + [DllImport ("libX11", EntryPoint="XSync")] + internal extern static void XSync (IntPtr display, IntPtr window); + + [DllImport ("libX11", EntryPoint="XGetIconSizes")] + internal extern static int XGetIconSizes (IntPtr display, IntPtr window, out IntPtr size_list, out int count); + + [DllImport ("libX11", EntryPoint="XSetErrorHandler")] + internal extern static IntPtr XSetErrorHandler (XErrorHandler error_handler); + + [DllImport ("libX11", EntryPoint="XGetErrorText")] + internal extern static IntPtr XGetErrorText (IntPtr display, byte code, StringBuilder buffer, int length); + + [DllImport ("libX11", EntryPoint="XInitThreads")] + internal extern static int XInitThreads (); + + [DllImport ("libX11", EntryPoint="XConvertSelection")] + internal extern static int XConvertSelection (IntPtr display, int selection, int target, int property, IntPtr requestor, IntPtr time); + + [DllImport ("libX11", EntryPoint="XGetSelectionOwner")] + internal extern static IntPtr XGetSelectionOwner (IntPtr display, int selection); + + [DllImport ("libX11", EntryPoint="XSetSelectionOwner")] + internal extern static int XSetSelectionOwner (IntPtr display, int selection, IntPtr owner, IntPtr time); + + [DllImport ("libX11", EntryPoint="XSetPlaneMask")] + internal extern static int XSetPlaneMask (IntPtr display, IntPtr gc, uint mask); + + [DllImport ("libX11", EntryPoint="XSetForeground")] + internal extern static int XSetForeground (IntPtr display, IntPtr gc, uint foreground); + + [DllImport ("libX11", EntryPoint="XSetBackground")] + internal extern static int XSetBackground (IntPtr display, IntPtr gc, uint background); + + #endregion + + #region gdk imports + [DllImport("libgdk-x11-2.0.so")] + static extern bool gdk_init_check (IntPtr argc, IntPtr argv); + + [DllImport("libgdk-x11-2.0.so")] + internal static extern IntPtr gdk_x11_display_get_xdisplay (IntPtr display); + + [DllImport("libgdk-x11-2.0.so")] + internal static extern IntPtr gdk_display_get_default (); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_pixmap_new (IntPtr drawable, int width, int height, int depth); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_x11_drawable_get_xid (IntPtr gdkdrawable); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_draw_drawable (IntPtr drawable_dest, IntPtr gdk_gc, IntPtr drawable_src, int xsrc, int ysrc, int xdest, int ydest, int width, int height); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_gc_new (IntPtr drawable); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_window_foreign_new (IntPtr anid); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_x11_lookup_xdisplay (IntPtr xdisplay); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_display_close (IntPtr display); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_display_beep (IntPtr display); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_display_sync (IntPtr display); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_get_default_root_window (); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_colormap_get_system (); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_x11_colormap_get_xcolormap (IntPtr gdk_colormap); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_destroy (IntPtr gdk_window); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_x11_grab_server (); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_x11_ungrab_server (); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_display_flush (IntPtr gdk_display); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_iconify (IntPtr gdk_window); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_deiconify (IntPtr gdk_window); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_set_decorations (IntPtr gdk_window, int decorations); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_screen_get_default (); + + [DllImport("libgdk-x11-2.0.so")] + static extern int gdk_screen_get_number (IntPtr gdk_screen); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_window_lookup (IntPtr anid); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_window_new (IntPtr gdk_parent, ref GdkWindowAttr gdk_window_attributes, int attributes_mask); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_set_events (IntPtr gdk_window, int event_mask); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_show (IntPtr window); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_set_title (IntPtr gdk_window, string title); + + [DllImport("libgdk-x11-2.0.so")] + static extern int gdk_window_get_origin (IntPtr gdk_window, out int x, out int y); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_window_get_geometry (IntPtr gdk_window, out int x, out int y, out int width, out int height, out int depth); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_property_change (IntPtr gdk_window, /*GdkAtom*/IntPtr property, /*GdkAtom*/IntPtr type, int format, int gdk_prop_mode, /*const guchar **/ IntPtr data, int nelements); + + [DllImport("libgdk-x11-2.0.so")] + static extern IntPtr gdk_window_get_parent (IntPtr gdk_window); + + [DllImport("libgdk-x11-2.0.so")] + static extern void gdk_display_get_maximal_cursor_size (IntPtr gdk_display, out uint width, out uint height); + + [DllImport("libgdk-x11-2.0.so")] + static extern int gdk_visual_get_best_depth (); + #endregion + + #region gobject imports + [DllImport("libglib-2.0.so")] + static extern void g_free (IntPtr mem); + + [DllImport("libgobject-2.0.so")] + static extern void g_object_unref (IntPtr nativeObject); + #endregion + } +} |
