move.c 18.6 KB
Newer Older
Chris Allegretta's avatar
Chris Allegretta committed
1
/* $Id$ */
Chris Allegretta's avatar
Chris Allegretta committed
2
3
4
/**************************************************************************
 *   move.c                                                               *
 *                                                                        *
5
 *   Copyright (C) 1999-2004 Chris Allegretta                             *
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
6
 *   Copyright (C) 2005-2006 David Lawrence Ramsey                        *
Chris Allegretta's avatar
Chris Allegretta committed
7
8
 *   This program is free software; you can redistribute it and/or modify *
 *   it under the terms of the GNU General Public License as published by *
9
 *   the Free Software Foundation; either version 2, or (at your option)  *
Chris Allegretta's avatar
Chris Allegretta committed
10
11
 *   any later version.                                                   *
 *                                                                        *
12
13
14
15
 *   This program is distributed in the hope that it will be useful, but  *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
 *   General Public License for more details.                             *
Chris Allegretta's avatar
Chris Allegretta committed
16
17
18
 *                                                                        *
 *   You should have received a copy of the GNU General Public License    *
 *   along with this program; if not, write to the Free Software          *
19
20
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
 *   02110-1301, USA.                                                     *
Chris Allegretta's avatar
Chris Allegretta committed
21
22
23
 *                                                                        *
 **************************************************************************/

24
#include "proto.h"
25

Chris Allegretta's avatar
Chris Allegretta committed
26
#include <string.h>
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
27
#include <ctype.h>
Chris Allegretta's avatar
Chris Allegretta committed
28

29
/* Move to the first line of the file. */
30
void do_first_line(void)
31
{
32
    const filestruct *current_save = openfile->current;
33
    size_t pww_save = openfile->placewewant;
34

35
36
    openfile->current = openfile->fileage;
    openfile->current_x = 0;
37
38
    openfile->placewewant = 0;

39
    edit_redraw(current_save, pww_save);
40
41
}

42
/* Move to the last line of the file. */
43
void do_last_line(void)
44
{
45
    const filestruct *current_save = openfile->current;
46
    size_t pww_save = openfile->placewewant;
47

48
    openfile->current = openfile->filebot;
49
50
    openfile->current_x = strlen(openfile->filebot->data);
    openfile->placewewant = xplustabs();
51
    openfile->current_y = editwinrows - 1;
52

53
    edit_redraw(current_save, pww_save);
54
55
}

56
/* Move up one page. */
57
void do_page_up(void)
58
{
59
60
    int i;

61
62
63
    /* If there's less than a page of text left on the screen, put the
     * cursor at the beginning of the first line of the file, and then
     * update the edit window. */
64
    if (openfile->current->lineno <= editwinrows - 2) {
65
	do_first_line();
66
67
	return;
    }
68

69
70
    /* If we're not in smooth scrolling mode, put the cursor at the
     * beginning of the top line of the edit window, as Pico does. */
71
#ifndef NANO_TINY
72
    if (!ISSET(SMOOTH_SCROLL)) {
73
#endif
74
75
	openfile->current = openfile->edittop;
	openfile->placewewant = 0;
76
#ifndef NANO_TINY
77
    }
78
#endif
79

80
81
    for (i = editwinrows - 2; i > 0 && openfile->current !=
	openfile->fileage; i--)
82
	openfile->current = openfile->current->prev;
83

84
85
    openfile->current_x = actual_x(openfile->current->data,
	openfile->placewewant);
86

87
88
    /* Scroll the edit window up a page. */
    edit_scroll(UP, editwinrows - 2);
89
90
}

91
/* Move down one page. */
92
void do_page_down(void)
93
{
94
95
    int i;

96
97
98
99
    /* If there's less than a page of text left on the screen, put the
     * cursor at the beginning of the last line of the file, and then
     * update the edit window. */
    if (openfile->current->lineno + editwinrows - 2 >=
100
	openfile->filebot->lineno) {
101
	do_last_line();
102
103
	return;
    }
104

105
106
    /* If we're not in smooth scrolling mode, put the cursor at the
     * beginning of the top line of the edit window, as Pico does. */
107
#ifndef NANO_TINY
108
    if (!ISSET(SMOOTH_SCROLL)) {
109
#endif
110
111
	openfile->current = openfile->edittop;
	openfile->placewewant = 0;
112
#ifndef NANO_TINY
113
    }
114
#endif
115

116
117
    for (i = editwinrows - 2; i > 0 && openfile->current !=
	openfile->filebot; i--)
118
	openfile->current = openfile->current->next;
119

120
121
    openfile->current_x = actual_x(openfile->current->data,
	openfile->placewewant);
122

123
124
    /* Scroll the edit window down a page. */
    edit_scroll(DOWN, editwinrows - 2);
125
126
}

127
#ifndef DISABLE_JUSTIFY
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
128
/* Move up to the beginning of the last beginning-of-paragraph line
129
130
 * before the current line.  If allow_update is TRUE, update the screen
 * afterwards. */
131
132
133
134
135
void do_para_begin(bool allow_update)
{
    const filestruct *current_save = openfile->current;
    const size_t pww_save = openfile->placewewant;

136
    if (openfile->current != openfile->fileage) {
137
138
139
140
141
142
	do {
	    openfile->current = openfile->current->prev;
	    openfile->current_y--;
	} while (!begpar(openfile->current));
    }

143
144
145
    openfile->current_x = 0;
    openfile->placewewant = 0;

146
147
148
149
    if (allow_update)
	edit_redraw(current_save, pww_save);
}

150
151
/* Move up to the beginning of the last beginning-of-paragraph line
 * before the current line, and update the screen afterwards. */
152
153
154
155
156
void do_para_begin_void(void)
{
    do_para_begin(TRUE);
}

David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
157
158
/* Move down to the beginning of the last line of the current paragraph.
 * Then move down one line farther if there is such a line, or to the
159
160
161
162
 * end of the current line if not.  If allow_update is TRUE, update the
 * screen afterwards.  A line is the last line of a paragraph if it is
 * in a paragraph, and the next line either is the beginning line of a
 * paragraph or isn't in a paragraph. */
163
164
165
166
167
void do_para_end(bool allow_update)
{
    const filestruct *const current_save = openfile->current;
    const size_t pww_save = openfile->placewewant;

168
169
    while (openfile->current != openfile->filebot &&
	!inpar(openfile->current))
170
171
	openfile->current = openfile->current->next;

172
    while (openfile->current != openfile->filebot &&
173
174
175
176
177
178
	inpar(openfile->current->next) &&
	!begpar(openfile->current->next)) {
	openfile->current = openfile->current->next;
	openfile->current_y++;
    }

179
    if (openfile->current != openfile->filebot) {
180
	openfile->current = openfile->current->next;
181
182
183
184
185
186
	openfile->current_x = 0;
	openfile->placewewant = 0;
    } else {
	openfile->current_x = strlen(openfile->current->data);
	openfile->placewewant = xplustabs();
    }
187
188
189
190
191

    if (allow_update)
	edit_redraw(current_save, pww_save);
}

192
193
194
/* Move down to the beginning of the last line of the current paragraph.
 * Then move down one line farther if there is such a line, or to the
 * end of the current line if not, and update the screen afterwards. */
195
196
197
198
199
200
void do_para_end_void(void)
{
    do_para_end(TRUE);
}
#endif /* !DISABLE_JUSTIFY */

201
#ifndef NANO_TINY
202
203
204
205
/* Move to the next word in the file.  If allow_punct is TRUE, treat
 * punctuation as part of a word.  If allow_update is TRUE, update the
 * screen afterwards.  Return TRUE if we started on a word, and FALSE
 * otherwise. */
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
bool do_next_word(bool allow_punct, bool allow_update)
{
    size_t pww_save = openfile->placewewant;
    const filestruct *current_save = openfile->current;
    char *char_mb;
    int char_mb_len;
    bool end_line = FALSE, started_on_word = FALSE;

    assert(openfile->current != NULL && openfile->current->data != NULL);

    char_mb = charalloc(mb_cur_max());

    /* Move forward until we find the character after the last letter of
     * the current word. */
    while (!end_line) {
	char_mb_len = parse_mbchar(openfile->current->data +
222
		openfile->current_x, char_mb, NULL);
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

	/* If we've found it, stop moving forward through the current
	 * line. */
	if (!is_word_mbchar(char_mb, allow_punct))
	    break;

	/* If we haven't found it, then we've started on a word, so set
	 * started_on_word to TRUE. */
	started_on_word = TRUE;

	if (openfile->current->data[openfile->current_x] == '\0')
	    end_line = TRUE;
	else
	    openfile->current_x += char_mb_len;
    }

    /* Move forward until we find the first letter of the next word. */
    if (openfile->current->data[openfile->current_x] == '\0')
	end_line = TRUE;
    else
	openfile->current_x += char_mb_len;

    for (; openfile->current != NULL;
	openfile->current = openfile->current->next) {
	while (!end_line) {
	    char_mb_len = parse_mbchar(openfile->current->data +
249
		openfile->current_x, char_mb, NULL);
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266

	    /* If we've found it, stop moving forward through the
	     * current line. */
	    if (is_word_mbchar(char_mb, allow_punct))
		break;

	    if (openfile->current->data[openfile->current_x] == '\0')
		end_line = TRUE;
	    else
		openfile->current_x += char_mb_len;
	}

	/* If we've found it, stop moving forward to the beginnings of
	 * subsequent lines. */
	if (!end_line)
	    break;

267
	if (openfile->current != openfile->filebot) {
268
269
270
271
272
273
274
275
276
	    end_line = FALSE;
	    openfile->current_x = 0;
	}
    }

    free(char_mb);

    /* If we haven't found it, leave the cursor at the end of the
     * file. */
277
    if (openfile->current == NULL)
278
279
280
281
282
283
284
285
286
287
288
289
	openfile->current = openfile->filebot;

    openfile->placewewant = xplustabs();

    /* If allow_update is TRUE, update the screen. */
    if (allow_update)
	edit_redraw(current_save, pww_save);

    /* Return whether we started on a word. */
    return started_on_word;
}

290
291
292
/* Move to the next word in the file, treating punctuation as part of a
 * word if the WORD_BOUNDS flag is set, and update the screen
 * afterwards. */
293
294
void do_next_word_void(void)
{
295
    do_next_word(ISSET(WORD_BOUNDS), TRUE);
296
297
}

298
299
300
301
/* Move to the previous word in the file.  If allow_punct is TRUE, treat
 * punctuation as part of a word.  If allow_update is TRUE, update the
 * screen afterwards.  Return TRUE if we started on a word, and FALSE
 * otherwise. */
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
bool do_prev_word(bool allow_punct, bool allow_update)
{
    size_t pww_save = openfile->placewewant;
    const filestruct *current_save = openfile->current;
    char *char_mb;
    int char_mb_len;
    bool begin_line = FALSE, started_on_word = FALSE;

    assert(openfile->current != NULL && openfile->current->data != NULL);

    char_mb = charalloc(mb_cur_max());

    /* Move backward until we find the character before the first letter
     * of the current word. */
    while (!begin_line) {
	char_mb_len = parse_mbchar(openfile->current->data +
318
		openfile->current_x, char_mb, NULL);
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347

	/* If we've found it, stop moving backward through the current
	 * line. */
	if (!is_word_mbchar(char_mb, allow_punct))
	    break;

	/* If we haven't found it, then we've started on a word, so set
	 * started_on_word to TRUE. */
	started_on_word = TRUE;

	if (openfile->current_x == 0)
	    begin_line = TRUE;
	else
	    openfile->current_x = move_mbleft(openfile->current->data,
		openfile->current_x);
    }

    /* Move backward until we find the last letter of the previous
     * word. */
    if (openfile->current_x == 0)
	begin_line = TRUE;
    else
	openfile->current_x = move_mbleft(openfile->current->data,
		openfile->current_x);

    for (; openfile->current != NULL;
	openfile->current = openfile->current->prev) {
	while (!begin_line) {
	    char_mb_len = parse_mbchar(openfile->current->data +
348
		openfile->current_x, char_mb, NULL);
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367

	    /* If we've found it, stop moving backward through the
	     * current line. */
	    if (is_word_mbchar(char_mb, allow_punct))
		break;

	    if (openfile->current_x == 0)
		begin_line = TRUE;
	    else
		openfile->current_x =
			move_mbleft(openfile->current->data,
			openfile->current_x);
	}

	/* If we've found it, stop moving backward to the ends of
	 * previous lines. */
	if (!begin_line)
	    break;

368
	if (openfile->current != openfile->fileage) {
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
	    begin_line = FALSE;
	    openfile->current_x = strlen(openfile->current->prev->data);
	}
    }

    /* If we haven't found it, leave the cursor at the beginning of the
     * file. */
    if (openfile->current == NULL)
	openfile->current = openfile->fileage;
    /* If we've found it, move backward until we find the character
     * before the first letter of the previous word. */
    else if (!begin_line) {
	if (openfile->current_x == 0)
	    begin_line = TRUE;
	else
	    openfile->current_x = move_mbleft(openfile->current->data,
		openfile->current_x);

	while (!begin_line) {
388
389
	    char_mb_len = parse_mbchar(openfile->current->data +
		openfile->current_x, char_mb, NULL);
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421

	    /* If we've found it, stop moving backward through the
	     * current line. */
	    if (!is_word_mbchar(char_mb, allow_punct))
		break;

	    if (openfile->current_x == 0)
		begin_line = TRUE;
	    else
		openfile->current_x =
			move_mbleft(openfile->current->data,
			openfile->current_x);
	}

	/* If we've found it, move forward to the first letter of the
	 * previous word. */
	if (!begin_line)
	    openfile->current_x += char_mb_len;
    }

    free(char_mb);

    openfile->placewewant = xplustabs();

    /* If allow_update is TRUE, update the screen. */
    if (allow_update)
	edit_redraw(current_save, pww_save);

    /* Return whether we started on a word. */
    return started_on_word;
}

422
423
424
/* Move to the previous word in the file, treating punctuation as part
 * of a word if the WORD_BOUNDS flag is set, and update the screen
 * afterwards. */
425
426
void do_prev_word_void(void)
{
427
    do_prev_word(ISSET(WORD_BOUNDS), TRUE);
428
}
429
#endif /* !NANO_TINY */
430

431
432
/* Move to the beginning of the current line.  If the SMART_HOME flag is
 * set, move to the first non-whitespace character of the current line
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
433
 * if we aren't already there, or to the beginning of the current line
434
 * if we are. */
435
436
437
438
void do_home(void)
{
    size_t pww_save = openfile->placewewant;

439
#ifndef NANO_TINY
440
441
442
443
444
445
446
447
448
449
450
451
452
453
    if (ISSET(SMART_HOME)) {
	size_t current_x_save = openfile->current_x;

	openfile->current_x = indent_length(openfile->current->data);

	if (openfile->current_x == current_x_save ||
		openfile->current->data[openfile->current_x] == '\0')
	    openfile->current_x = 0;

	openfile->placewewant = xplustabs();
    } else {
#endif
	openfile->current_x = 0;
	openfile->placewewant = 0;
454
#ifndef NANO_TINY
455
456
457
458
459
460
461
    }
#endif

    if (need_horizontal_update(pww_save))
	update_line(openfile->current, openfile->current_x);
}

462
/* Move to the end of the current line. */
463
464
465
466
467
468
469
470
471
472
473
void do_end(void)
{
    size_t pww_save = openfile->placewewant;

    openfile->current_x = strlen(openfile->current->data);
    openfile->placewewant = xplustabs();

    if (need_horizontal_update(pww_save))
	update_line(openfile->current, openfile->current_x);
}

474
475
476
477
478
479
480
481
482
/* If scroll_only is FALSE, move up one line.  If scroll_only is TRUE,
 * scroll up one line without scrolling the cursor. */
void do_up(
#ifndef NANO_TINY
	bool scroll_only
#else
	void
#endif
	)
Chris Allegretta's avatar
Chris Allegretta committed
483
{
484
485
486
487
488
489
490
    /* If we're at the top of the file, or if scroll_only is TRUE and
     * the top of the file is onscreen, get out. */
    if (openfile->current == openfile->fileage
#ifndef NANO_TINY
	|| (scroll_only && openfile->edittop == openfile->fileage)
#endif
	)
491
	return;
492

493
    assert(openfile->current_y == openfile->current->lineno - openfile->edittop->lineno);
494

495
    /* Move the current line of the edit window up. */
496
497
498
    openfile->current = openfile->current->prev;
    openfile->current_x = actual_x(openfile->current->data,
	openfile->placewewant);
499

500
501
502
503
504
505
506
507
508
509
    /* If scroll_only is FALSE and if we're on the first line of the
     * edit window, scroll the edit window up one line if we're in
     * smooth scrolling mode, or up half a page if we're not.  If
     * scroll_only is TRUE, scroll the edit window up one line
     * unconditionally. */
    if (openfile->current_y == 0
#ifndef NANO_TINY
	|| scroll_only
#endif
	)
510
	edit_scroll(UP,
511
#ifndef NANO_TINY
512
		(ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 :
513
#endif
514
		editwinrows / 2);
515
516

    /* If we're below the first line of the edit window, update the
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
517
518
519
     * line we were on before and the line we're on now.  The former
     * needs to be redrawn if we're not on the first page, and the
     * latter needs to be drawn unconditionally. */
520
    if (openfile->current_y > 0) {
521
522
523
524
	if (need_vertical_update(0))
	    update_line(openfile->current->next, 0);
	update_line(openfile->current, openfile->current_x);
    }
Chris Allegretta's avatar
Chris Allegretta committed
525
}
Chris Allegretta's avatar
Chris Allegretta committed
526

527
528
529
530
531
532
533
534
535
536
/* Move up one line. */
void do_up_void(void)
{
    do_up(
#ifndef NANO_TINY
	FALSE
#endif
	);
}

537
#ifndef NANO_TINY
538
/* Scroll up one line without scrolling the cursor. */
539
540
void do_scroll_up(void)
{
541
    do_up(TRUE);
542
}
543
#endif
544

545
546
547
548
549
550
551
552
553
/* If scroll_only is FALSE, move down one line.  If scroll_only is TRUE,
 * scroll down one line without scrolling the cursor. */
void do_down(
#ifndef NANO_TINY
	bool scroll_only
#else
	void
#endif
	)
554
{
555
    /* If we're at the bottom of the file, get out. */
556
    if (openfile->current == openfile->filebot)
557
	return;
Chris Allegretta's avatar
Chris Allegretta committed
558

559
    assert(openfile->current_y == openfile->current->lineno - openfile->edittop->lineno);
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
560

561
    /* Move the current line of the edit window down. */
562
563
564
    openfile->current = openfile->current->next;
    openfile->current_x = actual_x(openfile->current->data,
	openfile->placewewant);
Chris Allegretta's avatar
Chris Allegretta committed
565

566
567
568
569
570
571
572
573
574
575
    /* If scroll_only is FALSE and if we're on the first line of the
     * edit window, scroll the edit window down one line if we're in
     * smooth scrolling mode, or down half a page if we're not.  If
     * scroll_only is TRUE, scroll the edit window down one line
     * unconditionally. */
    if (openfile->current_y == editwinrows - 1
#ifndef NANO_TINY
	|| scroll_only
#endif
	)
576
	edit_scroll(DOWN,
577
#ifndef NANO_TINY
578
		(ISSET(SMOOTH_SCROLL) || scroll_only) ? 1 :
579
#endif
580
		editwinrows / 2);
581
582

    /* If we're above the last line of the edit window, update the line
David Lawrence Ramsey's avatar
David Lawrence Ramsey committed
583
584
585
     * we were on before and the line we're on now.  The former needs to
     * be redrawn if we're not on the first page, and the latter needs
     * to be drawn unconditionally. */
586
    if (openfile->current_y < editwinrows - 1) {
587
588
589
590
	if (need_vertical_update(0))
	    update_line(openfile->current->prev, 0);
	update_line(openfile->current, openfile->current_x);
    }
Chris Allegretta's avatar
Chris Allegretta committed
591
}
592

593
594
595
596
597
598
599
600
601
602
/* Move down one line. */
void do_down_void(void)
{
    do_down(
#ifndef NANO_TINY
	FALSE
#endif
	);
}

603
#ifndef NANO_TINY
604
/* Scroll down one line without scrolling the cursor. */
605
606
void do_scroll_down(void)
{
607
    do_down(TRUE);
608
}
609
#endif
Chris Allegretta's avatar
Chris Allegretta committed
610

611
/* Move left one character. */
612
void do_left(void)
Chris Allegretta's avatar
Chris Allegretta committed
613
{
614
615
616
617
618
619
    size_t pww_save = openfile->placewewant;

    if (openfile->current_x > 0)
	openfile->current_x = move_mbleft(openfile->current->data,
		openfile->current_x);
    else if (openfile->current != openfile->fileage) {
620
	do_up_void();
621
	openfile->current_x = strlen(openfile->current->data);
Chris Allegretta's avatar
Chris Allegretta committed
622
    }
623

624
    openfile->placewewant = xplustabs();
625

626
    if (need_horizontal_update(pww_save))
627
	update_line(openfile->current, openfile->current_x);
Chris Allegretta's avatar
Chris Allegretta committed
628
629
}

630
/* Move right one character. */
631
void do_right(void)
Chris Allegretta's avatar
Chris Allegretta committed
632
{
633
    size_t pww_save = openfile->placewewant;
634

635
    assert(openfile->current_x <= strlen(openfile->current->data));
636

637
638
639
    if (openfile->current->data[openfile->current_x] != '\0')
	openfile->current_x = move_mbright(openfile->current->data,
		openfile->current_x);
640
    else if (openfile->current != openfile->filebot) {
641
	do_down_void();
642
	openfile->current_x = 0;
Chris Allegretta's avatar
Chris Allegretta committed
643
    }
644

645
    openfile->placewewant = xplustabs();
646

647
    if (need_horizontal_update(pww_save))
648
	update_line(openfile->current, openfile->current_x);
Chris Allegretta's avatar
Chris Allegretta committed
649
}