2011年2月23日水曜日

エターナル・フォース・コントロール

Emacsのキーマップについて調べていたら、入力イベントを変換する機能というのを見つけたので無駄機能を使って遊んでみます。

(defun enable-force-ctrl ()
(interactive)
(aset keyboard-translate-table ?a ?\^a)
(aset keyboard-translate-table ?b ?\^b)
(aset keyboard-translate-table ?c ?\^c)
(aset keyboard-translate-table ?d ?\^d)
(aset keyboard-translate-table ?e ?\^e)
(aset keyboard-translate-table ?f ?\^f)
(aset keyboard-translate-table ?g ?\^g)
(aset keyboard-translate-table ?h ?\^h)
(aset keyboard-translate-table ?i ?\^i)
(aset keyboard-translate-table ?j ?\^j)
(aset keyboard-translate-table ?k ?\^k)
(aset keyboard-translate-table ?l ?\^l)
(aset keyboard-translate-table ?m ?\^m)
(aset keyboard-translate-table ?n ?\^n)
(aset keyboard-translate-table ?o ?\^o)
(aset keyboard-translate-table ?p ?\^p)
(aset keyboard-translate-table ?q ?\^q)
(aset keyboard-translate-table ?r ?\^r)
(aset keyboard-translate-table ?s ?\^s)
(aset keyboard-translate-table ?t ?\^t)
(aset keyboard-translate-table ?u ?\^u)
(aset keyboard-translate-table ?v ?\^v)
(aset keyboard-translate-table ?w ?\^w)
(aset keyboard-translate-table ?x ?\^x)
(aset keyboard-translate-table ?y ?\^y)
(aset keyboard-translate-table ?z ?\^z))

このコマンドを実行すると、アルファベット小文字のキー入力は問答無用でCtrl付きキーシーケンスに変換されます。一度実行されると解除するのが極めて困難な状況に陥ることでしょう。

以下、キーマップについてのメモ。

Emacsで入力イベントがどのようにキー列となり、どのキーマップのコマンドが実行されるか、ということを学ぶには、「37.8.2 入力イベント」や「21 キーマップ」あたりを見ると良さそう。

キーマップの探索順序は、このようになるようです。

  1. key-translation-map
  2. テキスト属性local-mapによる代替ローカルキーマップ
  3. マイナーモードキーマップ(リストの先頭から順番)
  4. ローカルキーマップ(メジャーモード毎のマップ)
  5. グローバルキーマップ

テキスト属性によるキーマップの位置がちょっとあやしい。

(訂正:マイナーモードキーマップとテキスト属性のキーマップが案の定逆っぽいので修正. 2011/02/23)

キー探索を行うためにlookup-key、key-binding、local-key-binding、global-key-binding、minor-mode-key-bindingといった関数が存在するので、自力で探索することもそこまで大変ではなさそうです。

この他に、一部のキーマップの定義を無効化して、代替となるキーマップを利用するために overriding-*-mapというような名前の変数が用意されています。

キーマップは他のキーマップを敬称する(親とする)こともできるようです。 set-keymap-parent関数でキーマップに親マップを設定します。

2011年2月9日水曜日

CommonQtでコンテキストメニュー

CommonQtでコンテキストメニュー(右クリック時に出てくるメニュー)を出してみます。

(asdf:load-system :qt)
(defpackage :test
(:use :cl :qt)
(:export main))

(in-package :test)

(enable-syntax)

(defvar *qapp*)

(defclass test-window ()
((quit-action :accessor quit-action-of :initform nil)
(file-menu :accessor file-menu-of :initform nil))
(:metaclass qt-class)
(:qt-superclass "QMainWindow")
(:override
("contextMenuEvent" context-menu-event)))

(defmethod initialize-instance :after ((instance test-window) &key parent)
(if parent
(new instance parent)
(new instance))
(setf (quit-action-of instance)
(#_new QAction "&Quit" instance))
(#_setShortcut (quit-action-of instance)
(#_new QKeySequence (#_CTRL "Qt") (#_Key_Q "Qt")))

(#_connect "QObject"
(quit-action-of instance) (QSIGNAL "triggered()")
instance (QSLOT "close()"))
(let ((menu-bar (#_menuBar instance)))
(setf (file-menu-of instance)
(#_addMenu menu-bar "&File"))
(#_addAction (file-menu-of instance)
(quit-action-of instance))))



(defmethod context-menu-event ((instance test-window) event)
(let ((menu (#_new QMenu instance)))
(#_addAction menu (quit-action-of instance))
(#_exec menu (#_globalPos event))))

(defun main ()
(setf *qapp* (make-qapplication))
(let ((window (make-instance 'test-window)))
(#_setGeometry window 300 100 300 200)
(#_show window)
(unwind-protect
(#_exec *qapp*)
(#_hide window))))

メニューバーも出してみましたが、自分の環境だとショートカットがうまく動かなくいです。

CommonLisp, Qt, CommonQt