314 lines
7.9 KiB
C
314 lines
7.9 KiB
C
/* Pure Gtk+-3 communication module.
|
|
|
|
Copyright (C) 1989, 1993-1994, 2005-2006, 2008-2024 Free Software
|
|
Foundation, Inc.
|
|
|
|
This file is part of GNU Emacs.
|
|
|
|
GNU Emacs is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or (at
|
|
your option) any later version.
|
|
|
|
GNU Emacs 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 General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
|
|
|
|
/* This should be the first include, as it may set up #defines affecting
|
|
interpretation of even the system includes. */
|
|
#include <config.h>
|
|
|
|
#include "pgtkterm.h"
|
|
|
|
static void
|
|
im_context_commit_cb (GtkIMContext *imc,
|
|
gchar *str,
|
|
gpointer user_data)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = user_data;
|
|
struct frame *f = dpyinfo->im.focused_frame;
|
|
|
|
if (dpyinfo->im.context == NULL)
|
|
return;
|
|
if (f == NULL)
|
|
return;
|
|
|
|
pgtk_enqueue_string (f, str);
|
|
}
|
|
|
|
static gboolean
|
|
im_context_retrieve_surrounding_cb (GtkIMContext *imc, gpointer user_data)
|
|
{
|
|
gtk_im_context_set_surrounding (imc, "", -1, 0);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
im_context_delete_surrounding_cb (GtkIMContext *imc, int offset, int n_chars,
|
|
gpointer user_data)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
static Lisp_Object
|
|
make_color_string (PangoAttrColor *pac)
|
|
{
|
|
char buf[256];
|
|
sprintf (buf, "#%02x%02x%02x",
|
|
pac->color.red >> 8, pac->color.green >> 8, pac->color.blue >> 8);
|
|
return build_string (buf);
|
|
}
|
|
|
|
static void
|
|
im_context_preedit_changed_cb (GtkIMContext *imc, gpointer user_data)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = user_data;
|
|
struct frame *f = dpyinfo->im.focused_frame;
|
|
char *str;
|
|
PangoAttrList *attrs;
|
|
int pos;
|
|
|
|
if (dpyinfo->im.context == NULL)
|
|
return;
|
|
if (f == NULL)
|
|
return;
|
|
|
|
gtk_im_context_get_preedit_string (imc, &str, &attrs, &pos);
|
|
|
|
|
|
/*
|
|
* (
|
|
* (TEXT (ul . COLOR) (bg . COLOR) (fg . COLOR))
|
|
* ...
|
|
* )
|
|
*/
|
|
Lisp_Object list = Qnil;
|
|
|
|
PangoAttrIterator *iter;
|
|
iter = pango_attr_list_get_iterator (attrs);
|
|
do
|
|
{
|
|
int st, ed;
|
|
int has_underline = 0;
|
|
Lisp_Object part = Qnil;
|
|
|
|
pango_attr_iterator_range (iter, &st, &ed);
|
|
|
|
if (ed > strlen (str))
|
|
ed = strlen (str);
|
|
if (st >= ed)
|
|
continue;
|
|
|
|
Lisp_Object text = make_string (str + st, ed - st);
|
|
part = Fcons (text, part);
|
|
|
|
PangoAttrInt *ul =
|
|
(PangoAttrInt *) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
|
|
if (ul != NULL)
|
|
{
|
|
if (ul->value != PANGO_UNDERLINE_NONE)
|
|
has_underline = 1;
|
|
}
|
|
|
|
PangoAttrColor *pac;
|
|
if (has_underline)
|
|
{
|
|
pac =
|
|
(PangoAttrColor *) pango_attr_iterator_get (iter,
|
|
PANGO_ATTR_UNDERLINE_COLOR);
|
|
if (pac != NULL)
|
|
part = Fcons (Fcons (Qul, make_color_string (pac)), part);
|
|
else
|
|
part = Fcons (Fcons (Qul, Qt), part);
|
|
}
|
|
|
|
pac =
|
|
(PangoAttrColor *) pango_attr_iterator_get (iter,
|
|
PANGO_ATTR_FOREGROUND);
|
|
if (pac != NULL)
|
|
part = Fcons (Fcons (Qfg, make_color_string (pac)), part);
|
|
|
|
pac =
|
|
(PangoAttrColor *) pango_attr_iterator_get (iter,
|
|
PANGO_ATTR_BACKGROUND);
|
|
if (pac != NULL)
|
|
part = Fcons (Fcons (Qbg, make_color_string (pac)), part);
|
|
|
|
part = Fnreverse (part);
|
|
list = Fcons (part, list);
|
|
}
|
|
while (pango_attr_iterator_next (iter));
|
|
|
|
list = Fnreverse (list);
|
|
pgtk_enqueue_preedit (f, list);
|
|
|
|
g_free (str);
|
|
pango_attr_list_unref (attrs);
|
|
}
|
|
|
|
static void
|
|
im_context_preedit_end_cb (GtkIMContext *imc, gpointer user_data)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = user_data;
|
|
struct frame *f = dpyinfo->im.focused_frame;
|
|
|
|
if (dpyinfo->im.context == NULL)
|
|
return;
|
|
if (f == NULL)
|
|
return;
|
|
|
|
pgtk_enqueue_preedit (f, Qnil);
|
|
}
|
|
|
|
static void
|
|
im_context_preedit_start_cb (GtkIMContext *imc, gpointer user_data)
|
|
{
|
|
}
|
|
|
|
void
|
|
pgtk_im_focus_in (struct frame *f)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
|
|
if (dpyinfo->im.context != NULL)
|
|
{
|
|
gtk_im_context_reset (dpyinfo->im.context);
|
|
gtk_im_context_set_client_window (dpyinfo->im.context,
|
|
gtk_widget_get_window
|
|
(FRAME_GTK_WIDGET (f)));
|
|
gtk_im_context_focus_in (dpyinfo->im.context);
|
|
}
|
|
dpyinfo->im.focused_frame = f;
|
|
}
|
|
|
|
void
|
|
pgtk_im_focus_out (struct frame *f)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
|
|
if (dpyinfo->im.focused_frame == f)
|
|
{
|
|
if (dpyinfo->im.context != NULL)
|
|
{
|
|
gtk_im_context_reset (dpyinfo->im.context);
|
|
gtk_im_context_focus_out (dpyinfo->im.context);
|
|
gtk_im_context_set_client_window (dpyinfo->im.context, NULL);
|
|
}
|
|
dpyinfo->im.focused_frame = NULL;
|
|
}
|
|
}
|
|
|
|
bool
|
|
pgtk_im_filter_keypress (struct frame *f, GdkEventKey * ev)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
|
|
if (dpyinfo->im.context != NULL)
|
|
{
|
|
if (gtk_im_context_filter_keypress (dpyinfo->im.context, ev))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void
|
|
pgtk_im_set_cursor_location (struct frame *f, int x, int y, int width,
|
|
int height)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
|
|
if (dpyinfo->im.context != NULL && dpyinfo->im.focused_frame == f)
|
|
{
|
|
GdkRectangle area = { x, y, width, height };
|
|
gtk_im_context_set_cursor_location (dpyinfo->im.context, &area);
|
|
}
|
|
}
|
|
|
|
static void
|
|
pgtk_im_use_context (struct pgtk_display_info *dpyinfo, bool use_p)
|
|
{
|
|
if (!use_p)
|
|
{
|
|
if (dpyinfo->im.context != NULL)
|
|
{
|
|
gtk_im_context_reset (dpyinfo->im.context);
|
|
gtk_im_context_focus_out (dpyinfo->im.context);
|
|
gtk_im_context_set_client_window (dpyinfo->im.context, NULL);
|
|
|
|
g_object_unref (dpyinfo->im.context);
|
|
dpyinfo->im.context = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dpyinfo->im.context == NULL)
|
|
{
|
|
dpyinfo->im.context = gtk_im_multicontext_new ();
|
|
g_signal_connect (dpyinfo->im.context, "commit",
|
|
G_CALLBACK (im_context_commit_cb), dpyinfo);
|
|
g_signal_connect (dpyinfo->im.context, "retrieve-surrounding",
|
|
G_CALLBACK (im_context_retrieve_surrounding_cb),
|
|
dpyinfo);
|
|
g_signal_connect (dpyinfo->im.context, "delete-surrounding",
|
|
G_CALLBACK (im_context_delete_surrounding_cb),
|
|
dpyinfo);
|
|
g_signal_connect (dpyinfo->im.context, "preedit-changed",
|
|
G_CALLBACK (im_context_preedit_changed_cb),
|
|
dpyinfo);
|
|
g_signal_connect (dpyinfo->im.context, "preedit-end",
|
|
G_CALLBACK (im_context_preedit_end_cb), dpyinfo);
|
|
g_signal_connect (dpyinfo->im.context, "preedit-start",
|
|
G_CALLBACK (im_context_preedit_start_cb),
|
|
dpyinfo);
|
|
gtk_im_context_set_use_preedit (dpyinfo->im.context, TRUE);
|
|
|
|
if (dpyinfo->im.focused_frame)
|
|
pgtk_im_focus_in (dpyinfo->im.focused_frame);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
pgtk_im_init (struct pgtk_display_info *dpyinfo)
|
|
{
|
|
dpyinfo->im.context = NULL;
|
|
|
|
pgtk_im_use_context (dpyinfo, !NILP (Vpgtk_use_im_context_on_new_connection));
|
|
}
|
|
|
|
void
|
|
pgtk_im_finish (struct pgtk_display_info *dpyinfo)
|
|
{
|
|
if (dpyinfo->im.context != NULL)
|
|
g_object_unref (dpyinfo->im.context);
|
|
dpyinfo->im.context = NULL;
|
|
}
|
|
|
|
DEFUN ("pgtk-use-im-context", Fpgtk_use_im_context, Spgtk_use_im_context, 1, 2, 0,
|
|
doc: /* Set whether to use GtkIMContext. */)
|
|
(Lisp_Object use_p, Lisp_Object terminal)
|
|
{
|
|
struct pgtk_display_info *dpyinfo = check_pgtk_display_info (terminal);
|
|
|
|
pgtk_im_use_context (dpyinfo, !NILP (use_p));
|
|
|
|
return Qnil;
|
|
}
|
|
|
|
void
|
|
syms_of_pgtkim (void)
|
|
{
|
|
defsubr (&Spgtk_use_im_context);
|
|
|
|
DEFSYM (Qpgtk_refresh_preedit, "pgtk-refresh-preedit");
|
|
DEFSYM (Qul, "ul");
|
|
DEFSYM (Qfg, "fg");
|
|
DEFSYM (Qbg, "bg");
|
|
|
|
DEFVAR_LISP ("pgtk-use-im-context-on-new-connection", Vpgtk_use_im_context_on_new_connection,
|
|
doc: /* Whether to use GtkIMContext on a new connection.
|
|
If you want to change it after connection, use the `pgtk-use-im-context'
|
|
function. */ );
|
|
Vpgtk_use_im_context_on_new_connection = Qt;
|
|
}
|