Skip to content
zero-input.el 62.8 KiB
Newer Older
;;; zero-input.el --- Zero Chinese input method framework -*- lexical-binding: t -*-

;; Licensed under the Apache License, Version 2.0 (the "License");
;; you may not use this file except in compliance with the License.
;; You may obtain a copy of the License at
;;
;;     http://www.apache.org/licenses/LICENSE-2.0
;;
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
;; See the License for the specific language governing permissions and
;; limitations under the License.

;; Version: 2.8.0
;; URL: https://gitlab.emacsos.com/sylecn/zero-el
;; Package-Requires: ((emacs "24.3") (s "1.2.0"))

;;; Commentary:

;; zero-input is a Chinese input method framework for Emacs, implemented as an
;; Emacs minor mode.
;;
;; zero-input-pinyin is bundled with zero, to use pinyin input method, add to
;; ~/.emacs file:
;;
;;   (require 'zero-input)
Yuanle Song's avatar
Yuanle Song committed
;;   (zero-input-set-default-im "pinyin")
;;   ;; Now you may bind a key to zero-input-mode to make it easy to
;;   ;; switch on/off the input method.
;;   (global-set-key (kbd "<f5>") 'zero-input-mode)
;; Now in any Emacs buffer, you may press F5 and start typing pinyin string.
;; zero-input supports Chinese punctuation mapping.  There are three modes,
;; none, basic, and full.  The default is basic mode, which only map most
;; essential punctuations.  You can cycle zero-punctuation-level in current
;; buffer by C-c , , You can change default Chinese punctuation level:
;;
;;   (setq-default zero-input-punctuation-level
;;     zero-input-punctuation-level-full)
;;
;; zero-input supports full-width mode.  You can toggle full-width mode in
;; current buffer by C-c , . You can enable full-width mode by default:
;;
;;   (setq-default zero-input-full-width-p t)
Yuanle Song's avatar
Yuanle Song committed
;; For other features, you may check README file at
;; https://gitlab.emacsos.com/sylecn/zero-el/
;;
;; zero-input.el is auto-generated from multiple other files.  See
Yuanle Song's avatar
Yuanle Song committed
;; zero-input.el.in and build.py for details.  It's created because
;; package-lint doesn't support multi-file package yet.  See issue #111 at
Yuanle Song's avatar
Yuanle Song committed
;; https://github.com/purcell/package-lint/issues/111

;;; Code:

(require 'dbus)
(eval-when-compile
  (require 'cl-lib)
  (require 'cl-macs))
(require 'ring)
(require 's)

;; body of zero-input-panel.el

;;================
;; implementation
;;================


(defun zero-input-panel-error-handler (event error)
  "Handle dbus errors.

EVENT and ERROR are error-handler arguments."
  (when (or (string-equal "com.emacsos.zero.Panel"
			  (dbus-event-interface-name event))
	    (s-contains-p "com.emacsos.zero.Panel" (cadr error)))
    (error "Zero-Input-panel dbus failed: %S" (cadr error))))

(add-hook 'dbus-event-error-functions 'zero-input-panel-error-handler)

(defun zero-input-panel-async-call (method _handler &rest args)
  "Call METHOD on zero-input-panel service asynchronously.

This is a wrapper around `dbus-call-method-asynchronously'.
ARGS optional extra args to pass to the wrapped function."
  (apply 'dbus-call-method-asynchronously
	 :session
	 "com.emacsos.zero.Panel1"	; well known name
	 "/com/emacsos/zero/Panel1"	; object path
	 "com.emacsos.zero.Panel1.PanelInterface" ; interface name
	 method nil :timeout 500 args))

;;=========================
;; public utility function
;;=========================

(defun zero-input-alist-to-asv (hints)
  "Convert Lisp alist to dbus a{sv} data structure.

HINTS should be an alist of form '((k1 [v1type] v1) (k2 [v2type] v2)).

For example,
\(zero-input-alist-to-asv
  '((\"name\" \"foo\")
    (\"timeout\" :int32 10)))
=>
'(:array
  (:dict-entry \"name\" (:variant \"foo\"))
  (:dict-entry \"timeout\" (:variant :int32 10)))"
  (if (null hints)
      '(:array :signature "{sv}")
    (let ((result '(:array)))
      (dolist (item hints)
	(push (list :dict-entry (car item) (cons :variant (cdr item))) result))
      (reverse result))))

;;============
;; public API
;;============

(defun zero-input-panel-move (x y)
  "Move panel to specific coordinate (X, Y).
Origin (0, 0) is at screen top left corner."
  (zero-input-panel-async-call "Move" nil :int32 x :int32 y))

(defun zero-input-panel-show-candidates (preedit_str candidate_length candidates &optional hints)
  "Show CANDIDATES.
Argument PREEDIT_STR the preedit string.
Argument CANDIDATE_LENGTH how many candidates are in candidates list."
  (zero-input-panel-async-call "ShowCandidates" nil
			 :string preedit_str
			 :uint32 candidate_length
			 (or candidates '(:array))
			 (zero-input-alist-to-asv hints)))

(defun zero-input-panel-show ()
  "Show panel."
  (zero-input-panel-async-call "Show" nil))

(defun zero-input-panel-hide ()
  "Hide panel."
  (zero-input-panel-async-call "Hide" nil))

(defun zero-input-panel-quit ()
  "Quit panel application."
  (interactive)
  (zero-input-panel-async-call "Quit" nil))

(provide 'zero-input-panel)

;; body of zero-input-framework.el

;;==============
;; dependencies
;;==============


;;=======
;; utils
;;=======

;; this function is from ibus.el
(defun zero-input--ibus-compute-pixel-position (&optional pos window)
  "Return geometry of object at POS in WINDOW as a list like \(X Y H).
X and Y are pixel coordinates relative to top left corner of frame which
WINDOW is in.  H is the pixel height of the object.

Omitting POS and WINDOW means use current position and selected window,
respectively."
  (let* ((frame (window-frame (or window (selected-window))))
	 (posn (posn-at-point (or pos (window-point window)) window))
	 (line (cdr (posn-actual-col-row posn)))
	 (line-height (and line
			   (or (window-line-height line window)
			       (and (redisplay t)
				    (window-line-height line window)))))
	 (x-y (or (posn-x-y posn)
		  (let ((geom (pos-visible-in-window-p
			       (or pos (window-point window)) window t)))
		    (and geom (cons (car geom) (cadr geom))))
		  '(0 . 0)))
	 (ax (+ (car (window-inside-pixel-edges window))
		(car x-y)))
	 (ay (+ (cadr (window-pixel-edges window))
		(or (nth 2 line-height) (cdr x-y))))
	 (height (or (car line-height)
		     (with-current-buffer (window-buffer window)
		       (cond
			;; `posn-object-width-height' returns an incorrect value
			;; when the header line is displayed (Emacs bug #4426).
			((and posn
			      (null header-line-format))
			 (cdr (posn-object-width-height posn)))
			((and (bound-and-true-p text-scale-mode)
			      (not (zerop (with-no-warnings
					    text-scale-mode-amount))))
			 (round (* (frame-char-height frame)
				   (with-no-warnings
Loading full blame...