]> Pileus Git - ~andy/gtk/blob - gtk/gtkselectionwindow.c
entry: Use GtkSelectionWindow for touch text selection
[~andy/gtk] / gtk / gtkselectionwindow.c
1 /* GTK - The GIMP Toolkit
2  * Copyright © 2013 Carlos Garnacho <carlosg@gnome.org>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /**
19  * SECTION:gtkselectionwindow
20  * @Short_description: Bubble window for content edition
21  * @Title: GtkSelectionWindow
22  *
23  * GtkSelection widget is a small helper object to implement
24  * touch-friendly content edition. #GtkEntry and #GtkTextView
25  * use this internally to allow text selection edition and
26  * manipulation.
27  */
28
29 #include "config.h"
30 #include "gtkselectionwindow.h"
31 #include "gtkprivate.h"
32 #include "gtkintl.h"
33
34 #define TOOLBAR_UI                               \
35   "<ui>"                                         \
36   "  <toolbar>"                                  \
37   "    <toolitem name='cut' action='Cut' />"     \
38   "    <toolitem name='copy' action='Copy' />"   \
39   "    <toolitem name='paste' action='Paste' />" \
40   "    <separator />"                            \
41   "  </toolbar>"                                 \
42   "</ui>"
43
44 static void _action_cut_cb   (GtkAction          *action,
45                               GtkSelectionWindow *window);
46 static void _action_copy_cb  (GtkAction          *action,
47                               GtkSelectionWindow *window);
48 static void _action_paste_cb (GtkAction          *action,
49                               GtkSelectionWindow *window);
50
51 static GtkActionEntry entries[] = {
52   { "Cut", GTK_STOCK_CUT, NULL, NULL, NULL, G_CALLBACK (_action_cut_cb) },
53   { "Copy", GTK_STOCK_COPY, NULL, NULL, NULL, G_CALLBACK (_action_copy_cb) },
54   { "Paste", GTK_STOCK_PASTE, NULL, NULL, NULL, G_CALLBACK (_action_paste_cb) }
55 };
56
57 enum {
58   PROP_EDITABLE = 1,
59   PROP_HAS_SELECTION
60 };
61
62 enum {
63   CUT,
64   COPY,
65   PASTE,
66   N_SIGNALS
67 };
68
69 static guint signals[N_SIGNALS] = { 0 };
70
71 struct _GtkSelectionWindowPrivate
72 {
73   GtkUIManager *ui_manager;
74   GtkWidget *toolbar;
75   guint editable      : 1;
76   guint has_selection : 1;
77 };
78
79 G_DEFINE_TYPE (GtkSelectionWindow, gtk_selection_window, GTK_TYPE_BUBBLE_WINDOW)
80
81 static void
82 _action_cut_cb (GtkAction          *action,
83                 GtkSelectionWindow *window)
84 {
85   g_signal_emit (window, signals[CUT], 0);
86 }
87
88 static void
89 _action_copy_cb (GtkAction          *action,
90                  GtkSelectionWindow *window)
91 {
92   g_signal_emit (window, signals[COPY], 0);
93 }
94
95 static void
96 _action_paste_cb (GtkAction          *action,
97                   GtkSelectionWindow *window)
98 {
99   g_signal_emit (window, signals[PASTE], 0);
100 }
101
102 static void
103 _gtk_selection_window_update_state (GtkSelectionWindow *window)
104 {
105   GtkSelectionWindowPrivate *priv;
106   GtkClipboard *clipboard;
107   gboolean text_available;
108   GtkAction *action;
109
110   priv = window->_priv;
111   clipboard = gtk_widget_get_clipboard (GTK_WIDGET (window),
112                                         GDK_SELECTION_CLIPBOARD);
113   text_available = gtk_clipboard_wait_is_text_available (clipboard);
114
115   action = gtk_ui_manager_get_action (priv->ui_manager, "/toolbar/cut");
116   gtk_action_set_sensitive (action, priv->editable && priv->has_selection);
117
118   action = gtk_ui_manager_get_action (priv->ui_manager, "/toolbar/copy");
119   gtk_action_set_sensitive (action, priv->has_selection);
120
121   action = gtk_ui_manager_get_action (priv->ui_manager, "/toolbar/paste");
122   gtk_action_set_sensitive (action, text_available && priv->editable);
123 }
124
125 static void
126 gtk_selection_window_map (GtkWidget *widget)
127 {
128   _gtk_selection_window_update_state (GTK_SELECTION_WINDOW (widget));
129   GTK_WIDGET_CLASS (gtk_selection_window_parent_class)->map (widget);
130 }
131
132 static void
133 gtk_selection_window_finalize (GObject *object)
134 {
135   GtkSelectionWindowPrivate *priv;
136
137   priv = GTK_SELECTION_WINDOW (object)->_priv;
138   g_object_unref (priv->ui_manager);
139
140   G_OBJECT_CLASS (gtk_selection_window_parent_class)->finalize (object);
141 }
142
143 static void
144 gtk_selection_window_set_property (GObject      *object,
145                                    guint         prop_id,
146                                    const GValue *value,
147                                    GParamSpec   *pspec)
148 {
149   switch (prop_id)
150     {
151     case PROP_EDITABLE:
152       gtk_selection_window_set_editable (GTK_SELECTION_WINDOW (object),
153                                          g_value_get_boolean (value));
154       break;
155     case PROP_HAS_SELECTION:
156       gtk_selection_window_set_has_selection (GTK_SELECTION_WINDOW (object),
157                                               g_value_get_boolean (value));
158       break;
159     default:
160       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
161       break;
162     }
163 }
164
165 static void
166 gtk_selection_window_get_property (GObject    *object,
167                                    guint       prop_id,
168                                    GValue     *value,
169                                    GParamSpec *pspec)
170 {
171   GtkSelectionWindowPrivate *priv;
172
173   priv = GTK_SELECTION_WINDOW (object)->_priv;
174
175   switch (prop_id)
176     {
177     case PROP_EDITABLE:
178       g_value_set_boolean (value, priv->editable);
179       break;
180     case PROP_HAS_SELECTION:
181       g_value_set_boolean (value, priv->has_selection);
182       break;
183     default:
184       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
185       break;
186     }
187 }
188
189 static void
190 gtk_selection_window_class_init (GtkSelectionWindowClass *klass)
191 {
192   GObjectClass *object_class = G_OBJECT_CLASS (klass);
193   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
194
195   object_class->finalize = gtk_selection_window_finalize;
196   object_class->set_property = gtk_selection_window_set_property;
197   object_class->get_property = gtk_selection_window_get_property;
198
199   widget_class->map = gtk_selection_window_map;
200
201   g_object_class_install_property (object_class,
202                                    PROP_EDITABLE,
203                                    g_param_spec_boolean ("editable",
204                                                          P_("Editable"),
205                                                          P_("Whether content is editable"),
206                                                          TRUE,
207                                                          GTK_PARAM_READWRITE |
208                                                          G_PARAM_CONSTRUCT));
209   g_object_class_install_property (object_class,
210                                    PROP_HAS_SELECTION,
211                                    g_param_spec_boolean ("has-selection",
212                                                          P_("Has selection"),
213                                                          P_("Whether there is any content currently selected"),
214                                                          TRUE,
215                                                          GTK_PARAM_READWRITE |
216                                                          G_PARAM_CONSTRUCT));
217   signals[CUT] =
218     g_signal_new (I_("cut"),
219                   G_OBJECT_CLASS_TYPE (object_class),
220                   G_SIGNAL_RUN_LAST, 0,
221                   NULL, NULL,
222                   g_cclosure_marshal_VOID__VOID,
223                   G_TYPE_NONE, 0);
224   signals[COPY] =
225     g_signal_new (I_("copy"),
226                   G_OBJECT_CLASS_TYPE (object_class),
227                   G_SIGNAL_RUN_LAST, 0,
228                   NULL, NULL,
229                   g_cclosure_marshal_VOID__VOID,
230                   G_TYPE_NONE, 0);
231   signals[PASTE] =
232     g_signal_new (I_("paste"),
233                   G_OBJECT_CLASS_TYPE (object_class),
234                   G_SIGNAL_RUN_LAST, 0,
235                   NULL, NULL,
236                   g_cclosure_marshal_VOID__VOID,
237                   G_TYPE_NONE, 0);
238
239   g_type_class_add_private (klass, sizeof (GtkSelectionWindowPrivate));
240 }
241
242 static void
243 gtk_selection_window_init (GtkSelectionWindow *window)
244 {
245   GtkSelectionWindowPrivate *priv;
246   GtkActionGroup *group;
247
248   window->_priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (window,
249                                                       GTK_TYPE_SELECTION_WINDOW,
250                                                       GtkSelectionWindowPrivate);
251
252   group = gtk_action_group_new ("SelectionToolbar");
253   gtk_action_group_add_actions (group, entries, G_N_ELEMENTS (entries), window);
254
255   priv->ui_manager = gtk_ui_manager_new ();
256   gtk_ui_manager_insert_action_group (priv->ui_manager, group, 0);
257   gtk_ui_manager_add_ui_from_string (priv->ui_manager, TOOLBAR_UI, -1, NULL);
258
259   priv->toolbar = gtk_ui_manager_get_widget (priv->ui_manager, "/toolbar");
260   gtk_toolbar_set_show_arrow (GTK_TOOLBAR (priv->toolbar), FALSE);
261   gtk_widget_show_all (priv->toolbar);
262
263   gtk_container_add (GTK_CONTAINER (window), priv->toolbar);
264 }
265
266 /**
267  * gtk_selection_window_new:
268  *
269  * Creates a new #GtkSelectionWindow
270  *
271  * Returns: a newly created #GtkSelectionWindow
272  **/
273 GtkWidget *
274 gtk_selection_window_new (void)
275 {
276   return g_object_new (GTK_TYPE_SELECTION_WINDOW, NULL);
277 }
278
279 /**
280  * gtk_selection_window_set_editable:
281  * @window: a #GtkSelectionWindow
282  * @editable: whether the current selection is editable
283  *
284  * Sets whether the current selection is editable. Toolbar options'
285  * sensitivity will change according to this.
286  *
287  * Since: 3.8
288  **/
289 void
290 gtk_selection_window_set_editable (GtkSelectionWindow *window,
291                                    gboolean            editable)
292 {
293   GtkSelectionWindowPrivate *priv;
294   gboolean need_update;
295
296   g_return_if_fail (GTK_IS_SELECTION_WINDOW (window));
297
298   priv = window->_priv;
299   need_update = priv->editable != editable &&
300     gtk_widget_get_visible (GTK_WIDGET (window));
301   priv->editable = editable;
302
303   if (need_update)
304     _gtk_selection_window_update_state (window);
305
306   g_object_notify (G_OBJECT (window), "editable");
307 }
308
309 /**
310  * gtk_selection_window_get_editable:
311  * @window: a #GtkSelectionWindow
312  *
313  * Returns whether the contents are editable according to @window
314  *
315  * Returns: whether the contents are editable
316  *
317  * Since: 3.8
318  **/
319 gboolean
320 gtk_selection_window_get_editable (GtkSelectionWindow *window)
321 {
322   GtkSelectionWindowPrivate *priv;
323
324   g_return_val_if_fail (GTK_IS_SELECTION_WINDOW (window), FALSE);
325
326   priv = window->_priv;
327
328   return priv->editable;
329 }
330
331 /**
332  * gtk_selection_window_set_has_selection:
333  * @window: a #GtkSelectionWindow
334  * @has_selection: whether there is currently a selection
335  *
336  * Sets whether there is any selected content. Toolbar options'
337  * sensitivity will change according to this.
338  *
339  * Since: 3.8
340  **/
341 void
342 gtk_selection_window_set_has_selection (GtkSelectionWindow *window,
343                                         gboolean            has_selection)
344 {
345   GtkSelectionWindowPrivate *priv;
346   gboolean need_update;
347
348   g_return_if_fail (GTK_IS_SELECTION_WINDOW (window));
349
350   priv = window->_priv;
351   need_update = priv->has_selection != has_selection &&
352     gtk_widget_get_visible (GTK_WIDGET (window));
353   priv->has_selection = has_selection;
354
355   if (need_update)
356     _gtk_selection_window_update_state (window);
357
358   g_object_notify (G_OBJECT (window), "has-selection");
359 }
360
361 /**
362  * gtk_selection_window_get_has_selection:
363  * @window: a #GtkSelectionWindow
364  *
365  * Returns whether there any content is selected according to @window
366  *
367  * Returns: whether there is selected content
368  *
369  * Since: 3.8
370  **/
371 gboolean
372 gtk_selection_window_get_has_selection (GtkSelectionWindow *window)
373 {
374   GtkSelectionWindowPrivate *priv;
375
376   g_return_val_if_fail (GTK_IS_SELECTION_WINDOW (window), FALSE);
377
378   priv = window->_priv;
379
380   return priv->has_selection;
381 }
382
383 /**
384  * gtk_selection_window_get_toolbar:
385  * @window: a #GtkSelectionWindow
386  *
387  * Returns the toolbar contained by @window so e.g. new elements
388  * can be added.
389  *
390  * Returns: the internal toolbar
391  *
392  * Since: 3.8
393  **/
394 GtkWidget *
395 gtk_selection_window_get_toolbar (GtkSelectionWindow *window)
396 {
397   GtkSelectionWindowPrivate *priv;
398
399   g_return_val_if_fail (GTK_IS_SELECTION_WINDOW (window), FALSE);
400
401   priv = window->_priv;
402
403   return priv->toolbar;
404 }