You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

411 lines
15 KiB

/*
* Xournal++
*
* Dummy widget needed for text editor event handling
*
* @author Xournal++ Team
* https://github.com/xournalpp/xournalpp
*
* @license GNU GPLv2 or later
*/
#pragma once
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#define GTK_TYPE_XOJ_INT_TXT (gtk_xoj_int_txt_get_type ())
#define GTK_XOJ_INT_TXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_XOJ_INT_TXT, GtkXojIntTxt))
#define GTK_XOJ_INT_TXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_XOJ_INT_TXT, GtkXojIntTxtClass))
#define GTK_IS_XOJ_INT_TXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_XOJ_INT_TXT))
#define GTK_IS_XOJ_INT_TXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_XOJ_INT_TXT))
#define GTK_XOJ_INT_TXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_XOJ_INT_TXT, GtkXojIntTxtClass))
typedef struct _GtkXojIntTxt GtkXojIntTxt;
typedef struct _GtkXojIntTxtClass GtkXojIntTxtClass;
struct _GtkXojIntTxt
{
GtkWidget widget;
TextEditor* te;
};
struct _GtkXojIntTxtClass
{
GtkWidgetClass parent_class;
};
GType gtk_xoj_int_txt_get_type(void);
GtkWidget* gtk_xoj_int_txt_new(TextEditor* te);
G_DEFINE_TYPE(GtkXojIntTxt, gtk_xoj_int_txt, GTK_TYPE_WIDGET)
static void gtk_invisible_realize(GtkWidget* widget);
static void gtk_invisible_style_set(GtkWidget* widget, GtkStyle* previous_style);
static void gtk_invisible_show(GtkWidget* widget);
static void gtk_invisible_size_allocate(GtkWidget* widget, GtkAllocation* allocation);
static GObject*
gtk_invisible_constructor(GType type, guint n_construct_properties, GObjectConstructParam* construct_params);
enum
{
SELECT_ALL,
DELETE_FROM_CURSOR,
BACKSPACE,
CUT_CLIPBOARD,
COPY_CLIPBOARD,
PASTE_CLIPBOARD,
TOGGLE_OVERWRITE,
MOVE_CURSOR,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = {0};
static void gtk_xoj_int_txt_select_all(GtkWidget* widget)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->selectAtCursor(TextEditor::SelectType::all);
}
static void gtk_xoj_int_txt_move_cursor(GtkWidget* widget, gint step, gint bitmask)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->moveCursor((GtkMovementStep) step, (bitmask & 2) ? 1 : -1, bitmask & 1);
}
static void gtk_xoj_int_txt_delete_from_cursor(GtkWidget* widget, gint type, gint count)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->deleteFromCursor((GtkDeleteType) type, count);
}
static void gtk_xoj_int_txt_backspace(GtkWidget* widget)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->backspace();
}
static void gtk_xoj_int_txt_cut_clipboard(GtkWidget* widget)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->cutToClipboard();
}
static void gtk_xoj_int_txt_copy_clipboard(GtkWidget* widget)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->copyToCliboard();
}
static void gtk_xoj_int_txt_paste_clipboard(GtkWidget* widget)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->pasteFromClipboard();
}
static void gtk_xoj_int_txt_toggle_overwrite(GtkWidget* widget)
{
GtkXojIntTxt* txt = GTK_XOJ_INT_TXT(widget);
txt->te->toggleOverwrite();
}
static void add_move_binding(GtkBindingSet* binding_set, guint keyval, guint modmask, int step, gint count)
{
g_assert((modmask & GDK_SHIFT_MASK) == 0);
gint bitmask = 0;
if (count == 1)
{
bitmask |= 2;
}
gtk_binding_entry_add_signal(binding_set, keyval, (GdkModifierType) modmask,
"move-cursor", 2, G_TYPE_INT, step,G_TYPE_INT, bitmask);
/* Selection-extending version */
gtk_binding_entry_add_signal(binding_set, keyval, (GdkModifierType) (modmask | GDK_SHIFT_MASK),
"move-cursor", 2, G_TYPE_INT, step, G_TYPE_INT, bitmask | 1);
}
static void gtk_xoj_int_txt_class_init(GtkXojIntTxtClass* klass)
{
GObjectClass* gobject_class;
GtkWidgetClass* widget_class;
widget_class = (GtkWidgetClass*) klass;
gobject_class = (GObjectClass*) klass;
widget_class->realize = gtk_invisible_realize;
widget_class->style_set = gtk_invisible_style_set;
widget_class->show = gtk_invisible_show;
widget_class->size_allocate = gtk_invisible_size_allocate;
gobject_class->constructor = gtk_invisible_constructor;
/**
* GtkTextView::select-all:
* @text_view: the object which received the signal
* @select: %TRUE to select, %FALSE to unselect
*
* The ::select-all signal is a
* <link linkend="keybinding-signals">keybinding signal</link>
* which gets emitted to select or unselect the complete
* contents of the text view.
*
* The default bindings for this signal are Ctrl-a and Ctrl-/
* for selecting and Shift-Ctrl-a and Ctrl-\ for unselecting.
*/
signals[SELECT_ALL] = g_signal_new_class_handler("select-all",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_select_all), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
);
signals[MOVE_CURSOR] = g_signal_new_class_handler("move-cursor",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_move_cursor), NULL, NULL, NULL, G_TYPE_NONE, 2,
G_TYPE_INT, G_TYPE_INT
);
signals[DELETE_FROM_CURSOR] = g_signal_new_class_handler("delete-from-cursor",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_delete_from_cursor), NULL, NULL, NULL, G_TYPE_NONE, 2,
G_TYPE_INT, G_TYPE_INT
);
signals[BACKSPACE] = g_signal_new_class_handler("backspace",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_backspace), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
);
signals[CUT_CLIPBOARD] = g_signal_new_class_handler("cut-clipboard",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_cut_clipboard), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
);
signals[COPY_CLIPBOARD] = g_signal_new_class_handler("copy-clipboard",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_copy_clipboard), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
);
signals[PASTE_CLIPBOARD] = g_signal_new_class_handler("paste-clipboard",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_paste_clipboard), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
);
signals[TOGGLE_OVERWRITE] = g_signal_new_class_handler("toggle-overwrite",
G_OBJECT_CLASS_TYPE(widget_class), (GSignalFlags) (G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION),
G_CALLBACK(gtk_xoj_int_txt_toggle_overwrite), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0
);
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
/*
* Key bindings
*/
GtkBindingSet* binding_set = gtk_binding_set_by_class(klass);
/* Moving the insertion point */
add_move_binding(binding_set, GDK_KEY_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS, 1);
add_move_binding(binding_set, GDK_KEY_KP_Right, 0, GTK_MOVEMENT_VISUAL_POSITIONS,
1);
add_move_binding(binding_set, GDK_KEY_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS, -1);
add_move_binding(binding_set, GDK_KEY_KP_Left, 0, GTK_MOVEMENT_VISUAL_POSITIONS,
-1);
add_move_binding(binding_set, GDK_KEY_Right, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS,
1);
add_move_binding(binding_set, GDK_KEY_KP_Right, GDK_CONTROL_MASK,
GTK_MOVEMENT_WORDS, 1);
add_move_binding(binding_set, GDK_KEY_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS,
-1);
add_move_binding(binding_set, GDK_KEY_KP_Left, GDK_CONTROL_MASK, GTK_MOVEMENT_WORDS,
-1);
add_move_binding(binding_set, GDK_KEY_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
add_move_binding(binding_set, GDK_KEY_KP_Up, 0, GTK_MOVEMENT_DISPLAY_LINES, -1);
add_move_binding(binding_set, GDK_KEY_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
add_move_binding(binding_set, GDK_KEY_KP_Down, 0, GTK_MOVEMENT_DISPLAY_LINES, 1);
add_move_binding(binding_set, GDK_KEY_Up, GDK_CONTROL_MASK, GTK_MOVEMENT_PARAGRAPHS,
-1);
add_move_binding(binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
GTK_MOVEMENT_PARAGRAPHS, -1);
add_move_binding(binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
GTK_MOVEMENT_PARAGRAPHS, 1);
add_move_binding(binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
GTK_MOVEMENT_PARAGRAPHS, 1);
add_move_binding(binding_set, GDK_KEY_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, -1);
add_move_binding(binding_set, GDK_KEY_KP_Home, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS,
-1);
add_move_binding(binding_set, GDK_KEY_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
add_move_binding(binding_set, GDK_KEY_KP_End, 0, GTK_MOVEMENT_DISPLAY_LINE_ENDS, 1);
add_move_binding(binding_set, GDK_KEY_Home, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, -1);
add_move_binding(binding_set, GDK_KEY_KP_Home, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, -1);
add_move_binding(binding_set, GDK_KEY_End, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, 1);
add_move_binding(binding_set, GDK_KEY_KP_End, GDK_CONTROL_MASK,
GTK_MOVEMENT_BUFFER_ENDS, 1);
add_move_binding(binding_set, GDK_KEY_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
add_move_binding(binding_set, GDK_KEY_KP_Page_Up, 0, GTK_MOVEMENT_PAGES, -1);
add_move_binding(binding_set, GDK_KEY_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
add_move_binding(binding_set, GDK_KEY_KP_Page_Down, 0, GTK_MOVEMENT_PAGES, 1);
add_move_binding(binding_set, GDK_KEY_Page_Up, GDK_CONTROL_MASK,
GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
add_move_binding(binding_set, GDK_KEY_KP_Page_Up, GDK_CONTROL_MASK,
GTK_MOVEMENT_HORIZONTAL_PAGES, -1);
add_move_binding(binding_set, GDK_KEY_Page_Down, GDK_CONTROL_MASK,
GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
add_move_binding(binding_set, GDK_KEY_KP_Page_Down, GDK_CONTROL_MASK,
GTK_MOVEMENT_HORIZONTAL_PAGES, 1);
/* Select all */
gtk_binding_entry_add_signal(binding_set, GDK_KEY_a, GDK_CONTROL_MASK, "select-all",
0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_slash, GDK_CONTROL_MASK,
"select-all", 0);
/* Deleting text */
gtk_binding_entry_add_signal(binding_set, GDK_KEY_Delete, (GdkModifierType) 0,
"delete-from-cursor", 2, G_TYPE_INT, GTK_DELETE_CHARS, G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_KP_Delete, (GdkModifierType) 0,
"delete-from-cursor", 2, G_TYPE_INT, GTK_DELETE_CHARS, G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_BackSpace, (GdkModifierType) 0,
"backspace", 0);
/* Make this do the same as Backspace, to help with mis-typing */
gtk_binding_entry_add_signal(binding_set, GDK_KEY_BackSpace, GDK_SHIFT_MASK,
"backspace", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_Delete, GDK_CONTROL_MASK,
"delete-from-cursor", 2, G_TYPE_INT, GTK_DELETE_WORD_ENDS, G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_KP_Delete, GDK_CONTROL_MASK,
"delete-from-cursor", 2, G_TYPE_INT, GTK_DELETE_WORD_ENDS, G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_BackSpace, GDK_CONTROL_MASK,
"delete-from-cursor", 2, G_TYPE_INT, GTK_DELETE_WORD_ENDS, G_TYPE_INT, -1);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_Delete,
(GdkModifierType) (GDK_SHIFT_MASK | GDK_CONTROL_MASK), "delete-from-cursor", 2,
G_TYPE_INT,
GTK_DELETE_PARAGRAPH_ENDS, G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_KP_Delete,
(GdkModifierType) (GDK_SHIFT_MASK | GDK_CONTROL_MASK), "delete-from-cursor", 2,
G_TYPE_INT,
GTK_DELETE_PARAGRAPH_ENDS, G_TYPE_INT, 1);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_BackSpace,
(GdkModifierType) (GDK_SHIFT_MASK | GDK_CONTROL_MASK), "delete-from-cursor", 2,
G_TYPE_INT,
GTK_DELETE_PARAGRAPH_ENDS, G_TYPE_INT, -1);
/* Cut/copy/paste */
gtk_binding_entry_add_signal(binding_set, GDK_KEY_x, GDK_CONTROL_MASK,
"cut-clipboard", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_c, GDK_CONTROL_MASK,
"copy-clipboard", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_v, GDK_CONTROL_MASK,
"paste-clipboard", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_Delete, GDK_SHIFT_MASK,
"cut-clipboard", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_Insert, GDK_CONTROL_MASK,
"copy-clipboard", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_Insert, GDK_SHIFT_MASK,
"paste-clipboard", 0);
/* Overwrite */
gtk_binding_entry_add_signal(binding_set, GDK_KEY_Insert, (GdkModifierType) 0,
"toggle-overwrite", 0);
gtk_binding_entry_add_signal(binding_set, GDK_KEY_KP_Insert, (GdkModifierType) 0,
"toggle-overwrite", 0);
}
static void gtk_xoj_int_txt_init(GtkXojIntTxt* invisible)
{
gtk_widget_set_has_window(GTK_WIDGET(invisible), TRUE);
}
GtkWidget*
gtk_xoj_int_txt_new(TextEditor* te)
{
GtkXojIntTxt* txt = (GtkXojIntTxt*) g_object_new(GTK_TYPE_XOJ_INT_TXT, NULL);
txt->te = te;
return (GtkWidget*) txt;
}
static void gtk_invisible_realize(GtkWidget* widget)
{
gtk_widget_set_realized(widget, TRUE);
GdkWindow* parent = gtk_widget_get_parent_window(widget);
if (parent == NULL)
{
parent = gdk_screen_get_root_window(gdk_screen_get_default());
}
GdkWindowAttr attributes;
attributes.x = -100;
attributes.y = -100;
attributes.width = 0;
attributes.height = 0;
attributes.window_type = GDK_WINDOW_TEMP;
attributes.wclass = GDK_INPUT_ONLY;
attributes.override_redirect = TRUE;
attributes.event_mask = gtk_widget_get_events(widget);
gint attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_NOREDIR;
gtk_widget_set_window(widget, gdk_window_new(parent, &attributes, attributes_mask));
gdk_window_set_user_data(gtk_widget_get_window(widget), widget);
}
static void gtk_invisible_style_set(GtkWidget* widget, GtkStyle* previous_style)
{
/* Don't chain up to parent implementation */
}
static void gtk_invisible_show(GtkWidget* widget)
{
gtk_widget_set_visible(widget, TRUE);
gtk_widget_map(widget);
}
static void gtk_invisible_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
{
gtk_widget_set_allocation(widget, allocation);
}
static GObject*
gtk_invisible_constructor(GType type, guint n_construct_properties, GObjectConstructParam* construct_params)
{
GObject* object;
object = G_OBJECT_CLASS(gtk_xoj_int_txt_parent_class)->constructor(type, n_construct_properties, construct_params);
GtkWidget* widget = GTK_WIDGET(object);
gtk_invisible_realize(widget);
return object;
}