File: gtk_intelligent_textview.rb

package info (click to toggle)
mikutter 3.0.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 9,396 kB
  • ctags: 1,916
  • sloc: ruby: 16,619; sh: 117; makefile: 27
file content (176 lines) | stat: -rw-r--r-- 6,233 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# -*- coding: utf-8 -*-
miquire :mui, 'extension', 'contextmenu'
miquire :core, 'plugin'
miquire :miku, 'miku'

require 'gtk2'
require 'uri'

class Gtk::IntelligentTextview < Gtk::TextView

  attr_accessor :fonts, :get_background

  # @@wayofopenlink = MIKU::Cons.list([URI.regexp(['http','https']), lambda{ |url, cancel|
  #                                      Gtk.openurl(url) }].freeze).freeze

  @@linkrule = MIKU::Cons.list([URI.regexp(['http','https']),
                                lambda{ |u, clicked| self.openurl(u) },
                                lambda{ |u, clicked|
                                  Gtk::ContextMenu.new(['リンクのURLをコピー', ret_nth, lambda{ |opt, w| Gtk::Clipboard.copy(u) }],
                                                       ['開く', ret_nth, lambda{ |opt, w| self.openurl(u) }]).
                                  popup(clicked, true)}])
  @@widgetrule = []

  # URLを開く方法を追加する。
  # 追加に成功したらtrueを返す。
  # def self.addopenway(condition, &open)
  #   if(type_check(condition => :===, open => :call))
  #     @@wayofopenlink = MIKU::Cons.new([condition, open].freeze, @@wayofopenlink).freeze
  #     true end end

  def self.addlinkrule(reg, leftclick, rightclick=nil)
    @@linkrule = MIKU::Cons.new([reg, leftclick, rightclick].freeze, @@linkrule).freeze end

  def self.addwidgetrule(reg, widget = nil)
    @@widgetrule = @@widgetrule.unshift([reg, (widget or Proc.new)]) end

  # URLを開く
  def self.openurl(url)
    # gen_openurl_proc(url).call
    Gtk::TimeLine.openurl(url)
    false end

  # def self.gen_openurl_proc(url, way_of_open_link = @@wayofopenlink)
  #   way_of_open_link.freeze
  #   lambda{
  #     way_of_open_link.each_with_index{ |way, index|
  #     condition, open = *way
  #     if(condition === url)
  #       open.call(url, gen_openurl_proc(url, way_of_open_link[(index+1)..(way_of_open_link.size)]))
  #       break end } } end

  def initialize(msg = nil, default_fonts = {}, *args)
    @fonts = default_fonts
    @get_background = lambda{ parent.style.bg(Gtk::STATE_NORMAL) }
    super(*args)
    self.editable = false
    self.cursor_visible = false
    self.wrap_mode = Gtk::TextTag::WRAP_CHAR
    gen_body(msg) if msg
  end

  # TODO プライベートにする
  def set_cursor(textview, cursor)
    textview.get_window(Gtk::TextView::WINDOW_TEXT).set_cursor(Gdk::Cursor.new(cursor))
  end

  def bg_modifier(color = @get_background.call)
    if color.is_a? Gtk::Style
      self.style = color
    elsif get_window(Gtk::TextView::WINDOW_TEXT).respond_to?(:background=)
      get_window(Gtk::TextView::WINDOW_TEXT).background = color end
    queue_draw
    false end

  # 新しいテキスト _msg_ に内容を差し替える。
  # ==== Args
  # [msg] 表示する文字列
  # ==== Return
  # self
  def rewind(msg)
    type_strict msg => String
    set_buffer(Gtk::TextBuffer.new)
    gen_body(msg)
  end

  private

  def fonts2tags(fonts)
    tags = Hash.new
    tags['font'] = UserConfig[fonts['font']] if fonts.has_key?('font')
    if fonts.has_key?('foreground')
      tags['foreground_gdk'] = Gdk::Color.new(*UserConfig[fonts['foreground']]) end
    tags
  end

  def gen_body(msg, fonts={})
    type_strict msg => String, fonts => Hash
    tags = fonts2tags(fonts)
    tag_shell = buffer.create_tag('shell', fonts2tags(fonts))
    buffer.insert(buffer.start_iter, msg, 'shell')
    apply_links
    apply_inner_widget
    set_events(tag_shell)
    self
  end


  def set_events(tag_shell)
    self.signal_connect('realize'){
      self.parent.signal_connect('style-set'){ bg_modifier } }
    self.signal_connect('realize'){ bg_modifier }
    self.signal_connect('visibility-notify-event'){
      if fonts['font'] and tag_shell.font != UserConfig[fonts['font']]
        tag_shell.font = UserConfig[fonts['font']] end
      if fonts['foreground'] and tag_shell.foreground_gdk.to_s != UserConfig[fonts['foreground']]
        tag_shell.foreground_gdk = Gdk::Color.new(*UserConfig[fonts['foreground']]) end
      false }
    self.signal_connect('event'){
      set_cursor(self, Gdk::Cursor::XTERM)
      false }
#    self.signal_connect('button_release_event'){ |widget, event|
#       Gtk::Lock.synchronize{
#         menu_pop(widget) if (event.button == 3) }
#     false }
  end

  def create_tag_ifnecessary(tagname, buffer, leftclick, rightclick)
    tag = buffer.create_tag(tagname, "underline" => Pango::UNDERLINE_SINGLE)
    tag.signal_connect('event'){ |this, textview, event, iter|
      result = false
      if(event.is_a?(Gdk::EventButton)) and
          (event.event_type == Gdk::Event::BUTTON_RELEASE) and
          not(textview.buffer.selection_bounds[2])
        if (event.button == 1 and leftclick)
          leftclick.call(tagname, textview)
        elsif(event.button == 3 and rightclick)
          rightclick.call(tagname, textview)
          result = true end
      elsif(event.is_a?(Gdk::EventMotion))
        set_cursor(textview, Gdk::Cursor::HAND2)
      end
      result }
    tag end

  def apply_links
    @@linkrule.each{ |param|
      reg, left, right = param
      buffer.text.scan(reg) {
        match = Regexp.last_match
        index = buffer.text[0, match.begin(0)].size
        body = match.to_s.freeze
        create_tag_ifnecessary(body, buffer, left, right) if not buffer.tag_table.lookup(body)
        range = buffer.get_range(index, body.size)
        buffer.apply_tag(body, *range)
      } } end

  def apply_inner_widget
    offset = 0
    @@widgetrule.each{ |param|
      reg, widget_generator = param
      buffer.text.scan(reg) { |match|
        match = Regexp.last_match
        index = [buffer.text.size, match.begin(0)].min
        body = match.to_s.freeze
        range = buffer.get_range(index, body.size + offset)
        widget = widget_generator.call(body)
        if widget
          self.add_child_at_anchor(widget, buffer.create_child_anchor(range[1]))
          offset += 1 end } } end
end

Plugin.create :gtk_intelligent_textview do
  on_entity_linkrule_added do |rule|
    ::Gtk::IntelligentTextview.addlinkrule(rule[:regexp], lambda{ |seg, tv| rule[:callback].call(face: seg, url: seg, textview: tv) }) if rule[:regexp]
  end
end