Hi,
I need to implement in a custom control on-the-spot Input method
editing. The Control is driven by WinForm events - although all the
drawing is completely custom.
I have had a first stab at adding support to mono's X11 classes to allow
a custom control to get at this information. I've attached the (not
finished) initial patch for comments and suggestions.
I have also attached a small test program which I use for testing this.
For which I run it with:
XMODIFIERS="@im=SCIM"
MONO_WINFORMS_XIM_STYLE=on-the-spot mono TestApp.exe
I have been testing with Scim using 'Amharic' and 'Chinese (Simplified)
- Wubi' (note: mono winforms seems to have problems rending Amharic -
but it's good enough for testing).
What I would like to know is what are the changes of accepting a patch
which allows custom controls to hook into mono's XIM use?
I have currently put externally access-able classes in a
System.Windows.Forms.X11 namespace, it this the right thing to do?
Another thing which I think may be necessary is the ability to have a
control specific xim or a least to be able to switch xim in application
code ( currently it seems to be one per applications) This would allow
some controls to do on-the-spot and some to do an alternative method
like off-the-spot.
Thanks
Tom
Index: class/Managed.Windows.Forms/System.Windows.Forms/X11Keyboard.cs
===================================================================
--- class/Managed.Windows.Forms/System.Windows.Forms/X11Keyboard.cs (revision 144586)
+++ class/Managed.Windows.Forms/System.Windows.Forms/X11Keyboard.cs (working copy)
@@ -32,11 +32,91 @@
//
using System;
using System.Collections;
+using System.Diagnostics;
using System.Drawing;
using System.Text;
using System.Globalization;
using System.Runtime.InteropServices;
+namespace System.Windows.Forms.X11
+{
+ public class XIM
+ {
+ /// <summary>
+ /// Allows user to request preedit notifications.
+ /// </summary>
+ public interface IPreedit
+ {
+ int PreeditStart(IntPtr xic, IntPtr clientData, IntPtr callData);
+ int PreeditDone(IntPtr xic, IntPtr clientData, IntPtr callData);
+ int PreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData);
+ int PreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData);
+ }
+
+ /// <summary>
+ /// Allow Applications to register interest in X Input methods preedit callbacks
+ /// TODO: Generalize this to allow specifiying window/control
+ /// </summary>
+ public static void RegisterPreeditNotification(IPreedit preedit)
+ {
+ System.Windows.Forms.XplatUIX11.Keyboard.RegisterPreeditNotification(preedit);
+ }
+
+ /// <summary>
+ /// Converts IntPtr if XIMPreeditDrawCallbackStruct into an easier to use form
+ /// </summary>
+ public class PreeditDrawInfo
+ {
+ public PreeditDrawInfo(IntPtr ximPreeditDrawPtr)
+ {
+ if (ximPreeditDrawPtr != IntPtr.Zero)
+ {
+ XIMPreeditDrawCallbackStruct preeditStruct = (XIMPreeditDrawCallbackStruct) Marshal.PtrToStructure (ximPreeditDrawPtr, typeof (XIMPreeditDrawCallbackStruct));
+ this.Caret = preeditStruct.Caret;
+ this.ChangeFirst = preeditStruct.ChangeFirst;
+ this.ChangeLength = preeditStruct.ChangeLength;
+ if (preeditStruct.Text != IntPtr.Zero)
+ {
+ XIMText text = (XIMText) Marshal.PtrToStructure(preeditStruct.Text, typeof(XIMText));
+ {
+ this.Length = text.Length;
+ if (text.Feedback != IntPtr.Zero)
+ {
+ XIMFeedbackStruct Feedback = (XIMFeedbackStruct) Marshal.PtrToStructure(text.Feedback, typeof(XIMFeedbackStruct));
+ this.Feedback = Feedback.FeedbackMask;
+ }
+ this.EncodingIsWChar = text.EncodingIsWChar;
+ if (text.String != IntPtr.Zero)
+ {
+ if (text.EncodingIsWChar)
+ this.String = Marshal.PtrToStringUni(text.String);
+ else
+ this.String = Marshal.PtrToStringAuto(text.String);
+ }
+ }
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return String.Format("PreeditDrawInfo: Carret = {0} ChangeFirst = {1} ChangeLength = {2} Length = {3} Feedback = {4} EncodingIsWChar = {5} String = {6}",
+ Caret, ChangeFirst, ChangeLength, Length, Feedback, EncodingIsWChar, String);
+ }
+
+ public int Caret = 0;
+ public int ChangeFirst = 0;
+ public int ChangeLength = 0;
+ public ushort Length = 0;
+ public ulong Feedback = 0;
+ public bool EncodingIsWChar = false;
+ public String String = string.Empty;
+ }
+
+ }
+
+}
+
namespace System.Windows.Forms {
internal class X11Keyboard : IDisposable {
@@ -63,6 +143,8 @@
private int NumLockMask;
private int AltGrMask;
+
+ private System.Windows.Forms.X11.XIM.IPreedit ximPreedit;
public X11Keyboard (IntPtr display, IntPtr clientWindow)
{
@@ -1001,9 +1083,14 @@
}
}
+ public void RegisterPreeditNotification(System.Windows.Forms.X11.XIM.IPreedit preedit)
+ {
+ ximPreedit = preedit;
+ }
+
private IntPtr CreateOnTheSpotXic (IntPtr window, IntPtr xim)
{
- callbackContext = new XIMCallbackContext (window);
+ callbackContext = new XIMCallbackContext (window, ximPreedit);
return callbackContext.CreateXic (window, xim);
}
@@ -1012,8 +1099,10 @@
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;
+
+ System.Windows.Forms.X11.XIM.IPreedit preeditApplicationCallback; // allows user application to register for preedit methods.
- public XIMCallbackContext (IntPtr clientWindow)
+ public XIMCallbackContext (IntPtr clientWindow, System.Windows.Forms.X11.XIM.IPreedit preedit)
{
startCB = new XIMCallback (IntPtr.Zero, DoPreeditStart);
doneCB = new XIMCallback (IntPtr.Zero, DoPreeditDone);
@@ -1027,6 +1116,8 @@
pDoneCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDoneCallback);
pDrawCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditDrawCallback);
pCaretCBN = Marshal.StringToHGlobalAnsi (XNames.XNPreeditCaretCallback);
+
+ preeditApplicationCallback = preedit;
}
~XIMCallbackContext ()
@@ -1052,27 +1143,33 @@
int DoPreeditStart (IntPtr xic, IntPtr clientData, IntPtr callData)
{
- Console.WriteLine ("DoPreeditStart");
+ Debug.WriteLine ("DoPreeditStart");
+ if (preeditApplicationCallback != null)
+ return preeditApplicationCallback.PreeditStart(xic, clientData, callData);
return 100;
}
int DoPreeditDone (IntPtr xic, IntPtr clientData, IntPtr callData)
{
- Console.WriteLine ("DoPreeditDone");
+ Debug.WriteLine ("DoPreeditDone");
+ if (preeditApplicationCallback != null)
+ return preeditApplicationCallback.PreeditDone(xic, clientData, callData);
return 0;
}
int DoPreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
{
- Console.WriteLine ("DoPreeditDraw");
- //XIMPreeditDrawCallbackStruct cd = (XIMPreeditDrawCallbackStruct) Marshal.PtrToStructure (callData, typeof (XIMPreeditDrawCallbackStruct));
+ Debug.WriteLine ("DoPreeditDraw");
+ if (preeditApplicationCallback != null)
+ return preeditApplicationCallback.PreeditDraw(xic, clientData, callData);
return 0;
}
int DoPreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
{
- Console.WriteLine ("DoPreeditCaret");
- //XIMPreeditCaretCallbackStruct cd = (XIMPreeditCaretCallbackStruct) Marshal.PtrToStructure (callData, typeof (XIMPreeditCaretCallbackStruct));
+ Debug.WriteLine ("DoPreeditCaret");
+ if (preeditApplicationCallback != null)
+ return preeditApplicationCallback.PreeditCaret(xic, clientData, callData);
return 0;
}
Index: class/Managed.Windows.Forms/System.Windows.Forms/X11Structs.cs
===================================================================
--- class/Managed.Windows.Forms/System.Windows.Forms/X11Structs.cs (revision 144586)
+++ class/Managed.Windows.Forms/System.Windows.Forms/X11Structs.cs (working copy)
@@ -1702,11 +1702,26 @@
gch.Free ();
}
}
+
+ internal enum XIMFeedback
+ {
+ Reverse = 1,
+ Underline = 2,
+ Highlight = 4,
+ Primary = 8,
+ Secondary = 16,
+ Tertiary = 32,
+ }
+ internal struct XIMFeedbackStruct
+ {
+ public ulong FeedbackMask; // one or more of XIMFeedback enum
+ }
+
internal struct XIMText
{
public ushort Length;
- public IntPtr Feedback;
+ public IntPtr Feedback; // to XIMFeedbackStruct
public bool EncodingIsWChar;
public IntPtr String; // it could be either char* or wchar_t*
}
Index: class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs
===================================================================
--- class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs (revision 144586)
+++ class/Managed.Windows.Forms/System.Windows.Forms/XplatUIX11.cs (working copy)
@@ -112,7 +112,7 @@
private static bool wake_waiting;
private static object wake_waiting_lock = new object ();
#endif //
- private static X11Keyboard Keyboard; //
+ internal static X11Keyboard Keyboard; //
private static X11Dnd Dnd;
private static Socket listen; //
private static Socket wake; //
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.X11;
using System.Collections;
/// <summary>
/// Proof on concept implementation of a user defined TextBox that does on-the-spot
/// input method editing.
/// This a number of know flaws - eg. doesn't end preedit when user moves cursor with mouse.
/// Need to run with MONO_WINFORMS_XIM_STYLE=on-the-spot mono
/// </summary>
public class OnTheSpotTextBox : TextBox, XIM.IPreedit
{
string backup; // store text before preedit.
#region IPreedit implementation
public int PreeditStart(IntPtr xic, IntPtr clientData, IntPtr callData)
{
backup = Text;
return 100;
}
public int PreeditDone(IntPtr xic, IntPtr clientData, IntPtr callData)
{
Clear();
AppendText(backup);
return 0;
}
public int PreeditDraw (IntPtr xic, IntPtr clientData, IntPtr callData)
{
XIM.PreeditDrawInfo info = new XIM.PreeditDrawInfo(callData);
if (info.ChangeLength > 0)
{
Clear();
AppendText(backup);
}
if (info.Caret > 0)
{
AppendText(info.String);
}
return 0;
}
public int PreeditCaret (IntPtr xic, IntPtr clientData, IntPtr callData)
{
return 0;
}
#endregion
}
#region Testing OnTheSpotTextBox
public class Form1 : Form
{
private OnTheSpotTextBox textBox1;
private ComboBox comboBox1;
public Form1()
{
InitializeComponent();
}
private IEnumerable Names(Graphics graphics)
{
var families = FontFamily.GetFamilies(graphics);
foreach(FontFamily f in families)
{
string s = f.GetName(0);
yield return s;
}
}
private void ComboBox1_SelectedIndexChanged(object sender,
System.EventArgs e)
{
textBox1.Font = new Font((string)comboBox1.SelectedItem, 10);
}
private void InitializeComponent()
{
this.textBox1 = new OnTheSpotTextBox();
System.Windows.Forms.X11.XIM.RegisterPreeditNotification(textBox1);
this.comboBox1 = new System.Windows.Forms.ComboBox();
comboBox1.Sorted = true;
this.SuspendLayout();
//
// textBox1
//
var graphics = textBox1.CreateGraphics();
foreach (string name in Names(graphics))
comboBox1.Items.Add(name);
this.comboBox1.SelectedIndexChanged +=
new System.EventHandler(ComboBox1_SelectedIndexChanged);
this.textBox1.Font = new Font("AR PL UMing CN", 10);
this.textBox1.AcceptsReturn = true;
this.textBox1.AcceptsTab = true;
this.textBox1.Multiline = true;
this.textBox1.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.textBox1.Top = 30;
this.textBox1.Height = 300;
this.textBox1.Width = 300;
//
// Form1
//
this.ClientSize = new System.Drawing.Size(284, 264);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.comboBox1);
this.Text = "TextBox Example";
this.ResumeLayout(false);
this.PerformLayout();
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
#endregion
_______________________________________________
Mono-devel-list mailing list
[email protected]
http://lists.ximian.com/mailman/listinfo/mono-devel-list