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

« back to all changes in this revision

Viewing changes to libide/ide-source-view-movements.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-movements.c
 
2
 *
 
3
 * Copyright (C) 2015 Christian Hergert <christian@hergert.me>
 
4
 *
 
5
 * This program is free software: you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation, either version 3 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
 */
 
18
 
 
19
#define G_LOG_DOMAIN "ide-source-view"
 
20
 
 
21
#include "ide-debug.h"
 
22
#include "ide-enums.h"
 
23
#include "ide-internal.h"
 
24
#include "ide-source-iter.h"
 
25
#include "ide-source-view-movements.h"
 
26
#include "ide-vim-iter.h"
 
27
 
 
28
#define ANCHOR_BEGIN "SELECTION_ANCHOR_BEGIN"
 
29
#define ANCHOR_END   "SELECTION_ANCHOR_END"
 
30
 
 
31
#define TRACE_ITER(iter) \
 
32
  IDE_TRACE_MSG("%d:%d", gtk_text_iter_get_line(iter), \
 
33
                gtk_text_iter_get_line_offset(iter))
 
34
 
 
35
typedef struct
 
36
{
 
37
  IdeSourceView         *self;
 
38
  /* The target_offset contains the ideal character line_offset. This can
 
39
   * sometimes be further forward than designed when the line does not have
 
40
   * enough characters to get back to the original position. -1 indicates
 
41
   * no preference.
 
42
   */
 
43
  gint                  *target_offset;
 
44
  IdeSourceViewMovement  type;                        /* Type of movement */
 
45
  GtkTextIter            insert;                      /* Current insert cursor location */
 
46
  GtkTextIter            selection;                   /* Current selection cursor location */
 
47
  gint                   count;                       /* Repeat count for movement */
 
48
  gunichar               modifier;                    /* For forward/backward char search */
 
49
  guint                  extend_selection : 1;        /* If selection should be extended */
 
50
  guint                  exclusive : 1;               /* See ":help exclusive" in vim */
 
51
  guint                  ignore_select : 1;           /* Don't update selection after movement */
 
52
  guint                  ignore_target_offset : 1;    /* Don't propagate new line offset */
 
53
  guint                  ignore_scroll_to_insert : 1; /* Don't scroll to insert mark */
 
54
} Movement;
 
55
 
 
56
typedef struct
 
57
{
 
58
  gunichar jump_to;
 
59
  gunichar jump_from;
 
60
  guint    depth;
 
61
} MatchingBracketState;
 
62
 
 
63
static gboolean
 
64
is_single_line_selection (const GtkTextIter *begin,
 
65
                          const GtkTextIter *end)
 
66
{
 
67
  if (gtk_text_iter_compare (begin, end) < 0)
 
68
    return ((gtk_text_iter_get_line_offset (begin) == 0) &&
 
69
            (gtk_text_iter_get_line_offset (end) == 0) &&
 
70
            ((gtk_text_iter_get_line (begin) + 1) ==
 
71
             gtk_text_iter_get_line (end)));
 
72
  else
 
73
    return ((gtk_text_iter_get_line_offset (begin) == 0) &&
 
74
            (gtk_text_iter_get_line_offset (end) == 0) &&
 
75
            ((gtk_text_iter_get_line (end) + 1) ==
 
76
             gtk_text_iter_get_line (begin)));
 
77
}
 
78
 
 
79
static gboolean
 
80
is_single_char_selection (const GtkTextIter *begin,
 
81
                          const GtkTextIter *end)
 
82
{
 
83
  GtkTextIter tmp;
 
84
 
 
85
  g_assert (begin);
 
86
  g_assert (end);
 
87
 
 
88
  tmp = *begin;
 
89
  if (gtk_text_iter_forward_char (&tmp) && gtk_text_iter_equal (&tmp, end))
 
90
    return TRUE;
 
91
 
 
92
  tmp = *end;
 
93
  if (gtk_text_iter_forward_char (&tmp) && gtk_text_iter_equal (&tmp, begin))
 
94
    return TRUE;
 
95
 
 
96
  return FALSE;
 
97
}
 
98
 
 
99
static gboolean
 
100
text_iter_forward_to_nonspace_captive (GtkTextIter *iter)
 
101
{
 
102
  while (!gtk_text_iter_ends_line (iter) && g_unichar_isspace (gtk_text_iter_get_char (iter)))
 
103
    if (!gtk_text_iter_forward_char (iter))
 
104
      return FALSE;
 
105
 
 
106
  return !g_unichar_isspace (gtk_text_iter_get_char (iter));
 
107
}
 
108
 
 
109
static void
 
110
select_range (Movement    *mv,
 
111
              GtkTextIter *insert_iter,
 
112
              GtkTextIter *selection_iter)
 
113
{
 
114
  GtkTextBuffer *buffer;
 
115
  GtkTextMark *insert;
 
116
  GtkTextMark *selection;
 
117
  gint insert_off;
 
118
  gint selection_off;
 
119
 
 
120
  g_assert (insert_iter);
 
121
  g_assert (selection_iter);
 
122
 
 
123
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
124
  insert = gtk_text_buffer_get_insert (buffer);
 
125
  selection = gtk_text_buffer_get_selection_bound (buffer);
 
126
 
 
127
  mv->ignore_select = TRUE;
 
128
 
 
129
  /*
 
130
   * If the caller is requesting that we select a single character, we will
 
131
   * keep the iter before that character. This more closely matches the visual
 
132
   * mode in VIM.
 
133
   */
 
134
  insert_off = gtk_text_iter_get_offset (insert_iter);
 
135
  selection_off = gtk_text_iter_get_offset (selection_iter);
 
136
  if ((insert_off - selection_off) == 1)
 
137
    gtk_text_iter_order (insert_iter, selection_iter);
 
138
 
 
139
  gtk_text_buffer_move_mark (buffer, insert, insert_iter);
 
140
  gtk_text_buffer_move_mark (buffer, selection, selection_iter);
 
141
}
 
142
 
 
143
static void
 
144
ensure_anchor_selected (Movement *mv)
 
145
{
 
146
  GtkTextBuffer *buffer;
 
147
  GtkTextMark *selection_mark;
 
148
  GtkTextMark *insert_mark;
 
149
  GtkTextIter anchor_begin;
 
150
  GtkTextIter anchor_end;
 
151
  GtkTextIter insert_iter;
 
152
  GtkTextIter selection_iter;
 
153
  GtkTextMark *selection_anchor_begin;
 
154
  GtkTextMark *selection_anchor_end;
 
155
 
 
156
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
157
 
 
158
  selection_anchor_begin = gtk_text_buffer_get_mark (buffer, ANCHOR_BEGIN);
 
159
  selection_anchor_end = gtk_text_buffer_get_mark (buffer, ANCHOR_END);
 
160
 
 
161
  if (!selection_anchor_begin || !selection_anchor_end)
 
162
    return;
 
163
 
 
164
  gtk_text_buffer_get_iter_at_mark (buffer, &anchor_begin, selection_anchor_begin);
 
165
  gtk_text_buffer_get_iter_at_mark (buffer, &anchor_end, selection_anchor_end);
 
166
 
 
167
  insert_mark = gtk_text_buffer_get_insert (buffer);
 
168
  gtk_text_buffer_get_iter_at_mark (buffer, &insert_iter, insert_mark);
 
169
 
 
170
  selection_mark = gtk_text_buffer_get_selection_bound (buffer);
 
171
  gtk_text_buffer_get_iter_at_mark (buffer, &selection_iter, selection_mark);
 
172
 
 
173
  if ((gtk_text_iter_compare (&selection_iter, &anchor_end) < 0) &&
 
174
      (gtk_text_iter_compare (&insert_iter, &anchor_end) < 0))
 
175
    {
 
176
      if (gtk_text_iter_compare (&insert_iter, &selection_iter) < 0)
 
177
        select_range (mv, &insert_iter, &anchor_end);
 
178
      else
 
179
        select_range (mv, &anchor_end, &selection_iter);
 
180
    }
 
181
  else if ((gtk_text_iter_compare (&selection_iter, &anchor_begin) > 0) &&
 
182
           (gtk_text_iter_compare (&insert_iter, &anchor_begin) > 0))
 
183
    {
 
184
      if (gtk_text_iter_compare (&insert_iter, &selection_iter) < 0)
 
185
        select_range (mv, &anchor_begin, &selection_iter);
 
186
      else
 
187
        select_range (mv, &insert_iter, &anchor_begin);
 
188
    }
 
189
}
 
190
 
 
191
static gboolean
 
192
text_iter_forward_to_empty_line (GtkTextIter *iter,
 
193
                                 GtkTextIter *bounds)
 
194
{
 
195
  if (!gtk_text_iter_forward_char (iter))
 
196
    return FALSE;
 
197
 
 
198
  while (gtk_text_iter_compare (iter, bounds) < 0)
 
199
    {
 
200
      if (gtk_text_iter_starts_line (iter) && gtk_text_iter_ends_line (iter))
 
201
        return TRUE;
 
202
      if (!gtk_text_iter_forward_char (iter))
 
203
        return FALSE;
 
204
    }
 
205
 
 
206
  return FALSE;
 
207
}
 
208
 
 
209
static void
 
210
ide_source_view_movements_get_selection (Movement *mv)
 
211
{
 
212
  GtkTextBuffer *buffer;
 
213
  GtkTextMark *mark;
 
214
 
 
215
  g_assert (mv);
 
216
  g_assert (IDE_IS_SOURCE_VIEW (mv->self));
 
217
 
 
218
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
219
 
 
220
  mark = gtk_text_buffer_get_insert (buffer);
 
221
  gtk_text_buffer_get_iter_at_mark (buffer, &mv->insert, mark);
 
222
 
 
223
  mark = gtk_text_buffer_get_selection_bound (buffer);
 
224
  gtk_text_buffer_get_iter_at_mark (buffer, &mv->selection, mark);
 
225
}
 
226
 
 
227
static void
 
228
ide_source_view_movements_select_range (Movement *mv)
 
229
{
 
230
  GtkTextBuffer *buffer;
 
231
  GtkTextMark *mark;
 
232
 
 
233
  g_assert (mv);
 
234
  g_assert (IDE_IS_SOURCE_VIEW (mv->self));
 
235
 
 
236
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
237
 
 
238
  if (mv->extend_selection)
 
239
    gtk_text_buffer_select_range (buffer, &mv->insert, &mv->selection);
 
240
  else
 
241
    gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
 
242
 
 
243
  mark = gtk_text_buffer_get_insert (buffer);
 
244
  gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW (mv->self), mark);
 
245
}
 
246
 
 
247
static void
 
248
ide_source_view_movements_nth_char (Movement *mv)
 
249
{
 
250
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
251
 
 
252
  for (; mv->count > 0; mv->count--)
 
253
    {
 
254
      if (gtk_text_iter_ends_line (&mv->insert))
 
255
        break;
 
256
      gtk_text_iter_forward_char (&mv->insert);
 
257
    }
 
258
 
 
259
  if (!mv->exclusive)
 
260
    gtk_text_iter_forward_char (&mv->insert);
 
261
}
 
262
 
 
263
static void
 
264
ide_source_view_movements_previous_char (Movement *mv)
 
265
{
 
266
  mv->count = MAX (1, mv->count);
 
267
 
 
268
  for (; mv->count; mv->count--)
 
269
    {
 
270
      if (gtk_text_iter_starts_line (&mv->insert))
 
271
        break;
 
272
      gtk_text_iter_backward_char (&mv->insert);
 
273
    }
 
274
 
 
275
  if (!mv->exclusive)
 
276
    gtk_text_iter_forward_char (&mv->insert);
 
277
}
 
278
 
 
279
static void
 
280
ide_source_view_movements_next_char (Movement *mv)
 
281
{
 
282
  mv->count = MAX (1, mv->count);
 
283
 
 
284
  for (; mv->count; mv->count--)
 
285
    {
 
286
      if (gtk_text_iter_ends_line (&mv->insert))
 
287
        break;
 
288
      gtk_text_iter_forward_char (&mv->insert);
 
289
    }
 
290
 
 
291
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
292
    gtk_text_iter_forward_char (&mv->insert);
 
293
}
 
294
 
 
295
static void
 
296
ide_source_view_movements_first_char (Movement *mv)
 
297
{
 
298
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
299
}
 
300
 
 
301
static void
 
302
ide_source_view_movements_first_nonspace_char (Movement *mv)
 
303
{
 
304
  gunichar ch;
 
305
 
 
306
  if (gtk_text_iter_get_line_offset (&mv->insert) != 0)
 
307
    gtk_text_iter_set_line_offset (&mv->insert, 0);
 
308
 
 
309
  while (!gtk_text_iter_ends_line (&mv->insert) &&
 
310
         (ch = gtk_text_iter_get_char (&mv->insert)) &&
 
311
         g_unichar_isspace (ch))
 
312
    gtk_text_iter_forward_char (&mv->insert);
 
313
 
 
314
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
315
    gtk_text_iter_forward_char (&mv->insert);
 
316
}
 
317
 
 
318
static void
 
319
ide_source_view_movements_line_chars (Movement *mv)
 
320
{
 
321
  GtkTextIter orig = mv->insert;
 
322
 
 
323
  /*
 
324
   * Selects the current position up to the first nonspace character.
 
325
   * If the cursor is at the line start, we will select the newline.
 
326
   * If only whitespace exists, we will select line offset of 0.
 
327
   */
 
328
 
 
329
  if (gtk_text_iter_starts_line (&mv->insert))
 
330
    {
 
331
      gtk_text_iter_backward_char (&mv->insert);
 
332
    }
 
333
  else
 
334
    {
 
335
      gunichar ch;
 
336
 
 
337
      gtk_text_iter_set_line_offset (&mv->insert, 0);
 
338
 
 
339
      while (!gtk_text_iter_ends_line (&mv->insert) &&
 
340
             (ch = gtk_text_iter_get_char (&mv->insert)) &&
 
341
             g_unichar_isspace (ch))
 
342
        gtk_text_iter_forward_char (&mv->insert);
 
343
 
 
344
      if (gtk_text_iter_ends_line (&mv->insert) ||
 
345
          (gtk_text_iter_compare (&orig, &mv->insert) <= 0))
 
346
        gtk_text_iter_set_line_offset (&mv->insert, 0);
 
347
    }
 
348
 
 
349
  if (!mv->exclusive)
 
350
    gtk_text_iter_forward_char (&mv->insert);
 
351
}
 
352
 
 
353
static void
 
354
ide_source_view_movements_line_end (Movement *mv)
 
355
{
 
356
  if (!gtk_text_iter_ends_line (&mv->insert))
 
357
    gtk_text_iter_forward_to_line_end (&mv->insert);
 
358
 
 
359
  if (!mv->exclusive)
 
360
    gtk_text_iter_forward_char (&mv->insert);
 
361
}
 
362
 
 
363
static void
 
364
ide_source_view_movements_middle_char (Movement *mv)
 
365
{
 
366
  GtkTextView *text_view = GTK_TEXT_VIEW (mv->self);
 
367
  GdkWindow *window;
 
368
  GdkRectangle rect;
 
369
  guint line_offset;
 
370
  int width;
 
371
  int chars_in_line;
 
372
 
 
373
  gtk_text_view_get_iter_location (text_view, &mv->insert, &rect);
 
374
  window = gtk_text_view_get_window (text_view, GTK_TEXT_WINDOW_TEXT);
 
375
 
 
376
  width = gdk_window_get_width (window);
 
377
  if (rect.width <= 0)
 
378
    return;
 
379
 
 
380
  chars_in_line = width / rect.width;
 
381
  if (chars_in_line == 0)
 
382
    return;
 
383
 
 
384
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
385
 
 
386
  for (line_offset = chars_in_line / 2; line_offset; line_offset--)
 
387
    if (!gtk_text_iter_forward_char (&mv->insert))
 
388
      break;
 
389
 
 
390
  if (!mv->exclusive)
 
391
    if (!gtk_text_iter_ends_line (&mv->insert))
 
392
      gtk_text_iter_forward_char (&mv->insert);
 
393
}
 
394
 
 
395
static void
 
396
ide_source_view_movements_last_char (Movement *mv)
 
397
{
 
398
  if (!gtk_text_iter_ends_line (&mv->insert))
 
399
    {
 
400
      gtk_text_iter_forward_to_line_end (&mv->insert);
 
401
      if (mv->exclusive && !gtk_text_iter_starts_line (&mv->insert))
 
402
        gtk_text_iter_backward_char (&mv->insert);
 
403
    }
 
404
}
 
405
 
 
406
static void
 
407
ide_source_view_movements_first_line (Movement *mv)
 
408
{
 
409
  gtk_text_iter_set_line (&mv->insert, mv->count);
 
410
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
411
}
 
412
 
 
413
static void
 
414
ide_source_view_movements_nth_line (Movement *mv)
 
415
{
 
416
  GtkTextBuffer *buffer;
 
417
 
 
418
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
419
 
 
420
  if (mv->count < 1)
 
421
    gtk_text_buffer_get_end_iter (buffer, &mv->insert);
 
422
  else
 
423
    gtk_text_iter_set_line (&mv->insert, mv->count - 1);
 
424
 
 
425
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
426
 
 
427
  while (!gtk_text_iter_ends_line (&mv->insert) &&
 
428
         g_unichar_isspace (gtk_text_iter_get_char (&mv->insert)))
 
429
    if (!gtk_text_iter_forward_char (&mv->insert))
 
430
      break;
 
431
}
 
432
 
 
433
static void
 
434
ide_source_view_movements_last_line (Movement *mv)
 
435
{
 
436
  GtkTextBuffer *buffer;
 
437
 
 
438
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
439
  gtk_text_buffer_get_end_iter (buffer, &mv->insert);
 
440
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
441
 
 
442
  if (mv->count)
 
443
    {
 
444
      gint line;
 
445
 
 
446
      line = gtk_text_iter_get_line (&mv->insert) - mv->count;
 
447
      gtk_text_iter_set_line (&mv->insert, MAX (0, line));
 
448
    }
 
449
}
 
450
 
 
451
static void
 
452
ide_source_view_movements_next_line (Movement *mv)
 
453
{
 
454
  GtkTextBuffer *buffer;
 
455
  gboolean has_selection;
 
456
  guint line;
 
457
  guint offset = 0;
 
458
 
 
459
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
460
 
 
461
  /* check for linewise */
 
462
  has_selection = !gtk_text_iter_equal (&mv->insert, &mv->selection) || !mv->exclusive;
 
463
 
 
464
  line = gtk_text_iter_get_line (&mv->insert);
 
465
 
 
466
  if ((*mv->target_offset) > 0)
 
467
    offset = *mv->target_offset;
 
468
 
 
469
  /*
 
470
   * If we have a whole line selected (from say `V`), then we need to swap
 
471
   * the cursor and selection. This feels to me like a slight bit of a hack.
 
472
   * There may be cause to actually have a selection mode and know the type
 
473
   * of selection (line vs individual characters).
 
474
   */
 
475
  if (is_single_line_selection (&mv->insert, &mv->selection))
 
476
    {
 
477
      guint target_line;
 
478
 
 
479
      if (gtk_text_iter_compare (&mv->insert, &mv->selection) < 0)
 
480
        gtk_text_iter_order (&mv->selection, &mv->insert);
 
481
 
 
482
      target_line = gtk_text_iter_get_line (&mv->insert) + 1;
 
483
      gtk_text_iter_set_line (&mv->insert, target_line);
 
484
 
 
485
      if (target_line != gtk_text_iter_get_line (&mv->insert))
 
486
        goto select_to_end;
 
487
 
 
488
      select_range (mv, &mv->insert, &mv->selection);
 
489
      ensure_anchor_selected (mv);
 
490
      return;
 
491
    }
 
492
 
 
493
  if (is_single_char_selection (&mv->insert, &mv->selection))
 
494
    {
 
495
      if (gtk_text_iter_compare (&mv->insert, &mv->selection) < 0)
 
496
        *mv->target_offset = ++offset;
 
497
    }
 
498
 
 
499
  gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, line + 1);
 
500
  if ((line + 1) == gtk_text_iter_get_line (&mv->insert))
 
501
    {
 
502
      for (; offset; offset--)
 
503
        if (!gtk_text_iter_ends_line (&mv->insert))
 
504
          if (!gtk_text_iter_forward_char (&mv->insert))
 
505
            break;
 
506
      if (has_selection)
 
507
        {
 
508
          select_range (mv, &mv->insert, &mv->selection);
 
509
          ensure_anchor_selected (mv);
 
510
        }
 
511
      else
 
512
        gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
 
513
    }
 
514
  else
 
515
    {
 
516
select_to_end:
 
517
      gtk_text_buffer_get_end_iter (buffer, &mv->insert);
 
518
      if (has_selection)
 
519
        {
 
520
          select_range (mv, &mv->insert, &mv->selection);
 
521
          ensure_anchor_selected (mv);
 
522
        }
 
523
      else
 
524
        gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
 
525
    }
 
526
 
 
527
  /* make sure selection/insert are up to date */
 
528
  if (!gtk_text_buffer_get_has_selection (buffer))
 
529
    mv->selection = mv->insert;
 
530
}
 
531
 
 
532
static void
 
533
ide_source_view_movements_previous_line (Movement *mv)
 
534
{
 
535
  GtkTextBuffer *buffer;
 
536
  gboolean has_selection;
 
537
  guint line;
 
538
  guint offset = 0;
 
539
 
 
540
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
541
 
 
542
  /* check for linewise */
 
543
  has_selection = !gtk_text_iter_equal (&mv->insert, &mv->selection) || !mv->exclusive;
 
544
 
 
545
  line = gtk_text_iter_get_line (&mv->insert);
 
546
 
 
547
  if ((*mv->target_offset) > 0)
 
548
    offset = *mv->target_offset;
 
549
 
 
550
  if (line == 0)
 
551
    return;
 
552
 
 
553
  /*
 
554
   * If we have a whole line selected (from say `V`), then we need to swap the cursor and
 
555
   * selection. This feels to me like a slight bit of a hack.  There may be cause to actually have
 
556
   * a selection mode and know the type of selection (line vs individual characters).
 
557
   */
 
558
  if (is_single_line_selection (&mv->insert, &mv->selection))
 
559
    {
 
560
      if (gtk_text_iter_compare (&mv->insert, &mv->selection) > 0)
 
561
        gtk_text_iter_order (&mv->insert, &mv->selection);
 
562
      gtk_text_iter_set_line (&mv->insert, gtk_text_iter_get_line (&mv->insert) - 1);
 
563
      select_range (mv, &mv->insert, &mv->selection);
 
564
      ensure_anchor_selected (mv);
 
565
      return;
 
566
    }
 
567
 
 
568
  if (is_single_char_selection (&mv->insert, &mv->selection))
 
569
    {
 
570
      if (gtk_text_iter_compare (&mv->insert, &mv->selection) > 0)
 
571
        {
 
572
          if (offset)
 
573
            --offset;
 
574
          *mv->target_offset = offset;
 
575
        }
 
576
    }
 
577
 
 
578
  gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, line - 1);
 
579
  if ((line - 1) == gtk_text_iter_get_line (&mv->insert))
 
580
    {
 
581
      for (; offset; offset--)
 
582
        if (!gtk_text_iter_ends_line (&mv->insert))
 
583
          if (!gtk_text_iter_forward_char (&mv->insert))
 
584
            break;
 
585
 
 
586
      if (has_selection)
 
587
        {
 
588
          if (gtk_text_iter_equal (&mv->insert, &mv->selection))
 
589
            gtk_text_iter_backward_char (&mv->insert);
 
590
          select_range (mv, &mv->insert, &mv->selection);
 
591
          ensure_anchor_selected (mv);
 
592
        }
 
593
      else
 
594
        gtk_text_buffer_select_range (buffer, &mv->insert, &mv->insert);
 
595
    }
 
596
 
 
597
  /* make sure selection/insert are up to date */
 
598
  if (!gtk_text_buffer_get_has_selection (buffer))
 
599
    mv->selection = mv->insert;
 
600
}
 
601
 
 
602
static void
 
603
ide_source_view_movements_screen_top (Movement *mv)
 
604
{
 
605
  GtkTextView *text_view = (GtkTextView *)mv->self;
 
606
  GdkRectangle rect;
 
607
 
 
608
  ide_source_view_get_visible_rect (mv->self, &rect);
 
609
  gtk_text_view_get_iter_at_location (text_view, &mv->insert, rect.x, rect.y);
 
610
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
611
}
 
612
 
 
613
static void
 
614
ide_source_view_movements_screen_middle (Movement *mv)
 
615
{
 
616
  GtkTextView *text_view = (GtkTextView *)mv->self;
 
617
  GdkRectangle rect;
 
618
 
 
619
  ide_source_view_get_visible_rect (mv->self, &rect);
 
620
  gtk_text_view_get_iter_at_location (text_view, &mv->insert, rect.x, rect.y + (rect.height / 2));
 
621
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
622
}
 
623
 
 
624
static void
 
625
ide_source_view_movements_screen_bottom (Movement *mv)
 
626
{
 
627
  GtkTextView *text_view = (GtkTextView *)mv->self;
 
628
  GdkRectangle rect;
 
629
 
 
630
  ide_source_view_get_visible_rect (mv->self, &rect);
 
631
  gtk_text_view_get_iter_at_location (text_view, &mv->insert, rect.x, rect.y + rect.height - 1);
 
632
  gtk_text_iter_set_line_offset (&mv->insert, 0);
 
633
}
 
634
 
 
635
static void
 
636
ide_source_view_movements_scroll_by_lines (Movement *mv,
 
637
                                           gint      lines)
 
638
{
 
639
  GtkTextView *text_view = (GtkTextView *)mv->self;
 
640
  GtkAdjustment *vadj;
 
641
  GtkTextBuffer *buffer;
 
642
  GtkTextIter begin;
 
643
  GtkTextIter end;
 
644
  GdkRectangle rect;
 
645
  gdouble amount;
 
646
  gdouble value;
 
647
  gdouble upper;
 
648
 
 
649
  if (lines == 0)
 
650
    return;
 
651
 
 
652
  vadj = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (mv->self));
 
653
  buffer = gtk_text_view_get_buffer (text_view);
 
654
 
 
655
  gtk_text_buffer_get_bounds (buffer, &begin, &end);
 
656
 
 
657
  if (lines > 0)
 
658
    {
 
659
      if (gtk_text_iter_get_line (&end) == gtk_text_iter_get_line (&mv->insert))
 
660
        return;
 
661
    }
 
662
  else if (lines < 0)
 
663
    {
 
664
      if (gtk_text_iter_get_line (&begin) == gtk_text_iter_get_line (&mv->insert))
 
665
        return;
 
666
    }
 
667
  else
 
668
    g_assert_not_reached ();
 
669
 
 
670
  gtk_text_view_get_iter_location (text_view, &mv->insert, &rect);
 
671
 
 
672
  amount = lines * rect.height;
 
673
 
 
674
  value = gtk_adjustment_get_value (vadj);
 
675
  upper = gtk_adjustment_get_upper (vadj);
 
676
  gtk_adjustment_set_value (vadj, CLAMP (value + amount, 0, upper));
 
677
 
 
678
  mv->ignore_scroll_to_insert = TRUE;
 
679
  ide_source_view_place_cursor_onscreen (mv->self);
 
680
}
 
681
 
 
682
static void
 
683
ide_source_view_movements_scroll (Movement *mv)
 
684
{
 
685
  GtkTextBuffer *buffer;
 
686
  GtkTextMark *mark;
 
687
  gint count = MAX (1, mv->count);
 
688
 
 
689
  if (mv->type == IDE_SOURCE_VIEW_MOVEMENT_SCREEN_DOWN)
 
690
    count = -count;
 
691
 
 
692
  ide_source_view_movements_scroll_by_lines (mv, count);
 
693
 
 
694
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
695
  mark = gtk_text_buffer_get_insert (buffer);
 
696
  gtk_text_buffer_get_iter_at_mark (buffer, &mv->insert, mark);
 
697
  ide_source_view_move_mark_onscreen (mv->self, mark);
 
698
}
 
699
 
 
700
static void
 
701
ide_source_view_movements_move_page (Movement *mv)
 
702
{
 
703
  GtkTextView *text_view = (GtkTextView *)mv->self;
 
704
  GtkTextBuffer *buffer;
 
705
  GtkTextMark *mark;
 
706
  GdkRectangle rect;
 
707
  GtkTextIter iter_top;
 
708
  GtkTextIter iter_bottom;
 
709
  GtkTextIter scroll_iter;
 
710
  gint scrolloff;
 
711
  gint half_page;
 
712
  gint line_top;
 
713
  gint line_bottom;
 
714
 
 
715
  gtk_text_view_get_visible_rect (text_view, &rect);
 
716
  gtk_text_view_get_iter_at_location (text_view, &iter_top, rect.x, rect.y);
 
717
  gtk_text_view_get_iter_at_location (text_view, &iter_bottom,
 
718
                                      rect.x + rect.width,
 
719
                                      rect.y + rect.height);
 
720
 
 
721
  buffer = gtk_text_view_get_buffer (text_view);
 
722
 
 
723
  line_top = gtk_text_iter_get_line (&iter_top);
 
724
  line_bottom = gtk_text_iter_get_line (&iter_bottom);
 
725
 
 
726
  half_page = MAX (1, (line_bottom - line_top) / 2);
 
727
  scrolloff = MIN (ide_source_view_get_scroll_offset (mv->self), half_page);
 
728
 
 
729
  switch ((int)mv->type)
 
730
    {
 
731
    case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_UP:
 
732
      ide_source_view_movements_scroll_by_lines (mv, -half_page);
 
733
      gtk_text_iter_backward_lines (&mv->insert, half_page);
 
734
      break;
 
735
 
 
736
    case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_DOWN:
 
737
      ide_source_view_movements_scroll_by_lines (mv, half_page);
 
738
      gtk_text_iter_forward_lines (&mv->insert, half_page);
 
739
      break;
 
740
 
 
741
    case IDE_SOURCE_VIEW_MOVEMENT_PAGE_UP:
 
742
      gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, MAX (0, line_top - scrolloff));
 
743
      text_iter_forward_to_nonspace_captive (&mv->insert);
 
744
      ide_source_view_movements_select_range (mv);
 
745
 
 
746
      mark = _ide_source_view_get_scroll_mark (mv->self);
 
747
      gtk_text_buffer_get_iter_at_line (buffer, &scroll_iter, line_top);
 
748
      gtk_text_buffer_move_mark (buffer, mark, &scroll_iter);
 
749
      gtk_text_view_scroll_to_mark (text_view, mark, 0.0, TRUE, 1.0, 1.0);
 
750
 
 
751
      mv->ignore_select = TRUE;
 
752
      mv->ignore_scroll_to_insert = TRUE;
 
753
      break;
 
754
 
 
755
    case IDE_SOURCE_VIEW_MOVEMENT_PAGE_DOWN:
 
756
      gtk_text_buffer_get_iter_at_line (buffer, &mv->insert, line_bottom + scrolloff);
 
757
      text_iter_forward_to_nonspace_captive (&mv->insert);
 
758
      ide_source_view_movements_select_range (mv);
 
759
 
 
760
      mark = _ide_source_view_get_scroll_mark (mv->self);
 
761
      gtk_text_buffer_get_iter_at_line (buffer, &scroll_iter, line_bottom);
 
762
      gtk_text_buffer_move_mark (buffer, mark, &scroll_iter);
 
763
      gtk_text_view_scroll_to_mark (text_view, mark, 0.0, TRUE, 1.0, 0.0);
 
764
 
 
765
      mv->ignore_select = TRUE;
 
766
      mv->ignore_scroll_to_insert = TRUE;
 
767
      break;
 
768
 
 
769
    default:
 
770
      g_assert_not_reached();
 
771
    }
 
772
}
 
773
 
 
774
static gboolean
 
775
bracket_predicate (gunichar ch,
 
776
                   gpointer user_data)
 
777
{
 
778
  MatchingBracketState *state = user_data;
 
779
 
 
780
  if (ch == state->jump_from)
 
781
    state->depth++;
 
782
  else if (ch == state->jump_to)
 
783
    state->depth--;
 
784
 
 
785
  return (state->depth == 0);
 
786
}
 
787
 
 
788
static void
 
789
ide_source_view_movements_match_special (Movement *mv)
 
790
{
 
791
  MatchingBracketState state;
 
792
  GtkTextIter copy;
 
793
  gboolean is_forward = FALSE;
 
794
  gboolean ret;
 
795
 
 
796
  copy = mv->insert;
 
797
 
 
798
  state.depth = 1;
 
799
  state.jump_from = gtk_text_iter_get_char (&mv->insert);
 
800
 
 
801
  switch (state.jump_from)
 
802
    {
 
803
    case '{':
 
804
      state.jump_to = '}';
 
805
      is_forward = TRUE;
 
806
      break;
 
807
 
 
808
    case '[':
 
809
      state.jump_to = ']';
 
810
      is_forward = TRUE;
 
811
      break;
 
812
 
 
813
    case '(':
 
814
      state.jump_to = ')';
 
815
      is_forward = TRUE;
 
816
      break;
 
817
 
 
818
    case '}':
 
819
      state.jump_to = '{';
 
820
      is_forward = FALSE;
 
821
      break;
 
822
 
 
823
    case ']':
 
824
      state.jump_to = '[';
 
825
      is_forward = FALSE;
 
826
      break;
 
827
 
 
828
    case ')':
 
829
      state.jump_to = '(';
 
830
      is_forward = FALSE;
 
831
      break;
 
832
 
 
833
    default:
 
834
      return;
 
835
    }
 
836
 
 
837
  if (is_forward)
 
838
    ret = gtk_text_iter_forward_find_char (&mv->insert, bracket_predicate, &state, NULL);
 
839
  else
 
840
    ret = gtk_text_iter_backward_find_char (&mv->insert, bracket_predicate, &state, NULL);
 
841
 
 
842
  if (!ret)
 
843
    mv->insert = copy;
 
844
  else if (!mv->exclusive)
 
845
    gtk_text_iter_forward_char (&mv->insert);
 
846
}
 
847
 
 
848
static void
 
849
ide_source_view_movements_scroll_center (Movement *mv)
 
850
{
 
851
  GtkTextView *text_view = (GtkTextView *)mv->self;
 
852
  GtkTextMark *insert;
 
853
  GtkTextBuffer *buffer;
 
854
 
 
855
  buffer = gtk_text_view_get_buffer (text_view);
 
856
  insert = gtk_text_buffer_get_insert (buffer);
 
857
 
 
858
  switch ((int)mv->type)
 
859
    {
 
860
    case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_BOTTOM:
 
861
      ide_source_view_scroll_to_mark (mv->self, insert, 0.0, TRUE, 1.0, 1.0, TRUE);
 
862
      break;
 
863
 
 
864
    case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_TOP:
 
865
      ide_source_view_scroll_to_mark (mv->self, insert, 0.0, TRUE, 1.0, 0.0, TRUE);
 
866
      break;
 
867
 
 
868
    case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_CENTER:
 
869
      ide_source_view_scroll_to_mark (mv->self, insert, 0.0, TRUE, 1.0, 0.5, TRUE);
 
870
      break;
 
871
 
 
872
    default:
 
873
      break;
 
874
    }
 
875
}
 
876
 
 
877
static void
 
878
ide_source_view_movements_next_word_end (Movement *mv)
 
879
{
 
880
  GtkTextIter copy;
 
881
 
 
882
  copy = mv->insert;
 
883
 
 
884
  _ide_vim_iter_forward_word_end (&mv->insert);
 
885
 
 
886
  /* prefer an empty line before word */
 
887
  text_iter_forward_to_empty_line (&copy, &mv->insert);
 
888
  if (gtk_text_iter_compare (&copy, &mv->insert) < 0)
 
889
    mv->insert = copy;
 
890
 
 
891
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
892
    gtk_text_iter_forward_char (&mv->insert);
 
893
}
 
894
 
 
895
static void
 
896
ide_source_view_movements_next_full_word_end (Movement *mv)
 
897
{
 
898
  GtkTextIter copy;
 
899
 
 
900
  copy = mv->insert;
 
901
 
 
902
  _ide_vim_iter_forward_WORD_end (&mv->insert);
 
903
 
 
904
  /* prefer an empty line before word */
 
905
  text_iter_forward_to_empty_line (&copy, &mv->insert);
 
906
  if (gtk_text_iter_compare (&copy, &mv->insert) < 0)
 
907
    mv->insert = copy;
 
908
 
 
909
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
910
    gtk_text_iter_forward_char (&mv->insert);
 
911
}
 
912
 
 
913
static void
 
914
ide_source_view_movements_next_word_start (Movement *mv)
 
915
{
 
916
  GtkTextIter copy;
 
917
 
 
918
  copy = mv->insert;
 
919
 
 
920
  _ide_vim_iter_forward_word_start (&mv->insert);
 
921
 
 
922
  /* prefer an empty line before word */
 
923
  text_iter_forward_to_empty_line (&copy, &mv->insert);
 
924
  if (gtk_text_iter_compare (&copy, &mv->insert) < 0)
 
925
    mv->insert = copy;
 
926
 
 
927
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
928
    gtk_text_iter_forward_char (&mv->insert);
 
929
}
 
930
 
 
931
static void
 
932
ide_source_view_movements_next_full_word_start (Movement *mv)
 
933
{
 
934
  GtkTextIter copy;
 
935
 
 
936
  copy = mv->insert;
 
937
 
 
938
  _ide_vim_iter_forward_WORD_start (&mv->insert);
 
939
 
 
940
  /* prefer an empty line before word */
 
941
  text_iter_forward_to_empty_line (&copy, &mv->insert);
 
942
  if (gtk_text_iter_compare (&copy, &mv->insert) < 0)
 
943
    mv->insert = copy;
 
944
 
 
945
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
946
    gtk_text_iter_forward_char (&mv->insert);
 
947
}
 
948
 
 
949
static void
 
950
ide_source_view_movements_previous_word_start (Movement *mv)
 
951
{
 
952
  GtkTextIter copy;
 
953
 
 
954
  copy = mv->insert;
 
955
 
 
956
  _ide_source_iter_backward_visible_word_start (&mv->insert);
 
957
 
 
958
  /*
 
959
   * Vim treats an empty line as a word.
 
960
   */
 
961
  if (gtk_text_iter_backward_char (&copy))
 
962
    if (gtk_text_iter_get_char (&copy) == '\n')
 
963
      mv->insert = copy;
 
964
 
 
965
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
966
    gtk_text_iter_forward_char (&mv->insert);
 
967
}
 
968
 
 
969
static void
 
970
ide_source_view_movements_previous_full_word_start (Movement *mv)
 
971
{
 
972
  GtkTextIter copy;
 
973
 
 
974
  copy = mv->insert;
 
975
 
 
976
  _ide_source_iter_backward_full_word_start (&mv->insert);
 
977
 
 
978
  /*
 
979
   * Vim treats an empty line as a word.
 
980
   */
 
981
  if (gtk_text_iter_backward_char (&copy))
 
982
    if (gtk_text_iter_get_char (&copy) == '\n')
 
983
      mv->insert = copy;
 
984
 
 
985
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
986
    gtk_text_iter_forward_char (&mv->insert);
 
987
}
 
988
 
 
989
static void
 
990
ide_source_view_movements_previous_word_end (Movement *mv)
 
991
{
 
992
  GtkTextIter copy;
 
993
 
 
994
  copy = mv->insert;
 
995
 
 
996
  _ide_vim_iter_backward_word_end (&mv->insert);
 
997
 
 
998
  /*
 
999
   * Vim treats an empty line as a word.
 
1000
   */
 
1001
  while ((gtk_text_iter_compare (&copy, &mv->insert) > 0) &&
 
1002
         gtk_text_iter_backward_char (&copy))
 
1003
    {
 
1004
      if (gtk_text_iter_starts_line (&copy) &&
 
1005
          gtk_text_iter_ends_line (&copy))
 
1006
        mv->insert = copy;
 
1007
    }
 
1008
 
 
1009
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
1010
    gtk_text_iter_forward_char (&mv->insert);
 
1011
}
 
1012
 
 
1013
static void
 
1014
ide_source_view_movements_previous_full_word_end (Movement *mv)
 
1015
{
 
1016
  GtkTextIter copy;
 
1017
 
 
1018
  copy = mv->insert;
 
1019
 
 
1020
  _ide_vim_iter_backward_WORD_end (&mv->insert);
 
1021
 
 
1022
  /*
 
1023
   * Vim treats an empty line as a word.
 
1024
   */
 
1025
  while ((gtk_text_iter_compare (&copy, &mv->insert) > 0) &&
 
1026
         gtk_text_iter_backward_char (&copy))
 
1027
    {
 
1028
      if (gtk_text_iter_starts_line (&copy) &&
 
1029
          gtk_text_iter_ends_line (&copy))
 
1030
        mv->insert = copy;
 
1031
    }
 
1032
 
 
1033
  if (!mv->exclusive && !gtk_text_iter_ends_line (&mv->insert))
 
1034
    gtk_text_iter_forward_char (&mv->insert);
 
1035
}
 
1036
 
 
1037
static void
 
1038
ide_source_view_movements_paragraph_start (Movement *mv)
 
1039
{
 
1040
  _ide_vim_iter_backward_paragraph_start (&mv->insert);
 
1041
 
 
1042
  if (mv->exclusive)
 
1043
    {
 
1044
      while (g_unichar_isspace (gtk_text_iter_get_char (&mv->insert)))
 
1045
        {
 
1046
          if (!gtk_text_iter_forward_char (&mv->insert))
 
1047
            break;
 
1048
        }
 
1049
    }
 
1050
}
 
1051
 
 
1052
static void
 
1053
ide_source_view_movements_paragraph_end (Movement *mv)
 
1054
{
 
1055
  _ide_vim_iter_forward_paragraph_end (&mv->insert);
 
1056
 
 
1057
  if (mv->exclusive)
 
1058
    {
 
1059
      gboolean adjust = FALSE;
 
1060
 
 
1061
      while (g_unichar_isspace (gtk_text_iter_get_char (&mv->insert)))
 
1062
        {
 
1063
          adjust = TRUE;
 
1064
          if (!gtk_text_iter_backward_char (&mv->insert))
 
1065
            break;
 
1066
        }
 
1067
 
 
1068
      if (adjust)
 
1069
        gtk_text_iter_forward_char (&mv->insert);
 
1070
    }
 
1071
}
 
1072
 
 
1073
static void
 
1074
ide_source_view_movements_sentence_start (Movement *mv)
 
1075
{
 
1076
  _ide_vim_iter_backward_sentence_start (&mv->insert);
 
1077
}
 
1078
 
 
1079
static void
 
1080
ide_source_view_movements_sentence_end (Movement *mv)
 
1081
{
 
1082
  _ide_vim_iter_forward_sentence_end (&mv->insert);
 
1083
}
 
1084
 
 
1085
static void
 
1086
ide_source_view_movements_line_percentage (Movement *mv)
 
1087
{
 
1088
  GtkTextBuffer *buffer;
 
1089
  GtkTextIter end;
 
1090
  guint end_line;
 
1091
 
 
1092
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (mv->self));
 
1093
  gtk_text_buffer_get_end_iter (buffer, &end);
 
1094
  end_line = gtk_text_iter_get_line (&end);
 
1095
 
 
1096
  if (!mv->count)
 
1097
    {
 
1098
      gtk_text_iter_set_line (&mv->insert, 0);
 
1099
    }
 
1100
  else
 
1101
    {
 
1102
      guint line;
 
1103
 
 
1104
      mv->count = MAX (1, mv->count);
 
1105
      line = (float)end_line * (mv->count / 100.0);
 
1106
      gtk_text_iter_set_line (&mv->insert, line);
 
1107
    }
 
1108
 
 
1109
  mv->count = 0;
 
1110
 
 
1111
  ide_source_view_movements_first_nonspace_char (mv);
 
1112
}
 
1113
 
 
1114
static void
 
1115
ide_source_view_movements_previous_unmatched (Movement *mv,
 
1116
                                              gunichar  target,
 
1117
                                              gunichar  opposite)
 
1118
{
 
1119
  GtkTextIter copy;
 
1120
  guint count = 1;
 
1121
 
 
1122
  g_assert (mv);
 
1123
  g_assert (target);
 
1124
  g_assert (opposite);
 
1125
 
 
1126
  copy = mv->insert;
 
1127
 
 
1128
  do
 
1129
    {
 
1130
      gunichar ch;
 
1131
 
 
1132
      if (!gtk_text_iter_backward_char (&mv->insert))
 
1133
        {
 
1134
          mv->insert = copy;
 
1135
          return;
 
1136
        }
 
1137
 
 
1138
      ch = gtk_text_iter_get_char (&mv->insert);
 
1139
 
 
1140
      if (ch == target)
 
1141
        count--;
 
1142
      else if (ch == opposite)
 
1143
        count++;
 
1144
 
 
1145
      if (!count)
 
1146
        {
 
1147
          if (!mv->exclusive)
 
1148
            gtk_text_iter_forward_char (&mv->insert);
 
1149
          return;
 
1150
        }
 
1151
    }
 
1152
  while (TRUE);
 
1153
 
 
1154
  g_assert_not_reached ();
 
1155
}
 
1156
 
 
1157
static void
 
1158
ide_source_view_movements_next_unmatched (Movement *mv,
 
1159
                                          gunichar  target,
 
1160
                                          gunichar  opposite)
 
1161
{
 
1162
  GtkTextIter copy;
 
1163
  guint count = 1;
 
1164
 
 
1165
  g_assert (mv);
 
1166
  g_assert (target);
 
1167
  g_assert (opposite);
 
1168
 
 
1169
  copy = mv->insert;
 
1170
 
 
1171
  do
 
1172
    {
 
1173
      gunichar ch;
 
1174
 
 
1175
      if (!gtk_text_iter_forward_char (&mv->insert))
 
1176
        {
 
1177
          mv->insert = copy;
 
1178
          return;
 
1179
        }
 
1180
 
 
1181
      ch = gtk_text_iter_get_char (&mv->insert);
 
1182
 
 
1183
      if (ch == target)
 
1184
        count--;
 
1185
      else if (ch == opposite)
 
1186
        count++;
 
1187
 
 
1188
      if (!count)
 
1189
        {
 
1190
          if (!mv->exclusive)
 
1191
            gtk_text_iter_forward_char (&mv->insert);
 
1192
          return;
 
1193
        }
 
1194
    }
 
1195
  while (TRUE);
 
1196
 
 
1197
  g_assert_not_reached ();
 
1198
}
 
1199
 
 
1200
static gboolean
 
1201
find_match (gunichar ch,
 
1202
            gpointer data)
 
1203
{
 
1204
  Movement *mv = data;
 
1205
 
 
1206
  return (mv->modifier == ch);
 
1207
}
 
1208
 
 
1209
static void
 
1210
ide_source_view_movements_next_match_modifier (Movement *mv)
 
1211
{
 
1212
  GtkTextIter insert;
 
1213
  GtkTextIter bounds;
 
1214
 
 
1215
  bounds = insert = mv->insert;
 
1216
  gtk_text_iter_forward_to_line_end (&bounds);
 
1217
 
 
1218
  if (gtk_text_iter_forward_find_char (&insert, find_match, mv, &bounds))
 
1219
    {
 
1220
      if (!mv->exclusive)
 
1221
        gtk_text_iter_forward_char (&insert);
 
1222
      mv->insert = insert;
 
1223
    }
 
1224
}
 
1225
 
 
1226
static void
 
1227
ide_source_view_movements_previous_match_modifier (Movement *mv)
 
1228
{
 
1229
  GtkTextIter insert;
 
1230
  GtkTextIter bounds;
 
1231
 
 
1232
  bounds = insert = mv->insert;
 
1233
  gtk_text_iter_set_line_offset (&bounds, 0);
 
1234
 
 
1235
  if (gtk_text_iter_backward_find_char (&insert, find_match, mv, &bounds))
 
1236
    {
 
1237
      if (!mv->exclusive)
 
1238
        gtk_text_iter_forward_char (&insert);
 
1239
      mv->insert = insert;
 
1240
    }
 
1241
}
 
1242
 
 
1243
void
 
1244
_ide_source_view_apply_movement (IdeSourceView         *self,
 
1245
                                 IdeSourceViewMovement  movement,
 
1246
                                 gboolean               extend_selection,
 
1247
                                 gboolean               exclusive,
 
1248
                                 guint                  count,
 
1249
                                 gunichar               modifier,
 
1250
                                 gint                  *target_offset)
 
1251
{
 
1252
  Movement mv = { 0 };
 
1253
  GtkTextBuffer *buffer;
 
1254
  GtkTextMark *insert;
 
1255
  gsize i;
 
1256
 
 
1257
  g_return_if_fail (IDE_IS_SOURCE_VIEW (self));
 
1258
 
 
1259
#ifdef IDE_ENABLE_TRACE
 
1260
  {
 
1261
    GEnumValue *enum_value;
 
1262
    GEnumClass *enum_class;
 
1263
 
 
1264
    enum_class = g_type_class_ref (IDE_TYPE_SOURCE_VIEW_MOVEMENT);
 
1265
    enum_value = g_enum_get_value (enum_class, movement);
 
1266
    IDE_TRACE_MSG ("movement(%s, extend_selection=%s, exclusive=%s, count=%u)",
 
1267
                   enum_value->value_nick,
 
1268
                   extend_selection ? "YES" : "NO",
 
1269
                   exclusive ? "YES" : "NO",
 
1270
                   count);
 
1271
    g_type_class_unref (enum_class);
 
1272
  }
 
1273
#endif
 
1274
 
 
1275
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (self));
 
1276
  insert = gtk_text_buffer_get_insert (buffer);
 
1277
 
 
1278
  mv.self = self;
 
1279
  mv.target_offset = target_offset;
 
1280
  mv.type = movement;
 
1281
  mv.extend_selection = extend_selection;
 
1282
  mv.exclusive = exclusive;
 
1283
  mv.count = count;
 
1284
  mv.ignore_select = FALSE;
 
1285
  mv.ignore_target_offset = FALSE;
 
1286
  mv.modifier = modifier;
 
1287
 
 
1288
  ide_source_view_movements_get_selection (&mv);
 
1289
 
 
1290
  switch (movement)
 
1291
    {
 
1292
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_OFFSET:
 
1293
      gtk_text_iter_backward_chars (&mv.insert, MAX (1, mv.count));
 
1294
      break;
 
1295
 
 
1296
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_OFFSET:
 
1297
      gtk_text_iter_forward_chars (&mv.insert, MAX (1, mv.count));
 
1298
      break;
 
1299
 
 
1300
    case IDE_SOURCE_VIEW_MOVEMENT_NTH_CHAR:
 
1301
      ide_source_view_movements_nth_char (&mv);
 
1302
      break;
 
1303
 
 
1304
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_CHAR:
 
1305
      ide_source_view_movements_previous_char (&mv);
 
1306
      break;
 
1307
 
 
1308
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_CHAR:
 
1309
      ide_source_view_movements_next_char (&mv);
 
1310
      break;
 
1311
 
 
1312
    case IDE_SOURCE_VIEW_MOVEMENT_FIRST_CHAR:
 
1313
      ide_source_view_movements_first_char (&mv);
 
1314
      break;
 
1315
 
 
1316
    case IDE_SOURCE_VIEW_MOVEMENT_FIRST_NONSPACE_CHAR:
 
1317
      ide_source_view_movements_first_nonspace_char (&mv);
 
1318
      break;
 
1319
 
 
1320
    case IDE_SOURCE_VIEW_MOVEMENT_MIDDLE_CHAR:
 
1321
      ide_source_view_movements_middle_char (&mv);
 
1322
      break;
 
1323
 
 
1324
    case IDE_SOURCE_VIEW_MOVEMENT_LAST_CHAR:
 
1325
      ide_source_view_movements_last_char (&mv);
 
1326
      break;
 
1327
 
 
1328
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_FULL_WORD_START:
 
1329
      for (i = MAX (1, mv.count); i > 0; i--)
 
1330
        ide_source_view_movements_previous_full_word_start (&mv);
 
1331
      break;
 
1332
 
 
1333
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_FULL_WORD_START:
 
1334
      for (i = MAX (1, mv.count); i > 0; i--)
 
1335
        ide_source_view_movements_next_full_word_start (&mv);
 
1336
      break;
 
1337
 
 
1338
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_FULL_WORD_END:
 
1339
      for (i = MAX (1, mv.count); i > 0; i--)
 
1340
        ide_source_view_movements_previous_full_word_end (&mv);
 
1341
      break;
 
1342
 
 
1343
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_FULL_WORD_END:
 
1344
      for (i = MAX (1, mv.count); i > 0; i--)
 
1345
        ide_source_view_movements_next_full_word_end (&mv);
 
1346
      break;
 
1347
 
 
1348
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_WORD_START:
 
1349
      for (i = MAX (1, mv.count); i > 0; i--)
 
1350
        ide_source_view_movements_previous_word_start (&mv);
 
1351
      break;
 
1352
 
 
1353
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_WORD_START:
 
1354
      for (i = MAX (1, mv.count); i > 0; i--)
 
1355
        ide_source_view_movements_next_word_start (&mv);
 
1356
      break;
 
1357
 
 
1358
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_WORD_END:
 
1359
      for (i = MAX (1, mv.count); i > 0; i--)
 
1360
        ide_source_view_movements_previous_word_end (&mv);
 
1361
      break;
 
1362
 
 
1363
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_WORD_END:
 
1364
      for (i = MAX (1, mv.count); i > 0; i--)
 
1365
        ide_source_view_movements_next_word_end (&mv);
 
1366
      break;
 
1367
 
 
1368
    case IDE_SOURCE_VIEW_MOVEMENT_SENTENCE_START:
 
1369
      for (i = MAX (1, mv.count); i > 0; i--)
 
1370
        ide_source_view_movements_sentence_start (&mv);
 
1371
      break;
 
1372
 
 
1373
    case IDE_SOURCE_VIEW_MOVEMENT_SENTENCE_END:
 
1374
      for (i = MAX (1, mv.count); i > 0; i--)
 
1375
        ide_source_view_movements_sentence_end (&mv);
 
1376
      break;
 
1377
 
 
1378
    case IDE_SOURCE_VIEW_MOVEMENT_PARAGRAPH_START:
 
1379
      for (i = MAX (1, mv.count); i > 0; i--)
 
1380
        {
 
1381
          mv.exclusive = exclusive && i == 1;
 
1382
          ide_source_view_movements_paragraph_start (&mv);
 
1383
        }
 
1384
      break;
 
1385
 
 
1386
    case IDE_SOURCE_VIEW_MOVEMENT_PARAGRAPH_END:
 
1387
      for (i = MAX (1, mv.count); i > 0; i--)
 
1388
        {
 
1389
          mv.exclusive = exclusive && i == 1;
 
1390
          ide_source_view_movements_paragraph_end (&mv);
 
1391
        }
 
1392
      break;
 
1393
 
 
1394
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_LINE:
 
1395
      mv.ignore_target_offset = TRUE;
 
1396
      mv.ignore_select = TRUE;
 
1397
      for (i = MAX (1, mv.count); i > 0; i--)
 
1398
        ide_source_view_movements_previous_line (&mv);
 
1399
      break;
 
1400
 
 
1401
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_LINE:
 
1402
      mv.ignore_target_offset = TRUE;
 
1403
      mv.ignore_select = TRUE;
 
1404
      for (i = MAX (1, mv.count); i > 0; i--)
 
1405
        ide_source_view_movements_next_line (&mv);
 
1406
      break;
 
1407
 
 
1408
    case IDE_SOURCE_VIEW_MOVEMENT_FIRST_LINE:
 
1409
      ide_source_view_movements_first_line (&mv);
 
1410
      break;
 
1411
 
 
1412
    case IDE_SOURCE_VIEW_MOVEMENT_NTH_LINE:
 
1413
      ide_source_view_movements_nth_line (&mv);
 
1414
      break;
 
1415
 
 
1416
    case IDE_SOURCE_VIEW_MOVEMENT_LAST_LINE:
 
1417
      ide_source_view_movements_last_line (&mv);
 
1418
      break;
 
1419
 
 
1420
    case IDE_SOURCE_VIEW_MOVEMENT_LINE_PERCENTAGE:
 
1421
      ide_source_view_movements_line_percentage (&mv);
 
1422
      break;
 
1423
 
 
1424
    case IDE_SOURCE_VIEW_MOVEMENT_LINE_CHARS:
 
1425
      ide_source_view_movements_line_chars (&mv);
 
1426
      break;
 
1427
 
 
1428
    case IDE_SOURCE_VIEW_MOVEMENT_LINE_END:
 
1429
      ide_source_view_movements_line_end (&mv);
 
1430
      break;
 
1431
 
 
1432
    case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_UP:
 
1433
    case IDE_SOURCE_VIEW_MOVEMENT_HALF_PAGE_DOWN:
 
1434
    case IDE_SOURCE_VIEW_MOVEMENT_PAGE_UP:
 
1435
    case IDE_SOURCE_VIEW_MOVEMENT_PAGE_DOWN:
 
1436
      for (i = MAX (1, mv.count); i > 0; i--)
 
1437
        ide_source_view_movements_move_page (&mv);
 
1438
      break;
 
1439
 
 
1440
    case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_DOWN:
 
1441
    case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_UP:
 
1442
      for (i = MAX (1, mv.count); i > 0; i--)
 
1443
        ide_source_view_movements_scroll (&mv);
 
1444
      break;
 
1445
 
 
1446
    case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_TOP:
 
1447
      ide_source_view_movements_screen_top (&mv);
 
1448
      break;
 
1449
 
 
1450
    case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_MIDDLE:
 
1451
      ide_source_view_movements_screen_middle (&mv);
 
1452
      break;
 
1453
 
 
1454
    case IDE_SOURCE_VIEW_MOVEMENT_SCREEN_BOTTOM:
 
1455
      ide_source_view_movements_screen_bottom (&mv);
 
1456
      break;
 
1457
 
 
1458
    case IDE_SOURCE_VIEW_MOVEMENT_MATCH_SPECIAL:
 
1459
      ide_source_view_movements_match_special (&mv);
 
1460
      break;
 
1461
 
 
1462
    case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_TOP:
 
1463
    case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_CENTER:
 
1464
    case IDE_SOURCE_VIEW_MOVEMENT_SCROLL_SCREEN_BOTTOM:
 
1465
      ide_source_view_movements_scroll_center (&mv);
 
1466
      break;
 
1467
 
 
1468
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_UNMATCHED_BRACE:
 
1469
      for (i = MAX (1, mv.count); i > 0; i--)
 
1470
        ide_source_view_movements_previous_unmatched (&mv, '{', '}');
 
1471
      break;
 
1472
 
 
1473
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_UNMATCHED_BRACE:
 
1474
      for (i = MAX (1, mv.count); i > 0; i--)
 
1475
        ide_source_view_movements_next_unmatched (&mv, '}', '{');
 
1476
      break;
 
1477
 
 
1478
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_UNMATCHED_PAREN:
 
1479
      for (i = MAX (1, mv.count); i > 0; i--)
 
1480
        ide_source_view_movements_previous_unmatched (&mv, '(', ')');
 
1481
      break;
 
1482
 
 
1483
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_UNMATCHED_PAREN:
 
1484
      for (i = MAX (1, mv.count); i > 0; i--)
 
1485
        ide_source_view_movements_next_unmatched (&mv, ')', '(');
 
1486
      break;
 
1487
 
 
1488
    case IDE_SOURCE_VIEW_MOVEMENT_NEXT_MATCH_MODIFIER:
 
1489
      for (i = MAX (1, mv.count); i > 0; i--)
 
1490
        ide_source_view_movements_next_match_modifier (&mv);
 
1491
      break;
 
1492
 
 
1493
    case IDE_SOURCE_VIEW_MOVEMENT_PREVIOUS_MATCH_MODIFIER:
 
1494
      for (i = MAX (1, mv.count); i > 0; i--)
 
1495
        ide_source_view_movements_previous_match_modifier (&mv);
 
1496
      break;
 
1497
 
 
1498
    default:
 
1499
      g_return_if_reached ();
 
1500
    }
 
1501
 
 
1502
  if (!mv.ignore_select)
 
1503
    ide_source_view_movements_select_range (&mv);
 
1504
 
 
1505
  if (!mv.ignore_target_offset)
 
1506
    *target_offset = gtk_text_iter_get_line_offset (&mv.insert);
 
1507
 
 
1508
  if (!mv.ignore_scroll_to_insert)
 
1509
    ide_source_view_scroll_mark_onscreen (self, insert, TRUE, 0.5, 0.5);
 
1510
}