2013-05-20 17:48:32 +00:00
|
|
|
/*
|
|
|
|
* gtkimmodulequartz
|
|
|
|
* Copyright (C) 2011 Hiroyuki Yamamoto
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2014-01-12 12:56:49 +00:00
|
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
2013-05-20 17:48:32 +00:00
|
|
|
*
|
|
|
|
* $Id:$
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "gtk/gtkintl.h"
|
|
|
|
#include "gtk/gtkimmodule.h"
|
|
|
|
|
|
|
|
#include "gdk/quartz/gdkquartz.h"
|
|
|
|
#include "gdk/quartz/GdkQuartzView.h"
|
|
|
|
|
|
|
|
#define GTK_IM_CONTEXT_TYPE_QUARTZ (type_quartz)
|
|
|
|
#define GTK_IM_CONTEXT_QUARTZ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_IM_CONTEXT_TYPE_QUARTZ, GtkIMContextQuartz))
|
|
|
|
#define GTK_IM_CONTEXT_QUARTZ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GTK_IM_CONTEXT_TYPE_QUARTZ, GtkIMContextQuartzClass))
|
|
|
|
|
|
|
|
typedef struct _GtkIMContextQuartz
|
|
|
|
{
|
|
|
|
GtkIMContext parent;
|
|
|
|
GtkIMContext *slave;
|
|
|
|
GdkWindow *client_window;
|
|
|
|
gchar *preedit_str;
|
|
|
|
unsigned int cursor_index;
|
|
|
|
unsigned int selected_len;
|
|
|
|
GdkRectangle *cursor_rect;
|
|
|
|
gboolean focused;
|
|
|
|
} GtkIMContextQuartz;
|
|
|
|
|
|
|
|
typedef struct _GtkIMContextQuartzClass
|
|
|
|
{
|
|
|
|
GtkIMContextClass parent_class;
|
|
|
|
} GtkIMContextQuartzClass;
|
|
|
|
|
|
|
|
GType type_quartz = 0;
|
|
|
|
static GObjectClass *parent_class;
|
|
|
|
|
|
|
|
static const GtkIMContextInfo imquartz_info =
|
|
|
|
{
|
|
|
|
"quartz",
|
2015-02-24 03:00:55 +00:00
|
|
|
NC_("input method menu", "Mac OS X Quartz"),
|
2013-05-20 17:48:32 +00:00
|
|
|
GETTEXT_PACKAGE,
|
|
|
|
GTK_LOCALEDIR,
|
|
|
|
"ja:ko:zh:*",
|
|
|
|
};
|
|
|
|
|
|
|
|
static const GtkIMContextInfo *info_list[] =
|
|
|
|
{
|
|
|
|
&imquartz_info,
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifndef INCLUDE_IM_quartz
|
|
|
|
#define MODULE_ENTRY(type,function) G_MODULE_EXPORT type im_module_ ## function
|
|
|
|
#else
|
|
|
|
#define MODULE_ENTRY(type, function) type _gtk_immodule_quartz_ ## function
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
|
|
|
quartz_get_preedit_string (GtkIMContext *context,
|
|
|
|
gchar **str,
|
|
|
|
PangoAttrList **attrs,
|
|
|
|
gint *cursor_pos)
|
|
|
|
{
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_get_preedit_string\n"));
|
|
|
|
|
|
|
|
if (str)
|
|
|
|
*str = qc->preedit_str ? g_strdup (qc->preedit_str) : g_strdup ("");
|
|
|
|
|
|
|
|
if (attrs)
|
|
|
|
{
|
|
|
|
*attrs = pango_attr_list_new ();
|
|
|
|
int len = g_utf8_strlen (*str, -1);
|
|
|
|
gchar *ch = *str;
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
PangoAttribute *attr;
|
|
|
|
int i = 0;
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
gchar *s = ch;
|
|
|
|
ch = g_utf8_next_char (ch);
|
|
|
|
|
|
|
|
if (i >= qc->cursor_index &&
|
|
|
|
i < qc->cursor_index + qc->selected_len)
|
|
|
|
attr = pango_attr_underline_new (PANGO_UNDERLINE_DOUBLE);
|
|
|
|
else
|
|
|
|
attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
|
|
|
|
|
|
|
|
attr->start_index = s - *str;
|
|
|
|
if (!*ch)
|
|
|
|
attr->end_index = attr->start_index + strlen (s);
|
|
|
|
else
|
|
|
|
attr->end_index = ch - *str;
|
|
|
|
|
|
|
|
pango_attr_list_change (*attrs, attr);
|
|
|
|
|
|
|
|
if (!*ch)
|
|
|
|
break;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (cursor_pos)
|
|
|
|
*cursor_pos = qc->cursor_index;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
2013-08-16 16:54:34 +00:00
|
|
|
output_result (GtkIMContext *context,
|
|
|
|
GdkWindow *win)
|
2013-05-20 17:48:32 +00:00
|
|
|
{
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
gboolean retval = FALSE;
|
|
|
|
gchar *fixed_str, *marked_str;
|
|
|
|
|
|
|
|
fixed_str = g_object_get_data (G_OBJECT (win), TIC_INSERT_TEXT);
|
|
|
|
marked_str = g_object_get_data (G_OBJECT (win), TIC_MARKED_TEXT);
|
|
|
|
if (fixed_str)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("tic-insert-text: %s\n", fixed_str));
|
|
|
|
g_free (qc->preedit_str);
|
|
|
|
qc->preedit_str = NULL;
|
|
|
|
g_object_set_data (G_OBJECT (win), TIC_INSERT_TEXT, NULL);
|
|
|
|
g_signal_emit_by_name (context, "commit", fixed_str);
|
|
|
|
g_signal_emit_by_name (context, "preedit_changed");
|
|
|
|
|
|
|
|
unsigned int filtered =
|
|
|
|
GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (win),
|
|
|
|
GIC_FILTER_KEY));
|
|
|
|
GTK_NOTE (MISC, g_print ("filtered, %d\n", filtered));
|
|
|
|
if (filtered)
|
|
|
|
retval = TRUE;
|
|
|
|
else
|
|
|
|
retval = FALSE;
|
|
|
|
}
|
|
|
|
if (marked_str)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("tic-marked-text: %s\n", marked_str));
|
|
|
|
qc->cursor_index =
|
|
|
|
GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (win),
|
|
|
|
TIC_SELECTED_POS));
|
|
|
|
qc->selected_len =
|
|
|
|
GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (win),
|
|
|
|
TIC_SELECTED_LEN));
|
|
|
|
g_free (qc->preedit_str);
|
|
|
|
qc->preedit_str = g_strdup (marked_str);
|
|
|
|
g_object_set_data (G_OBJECT (win), TIC_MARKED_TEXT, NULL);
|
|
|
|
g_signal_emit_by_name (context, "preedit_changed");
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
2013-08-29 21:12:09 +00:00
|
|
|
if (!fixed_str && !marked_str)
|
2013-08-16 16:54:34 +00:00
|
|
|
{
|
|
|
|
if (qc->preedit_str && strlen (qc->preedit_str) > 0)
|
|
|
|
retval = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
quartz_filter_keypress (GtkIMContext *context,
|
|
|
|
GdkEventKey *event)
|
|
|
|
{
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
gboolean retval;
|
|
|
|
NSView *nsview;
|
|
|
|
GdkWindow *win;
|
|
|
|
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_filter_keypress\n"));
|
|
|
|
|
|
|
|
if (!qc->client_window)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
nsview = gdk_quartz_window_get_nsview (qc->client_window);
|
|
|
|
if (GDK_IS_WINDOW (nsview))
|
|
|
|
/* it gets GDK_WINDOW in some cases */
|
|
|
|
return gtk_im_context_filter_keypress (qc->slave, event);
|
|
|
|
else
|
|
|
|
win = (GdkWindow *)[ (GdkQuartzView *)nsview gdkWindow];
|
|
|
|
GTK_NOTE (MISC, g_print ("client_window: %p, win: %p, nsview: %p\n",
|
|
|
|
qc->client_window, win, nsview));
|
|
|
|
|
|
|
|
NSEvent *nsevent = gdk_quartz_event_get_nsevent ((GdkEvent *)event);
|
|
|
|
|
|
|
|
if (!nsevent)
|
|
|
|
{
|
|
|
|
if (event->hardware_keycode == 0 && event->keyval == 0xffffff)
|
|
|
|
/* update text input changes by mouse events */
|
2013-08-29 21:12:09 +00:00
|
|
|
return output_result (context, win);
|
2013-08-16 16:54:34 +00:00
|
|
|
else
|
|
|
|
return gtk_im_context_filter_keypress (qc->slave, event);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event->type == GDK_KEY_RELEASE)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (event->hardware_keycode == 55) /* Command */
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
NSEventType etype = [nsevent type];
|
|
|
|
if (etype == NSKeyDown)
|
|
|
|
{
|
|
|
|
g_object_set_data (G_OBJECT (win), TIC_IN_KEY_DOWN,
|
|
|
|
GUINT_TO_POINTER (TRUE));
|
|
|
|
[nsview keyDown: nsevent];
|
|
|
|
}
|
|
|
|
/* JIS_Eisu || JIS_Kana */
|
|
|
|
if (event->hardware_keycode == 102 || event->hardware_keycode == 104)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
retval = output_result(context, win);
|
|
|
|
g_object_set_data (G_OBJECT (win), TIC_IN_KEY_DOWN,
|
|
|
|
GUINT_TO_POINTER (FALSE));
|
2013-05-20 17:48:32 +00:00
|
|
|
GTK_NOTE (MISC, g_print ("quartz_filter_keypress done\n"));
|
2013-08-16 16:54:34 +00:00
|
|
|
|
2013-05-20 17:48:32 +00:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-08-15 16:44:02 +00:00
|
|
|
static void
|
|
|
|
discard_preedit (GtkIMContext *context)
|
|
|
|
{
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
|
|
|
|
if (!qc->client_window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
NSView *nsview = gdk_quartz_window_get_nsview (qc->client_window);
|
|
|
|
if (!nsview)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (GDK_IS_WINDOW (nsview))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* reset any partial input for this NSView */
|
|
|
|
[(GdkQuartzView *)nsview unmarkText];
|
|
|
|
NSInputManager *currentInputManager = [NSInputManager currentInputManager];
|
|
|
|
[currentInputManager markedTextAbandoned:nsview];
|
|
|
|
|
2013-08-15 20:08:34 +00:00
|
|
|
if (qc->preedit_str && strlen (qc->preedit_str) > 0)
|
2013-08-15 16:44:02 +00:00
|
|
|
{
|
|
|
|
g_signal_emit_by_name (context, "commit", qc->preedit_str);
|
|
|
|
|
|
|
|
g_free (qc->preedit_str);
|
|
|
|
qc->preedit_str = NULL;
|
|
|
|
g_signal_emit_by_name (context, "preedit_changed");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-05-20 17:48:32 +00:00
|
|
|
static void
|
|
|
|
quartz_reset (GtkIMContext *context)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_reset\n"));
|
2013-08-15 16:44:02 +00:00
|
|
|
discard_preedit (context);
|
2013-05-20 17:48:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
quartz_set_client_window (GtkIMContext *context, GdkWindow *window)
|
|
|
|
{
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_set_client_window: %p\n", window));
|
|
|
|
|
|
|
|
qc->client_window = window;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
quartz_focus_in (GtkIMContext *context)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_focus_in\n"));
|
|
|
|
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
qc->focused = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
quartz_focus_out (GtkIMContext *context)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_focus_out\n"));
|
|
|
|
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
qc->focused = FALSE;
|
2013-08-15 16:44:02 +00:00
|
|
|
|
|
|
|
/* Commit any partially built strings or it'll mess up other GTK+ widgets in the window */
|
|
|
|
discard_preedit (context);
|
2013-05-20 17:48:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
quartz_set_cursor_location (GtkIMContext *context, GdkRectangle *area)
|
|
|
|
{
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (context);
|
|
|
|
gint x, y;
|
|
|
|
NSView *nsview;
|
|
|
|
GdkWindow *win;
|
|
|
|
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_set_cursor_location\n"));
|
|
|
|
|
|
|
|
if (!qc->client_window)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!qc->focused)
|
|
|
|
return;
|
|
|
|
|
|
|
|
qc->cursor_rect->x = area->x;
|
|
|
|
qc->cursor_rect->y = area->y;
|
|
|
|
qc->cursor_rect->width = area->width;
|
|
|
|
qc->cursor_rect->height = area->height;
|
|
|
|
|
|
|
|
gdk_window_get_origin (qc->client_window, &x, &y);
|
|
|
|
|
|
|
|
qc->cursor_rect->x = area->x + x;
|
|
|
|
qc->cursor_rect->y = area->y + y;
|
|
|
|
|
|
|
|
nsview = gdk_quartz_window_get_nsview (qc->client_window);
|
|
|
|
if (GDK_IS_WINDOW (nsview))
|
|
|
|
/* it returns GDK_WINDOW in some cases */
|
|
|
|
return;
|
|
|
|
|
|
|
|
win = (GdkWindow *)[ (GdkQuartzView*)nsview gdkWindow];
|
|
|
|
g_object_set_data (G_OBJECT (win), GIC_CURSOR_RECT, qc->cursor_rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
quartz_set_use_preedit (GtkIMContext *context, gboolean use_preedit)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("quartz_set_use_preedit: %d\n", use_preedit));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
commit_cb (GtkIMContext *context, const gchar *str, GtkIMContextQuartz *qc)
|
|
|
|
{
|
|
|
|
g_signal_emit_by_name (qc, "commit", str);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
imquartz_finalize (GObject *obj)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("imquartz_finalize\n"));
|
|
|
|
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (obj);
|
|
|
|
g_free (qc->preedit_str);
|
|
|
|
qc->preedit_str = NULL;
|
|
|
|
g_free (qc->cursor_rect);
|
|
|
|
qc->cursor_rect = NULL;
|
|
|
|
|
|
|
|
g_signal_handlers_disconnect_by_func (qc->slave, (gpointer)commit_cb, qc);
|
|
|
|
g_object_unref (qc->slave);
|
|
|
|
|
|
|
|
parent_class->finalize (obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gtk_im_context_quartz_class_init (GtkIMContextClass *klass)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("gtk_im_context_quartz_class_init\n"));
|
|
|
|
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
|
|
|
|
klass->get_preedit_string = quartz_get_preedit_string;
|
|
|
|
klass->filter_keypress = quartz_filter_keypress;
|
|
|
|
klass->reset = quartz_reset;
|
|
|
|
klass->set_client_window = quartz_set_client_window;
|
|
|
|
klass->focus_in = quartz_focus_in;
|
|
|
|
klass->focus_out = quartz_focus_out;
|
|
|
|
klass->set_cursor_location = quartz_set_cursor_location;
|
|
|
|
klass->set_use_preedit = quartz_set_use_preedit;
|
|
|
|
|
|
|
|
object_class->finalize = imquartz_finalize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gtk_im_context_quartz_init (GtkIMContext *im_context)
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("gtk_im_context_quartz_init\n"));
|
|
|
|
|
|
|
|
GtkIMContextQuartz *qc = GTK_IM_CONTEXT_QUARTZ (im_context);
|
|
|
|
qc->preedit_str = g_strdup ("");
|
|
|
|
qc->cursor_index = 0;
|
|
|
|
qc->selected_len = 0;
|
|
|
|
qc->cursor_rect = g_malloc (sizeof (GdkRectangle));
|
|
|
|
qc->focused = FALSE;
|
|
|
|
|
|
|
|
qc->slave = g_object_new (GTK_TYPE_IM_CONTEXT_SIMPLE, NULL);
|
|
|
|
g_signal_connect (G_OBJECT (qc->slave), "commit", G_CALLBACK (commit_cb), qc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
gtk_im_context_quartz_register_type (GTypeModule *module)
|
|
|
|
{
|
|
|
|
const GTypeInfo object_info =
|
|
|
|
{
|
|
|
|
sizeof (GtkIMContextQuartzClass),
|
|
|
|
(GBaseInitFunc) NULL,
|
|
|
|
(GBaseFinalizeFunc) NULL,
|
|
|
|
(GClassInitFunc) gtk_im_context_quartz_class_init,
|
|
|
|
NULL, /* class_finalize */
|
|
|
|
NULL, /* class_data */
|
|
|
|
sizeof (GtkIMContextQuartz),
|
|
|
|
0,
|
|
|
|
(GInstanceInitFunc) gtk_im_context_quartz_init,
|
|
|
|
};
|
|
|
|
|
|
|
|
type_quartz =
|
|
|
|
g_type_module_register_type (module,
|
|
|
|
GTK_TYPE_IM_CONTEXT,
|
|
|
|
"GtkIMContextQuartz",
|
|
|
|
&object_info, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_ENTRY (void, init) (GTypeModule * module)
|
|
|
|
{
|
|
|
|
gtk_im_context_quartz_register_type (module);
|
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_ENTRY (void, exit) (void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_ENTRY (void, list) (const GtkIMContextInfo *** contexts, int *n_contexts)
|
|
|
|
{
|
|
|
|
*contexts = info_list;
|
|
|
|
*n_contexts = G_N_ELEMENTS (info_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
MODULE_ENTRY (GtkIMContext *, create) (const gchar * context_id)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (context_id, NULL);
|
|
|
|
|
|
|
|
if (!strcmp (context_id, "quartz"))
|
|
|
|
{
|
|
|
|
GTK_NOTE (MISC, g_print ("immodule_quartz create\n"));
|
|
|
|
return g_object_new (type_quartz, NULL);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return NULL;
|
|
|
|
}
|