1 /*
2 * Copyright © 2018 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <config.h>
19 #include "cc-input-row.h"
20 #include "cc-input-source-ibus.h"
21
22 struct _CcInputRow
23 {
24 GtkListBoxRow parent_instance;
25
26 CcInputSource *source;
27
28 GtkEventBox *drag_handle;
29 GtkLabel *name_label;
30 GtkButton *remove_button;
31 GtkButton *settings_button;
32 GtkSeparator *settings_separator;
33
34 GtkListBox *drag_widget;
35 };
36
37 G_DEFINE_TYPE (CcInputRow, cc_input_row, GTK_TYPE_LIST_BOX_ROW)
38
39 enum
40 {
41 SIGNAL_SHOW_SETTINGS,
42 SIGNAL_SHOW_LAYOUT,
43 SIGNAL_MOVE_ROW,
44 SIGNAL_REMOVE_ROW,
45 SIGNAL_LAST
46 };
47
48 static guint signals[SIGNAL_LAST] = { 0, };
49
50 static void
drag_begin_cb(CcInputRow * self,GdkDragContext * drag_context)51 drag_begin_cb (CcInputRow *self,
52 GdkDragContext *drag_context)
53 {
54 GtkAllocation alloc;
55 gint x = 0, y = 0;
56
57 gtk_widget_get_allocation (GTK_WIDGET (self), &alloc);
58
59 gdk_window_get_device_position (gtk_widget_get_window (GTK_WIDGET (self)),
60 gdk_drag_context_get_device (drag_context),
61 &x, &y, NULL);
62
63 self->drag_widget = GTK_LIST_BOX (gtk_list_box_new ());
64 gtk_widget_show (GTK_WIDGET (self->drag_widget));
65 gtk_widget_set_size_request (GTK_WIDGET (self->drag_widget), alloc.width, alloc.height);
66 CcInputRow *drag_row = cc_input_row_new (self->source);
67 gtk_widget_show (GTK_WIDGET (drag_row));
68 gtk_container_add (GTK_CONTAINER (self->drag_widget), GTK_WIDGET (drag_row));
69 gtk_list_box_drag_highlight_row (self->drag_widget, GTK_LIST_BOX_ROW (drag_row));
70
71 gtk_drag_set_icon_widget (drag_context, GTK_WIDGET (self->drag_widget), x - alloc.x, y - alloc.y);
72 }
73
74 static void
drag_end_cb(CcInputRow * self)75 drag_end_cb (CcInputRow *self)
76 {
77 g_clear_pointer ((GtkWidget **) &self->drag_widget, gtk_widget_destroy);
78 }
79
80 static void
drag_data_get_cb(CcInputRow * self,GdkDragContext * context,GtkSelectionData * selection_data,guint info,guint time_)81 drag_data_get_cb (CcInputRow *self,
82 GdkDragContext *context,
83 GtkSelectionData *selection_data,
84 guint info,
85 guint time_)
86 {
87 gtk_selection_data_set (selection_data,
88 gdk_atom_intern_static_string ("GTK_LIST_BOX_ROW"),
89 32,
90 (const guchar *)&self,
91 sizeof (gpointer));
92 }
93
94 static void
drag_data_received_cb(CcInputRow * self,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint time_)95 drag_data_received_cb (CcInputRow *self,
96 GdkDragContext *context,
97 gint x,
98 gint y,
99 GtkSelectionData *selection_data,
100 guint info,
101 guint time_)
102 {
103 CcInputRow *source;
104
105 source = *((CcInputRow **) gtk_selection_data_get_data (selection_data));
106
107 if (source == self)
108 return;
109
110 g_signal_emit (source,
111 signals[SIGNAL_MOVE_ROW],
112 0,
113 self);
114 }
115
116 static void
move_up_button_clicked_cb(CcInputRow * self,GtkButton * button)117 move_up_button_clicked_cb (CcInputRow *self,
118 GtkButton *button)
119 {
120 GtkListBox *list_box = GTK_LIST_BOX (gtk_widget_get_parent (GTK_WIDGET (self)));
121 gint previous_idx = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (self)) - 1;
122 GtkListBoxRow *previous_row = gtk_list_box_get_row_at_index (list_box, previous_idx);
123
124 if (previous_row == NULL)
125 return;
126
127 g_signal_emit (self,
128 signals[SIGNAL_MOVE_ROW],
129 0,
130 previous_row);
131 }
132
133 static void
move_down_button_clicked_cb(CcInputRow * self,GtkButton * button)134 move_down_button_clicked_cb (CcInputRow *self,
135 GtkButton *button)
136 {
137 GtkListBox *list_box = GTK_LIST_BOX (gtk_widget_get_parent (GTK_WIDGET (self)));
138 gint next_idx = gtk_list_box_row_get_index (GTK_LIST_BOX_ROW (self)) + 1;
139 GtkListBoxRow *next_row = gtk_list_box_get_row_at_index (list_box, next_idx);
140
141 if (next_row == NULL)
142 return;
143
144 g_signal_emit (next_row,
145 signals[SIGNAL_MOVE_ROW],
146 0,
147 self);
148 }
149
150 static void
settings_button_clicked_cb(CcInputRow * self)151 settings_button_clicked_cb (CcInputRow *self)
152 {
153 g_signal_emit (self,
154 signals[SIGNAL_SHOW_SETTINGS],
155 0);
156 }
157
158 static void
layout_button_clicked_cb(CcInputRow * self)159 layout_button_clicked_cb (CcInputRow *self)
160 {
161 g_signal_emit (self,
162 signals[SIGNAL_SHOW_LAYOUT],
163 0);
164 }
165
166 static void
remove_button_clicked_cb(CcInputRow * self)167 remove_button_clicked_cb (CcInputRow *self)
168 {
169 g_signal_emit (self,
170 signals[SIGNAL_REMOVE_ROW],
171 0);
172 }
173
174 static void
cc_input_row_dispose(GObject * object)175 cc_input_row_dispose (GObject *object)
176 {
177 CcInputRow *self = CC_INPUT_ROW (object);
178
179 g_clear_object (&self->source);
180
181 G_OBJECT_CLASS (cc_input_row_parent_class)->dispose (object);
182 }
183
184 void
cc_input_row_class_init(CcInputRowClass * klass)185 cc_input_row_class_init (CcInputRowClass *klass)
186 {
187 GObjectClass *object_class = G_OBJECT_CLASS (klass);
188 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
189
190 object_class->dispose = cc_input_row_dispose;
191
192 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/keyboard/cc-input-row.ui");
193
194 gtk_widget_class_bind_template_child (widget_class, CcInputRow, drag_handle);
195 gtk_widget_class_bind_template_child (widget_class, CcInputRow, name_label);
196 gtk_widget_class_bind_template_child (widget_class, CcInputRow, remove_button);
197 gtk_widget_class_bind_template_child (widget_class, CcInputRow, settings_button);
198 gtk_widget_class_bind_template_child (widget_class, CcInputRow, settings_separator);
199
200 gtk_widget_class_bind_template_callback (widget_class, drag_begin_cb);
201 gtk_widget_class_bind_template_callback (widget_class, drag_data_get_cb);
202 gtk_widget_class_bind_template_callback (widget_class, drag_data_received_cb);
203 gtk_widget_class_bind_template_callback (widget_class, drag_end_cb);
204 gtk_widget_class_bind_template_callback (widget_class, layout_button_clicked_cb);
205 gtk_widget_class_bind_template_callback (widget_class, move_down_button_clicked_cb);
206 gtk_widget_class_bind_template_callback (widget_class, move_up_button_clicked_cb);
207 gtk_widget_class_bind_template_callback (widget_class, remove_button_clicked_cb);
208 gtk_widget_class_bind_template_callback (widget_class, settings_button_clicked_cb);
209
210 signals[SIGNAL_SHOW_SETTINGS] =
211 g_signal_new ("show-settings",
212 G_TYPE_FROM_CLASS (object_class),
213 G_SIGNAL_RUN_LAST,
214 0,
215 NULL, NULL,
216 NULL,
217 G_TYPE_NONE,
218 0);
219
220 signals[SIGNAL_SHOW_LAYOUT] =
221 g_signal_new ("show-layout",
222 G_TYPE_FROM_CLASS (object_class),
223 G_SIGNAL_RUN_LAST,
224 0,
225 NULL, NULL,
226 NULL,
227 G_TYPE_NONE,
228 0);
229
230 signals[SIGNAL_MOVE_ROW] =
231 g_signal_new ("move-row",
232 G_TYPE_FROM_CLASS (object_class),
233 G_SIGNAL_RUN_LAST,
234 0,
235 NULL, NULL,
236 NULL,
237 G_TYPE_NONE,
238 1, CC_TYPE_INPUT_ROW);
239
240 signals[SIGNAL_REMOVE_ROW] =
241 g_signal_new ("remove-row",
242 G_TYPE_FROM_CLASS (object_class),
243 G_SIGNAL_RUN_LAST,
244 0,
245 NULL, NULL,
246 NULL,
247 G_TYPE_NONE,
248 0);
249 }
250
251 static GtkTargetEntry entries[] =
252 {
253 { "GTK_LIST_BOX_ROW", GTK_TARGET_SAME_APP, 0 }
254 };
255
256 void
cc_input_row_init(CcInputRow * self)257 cc_input_row_init (CcInputRow *self)
258 {
259 gtk_widget_init_template (GTK_WIDGET (self));
260
261 gtk_drag_source_set (GTK_WIDGET (self->drag_handle), GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
262 gtk_drag_dest_set (GTK_WIDGET (self), GTK_DEST_DEFAULT_ALL, entries, 1, GDK_ACTION_MOVE);
263 }
264
265 static void
label_changed_cb(CcInputRow * self)266 label_changed_cb (CcInputRow *self)
267 {
268 g_autofree gchar *label = cc_input_source_get_label (self->source);
269 gtk_label_set_text (self->name_label, label);
270 }
271
272 CcInputRow *
cc_input_row_new(CcInputSource * source)273 cc_input_row_new (CcInputSource *source)
274 {
275 CcInputRow *self;
276
277 self = g_object_new (CC_TYPE_INPUT_ROW, NULL);
278 self->source = g_object_ref (source);
279
280 g_signal_connect_object (source, "label-changed", G_CALLBACK (label_changed_cb), self, G_CONNECT_SWAPPED);
281 label_changed_cb (self);
282
283 gtk_widget_set_visible (GTK_WIDGET (self->settings_button), CC_IS_INPUT_SOURCE_IBUS (source));
284 gtk_widget_set_visible (GTK_WIDGET (self->settings_separator), CC_IS_INPUT_SOURCE_IBUS (source));
285
286 return self;
287 }
288
289 CcInputSource *
cc_input_row_get_source(CcInputRow * self)290 cc_input_row_get_source (CcInputRow *self)
291 {
292 g_return_val_if_fail (CC_IS_INPUT_ROW (self), NULL);
293 return self->source;
294 }
295
296 void
cc_input_row_set_removable(CcInputRow * self,gboolean removable)297 cc_input_row_set_removable (CcInputRow *self,
298 gboolean removable)
299 {
300 g_return_if_fail (CC_IS_INPUT_ROW (self));
301 gtk_widget_set_sensitive (GTK_WIDGET (self->remove_button), removable);
302 }
303
304 void
cc_input_row_set_draggable(CcInputRow * self,gboolean draggable)305 cc_input_row_set_draggable (CcInputRow *self,
306 gboolean draggable)
307 {
308 if (draggable)
309 gtk_drag_source_set (GTK_WIDGET (self->drag_handle), GDK_BUTTON1_MASK, entries, 1, GDK_ACTION_MOVE);
310 else
311 gtk_drag_source_unset (GTK_WIDGET (self->drag_handle));
312 }
313