Generate xpms for font icons, when not present

Also use generate xpms when font icons are not present
This commit is contained in:
Matthew Fidler 2016-04-04 08:15:38 -05:00
parent f1e6f30d85
commit ec1f3b7472

View file

@ -601,7 +601,10 @@ everywhere else."
(file-exists-p (mode-icons--get-emoji-xpm-file icon-spec))))) (file-exists-p (mode-icons--get-emoji-xpm-file icon-spec)))))
(and (eq (nth 2 icon-spec) 'jpg) (image-type-available-p 'jpeg)) (and (eq (nth 2 icon-spec) 'jpg) (image-type-available-p 'jpeg))
(and (eq (nth 2 icon-spec) 'xpm-bw) (image-type-available-p 'xpm)) (and (eq (nth 2 icon-spec) 'xpm-bw) (image-type-available-p 'xpm))
(mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec)) (or (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec))
(and (image-type-available-p 'xpm)
(mode-icons--get-font-xpm-file icon-spec)
(file-exists-p (mode-icons--get-font-xpm-file icon-spec))))
(or (image-type-available-p (nth 2 icon-spec)) (or (image-type-available-p (nth 2 icon-spec))
(and (eq (nth 2 icon-spec) 'png) (and (eq (nth 2 icon-spec) 'png)
(and (image-type-available-p 'xpm) (and (image-type-available-p 'xpm)
@ -614,10 +617,11 @@ everywhere else."
(defvar mode-icons--gimp (executable-find "gimp") (defvar mode-icons--gimp (executable-find "gimp")
"Gimp to convert png to xpm.") "Gimp to convert png to xpm.")
(defvar mode-icons--gimp-inferior-args "-i -d -f -b -") (defvar mode-icons--gimp-inferior-args "-i -d -b -")
(defvar mode-icons--stop-gimp-after 120 (defvar mode-icons--stop-gimp-after nil
"Seconds of idle time before mode-icons gimp is stopped.") "Seconds of idle time before mode-icons gimp is stopped.
When nil, don't stop the gimp inferior mode.")
(defvar mode-icons--stop-gimp-timer nil) (defvar mode-icons--stop-gimp-timer nil)
@ -647,6 +651,7 @@ everywhere else."
(and (setq buf (get-buffer "*mode-icons-gimp*")) (and (setq buf (get-buffer "*mode-icons-gimp*"))
(with-current-buffer buf (with-current-buffer buf
(goto-char (point-min)) (goto-char (point-min))
(ignore-errors (comint-send-string "\n"))
(when (re-search-forward "ts>" nil t) (when (re-search-forward "ts>" nil t)
(setq mode-icons--gimp-ready-p t)))))))) (setq mode-icons--gimp-ready-p t))))))))
@ -681,6 +686,88 @@ everywhere else."
(kill-buffer (get-buffer "*mode-icons-gimp*"))) (kill-buffer (get-buffer "*mode-icons-gimp*")))
(t (run-with-idle-timer 1 nil #'mode-icons--stop-gimp-inferior)))))) (t (run-with-idle-timer 1 nil #'mode-icons--stop-gimp-inferior))))))
(defvar mode-icons--font-to-xpm-gimp-script
(replace-regexp-in-string
"[ \n\t]+" " "
"(let* ((image-width 1024)
(image-height 20)
(buffer-image 1)
(text \"%s\")
(font-size 20)
(font-name \"%s\")
(xpm-image \"%s\")
(bg-color '(255 255 255))
(fg-color '(0 0 0))
(image (car (gimp-image-new 1024 16 0)))
(layer (car (gimp-layer-new image image-width image-height RGB-IMAGE \"layer 1\" 100 NORMAL)))
(out-text)
(out-width)
(out-height)
(out-buffer)
(drawable))
(gimp-image-add-layer image layer 0)
(gimp-context-set-background bg-color)
(gimp-context-set-foreground fg-color)
(gimp-layer-add-alpha layer)
(gimp-drawable-fill layer TRANSPARENT-FILL)
(set! out-text (car (gimp-text-fontname image layer 0 0 text 0 TRUE font-size PIXELS font-name)))
(set! out-width (car (gimp-drawable-width out-text)))
(set! out-height (car (gimp-drawable-height out-text)))
(set! out-buffer (* out-height (/ buffer-image 100)))
(set! out-height (+ out-height out-buffer out-buffer))
(set! out-width (+ out-width out-buffer out-buffer))
(gimp-image-resize image out-width out-height 0 0)
(gimp-layer-resize layer out-width out-height 0 0)
(gimp-layer-set-offsets out-text out-buffer out-buffer)
(set! drawable (car (gimp-image-get-active-layer image)))
(file-xpm-save RUN-NONINTERACTIVE image drawable xpm-image xpm-image 127)
(gimp-image-delete image))")
"Gimp scheme script to convert a font character to xpm file.")
(defvar mode-icons--convert-text-to-xpm (make-hash-table :test 'equal))
(defun mode-icons--convert-text-to-xpm (text font xpm)
"Convert TEXT in FONT to XPM file using gimp."
(when (and mode-icons--gimp (file-exists-p mode-icons--gimp)
xpm (not (gethash xpm mode-icons--convert-text-to-xpm))
(not (file-exists-p xpm)))
(puthash xpm t mode-icons--convert-text-to-xpm)
(mode-icons--process-gimp (format mode-icons--font-to-xpm-gimp-script text font xpm))))
(defun mode-icons--get-font-xpm-file (icon-spec &optional icon-name)
"Get the font icon equivalent xpm file name from ICON-SPEC.
When ICON-NAME is non-nil, return the small icon name without the
extension or directory."
(let* ((xpm-int (or (and (stringp (nth 1 icon-spec))
(= 1 (length (nth 1 icon-spec)))
(aref (nth 1 icon-spec) 0))
(and (integerp (nth 1 icon-spec))
(nth 1 icon-spec))))
(xpm-base (and (integerp xpm-int)
(format "%s-%x" (nth 2 icon-spec)
xpm-int))))
(and xpm-base
(if icon-name
xpm-base
(mode-icons-get-icon-file (concat xpm-base ".xpm"))))))
(defun mode-icons--create-font-xpm-file (icon-spec)
"Create a font-based xpm file based on ICON-SPEC."
(mode-icons--convert-text-to-xpm
(or (and (stringp (nth 1 icon-spec))
(nth 1 icon-spec))
(and (integerp (nth 1 icon-spec))
(make-string 1 (nth 1 icon-spec))))
(symbol-name (nth 2 icon-spec))
(mode-icons--get-font-xpm-file icon-spec)))
(defun mode-icons--convert-all-font-icons-to-xpm ()
"Convert all font icons to xpm files."
(interactive)
(setq mode-icons--convert-text-to-xpm (make-hash-table :test 'equal))
(dolist (icon-spec mode-icons)
(when (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec))
(mode-icons--create-font-xpm-file icon-spec))))
(defvar mode-icons--png-to-xpm-gimp-script (defvar mode-icons--png-to-xpm-gimp-script
(replace-regexp-in-string (replace-regexp-in-string
"[ \n\t]+" " " "[ \n\t]+" " "
@ -694,19 +781,19 @@ everywhere else."
(new-width (inexact->exact (round (* width (/ new-height height)))))) (new-width (inexact->exact (round (* width (/ new-height height))))))
(gimp-image-resize image 16 new-width 0 0) (gimp-image-resize image 16 new-width 0 0)
(set! drawable (car (gimp-image-get-active-layer image))) (set! drawable (car (gimp-image-get-active-layer image)))
(file-xpm-save RUN-NONINTERACTIVE image drawable xpm-image xpm-image 127))") (file-xpm-save RUN-NONINTERACTIVE image drawable xpm-image xpm-image 127)
(gimp-image-delete image))")
"Gimp scheme script to convert png to xpm.") "Gimp scheme script to convert png to xpm.")
(defvar mode-icons--convert-png-to-xpm (make-hash-table :test 'equal) (defvar mode-icons--convert-png-to-xpm (make-hash-table :test 'equal)
"Hash table to make sure you only convert once.") "Hash table to make sure you only convert once.")
(setq mode-icons--convert-png-to-xpm (make-hash-table :test 'equal))
(defun mode-icons--convert-png-to-xpm (png xpm) (defun mode-icons--convert-png-to-xpm (png xpm)
"Covert PNG to a ?x16 XPM using `mode-icons--gimp'." "Covert PNG to a ?x16 XPM using `mode-icons--gimp'."
(when (and mode-icons--gimp (file-exists-p mode-icons--gimp) (when (and mode-icons--gimp (file-exists-p mode-icons--gimp)
xpm (not (gethash (list png xpm) mode-icons--convert-png-to-xpm)) xpm (not (gethash (list png xpm) mode-icons--convert-png-to-xpm))
(not (file-exists-p xpm))) (not (file-exists-p xpm)))
(puthash (list png xpm) t mode-icons--convert-png-to-xpm)
(mode-icons--process-gimp (format mode-icons--png-to-xpm-gimp-script png xpm)))) (mode-icons--process-gimp (format mode-icons--png-to-xpm-gimp-script png xpm))))
(defun mode-icons--get-png-xpm-file (icon-spec &optional icon-name) (defun mode-icons--get-png-xpm-file (icon-spec &optional icon-name)
@ -724,7 +811,7 @@ This only supports emoji enclosed in a \":\" like :herb:.
When ICON-NAME is non-nil, return the mode-icons icon name. When ICON-NAME is non-nil, return the mode-icons icon name.
For :herb: it would be e-herb." For :herb: it would be e-herb."
(let* ((xpm-base (nth 1 icon-spec)) (let* ((xpm-base (nth 1 icon-spec))
) file)
(when (char-equal (aref xpm-base 0) ?:) (when (char-equal (aref xpm-base 0) ?:)
(setq file (substring xpm-base 1)) (setq file (substring xpm-base 1))
(when (char-equal (aref (substring xpm-base -1) 0) ?:) (when (char-equal (aref (substring xpm-base -1) 0) ?:)
@ -768,12 +855,27 @@ If possible, convert the png file to an xpm file."
:face face) :face face)
'mode-icons-p icon-spec))))) 'mode-icons-p icon-spec)))))
(defcustom mode-icons-prefer-xpm-over-emoji nil
"Prefer generated xpms over fonts.
If mode-icons has a generated font character, prefer that over
the actual font."
:type 'boolean
:group 'mode-icons)
(defcustom mode-icons-generate-emoji-xpms nil
"Generate font compatibility xpms for fonts."
:type 'boolean
:group 'mode-icons)
(defun mode-icons--get-emoji (mode icon-spec &optional face) (defun mode-icons--get-emoji (mode icon-spec &optional face)
"Get MODE emoji for ICON-SPEC using FACE." "Get MODE emoji for ICON-SPEC using FACE."
(let* ((xpm (mode-icons--get-emoji-xpm-file icon-spec)) (let* ((xpm (mode-icons--get-emoji-xpm-file icon-spec))
(xpm-name (mode-icons--get-emoji-xpm-file icon-spec t)) (xpm-name (mode-icons--get-emoji-xpm-file icon-spec t))
(xpm-p (file-readable-p xpm))) (xpm-p (file-readable-p xpm)))
(if xpm-p (if (or (and mode-icons-prefer-xpm-over-emoji xpm-p)
(and xpm-p (not (featurep 'emojify)))
(and xpm-p (not (image-type-available-p 'png))))
(propertize (format "%s" mode) 'display (propertize (format "%s" mode) 'display
(mode-icons-get-icon-display (mode-icons-get-icon-display
xpm-name 'xpm xpm-name 'xpm
@ -790,7 +892,8 @@ If possible, convert the png file to an xpm file."
(if (not (file-exists-p image-file)) (if (not (file-exists-p image-file))
(propertize (format "%s" mode) (propertize (format "%s" mode)
'mode-icons-p icon-spec) 'mode-icons-p icon-spec)
(mode-icons--convert-png-to-xpm image-file xpm) (when mode-icons-generate-emoji-xpms
(mode-icons--convert-png-to-xpm image-file xpm))
(propertize (format "%s" mode) (propertize (format "%s" mode)
'display 'display
(create-image image-file (create-image image-file
@ -808,6 +911,55 @@ If possible, convert the png file to an xpm file."
:height (emojify-default-font-height)) :height (emojify-default-font-height))
'mode-icons-p icon-spec)))))) 'mode-icons-p icon-spec))))))
(defcustom mode-icons-prefer-xpm-over-font nil
"Prefer generated xpms over fonts.
If mode-icons has a generated font character, prefer that over
the actual font."
:type 'boolean
:group 'mode-icons)
(defcustom mode-icons-generate-font-xpms nil
"Generate font compatibility xpms for fonts."
:type 'boolean
:group 'mode-icons)
(defun mode-icons--get-font (mode icon-spec &optional face)
"Get font for MODE based on ICON-SPEC, and FACE."
;; Use `compose-region' because it allows clicable text.
(let* ((xpm (mode-icons--get-font-xpm-file icon-spec))
(xpm-name (mode-icons--get-font-xpm-file icon-spec t))
(xpm-p (file-readable-p xpm)))
(when (and (not xpm-p) mode-icons-generate-font-xpms)
(mode-icons--create-font-xpm-file icon-spec))
(if (and xpm-p (or mode-icons-prefer-xpm-over-font
(not (mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec)))))
(propertize (format "%s" mode) 'display
(mode-icons-get-icon-display
xpm-name 'xpm
(or face
(and (mode-icons--selected-window-active)
'mode-line)
'mode-line-inactive))
'mode-icons-p (list (nth 0 icon-spec) xpm-name 'xpm-bw)
'face face)
(with-temp-buffer
(if (stringp mode)
(insert mode)
(insert (or (and (integerp (nth 1 icon-spec))
(make-string 1 (nth 1 icon-spec)))
(nth 1 icon-spec))))
(compose-region (point-min) (point-max) (or (and (integerp (nth 1 icon-spec))
(make-string 1 (nth 1 icon-spec)))
(nth 1 icon-spec)))
(put-text-property (point-min) (point-max)
'face (or face
(and (mode-icons--selected-window-active)
'mode-line)
'mode-line-inactive))
(put-text-property (point-min) (point-max)
'mode-icons-p icon-spec)
(buffer-string)))))
(defun mode-icons-propertize-mode (mode icon-spec &optional face) (defun mode-icons-propertize-mode (mode icon-spec &optional face)
"Propertize MODE with ICON-SPEC. "Propertize MODE with ICON-SPEC.
@ -825,18 +977,7 @@ FACE is the face to match when a xpm-bw image is used."
'mode-icons-p icon-spec)) 'mode-icons-p icon-spec))
((mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec)) ((mode-icons-supported-font-p (nth 1 icon-spec) (nth 2 icon-spec))
;; (propertize mode 'display (nth 1 icon-spec) 'mode-icons-p t) ;; (propertize mode 'display (nth 1 icon-spec) 'mode-icons-p t)
;; Use `compose-region' because it allows clicable text. (mode-icons--get-font mode icon-spec face))
(with-temp-buffer
(if (stringp mode)
(insert mode)
(insert (or (and (integerp (nth 1 icon-spec))
(make-string 1 (nth 1 icon-spec)))
(nth 1 icon-spec))))
(compose-region (point-min) (point-max) (or (and (integerp (nth 1 icon-spec))
(make-string 1 (nth 1 icon-spec)))
(nth 1 icon-spec)))
(put-text-property (point-min) (point-max) 'mode-icons-p icon-spec)
(buffer-string)))
((and (stringp (nth 1 icon-spec)) (eq (nth 2 icon-spec) 'emoji)) ((and (stringp (nth 1 icon-spec)) (eq (nth 2 icon-spec) 'emoji))
(mode-icons--get-emoji mode icon-spec face)) (mode-icons--get-emoji mode icon-spec face))
((and (stringp (nth 1 icon-spec)) (eq (nth 2 icon-spec) 'png)) ((and (stringp (nth 1 icon-spec)) (eq (nth 2 icon-spec) 'png))
@ -1069,6 +1210,9 @@ Use FACE when specified."
(or face (or face
(and active 'mode-line) (and active 'mode-line)
'mode-line-inactive)) 'mode-line-inactive))
'face (or face
(and active 'mode-line)
'mode-line-inactive)
'mode-icons-p icon-spec)) 'mode-icons-p icon-spec))
((and icon-spec (memq (nth 2 icon-spec) '(emoji)) ((and icon-spec (memq (nth 2 icon-spec) '(emoji))
(file-exists-p (mode-icons--get-emoji-xpm-file icon-spec))) (file-exists-p (mode-icons--get-emoji-xpm-file icon-spec)))
@ -1078,25 +1222,35 @@ Use FACE when specified."
(or face (or face
(and active 'mode-line) (and active 'mode-line)
'mode-line-inactive)) 'mode-line-inactive))
'face (or face
(and active 'mode-line)
'mode-line-inactive)
'mode-icons-p icon-spec)) 'mode-icons-p icon-spec))
(t mode)))) (t (propertize mode
'face (or face
(and active 'mode-line)
'mode-line-inactive))))))
(defun mode-icons--generate-minor-mode-list (&optional face) (defun mode-icons--generate-minor-mode-list (&optional face)
"Extracts all rich strings necessary for the minor mode list. "Extracts all rich strings necessary for the minor mode list.
When FACE is non-nil, use FACE to render the `minor-mode-alist.'" When FACE is non-nil, use FACE to render the `minor-mode-alist'."
(let ((active (mode-icons--selected-window-active))) (let ((active (mode-icons--selected-window-active)))
(delete " " (delete "" (mapcar (lambda(mode) (delete " " (delete "" (mapcar (lambda(mode)
(concat " " (eval `(propertize ,(mode-icons--recolor-minor-mode-image mode active face) (concat " " (eval `(propertize ,(mode-icons--recolor-minor-mode-image mode active face)
,@mode-icons-minor-mode-base-text-properties)))) ,@mode-icons-minor-mode-base-text-properties))))
(split-string (format-mode-line minor-mode-alist))))))) (split-string (format-mode-line minor-mode-alist)))))))
(defun mode-icons--generate-narrow () (defun mode-icons--generate-narrow (&optional face)
"Extracts all rich strings necessary for narrow indicator." "Extracts all rich strings necessary for narrow indicator.
(let (icon-spec) When FACE is non-nil, use FACE to render the narrow indicator."
(let ((active (mode-icons--selected-window-active))
icon-spec)
(delete " " (delete "" (mapcar (lambda(mode) (delete " " (delete "" (mapcar (lambda(mode)
(concat " " (eval `(propertize (concat " " (eval `(propertize
,(if (setq icon-spec (mode-icons-get-icon-spec mode)) ,(if (setq icon-spec (mode-icons-get-icon-spec (concat " " mode)))
(mode-icons-propertize-mode mode icon-spec) (mode-icons--recolor-minor-mode-image
(mode-icons-propertize-mode (concat " " mode) icon-spec)
active face)
mode) mode)
,@mode-icons-narrow-text-properties)))) ,@mode-icons-narrow-text-properties))))
(split-string (format-mode-line "%n"))))))) (split-string (format-mode-line "%n")))))))
@ -1107,8 +1261,10 @@ When FACE is non-nil, use FACE to render the `minor-mode-alist.'"
:type 'boolean :type 'boolean
:group 'mode-icons) :group 'mode-icons)
(defun mode-icons--read-only-status () (defun mode-icons--read-only-status (&optional face)
"Get Read Only Status icon." "Get Read Only Status icon.
FACE is the face to render the icon in."
(let ((active (mode-icons--selected-window-active)))
(eval `(propertize (eval `(propertize
,(let ((ro (format-mode-line "%1*")) ,(let ((ro (format-mode-line "%1*"))
icon-spec) icon-spec)
@ -1121,20 +1277,23 @@ When FACE is non-nil, use FACE to render the `minor-mode-alist.'"
(if (setq icon-spec (mode-icons-get-icon-spec 'writable)) (if (setq icon-spec (mode-icons-get-icon-spec 'writable))
(mode-icons-propertize-mode 'writable icon-spec) (mode-icons-propertize-mode 'writable icon-spec)
ro))) ro)))
"")) "")
ro (mode-icons--recolor-minor-mode-image ro active face))
(when (and mode-icons-read-only-space (when (and mode-icons-read-only-space
(not (string= ro ""))) (not (string= ro "")))
(setq ro (concat ro " "))) (setq ro (concat ro " ")))
ro) ro)
,@mode-icons-read-only-text-properties))) ,@mode-icons-read-only-text-properties))))
(defcustom mode-icons-modified-status-space t (defcustom mode-icons-modified-status-space t
"Add Space to modified status." "Add Space to modified status."
:type 'boolean :type 'boolean
:group 'mode-icons) :group 'mode-icons)
(defun mode-icons--modified-status () (defun mode-icons--modified-status (&optional face)
"Get modified status icon." "Get modified status icon.
FACE is the face to render the icon in."
(let ((active (mode-icons--selected-window-active)))
(eval `(propertize (eval `(propertize
,(or (ignore-errors ,(or (ignore-errors
(let* ((bfn (buffer-file-name)) (let* ((bfn (buffer-file-name))
@ -1164,12 +1323,13 @@ When FACE is non-nil, use FACE to render the `minor-mode-alist.'"
(mode-icons-propertize-mode 'saved icon-spec) (mode-icons-propertize-mode 'saved icon-spec)
mod))) mod)))
"")) ""))
(setq mod (mode-icons--recolor-minor-mode-image mod active face))
(when (and mode-icons-modified-status-space (when (and mode-icons-modified-status-space
(stringp mod) (stringp mod)
(not (string= mod ""))) (not (string= mod "")))
(setq mod (concat mod " "))) (setq mod (concat mod " ")))
mod)) "") mod)) "")
,@mode-icons-modified-text-properties))) ,@mode-icons-modified-text-properties))))
;; Based on rich-minority by Artur Malabarba ;; Based on rich-minority by Artur Malabarba
(defvar mode-icons--backup-construct nil) (defvar mode-icons--backup-construct nil)
@ -1217,12 +1377,14 @@ When FACE is non-nil, use FACE to render the `minor-mode-alist.'"
:type 'boolean :type 'boolean
:group 'mode-icons) :group 'mode-icons)
(defun mode-icons--mode-line-eol-desc (&optional string) (defun mode-icons--mode-line-eol-desc (&optional string face)
"Modify `mode-line-eol-desc' to have icons. "Modify `mode-line-eol-desc' to have icons.
STRING is the string to modify, or if absent, the value from `mode-line-eol-desc'." STRING is the string to modify, or if absent, the value from `mode-line-eol-desc'.
FACE is the face that will be used to render the segment."
(let* ((str (or string (mode-line-eol-desc))) (let* ((str (or string (mode-line-eol-desc)))
(props (text-properties-at 0 str)) (props (text-properties-at 0 str))
(lt2 "") (lt2 "")
(active (mode-icons--selected-window-active))
icon-spec) icon-spec)
(setq str (or (cond (setq str (or (cond
((string= "(Unix)" str) ((string= "(Unix)" str)
@ -1248,6 +1410,7 @@ STRING is the string to modify, or if absent, the value from `mode-line-eol-desc
str)) str))
(t str)) (t str))
"")) ""))
(setq str (mode-icons--recolor-minor-mode-image str active face))
(when mode-icons-eol-text (when mode-icons-eol-text
(setq str (concat str lt2))) (setq str (concat str lt2)))
(when (and mode-icons-eol-space (when (and mode-icons-eol-space