forked from AuroraMiddleware/gtk
16b5a88097
Use the same initial check for the accessible object type that we use when connecting the signal, in case we try to disconnect signals on different widgets. Additionally, check before accessing data that might have already been removed. Fixes: #3403
1569 lines
50 KiB
C
1569 lines
50 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)
|
|
{
|
|
GtkTextBuffer *buffer = buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget));
|
|
GtkTextIter iter;
|
|
int start_offset = 0;
|
|
int end_offset = 0;
|
|
int offset = 0;
|
|
double x_align = -1.0, y_align = -1.0;
|
|
AtspiScrollType scroll_type;
|
|
gboolean is_rtl = FALSE, use_align = TRUE;
|
|
gboolean ret = FALSE;
|
|
|
|
g_variant_get (parameters, "(iiu)", &start_offset, &end_offset, &scroll_type);
|
|
|
|
if (end_offset < start_offset)
|
|
{
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_INVALID_ARGS,
|
|
"Negative offset is not supported");
|
|
return;
|
|
}
|
|
|
|
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
|
is_rtl = TRUE;
|
|
|
|
switch (scroll_type)
|
|
{
|
|
case ATSPI_SCROLL_TOP_LEFT:
|
|
offset = (is_rtl) ? end_offset : start_offset;
|
|
x_align = 0.0;
|
|
y_align = 0.0;
|
|
break;
|
|
|
|
case ATSPI_SCROLL_BOTTOM_RIGHT:
|
|
offset = (is_rtl) ? start_offset : end_offset;
|
|
x_align = 1.0;
|
|
y_align = 1.0;
|
|
break;
|
|
|
|
case ATSPI_SCROLL_TOP_EDGE:
|
|
offset = start_offset;
|
|
y_align = 0.0;
|
|
break;
|
|
|
|
case ATSPI_SCROLL_BOTTOM_EDGE:
|
|
offset = end_offset;
|
|
y_align = 1.0;
|
|
break;
|
|
|
|
case ATSPI_SCROLL_LEFT_EDGE:
|
|
offset = (is_rtl) ? end_offset : start_offset;
|
|
x_align = 0.0;
|
|
break;
|
|
|
|
case ATSPI_SCROLL_RIGHT_EDGE:
|
|
offset = (is_rtl) ? start_offset : end_offset;
|
|
x_align = 1.0;
|
|
break;
|
|
|
|
case ATSPI_SCROLL_ANYWHERE:
|
|
offset = start_offset;
|
|
use_align = FALSE;
|
|
x_align = 0.0;
|
|
y_align = 0.0;
|
|
break;
|
|
|
|
default:
|
|
g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR,
|
|
G_DBUS_ERROR_INVALID_ARGS,
|
|
"Invalid scroll type");
|
|
return;
|
|
}
|
|
|
|
gtk_text_buffer_get_iter_at_offset (buffer, &iter, offset);
|
|
|
|
if (use_align && (x_align != -1.0 || y_align != -1.0))
|
|
{
|
|
GdkRectangle visible_rect, iter_rect;
|
|
|
|
gtk_text_view_get_visible_rect (GTK_TEXT_VIEW (widget), &visible_rect);
|
|
gtk_text_view_get_iter_location (GTK_TEXT_VIEW (widget), &iter, &iter_rect);
|
|
|
|
if (x_align == -1.0)
|
|
x_align = ((double) (iter_rect.x - visible_rect.x)) / (visible_rect.width - 1);
|
|
if (y_align == -1.0)
|
|
y_align = ((double) (iter_rect.y - visible_rect.y)) / (visible_rect.height - 1);
|
|
}
|
|
|
|
ret = gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW (widget), &iter,
|
|
0.0,
|
|
use_align,
|
|
x_align, y_align);
|
|
|
|
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", ret));
|
|
}
|
|
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)
|
|
{
|
|
if (!GTK_IS_EDITABLE (accessible) &&
|
|
!GTK_IS_TEXT_VIEW (accessible))
|
|
return;
|
|
|
|
TextChanged *changed;
|
|
|
|
changed = g_object_get_data (G_OBJECT (accessible), "accessible-text-data");
|
|
if (changed == NULL)
|
|
return;
|
|
|
|
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: */
|