From d342e8f7b4ce13465dc9da17d58ce50fe81e9c11 Mon Sep 17 00:00:00 2001 From: "bryner%brianryner.com" Date: Tue, 7 Sep 2004 21:21:48 +0000 Subject: [PATCH] Add support for native keybindings for input and textarea for gtk2. This allows us to respect the GTK keybinding preferences. Bug 257405, r=blizzard, sr=roc. --- allmakefiles.sh | 1 + content/xbl/builtin/Makefile.in | 4 + content/xbl/builtin/gtk2/.cvsignore | 1 + content/xbl/builtin/gtk2/Makefile.in | 0 .../xbl/builtin/gtk2/platformHTMLBindings.xml | 110 +++++++ layout/build/nsLayoutModule.cpp | 2 + layout/forms/nsTextControlFrame.cpp | 205 +++++++++++- layout/forms/nsTextControlFrame.h | 3 + layout/html/forms/src/nsTextControlFrame.cpp | 205 +++++++++++- layout/html/forms/src/nsTextControlFrame.h | 3 + widget/public/Makefile.in | 1 + widget/public/nsINativeKeyBindings.h | 80 +++++ widget/src/gtk2/Makefile.in | 1 + widget/src/gtk2/nsGtkKeyUtils.cpp | 47 +++ widget/src/gtk2/nsGtkKeyUtils.h | 1 + widget/src/gtk2/nsNativeKeyBindings.cpp | 296 ++++++++++++++++++ widget/src/gtk2/nsNativeKeyBindings.h | 93 ++++++ widget/src/gtk2/nsWidgetFactory.cpp | 52 +++ 18 files changed, 1089 insertions(+), 16 deletions(-) create mode 100644 content/xbl/builtin/gtk2/.cvsignore create mode 100644 content/xbl/builtin/gtk2/Makefile.in create mode 100644 content/xbl/builtin/gtk2/platformHTMLBindings.xml create mode 100644 widget/public/nsINativeKeyBindings.h create mode 100644 widget/src/gtk2/nsNativeKeyBindings.cpp create mode 100644 widget/src/gtk2/nsNativeKeyBindings.h diff --git a/allmakefiles.sh b/allmakefiles.sh index de08101962d..5975cc99519 100755 --- a/allmakefiles.sh +++ b/allmakefiles.sh @@ -299,6 +299,7 @@ content/xbl/Makefile content/xbl/public/Makefile content/xbl/src/Makefile content/xbl/builtin/Makefile +content/xbl/builtin/gtk2/Makefile content/xbl/builtin/unix/Makefile content/xbl/builtin/win/Makefile content/xsl/Makefile diff --git a/content/xbl/builtin/Makefile.in b/content/xbl/builtin/Makefile.in index 3146609842f..34af14eb842 100644 --- a/content/xbl/builtin/Makefile.in +++ b/content/xbl/builtin/Makefile.in @@ -48,9 +48,13 @@ else ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) DIRS = mac else +ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2) +DIRS = gtk2 +else DIRS = unix endif endif +endif include $(topsrcdir)/config/rules.mk diff --git a/content/xbl/builtin/gtk2/.cvsignore b/content/xbl/builtin/gtk2/.cvsignore new file mode 100644 index 00000000000..f3c7a7c5da6 --- /dev/null +++ b/content/xbl/builtin/gtk2/.cvsignore @@ -0,0 +1 @@ +Makefile diff --git a/content/xbl/builtin/gtk2/Makefile.in b/content/xbl/builtin/gtk2/Makefile.in new file mode 100644 index 00000000000..e69de29bb2d diff --git a/content/xbl/builtin/gtk2/platformHTMLBindings.xml b/content/xbl/builtin/gtk2/platformHTMLBindings.xml new file mode 100644 index 00000000000..6e51523e767 --- /dev/null +++ b/content/xbl/builtin/gtk2/platformHTMLBindings.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index c81eae63dca..3e1de085ab8 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -128,6 +128,7 @@ #include "nsStyleSet.h" #include "nsImageFrame.h" #include "nsILanguageAtomService.h" +#include "nsTextControlFrame.h" // view stuff #include "nsViewsCID.h" @@ -417,6 +418,7 @@ Shutdown() GlobalWindowImpl::ShutDown(); nsDOMClassInfo::ShutDown(); + nsTextControlFrame::ShutDown(); } #ifdef NS_DEBUG diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index 6817f6ee1cf..fbc482e60cd 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -120,6 +120,10 @@ #include "nsIDOMText.h" //for multiline getselection #include "nsNodeInfoManager.h" #include "nsContentCreatorFunctions.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMEventGroup.h" +#include "nsIDOM3EventTarget.h" +#include "nsINativeKeyBindings.h" #ifdef IBMBIDI #include "nsIBidiKeyboard.h" @@ -138,8 +142,12 @@ static const PRInt32 DEFAULT_COLS = 20; static const PRInt32 DEFAULT_ROWS = 1; static const PRInt32 DEFAULT_ROWS_TEXTAREA = 2; +static nsINativeKeyBindings *sNativeInputBindings = nsnull; +static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull; + class nsTextInputListener : public nsISelectionListener, public nsIDOMFocusListener, + public nsIDOMKeyListener, public nsIEditorObserver, public nsSupportsWeakReference { @@ -169,12 +177,19 @@ public: NS_IMETHOD Blur (nsIDOMEvent* aEvent); /* END interfaces from nsIDOMFocusListener*/ + // nsIDOMKeyListener + NS_IMETHOD KeyDown(nsIDOMEvent *aKeyEvent); + NS_IMETHOD KeyPress(nsIDOMEvent *aKeyEvent); + NS_IMETHOD KeyUp(nsIDOMEvent *aKeyEvent); + NS_DECL_NSIEDITOROBSERVER protected: nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate); + NS_HIDDEN_(nsINativeKeyBindings*) GetKeyBindings(); + protected: nsTextControlFrame* mFrame; // weak reference @@ -211,9 +226,18 @@ nsTextInputListener::~nsTextInputListener() { } -NS_IMPL_ISUPPORTS5(nsTextInputListener, nsISelectionListener, - nsIDOMEventListener, nsIDOMFocusListener, - nsIEditorObserver, nsISupportsWeakReference) +NS_IMPL_ADDREF(nsTextInputListener) +NS_IMPL_RELEASE(nsTextInputListener) + +NS_INTERFACE_MAP_BEGIN(nsTextInputListener) + NS_INTERFACE_MAP_ENTRY(nsISelectionListener) + NS_INTERFACE_MAP_ENTRY(nsIEditorObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFocusListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFocusListener) +NS_INTERFACE_MAP_END // BEGIN nsIDOMSelectionListener @@ -313,6 +337,103 @@ nsTextInputListener::Blur(nsIDOMEvent* aEvent) // END nsIFocusListener +// BEGIN nsIDOMKeyListener + +static void +DoCommandCallback(const char *aCommand, void *aData) +{ + nsTextControlFrame *frame = NS_STATIC_CAST(nsTextControlFrame*, aData); + nsIContent *content = frame->GetContent(); + + nsCOMPtr controllers; + nsCOMPtr input = do_QueryInterface(content); + if (input) { + input->GetControllers(getter_AddRefs(controllers)); + } else { + nsCOMPtr textArea = + do_QueryInterface(content); + + if (textArea) { + textArea->GetControllers(getter_AddRefs(controllers)); + } + } + + if (!controllers) { + NS_WARNING("Could not get controllers"); + return; + } + + nsCOMPtr controller; + controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller)); + if (controller) { + controller->DoCommand(aCommand); + } +} + +static PRBool +DOMEventToNativeKeyEvent(nsIDOMEvent *aDOMEvent, + nsNativeKeyEvent *aNativeEvent) +{ + nsCOMPtr nsevent = do_QueryInterface(aDOMEvent); + PRBool defaultPrevented; + nsevent->GetPreventDefault(&defaultPrevented); + if (defaultPrevented) + return PR_FALSE; + + nsCOMPtr keyEvent = do_QueryInterface(aDOMEvent); + + keyEvent->GetCharCode(&aNativeEvent->charCode); + keyEvent->GetKeyCode(&aNativeEvent->keyCode); + keyEvent->GetAltKey(&aNativeEvent->altKey); + keyEvent->GetCtrlKey(&aNativeEvent->ctrlKey); + keyEvent->GetShiftKey(&aNativeEvent->shiftKey); + keyEvent->GetMetaKey(&aNativeEvent->metaKey); + + return PR_TRUE; +} + +NS_IMETHODIMP +nsTextInputListener::KeyDown(nsIDOMEvent *aKeyEvent) +{ + nsNativeKeyEvent nativeEvent; + nsINativeKeyBindings *bindings = GetKeyBindings(); + if (bindings && DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent)) { + if (bindings->KeyDown(nativeEvent, DoCommandCallback, mFrame)) { + aKeyEvent->PreventDefault(); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsTextInputListener::KeyPress(nsIDOMEvent *aKeyEvent) +{ + nsNativeKeyEvent nativeEvent; + nsINativeKeyBindings *bindings = GetKeyBindings(); + if (bindings && DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent)) { + if (bindings->KeyPress(nativeEvent, DoCommandCallback, mFrame)) { + aKeyEvent->PreventDefault(); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsTextInputListener::KeyUp(nsIDOMEvent *aKeyEvent) +{ + nsNativeKeyEvent nativeEvent; + nsINativeKeyBindings *bindings = GetKeyBindings(); + if (bindings && DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent)) { + if (bindings->KeyUp(nativeEvent, DoCommandCallback, mFrame)) { + aKeyEvent->PreventDefault(); + } + } + + return NS_OK; +} +// END nsIDOMKeyListener // BEGIN nsIEditorObserver @@ -371,6 +492,36 @@ nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate) return domWindow->UpdateCommands(commandsToUpdate); } +nsINativeKeyBindings* +nsTextInputListener::GetKeyBindings() +{ + if (mFrame->IsTextArea()) { + static PRBool sNoTextAreaBindings = PR_FALSE; + + if (!sNativeTextAreaBindings && !sNoTextAreaBindings) { + CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea", + &sNativeTextAreaBindings); + + if (!sNativeTextAreaBindings) { + sNoTextAreaBindings = PR_TRUE; + } + } + + return sNativeTextAreaBindings; + } + + static PRBool sNoInputBindings = PR_FALSE; + if (!sNativeInputBindings && !sNoInputBindings) { + CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input", + &sNativeInputBindings); + + if (!sNativeInputBindings) { + sNoInputBindings = PR_TRUE; + } + } + + return sNativeInputBindings; +} // END nsTextInputListener @@ -1255,6 +1406,22 @@ nsTextControlFrame::PreDestroy(nsPresContext* aPresContext) { erP->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *,mTextListener), NS_GET_IID(nsIDOMFocusListener)); } + + nsCOMPtr systemGroup; + erP->GetSystemEventGroup(getter_AddRefs(systemGroup)); + nsCOMPtr dom3Targ = do_QueryInterface(mContent); + if (dom3Targ) { + // cast because of ambiguous base + nsIDOMEventListener *listener = NS_STATIC_CAST(nsIDOMKeyListener*, + mTextListener); + + dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keydown"), + listener, PR_FALSE, systemGroup); + dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"), + listener, PR_FALSE, systemGroup); + dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keyup"), + listener, PR_FALSE, systemGroup); + } } mDidPreDestroy = PR_TRUE; @@ -3005,11 +3172,10 @@ nsTextControlFrame::SetInitialChildList(nsPresContext* aPresContext, scrollableFrame->SetScrollbarVisibility(PR_FALSE, PR_FALSE); } - //register keylistener - nsCOMPtr erP; - if (NS_SUCCEEDED(mContent->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), getter_AddRefs(erP))) && erP) - { - // register the event listeners with the DOM event reveiver + //register focus and key listeners + nsCOMPtr erP = do_QueryInterface(mContent); + if (erP) { + // register the event listeners with the DOM event receiver rv = erP->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *,mTextListener), NS_GET_IID(nsIDOMFocusListener)); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register focus listener"); // XXXbryner do we need to check for a null presshell here? @@ -3017,6 +3183,22 @@ nsTextControlFrame::SetInitialChildList(nsPresContext* aPresContext, return NS_ERROR_FAILURE; } + nsCOMPtr systemGroup; + erP->GetSystemEventGroup(getter_AddRefs(systemGroup)); + nsCOMPtr dom3Targ = do_QueryInterface(mContent); + if (dom3Targ) { + // cast because of ambiguous base + nsIDOMEventListener *listener = NS_STATIC_CAST(nsIDOMKeyListener*, + mTextListener); + + dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keydown"), + listener, PR_FALSE, systemGroup); + dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keypress"), + listener, PR_FALSE, systemGroup); + dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keyup"), + listener, PR_FALSE, systemGroup); + } + while(first) { nsIView *view = first->GetView(); @@ -3105,3 +3287,10 @@ nsTextControlFrame::HandleEvent(nsPresContext* aPresContext, return nsStackFrame::HandleEvent(aPresContext, aEvent, aEventStatus); } + +/* static */ void +nsTextControlFrame::ShutDown() +{ + NS_IF_RELEASE(sNativeTextAreaBindings); + NS_IF_RELEASE(sNativeInputBindings); +} diff --git a/layout/forms/nsTextControlFrame.h b/layout/forms/nsTextControlFrame.h index 237cfc9210f..517be789523 100644 --- a/layout/forms/nsTextControlFrame.h +++ b/layout/forms/nsTextControlFrame.h @@ -212,6 +212,9 @@ public: //for methods who access nsTextControlFrame directly nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32 *aResult); nsresult OffsetToDOMPoint(PRInt32 aOffset, nsIDOMNode** aResult, PRInt32* aPosition); + /* called to free up native keybinding services */ + static NS_HIDDEN_(void) ShutDown(); + protected: /** diff --git a/layout/html/forms/src/nsTextControlFrame.cpp b/layout/html/forms/src/nsTextControlFrame.cpp index 6817f6ee1cf..fbc482e60cd 100644 --- a/layout/html/forms/src/nsTextControlFrame.cpp +++ b/layout/html/forms/src/nsTextControlFrame.cpp @@ -120,6 +120,10 @@ #include "nsIDOMText.h" //for multiline getselection #include "nsNodeInfoManager.h" #include "nsContentCreatorFunctions.h" +#include "nsIDOMKeyListener.h" +#include "nsIDOMEventGroup.h" +#include "nsIDOM3EventTarget.h" +#include "nsINativeKeyBindings.h" #ifdef IBMBIDI #include "nsIBidiKeyboard.h" @@ -138,8 +142,12 @@ static const PRInt32 DEFAULT_COLS = 20; static const PRInt32 DEFAULT_ROWS = 1; static const PRInt32 DEFAULT_ROWS_TEXTAREA = 2; +static nsINativeKeyBindings *sNativeInputBindings = nsnull; +static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull; + class nsTextInputListener : public nsISelectionListener, public nsIDOMFocusListener, + public nsIDOMKeyListener, public nsIEditorObserver, public nsSupportsWeakReference { @@ -169,12 +177,19 @@ public: NS_IMETHOD Blur (nsIDOMEvent* aEvent); /* END interfaces from nsIDOMFocusListener*/ + // nsIDOMKeyListener + NS_IMETHOD KeyDown(nsIDOMEvent *aKeyEvent); + NS_IMETHOD KeyPress(nsIDOMEvent *aKeyEvent); + NS_IMETHOD KeyUp(nsIDOMEvent *aKeyEvent); + NS_DECL_NSIEDITOROBSERVER protected: nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate); + NS_HIDDEN_(nsINativeKeyBindings*) GetKeyBindings(); + protected: nsTextControlFrame* mFrame; // weak reference @@ -211,9 +226,18 @@ nsTextInputListener::~nsTextInputListener() { } -NS_IMPL_ISUPPORTS5(nsTextInputListener, nsISelectionListener, - nsIDOMEventListener, nsIDOMFocusListener, - nsIEditorObserver, nsISupportsWeakReference) +NS_IMPL_ADDREF(nsTextInputListener) +NS_IMPL_RELEASE(nsTextInputListener) + +NS_INTERFACE_MAP_BEGIN(nsTextInputListener) + NS_INTERFACE_MAP_ENTRY(nsISelectionListener) + NS_INTERFACE_MAP_ENTRY(nsIEditorObserver) + NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener) + NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFocusListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFocusListener) +NS_INTERFACE_MAP_END // BEGIN nsIDOMSelectionListener @@ -313,6 +337,103 @@ nsTextInputListener::Blur(nsIDOMEvent* aEvent) // END nsIFocusListener +// BEGIN nsIDOMKeyListener + +static void +DoCommandCallback(const char *aCommand, void *aData) +{ + nsTextControlFrame *frame = NS_STATIC_CAST(nsTextControlFrame*, aData); + nsIContent *content = frame->GetContent(); + + nsCOMPtr controllers; + nsCOMPtr input = do_QueryInterface(content); + if (input) { + input->GetControllers(getter_AddRefs(controllers)); + } else { + nsCOMPtr textArea = + do_QueryInterface(content); + + if (textArea) { + textArea->GetControllers(getter_AddRefs(controllers)); + } + } + + if (!controllers) { + NS_WARNING("Could not get controllers"); + return; + } + + nsCOMPtr controller; + controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller)); + if (controller) { + controller->DoCommand(aCommand); + } +} + +static PRBool +DOMEventToNativeKeyEvent(nsIDOMEvent *aDOMEvent, + nsNativeKeyEvent *aNativeEvent) +{ + nsCOMPtr nsevent = do_QueryInterface(aDOMEvent); + PRBool defaultPrevented; + nsevent->GetPreventDefault(&defaultPrevented); + if (defaultPrevented) + return PR_FALSE; + + nsCOMPtr keyEvent = do_QueryInterface(aDOMEvent); + + keyEvent->GetCharCode(&aNativeEvent->charCode); + keyEvent->GetKeyCode(&aNativeEvent->keyCode); + keyEvent->GetAltKey(&aNativeEvent->altKey); + keyEvent->GetCtrlKey(&aNativeEvent->ctrlKey); + keyEvent->GetShiftKey(&aNativeEvent->shiftKey); + keyEvent->GetMetaKey(&aNativeEvent->metaKey); + + return PR_TRUE; +} + +NS_IMETHODIMP +nsTextInputListener::KeyDown(nsIDOMEvent *aKeyEvent) +{ + nsNativeKeyEvent nativeEvent; + nsINativeKeyBindings *bindings = GetKeyBindings(); + if (bindings && DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent)) { + if (bindings->KeyDown(nativeEvent, DoCommandCallback, mFrame)) { + aKeyEvent->PreventDefault(); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsTextInputListener::KeyPress(nsIDOMEvent *aKeyEvent) +{ + nsNativeKeyEvent nativeEvent; + nsINativeKeyBindings *bindings = GetKeyBindings(); + if (bindings && DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent)) { + if (bindings->KeyPress(nativeEvent, DoCommandCallback, mFrame)) { + aKeyEvent->PreventDefault(); + } + } + + return NS_OK; +} + +NS_IMETHODIMP +nsTextInputListener::KeyUp(nsIDOMEvent *aKeyEvent) +{ + nsNativeKeyEvent nativeEvent; + nsINativeKeyBindings *bindings = GetKeyBindings(); + if (bindings && DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent)) { + if (bindings->KeyUp(nativeEvent, DoCommandCallback, mFrame)) { + aKeyEvent->PreventDefault(); + } + } + + return NS_OK; +} +// END nsIDOMKeyListener // BEGIN nsIEditorObserver @@ -371,6 +492,36 @@ nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate) return domWindow->UpdateCommands(commandsToUpdate); } +nsINativeKeyBindings* +nsTextInputListener::GetKeyBindings() +{ + if (mFrame->IsTextArea()) { + static PRBool sNoTextAreaBindings = PR_FALSE; + + if (!sNativeTextAreaBindings && !sNoTextAreaBindings) { + CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea", + &sNativeTextAreaBindings); + + if (!sNativeTextAreaBindings) { + sNoTextAreaBindings = PR_TRUE; + } + } + + return sNativeTextAreaBindings; + } + + static PRBool sNoInputBindings = PR_FALSE; + if (!sNativeInputBindings && !sNoInputBindings) { + CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input", + &sNativeInputBindings); + + if (!sNativeInputBindings) { + sNoInputBindings = PR_TRUE; + } + } + + return sNativeInputBindings; +} // END nsTextInputListener @@ -1255,6 +1406,22 @@ nsTextControlFrame::PreDestroy(nsPresContext* aPresContext) { erP->RemoveEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *,mTextListener), NS_GET_IID(nsIDOMFocusListener)); } + + nsCOMPtr systemGroup; + erP->GetSystemEventGroup(getter_AddRefs(systemGroup)); + nsCOMPtr dom3Targ = do_QueryInterface(mContent); + if (dom3Targ) { + // cast because of ambiguous base + nsIDOMEventListener *listener = NS_STATIC_CAST(nsIDOMKeyListener*, + mTextListener); + + dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keydown"), + listener, PR_FALSE, systemGroup); + dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"), + listener, PR_FALSE, systemGroup); + dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keyup"), + listener, PR_FALSE, systemGroup); + } } mDidPreDestroy = PR_TRUE; @@ -3005,11 +3172,10 @@ nsTextControlFrame::SetInitialChildList(nsPresContext* aPresContext, scrollableFrame->SetScrollbarVisibility(PR_FALSE, PR_FALSE); } - //register keylistener - nsCOMPtr erP; - if (NS_SUCCEEDED(mContent->QueryInterface(NS_GET_IID(nsIDOMEventReceiver), getter_AddRefs(erP))) && erP) - { - // register the event listeners with the DOM event reveiver + //register focus and key listeners + nsCOMPtr erP = do_QueryInterface(mContent); + if (erP) { + // register the event listeners with the DOM event receiver rv = erP->AddEventListenerByIID(NS_STATIC_CAST(nsIDOMFocusListener *,mTextListener), NS_GET_IID(nsIDOMFocusListener)); NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register focus listener"); // XXXbryner do we need to check for a null presshell here? @@ -3017,6 +3183,22 @@ nsTextControlFrame::SetInitialChildList(nsPresContext* aPresContext, return NS_ERROR_FAILURE; } + nsCOMPtr systemGroup; + erP->GetSystemEventGroup(getter_AddRefs(systemGroup)); + nsCOMPtr dom3Targ = do_QueryInterface(mContent); + if (dom3Targ) { + // cast because of ambiguous base + nsIDOMEventListener *listener = NS_STATIC_CAST(nsIDOMKeyListener*, + mTextListener); + + dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keydown"), + listener, PR_FALSE, systemGroup); + dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keypress"), + listener, PR_FALSE, systemGroup); + dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keyup"), + listener, PR_FALSE, systemGroup); + } + while(first) { nsIView *view = first->GetView(); @@ -3105,3 +3287,10 @@ nsTextControlFrame::HandleEvent(nsPresContext* aPresContext, return nsStackFrame::HandleEvent(aPresContext, aEvent, aEventStatus); } + +/* static */ void +nsTextControlFrame::ShutDown() +{ + NS_IF_RELEASE(sNativeTextAreaBindings); + NS_IF_RELEASE(sNativeInputBindings); +} diff --git a/layout/html/forms/src/nsTextControlFrame.h b/layout/html/forms/src/nsTextControlFrame.h index 237cfc9210f..517be789523 100644 --- a/layout/html/forms/src/nsTextControlFrame.h +++ b/layout/html/forms/src/nsTextControlFrame.h @@ -212,6 +212,9 @@ public: //for methods who access nsTextControlFrame directly nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32 *aResult); nsresult OffsetToDOMPoint(PRInt32 aOffset, nsIDOMNode** aResult, PRInt32* aPosition); + /* called to free up native keybinding services */ + static NS_HIDDEN_(void) ShutDown(); + protected: /** diff --git a/widget/public/Makefile.in b/widget/public/Makefile.in index 2b38cd31980..c243937db6c 100644 --- a/widget/public/Makefile.in +++ b/widget/public/Makefile.in @@ -71,6 +71,7 @@ EXPORTS = \ nsIDragSessionOS2.h \ nsIXRemoteWidgetHelper.h \ nsIPluginWidget.h \ + nsINativeKeyBindings.h \ $(NULL) ifneq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) diff --git a/widget/public/nsINativeKeyBindings.h b/widget/public/nsINativeKeyBindings.h new file mode 100644 index 00000000000..f41151877f0 --- /dev/null +++ b/widget/public/nsINativeKeyBindings.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is + * IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner + * + * 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 + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsINativeKeyBindings_h_ +#define nsINativeKeyBindings_h_ + +#include "nsISupports.h" + +#define NS_INATIVEKEYBINDINGS_IID \ +{0x606c54e7, 0x0593, 0x4750, {0x99, 0xd9, 0x4e, 0x1b, 0xcc, 0xec, 0x98, 0xd9}} + +#define NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX \ + "@mozilla.org/widget/native-key-bindings;1?type=" + +struct nsNativeKeyEvent +{ + PRUint32 keyCode; + PRUint32 charCode; + PRBool altKey; + PRBool ctrlKey; + PRBool shiftKey; + PRBool metaKey; +}; + +class nsINativeKeyBindings : public nsISupports +{ + public: + typedef void (*DoCommandCallback)(const char *, void*); + + NS_DEFINE_STATIC_IID_ACCESSOR(NS_INATIVEKEYBINDINGS_IID) + + virtual NS_HIDDEN_(PRBool) KeyDown(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, + void *aCallbackData) = 0; + + virtual NS_HIDDEN_(PRBool) KeyPress(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, + void *aCallbackData) = 0; + + virtual NS_HIDDEN_(PRBool) KeyUp(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, + void *aCallbackData) = 0; +}; + +#endif diff --git a/widget/src/gtk2/Makefile.in b/widget/src/gtk2/Makefile.in index 761f4c263e7..31f891a0dee 100644 --- a/widget/src/gtk2/Makefile.in +++ b/widget/src/gtk2/Makefile.in @@ -81,6 +81,7 @@ CPPSRCS = \ nsDragService.cpp \ nsFilePicker.cpp \ nsSound.cpp \ + nsNativeKeyBindings.cpp \ $(NULL) # build our subdirs, too diff --git a/widget/src/gtk2/nsGtkKeyUtils.cpp b/widget/src/gtk2/nsGtkKeyUtils.cpp index 4db91564bbd..29b458bd41d 100644 --- a/widget/src/gtk2/nsGtkKeyUtils.cpp +++ b/widget/src/gtk2/nsGtkKeyUtils.cpp @@ -215,6 +215,53 @@ GdkKeyCodeToDOMKeyCode(int aKeysym) return((int)0); } +int +DOMKeyCodeToGdkKeyCode(PRUint32 aKeysym) +{ + int i, length = 0; + + // First, try to handle alphanumeric input, not listed in nsKeycodes: + // most likely, more letters will be getting typed in than things in + // the key list, so we will look through these first. + + if (aKeysym >= NS_VK_A && aKeysym <= NS_VK_Z) + // gdk and DOM both use the ASCII codes for these keys. + return aKeysym; + + // numbers + if (aKeysym >= NS_VK_0 && aKeysym <= NS_VK_9) + // gdk and DOM both use the ASCII codes for these keys. + return aKeysym - GDK_0 + NS_VK_0; + + // keypad numbers + if (aKeysym >= NS_VK_NUMPAD0 && aKeysym <= NS_VK_NUMPAD9) + return aKeysym - NS_VK_NUMPAD0 + GDK_KP_0; + + // map Sun Keyboard special keysyms + if (IS_XSUN_XSERVER(GDK_DISPLAY())) { + length = NS_ARRAY_LENGTH(nsSunKeycodes); + for (i = 0; i < length; ++i) { + if (nsSunKeycodes[i].vkCode == aKeysym) { + return nsSunKeycodes[i].keysym; + } + } + } + + // misc other things + length = NS_ARRAY_LENGTH(nsKeycodes); + for (i = 0; i < length; ++i) { + if (nsKeycodes[i].vkCode == aKeysym) { + return nsKeycodes[i].keysym; + } + } + + // function keys + if (aKeysym >= NS_VK_F1 && aKeysym <= NS_VK_F9) + return aKeysym - NS_VK_F1 + GDK_F1; + + return 0; +} + // Convert gdk key event keyvals to char codes if printable, 0 otherwise PRUint32 nsConvertCharCodeToUnicode(GdkEventKey* aEvent) { diff --git a/widget/src/gtk2/nsGtkKeyUtils.h b/widget/src/gtk2/nsGtkKeyUtils.h index a5d60f3942c..036c116bbb8 100644 --- a/widget/src/gtk2/nsGtkKeyUtils.h +++ b/widget/src/gtk2/nsGtkKeyUtils.h @@ -40,6 +40,7 @@ #define __nsGdkKeyUtils_h__ int GdkKeyCodeToDOMKeyCode (int aKeysym); +int DOMKeyCodeToGdkKeyCode (PRUint32 aKeysym); PRUint32 nsConvertCharCodeToUnicode (GdkEventKey* aEvent); #endif /* __nsGdkKeyUtils_h__ */ diff --git a/widget/src/gtk2/nsNativeKeyBindings.cpp b/widget/src/gtk2/nsNativeKeyBindings.cpp new file mode 100644 index 00000000000..7dcc3c7af99 --- /dev/null +++ b/widget/src/gtk2/nsNativeKeyBindings.cpp @@ -0,0 +1,296 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is + * IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner + * + * 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 + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsNativeKeyBindings.h" +#include "nsString.h" +#include "nsMemory.h" +#include "nsGtkKeyUtils.h" + +#include +#include +#include +#include + +static nsINativeKeyBindings::DoCommandCallback gCurrentCallback; +static void *gCurrentCallbackData; + +// Common GtkEntry and GtkTextView signals +static void +copy_clipboard_cb(GtkWidget *w, gpointer user_data) +{ + gCurrentCallback("cmd_copy", gCurrentCallbackData); + g_signal_stop_emission_by_name(w, "copy_clipboard"); +} + +static void +cut_clipboard_cb(GtkWidget *w, gpointer user_data) +{ + gCurrentCallback("cmd_cut", gCurrentCallbackData); + g_signal_stop_emission_by_name(w, "cut_clipboard"); +} + +// GTK distinguishes between display lines (wrapped, as they appear on the +// screen) and paragraphs, which are runs of text terminated by a newline. +// We don't have this distinction, so we always use editor's notion of +// lines, which are newline-terminated. + +static const char *const sDeleteCommands[][2] = { + // backward, forward + { "cmd_deleteCharBackward", "cmd_deleteCharForward" }, // CHARS + { "cmd_deleteWordBackward", "cmd_deleteWordForward" }, // WORD_ENDS + { "cmd_deleteWordBackward", "cmd_deleteWordForward" }, // WORDS + { "cmd_deleteToBeginningOfLine", "cmd_deleteToEndOfLine" }, // LINES + { "cmd_deleteToBeginningOfLine", "cmd_deleteToEndOfLine" }, // LINE_ENDS + { "cmd_deleteToBeginningOfLine", "cmd_deleteToEndOfLine" }, // PARAGRAPH_ENDS + { "cmd_deleteToBeginningOfLine", "cmd_deleteToEndOfLine" }, // PARAGRAPHS + // This deletes from the end of the previous word to the beginning of the + // next word, but only if the caret is not in a word. + // XXX need to implement in editor + { nsnull, nsnull } // WHITESPACE +}; + +static void +delete_from_cursor_cb(GtkWidget *w, GtkDeleteType del_type, + gint count, gpointer user_data) +{ + g_signal_stop_emission_by_name(w, "delete_from_cursor"); + + PRBool forward = count > 0; + if (PRUint32(del_type) >= NS_ARRAY_LENGTH(sDeleteCommands)) { + // unsupported deletion type + return; + } + + if (del_type == GTK_DELETE_WORDS) { + // This works like word_ends, except we first move the caret to the + // beginning/end of the current word. + if (forward) { + gCurrentCallback("cmd_wordNext", gCurrentCallbackData); + gCurrentCallback("cmd_wordPrevious", gCurrentCallbackData); + } else { + gCurrentCallback("cmd_wordPrevious", gCurrentCallbackData); + gCurrentCallback("cmd_wordNext", gCurrentCallbackData); + } + } else if (del_type == GTK_DELETE_DISPLAY_LINES || + del_type == GTK_DELETE_PARAGRAPHS) { + + // This works like display_line_ends, except we first move the caret to the + // beginning/end of the current line. + if (forward) { + gCurrentCallback("cmd_beginLine", gCurrentCallbackData); + } else { + gCurrentCallback("cmd_endLine", gCurrentCallbackData); + } + } + + const char *cmd = sDeleteCommands[del_type][forward]; + if (!cmd) + return; // unsupported command + + count = PR_ABS(count); + for (int i = 0; i < count; ++i) { + gCurrentCallback(cmd, gCurrentCallbackData); + } +} + +static const char *const sMoveCommands[][2][2] = { + // non-extend { backward, forward }, extend { backward, forward } + // GTK differentiates between logical position, which is prev/next, + // and visual position, which is always left/right. + // We should fix this to work the same way for RTL text input. + { // LOGICAL_POSITIONS + { "cmd_charPrevious", "cmd_charNext" }, + { "cmd_selectCharPrevious", "cmd_selectCharNext" } + }, + { // VISUAL_POSITIONS + { "cmd_charPrevious", "cmd_charNext" }, + { "cmd_selectCharPrevious", "cmd_selectCharNext" } + }, + { // WORDS + { "cmd_wordPrevious", "cmd_wordNext" }, + { "cmd_selectWordPrevious", "cmd_selectWordNext" } + }, + { // DISPLAY_LINES + { "cmd_linePrevious", "cmd_lineNext" }, + { "cmd_selectLinePrevious", "cmd_selectLineNext" } + }, + { // DISPLAY_LINE_ENDS + { "cmd_beginLine", "cmd_endLine" }, + { "cmd_selectBeginLine", "cmd_selectEndLine" } + }, + { // PARAGRAPHS + { "cmd_linePrevious", "cmd_lineNext" }, + { "cmd_selectLinePrevious", "cmd_selectLineNext" } + }, + { // PARAGRAPH_ENDS + { "cmd_beginLine", "cmd_endLine" }, + { "cmd_selectBeginLine", "cmd_selectEndLine" } + }, + { // PAGES + { "cmd_movePageUp", "cmd_movePageDown" }, + { "cmd_selectPageUp", "cmd_selectPageDown" } + }, + { // BUFFER_ENDS + { "cmd_moveTop", "cmd_moveBottom" }, + { "cmd_selectTop", "cmd_selectBottom" } + }, + { // HORIZONTAL_PAGES (unsupported) + { nsnull, nsnull }, + { nsnull, nsnull } + } +}; + +static void +move_cursor_cb(GtkWidget *w, GtkMovementStep step, gint count, + gboolean extend_selection, gpointer user_data) +{ + g_signal_stop_emission_by_name(w, "move_cursor"); + PRBool forward = count > 0; + if (PRUint32(step) >= NS_ARRAY_LENGTH(sMoveCommands)) { + // unsupported movement type + return; + } + + const char *cmd = sMoveCommands[step][extend_selection][forward]; + if (!cmd) + return; // unsupported command + + + count = PR_ABS(count); + for (int i = 0; i < count; ++i) { + gCurrentCallback(cmd, gCurrentCallbackData); + } +} + +static void +paste_clipboard_cb(GtkWidget *w, gpointer user_data) +{ + gCurrentCallback("cmd_paste", gCurrentCallbackData); + g_signal_stop_emission_by_name(w, "paste_clipboard"); +} + +// GtkTextView-only signals +static void +select_all_cb(GtkWidget *w, gboolean select, gpointer user_data) +{ + gCurrentCallback("cmd_selectAll", gCurrentCallbackData); + g_signal_stop_emission_by_name(w, "select_all"); +} + +void +nsNativeKeyBindings::Init(NativeKeyBindingsType aType) +{ + switch (aType) { + case eKeyBindings_Input: + mNativeTarget = gtk_entry_new(); + break; + case eKeyBindings_TextArea: + mNativeTarget = gtk_text_view_new(); + g_signal_connect(G_OBJECT(mNativeTarget), "select_all", + G_CALLBACK(select_all_cb), this); + break; + } + + g_signal_connect(G_OBJECT(mNativeTarget), "copy_clipboard", + G_CALLBACK(copy_clipboard_cb), this); + g_signal_connect(G_OBJECT(mNativeTarget), "cut_clipboard", + G_CALLBACK(cut_clipboard_cb), this); + g_signal_connect(G_OBJECT(mNativeTarget), "delete_from_cursor", + G_CALLBACK(delete_from_cursor_cb), this); + g_signal_connect(G_OBJECT(mNativeTarget), "move_cursor", + G_CALLBACK(move_cursor_cb), this); + g_signal_connect(G_OBJECT(mNativeTarget), "paste_clipboard", + G_CALLBACK(paste_clipboard_cb), this); +} + +nsNativeKeyBindings::~nsNativeKeyBindings() +{ + gtk_widget_destroy(mNativeTarget); +} + +NS_IMPL_ISUPPORTS1(nsNativeKeyBindings, nsINativeKeyBindings) + +PRBool +nsNativeKeyBindings::KeyDown(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, void *aCallbackData) +{ + return PR_FALSE; +} + +PRBool +nsNativeKeyBindings::KeyPress(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, void *aCallbackData) +{ + PRUint32 keyCode; + + if (aEvent.charCode != 0) + keyCode = gdk_unicode_to_keyval(aEvent.charCode); + else + keyCode = DOMKeyCodeToGdkKeyCode(aEvent.keyCode); + + int modifiers = 0; + if (aEvent.altKey) + modifiers |= GDK_MOD1_MASK; + if (aEvent.ctrlKey) + modifiers |= GDK_CONTROL_MASK; + if (aEvent.shiftKey) + modifiers |= GDK_SHIFT_MASK; + // we don't support meta + + gCurrentCallback = aCallback; + gCurrentCallbackData = aCallbackData; + + PRBool handled = PR_FALSE; + + if (gtk_bindings_activate(GTK_OBJECT(mNativeTarget), + keyCode, GdkModifierType(modifiers))) { + handled = PR_TRUE; + } + + gCurrentCallback = nsnull; + gCurrentCallbackData = nsnull; + + return handled; +} + +PRBool +nsNativeKeyBindings::KeyUp(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, void *aCallbackData) +{ + return PR_FALSE; +} diff --git a/widget/src/gtk2/nsNativeKeyBindings.h b/widget/src/gtk2/nsNativeKeyBindings.h new file mode 100644 index 00000000000..3baece47055 --- /dev/null +++ b/widget/src/gtk2/nsNativeKeyBindings.h @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla. + * + * The Initial Developer of the Original Code is + * IBM Corporation. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner + * + * 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 + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsNativeKeyBindings_h_ +#define nsNativeKeyBindings_h_ + +// X.h defines KeyPress +#ifdef KeyPress +#undef KeyPress +#endif + +#include "nsINativeKeyBindings.h" +#include + +enum NativeKeyBindingsType { + eKeyBindings_Input, + eKeyBindings_TextArea +}; + +#define NS_NATIVEKEYBINDINGSINPUT_CID \ +{0x5c337258, 0xa580, 0x472e, {0x86, 0x15, 0xf2, 0x77, 0xdd, 0xc5, 0xbb, 0x06}} + +#define NS_NATIVEKEYBINDINGSINPUT_CONTRACTID \ +NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input" + +#define NS_NATIVEKEYBINDINGSTEXTAREA_CID \ +{0x2a898043, 0x180f, 0x4c8b, {0x8e, 0x54, 0x41, 0x0c, 0x7a, 0x54, 0x0f, 0x27}} + +#define NS_NATIVEKEYBINDINGSTEXTAREA_CONTRACTID \ +NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea" + +class nsNativeKeyBindings : public nsINativeKeyBindings +{ +public: + NS_HIDDEN_(void) Init(NativeKeyBindingsType aType); + + NS_DECL_ISUPPORTS + + // nsINativeKeyBindings + virtual NS_HIDDEN_(PRBool) KeyDown(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, + void *aCallbackData); + + virtual NS_HIDDEN_(PRBool) KeyPress(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, + void *aCallbackData); + + virtual NS_HIDDEN_(PRBool) KeyUp(const nsNativeKeyEvent& aEvent, + DoCommandCallback aCallback, + void *aCallbackData); + +private: + ~nsNativeKeyBindings() NS_HIDDEN; + + GtkWidget *mNativeTarget; +}; + +#endif diff --git a/widget/src/gtk2/nsWidgetFactory.cpp b/widget/src/gtk2/nsWidgetFactory.cpp index e3bc507476e..41eeed704b3 100644 --- a/widget/src/gtk2/nsWidgetFactory.cpp +++ b/widget/src/gtk2/nsWidgetFactory.cpp @@ -51,6 +51,7 @@ #include "nsFilePicker.h" #include "nsSound.h" #include "nsBidiKeyboard.h" +#include "nsNativeKeyBindings.h" #include "nsIComponentRegistrar.h" #include "nsComponentManagerUtils.h" @@ -147,6 +148,49 @@ nsFilePickerUnregisterSelf(nsIComponentManager *aCompMgr, return rv; } +static NS_IMETHODIMP +nsNativeKeyBindingsConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult, + NativeKeyBindingsType aKeyBindingsType) +{ + nsresult rv; + + nsNativeKeyBindings *inst; + + *aResult = NULL; + if (NULL != aOuter) { + rv = NS_ERROR_NO_AGGREGATION; + return rv; + } + + NS_NEWXPCOM(inst, nsNativeKeyBindings); + if (NULL == inst) { + rv = NS_ERROR_OUT_OF_MEMORY; + return rv; + } + NS_ADDREF(inst); + inst->Init(aKeyBindingsType); + rv = inst->QueryInterface(aIID, aResult); + NS_RELEASE(inst); + + return rv; +} + +static NS_IMETHODIMP +nsNativeKeyBindingsInputConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + return nsNativeKeyBindingsConstructor(aOuter, aIID, aResult, + eKeyBindings_Input); +} + +static NS_IMETHODIMP +nsNativeKeyBindingsTextAreaConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) +{ + return nsNativeKeyBindingsConstructor(aOuter, aIID, aResult, + eKeyBindings_TextArea); +} static const nsModuleComponentInfo components[] = { @@ -204,6 +248,14 @@ static const nsModuleComponentInfo components[] = NS_BIDIKEYBOARD_CID, "@mozilla.org/widget/bidikeyboard;1", nsBidiKeyboardConstructor }, + { "Input Native Keybindings", + NS_NATIVEKEYBINDINGSINPUT_CID, + NS_NATIVEKEYBINDINGSINPUT_CONTRACTID, + nsNativeKeyBindingsInputConstructor }, + { "TextArea Native Keybindings", + NS_NATIVEKEYBINDINGSTEXTAREA_CID, + NS_NATIVEKEYBINDINGSTEXTAREA_CONTRACTID, + nsNativeKeyBindingsTextAreaConstructor } }; PR_STATIC_CALLBACK(void)