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
extend Gem::Deprecate
attr_accessor :fonts
attr_writer :style_generator
alias :get_background= :style_generator=
deprecate :get_background=, "style_generator=", 2017, 02
@@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 = []
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 initialize(msg = nil, default_fonts = {}, *rest, style: nil)
super(*rest)
@fonts = default_fonts
@style_generator = style
self.editable = false
self.cursor_visible = false
self.wrap_mode = Gtk::TextTag::WRAP_CHAR
gen_body(msg) if msg
end
# このウィジェットの背景色を返す
# ==== Return
# Gtk::Style
def style_generator
if @style_generator.respond_to? :to_proc
@style_generator.to_proc.call
elsif @style_generator
@style_generator
else
parent.style.bg(Gtk::STATE_NORMAL)
end
end
alias :get_background :style_generator
deprecate :get_background, "style_generator", 2017, 02
# TODO プライベートにする
def set_cursor(textview, cursor)
textview.get_window(Gtk::TextView::WINDOW_TEXT).set_cursor(Gdk::Cursor.new(cursor))
end
def bg_modifier(color = style_generator)
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
|