Bug 768742 - Implement Full OS/2 IME support. r=daveryeo
authorKO Myung-Hun <komh@chollian.net>
Wed, 20 Jun 2012 19:09:05 +0900
changeset 98314 78353003288e
parent 98313 936ee90e6e54
child 98315 5c30c255b490
push id23040
push userryanvm@gmail.com
push dateWed, 04 Jul 2012 16:48:55 +0000
treeherdermozilla-central@f4a40f677391 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaveryeo
bugs768742
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 768742 - Implement Full OS/2 IME support. r=daveryeo
widget/os2/nsWidgetFactory.cpp
widget/os2/nsWindow.cpp
widget/os2/nsWindow.h
--- a/widget/os2/nsWidgetFactory.cpp
+++ b/widget/os2/nsWidgetFactory.cpp
@@ -21,22 +21,21 @@
  * 03/20/2001       achimha@innotek.de     Added class for embedded module init
  * 12/16/2001       pavlov@netscape.com    Removed timer stuff
  */
 
 #include "mozilla/ModuleUtils.h"
 #include "nsIModule.h"
 #include "nsCOMPtr.h"
 #include "nsWidgetsCID.h"
-
 // class definition headers
+#include "nsWindow.h"
 #include "nsAppShell.h"
 #include "nsAppShellSingleton.h"
 #include "nsBidiKeyboard.h"
-#include "nsWindow.h"
 #include "nsDragService.h"
 #include "nsIFile.h"
 #include "nsFilePicker.h"
 #include "nsLookAndFeel.h"
 #include "nsSound.h"
 
 // Drag & Drop, Clipboard
 #include "nsClipboard.h"
--- a/widget/os2/nsWindow.cpp
+++ b/widget/os2/nsWindow.cpp
@@ -17,16 +17,17 @@
  *  - Plugin Operations
  *  - Top-level (frame window) Operations
  *  - Mouse Pointers
  *  - Rollup Event Handlers
  *  - nsWindow's Window Procedure
  *  - Window Message Handlers
  *  - Drag & Drop - Target methods
  *  - Keyboard Handlers
+ *  - IME
  *  - Event Dispatch
  *
  */
 //=============================================================================
 
 #include "nsWindow.h"
 #include "os2FrameWindow.h"
 #include "gfxContext.h"
@@ -37,21 +38,20 @@
 #include "nsGfxCIID.h"
 #include "nsHashKeys.h"
 #include "nsIRollupListener.h"
 #include "nsIScreenManager.h"
 #include "nsOS2Uni.h"
 #include "nsTHashtable.h"
 #include "nsGkAtoms.h"
 #include "wdgtos2rc.h"
-
 #include "mozilla/Preferences.h"
+#include <os2im.h>
 
 using namespace mozilla;
-
 //=============================================================================
 //  Macros
 //=============================================================================
 
 // Drag and Drop
 
 // d&d flags - actions that might cause problems during d&d
 #define ACTION_PAINT    1
@@ -158,19 +158,26 @@ static HPOINTER     sPtrArray[IDC_COUNT]
 static POINTS       sLastButton1Down = {0,0};
 
 // set when any nsWindow is being dragged over
 static PRUint32     sDragStatus = 0;
 
 #ifdef DEBUG_FOCUS
   int currentWindowIdentifier = 0;
 #endif
+// IME stuffs
+static HMODULE sIm32Mod = NULLHANDLE;
+static APIRET (APIENTRY *spfnImGetInstance)(HWND, PHIMI);
+static APIRET (APIENTRY *spfnImReleaseInstance)(HWND, HIMI);
+static APIRET (APIENTRY *spfnImGetConversionString)(HIMI, ULONG, PVOID,
+                                                    PULONG);
+static APIRET (APIENTRY *spfnImGetResultString)(HIMI, ULONG, PVOID, PULONG);
+static APIRET (APIENTRY *spfnImRequestIME)(HIMI, ULONG, ULONG, ULONG);
 
 //-----------------------------------------------------------------------------
-
 static PRUint32     WMChar2KeyCode(MPARAM mp1, MPARAM mp2);
 
 //=============================================================================
 //  nsWindow Create / Destroy
 //=============================================================================
 
 nsWindow::nsWindow() : nsBaseWidget()
 {
@@ -184,17 +191,17 @@ nsWindow::nsWindow() : nsBaseWidget()
   mIsDestroying       = false;
   mInSetFocus         = false;
   mNoPaint            = false;
   mDragHps            = 0;
   mDragStatus         = 0;
   mClipWnd            = 0;
   mCssCursorHPtr      = 0;
   mThebesSurface      = 0;
-
+  mIsComposing        = false;
   if (!gOS2Flags) {
     InitGlobals();
   }
 }
 
 //-----------------------------------------------------------------------------
 
 nsWindow::~nsWindow()
@@ -269,19 +276,59 @@ void nsWindow::InitGlobals()
   // This is ugly. The Thinkpad TrackPoint driver checks to see whether
   // or not a window actually has a scroll bar as a child before sending
   // it scroll messages. Needless to say, no Mozilla window has real scroll
   // bars. So if you have the "os2.trackpoint" preference set, we put an
   // invisible scroll bar on every child window so we can scroll.
   if (Preferences::GetBool("os2.trackpoint", false)) {
     gOS2Flags |= kIsTrackPoint;
   }
+
+  InitIME();
 }
 
 //-----------------------------------------------------------------------------
+// Determine whether to use IME
+static
+void InitIME()
+{
+  if (!getenv("MOZ_IME_OVERTHESPOT")) {
+    CHAR szName[CCHMAXPATH];
+    ULONG rc;
+
+    rc = DosLoadModule(szName, sizeof(szName), "os2im", &sIm32Mod);
+
+    if (!rc)
+      rc = DosQueryProcAddr(sIm32Mod, 104, NULL,
+                            (PFN *)&spfnImGetInstance);
+
+    if (!rc)
+      rc = DosQueryProcAddr(sIm32Mod, 106, NULL,
+                            (PFN *)&spfnImReleaseInstance);
+
+    if (!rc)
+      rc = DosQueryProcAddr(sIm32Mod, 118, NULL,
+                            (PFN *)&spfnImGetConversionString);
+
+    if (!rc)
+      rc = DosQueryProcAddr(sIm32Mod, 122, NULL,
+                            (PFN *)&spfnImGetResultString);
+
+    if (!rc)
+      rc = DosQueryProcAddr(sIm32Mod, 131, NULL,
+                            (PFN *)&spfnImRequestIME);
+
+    if (rc) {
+      DosFreeModule(sIm32Mod);
+
+      sIm32Mod = NULLHANDLE;
+    }
+  }
+}
+//-----------------------------------------------------------------------------
 // Release Module-level variables.
 
 // static
 void nsWindow::ReleaseGlobals()
 {
   for (int i = 0; i < IDC_COUNT; i++) {
     WinDestroyPointer(sPtrArray[i]);
   }
@@ -1836,16 +1883,20 @@ MRESULT nsWindow::ProcessMessage(ULONG m
     case DM_DROPHELP:
       OnDragDropMsg(msg, mp1, mp2, mresult);
       isDone = true;
       break;
 
     case WM_QUERYCONVERTPOS:
       isDone = OnQueryConvertPos(mp1, mresult);
       break;
+
+    case WM_IMEREQUEST:
+      isDone = OnImeRequest(mp1, mp2);
+      break;
   }
   // If an event handler signalled that we should consume the event,
   // return.  Otherwise, pass it on to the default wndproc.
   if (!isDone) {
     mresult = WinDefWindowProc(mWnd, msg, mp1, mp2);
   }
 
   return mresult;
@@ -2411,16 +2462,166 @@ bool nsWindow::OnQueryConvertPos(MPARAM 
   pCursorPos->xRight = pCursorPos->xLeft + caret.mReply.mRect.width;
   pCursorPos->yTop = pCursorPos->yBottom + caret.mReply.mRect.height;
   NS2PM(*pCursorPos);
 
   mresult = (MRESULT)QCP_CONVERT;
 
   return true;
 }
+bool nsWindow::ImeResultString(HIMI himi)
+{
+  PCHAR pBuf;
+  ULONG ulBufLen;
+
+  // Get a buffer size
+  ulBufLen = 0;
+  if (spfnImGetResultString(himi, IMR_RESULT_RESULTSTRING, NULL, &ulBufLen))
+    return false;
+
+  pBuf = new CHAR[ulBufLen];
+  if (!pBuf)
+    return false;
+
+  if (spfnImGetResultString(himi, IMR_RESULT_RESULTSTRING, pBuf,
+                            &ulBufLen)) {
+    delete pBuf;
+
+    return false;
+  }
+
+  if (!mIsComposing) {
+    nsCompositionEvent start(true, NS_COMPOSITION_START, this);
+    InitEvent(start);
+    DispatchWindowEvent(&start);
+
+    mIsComposing = true;
+  }
+
+  nsAutoChar16Buffer outBuf;
+  PRInt32 outBufLen;
+  MultiByteToWideChar(0, pBuf, ulBufLen, outBuf, outBufLen);
+
+  delete pBuf;
+
+  nsTextEvent text(true, NS_TEXT_TEXT, this);
+  InitEvent(text);
+  text.theText = outBuf.Elements();
+  DispatchWindowEvent(&text);
+
+  nsCompositionEvent end(true, NS_COMPOSITION_END, this);
+  InitEvent(end);
+  end.data = text.theText;
+  DispatchWindowEvent(&end);
+  mIsComposing = false;
+
+  return true;
+}
+
+bool nsWindow::ImeConversionString(HIMI himi)
+{
+  PCHAR pBuf;
+  ULONG ulBufLen;
+
+  // Get a buffer size
+  ulBufLen = 0;
+  if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONSTRING, NULL,
+                                &ulBufLen))
+    return false;
+
+  pBuf = new CHAR[ulBufLen];
+  if (!pBuf)
+    return false;
+
+  if (spfnImGetConversionString(himi, IMR_CONV_CONVERSIONSTRING, pBuf,
+                                &ulBufLen)) {
+    delete pBuf;
+
+    return false;
+  }
+
+  if (!mIsComposing) {
+    nsCompositionEvent start(true, NS_COMPOSITION_START, this);
+    InitEvent(start);
+    DispatchWindowEvent(&start);
+
+    mIsComposing = true;
+  }
+
+  nsAutoChar16Buffer outBuf;
+  PRInt32 outBufLen;
+  MultiByteToWideChar(0, pBuf, ulBufLen, outBuf, outBufLen);
+
+  delete pBuf;
+
+  nsAutoTArray<nsTextRange, 4> textRanges;
+
+  // Is there a conversion string ?
+  if (outBufLen) {
+    nsTextRange newRange;
+    newRange.mStartOffset = 0;
+    newRange.mEndOffset = outBufLen;
+    newRange.mRangeType = NS_TEXTRANGE_SELECTEDRAWTEXT;
+    textRanges.AppendElement(newRange);
+
+    newRange.mStartOffset = outBufLen;
+    newRange.mEndOffset = newRange.mStartOffset;
+    newRange.mRangeType = NS_TEXTRANGE_CARETPOSITION;
+    textRanges.AppendElement(newRange);
+  }
+
+  nsTextEvent text(true, NS_TEXT_TEXT, this);
+  InitEvent(text);
+  text.theText = outBuf.Elements();
+  text.rangeArray = textRanges.Elements();
+  text.rangeCount = textRanges.Length();
+  DispatchWindowEvent(&text);
+
+  if (outBufLen) {
+    nsCompositionEvent update(true, NS_COMPOSITION_UPDATE, this);
+    InitEvent(update);
+    update.data = text.theText;
+    DispatchWindowEvent(&update);
+  } else {  // IME conversion was canceled ?
+    nsCompositionEvent end(true, NS_COMPOSITION_END, this);
+    InitEvent(end);
+    end.data = text.theText;
+    DispatchWindowEvent(&end);
+
+    mIsComposing = false;
+  }
+
+  return true;
+}
+
+bool nsWindow::OnImeRequest(MPARAM mp1, MPARAM mp2)
+{
+  HIMI himi;
+  bool rc;
+
+  if (!sIm32Mod)
+    return false;
+
+  if (SHORT1FROMMP(mp1) != IMR_CONVRESULT)
+    return false;
+
+  if (spfnImGetInstance(mWnd, &himi))
+    return false;
+
+  if (LONGFROMMP(mp2) & IMR_RESULT_RESULTSTRING)
+    rc = ImeResultString(himi);
+  else if (LONGFROMMP(mp2) & IMR_CONV_CONVERSIONSTRING)
+    rc = ImeConversionString(himi);
+  else
+    rc = true;
+
+  spfnImReleaseInstance(mWnd, himi);
+
+  return rc;
+}
 
 //-----------------------------------------------------------------------------
 // Key handler.  Specs for the various text messages are really confused;
 // see other platforms for best results of how things are supposed to work.
 //
 // Perhaps more importantly, the main man listening to these events
 // (besides random bits of javascript) is ender -- see
 // mozilla/editor/base/nsEditorEventListeners.cpp.
@@ -2835,16 +3036,24 @@ bool nsWindow::DispatchMouseEvent(PRUint
 {
   NS_ENSURE_TRUE(aEventType, false);
 
   nsMouseEvent event(true, aEventType, this, nsMouseEvent::eReal,
                      aIsContextMenuKey
                      ? nsMouseEvent::eContextMenuKey
                      : nsMouseEvent::eNormal);
   event.button = aButton;
+  if (aEventType == NS_MOUSE_BUTTON_DOWN && mIsComposing) {
+    // If IME is composing, let it complete.
+    HIMI himi;
+
+    spfnImGetInstance(mWnd, &himi);
+    spfnImRequestIME(himi, REQ_CONVERSIONSTRING, CNV_COMPLETE, 0);
+    spfnImReleaseInstance(mWnd, himi);
+  }
 
   if (aEventType == NS_MOUSE_ENTER || aEventType == NS_MOUSE_EXIT) {
     // Ignore enter/leave msgs forwarded from the frame to FID_CLIENT
     // because we're only interested msgs involving the content area.
     if (HWNDFROMMP(mp1) != mWnd) {
       return FALSE;
     }
 
--- a/widget/os2/nsWindow.h
+++ b/widget/os2/nsWindow.h
@@ -50,20 +50,20 @@
 #include "nsBaseWidget.h"
 #include "gfxASurface.h"
 
 #define INCL_DOS
 #define INCL_WIN
 #define INCL_NLS
 #define INCL_GPI
 #include <os2.h>
+#include <os2im.h>
 
 //-----------------------------------------------------------------------------
 // Items that may not be in the OS/2 Toolkit headers
-
 // For WM_MOUSEENTER/LEAVE, mp2 is the other window.
 #ifndef WM_MOUSEENTER
 #define WM_MOUSEENTER   0x041E
 #endif
 
 #ifndef WM_MOUSELEAVE
 #define WM_MOUSELEAVE   0x041F
 #endif
@@ -226,16 +226,19 @@ protected:
   bool                  OnPaint();
   bool                  OnMouseChord(MPARAM mp1, MPARAM mp2);
   bool                  OnDragDropMsg(ULONG msg, MPARAM mp1, MPARAM mp2,
                                       MRESULT& mr);
   bool                  CheckDragStatus(PRUint32 aAction, HPS* aHps);
   bool                  ReleaseIfDragHPS(HPS aHps);
   bool                  OnTranslateAccelerator(PQMSG pQmsg);
   bool                  OnQueryConvertPos(MPARAM mp1, MRESULT& mresult);
+  bool                  ImeResultString(HIMI himi);
+  bool                  ImeConversionString(HIMI himi);
+  bool                  OnImeRequest(MPARAM mp1, MPARAM mp2);
   bool                  DispatchKeyEvent(MPARAM mp1, MPARAM mp2);
   void                  InitEvent(nsGUIEvent& event, nsIntPoint* pt = 0);
   bool                  DispatchWindowEvent(nsGUIEvent* event);
   bool                  DispatchWindowEvent(nsGUIEvent* event,
                                             nsEventStatus& aStatus);
   bool                  DispatchCommandEvent(PRUint32 aEventCommand);
   bool                  DispatchDragDropEvent(PRUint32 aMsg);
   bool                  DispatchMoveEvent(PRInt32 aX, PRInt32 aY);
@@ -262,16 +265,17 @@ protected:
   bool          mInSetFocus;        // prevent recursive calls
   bool          mNoPaint;           // true if window is never visible
   HPS           mDragHps;           // retrieved by DrgGetPS() during a drag
   PRUint32      mDragStatus;        // set when object is being dragged over
   HWND          mClipWnd;           // used to clip plugin windows
   HPOINTER      mCssCursorHPtr;     // created by SetCursor(imgIContainer*)
   nsCOMPtr<imgIContainer> mCssCursorImg;// saved by SetCursor(imgIContainer*)
   nsRefPtr<gfxOS2Surface> mThebesSurface;
+  bool          mIsComposing;
 #ifdef DEBUG_FOCUS
   int           mWindowIdentifier;  // a serial number for each new window
 #endif
   InputContext  mInputContext;
 };
 
 //=============================================================================
 //  nsChildWindow