Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2929_PrioritizedGrammarEnumeration/HeuristicLab.ExtLibs/HeuristicLab.AvalonEdit/5.0.1/AvalonEdit-5.0.1/Editing/ImeSupport.cs

Last change on this file was 11700, checked in by jkarder, 10 years ago

#2077: created branch and added first version

File size: 5.7 KB
Line 
1// Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19using System;
20using System.ComponentModel;
21using System.Diagnostics;
22using System.Linq;
23using System.Runtime.CompilerServices;
24using System.Runtime.InteropServices;
25using System.Security;
26using System.Windows;
27using System.Windows.Controls;
28using System.Windows.Input;
29using System.Windows.Interop;
30using System.Windows.Media;
31using System.Windows.Media.TextFormatting;
32using ICSharpCode.AvalonEdit;
33using ICSharpCode.AvalonEdit.Document;
34using ICSharpCode.AvalonEdit.Rendering;
35
36namespace ICSharpCode.AvalonEdit.Editing
37{
38  class ImeSupport
39  {
40    readonly TextArea textArea;
41    IntPtr currentContext;
42    IntPtr previousContext;
43    IntPtr defaultImeWnd;
44    HwndSource hwndSource;
45    EventHandler requerySuggestedHandler; // we need to keep the event handler instance alive because CommandManager.RequerySuggested uses weak references
46    bool isReadOnly;
47   
48    public ImeSupport(TextArea textArea)
49    {
50      if (textArea == null)
51        throw new ArgumentNullException("textArea");
52      this.textArea = textArea;
53      InputMethod.SetIsInputMethodSuspended(this.textArea, textArea.Options.EnableImeSupport);
54      // We listen to CommandManager.RequerySuggested for both caret offset changes and changes to the set of read-only sections.
55      // This is because there's no dedicated event for read-only section changes; but RequerySuggested needs to be raised anyways
56      // to invalidate the Paste command.
57      requerySuggestedHandler = OnRequerySuggested;
58      CommandManager.RequerySuggested += requerySuggestedHandler;
59      textArea.OptionChanged += TextAreaOptionChanged;
60    }
61
62    void OnRequerySuggested(object sender, EventArgs e)
63    {
64      UpdateImeEnabled();
65    }
66   
67    void TextAreaOptionChanged(object sender, PropertyChangedEventArgs e)
68    {
69      if (e.PropertyName == "EnableImeSupport") {
70        InputMethod.SetIsInputMethodSuspended(this.textArea, textArea.Options.EnableImeSupport);
71        UpdateImeEnabled();
72      }
73    }
74   
75    public void OnGotKeyboardFocus(KeyboardFocusChangedEventArgs e)
76    {
77      UpdateImeEnabled();
78    }
79   
80    public void OnLostKeyboardFocus(KeyboardFocusChangedEventArgs e)
81    {
82      if (e.OldFocus == textArea && currentContext != IntPtr.Zero)
83        ImeNativeWrapper.NotifyIme(currentContext);
84      ClearContext();
85    }
86   
87    void UpdateImeEnabled()
88    {
89      if (textArea.Options.EnableImeSupport && textArea.IsKeyboardFocused) {
90        bool newReadOnly = !textArea.ReadOnlySectionProvider.CanInsert(textArea.Caret.Offset);
91        if (hwndSource == null || isReadOnly != newReadOnly) {
92          ClearContext(); // clear existing context (on read-only change)
93          isReadOnly = newReadOnly;
94          CreateContext();
95        }
96      } else {
97        ClearContext();
98      }
99    }
100   
101    void ClearContext()
102    {
103      if (hwndSource != null) {
104        ImeNativeWrapper.ImmAssociateContext(hwndSource.Handle, previousContext);
105        ImeNativeWrapper.ImmReleaseContext(defaultImeWnd, currentContext);
106        currentContext = IntPtr.Zero;
107        defaultImeWnd = IntPtr.Zero;
108        hwndSource.RemoveHook(WndProc);
109        hwndSource = null;
110      }
111    }
112   
113    void CreateContext()
114    {
115      hwndSource = (HwndSource)PresentationSource.FromVisual(this.textArea);
116      if (hwndSource != null) {
117        if (isReadOnly) {
118          defaultImeWnd = IntPtr.Zero;
119          currentContext = IntPtr.Zero;
120        } else {
121          defaultImeWnd = ImeNativeWrapper.ImmGetDefaultIMEWnd(IntPtr.Zero);
122          currentContext = ImeNativeWrapper.ImmGetContext(defaultImeWnd);
123        }
124        previousContext = ImeNativeWrapper.ImmAssociateContext(hwndSource.Handle, currentContext);
125        hwndSource.AddHook(WndProc);
126        // UpdateCompositionWindow() will be called by the caret becoming visible
127       
128        var threadMgr = ImeNativeWrapper.GetTextFrameworkThreadManager();
129        if (threadMgr != null) {
130          // Even though the docu says passing null is invalid, this seems to help
131          // activating the IME on the default input context that is shared with WPF
132          threadMgr.SetFocus(IntPtr.Zero);
133        }
134      }
135    }
136   
137    IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
138    {
139      switch (msg) {
140        case ImeNativeWrapper.WM_INPUTLANGCHANGE:
141          // Don't mark the message as handled; other windows
142          // might want to handle it as well.
143         
144          // If we have a context, recreate it
145          if (hwndSource != null) {
146            ClearContext();
147            CreateContext();
148          }
149          break;
150        case ImeNativeWrapper.WM_IME_COMPOSITION:
151          UpdateCompositionWindow();
152          break;
153      }
154      return IntPtr.Zero;
155    }
156   
157    public void UpdateCompositionWindow()
158    {
159      if (currentContext != IntPtr.Zero) {
160        ImeNativeWrapper.SetCompositionFont(hwndSource, currentContext, textArea);
161        ImeNativeWrapper.SetCompositionWindow(hwndSource, currentContext, textArea);
162      }
163    }
164  }
165}
Note: See TracBrowser for help on using the repository browser.