forked from AuroraMiddleware/gtk
9f9e7dffef
Our Text implementation requires that we have a GtkEditable with a delegate that is a GtkText widget. This change make the Text implementation work for the custom widget in the tagged entry demo.
1470 lines
46 KiB
C
1470 lines
46 KiB
C
/* gtkatspitext.c: Text interface for GtkAtspiContext
|
|
*
|
|
* Copyright 2020 Red Hat, Inc
|
|
*
|
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
|
*
|
|
* 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.1 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
|
|
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtkatspitextprivate.h"
|
|
|
|
#include "gtkatspiprivate.h"
|
|
#include "gtkatspiutilsprivate.h"
|
|
#include "gtkatspipangoprivate.h"
|
|
#include "gtkatspitextbufferprivate.h"
|
|
|
|
#include "a11y/atspi/atspi-text.h"
|
|
|
|
#include "gtkatcontextprivate.h"
|
|
#include "gtkdebug.h"
|
|
#include "gtkeditable.h"
|
|
#include "gtklabelprivate.h"
|
|
#include "gtkentryprivate.h"
|
|
#include "gtksearchentryprivate.h"
|
|
#include "gtkpasswordentryprivate.h"
|
|
#include "gtkspinbuttonprivate.h"
|
|
#include "gtktextview.h"
|
|
|
|
#include <gio/gio.h>
|
|
|
|
/* {{{ GtkLabel */
|
|
|
|
static void
|
|
label_handle_method (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *method_name,
|
|
GVariant *parameters,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
GtkATContext *self = user_data;
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (self);
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
|
|
if (g_strcmp0 (method_name, "GetCaretOffset") == 0)
|
|
{
|
|
int offset;
|
|
|
|
offset = _gtk_label_get_cursor_position (GTK_LABEL (widget));
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", offset));
|
|
}
|
|
else if (g_strcmp0 (method_name, "SetCaretOffset") == 0)
|
|
{
|
|
int offset;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
ret = gtk_label_get_selectable (GTK_LABEL (widget));
|
|
if (ret)
|
|
gtk_label_select_region (GTK_LABEL (widget), offset, offset);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetText") == 0)
|
|
{
|
|
int start, end;
|
|
const char *text;
|
|
int len;
|
|
char *string;
|
|
|
|
g_variant_get (parameters, "(ii)", &start, &end);
|
|
|
|
text = gtk_label_get_text (GTK_LABEL (widget));
|
|
len = g_utf8_strlen (text, -1);
|
|
|
|
start = CLAMP (start, 0, len);
|
|
end = CLAMP (end, 0, len);
|
|
|
|
if (end <= start)
|
|
string = g_strdup ("");
|
|
else
|
|
{
|
|
const char *p, *q;
|
|
p = g_utf8_offset_to_pointer (text, start);
|
|
q = g_utf8_offset_to_pointer (text, end);
|
|
string = g_strndup (p, q - p);
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", string));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextBeforeOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_pango_get_text_before (layout, offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextAtOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_pango_get_text_at (layout, offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextAfterOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_pango_get_text_after (layout, offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetCharacterAtOffset") == 0)
|
|
{
|
|
int offset;
|
|
const char *text;
|
|
gunichar ch = 0;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
text = gtk_label_get_text (GTK_LABEL (widget));
|
|
|
|
if (0 <= offset && offset < g_utf8_strlen (text, -1))
|
|
ch = g_utf8_get_char (g_utf8_offset_to_pointer (text, offset));
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ch));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetStringAtOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
int offset;
|
|
AtspiTextGranularity granularity;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &granularity);
|
|
|
|
string = gtk_pango_get_string_at (layout, offset, granularity, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributes") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributeValue") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
const char *name;
|
|
int start, end;
|
|
GVariant *attrs;
|
|
const char *val;
|
|
|
|
g_variant_get (parameters, "(i&s)", &offset, &name);
|
|
|
|
gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end);
|
|
|
|
attrs = g_variant_builder_end (&builder);
|
|
if (!g_variant_lookup (attrs, name, "&s", &val))
|
|
val = "";
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", val));
|
|
g_variant_unref (attrs);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributeRun") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
gboolean include_defaults;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(ib)", &offset, &include_defaults);
|
|
|
|
if (include_defaults)
|
|
gtk_pango_get_default_attributes (layout, &builder);
|
|
|
|
gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetDefaultAttributes") == 0 ||
|
|
g_strcmp0 (method_name, "GetDefaultAttributeSet") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_label_get_layout (GTK_LABEL (widget));
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
|
|
gtk_pango_get_default_attributes (layout, &builder);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss})", &builder));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetNSelections") == 0)
|
|
{
|
|
int n = 0;
|
|
|
|
if (gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
|
|
n = 1;
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", n));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetSelection") == 0)
|
|
{
|
|
int num;
|
|
int start, end;
|
|
gboolean ret = TRUE;
|
|
|
|
g_variant_get (parameters, "(i)", &num);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_label_get_selection_bounds (GTK_LABEL (widget), &start, &end))
|
|
ret = FALSE;
|
|
}
|
|
|
|
if (!ret)
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Not a valid selection: %d", num);
|
|
else
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ii)", start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "AddSelection") == 0)
|
|
{
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(ii)", &start, &end);
|
|
|
|
if (!gtk_label_get_selectable (GTK_LABEL (widget)) ||
|
|
gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_label_select_region (GTK_LABEL (widget), start, end);
|
|
ret = TRUE;
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "RemoveSelection") == 0)
|
|
{
|
|
int num;
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(i)", &num);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_label_get_selectable (GTK_LABEL (widget)) ||
|
|
!gtk_label_get_selection_bounds (GTK_LABEL (widget), &start, &end))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_label_select_region (GTK_LABEL (widget), end, end);
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "SetSelection") == 0)
|
|
{
|
|
int num;
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(iii)", &num, &start, &end);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_label_get_selectable (GTK_LABEL (widget)) ||
|
|
!gtk_label_get_selection_bounds (GTK_LABEL (widget), NULL, NULL))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_label_select_region (GTK_LABEL (widget), start, end);
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetCharacterExtents") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetRangeExtents") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetBoundedRanges") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "ScrollSubstringTo") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "ScrollSubstringToPoint") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
}
|
|
|
|
static GVariant *
|
|
label_get_property (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *property_name,
|
|
GError **error,
|
|
gpointer user_data)
|
|
{
|
|
GtkATContext *self = user_data;
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (self);
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
|
|
if (g_strcmp0 (property_name, "CharacterCount") == 0)
|
|
{
|
|
const char *text;
|
|
int len;
|
|
|
|
text = gtk_label_get_text (GTK_LABEL (widget));
|
|
len = g_utf8_strlen (text, -1);
|
|
|
|
return g_variant_new_int32 (len);
|
|
}
|
|
else if (g_strcmp0 (property_name, "CaretOffset") == 0)
|
|
{
|
|
int offset;
|
|
|
|
offset = _gtk_label_get_cursor_position (GTK_LABEL (widget));
|
|
|
|
return g_variant_new_int32 (offset);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const GDBusInterfaceVTable label_vtable = {
|
|
label_handle_method,
|
|
label_get_property,
|
|
NULL,
|
|
};
|
|
|
|
/* }}} */
|
|
/* {{{ GtkEditable */
|
|
|
|
static GtkText *
|
|
gtk_editable_get_text_widget (GtkWidget *widget)
|
|
{
|
|
if (GTK_IS_EDITABLE (widget))
|
|
{
|
|
GtkEditable *delegate;
|
|
|
|
delegate = gtk_editable_get_delegate (GTK_EDITABLE (widget));
|
|
|
|
if (GTK_IS_TEXT (delegate))
|
|
return GTK_TEXT (delegate);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
editable_handle_method (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *method_name,
|
|
GVariant *parameters,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
GtkATContext *self = user_data;
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (self);
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
GtkText *text_widget = gtk_editable_get_text_widget (widget);
|
|
|
|
if (g_strcmp0 (method_name, "GetCaretOffset") == 0)
|
|
{
|
|
int offset;
|
|
|
|
offset = gtk_editable_get_position (GTK_EDITABLE (widget));
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", offset));
|
|
}
|
|
else if (g_strcmp0 (method_name, "SetCaretOffset") == 0)
|
|
{
|
|
int offset;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
gtk_editable_set_position (GTK_EDITABLE (widget), offset);
|
|
ret = TRUE;
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetText") == 0)
|
|
{
|
|
int start, end;
|
|
const char *text;
|
|
int len;
|
|
char *string;
|
|
|
|
g_variant_get (parameters, "(ii)", &start, &end);
|
|
|
|
text = gtk_editable_get_text (GTK_EDITABLE (widget));
|
|
len = g_utf8_strlen (text, -1);
|
|
|
|
start = CLAMP (start, 0, len);
|
|
end = CLAMP (end, 0, len);
|
|
|
|
if (end <= start)
|
|
string = g_strdup ("");
|
|
else
|
|
{
|
|
const char *p, *q;
|
|
p = g_utf8_offset_to_pointer (text, start);
|
|
q = g_utf8_offset_to_pointer (text, end);
|
|
string = g_strndup (p, q - p);
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", string));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextBeforeOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_pango_get_text_before (layout, offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextAtOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_pango_get_text_at (layout, offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextAfterOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_pango_get_text_after (layout, offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetCharacterAtOffset") == 0)
|
|
{
|
|
int offset;
|
|
const char *text;
|
|
gunichar ch = 0;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
text = gtk_editable_get_text (GTK_EDITABLE (widget));
|
|
if (0 <= offset && offset < g_utf8_strlen (text, -1))
|
|
ch = g_utf8_get_char (g_utf8_offset_to_pointer (text, offset));
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ch));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetStringAtOffset") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
int offset;
|
|
AtspiTextGranularity granularity;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &granularity);
|
|
|
|
string = gtk_pango_get_string_at (layout, offset, granularity, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributes") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributeValue") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
const char *name;
|
|
int start, end;
|
|
GVariant *attrs;
|
|
const char *val;
|
|
|
|
g_variant_get (parameters, "(i&s)", &offset, &name);
|
|
|
|
gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end);
|
|
attrs = g_variant_builder_end (&builder);
|
|
if (!g_variant_lookup (attrs, name, "&s", &val))
|
|
val = "";
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", val));
|
|
g_variant_unref (attrs);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributeRun") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
gboolean include_defaults;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(ib)", &offset, &include_defaults);
|
|
|
|
if (include_defaults)
|
|
gtk_pango_get_default_attributes (layout, &builder);
|
|
|
|
gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetDefaultAttributes") == 0 ||
|
|
g_strcmp0 (method_name, "GetDefaultAttributeSet") == 0)
|
|
{
|
|
PangoLayout *layout = gtk_text_get_layout (text_widget);
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
|
|
gtk_pango_get_default_attributes (layout, &builder);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss})", &builder));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetNSelections") == 0)
|
|
{
|
|
int n = 0;
|
|
|
|
if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), NULL, NULL))
|
|
n = 1;
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", n));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetSelection") == 0)
|
|
{
|
|
int num;
|
|
int start, end;
|
|
gboolean ret = TRUE;
|
|
|
|
g_variant_get (parameters, "(i)", &num);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
|
|
ret = FALSE;
|
|
}
|
|
|
|
if (!ret)
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Not a valid selection: %d", num);
|
|
else
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ii)", start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "AddSelection") == 0)
|
|
{
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(ii)", &start, &end);
|
|
|
|
if (gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), NULL, NULL))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_editable_select_region (GTK_EDITABLE (widget), start, end);
|
|
ret = TRUE;
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "RemoveSelection") == 0)
|
|
{
|
|
int num;
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(i)", &num);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), &start, &end))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_editable_select_region (GTK_EDITABLE (widget), end, end);
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "SetSelection") == 0)
|
|
{
|
|
int num;
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(iii)", &num, &start, &end);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_editable_get_selection_bounds (GTK_EDITABLE (widget), NULL, NULL))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_editable_select_region (GTK_EDITABLE (widget), start, end);
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetCharacterExtents") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetRangeExtents") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetBoundedRanges") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "ScrollSubstringTo") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "ScrollSubstringToPoint") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
}
|
|
|
|
static GVariant *
|
|
editable_get_property (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *property_name,
|
|
GError **error,
|
|
gpointer user_data)
|
|
{
|
|
GtkATContext *self = user_data;
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (self);
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
|
|
if (g_strcmp0 (property_name, "CharacterCount") == 0)
|
|
{
|
|
const char *text;
|
|
int len;
|
|
|
|
text = gtk_editable_get_text (GTK_EDITABLE (widget));
|
|
len = g_utf8_strlen (text, -1);
|
|
|
|
return g_variant_new_int32 (len);
|
|
}
|
|
else if (g_strcmp0 (property_name, "CaretOffset") == 0)
|
|
{
|
|
int offset;
|
|
|
|
offset = gtk_editable_get_position (GTK_EDITABLE (widget));
|
|
|
|
return g_variant_new_int32 (offset);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const GDBusInterfaceVTable editable_vtable = {
|
|
editable_handle_method,
|
|
editable_get_property,
|
|
NULL,
|
|
};
|
|
|
|
/* }}} */
|
|
/* {{{ GtkTextView */
|
|
|
|
static void
|
|
text_view_handle_method (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *method_name,
|
|
GVariant *parameters,
|
|
GDBusMethodInvocation *invocation,
|
|
gpointer user_data)
|
|
{
|
|
GtkATContext *self = user_data;
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (self);
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
|
|
if (g_strcmp0 (method_name, "GetCaretOffset") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextMark *insert;
|
|
GtkTextIter iter;
|
|
int offset;
|
|
|
|
insert = gtk_text_buffer_get_insert (buffer);
|
|
gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
|
|
offset = gtk_text_iter_get_offset (&iter);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", offset));
|
|
}
|
|
else if (g_strcmp0 (method_name, "SetCaretOffset") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextIter iter;
|
|
int offset;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
|
|
gtk_text_buffer_place_cursor (buffer, &iter);
|
|
gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (widget), &iter, 0, FALSE, 0, 0);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetText") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextIter start_iter, end_iter;
|
|
int start, end;
|
|
char *string;
|
|
|
|
g_variant_get (parameters, "(ii)", &start, &end);
|
|
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
|
|
|
|
string = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", string));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextBeforeOffset") == 0)
|
|
{
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_text_view_get_text_before (GTK_TEXT_VIEW (widget), offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextAtOffset") == 0)
|
|
{
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_text_view_get_text_at (GTK_TEXT_VIEW (widget), offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetTextAfterOffset") == 0)
|
|
{
|
|
int offset;
|
|
AtspiTextBoundaryType boundary_type;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &boundary_type);
|
|
|
|
string = gtk_text_view_get_text_after (GTK_TEXT_VIEW (widget), offset, boundary_type, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetCharacterAtOffset") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
int offset;
|
|
gunichar ch = 0;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
if (offset >= 0 && offset < gtk_text_buffer_get_char_count (buffer))
|
|
{
|
|
GtkTextIter start, end;
|
|
char *string;
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &start, offset);
|
|
end = start;
|
|
gtk_text_iter_forward_char (&end);
|
|
string = gtk_text_buffer_get_slice (buffer, &start, &end, FALSE);
|
|
ch = g_utf8_get_char (string);
|
|
g_free (string);
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ch));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetStringAtOffset") == 0)
|
|
{
|
|
int offset;
|
|
AtspiTextGranularity granularity;
|
|
char *string;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(iu)", &offset, &granularity);
|
|
|
|
string = gtk_text_view_get_string_at (GTK_TEXT_VIEW (widget), offset, granularity, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end));
|
|
g_free (string);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributes") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(i)", &offset);
|
|
|
|
gtk_text_buffer_get_run_attributes (buffer, &builder, offset, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributeValue") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
const char *name;
|
|
int start, end;
|
|
GVariant *attrs;
|
|
const char *val;
|
|
|
|
g_variant_get (parameters, "(i&s)", &offset, &name);
|
|
|
|
gtk_text_buffer_get_run_attributes (buffer, &builder, offset, &start, &end);
|
|
|
|
attrs = g_variant_builder_end (&builder);
|
|
if (!g_variant_lookup (attrs, name, "&s", &val))
|
|
val = "";
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", val));
|
|
g_variant_unref (attrs);
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetAttributeRun") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
int offset;
|
|
gboolean include_defaults;
|
|
int start, end;
|
|
|
|
g_variant_get (parameters, "(ib)", &offset, &include_defaults);
|
|
|
|
if (include_defaults)
|
|
gtk_text_view_add_default_attributes (GTK_TEXT_VIEW (widget), &builder);
|
|
|
|
gtk_text_buffer_get_run_attributes (buffer, &builder, offset, &start, &end);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetDefaultAttributes") == 0 ||
|
|
g_strcmp0 (method_name, "GetDefaultAttributeSet") == 0)
|
|
{
|
|
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}"));
|
|
|
|
gtk_text_view_add_default_attributes (GTK_TEXT_VIEW (widget), &builder);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss})", &builder));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetNSelections") == 0)
|
|
{
|
|
int n = 0;
|
|
|
|
if (gtk_text_buffer_get_selection_bounds (gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)), NULL, NULL))
|
|
n = 1;
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", n));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetSelection") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextIter start_iter, end_iter;
|
|
int num;
|
|
int start, end;
|
|
gboolean ret = TRUE;
|
|
|
|
g_variant_get (parameters, "(i)", &num);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
start = gtk_text_iter_get_offset (&start_iter);
|
|
end = gtk_text_iter_get_offset (&end_iter);
|
|
}
|
|
}
|
|
|
|
if (!ret)
|
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Not a valid selection: %d", num);
|
|
else
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ii)", start, end));
|
|
}
|
|
else if (g_strcmp0 (method_name, "AddSelection") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextIter start_iter, end_iter;
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(ii)", &start, &end);
|
|
|
|
if (gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
|
|
gtk_text_buffer_select_range (buffer, &start_iter, &end_iter);
|
|
ret = TRUE;
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "RemoveSelection") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextIter start_iter, end_iter;
|
|
int num;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(i)", &num);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_text_buffer_get_selection_bounds (buffer, &start_iter, &end_iter))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_text_buffer_select_range (buffer, &end_iter, &end_iter);
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "SetSelection") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextIter start_iter, end_iter;
|
|
int num;
|
|
int start, end;
|
|
gboolean ret;
|
|
|
|
g_variant_get (parameters, "(iii)", &num, &start, &end);
|
|
|
|
if (num != 0)
|
|
ret = FALSE;
|
|
else
|
|
{
|
|
if (!gtk_text_buffer_get_selection_bounds (buffer, NULL, NULL))
|
|
{
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
|
|
gtk_text_buffer_select_range (buffer, &end_iter, &end_iter);
|
|
ret = TRUE;
|
|
}
|
|
}
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetCharacterExtents") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetRangeExtents") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "GetBoundedRanges") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "ScrollSubstringTo") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
else if (g_strcmp0 (method_name, "ScrollSubstringToPoint") == 0)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
|
|
}
|
|
}
|
|
|
|
static GVariant *
|
|
text_view_get_property (GDBusConnection *connection,
|
|
const gchar *sender,
|
|
const gchar *object_path,
|
|
const gchar *interface_name,
|
|
const gchar *property_name,
|
|
GError **error,
|
|
gpointer user_data)
|
|
{
|
|
GtkATContext *self = user_data;
|
|
GtkAccessible *accessible = gtk_at_context_get_accessible (self);
|
|
GtkWidget *widget = GTK_WIDGET (accessible);
|
|
|
|
if (g_strcmp0 (property_name, "CharacterCount") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
int len;
|
|
|
|
len = gtk_text_buffer_get_char_count (buffer);
|
|
|
|
return g_variant_new_int32 (len);
|
|
}
|
|
else if (g_strcmp0 (property_name, "CaretOffset") == 0)
|
|
{
|
|
GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextMark *insert;
|
|
GtkTextIter iter;
|
|
int offset;
|
|
|
|
insert = gtk_text_buffer_get_insert (buffer);
|
|
gtk_text_buffer_get_iter_at_mark (buffer, &iter, insert);
|
|
offset = gtk_text_iter_get_offset (&iter);
|
|
|
|
return g_variant_new_int32 (offset);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const GDBusInterfaceVTable text_view_vtable = {
|
|
text_view_handle_method,
|
|
text_view_get_property,
|
|
NULL,
|
|
};
|
|
|
|
/* }}} */
|
|
|
|
const GDBusInterfaceVTable *
|
|
gtk_atspi_get_text_vtable (GtkAccessible *accessible)
|
|
{
|
|
if (GTK_IS_LABEL (accessible))
|
|
return &label_vtable;
|
|
else if (GTK_IS_EDITABLE (accessible) &&
|
|
GTK_IS_TEXT (gtk_editable_get_delegate (GTK_EDITABLE (accessible))))
|
|
return &editable_vtable;
|
|
else if (GTK_IS_TEXT_VIEW (accessible))
|
|
return &text_view_vtable;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
typedef struct {
|
|
void (* text_changed) (gpointer data,
|
|
const char *kind,
|
|
int start,
|
|
int end,
|
|
const char *text);
|
|
void (* selection_changed) (gpointer data,
|
|
const char *kind,
|
|
int cursor_position);
|
|
|
|
gpointer data;
|
|
GtkTextBuffer *buffer;
|
|
int cursor_position;
|
|
int selection_bound;
|
|
} TextChanged;
|
|
|
|
/* {{{ GtkEditable notification */
|
|
|
|
static void
|
|
insert_text_cb (GtkEditable *editable,
|
|
char *new_text,
|
|
int new_text_length,
|
|
int *position,
|
|
TextChanged *changed)
|
|
{
|
|
int length;
|
|
|
|
if (new_text_length == 0)
|
|
return;
|
|
|
|
length = g_utf8_strlen (new_text, new_text_length);
|
|
changed->text_changed (changed->data, "insert", *position - length, length, new_text);
|
|
}
|
|
|
|
static void
|
|
delete_text_cb (GtkEditable *editable,
|
|
int start,
|
|
int end,
|
|
TextChanged *changed)
|
|
{
|
|
char *text;
|
|
|
|
if (start == end)
|
|
return;
|
|
|
|
text = gtk_editable_get_chars (editable, start, end);
|
|
changed->text_changed (changed->data, "delete", start, end - start, text);
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
update_selection (TextChanged *changed,
|
|
int cursor_position,
|
|
int selection_bound)
|
|
{
|
|
gboolean caret_moved, bound_moved;
|
|
|
|
caret_moved = cursor_position != changed->cursor_position;
|
|
bound_moved = selection_bound != changed->selection_bound;
|
|
|
|
if (!caret_moved && !bound_moved)
|
|
return;
|
|
|
|
changed->cursor_position = cursor_position;
|
|
changed->selection_bound = selection_bound;
|
|
|
|
if (caret_moved)
|
|
changed->selection_changed (changed->data, "text-caret-moved", changed->cursor_position);
|
|
|
|
if (caret_moved || bound_moved)
|
|
changed->selection_changed (changed->data, "text-selection-changed", 0);
|
|
}
|
|
|
|
static void
|
|
notify_cb (GObject *object,
|
|
GParamSpec *pspec,
|
|
TextChanged *changed)
|
|
{
|
|
if (g_strcmp0 (pspec->name, "cursor-position") == 0 ||
|
|
g_strcmp0 (pspec->name, "selection-bound") == 0)
|
|
{
|
|
int cursor_position, selection_bound;
|
|
|
|
gtk_editable_get_selection_bounds (GTK_EDITABLE (object), &cursor_position, &selection_bound);
|
|
update_selection (changed, cursor_position, selection_bound);
|
|
}
|
|
}
|
|
|
|
static void
|
|
update_cursor (GtkTextBuffer *buffer,
|
|
TextChanged *changed)
|
|
{
|
|
GtkTextIter iter;
|
|
int cursor_position, selection_bound;
|
|
|
|
gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_insert (buffer));
|
|
cursor_position = gtk_text_iter_get_offset (&iter);
|
|
|
|
gtk_text_buffer_get_iter_at_mark (buffer, &iter, gtk_text_buffer_get_selection_bound (buffer));
|
|
|
|
selection_bound = gtk_text_iter_get_offset (&iter);
|
|
|
|
update_selection (changed, cursor_position, selection_bound);
|
|
}
|
|
|
|
/* }}} */
|
|
/* {{{ GtkTextView notification */
|
|
|
|
static void
|
|
insert_range_cb (GtkTextBuffer *buffer,
|
|
GtkTextIter *iter,
|
|
char *text,
|
|
int len,
|
|
TextChanged *changed)
|
|
{
|
|
int position;
|
|
int length;
|
|
|
|
position = gtk_text_iter_get_offset (iter);
|
|
length = g_utf8_strlen (text, len);
|
|
|
|
changed->text_changed (changed->data, "insert", position - length, length, text);
|
|
|
|
update_cursor (buffer, changed);
|
|
}
|
|
|
|
static void
|
|
delete_range_cb (GtkTextBuffer *buffer,
|
|
GtkTextIter *start,
|
|
GtkTextIter *end,
|
|
TextChanged *changed)
|
|
{
|
|
int offset, length;
|
|
char *text;
|
|
|
|
text = gtk_text_buffer_get_slice (buffer, start, end, FALSE);
|
|
|
|
offset = gtk_text_iter_get_offset (start);
|
|
length = gtk_text_iter_get_offset (end) - offset;
|
|
|
|
changed->text_changed (changed->data, "delete", offset, length, text);
|
|
|
|
g_free (text);
|
|
}
|
|
|
|
static void
|
|
delete_range_after_cb (GtkTextBuffer *buffer,
|
|
GtkTextIter *start,
|
|
GtkTextIter *end,
|
|
TextChanged *changed)
|
|
{
|
|
update_cursor (buffer, changed);
|
|
}
|
|
|
|
static void
|
|
mark_set_cb (GtkTextBuffer *buffer,
|
|
GtkTextIter *location,
|
|
GtkTextMark *mark,
|
|
TextChanged *changed)
|
|
{
|
|
if (mark == gtk_text_buffer_get_insert (buffer) ||
|
|
mark == gtk_text_buffer_get_selection_bound (buffer))
|
|
update_cursor (buffer, changed);
|
|
}
|
|
|
|
static void
|
|
buffer_changed (GtkWidget *widget,
|
|
GParamSpec *pspec,
|
|
TextChanged *changed)
|
|
{
|
|
GtkTextBuffer *buffer;
|
|
GtkTextIter start, end;
|
|
char *text;
|
|
|
|
buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
|
|
if (changed->buffer)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, insert_range_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_after_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, mark_set_cb, changed);
|
|
|
|
gtk_text_buffer_get_bounds (changed->buffer, &start, &end);
|
|
text = gtk_text_buffer_get_slice (changed->buffer, &start, &end, FALSE);
|
|
changed->text_changed (changed->data, "delete", 0, gtk_text_buffer_get_char_count (changed->buffer), text);
|
|
g_free (text);
|
|
|
|
update_selection (changed, 0, 0);
|
|
|
|
g_clear_object (&changed->buffer);
|
|
}
|
|
|
|
changed->buffer = buffer;
|
|
|
|
if (changed->buffer)
|
|
{
|
|
g_object_ref (changed->buffer);
|
|
g_signal_connect (changed->buffer, "insert-text", G_CALLBACK (insert_range_cb), changed);
|
|
g_signal_connect (changed->buffer, "delete-range", G_CALLBACK (delete_range_cb), changed);
|
|
g_signal_connect_after (changed->buffer, "delete-range", G_CALLBACK (delete_range_after_cb), changed);
|
|
g_signal_connect_after (changed->buffer, "mark-set", G_CALLBACK (mark_set_cb), changed);
|
|
|
|
gtk_text_buffer_get_bounds (changed->buffer, &start, &end);
|
|
text = gtk_text_buffer_get_slice (changed->buffer, &start, &end, FALSE);
|
|
changed->text_changed (changed->data, "insert", 0, gtk_text_buffer_get_char_count (changed->buffer), text);
|
|
g_free (text);
|
|
|
|
update_cursor (changed->buffer, changed);
|
|
}
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
void
|
|
gtk_atspi_connect_text_signals (GtkAccessible *accessible,
|
|
GtkAtspiTextChangedCallback text_changed,
|
|
GtkAtspiTextSelectionCallback selection_changed,
|
|
gpointer data)
|
|
{
|
|
TextChanged *changed;
|
|
|
|
if (!GTK_IS_EDITABLE (accessible) &&
|
|
!GTK_IS_TEXT_VIEW (accessible))
|
|
return;
|
|
|
|
changed = g_new0 (TextChanged, 1);
|
|
changed->text_changed = text_changed;
|
|
changed->selection_changed = selection_changed;
|
|
changed->data = data;
|
|
|
|
g_object_set_data_full (G_OBJECT (accessible), "accessible-text-data", changed, g_free);
|
|
|
|
if (GTK_IS_EDITABLE (accessible))
|
|
{
|
|
GtkText *text = gtk_editable_get_text_widget (GTK_WIDGET (accessible));
|
|
|
|
if (text)
|
|
{
|
|
g_signal_connect_after (text, "insert-text", G_CALLBACK (insert_text_cb), changed);
|
|
g_signal_connect (text, "delete-text", G_CALLBACK (delete_text_cb), changed);
|
|
g_signal_connect (text, "notify", G_CALLBACK (notify_cb), changed);
|
|
|
|
gtk_editable_get_selection_bounds (GTK_EDITABLE (text), &changed->cursor_position, &changed->selection_bound);
|
|
}
|
|
}
|
|
else if (GTK_IS_TEXT_VIEW (accessible))
|
|
{
|
|
g_signal_connect (accessible, "notify::buffer", G_CALLBACK (buffer_changed), changed);
|
|
buffer_changed (GTK_WIDGET (accessible), NULL, changed);
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_atspi_disconnect_text_signals (GtkAccessible *accessible)
|
|
{
|
|
TextChanged *changed;
|
|
|
|
changed = g_object_get_data (G_OBJECT (accessible), "accessible-text-data");
|
|
|
|
if (GTK_IS_EDITABLE (accessible))
|
|
{
|
|
GtkText *text = gtk_editable_get_text_widget (GTK_WIDGET (accessible));
|
|
|
|
if (text)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (text, insert_text_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (text, delete_text_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (text, notify_cb, changed);
|
|
}
|
|
}
|
|
else if (GTK_IS_TEXT_VIEW (accessible))
|
|
{
|
|
g_signal_handlers_disconnect_by_func (accessible, buffer_changed, changed);
|
|
if (changed->buffer)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, insert_range_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, delete_range_after_cb, changed);
|
|
g_signal_handlers_disconnect_by_func (changed->buffer, mark_set_cb, changed);
|
|
}
|
|
g_clear_object (&changed->buffer);
|
|
}
|
|
|
|
g_object_set_data (G_OBJECT (accessible), "accessible-text-data", NULL);
|
|
}
|
|
|
|
/* vim:set foldmethod=marker expandtab: */
|