bug-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#13399: 24.3.50; Word-wrap can't wrap at zero-width space U-200B


From: martin rudalics
Subject: bug#13399: 24.3.50; Word-wrap can't wrap at zero-width space U-200B
Date: Mon, 04 Feb 2013 18:04:14 +0100

> But we are bike-shedding.

Indeed.  How about the attached patch?

martin
=== modified file 'lisp/window.el'
--- lisp/window.el      2013-01-02 16:13:04 +0000
+++ lisp/window.el      2013-02-04 10:17:41 +0000
@@ -6074,211 +6074,428 @@
                             (eobp)
                             window))))
 
-;;; Resizing buffers to fit their contents exactly.
+;;; Resizing windows and frames to fit their contents exactly.
+(defcustom fit-window-to-buffer-horizontally nil
+  "Non-nil means `fit-window-to-buffer' can resize windows horizontally.
+If this is nil, `fit-window-to-buffer' never resizes windows
+horizontally.  If this is `only', it can resize windows
+horizontally only.  Any other value means `fit-window-to-buffer'
+can resize windows in both dimensions."
+  :type 'boolean
+  :version "24.4"
+  :group 'help)
+
 (defcustom fit-frame-to-buffer nil
-  "Non-nil means `fit-window-to-buffer' can resize frames.
+  "Non-nil means `fit-frame-to-buffer' can resize frames.
 A frame can be resized if and only if its root window is a live
-window.  The height of the root window is subject to the values
-of `fit-frame-to-buffer-max-height' and `window-min-height'."
+window.  If this is `horizontally', frames can be resized
+horizontally only.  If this is `vertically', frames can be
+resized vertically only.  Any other non-nil value means frames
+can be resized in both dimensions.  See also
+`fit-frame-to-buffer-margins' and `fit-frame-to-buffer-sizes'."
   :type 'boolean
-  :version "24.3"
+  :version "24.4"
   :group 'help)
 
-(defcustom fit-frame-to-buffer-bottom-margin 4
-  "Bottom margin for the command `fit-frame-to-buffer'.
-This is the number of lines that function leaves free at the bottom of
-the display, in order to not obscure any system task bar or panel.
-If you do not have one (or if it is vertical) you might want to
-reduce this.  If it is thicker, you might want to increase this."
-  ;; If you set this too small, fit-frame-to-buffer can shift the
-  ;; frame up to avoid the panel.
-  :type 'integer
-  :version "24.3"
-  :group 'windows)
-
-(defun fit-frame-to-buffer (&optional frame max-height min-height)
+(defcustom fit-frame-to-buffer-margins '(0 0 0 72)
+  "Margins around frame for `fit-frame-to-buffer'.
+This list specifies the numbers of pixels to be left free on the
+left, above, the right, and below a frame that shall be fit to
+its buffer.  The value specified here can be overridden for a
+specific frame by that frame's `fit-frame-to-buffer-margins'
+parameter, if present.
+
+On some window systems the calculation of frame sizes can be
+incorrect.  Increasing the value of the third and/or fourth
+element of this variable can fix that.
+
+See also `fit-frame-to-buffer-sizes'."
+  :version "24.4"
+  :type '(list
+         (integer :tag "Left" :size 5)
+         (integer :tag " Above" :size 5)
+         (integer :tag " Right" :size 5)
+         (integer :tag " Below" :size 5))
+  :group 'windows)
+
+(defcustom fit-frame-to-buffer-sizes '(nil nil nil nil)
+  "Size boundaries of frame for `fit-frame-to-buffer'.
+This list specifies the total maximum and minimum lines and
+maximum and minimum columns of the root window of any frame that
+shall be fit to its buffer.  If any of these values is non-nil,
+it will override the value supplied by the respective arguments
+of `fit-frame-to-buffer'.
+
+On window systems where the menubar can wrap, fitting a frame to
+its buffer may swallow the last line(s).  Specifying an
+appropriate minimum width value here can avoid such wrapping.
+
+See also `fit-frame-to-buffer-margins'."
+  :version "24.4"
+  :type '(list
+         (choice
+          :tag "Maximum Height"
+          :value nil
+          :format "%[MaxHeight%] %v  "
+          (const :tag "None" :format "%t" nil)
+          (integer :tag "Lines" :size 5))
+         (choice
+          :tag "Minimum Height"
+          :value nil
+          :format "%[MinHeight%] %v  "
+          (const :tag "None" :format "%t" nil)
+          (integer :tag "Lines" :size 5))
+         (choice
+          :tag "Maximum Width"
+          :value nil
+          :format "%[MaxWidth%] %v  "
+          (const :tag "None" :format "%t" nil)
+          (integer :tag "Columns" :size 5))
+         (choice
+          :tag "Minimum Width"
+          :value nil
+          :format "%[MinWidth%] %v\n"
+          (const :tag "None" :format "%t" nil)
+          (integer :tag "Columns" :size 5)))
+  :group 'windows)
+
+(defun window--sanitize-margin (margin left right)
+  "Return MARGIN if it's a number between LEFT and RIGHT."
+  (if (and (numberp margin)
+          (<= left (- right margin)) (<= margin right))
+      margin
+    0))
+
+(defun fit-frame-to-buffer (&optional frame max-height min-height max-width 
min-width)
   "Adjust height of FRAME to display its buffer contents exactly.
 FRAME can be any live frame and defaults to the selected one.
+MAX-HEIGHT, MIN-HEIGHT, MAX-WIDTH and MIN-WIDTH specify bounds on
+the new total size of FRAME's root window.
 
-Optional argument MAX-HEIGHT specifies the maximum height of FRAME.
-It defaults to the height of the display below the current
-top line of FRAME, minus `fit-frame-to-buffer-bottom-margin'.
-Optional argument MIN-HEIGHT specifies the minimum height of FRAME.
-The default corresponds to `window-min-height'."
+The option `fit-frame-to-buffer' controls whether this function
+has any effect.  New position and size of FRAME are additionally
+determined by the options `fit-frame-to-buffer-sizes' and
+`fit-frame-to-buffer-margins' or the corresponding parameters of
+FRAME.  This function can fail to fit the buffer's height when
+`word-wrap' is turned on in that buffer."
   (interactive)
   (setq frame (window-normalize-frame frame))
-  (let* ((root (frame-root-window frame))
-        (frame-min-height
-         (+ (- (frame-height frame) (window-total-size root))
-            window-min-height))
-        (frame-top (frame-parameter frame 'top))
-        (top (if (consp frame-top)
-                 (funcall (car frame-top) (cadr frame-top))
-               frame-top))
-        (frame-max-height
-         (- (/ (- (x-display-pixel-height frame) top)
-               (frame-char-height frame))
-            fit-frame-to-buffer-bottom-margin))
-        (compensate 0)
-        delta)
-    (when (and (window-live-p root) (not (window-size-fixed-p root)))
-      (with-selected-window root
-       (cond
-        ((not max-height)
-         (setq max-height frame-max-height))
-        ((numberp max-height)
-         (setq max-height (min max-height frame-max-height)))
-        (t
-         (error "%s is an invalid maximum height" max-height)))
-       (cond
-        ((not min-height)
-         (setq min-height frame-min-height))
-        ((numberp min-height)
-         (setq min-height (min min-height frame-min-height)))
-        (t
-         (error "%s is an invalid minimum height" min-height)))
-       ;; When tool-bar-mode is enabled and we have just created a new
-       ;; frame, reserve lines for toolbar resizing.  This is needed
-       ;; because for reasons unknown to me Emacs (1) reserves one line
-       ;; for the toolbar when making the initial frame and toolbars
-       ;; are enabled, and (2) later adds the remaining lines needed.
-       ;; Our code runs IN BETWEEN (1) and (2).  YMMV when you're on a
-       ;; system that behaves differently.
-       (let ((quit-restore (window-parameter root 'quit-restore))
-             (lines (tool-bar-lines-needed frame)))
-         (when (and quit-restore (eq (car quit-restore) 'frame)
-                    (not (zerop lines)))
-           (setq compensate (1- lines))))
-       (message "%s" compensate)
-       (setq delta
-             ;; Always count a final newline - we don't do any
-             ;; post-processing, so let's play safe.
-             (+ (count-screen-lines nil nil t)
-                (- (window-body-size))
-                compensate)))
-      ;; Move away from final newline.
-      (when (and (eobp) (bolp) (not (bobp)))
-       (set-window-point root (line-beginning-position 0)))
-      (set-window-start root (point-min))
-      (set-window-vscroll root 0)
-      (condition-case nil
-         (set-frame-height
-          frame
-          (min (max (+ (frame-height frame) delta)
-                    min-height)
-               max-height))
-       (error (setq delta nil))))
-    delta))
+  (when (and (window-live-p (frame-root-window frame))
+            fit-frame-to-buffer
+            (or (not window-size-fixed)
+                (and (eq window-size-fixed 'height)
+                     (not (eq fit-frame-to-buffer 'vertically)))
+                (and (eq window-size-fixed 'width)
+                     (not (eq fit-frame-to-buffer 'horizontally)))))
+    (with-selected-window (frame-root-window frame)
+      (let* ((window (frame-root-window frame))
+            (char-width (frame-char-width))
+            (char-height (frame-char-height))
+            (display-width (display-pixel-width frame))
+            (display-height (display-pixel-height frame))
+            ;; Sanitize margins.
+            (margins (or (frame-parameter frame 'fit-frame-to-buffer-margins)
+                         fit-frame-to-buffer-margins))
+            (left-margin (window--sanitize-margin
+                          (nth 0 margins) 0 display-width))
+            (top-margin (window--sanitize-margin
+                         (nth 1 margins) 0 display-height))
+            (right-margin (window--sanitize-margin
+                           (nth 2 margins) left-margin display-width))
+            (bottom-margin (window--sanitize-margin
+                            (nth 3 margins) top-margin display-height))
+            ;; The pixel width of FRAME.
+            (frame-width (frame-pixel-width))
+            ;; The difference between FRAME's pixel and parameter
+            ;; widths.
+            (frame-extra-width
+             (- frame-width (* (frame-width) char-width)))
+            ;; The pixel height of FRAME's window.
+            (window-body-width (* (window-body-width) char-width))
+            ;; The difference in pixels between total and body width of
+            ;; FRAME's window.
+            (window-extra-width
+             (- (* (window-total-width) char-width) window-body-width))
+            ;; The difference in pixels between the frame's pixel width
+            ;; and the window's body width.
+            (extra-width
+             (* char-width (- (frame-width) (window-body-width))))
+            ;; The maximum width we can use for fitting.
+            (fit-width
+             (- display-width (- frame-width window-body-width)
+                left-margin right-margin))
+            ;; The pixel position of FRAME's left border.  We usually
+            ;; try to leave this alone.
+            (left
+             (let ((left (frame-parameter nil 'left)))
+               (if (consp left)
+                   (funcall (car left) (cadr left))
+                 left)))
+            ;; The pixel height of FRAME.
+            (frame-height (frame-pixel-height))
+            ;; The difference between FRAME's pixel and parameter
+            ;; heights.
+            (frame-extra-height
+             (- frame-height (* (frame-height) char-height)))
+            ;; When tool-bar-mode is enabled and we just created a new
+            ;; frame, reserve lines for toolbar resizing.  Needed
+            ;; because for reasons unknown to me Emacs (1) reserves one
+            ;; line for the toolbar when making the initial frame and
+            ;; toolbars are enabled, and (2) later adds the remaining
+            ;; lines needed.  Our code runs IN BETWEEN (1) and (2).
+            ;; YMMV when you're on a system that behaves differently.
+            (toolbar-extra-height
+             (let ((quit-restore (window-parameter window 'quit-restore))
+                   (lines (tool-bar-lines-needed frame)))
+               (* char-height
+                  (if (and quit-restore (eq (car quit-restore) 'frame)
+                           (not (zerop lines)))
+                      (1- lines)
+                    0))))
+            ;; The pixel height of FRAME's window.
+            (window-body-height (* (window-body-height) char-height))
+            ;; The difference in pixels between total and body height
+            ;; of FRAME's window.
+            (window-extra-height
+             (- (* (window-total-height) char-height) window-body-height))
+            ;; The difference in pixels between the frame's pixel
+            ;; height and the window's body height.
+            (extra-height
+             (* (- (frame-height) (window-body-height)) char-height))
+            ;; The maximum height we can use for fitting.
+            (fit-height
+             (- display-height (- frame-height window-body-height)
+                top-margin bottom-margin toolbar-extra-height))
+            ;; The pixel position of FRAME's top border.  We usually
+            ;; try to leave this alone.
+            (top
+             (let ((top (frame-parameter nil 'top)))
+               (if (consp top)
+                   (funcall (car top) (cadr top))
+                 top)))
+            ;; Sanitize minimum and maximum sizes.
+            (sizes (or (frame-parameter frame 'fit-frame-to-buffer-sizes)
+                       fit-frame-to-buffer-sizes))
+            (max-height
+             (cond
+              ((numberp (nth 0 sizes))
+               (- (* (nth 0 sizes) char-height) window-extra-height))
+              ((numberp max-height)
+               (- (* max-height char-height) window-extra-height))))
+            (min-height
+             (cond
+              ((numberp (nth 1 sizes))
+               (- (* (nth 1 sizes) char-height) window-extra-height))
+              ((numberp min-height)
+               (- (* min-height char-height) window-extra-height))))
+            (max-width
+             (cond
+              ((numberp (nth 2 sizes))
+               (- (* (nth 2 sizes) char-width) window-extra-width))
+              ((numberp max-width)
+               (- (* max-width char-width) window-extra-width))))
+            (min-width
+             (cond
+              ((numberp (nth 3 sizes))
+               (- (* (nth 3 sizes) char-width) window-extra-width))
+              ((numberp min-width)
+               (- (* min-width char-width) window-extra-width))))
+            (value (window-text-pixel-size
+                    nil (point-min) (point-max)
+                    display-width display-height))
+            (width (car value))
+            (height (cdr value))
+            remainder)
+       ;; Round sizes (hopefully we can drop these as soon as we can
+       ;; resize pixelwise).  First add pixels to obtain full last
+       ;; lines and columns.
+       (setq remainder (% width char-width))
+       (unless (zerop remainder)
+         (setq width (+ width (- char-width remainder))))
+       (setq remainder (% height char-height))
+       (setq height (+ height (- char-height remainder)))
+       ;; Now make sure that we don't get larger than our rounded
+       ;; maximum lines and columns.
+       (when (> width fit-width)
+         (setq width (- fit-width (% fit-width char-width))))
+       (when (> height fit-height)
+         (setq height (- fit-height (% fit-height char-height))))
+       ;; Don't change height or width when the window's size is fixed
+       ;; in either direction.
+       (cond
+        ((eq window-size-fixed 'height)
+         (setq height nil))
+        ((eq window-size-fixed 'width)
+         (setq height nil)))
+       (when width
+         ;; Fit to maximum and minimum widths.
+         (when max-width
+           (setq width (min width max-width)))
+         (when min-width
+           (setq width (max width min-width)))
+         ;; Add extra width.
+         (setq width (+ width extra-width))
+         ;; Preserve right margin.
+         (let ((right (+ left width frame-extra-width))
+               (max-right (- display-width right-margin)))
+           (cond
+            ((> right max-right)
+             ;; Move FRAME to left.
+             (setq left (max 0 (- left (- right max-right)))))
+            ((< left left-margin)
+             ;; Move frame to right.
+             (setq left left-margin)))))
+       (when height
+         ;; Fit to maximum and minimum heights.
+         (when max-height
+           (setq height (min height max-height)))
+         (when min-height
+           (setq height (max height min-height)))
+         ;; Add extra height.
+         (setq height (+ height extra-height))
+         ;; Preserve bottom and top margins.
+         (let ((bottom (+ top height frame-extra-height))
+               (max-bottom (- display-height bottom-margin)))
+           (cond
+            ((> bottom max-bottom)
+             ;; Move FRAME to left.
+             (setq top (max 0 (- top (- bottom max-bottom)))))
+            ((< top top-margin)
+             ;; Move frame down.
+             (setq top top-margin)))))
+       ;; Apply changes.
+       (set-frame-position frame left top)
+       (set-frame-size
+        frame
+        (if width (/ width char-width) (frame-width))
+        (if height (/ height char-height) (frame-height)))))))
 
-(defun fit-window-to-buffer (&optional window max-height min-height)
-  "Adjust height of WINDOW to display its buffer's contents exactly.
+(defun fit-window-to-buffer (&optional window max-height min-height max-width 
min-width)
+  "Adjust size of WINDOW to display its buffer's contents exactly.
 WINDOW must be a live window and defaults to the selected one.
 
-Optional argument MAX-HEIGHT specifies the maximum height of
-WINDOW and defaults to the height of WINDOW's frame.  Optional
-argument MIN-HEIGHT specifies the minimum height of WINDOW and
-defaults to `window-min-height'.  Both MAX-HEIGHT and MIN-HEIGHT
-are specified in lines and include the mode line and header line,
-if any.
-
-If WINDOW is a full height window, then if the option
-`fit-frame-to-buffer' is non-nil, this calls the function
-`fit-frame-to-buffer' to adjust the frame height.
-
-Return the number of lines by which WINDOW was enlarged or
-shrunk.  If an error occurs during resizing, return nil but don't
-signal an error.
+If WINDOW is part of a vertical combination, adjust WINDOW's
+height.  The new height is calculated from the number of lines of
+the accessible portion of its buffer.  The optional argument
+MAX-HEIGHT specifies a maximum height and defaults to the height
+of WINDOW's frame.  The optional argument MIN-HEIGHT specifies a
+minimum height and defaults to `window-min-height'.  Both
+MAX-HEIGHT and MIN-HEIGHT are specified in lines and include the
+mode line and header line, if any.
+
+If WINDOW is part of a horizontal combination and the value of
+the option `fit-window-to-buffer-horizontally' is non-nil, adjust
+WINDOW's height.  The new width of WINDOW is calculated from the
+maximum length of its buffer's lines that follow the current
+start position of WINDOW.  The optional argument MAX-WIDTH
+specifies a maximum width and defaults to the width of WINDOW's
+frame.  The optional argument MIN-WIDTH specifies a minimum width
+and defaults to `window-min-width'.  Both MAX-WIDTH and MIN-WIDTH
+are specified in columns and include fringes, margins and
+scrollbars, if any.
+
+If WINDOW is its frame's root window, then if the option
+`fit-frame-to-buffer' is non-nil, call `fit-frame-to-buffer' to
+adjust the frame's size.
 
 Note that even if this function makes WINDOW large enough to show
-_all_ lines of its buffer you might not see the first lines when
-WINDOW was scrolled."
+_all_ parts of its buffer you might not see the first part when
+WINDOW was scrolled.  If WINDOW is resized horizontally, you will
+not see the top of its buffer unless WINDOW starts at its minimum
+accessible position."
   (interactive)
   (setq window (window-normalize-window window t))
-  (cond
-   ((window-size-fixed-p window))
-   ((window-full-height-p window)
-    (when fit-frame-to-buffer
-      (fit-frame-to-buffer (window-frame window))))
-   (t
+  (if (eq window (frame-root-window window))
+      (when fit-frame-to-buffer
+       ;; Fit WINDOW's frame to buffer.
+       (fit-frame-to-buffer
+        (window-frame window) max-height min-height max-width min-width))
     (with-selected-window window
-      (let* ((height (window-total-size))
+      (let* ((frame (window-frame))
+            (char-height (frame-char-height))
+            (char-width (frame-char-width))
+            (display-height (display-pixel-height))
+            (total-height (window-total-height))
+            (body-height (window-body-height))
+            (body-width (window-body-width))
             (min-height
-             ;; Adjust MIN-HEIGHT.
+             ;; Sanitize MIN-HEIGHT.
              (if (numberp min-height)
                  ;; Can't get smaller than `window-safe-min-height'.
                  (max min-height window-safe-min-height)
                ;; Preserve header and mode line if present.
                (window-min-size nil nil t)))
             (max-height
-             ;; Adjust MAX-HEIGHT.
+             ;; Sanitize MAX-HEIGHT.
              (if (numberp max-height)
-                 ;; Can't get larger than height of frame.
-                 (min max-height
-                      (window-total-size (frame-root-window window)))
-               ;; Don't delete other windows.
-               (+ height (window-max-delta nil nil window))))
-            ;; Make `desired-height' the height necessary to show
-            ;; all of WINDOW's buffer, constrained by MIN-HEIGHT
-            ;; and MAX-HEIGHT.
-            (desired-height
-             (max
-              (min
-               (+ (count-screen-lines)
-                  ;; For non-minibuffers count the mode line, if any.
-                  (if (and (not (window-minibuffer-p window))
-                           mode-line-format)
-                      1
-                    0)
-                  ;; Count the header line, if any.
-                  (if header-line-format 1 0))
-               max-height)
-              min-height))
-            (desired-delta
-             (- desired-height (window-total-size window)))
-            (delta
-             (if (> desired-delta 0)
-                 (min desired-delta
-                      (window-max-delta window nil window))
-               (max desired-delta
-                    (- (window-min-delta window nil window))))))
-       (condition-case nil
-           (if (zerop delta)
-               ;; Return zero if DELTA became zero in the process.
-               0
-             ;; Don't try to redisplay with the cursor at the end on its
-             ;; own line--that would force a scroll and spoil things.
-             (when (and (eobp) (bolp) (not (bobp)))
-               ;; It's silly to put `point' at the end of the previous
-               ;; line and so maybe force horizontal scrolling.
-               (set-window-point window (line-beginning-position 0)))
-             ;; Call `window-resize' with OVERRIDE argument equal WINDOW.
-             (window-resize window delta nil window)
-             ;; Check if the last line is surely fully visible.  If
-             ;; not, enlarge the window.
-             (let ((end (save-excursion
-                          (goto-char (point-max))
-                          (when (and (bolp) (not (bobp)))
-                            ;; Don't include final newline.
-                            (backward-char 1))
-                          (when truncate-lines
-                            ;; If line-wrapping is turned off, test the
-                            ;; beginning of the last line for
-                            ;; visibility instead of the end, as the
-                            ;; end of the line could be invisible by
-                            ;; virtue of extending past the edge of the
-                            ;; window.
-                            (forward-line 0))
-                          (point))))
-               (set-window-vscroll window 0)
-               ;; This loop might in some rare pathological cases raise
-               ;; an error - another reason for the `condition-case'.
-               (while (and (< desired-height max-height)
-                           (= desired-height (window-total-size))
-                           (not (pos-visible-in-window-p end)))
-                 (window-resize window 1 nil window)
-                 (setq desired-height (1+ desired-height)))))
-         (error (setq delta nil)))
-       delta)))))
+                 (min (+ total-height (window-max-delta)) max-height)
+               (+ total-height (window-max-delta))))
+            height)
+       (cond
+        ;; If WINDOW is vertically combined, try to resize it
+        ;; vertically.
+        ((and (not (eq fit-window-to-buffer-horizontally 'only))
+              (not (window-size-fixed-p window))
+              (window-combined-p))
+         ;; Vertically we always want to fit the entire buffer.
+         ;; WINDOW'S height can't get larger than its frame's pixel
+         ;; height.  Its width remains fixed.
+         (setq height (cdr (window-text-pixel-size
+                            nil (point-min) (point-max)
+                            (* body-width char-width)
+                            (frame-pixel-height))))
+         ;; Round height.
+         (setq height (+ (/ height char-height)
+                         (if (zerop (% height char-height)) 0 1)))
+         (unless (= height body-height)
+           (window-resize-no-error
+            window
+            (- (max min-height
+                    (min max-height
+                         (+ total-height (- height body-height))))
+               total-height)
+            nil window)))
+        ;; If WINDOW is horizontally combined, try to resize it
+        ;; horizontally.
+        ((and fit-window-to-buffer-horizontally
+              (not (window-size-fixed-p window t))
+              (window-combined-p nil t))
+         (let* ((display-width (display-pixel-width))
+                (total-width (window-total-width))
+                (min-width
+                 ;; Sanitize MIN-WIDTH.
+                 (if (numberp min-width)
+                     ;; Can't get smaller than `window-safe-min-width'.
+                     (max min-width window-safe-min-width)
+                   ;; Preserve fringes, margines, scrollbars if present.
+                   (window-min-size nil nil t)))
+                (max-width
+                 ;; Sanitize MAX-WIDTH.
+                 (if (numberp max-width)
+                     (min (+ total-width (window-max-delta nil t)) max-width)
+                   (+ total-width (window-max-delta nil t))))
+                ;; When fitting vertically, assume that WINDOW's start
+                ;; position remains unaltered.  WINDOW can't get wider
+                ;; than its frame's pixel width, its height remains
+                ;; unaltered.
+                (width (car (window-text-pixel-size
+                             nil (window-start) (point-max)
+                             (frame-pixel-width)
+                             ;; Add one char-height to assure that
+                             ;; we're on the safe side.  This
+                             ;; overshoots when the first line below
+                             ;; the bottom is wider than the window.
+                             (* body-height char-height)))))
+           (setq width (+ (/ width char-width)
+                          (if (zerop (% width char-width)) 0 1)))
+           (unless (= width body-width)
+             (window-resize-no-error
+              window
+              (- (max min-width
+                      (min max-width
+                           (+ total-width (- width body-width))))
+                 total-width)
+              t window)))))))))
 
 (defun window-safely-shrinkable-p (&optional window)
   "Return t if WINDOW can be shrunk without shrinking other windows.

=== modified file 'src/dispextern.h'
--- src/dispextern.h    2013-01-02 16:13:04 +0000
+++ src/dispextern.h    2013-02-04 10:23:56 +0000
@@ -3054,7 +3054,7 @@
 void init_iterator_to_row_start (struct it *, struct window *,
                                  struct glyph_row *);
 void start_display (struct it *, struct window *, struct text_pos);
-void move_it_to (struct it *, ptrdiff_t, int, int, int, int);
+int move_it_to (struct it *, ptrdiff_t, int, int, int, int);
 void move_it_vertically (struct it *, int);
 void move_it_vertically_backward (struct it *, int);
 void move_it_by_lines (struct it *, ptrdiff_t);

=== modified file 'src/xdisp.c'
--- src/xdisp.c 2013-01-05 21:18:01 +0000
+++ src/xdisp.c 2013-02-04 13:43:35 +0000
@@ -8734,13 +8734,17 @@
 
    If TO_CHARPOS is in invisible text, e.g. a truncated part of a
    screen line, this function will set IT to the next position that is
-   displayed to the right of TO_CHARPOS on the screen.  */
-
-void
+   displayed to the right of TO_CHARPOS on the screen.
+
+   Return the maximum pixel length of any line scanned but never more
+   than it.last_visible_x.  */
+
+int
 move_it_to (struct it *it, ptrdiff_t to_charpos, int to_x, int to_y, int 
to_vpos, int op)
 {
   enum move_it_result skip, skip2 = MOVE_X_REACHED;
   int line_height, line_start_x = 0, reached = 0;
+  int max_current_x = 0;
   void *backup_data = NULL;
 
   for (;;)
@@ -8814,7 +8818,10 @@
 
          /* If TO_CHARPOS is reached or ZV, we don't have to do more.  */
          if (skip == MOVE_POS_MATCH_OR_ZV)
-           reached = 5;
+           {
+             max_current_x = max (it->current_x, max_current_x);
+             reached = 5;
+           }
          else if (skip == MOVE_X_REACHED)
            {
              /* If TO_X was reached, we want to know whether TO_Y is
@@ -8871,6 +8878,9 @@
              if (to_y >= it->current_y
                  && to_y < it->current_y + line_height)
                {
+                 if (to_y > it->current_y)
+                   max_current_x = max (it->current_x, max_current_x);
+
                  /* When word-wrap is on, TO_X may lie past the end
                     of a wrapped line.  Then it->current is the
                     character on the next line, so backtrack to the
@@ -8883,6 +8893,7 @@
                      skip = move_it_in_display_line_to
                        (it, -1, prev_x, MOVE_TO_X);
                    }
+
                  reached = 6;
                }
            }
@@ -8908,15 +8919,18 @@
       switch (skip)
        {
        case MOVE_POS_MATCH_OR_ZV:
+         max_current_x = max (it->current_x, max_current_x);
          reached = 8;
          goto out;
 
        case MOVE_NEWLINE_OR_CR:
+         max_current_x = max (it->current_x, max_current_x);
          set_iterator_to_next (it, 1);
          it->continuation_lines_width = 0;
          break;
 
        case MOVE_LINE_TRUNCATED:
+         max_current_x = it->last_visible_x;
          it->continuation_lines_width = 0;
          reseat_at_next_visible_line_start (it, 0);
          if ((op & MOVE_TO_POS) != 0
@@ -8928,6 +8942,7 @@
          break;
 
        case MOVE_LINE_CONTINUED:
+         max_current_x = it->last_visible_x;
          /* For continued lines ending in a tab, some of the glyphs
             associated with the tab are displayed on the current
             line.  Since it->current_x does not include these glyphs,
@@ -8997,6 +9012,8 @@
     bidi_unshelve_cache (backup_data, 1);
 
   TRACE_MOVE ((stderr, "move_it_to: reached %d\n", reached));
+
+  return max_current_x;
 }
 
 
@@ -9326,6 +9343,95 @@
          && it->dpvec + it->current.dpvec_index != it->dpend);
 }
 
+DEFUN ("window-text-pixel-size", Fwindow_text_pixel_size, 
Swindow_text_pixel_size, 0, 5, 0,
+       doc: /* Return the size of the text of WINDOW's buffer in pixels.
+WINDOW must be a live window and defaults to the selected one.  The
+return value is a cons of the maximum pixel-width of any text line and
+the maximum pixel-height of all text lines.
+
+The optional argument FROM, if non-nil, specifies the first text
+position and defaults to the minimum accessible position of the buffer.
+TO, if non-nil, specifies the last text position and defaults to the
+maximum accessible position of the buffer.
+
+The optional argument X_LIMIT, if non-nil, specifies the maximum text
+width that can be returned.  X_LIMIT nil or omitted, means to use the
+pixel-width of WINDOW's body; use this if you do not intend to change
+the width of WINDOW.  Use the maximum width WINDOW may assume if you
+intend to change WINDOW's width.
+
+The optional argument Y_LIMIT, if non-nil, specifies the maximum text
+height that can be returned.  Text lines whose y-coordinate is beyond
+Y_LIMIT are ignored.  Since calculating the text height of a large
+buffer can take some time, it makes sense to specify this argument if
+the size of the buffer is unknown.  */)
+  (Lisp_Object window, Lisp_Object from, Lisp_Object to, Lisp_Object x_limit, 
Lisp_Object y_limit)
+{
+  struct window *w = decode_live_window (window);
+  Lisp_Object buf;
+  struct buffer *b;
+  struct it it;
+  struct buffer *old_buffer = NULL;
+  ptrdiff_t start, end;
+  struct text_pos startp, endp;
+  void *itdata = NULL;
+  int max_y = -1, x, y;
+
+  buf = w->buffer;
+  CHECK_BUFFER (buf);
+  b = XBUFFER (buf);
+
+  if (NILP (from))
+    start = BEGV;
+  else
+    {
+      CHECK_NUMBER_COERCE_MARKER (from);
+      start = min (max (XINT (from), BEGV), ZV);
+    }
+
+  if (NILP (to))
+    end = ZV;
+  else
+    {
+      CHECK_NUMBER_COERCE_MARKER (to);
+      end = max (start, min (XINT (to), ZV));
+    }
+
+  if (b != current_buffer)
+    {
+      old_buffer = current_buffer;
+      set_buffer_internal (b);
+    }
+
+  if (!NILP (y_limit))
+    {
+      CHECK_NUMBER (y_limit);
+      max_y = XINT (y_limit);
+    }
+
+  itdata = bidi_shelve_cache ();
+  SET_TEXT_POS (startp, start, CHAR_TO_BYTE (start));
+  start_display (&it, w, startp);
+
+  if (!NILP (x_limit))
+    {
+      CHECK_NUMBER (x_limit);
+      it.last_visible_x = XINT (x_limit);
+    }
+
+  /* Actually, we never want move_it_to stop at to_x.  But to make sure
+     that move_it_in_display_line_to always moves far enough, we set it
+     to MOST_POSITIVE_FIXNUM and specify MOVE_TO_X.  */
+  x = move_it_to (&it, end, MOST_POSITIVE_FIXNUM, max_y, -1,
+                 MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y);
+  y = it.current_y;
+  bidi_unshelve_cache (itdata, 0);
+
+  if (old_buffer)
+    set_buffer_internal (old_buffer);
+
+  return Fcons (make_number (x), make_number (y));
+}
 
 /***********************************************************************
                               Messages
@@ -28808,6 +28914,7 @@
   defsubr (&Sformat_mode_line);
   defsubr (&Sinvisible_p);
   defsubr (&Scurrent_bidi_paragraph_direction);
+  defsubr (&Swindow_text_pixel_size);
 
   DEFSYM (Qmenu_bar_update_hook, "menu-bar-update-hook");
   DEFSYM (Qoverriding_terminal_local_map, "overriding-terminal-local-map");



reply via email to

[Prev in Thread] Current Thread [Next in Thread]