app: IM preedit displayed as expected.

When gtk_im_context_get_preedit_string(), we have to inspect the
returned Pango attributes, so that the preedit string can be displayed
in the expected fashion (i.e. as in other programs).
Some input methods in particular would even break the preedit strings
in several chunks of text displayed differently (for instance Japanese),
depending on the cursor position within the preedit string.
This commit is contained in:
Jehan 2016-05-09 00:35:19 +02:00
parent f1dbd57d49
commit 2cfed0cb52
2 changed files with 170 additions and 19 deletions

View File

@ -533,6 +533,27 @@ gimp_text_tool_editor_key_release (GimpTextTool *text_tool,
void
gimp_text_tool_reset_im_context (GimpTextTool *text_tool)
{
/* Cancel any ungoing preedit on reset. */
if (text_tool->preedit_string && *text_tool->preedit_string)
{
GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
GtkTextIter start;
GtkTextIter end;
gtk_text_buffer_get_iter_at_mark (buffer, &start,
text_tool->preedit_start);
gtk_text_buffer_get_iter_at_mark (buffer, &end,
text_tool->preedit_end);
gtk_text_buffer_delete_interactive (buffer, &start, &end, TRUE);
g_free (text_tool->preedit_string);
text_tool->preedit_string = NULL;
gtk_text_buffer_delete_mark (buffer, text_tool->preedit_start);
gtk_text_buffer_delete_mark (buffer, text_tool->preedit_end);
text_tool->preedit_start = NULL;
text_tool->preedit_end = NULL;
}
if (text_tool->needs_im_reset)
{
text_tool->needs_im_reset = FALSE;
@ -1342,40 +1363,148 @@ gimp_text_tool_im_preedit_changed (GtkIMContext *context,
GimpTextTool *text_tool)
{
GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
PangoAttrList *attrs;
if (text_tool->preedit_string)
g_free (text_tool->preedit_string);
{
if (*text_tool->preedit_string)
{
GtkTextIter start;
GtkTextIter end;
gtk_im_context_get_preedit_string (context,
&text_tool->preedit_string, NULL,
&text_tool->preedit_cursor);
gtk_text_buffer_get_iter_at_mark (buffer, &start,
text_tool->preedit_start);
gtk_text_buffer_get_iter_at_mark (buffer, &end,
text_tool->preedit_end);
gtk_text_buffer_delete_interactive (buffer, &start, &end, TRUE);
gtk_text_buffer_delete_mark (buffer, text_tool->preedit_start);
gtk_text_buffer_delete_mark (buffer, text_tool->preedit_end);
text_tool->preedit_start = NULL;
text_tool->preedit_end = NULL;
}
g_free (text_tool->preedit_string);
text_tool->preedit_string = NULL;
}
gimp_text_tool_delete_selection (text_tool);
gtk_im_context_get_preedit_string (context,
&text_tool->preedit_string, &attrs,
&text_tool->preedit_cursor);
if (text_tool->preedit_string && *text_tool->preedit_string)
{
GtkTextIter start;
GtkTextIter end;
gint line;
gint pos;
PangoAttrIterator *attr_iter;
GtkTextIter iter;
gint i;
/* Get current position. */
gtk_text_buffer_get_iter_at_mark (buffer, &start,
/* Save the preedit start position. */
gtk_text_buffer_get_iter_at_mark (buffer, &iter,
gtk_text_buffer_get_insert (buffer));
pos = gtk_text_iter_get_line_index (&start);
line = gtk_text_iter_get_line (&start);
text_tool->preedit_start = gtk_text_buffer_create_mark (buffer,
"preedit-start",
&iter, TRUE);
/* Insert the preedit text at current cursor position. */
gimp_text_tool_enter_text (text_tool, text_tool->preedit_string);
/* Loop through chunks of preedit text with different attributes. */
attr_iter = pango_attr_list_get_iterator (attrs);
do
{
gint attr_start;
gint attr_end;
/* Get the new position. */
gtk_text_buffer_get_iter_at_mark (buffer, &end,
pango_attr_iterator_range (attr_iter, &attr_start, &attr_end);
if (attr_start < strlen (text_tool->preedit_string))
{
GSList *attrs_pos;
GtkTextMark *start_mark;
GtkTextIter start;
GtkTextIter end;
gtk_text_buffer_get_iter_at_mark (buffer, &start,
gtk_text_buffer_get_insert (buffer));
start_mark = gtk_text_buffer_create_mark (buffer,
NULL,
&start, TRUE);
gtk_text_buffer_begin_user_action (buffer);
/* Insert the preedit chunk at current cursor position. */
gtk_text_buffer_insert_at_cursor (GTK_TEXT_BUFFER (text_tool->buffer),
text_tool->preedit_string + attr_start,
attr_end - attr_start);
gtk_text_buffer_get_iter_at_mark (buffer, &start,
start_mark);
gtk_text_buffer_delete_mark (buffer, start_mark);
gtk_text_buffer_get_iter_at_mark (buffer, &end,
gtk_text_buffer_get_insert (buffer));
/* Apply text attributes to preedit text. */
attrs_pos = pango_attr_iterator_get_attrs (attr_iter);
while (attrs_pos)
{
PangoAttribute *attr = attrs_pos->data;
if (attr)
{
switch (attr->klass->type)
{
case PANGO_ATTR_UNDERLINE:
gtk_text_buffer_apply_tag (buffer,
text_tool->buffer->underline_tag,
&start, &end);
break;
case PANGO_ATTR_BACKGROUND:
case PANGO_ATTR_FOREGROUND:
{
PangoAttrColor *color_attr = (PangoAttrColor *) attr;
GimpRGB color;
color.r = (gdouble) color_attr->color.red / 65535.0;
color.g = (gdouble) color_attr->color.green / 65535.0;
color.b = (gdouble) color_attr->color.blue / 65535.0;
if (attr->klass->type == PANGO_ATTR_BACKGROUND)
{
gimp_text_buffer_set_bg_color (text_tool->buffer,
&start, &end,
&color);
}
else
{
gimp_text_buffer_set_color (text_tool->buffer,
&start, &end,
&color);
}
}
break;
default:
/* Unsupported tags. */
break;
}
}
attrs_pos = attrs_pos->next;
}
gtk_text_buffer_end_user_action (buffer);
}
}
while (pango_attr_iterator_next (attr_iter));
/* Save the preedit end position. */
gtk_text_buffer_get_iter_at_mark (buffer, &iter,
gtk_text_buffer_get_insert (buffer));
text_tool->preedit_end = gtk_text_buffer_create_mark (buffer,
"preedit-end",
&iter, TRUE);
/* Select the preedit text. */
gtk_text_buffer_get_iter_at_line_index (buffer, &start, line, pos);
gtk_text_buffer_select_range (buffer, &start, &end);
/* Move the cursor to the expected location. */
gtk_text_buffer_get_iter_at_mark (buffer, &iter, text_tool->preedit_start);
for (i = 0; i < text_tool->preedit_cursor; i++)
gtk_text_iter_forward_char (&iter);
gtk_text_buffer_place_cursor (buffer, &iter);
pango_attr_iterator_destroy (attr_iter);
}
pango_attr_list_unref (attrs);
}
static void
@ -1383,7 +1512,27 @@ gimp_text_tool_im_commit (GtkIMContext *context,
const gchar *str,
GimpTextTool *text_tool)
{
if (text_tool->preedit_string && *text_tool->preedit_string)
{
GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer);
GtkTextIter start;
GtkTextIter end;
gtk_text_buffer_get_iter_at_mark (buffer, &start,
text_tool->preedit_start);
gtk_text_buffer_get_iter_at_mark (buffer, &end,
text_tool->preedit_end);
gtk_text_buffer_delete_interactive (buffer, &start, &end, TRUE);
gtk_text_buffer_delete_mark (buffer, text_tool->preedit_start);
gtk_text_buffer_delete_mark (buffer, text_tool->preedit_end);
text_tool->preedit_start = NULL;
text_tool->preedit_end = NULL;
}
gimp_text_tool_enter_text (text_tool, str);
g_free (text_tool->preedit_string);
text_tool->preedit_string = NULL;
}
static gboolean

View File

@ -79,6 +79,8 @@ struct _GimpTextTool
gchar *preedit_string;
gint preedit_cursor;
GtkTextMark *preedit_start;
GtkTextMark *preedit_end;
gboolean overwrite_mode;
gint x_pos;