Created
February 29, 2012 11:43
-
-
Save penguin2716/1940257 to your computer and use it in GitHub Desktop.
mikutterの投稿ボックスにEmacsっぽいキーバインドを付けるやつ(スタックがStringになるバグを除去)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#-*- coding: utf-8 -*- | |
=begin | |
* これは何? | |
mikutterの投稿ボックスにEmacsっぽいキーバインドを付けます. | |
だいたい次みたいなことができます. | |
・C-[fbnpae] カーソルの移動 | |
・C-[dh] 文字の削除 | |
・C-SPC 選択のトグル | |
・C-[/z] 戻る | |
・C-w 選択領域のカット | |
・C-k 行末までカット | |
・C-y カーソル位置に貼り付け | |
・C-g 選択のトグルをOFF | |
・C-A C-aの全選択がなくなったのでC-Aで全選択にしてみた | |
* 使い方とか | |
ファイルをコピーして,ソースを2行書き換えます. | |
1. gtk_emacslike_textview.rb を mikutter/core/mui/ にコピー | |
2. 下のパッチをあてます | |
3. mikutter を再起動しましょう | |
--- core/mui/gtk_postbox.rb.org 2012-02-29 11:33:51.421695468 +0900 | |
+++ core/mui/gtk_postbox.rb 2012-02-29 20:08:21.966950690 +0900 | |
@@ -8,6 +8,7 @@ | |
require 'thread' | |
miquire :mui, 'miracle_painter' | |
miquire :mui, 'intelligent_textview' | |
+miquire :mui, 'emacslike_textview' | |
module Gtk | |
class PostBox < Gtk::EventBox | |
@@ -62,7 +63,7 @@ | |
def widget_post | |
return @post if defined?(@post) | |
- @post = Gtk::TextView.new | |
+ @post = Gtk::EmacsLikeTextView.new | |
post_set_default_text(@post) | |
@post.wrap_mode = Gtk::TextTag::WRAP_CHAR | |
@post.border_width = 2 | |
* 投稿が完了してもundoスタックが残ってるんだけど | |
仕様です. | |
* バグを見つけました! | |
@penguin2716 までリプライとか飛ばして頂けるとうれしいです. | |
* コードが汚い | |
ごめんなさい. | |
=end | |
require 'gtk2' | |
module Gtk | |
class EmacsLikeTextView < Gtk::TextView | |
# @@hist_limit : 履歴スタックの最大保存数 | |
# @@targetkey : Ctrlで装飾してEmacsっぽいキーバインドにするキー. | |
# 元から割り当てられていた機能は呼ばない. | |
# @@unselectkey : 選択トグルを自動的にOFFにするキー. | |
# @select : 選択トグルのON/OFFを格納 | |
# @history_stack : 履歴スタック | |
@@hist_limit = 100 | |
@@targetkey = ['A', 'space', 'g', 'f', 'b', 'n', 'p', 'a', | |
'e', 'd', 'h', 'w', 'k', 'y', 'slash', 'z'] | |
@@unselectkey = ['g', 'd', 'h', 'w', 'k', 'y', 'slash', 'z'] | |
def initialize | |
super | |
@select = false | |
@history_stack = [] | |
@history_stack.push(self.buffer.text) | |
# バッファが変更されたら自動的に履歴スタックに積む | |
self.buffer.signal_connect('changed') { | |
self.push_buffer | |
} | |
# キーバインドの追加 | |
self.signal_connect('key_press_event') { |w, e| | |
if Gdk::Window::ModifierType::CONTROL_MASK == | |
e.state & Gdk::Window::CONTROL_MASK then | |
key = Gdk::Keyval.to_name(e.keyval) | |
# 選択トグルの解除 | |
if @@unselectkey.select{|k| k == key}.length > 0 then | |
@select = false | |
end | |
case key | |
when 'A' # 全選択 | |
self.select_all(true) | |
when 'space' # 選択トグルのON/OFF | |
if @select then | |
@select = false | |
else | |
@select = true | |
end | |
when 'g' # 選択解除 | |
self.select_all(false) | |
when 'f' # 右に移動 | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, 1, @select) | |
when 'b' # 左に移動 | |
self.move_cursor(Gtk::MOVEMENT_VISUAL_POSITIONS, -1, @select) | |
when 'n' # 次の行に移動 | |
self.move_cursor(Gtk::MOVEMENT_DISPLAY_LINES, 1, @select) | |
when 'p' # 前の行に移動 | |
self.move_cursor(Gtk::MOVEMENT_DISPLAY_LINES, -1, @select) | |
when 'a' # 行頭へ移動 | |
self.move_cursor(Gtk::MOVEMENT_PARAGRAPH_ENDS, -1, @select) | |
when 'e' # 行末へ移動 | |
self.move_cursor(Gtk::MOVEMENT_PARAGRAPH_ENDS, 1, @select) | |
when 'd' # Deleteの挙動 | |
self.delete_from_cursor(Gtk::DELETE_CHARS, 1) | |
when 'h' # BackSpaceの挙動 | |
self.delete_from_cursor(Gtk::DELETE_CHARS, -1) | |
when 'w' # カット | |
self.cut_clipboard | |
when 'k' # 現在位置から行末までカット.行末の場合はDeleteの挙動になる | |
before = self.buffer.text | |
self.move_cursor(Gtk::MOVEMENT_PARAGRAPH_ENDS, 1, true) | |
self.cut_clipboard | |
if before == self.buffer.text then | |
self.delete_from_cursor(Gtk::DELETE_CHARS, 1) | |
end | |
when 'y' # 現在位置に貼り付け | |
self.paste_clipboard | |
when 'slash', 'z' # undoの挙動 | |
self.undo | |
end | |
end | |
# Emacsっぽいキーバインドとして実行したら,もとから割り当てられていた機能は呼ばない | |
if @@targetkey.select{|k| k == key}.length > 0 then | |
true | |
else | |
false | |
end | |
} | |
end | |
# 現在のバッファと最新の履歴が異なっていればスタックに現在の状態を追加 | |
def push_buffer | |
if @history_stack == nil then | |
@history_stack = [''] | |
end | |
if self.buffer.text != '' then | |
if @history_stack[-1] != self.buffer.text then | |
@history_stack.push(self.buffer.text) | |
end | |
if @history_stack.size > @@hist_limit then | |
@history_stack.delete(@history_stack[0]) | |
end | |
end | |
end | |
# undoの実装.バッファの内容を変更すると自動的に履歴スタックに追加されるので, | |
# 履歴スタックに追加したら最新の履歴を捨てる | |
def undo | |
top = @history_stack[-1] | |
if top != nil then | |
if top == self.buffer.text then | |
# 最新履歴が現在の状態と同じなら,2番目の履歴を参照 | |
@history_stack.pop | |
second = @history_stack.pop | |
if second != nil then | |
self.buffer.set_text(second) | |
@history_stack.pop | |
else # 上から2番目が空 | |
self.buffer.set_text('') | |
@history_stack.pop | |
end | |
else | |
self.buffer.set_text(top) | |
@history_stack.pop | |
end | |
else # 履歴スタックが空 | |
self.buffer.set_text('') | |
@history_stack.pop | |
end | |
end | |
# 初期状態にリセットする.現在は使っていない | |
def reset | |
@history_stack = [] | |
@select = false | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment