This file is part of GNU Emacs.
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING. If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
#include "buffer.h"
#include "character.h"
#include "category.h"
+#include "composite.h"
#include "indent.h"
#include "keyboard.h"
#include "frame.h"
#include "termopts.h"
#include "disptab.h"
#include "intervals.h"
+#include "dispextern.h"
#include "region-cache.h"
/* Indentation can insert tabs if this is non-zero;
return pos;
}
\f
-/* If a composition starts at POS/POS_BYTE and it doesn't stride over
- POINT, set *LEN / *LEN_BYTE to the character and byte lengths, *WIDTH
- to the width, and return 1. Otherwise, return 0. */
-
-static int
-check_composition (pos, pos_byte, point, len, len_byte, width)
- int pos, pos_byte, point;
- int *len, *len_byte, *width;
-{
- Lisp_Object prop;
- EMACS_INT start, end;
- int id;
-
- if (! find_composition (pos, -1, &start, &end, &prop, Qnil)
- || pos != start || point < end
- || !COMPOSITION_VALID_P (start, end, prop))
- return 0;
- if ((id = get_composition_id (pos, pos_byte, end - pos, prop, Qnil)) < 0)
- return 0;
-
- *len = COMPOSITION_LENGTH (prop);
- *len_byte = CHAR_TO_BYTE (end) - pos_byte;
- *width = composition_table[id]->width;
- return 1;
-}
-\f
/* Set variables WIDTH and BYTES for a multibyte sequence starting at P.
DP is a display table or NULL.
return col;
}
\f
+extern Lisp_Object Qspace, QCwidth, QCalign_to;
+
+/* Check the presence of a display property and compute its width.
+ If a property was found and its width was found as well, return
+ its width (>= 0) and set the position of the end of the property
+ in ENDPOS.
+ Otherwise just return -1. */
+static int
+check_display_width (EMACS_INT pos, EMACS_INT col, EMACS_INT *endpos)
+{
+ Lisp_Object val, overlay;
+
+ if (CONSP (val = get_char_property_and_overlay
+ (make_number (pos), Qdisplay, Qnil, &overlay))
+ && EQ (Qspace, XCAR (val)))
+ { /* FIXME: Use calc_pixel_width_or_height, as in term.c. */
+ Lisp_Object plist = XCDR (val), prop;
+ int width = -1;
+
+ if ((prop = Fplist_get (plist, QCwidth), NATNUMP (prop)))
+ width = XINT (prop);
+ else if (FLOATP (prop))
+ width = (int)(XFLOAT_DATA (prop) + 0.5);
+ else if ((prop = Fplist_get (plist, QCalign_to), NATNUMP (prop)))
+ width = XINT (prop) - col;
+ else if (FLOATP (prop))
+ width = (int)(XFLOAT_DATA (prop) + 0.5) - col;
+
+ if (width >= 0)
+ {
+ EMACS_INT start;
+ if (OVERLAYP (overlay))
+ *endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
+ else
+ get_property_and_range (pos, Qdisplay, &val, &start, endpos, Qnil);
+ return width;
+ }
+ }
+ return -1;
+}
+
/* Scanning from the beginning of the current line, stop at the buffer
position ENDPOS or at the column GOALCOL or at the end of line, whichever
comes first.
register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
register struct Lisp_Char_Table *dp = buffer_display_table ();
int multibyte = !NILP (current_buffer->enable_multibyte_characters);
+ struct composition_it cmp_it;
/* Start the scan at the beginning of this line with column number 0. */
register EMACS_INT col = 0, prev_col = 0;
}
if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
+ bzero (&cmp_it, sizeof cmp_it);
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil);
/* Scan forward to the target position. */
while (scan < end)
break;
prev_col = col;
- /* Check composition sequence. */
- {
- int len, len_byte, width;
-
- if (check_composition (scan, scan_byte, end,
- &len, &len_byte, &width))
+ { /* Check display property. */
+ EMACS_INT end;
+ int width = check_display_width (scan, col, &end);
+ if (width >= 0)
{
- scan += len;
- scan_byte += len_byte;
- if (scan <= end)
- col += width;
- continue;
+ col += width;
+ if (end > scan) /* Avoid infinite loops with 0-width overlays. */
+ {
+ scan = end; scan_byte = charpos_to_bytepos (scan);
+ continue;
+ }
}
}
+ /* Check composition sequence. */
+ if (cmp_it.id >= 0
+ || (scan == cmp_it.stop_pos
+ && composition_reseat_it (&cmp_it, scan, scan_byte, end,
+ XWINDOW (selected_window), NULL, Qnil)))
+ composition_update_it (&cmp_it, scan, scan_byte, Qnil);
+ if (cmp_it.id >= 0)
+ {
+ scan += cmp_it.nchars;
+ scan_byte += cmp_it.nbytes;
+ if (scan <= end)
+ col += cmp_it.width;
+ if (cmp_it.to == cmp_it.nglyphs)
+ {
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, scan, scan_byte, end,
+ Qnil);
+ }
+ else
+ cmp_it.from = cmp_it.to;
+ continue;
+ }
+
c = FETCH_BYTE (scan_byte);
/* See if there is a display table and it relates
and scan through it again. */
if (!NILP (force) && col > goal)
{
+ int c;
EMACS_INT pos_byte = PT_BYTE;
- DEC_POS (pos_byte);
- int c = FETCH_CHAR (pos_byte);
+ DEC_POS (pos_byte);
+ c = FETCH_CHAR (pos_byte);
if (c == '\t' && prev_col < goal)
{
EMACS_INT goal_pt, goal_pt_byte;
EMACS_INT prev_tab_offset; /* Previous tab offset. */
EMACS_INT continuation_glyph_width;
+ struct composition_it cmp_it;
+
XSETBUFFER (buffer, current_buffer);
XSETWINDOW (window, win);
pos_byte = prev_pos_byte = CHAR_TO_BYTE (from);
contin_hpos = 0;
prev_tab_offset = tab_offset;
+ bzero (&cmp_it, sizeof cmp_it);
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, Qnil);
+
while (1)
{
while (pos == next_boundary)
if (hpos > width)
{
- if (hscroll
- || (truncate_partial_width_windows
- && ((width + continuation_glyph_width)
- < FRAME_COLS (XFRAME (WINDOW_FRAME (win)))))
+ int total_width = width + continuation_glyph_width;
+ int truncate = 0;
+
+ if (!NILP (Vtruncate_partial_width_windows)
+ && (total_width < FRAME_COLS (XFRAME (WINDOW_FRAME (win)))))
+ {
+ if (INTEGERP (Vtruncate_partial_width_windows))
+ truncate
+ = total_width < XFASTINT (Vtruncate_partial_width_windows);
+ else
+ truncate = 1;
+ }
+
+ if (hscroll || truncate
|| !NILP (current_buffer->truncate_lines))
{
/* Truncating: skip to newline, unless we are already past
EMACS_INT i, n;
Lisp_Object charvec;
- c = FETCH_BYTE (pos_byte);
-
/* Check composition sequence. */
- {
- int len, len_byte, width;
-
- if (check_composition (pos, pos_byte, to, &len, &len_byte, &width))
- {
- pos += len;
- pos_byte += len_byte;
- hpos += width;
- continue;
- }
- }
+ if (cmp_it.id >= 0
+ || (pos == cmp_it.stop_pos
+ && composition_reseat_it (&cmp_it, pos, pos_byte, to, win,
+ NULL, Qnil)))
+ composition_update_it (&cmp_it, pos, pos_byte, Qnil);
+ if (cmp_it.id >= 0)
+ {
+ pos += cmp_it.nchars;
+ pos_byte += cmp_it.nbytes;
+ hpos += cmp_it.width;
+ if (cmp_it.to == cmp_it.nglyphs)
+ {
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, pos, pos_byte, to,
+ Qnil);
+ }
+ else
+ cmp_it.from = cmp_it.to;
+ continue;
+ }
+ c = FETCH_BYTE (pos_byte);
pos++, pos_byte++;
/* Perhaps add some info to the width_run_cache. */
parameters such as width, horizontal scrolling, and so on.
The default is to use the selected window's parameters.
+LINES can optionally take the form (COLS . LINES), in which case
+the motion will not stop at the start of a screen line but on
+its column COLS (if such exists on that line, that is).
+
`vertical-motion' always uses the current buffer,
regardless of which buffer is displayed in WINDOW.
This is consistent with other cursor motion functions
struct window *w;
Lisp_Object old_buffer;
struct gcpro gcpro1;
+ Lisp_Object lcols = Qnil;
+ double cols;
+
+ /* Allow LINES to be of the form (HPOS . VPOS) aka (COLUMNS . LINES). */
+ if (CONSP (lines) && (NUMBERP (XCAR (lines))))
+ {
+ lcols = XCAR (lines);
+ cols = INTEGERP (lcols) ? (double) XINT (lcols) : XFLOAT_DATA (lcols);
+ lines = XCDR (lines);
+ }
CHECK_NUMBER (lines);
if (! NILP (window))
}
else
{
- int it_start;
- int oselective;
- int it_overshoot_expected;
+ int it_start, oselective, first_x, it_overshoot_expected;
SET_TEXT_POS (pt, PT, PT_BYTE);
start_display (&it, w, pt);
-
- /* Scan from the start of the line containing PT. If we don't
- do this, we start moving with IT->current_x == 0, while PT is
- really at some x > 0. The effect is, in continuation lines, that
- we end up with the iterator placed at where it thinks X is 0,
- while the end position is really at some X > 0, the same X that
- PT had. */
+ first_x = it.first_visible_x;
it_start = IT_CHARPOS (it);
- /* We expect the call to move_it_to, further down, to overshoot
- if the starting point is on an image, stretch glyph,
- composition, or Lisp string. We won't need to backtrack in
- this situation, except for one corner case: when the Lisp
- string contains a newline. */
- if (it.method == GET_FROM_STRING)
+ /* See comments below for why we calculate this. */
+ if (XINT (lines) > 0)
{
- const char *s = SDATA (it.string);
- const char *e = s + SBYTES (it.string);
-
- while (s < e && *s != '\n')
- ++s;
-
- /* If there is no newline in the string, we need to check
- whether there is a newline immediately after the string
- in move_it_to below. This may happen if there is an
- overlay with an after-string just before the newline. */
- it_overshoot_expected = (s == e) ? -1 : 0;
+ if (it.cmp_it.id >= 0)
+ it_overshoot_expected = 1;
+ else if (it.method == GET_FROM_STRING)
+ {
+ const char *s = SDATA (it.string);
+ const char *e = s + SBYTES (it.string);
+ while (s < e && *s != '\n')
+ ++s;
+ it_overshoot_expected = (s == e) ? -1 : 0;
+ }
+ else
+ it_overshoot_expected = (it.method == GET_FROM_IMAGE
+ || it.method == GET_FROM_STRETCH);
}
- else
- it_overshoot_expected = (it.method == GET_FROM_IMAGE
- || it.method == GET_FROM_STRETCH
- || it.method == GET_FROM_COMPOSITION);
+ /* Scan from the start of the line containing PT. If we don't
+ do this, we start moving with IT->current_x == 0, while PT is
+ really at some x > 0. */
reseat_at_previous_visible_line_start (&it);
it.current_x = it.hpos = 0;
/* Temporarily disable selective display so we don't move too far */
move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
it.selective = oselective;
- /* Move back if we got too far. This may happen if
- truncate-lines is on and PT is beyond right margin.
- Don't go back if the overshoot is expected (see above). */
- if (IT_CHARPOS (it) > it_start && XINT (lines) > 0
- && (!it_overshoot_expected
- || (it_overshoot_expected < 0
- && it.method == GET_FROM_BUFFER
- && it.c == '\n')))
- move_it_by_lines (&it, -1, 0);
-
- it.vpos = 0;
- /* Do this even if LINES is 0, so that we move back
- to the beginning of the current line as we ought. */
- if (XINT (lines) >= 0 || IT_CHARPOS (it) > 0)
- move_it_by_lines (&it, XINT (lines), 0);
+ if (XINT (lines) <= 0)
+ {
+ it.vpos = 0;
+ /* Do this even if LINES is 0, so that we move back to the
+ beginning of the current line as we ought. */
+ if (XINT (lines) == 0 || IT_CHARPOS (it) > 0)
+ move_it_by_lines (&it, XINT (lines), 0);
+ }
+ else
+ {
+ if (IT_CHARPOS (it) > PT)
+ {
+ /* IT may move too far if truncate-lines is on and PT
+ lies beyond the right margin. In that case,
+ backtrack unless the starting point is on an image,
+ stretch glyph, composition, or Lisp string. */
+ if (!it_overshoot_expected
+ /* Also, backtrack if the Lisp string contains no
+ newline, but there is a newline right after it.
+ In this case, IT overshoots if there is an
+ after-string just before the newline. */
+ || (it_overshoot_expected < 0
+ && it.method == GET_FROM_BUFFER
+ && it.c == '\n'))
+ move_it_by_lines (&it, -1, 0);
+ it.vpos = 0;
+ move_it_by_lines (&it, XINT (lines), 0);
+ }
+ else
+ {
+ /* Otherwise, we are at the first row occupied by PT,
+ which might span multiple screen lines (e.g., if it's
+ on a multi-line display string). We want to start
+ from the last line that it occupies. */
+ if (PT < ZV)
+ {
+ while (IT_CHARPOS (it) <= PT)
+ {
+ it.vpos = 0;
+ move_it_by_lines (&it, 1, 0);
+ }
+ if (XINT (lines) > 1)
+ move_it_by_lines (&it, XINT (lines) - 1, 0);
+ }
+ else
+ {
+ it.vpos = 0;
+ move_it_by_lines (&it, XINT (lines), 0);
+ }
+ }
+ }
+
+ /* Move to the goal column, if one was specified. */
+ if (!NILP (lcols))
+ {
+ /* If the window was originally hscrolled, move forward by
+ the hscrolled amount first. */
+ if (first_x > 0)
+ {
+ move_it_in_display_line (&it, ZV, first_x, MOVE_TO_X);
+ it.current_x = 0;
+ }
+ move_it_in_display_line
+ (&it, ZV,
+ (int)(cols * FRAME_COLUMN_WIDTH (XFRAME (w->frame)) + 0.5),
+ MOVE_TO_X);
+ }
SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
}