Mercurial > hg > xemacs-beta
diff src/indent.c @ 288:e11d67e05968 r21-0b42
Import from CVS: tag r21-0b42
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:35:54 +0200 |
parents | 6330739388db |
children | 70ad99077275 |
line wrap: on
line diff
--- a/src/indent.c Mon Aug 13 10:35:07 2007 +0200 +++ b/src/indent.c Mon Aug 13 10:35:54 2007 +0200 @@ -649,8 +649,7 @@ return vmotion_1 (w, orig, vtarget, ret_vpos, NULL); } -/* Helper for Fvertical_motion and Fvertical_motion_pixels. - * Share as much code as possible so these stay synched. +/* Helper for Fvertical_motion. */ static Lisp_Object vertical_motion_1 (Lisp_Object lines, Lisp_Object window, @@ -692,7 +691,7 @@ return make_int (value); } -DEFUN ("vertical-motion", Fvertical_motion, 1, 2, 0, /* +DEFUN ("vertical-motion", Fvertical_motion, 1, 3, 0, /* Move to start of frame line LINES lines down. If LINES is negative, this is moving up. Optional second argument is WINDOW to move in, @@ -700,33 +699,171 @@ Sets point to position found; this may be start of line or just the start of a continuation line. -Returns number of lines moved; may be closer to zero than LINES -if beginning or end of buffer was reached. +If optional third argument PIXELS is nil, returns number +of lines moved; may be closer to zero than LINES if beginning +or end of buffer was reached. If PIXELS is non-nil, the +vertical pixel height of the motion which took place is +returned instead of the actual number of lines moved. A +motion of zero lines returns the height of the current line. Note that `vertical-motion' sets WINDOW's buffer's point, not WINDOW's point. (This differs from FSF Emacs, which buggily always sets current buffer's point, regardless of WINDOW.) */ - (lines, window)) + (lines, window, pixels)) { - return vertical_motion_1 (lines, window, /* pixels = */ 0); + return vertical_motion_1 (lines, window, !NILP (pixels)); } -DEFUN ("vertical-motion-pixels", Fvertical_motion_pixels, 1, 2, 0, /* -Move to start of frame line LINES lines down. -If LINES is negative, this is moving up. +/* + * Like vmotion() but requested and returned movement is in pixels. + * HOW specifies the stopping condition. Positive means move at least + * PIXELS. Negative means at most. Zero means as close as possible. + */ +Bufpos +vmotion_pixels (Lisp_Object window, Bufpos start, int pixels, int how, + int *motion) +{ + struct window *w; + Bufpos eobuf, bobuf; + int defheight; + int needed; + int line, next; + int remain, abspix, dirn; + int elt, nelt; + int i; + line_start_cache_dynarr *cache; + int previous = -1; + int lines; + + if (NILP (window)) + window = Fselected_window (Qnil); + + CHECK_WINDOW (window); + w = XWINDOW (window); + + eobuf = BUF_ZV (XBUFFER (w->buffer)); + bobuf = BUF_BEGV (XBUFFER (w->buffer)); + + default_face_height_and_width (window, &defheight, NULL); + + /* guess num lines needed in line start cache + a few extra */ + abspix = abs (pixels); + needed = (abspix + defheight-1)/defheight + 3; + + dirn = (pixels >= 0) ? 1 : -1; + + while (1) + { + elt = point_in_line_start_cache (w, start, needed); + assert (elt >= 0); /* in the cache */ + + cache = w->line_start_cache; + nelt = Dynarr_length (cache); + + *motion = 0; + + if (pixels == 0) + /* No vertical motion requested so we just return the position + of the beginning of the current display line. */ + return Dynarr_atp (cache, elt)->start; + + if ((dirn < 0 && elt == 0 && + Dynarr_atp (cache, elt)->start <= bobuf) || + (dirn > 0 && elt == nelt-1 && + Dynarr_atp (cache, elt)->end >= eobuf)) + return Dynarr_atp (cache, elt)->start; + + remain = abspix; + for (i = elt; (dirn > 0) ? (i < nelt) : (i > 0); i += dirn) + { + /* cache line we're considering moving over */ + int ii = (dirn > 0) ? i : i-1; + + if (remain < 0) + return Dynarr_atp (cache, i)->start; + + line = Dynarr_atp (cache, ii)->height; + next = remain - line; + + /* is stopping condition satisfied? */ + if ((how > 0 && remain <= 0) || /* at least */ + (how < 0 && next < 0) || /* at most */ + (how == 0 && remain <= abs (next))) /* closest */ + return Dynarr_atp (cache, i)->start; + + /* moving down and nowhere left to go? */ + if (dirn > 0 && Dynarr_atp (cache, ii)->end >= eobuf) + return Dynarr_atp (cache, ii)->start; + + /* take the step */ + remain = next; + *motion += dirn * line; + + /* moving up and nowhere left to go? */ + if (dirn < 0 && Dynarr_atp (cache, ii)->start <= bobuf) + return Dynarr_atp (cache, ii)->start; + } + + /* get here => need more cache lines. try again. */ + assert (abs (*motion) > previous); /* progress? */ + previous = abs (*motion); + + lines = (pixels < 0) ? elt : (nelt - elt); + needed += (remain*lines + abspix-1)/abspix + 3; + } + + RETURN_NOT_REACHED(0) /* shut up compiler */ +} + +DEFUN ("vertical-motion-pixels", Fvertical_motion_pixels, 1, 3, 0, /* +Move to start of frame line PIXELS vertical pixels down. +If PIXELS is negative, this is moving up. +The actual vertical motion in pixels is returned. + Optional second argument is WINDOW to move in, the default is the selected window. -This function is identical in behavior to `vertical-motion' -except that the vertical pixel height of the motion which -took place is returned instead of the actual number of lines -moved. A motion of zero lines returns the height of the -current line. +Optional third argument HOW specifies when to stop. A value +less than zero indicates that the motion should be no more +than PIXELS. A value greater than zero indicates that the +motion should be at least PIXELS. Any other value indicates +that the motion should be as close as possible to PIXELS. */ - (lines, window)) + (pixels, window, how)) { - return vertical_motion_1 (lines, window, /* pixels = */ 1); + Bufpos bufpos; + Bufpos orig; + int selected; + int motion; + int howto; + struct window *w; + + if (NILP (window)) + window = Fselected_window (Qnil); + + CHECK_WINDOW (window); + CHECK_INT (pixels); + + selected = (EQ (window, Fselected_window (Qnil))); + + w = XWINDOW (window); + + orig = selected ? BUF_PT (XBUFFER (w->buffer)) + : marker_position (w->pointm[CURRENT_DISP]); + + howto = INTP (how) ? XINT (how) : 0; + + bufpos = vmotion_pixels (window, orig, XINT (pixels), howto, &motion); + + if (selected) + BUF_SET_PT (XBUFFER (w->buffer), bufpos); + else + set_marker_restricted (w->pointm[CURRENT_DISP], + make_int(bufpos), + w->buffer); + + return make_int (motion); }