~ubuntu-branches/debian/stretch/gnome-builder/stretch

« back to all changes in this revision

Viewing changes to libide/ide-source-view-mode.c

  • Committer: Package Import Robot
  • Author(s): Andreas Henriksson
  • Date: 2015-10-11 12:38:45 UTC
  • Revision ID: package-import@ubuntu.com-20151011123845-a0hvkz01se0p1p5a
Tags: upstream-3.16.3
ImportĀ upstreamĀ versionĀ 3.16.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* ide-source-view-mode.c
 
2
 *
 
3
 * Copyright (C) 2015 Alexander Larsson <alexl@redhat.com>
 
4
 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
 
5
 *
 
6
 * This program is free software: you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation, either version 3 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#define G_LOG_DOMAIN "ide-source-view-mode"
 
21
 
 
22
#include <glib/gi18n.h>
 
23
 
 
24
#include "ide-debug.h"
 
25
#include "ide-source-view.h"
 
26
#include "ide-source-view-mode.h"
 
27
 
 
28
struct _IdeSourceViewMode
 
29
{
 
30
  GtkWidget              parent_instance;
 
31
 
 
32
  GtkWidget             *view;
 
33
  char                  *name;
 
34
  char                  *display_name;
 
35
  gchar                 *default_mode;
 
36
  IdeSourceViewModeType  type;
 
37
};
 
38
 
 
39
G_DEFINE_TYPE (IdeSourceViewMode, ide_source_view_mode, GTK_TYPE_WIDGET)
 
40
 
 
41
enum {
 
42
  PROP_0,
 
43
  PROP_NAME,
 
44
  LAST_PROP
 
45
};
 
46
 
 
47
static GParamSpec *gParamSpecs [LAST_PROP];
 
48
 
 
49
static void
 
50
get_param (IdeSourceViewMode *self,
 
51
           const gchar       *param,
 
52
           GValue            *value)
 
53
{
 
54
  GtkStyleContext *context;
 
55
 
 
56
  g_assert (IDE_IS_SOURCE_VIEW_MODE (self));
 
57
  g_assert (param != NULL);
 
58
  g_assert (value != NULL);
 
59
 
 
60
  context = gtk_widget_get_style_context (GTK_WIDGET (self));
 
61
  gtk_style_context_get_style_property (context, param, value);
 
62
}
 
63
 
 
64
gboolean
 
65
get_boolean_param (IdeSourceViewMode *self,
 
66
                   const gchar       *param)
 
67
{
 
68
  GValue value = { 0 };
 
69
  gboolean ret;
 
70
 
 
71
  g_value_init (&value, G_TYPE_BOOLEAN);
 
72
  get_param (self, param, &value);
 
73
  ret = g_value_get_boolean (&value);
 
74
  g_value_unset (&value);
 
75
 
 
76
  return ret;
 
77
}
 
78
 
 
79
gchar *
 
80
get_string_param (IdeSourceViewMode *self,
 
81
                   const gchar       *param)
 
82
{
 
83
  GValue value = { 0 };
 
84
  gchar *ret;
 
85
 
 
86
  g_value_init (&value, G_TYPE_STRING);
 
87
  get_param (self, param, &value);
 
88
  ret = g_value_dup_string (&value);
 
89
  g_value_unset (&value);
 
90
 
 
91
  return ret;
 
92
}
 
93
 
 
94
const gchar *
 
95
ide_source_view_mode_get_default_mode (IdeSourceViewMode *self)
 
96
{
 
97
  /*
 
98
   * instead of switching back to "default" mode, use this mode instead
 
99
   * if no other mode is specified.
 
100
   */
 
101
  return self->default_mode;
 
102
}
 
103
 
 
104
const gchar *
 
105
ide_source_view_mode_get_display_name (IdeSourceViewMode *self)
 
106
{
 
107
  return self->display_name;
 
108
}
 
109
 
 
110
gboolean
 
111
ide_source_view_mode_get_repeat_insert_with_count (IdeSourceViewMode *self)
 
112
{
 
113
  /*
 
114
   * If count is 10, and you type -, you will get ----------
 
115
   */
 
116
  return get_boolean_param (self, "repeat-insert-with-count");
 
117
}
 
118
 
 
119
gboolean
 
120
ide_source_view_mode_get_suppress_unbound (IdeSourceViewMode *self)
 
121
{
 
122
  /*
 
123
   * unknown keypresses are swallowed. you probably want to use this
 
124
   * with a transient mode.
 
125
   */
 
126
  return get_boolean_param (self, "suppress-unbound");
 
127
}
 
128
 
 
129
gboolean
 
130
ide_source_view_mode_get_block_cursor (IdeSourceViewMode *self)
 
131
{
 
132
  /*
 
133
   * fakes a block cursor by using overwrite mode in textview.
 
134
   * you probably want to use this with "suppress-unbound".
 
135
   */
 
136
  return get_boolean_param (self, "block-cursor");
 
137
}
 
138
gboolean
 
139
ide_source_view_mode_get_keep_mark_on_char (IdeSourceViewMode *self)
 
140
{
 
141
  /* forces the source view to not let the cursor reach the end of the
 
142
   * line (basically an iter over \n). this is useful for emulating vim
 
143
   */
 
144
  return get_boolean_param (self, "keep-mark-on-char");
 
145
}
 
146
 
 
147
static void
 
148
ide_source_view_mode_finalize (GObject *object)
 
149
{
 
150
  IdeSourceViewMode *self = IDE_SOURCE_VIEW_MODE (object);
 
151
 
 
152
  g_clear_object (&self->view);
 
153
  g_clear_pointer (&self->name, g_free);
 
154
  g_clear_pointer (&self->default_mode, g_free);
 
155
  g_clear_pointer (&self->display_name, g_free);
 
156
  self->type = 0;
 
157
 
 
158
  G_OBJECT_CLASS (ide_source_view_mode_parent_class)->finalize (object);
 
159
}
 
160
 
 
161
static void
 
162
proxy_closure_marshal (GClosure     *closure,
 
163
                       GValue       *return_value,
 
164
                       guint         n_param_values,
 
165
                       const GValue *param_values,
 
166
                       gpointer      invocation_hint,
 
167
                       gpointer      marshal_data)
 
168
{
 
169
  GValue *param_copy;
 
170
  IdeSourceViewMode *mode = IDE_SOURCE_VIEW_MODE (g_value_get_object (&param_values[0]));
 
171
 
 
172
  param_copy = g_memdup (param_values, sizeof (GValue) * n_param_values);
 
173
 
 
174
  param_copy[0].data[0].v_pointer = mode->view;
 
175
  g_signal_emitv (param_copy,
 
176
                  GPOINTER_TO_INT (closure->data),
 
177
                  0,
 
178
                  return_value);
 
179
  g_free (param_copy);
 
180
}
 
181
 
 
182
static void
 
183
proxy_all_action_signals (GType type)
 
184
{
 
185
  GClosure *proxy;
 
186
  guint *signals;
 
187
  guint n_signals, i;
 
188
  GSignalQuery query;
 
189
 
 
190
  signals = g_signal_list_ids (type, &n_signals);
 
191
  for (i = 0; i < n_signals; i++)
 
192
    {
 
193
      g_signal_query (signals[i], &query);
 
194
 
 
195
      if (query.signal_flags & G_SIGNAL_ACTION)
 
196
        {
 
197
          proxy = g_closure_new_simple (sizeof (GClosure), GINT_TO_POINTER (query.signal_id));
 
198
          g_closure_set_meta_marshal (proxy, NULL, proxy_closure_marshal);
 
199
          g_signal_newv (query.signal_name,
 
200
                         IDE_TYPE_SOURCE_VIEW_MODE,
 
201
                         G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 
202
                         proxy,
 
203
                         NULL, NULL, NULL,
 
204
                         query.return_type,
 
205
                         query.n_params,
 
206
                         (GType *)query.param_types);
 
207
        }
 
208
    }
 
209
}
 
210
 
 
211
const gchar *
 
212
ide_source_view_mode_get_name (IdeSourceViewMode *mode)
 
213
{
 
214
  g_return_val_if_fail (IDE_IS_SOURCE_VIEW_MODE (mode), NULL);
 
215
 
 
216
  return mode->name;
 
217
}
 
218
 
 
219
static void
 
220
ide_source_view_mode_get_property (GObject    *object,
 
221
                                   guint       prop_id,
 
222
                                   GValue     *value,
 
223
                                   GParamSpec *pspec)
 
224
{
 
225
  IdeSourceViewMode *mode = IDE_SOURCE_VIEW_MODE(object);
 
226
 
 
227
  switch (prop_id)
 
228
    {
 
229
    case PROP_NAME:
 
230
      g_value_set_string (value, ide_source_view_mode_get_name (mode));
 
231
      break;
 
232
 
 
233
    default:
 
234
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
 
235
    }
 
236
}
 
237
 
 
238
static void
 
239
ide_source_view_mode_class_init (IdeSourceViewModeClass *klass)
 
240
{
 
241
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
242
  GtkBindingSet *binding_set, *parent_binding_set;
 
243
  GType type;
 
244
 
 
245
  object_class->finalize = ide_source_view_mode_finalize;
 
246
  object_class->get_property = ide_source_view_mode_get_property;
 
247
 
 
248
  gParamSpecs [PROP_NAME] =
 
249
    g_param_spec_string ("name",
 
250
                          _("Name"),
 
251
                          _("The name of the mode."),
 
252
                          NULL,
 
253
                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
254
 
 
255
  g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 
256
 
 
257
  gtk_widget_class_install_style_property (GTK_WIDGET_CLASS (klass),
 
258
                                           g_param_spec_boolean ("suppress-unbound",
 
259
                                                                 "Supress Unbound",
 
260
                                                                 "Suppress Unbound Keypresses",
 
261
                                                                 FALSE,
 
262
                                                                 (G_PARAM_READABLE |
 
263
                                                                  G_PARAM_STATIC_STRINGS)));
 
264
 
 
265
  gtk_widget_class_install_style_property (GTK_WIDGET_CLASS (klass),
 
266
                                           g_param_spec_boolean ("block-cursor",
 
267
                                                                 "Block Cursor",
 
268
                                                                 "Use fake block cursor by "
 
269
                                                                  "using overwrite mode.",
 
270
                                                                 FALSE,
 
271
                                                                 (G_PARAM_READABLE |
 
272
                                                                  G_PARAM_STATIC_STRINGS)));
 
273
 
 
274
  gtk_widget_class_install_style_property (GTK_WIDGET_CLASS (klass),
 
275
                                           g_param_spec_boolean ("keep-mark-on-char",
 
276
                                                                 "Keep Mark on Char",
 
277
                                                                 "Don't allow the cursor to "
 
278
                                                                  "move to line end.",
 
279
                                                                 FALSE,
 
280
                                                                 (G_PARAM_READABLE |
 
281
                                                                  G_PARAM_STATIC_STRINGS)));
 
282
 
 
283
  gtk_widget_class_install_style_property (GTK_WIDGET_CLASS (klass),
 
284
                                           g_param_spec_string ("display-name",
 
285
                                                                "Display Name",
 
286
                                                                "Display name for mode",
 
287
                                                                NULL,
 
288
                                                                (G_PARAM_READABLE |
 
289
                                                                 G_PARAM_STATIC_STRINGS)));
 
290
 
 
291
  gtk_widget_class_install_style_property (GTK_WIDGET_CLASS (klass),
 
292
                                           g_param_spec_string ("default-mode",
 
293
                                                                "Default Mode",
 
294
                                                                "Suggest a followup default mode",
 
295
                                                                NULL,
 
296
                                                                (G_PARAM_READABLE |
 
297
                                                                 G_PARAM_STATIC_STRINGS)));
 
298
 
 
299
  gtk_widget_class_install_style_property (GTK_WIDGET_CLASS (klass),
 
300
                                           g_param_spec_boolean ("repeat-insert-with-count",
 
301
                                                                 "Repeat Insert with Count",
 
302
                                                                 "Use the current count to "
 
303
                                                                  "repeat the insertion.",
 
304
                                                                 FALSE,
 
305
                                                                 (G_PARAM_READABLE |
 
306
                                                                  G_PARAM_STATIC_STRINGS)));
 
307
 
 
308
  /* Proxy all action signals from source view */
 
309
  type = IDE_TYPE_SOURCE_VIEW;
 
310
  while (type != G_TYPE_INVALID && type != GTK_TYPE_WIDGET)
 
311
    {
 
312
      proxy_all_action_signals (type);
 
313
      type = g_type_parent (type);
 
314
    }
 
315
 
 
316
  /* Add unbind all entries from parent classes (which is
 
317
     really just the GtkWidget ones) so that we *only* add
 
318
     stuff via modes. Any default ones are handled in the
 
319
     normal fallback paths after mode elements are done. */
 
320
  binding_set = gtk_binding_set_by_class (klass);
 
321
 
 
322
  type = g_type_parent (IDE_TYPE_SOURCE_VIEW_MODE);
 
323
  while (type)
 
324
    {
 
325
      parent_binding_set = gtk_binding_set_find (g_type_name (type));
 
326
      type = g_type_parent (type);
 
327
 
 
328
      if (parent_binding_set)
 
329
        {
 
330
          GtkBindingEntry *entry = parent_binding_set->entries;
 
331
 
 
332
          while (entry != NULL)
 
333
            {
 
334
              gtk_binding_entry_skip (binding_set, entry->keyval, entry->modifiers);
 
335
              entry = entry->set_next;
 
336
            }
 
337
        }
 
338
    }
 
339
}
 
340
 
 
341
static void
 
342
ide_source_view_mode_init (IdeSourceViewMode *mode)
 
343
{
 
344
}
 
345
 
 
346
static gboolean
 
347
is_modifier_key (GdkEventKey *event)
 
348
{
 
349
  static const guint modifier_keyvals[] = {
 
350
    GDK_KEY_Shift_L, GDK_KEY_Shift_R, GDK_KEY_Shift_Lock,
 
351
    GDK_KEY_Caps_Lock, GDK_KEY_ISO_Lock, GDK_KEY_Control_L,
 
352
    GDK_KEY_Control_R, GDK_KEY_Meta_L, GDK_KEY_Meta_R,
 
353
    GDK_KEY_Alt_L, GDK_KEY_Alt_R, GDK_KEY_Super_L, GDK_KEY_Super_R,
 
354
    GDK_KEY_Hyper_L, GDK_KEY_Hyper_R, GDK_KEY_ISO_Level3_Shift,
 
355
    GDK_KEY_ISO_Next_Group, GDK_KEY_ISO_Prev_Group,
 
356
    GDK_KEY_ISO_First_Group, GDK_KEY_ISO_Last_Group,
 
357
    GDK_KEY_Mode_switch, GDK_KEY_Num_Lock, GDK_KEY_Multi_key,
 
358
    GDK_KEY_Scroll_Lock,
 
359
    0
 
360
  };
 
361
  const guint *ac_val;
 
362
 
 
363
  ac_val = modifier_keyvals;
 
364
  while (*ac_val)
 
365
    {
 
366
      if (event->keyval == *ac_val++)
 
367
        return TRUE;
 
368
    }
 
369
 
 
370
  return FALSE;
 
371
}
 
372
 
 
373
static gboolean
 
374
toplevel_is_offscreen (GdkWindow *window)
 
375
{
 
376
  /*
 
377
   * FIXME: This function is a workaround for a segfault in gdk_window_beep()
 
378
   *        with offscreen windows.
 
379
   *
 
380
   *        https://bugzilla.gnome.org/show_bug.cgi?id=748341
 
381
   */
 
382
  for (;
 
383
       window != NULL;
 
384
       window = gdk_window_get_parent (window))
 
385
    {
 
386
      GdkWindowType type;
 
387
 
 
388
      type = gdk_window_get_window_type (window);
 
389
 
 
390
      if (type == GDK_WINDOW_OFFSCREEN)
 
391
        return TRUE;
 
392
    }
 
393
 
 
394
  return FALSE;
 
395
}
 
396
 
 
397
gboolean
 
398
_ide_source_view_mode_do_event (IdeSourceViewMode *mode,
 
399
                                GdkEventKey       *event,
 
400
                                gboolean          *remove)
 
401
{
 
402
  GtkStyleContext *context;
 
403
  gboolean suppress_unbound;
 
404
  gboolean handled;
 
405
 
 
406
  g_return_val_if_fail (IDE_IS_SOURCE_VIEW_MODE (mode), FALSE);
 
407
  g_return_val_if_fail (event, FALSE);
 
408
  g_return_val_if_fail (remove, FALSE);
 
409
 
 
410
  context = gtk_widget_get_style_context (GTK_WIDGET (mode));
 
411
 
 
412
  suppress_unbound = ide_source_view_mode_get_suppress_unbound (mode);
 
413
 
 
414
  g_object_ref (context);
 
415
  gtk_style_context_save (context);
 
416
  gtk_style_context_add_class (context, mode->name);
 
417
  handled = gtk_bindings_activate_event (G_OBJECT (mode), event);
 
418
  gtk_style_context_restore (context);
 
419
  g_object_unref (context);
 
420
 
 
421
  *remove = FALSE;
 
422
  switch (mode->type)
 
423
    {
 
424
    case IDE_SOURCE_VIEW_MODE_TYPE_TRANSIENT:
 
425
      if (handled)
 
426
        {
 
427
          *remove = TRUE;
 
428
        }
 
429
      else
 
430
        {
 
431
          if (!is_modifier_key (event))
 
432
            {
 
433
              if (!toplevel_is_offscreen (event->window))
 
434
                gtk_widget_error_bell (mode->view);
 
435
              handled = TRUE;
 
436
              *remove = TRUE;
 
437
            }
 
438
        }
 
439
      break;
 
440
 
 
441
    case IDE_SOURCE_VIEW_MODE_TYPE_PERMANENT:
 
442
      {
 
443
        /* don't block possible accelerators, but supress others */
 
444
        if (!handled && suppress_unbound && ((event->state & GDK_MODIFIER_MASK) == 0))
 
445
          {
 
446
            if (!is_modifier_key (event) && !toplevel_is_offscreen (event->window))
 
447
              gdk_window_beep (event->window);
 
448
 
 
449
            /* cancel any inflight macros */
 
450
            g_signal_emit_by_name (mode->view, "end-macro");
 
451
 
 
452
            handled = TRUE;
 
453
          }
 
454
      }
 
455
      break;
 
456
 
 
457
    case IDE_SOURCE_VIEW_MODE_TYPE_MODAL:
 
458
      handled = TRUE;
 
459
      break;
 
460
 
 
461
    default:
 
462
      g_assert_not_reached ();
 
463
    }
 
464
 
 
465
  return handled;
 
466
}
 
467
 
 
468
IdeSourceViewMode *
 
469
_ide_source_view_mode_new (GtkWidget             *view,
 
470
                           const char            *name,
 
471
                           IdeSourceViewModeType  type)
 
472
{
 
473
  IdeSourceViewMode *mode;
 
474
 
 
475
  mode = g_object_new (IDE_TYPE_SOURCE_VIEW_MODE, NULL);
 
476
 
 
477
  mode->view = g_object_ref (view);
 
478
  mode->name = g_strdup (name);
 
479
  mode->type = type;
 
480
 
 
481
  if (mode->name != NULL)
 
482
    {
 
483
      GtkStyleContext *context;
 
484
 
 
485
      context = gtk_widget_get_style_context (GTK_WIDGET (mode));
 
486
      gtk_style_context_add_class (context, mode->name);
 
487
    }
 
488
 
 
489
  mode->default_mode = get_string_param (mode, "default-mode");
 
490
  mode->display_name = get_string_param (mode, "display-name");
 
491
 
 
492
  IDE_TRACE_MSG ("supress_unbound = %d", ide_source_view_mode_get_suppress_unbound (mode));
 
493
  IDE_TRACE_MSG ("block_cursor = %d", ide_source_view_mode_get_block_cursor (mode));
 
494
  IDE_TRACE_MSG ("type = %d", (int)mode->type);
 
495
  IDE_TRACE_MSG ("default_mode = %s", mode->default_mode ?: "(null)");
 
496
  IDE_TRACE_MSG ("display_name = %s", mode->display_name ?: "(null)");
 
497
 
 
498
  return g_object_ref_sink (mode);
 
499
}
 
500
 
 
501
IdeSourceViewModeType
 
502
ide_source_view_mode_get_mode_type (IdeSourceViewMode *self)
 
503
{
 
504
  g_return_val_if_fail (IDE_IS_SOURCE_VIEW_MODE (self), 0);
 
505
  return self->type;
 
506
}