зеркало из https://github.com/mozilla/pjs.git
Bug 321468 [GTK2] Implement nsIKBStateControl Interface (event of accesskeys and shortcut keys are eaten by IME) r=masaki.katakai, sr=roc
This commit is contained in:
Родитель
d7552a3b88
Коммит
641dcce1d5
|
@ -22,6 +22,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -237,10 +238,13 @@ static nsWindow *gIMEFocusWindow = NULL;
|
|||
static GdkEventKey *gKeyEvent = NULL;
|
||||
static PRBool gKeyEventCommitted = PR_FALSE;
|
||||
static PRBool gKeyEventChanged = PR_FALSE;
|
||||
static PRBool gIMESuppressCommit = PR_FALSE;
|
||||
|
||||
static void IM_commit_cb (GtkIMContext *aContext,
|
||||
const gchar *aString,
|
||||
nsWindow *aWindow);
|
||||
static void IM_commit_cb_internal (const gchar *aString,
|
||||
nsWindow *aWindow);
|
||||
static void IM_preedit_changed_cb (GtkIMContext *aContext,
|
||||
nsWindow *aWindow);
|
||||
static void IM_set_text_range (const PRInt32 aLen,
|
||||
|
@ -250,6 +254,8 @@ static void IM_set_text_range (const PRInt32 aLen,
|
|||
PRUint32 *aTextRangeListLengthResult,
|
||||
nsTextRangeArray *aTextRangeListResult);
|
||||
|
||||
static nsWindow *IM_get_owning_window(MozDrawingarea *aArea);
|
||||
|
||||
static GtkIMContext *IM_get_input_context(MozDrawingarea *aArea);
|
||||
|
||||
// If after selecting profile window, the startup fail, please refer to
|
||||
|
@ -313,8 +319,7 @@ nsWindow::nsWindow()
|
|||
mDragMotionTimerID = 0;
|
||||
|
||||
#ifdef USE_XIM
|
||||
mIMContext = nsnull;
|
||||
mComposingText = PR_FALSE;
|
||||
mIMEData = nsnull;
|
||||
#endif
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
|
@ -349,8 +354,14 @@ nsWindow::ReleaseGlobals()
|
|||
}
|
||||
}
|
||||
|
||||
#ifndef USE_XIM
|
||||
NS_IMPL_ISUPPORTS_INHERITED1(nsWindow, nsCommonWidget,
|
||||
nsISupportsWeakReference)
|
||||
#else
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsWindow, nsCommonWidget,
|
||||
nsISupportsWeakReference,
|
||||
nsIKBStateControl)
|
||||
#endif
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::Create(nsIWidget *aParent,
|
||||
|
@ -1690,6 +1701,11 @@ nsWindow::OnButtonPressEvent(GtkWidget *aWidget, GdkEventButton *aEvent)
|
|||
PRUint32 eventType;
|
||||
nsEventStatus status;
|
||||
|
||||
#ifdef USE_XIM
|
||||
if (gIMEFocusWindow)
|
||||
gIMEFocusWindow->ResetInputStateInternal();
|
||||
#endif
|
||||
|
||||
// If you double click in GDK, it will actually generate a single
|
||||
// click event before sending the double click event, and this is
|
||||
// different than the DOM spec. GDK puts this in the queue
|
||||
|
@ -1871,7 +1887,7 @@ nsWindow::OnKeyPressEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
|
|||
// if we are in the middle of composing text, XIM gets to see it
|
||||
// before mozilla does.
|
||||
LOGIM(("key press [%p]: composing %d val %d\n",
|
||||
(void *)this, mComposingText, aEvent->keyval));
|
||||
(void *)this, IMEComposingWindow() != nsnull, aEvent->keyval));
|
||||
if (IMEFilterEvent(aEvent))
|
||||
return TRUE;
|
||||
LOGIM(("sending as regular key press event\n"));
|
||||
|
@ -4570,22 +4586,37 @@ nsChildWindow::~nsChildWindow()
|
|||
void
|
||||
nsWindow::IMEDestroyContext(void)
|
||||
{
|
||||
if (!mIMEData) {
|
||||
// Clear reference to this.
|
||||
if (IMEComposingWindow() == this)
|
||||
CancelIMECompositionInternal();
|
||||
if (gIMEFocusWindow == this)
|
||||
gIMEFocusWindow = nsnull;
|
||||
return;
|
||||
}
|
||||
|
||||
// If this is the focus window and we have an IM context we need
|
||||
// to unset the focus on this window before we destroy the window.
|
||||
if (gIMEFocusWindow == this) {
|
||||
GtkIMContext *im = IMEGetContext();
|
||||
if (im && gIMEFocusWindow && gIMEFocusWindow->IMEGetContext() == im) {
|
||||
gIMEFocusWindow->IMELoseFocus();
|
||||
gIMEFocusWindow = nsnull;
|
||||
}
|
||||
|
||||
if (!mIMContext)
|
||||
return;
|
||||
if (mIMEData->mContext) {
|
||||
gtk_im_context_set_client_window(mIMEData->mContext, nsnull);
|
||||
g_object_unref(G_OBJECT(mIMEData->mContext));
|
||||
}
|
||||
|
||||
gtk_im_context_set_client_window(mIMContext, NULL);
|
||||
g_object_unref(G_OBJECT(mIMContext));
|
||||
mIMContext = nsnull;
|
||||
if (mIMEData->mDummyContext) {
|
||||
gtk_im_context_set_client_window(mIMEData->mDummyContext, nsnull);
|
||||
g_object_unref(G_OBJECT(mIMEData->mDummyContext));
|
||||
}
|
||||
|
||||
delete mIMEData;
|
||||
mIMEData = nsnull;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsWindow::IMESetFocus(void)
|
||||
{
|
||||
|
@ -4596,6 +4627,12 @@ nsWindow::IMESetFocus(void)
|
|||
|
||||
gtk_im_context_focus_in(im);
|
||||
gIMEFocusWindow = this;
|
||||
|
||||
if (!IMEIsEnabled()) {
|
||||
// We should release IME focus for uim and scim.
|
||||
// These IMs are using snooper that is released at losing focus.
|
||||
IMELoseFocus();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -4614,12 +4651,17 @@ nsWindow::IMEComposeStart(void)
|
|||
{
|
||||
LOGIM(("IMEComposeStart [%p]\n", (void *)this));
|
||||
|
||||
if (mComposingText) {
|
||||
if (IMEComposingWindow()) {
|
||||
NS_WARNING("tried to re-start text composition\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mComposingText = PR_TRUE;
|
||||
nsWindow *win = IMEGetOwningWindow();
|
||||
if (win) {
|
||||
nsIMEData *data = win->mIMEData;
|
||||
if (data)
|
||||
data->mComposingWindow = this;
|
||||
}
|
||||
|
||||
nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_START, this);
|
||||
|
||||
|
@ -4650,7 +4692,7 @@ nsWindow::IMEComposeText (const PRUnichar *aText,
|
|||
const PangoAttrList *aFeedback)
|
||||
{
|
||||
// Send our start composition event if we need to
|
||||
if (!mComposingText)
|
||||
if (!IMEComposingWindow())
|
||||
IMEComposeStart();
|
||||
|
||||
LOGIM(("IMEComposeText\n"));
|
||||
|
@ -4694,12 +4736,17 @@ nsWindow::IMEComposeEnd(void)
|
|||
{
|
||||
LOGIM(("IMEComposeEnd [%p]\n", (void *)this));
|
||||
|
||||
if (!mComposingText) {
|
||||
if (!IMEComposingWindow()) {
|
||||
NS_WARNING("tried to end text composition before it was started");
|
||||
return;
|
||||
}
|
||||
|
||||
mComposingText = PR_FALSE;
|
||||
nsWindow *win = IMEGetOwningWindow();
|
||||
if (win) {
|
||||
nsIMEData *data = win->mIMEData;
|
||||
if (data)
|
||||
data->mComposingWindow = nsnull;
|
||||
}
|
||||
|
||||
nsCompositionEvent compEvent(PR_TRUE, NS_COMPOSITION_END, this);
|
||||
|
||||
|
@ -4713,26 +4760,63 @@ nsWindow::IMEGetContext()
|
|||
return IM_get_input_context(this->mDrawingarea);
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsWindow::IMEIsEnabled(void)
|
||||
{
|
||||
nsWindow *win = IMEGetOwningWindow();
|
||||
if (!win)
|
||||
return PR_FALSE;
|
||||
return win->mIMEData ? win->mIMEData->mEnabled : PR_FALSE;
|
||||
}
|
||||
|
||||
nsWindow*
|
||||
nsWindow::IMEComposingWindow(void)
|
||||
{
|
||||
nsWindow *win = IMEGetOwningWindow();
|
||||
if (!win)
|
||||
return nsnull;
|
||||
return win->mIMEData ? win->mIMEData->mComposingWindow : nsnull;
|
||||
}
|
||||
|
||||
nsWindow*
|
||||
nsWindow::IMEGetOwningWindow(void)
|
||||
{
|
||||
nsWindow *window = IM_get_owning_window(this->mDrawingarea);
|
||||
return window;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::IMECreateContext(void)
|
||||
{
|
||||
GtkIMContext *im = gtk_im_multicontext_new();
|
||||
if (!im)
|
||||
mIMEData = new nsIMEData;
|
||||
if (!mIMEData)
|
||||
return;
|
||||
|
||||
gtk_im_context_set_client_window(im, GTK_WIDGET(mContainer)->window);
|
||||
mIMEData->mContext = gtk_im_multicontext_new();
|
||||
mIMEData->mDummyContext = gtk_im_multicontext_new();
|
||||
if (!mIMEData->mContext || !mIMEData->mDummyContext) {
|
||||
NS_ERROR("failed to create IM context.");
|
||||
IMEDestroyContext();
|
||||
return;
|
||||
}
|
||||
|
||||
g_signal_connect(G_OBJECT(im), "preedit_changed",
|
||||
gtk_im_context_set_client_window(mIMEData->mContext,
|
||||
GTK_WIDGET(mContainer)->window);
|
||||
gtk_im_context_set_client_window(mIMEData->mDummyContext,
|
||||
GTK_WIDGET(mContainer)->window);
|
||||
|
||||
g_signal_connect(G_OBJECT(mIMEData->mContext), "preedit_changed",
|
||||
G_CALLBACK(IM_preedit_changed_cb), this);
|
||||
g_signal_connect(G_OBJECT(im), "commit",
|
||||
g_signal_connect(G_OBJECT(mIMEData->mContext), "commit",
|
||||
G_CALLBACK(IM_commit_cb), this);
|
||||
|
||||
mIMContext = im;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsWindow::IMEFilterEvent(GdkEventKey *aEvent)
|
||||
{
|
||||
if (!IMEIsEnabled())
|
||||
return FALSE;
|
||||
|
||||
GtkIMContext *im = IMEGetContext();
|
||||
if (!im)
|
||||
return FALSE;
|
||||
|
@ -4762,6 +4846,124 @@ nsWindow::IMEFilterEvent(GdkEventKey *aEvent)
|
|||
return retval;
|
||||
}
|
||||
|
||||
/* nsIKBStateControl */
|
||||
NS_IMETHODIMP
|
||||
nsWindow::ResetInputState()
|
||||
{
|
||||
// We should not implement this until bug 327003 is fixed.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWindow::ResetInputStateInternal()
|
||||
{
|
||||
nsWindow *win = IMEComposingWindow();
|
||||
if (win) {
|
||||
GtkIMContext *im = IMEGetContext();
|
||||
if (!im)
|
||||
return NS_OK;
|
||||
|
||||
gchar *preedit_string;
|
||||
gint cursor_pos;
|
||||
PangoAttrList *feedback_list;
|
||||
gtk_im_context_get_preedit_string(im, &preedit_string,
|
||||
&feedback_list, &cursor_pos);
|
||||
if (preedit_string && *preedit_string) {
|
||||
IM_commit_cb_internal(preedit_string, win);
|
||||
g_free(preedit_string);
|
||||
}
|
||||
if (feedback_list)
|
||||
pango_attr_list_unref(feedback_list);
|
||||
}
|
||||
|
||||
CancelIMECompositionInternal();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::SetIMEOpenState(PRBool aState)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::GetIMEOpenState(PRBool* aState)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::SetIMEEnabled(PRBool aState)
|
||||
{
|
||||
nsWindow *window = IMEGetOwningWindow();
|
||||
if (!window || !window->mIMEData || window->mIMEData->mEnabled == aState)
|
||||
return NS_OK;
|
||||
|
||||
GtkIMContext *focusedIm = nsnull;
|
||||
// XXX Don't we need to check gFocusWindow?
|
||||
nsWindow *focusedWin = gIMEFocusWindow;
|
||||
if (focusedWin) {
|
||||
nsWindow *owningWin = focusedWin->IMEGetOwningWindow();
|
||||
if (owningWin && owningWin->mIMEData)
|
||||
focusedIm = owningWin->mIMEData->mContext;
|
||||
}
|
||||
|
||||
if (focusedIm && focusedIm == window->mIMEData->mContext) {
|
||||
// Release current IME focus if IME is enabled.
|
||||
if (window->mIMEData->mEnabled)
|
||||
focusedWin->IMELoseFocus();
|
||||
|
||||
window->mIMEData->mEnabled = aState;
|
||||
|
||||
// Even when aState is not PR_TRUE, we need to set IME focus.
|
||||
// Because some IMs are updating the status bar of them in this time.
|
||||
focusedWin->IMESetFocus();
|
||||
} else {
|
||||
if (window->mIMEData->mEnabled)
|
||||
ResetInputStateInternal();
|
||||
window->mIMEData->mEnabled = aState;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::GetIMEEnabled(PRBool* aState)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aState);
|
||||
*aState = IMEIsEnabled();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::CancelIMEComposition()
|
||||
{
|
||||
// We should not implement this until bug 327003 is fixed.
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsWindow::CancelIMECompositionInternal()
|
||||
{
|
||||
GtkIMContext *im = IMEGetContext();
|
||||
if (!im)
|
||||
return NS_OK;
|
||||
|
||||
NS_ASSERTION(gIMESuppressCommit, "CancelIMEComposition is already called!");
|
||||
gIMESuppressCommit = PR_TRUE;
|
||||
gtk_im_context_reset(im);
|
||||
gIMESuppressCommit = PR_FALSE;
|
||||
|
||||
nsWindow *win = IMEComposingWindow();
|
||||
if (win) {
|
||||
win->IMEComposeText(nsnull, 0, nsnull, 0, nsnull);
|
||||
win->IMEComposeEnd();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
IM_preedit_changed_cb(GtkIMContext *aContext,
|
||||
|
@ -4826,8 +5028,8 @@ IM_commit_cb (GtkIMContext *aContext,
|
|||
const gchar *aUtf8_str,
|
||||
nsWindow *aWindow)
|
||||
{
|
||||
gunichar2 *uniStr;
|
||||
glong uniStrLen;
|
||||
if (gIMESuppressCommit)
|
||||
return;
|
||||
|
||||
LOGIM(("IM_commit_cb\n"));
|
||||
|
||||
|
@ -4859,9 +5061,16 @@ IM_commit_cb (GtkIMContext *aContext,
|
|||
}
|
||||
|
||||
gKeyEventChanged = PR_TRUE;
|
||||
IM_commit_cb_internal(aUtf8_str, window);
|
||||
}
|
||||
|
||||
uniStr = NULL;
|
||||
uniStrLen = 0;
|
||||
/* static */
|
||||
void
|
||||
IM_commit_cb_internal (const gchar *aUtf8_str,
|
||||
nsWindow *aWindow)
|
||||
{
|
||||
gunichar2 *uniStr = nsnull;
|
||||
glong uniStrLen = 0;
|
||||
uniStr = g_utf8_to_utf16(aUtf8_str, -1, NULL, &uniStrLen, NULL);
|
||||
|
||||
if (!uniStr) {
|
||||
|
@ -4870,9 +5079,9 @@ IM_commit_cb (GtkIMContext *aContext,
|
|||
}
|
||||
|
||||
if (uniStrLen) {
|
||||
window->IMEComposeText((const PRUnichar *)uniStr,
|
||||
(PRInt32)uniStrLen, NULL, 0, NULL);
|
||||
window->IMEComposeEnd();
|
||||
aWindow->IMEComposeText((const PRUnichar *)uniStr,
|
||||
(PRInt32)uniStrLen, nsnull, 0, nsnull);
|
||||
aWindow->IMEComposeEnd();
|
||||
}
|
||||
|
||||
g_free(uniStr);
|
||||
|
@ -5000,16 +5209,29 @@ IM_set_text_range(const PRInt32 aLen,
|
|||
pango_attr_iterator_destroy(aFeedbackIterator);
|
||||
}
|
||||
|
||||
/* static */
|
||||
nsWindow *
|
||||
IM_get_owning_window(MozDrawingarea *aArea)
|
||||
{
|
||||
if (!aArea)
|
||||
return nsnull;
|
||||
GtkWidget *owningWidget =
|
||||
get_gtk_widget_for_gdk_window(aArea->inner_window);
|
||||
if (!owningWidget)
|
||||
return nsnull;
|
||||
return get_window_for_gtk_widget(owningWidget);
|
||||
}
|
||||
|
||||
/* static */
|
||||
GtkIMContext *
|
||||
IM_get_input_context(MozDrawingarea *aArea)
|
||||
{
|
||||
GtkWidget *owningWidget =
|
||||
get_gtk_widget_for_gdk_window(aArea->inner_window);
|
||||
|
||||
nsWindow *owningWindow = get_window_for_gtk_widget(owningWidget);
|
||||
|
||||
return owningWindow->mIMContext;
|
||||
if (!aArea)
|
||||
return nsnull;
|
||||
nsWindow::nsIMEData *data = IM_get_owning_window(aArea)->mIMEData;
|
||||
if (!data)
|
||||
return nsnull;
|
||||
return data->mEnabled ? data->mContext : data->mDummyContext;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -62,9 +63,14 @@
|
|||
#ifdef USE_XIM
|
||||
#include <gtk/gtkimmulticontext.h>
|
||||
#include "pldhash.h"
|
||||
#include "nsIKBStateControl.h"
|
||||
#endif
|
||||
|
||||
class nsWindow : public nsCommonWidget, public nsSupportsWeakReference {
|
||||
class nsWindow : public nsCommonWidget, public nsSupportsWeakReference
|
||||
#ifdef USE_XIM
|
||||
,public nsIKBStateControl
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
nsWindow();
|
||||
virtual ~nsWindow();
|
||||
|
@ -271,11 +277,55 @@ public:
|
|||
const PangoAttrList *aFeedback);
|
||||
void IMEComposeEnd (void);
|
||||
GtkIMContext* IMEGetContext (void);
|
||||
nsWindow* IMEGetOwningWindow(void);
|
||||
PRBool IMEIsEnabled (void);
|
||||
nsWindow* IMEComposingWindow(void);
|
||||
void IMECreateContext (void);
|
||||
PRBool IMEFilterEvent (GdkEventKey *aEvent);
|
||||
|
||||
GtkIMContext *mIMContext;
|
||||
PRBool mComposingText;
|
||||
/*
|
||||
* |mIMEData| has all IME data for the window and its children widgets.
|
||||
* So, this is created only on owner widget. Therefore, when the
|
||||
* focused widget needs the data, it get from its owning window.
|
||||
* See |IM_get_input_context|.
|
||||
*/
|
||||
struct nsIMEData {
|
||||
// Actual context. This is used for handling the user's input.
|
||||
GtkIMContext *mContext;
|
||||
// mDummyContext is a dummy context and will be used in IMESetFocus()
|
||||
// when mEnabled is false. This mDummyContext IM state is always
|
||||
// "off", so it works to switch conversion mode to OFF on IM status
|
||||
// window.
|
||||
GtkIMContext *mDummyContext;
|
||||
// This mComposingWindow is set in IMEComposeStart(), when user starts
|
||||
// composition, then unset in IMEComposeEnd() when user ends the
|
||||
// composition. We will keep the widget where the actual composition is
|
||||
// started. During the composition, we may get some events like
|
||||
// ResetInputStateInternal() and CancelIMECompositionInternal() by
|
||||
// changing input focus, we will use the original widget of
|
||||
// mComposingWindow to commit or reset the composition.
|
||||
nsWindow *mComposingWindow;
|
||||
// IME enabled state in this window.
|
||||
PRPackedBool mEnabled;
|
||||
nsIMEData() {
|
||||
mContext = nsnull;
|
||||
mDummyContext = nsnull;
|
||||
mComposingWindow = nsnull;
|
||||
mEnabled = PR_TRUE;
|
||||
}
|
||||
};
|
||||
nsIMEData *mIMEData;
|
||||
|
||||
// nsIKBStateControl interface
|
||||
NS_IMETHOD ResetInputState();
|
||||
NS_IMETHOD SetIMEOpenState(PRBool aState);
|
||||
NS_IMETHOD GetIMEOpenState(PRBool* aState);
|
||||
NS_IMETHOD SetIMEEnabled(PRBool aState);
|
||||
NS_IMETHOD GetIMEEnabled(PRBool* aState);
|
||||
NS_IMETHOD CancelIMEComposition();
|
||||
|
||||
nsresult ResetInputStateInternal();
|
||||
nsresult CancelIMECompositionInternal();
|
||||
|
||||
#endif
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче