fill_paragraph Text

Gtk::Text は良くできてると思うんですが、Emacs に慣れた身にはやはり 機能不足に感じます。ということで、まずは fill_paragraph を実装して みました。Alt-q で段落を指定幅に整形します。漢字コードは EUC にの み対応しています。ん〜、かなり長くなってしまいました。

module GtkTextFillParagraph

  def initialize(*args)
    super(*args)
    @fill_column = 70
    @kinsoku_list = [' ', '、', '。']
    signal_connect('key-press-event') do |w, ev|
      if ev.state == 8 and ev.keyval == ?q then
	s, e = get_paragraph
	str = get_chars s, e
	delete_text s, e
	insert_text fill_paragraph(str), s
      end
    end
  end

  attr :fill_column, true

  def fill_paragraph(s)
    prefix = /\A[\#^gt;]+ +/.match(s).to_s
    col = 0
    ret = ''
    s = s.gsub(/^#{prefix}/, '').gsub(/([\200-\376])\n|\n([\200-\376])/n, '\\1\\2').gsub(/(\S)\s+/, '\\1 ')
    s = prefix + s
    s.scan(/[\041-\176]+|./).each do |c|
      col2 = if c[0] < 0x80 then c.length elsif c[0] == 0x8e then 1 else 2 end
      if col == 0 then
	col = col2
      elsif not @kinsoku_list.include? c and col + col2 > @fill_column then
	ret << "\n#{prefix}"
	col = prefix.length + col2
      else
	col += col2
      end
      ret << c
    end
    ret
  end

  private

  def pos_byte(str, pos)
    str =~ /\A.{#{pos}}/p
    $&.length
  end

  def wclen(str)
    str.split(//).length
  end

  def get_paragraph()
    p = position
    l = get_length
    s = get_chars(0, -1)
    p2 = pos_byte s, p

    pre = if p2 == 0 then pre = ''
	  elsif s[0..p2-1] =~ /\n\n/p then pre = $'	#'
	  else pre = s[0..p2-1]
	  end
    aft = if s[p2..-1] =~ /\n\n/p then $`		#`
	  else aft = s[p2..-1]
	  end

    if pre[-1] == 0x0a and aft[0] == 0x0a then pre = aft = '' end

    startp = p - wclen(pre)
    endp = p + wclen(aft)
    [startp, endp]
  end
end

使用例

require 'gtk'

class MyText < Gtk::Text
  include GtkTextFillParagraph
end

w = Gtk::Window::new
t = MyText::new
t.set_editable true
w.add t
w.show_all

Gtk::main

Last modified: Sat Sep 15 04:13:10 JST 2001