1
// -*- Mode: C++; indent-tabs-mode: ni; tab-width: 2 -*-
3
* Copyright (C) 2011 Canonical Ltd
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Pubic License version 3 as
7
* published by the Free Software Foundation.
9
* This program is distributed in the hope that it wi be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more detais.
14
* You shoud have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
17
* Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
22
#include "IMTextEntry.h"
24
#include <boost/lexical_cast.hpp>
25
#include <NuxCore/Logger.h>
26
#include <UnityCore/GLibWrapper.h>
35
nux::logging::Logger logger("unity.dash.imtextentry");
38
NUX_IMPLEMENT_OBJECT_TYPE(IMTextEntry);
40
IMTextEntry::IMTextEntry()
41
: TextEntry("", "", 80085)
49
//FIXME: Make event forwarding work before enabling
50
// im_enabled_ ? SetupMultiIM() : SetupSimpleIM();
53
FocusChanged.connect(sigc::mem_fun(this, &IMTextEntry::OnFocusChanged));
54
OnKeyNavFocusChange.connect(sigc::mem_fun(this, &IMTextEntry::OnFocusChanged));
55
mouse_up.connect(sigc::mem_fun(this, &IMTextEntry::OnMouseButtonUp));
58
IMTextEntry::~IMTextEntry()
61
g_object_unref(im_context_);
63
g_object_unref(client_window_);
66
void IMTextEntry::CheckIMEnabled()
68
const char* module = g_getenv("GTK_IM_MODULE");
70
g_strcmp0(module, "") &&
71
g_strcmp0(module, "gtk-im-context-simple"))
74
LOG_DEBUG(logger) << "Input method support is "
75
<< (im_enabled_ ? "enabled" : "disabled");
78
void IMTextEntry::SetupSimpleIM()
80
im_context_ = gtk_im_context_simple_new();
82
sig_manager_.Add(new Signal<void, GtkIMContext*, char*>(im_context_, "commit", sigc::mem_fun(this, &IMTextEntry::OnCommit)));
83
sig_manager_.Add(new Signal<void, GtkIMContext*>(im_context_, "preedit-changed", sigc::mem_fun(this, &IMTextEntry::OnPreeditChanged)));
84
sig_manager_.Add(new Signal<void, GtkIMContext*>(im_context_, "preedit-start", sigc::mem_fun(this, &IMTextEntry::OnPreeditStart)));
85
sig_manager_.Add(new Signal<void, GtkIMContext*>(im_context_, "preedit-end", sigc::mem_fun(this, &IMTextEntry::OnPreeditEnd)));
88
void IMTextEntry::SetupMultiIM()
90
im_context_ = gtk_im_multicontext_new();
92
sig_manager_.Add(new Signal<void, GtkIMContext*, char*>(im_context_, "commit", sigc::mem_fun(this, &IMTextEntry::OnCommit)));
93
sig_manager_.Add(new Signal<void, GtkIMContext*>(im_context_, "preedit-changed", sigc::mem_fun(this, &IMTextEntry::OnPreeditChanged)));
94
sig_manager_.Add(new Signal<void, GtkIMContext*>(im_context_, "preedit-start", sigc::mem_fun(this, &IMTextEntry::OnPreeditStart)));
95
sig_manager_.Add(new Signal<void, GtkIMContext*>(im_context_, "preedit-end", sigc::mem_fun(this, &IMTextEntry::OnPreeditEnd)));
98
bool IMTextEntry::InspectKeyEvent(unsigned int event_type,
100
const char* character)
102
bool propagate_event = !(TryHandleEvent(event_type, keysym, character));
104
LOG_DEBUG(logger) << "Input method ("
105
<< (im_enabled_ ? gtk_im_multicontext_get_context_id(GTK_IM_MULTICONTEXT(im_context_)) : "simple")
107
<< (propagate_event ? "did not handle " : "handled ")
109
<< (event_type == NUX_KEYDOWN ? "press" : "release")
112
std::string preedit = preedit_string;
113
if (preedit.length() < 1 &&
114
(keysym == NUX_VK_ENTER ||
115
keysym == NUX_KP_ENTER ||
116
keysym == NUX_VK_UP ||
117
keysym == NUX_VK_DOWN ||
118
keysym == NUX_VK_LEFT ||
119
keysym == NUX_VK_RIGHT ||
120
keysym == NUX_VK_LEFT_TAB ||
121
keysym == NUX_VK_TAB ||
122
keysym == NUX_VK_ESCAPE ||
123
keysym == NUX_VK_DELETE ||
124
keysym == NUX_VK_BACKSPACE ||
125
keysym == NUX_VK_HOME ||
126
keysym == NUX_VK_END))
128
propagate_event = true;
131
return propagate_event ? TextEntry::InspectKeyEvent(event_type, keysym, character)
135
bool IMTextEntry::TryHandleEvent(unsigned int eventType,
137
const char* character)
139
nux::Event event = nux::GetGraphicsThread()->GetWindow().GetCurrentEvent();
141
CheckValidClientWindow(event.e_x11_window);
144
KeyEventToGdkEventKey(event, ev);
146
return gtk_im_context_filter_keypress(im_context_, &ev);
149
inline void IMTextEntry::CheckValidClientWindow(Window window)
153
client_window_ = gdk_x11_window_foreign_new_for_display(gdk_display_get_default(), window);
154
gtk_im_context_set_client_window(im_context_, client_window_);
158
gtk_im_context_focus_in(im_context_);
163
void IMTextEntry::KeyEventToGdkEventKey(Event& event, GdkEventKey& gdk_event)
165
gdk_event.type = event.e_event == nux::NUX_KEYDOWN ? GDK_KEY_PRESS : GDK_KEY_RELEASE;
167
gdk_event.window = client_window_;
168
gdk_event.window = 0;
169
gdk_event.send_event = FALSE;
170
gdk_event.time = event.e_x11_timestamp;
171
gdk_event.state = event.e_x11_state;
172
gdk_event.keyval = event.e_keysym;
174
gchar* txt = const_cast<gchar*>(event.GetText());
175
gdk_event.length = strlen(txt);
176
gdk_event.string = txt;
178
gdk_event.hardware_keycode = event.e_x11_keycode;
180
gdk_event.is_modifier = 0;
183
void IMTextEntry::OnFocusChanged(nux::Area* area)
186
LOG_DEBUG(logger) << "Focus changed " << boost::lexical_cast<bool>(GetFocused());
190
gtk_im_context_focus_in(im_context_);
194
gtk_im_context_focus_out(im_context_);
195
gtk_im_context_reset(im_context_);
199
void IMTextEntry::OnCommit(GtkIMContext* context, char* str)
201
LOG_DEBUG(logger) << "Commit: " << str;
204
std::string new_text = GetText() + str;
205
int cursor = cursor_;
206
SetText(new_text.c_str());
207
SetCursor(cursor + strlen(str));
211
void IMTextEntry::OnPreeditChanged(GtkIMContext* context)
213
glib::String preedit;
216
gtk_im_context_get_preedit_string(context, &preedit, NULL, &cursor_pos);
218
LOG_DEBUG(logger) << "Preedit changed: " << preedit;
220
preedit_string = preedit.Str();
223
void IMTextEntry::OnPreeditStart(GtkIMContext* context)
228
LOG_DEBUG(logger) << "Preedit start";
231
void IMTextEntry::OnPreeditEnd(GtkIMContext* context)
235
gtk_im_context_reset(im_context_);
237
LOG_DEBUG(logger) << "Preedit ended";
240
void IMTextEntry::OnMouseButtonUp(int x, int y, unsigned long bflags, unsigned long kflags)
242
if (nux::GetEventButton(bflags) == 3 && im_enabled_)
244
GtkWidget* menu = gtk_menu_new();
245
gtk_im_multicontext_append_menuitems(GTK_IM_MULTICONTEXT(im_context_),
246
GTK_MENU_SHELL(menu));
247
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, GDK_CURRENT_TIME);