1
|
# -*- coding: utf-8 -*-
|
2
|
|
3
|
miquire :core, 'plugin'
|
4
|
|
5
|
require 'gtk2'
|
6
|
|
7
|
=begin rdoc
|
8
|
プラグインに、簡単に設定ファイルを定義する機能を提供する。
|
9
|
以下の例は、このクラスを利用してプラグインの設定画面を定義する例。
|
10
|
Plugin.create(:test) do
|
11
|
settings("設定") do
|
12
|
boolean "チェックする", :test_check
|
13
|
end
|
14
|
end
|
15
|
|
16
|
settingsの中身は、 Plugin::Settings のインスタンスの中で実行される。
|
17
|
つまり、 Plugin::Settings のインスタンスメソッドは、 _settings{}_ の中で実行できるメソッドと同じです。
|
18
|
例ではbooleanメソッドを呼び出して、真偽値を入力させるウィジェットを配置させるように定義している
|
19
|
(チェックボックス)。明確にウィジェットを設定できるわけではなくて、値の意味を定義するだけなので、
|
20
|
前後関係などに影響されてウィジェットが変わる場合があるかも。
|
21
|
=end
|
22
|
class Plugin::Settings < Gtk::VBox
|
23
|
|
24
|
def initialize(plugin)
|
25
|
type_strict plugin => Plugin
|
26
|
super()
|
27
|
@plugin = plugin
|
28
|
if block_given?
|
29
|
instance_eval(&Proc.new)
|
30
|
end
|
31
|
end
|
32
|
|
33
|
# 複数行テキスト
|
34
|
# ==== Args
|
35
|
# [label] ラベル
|
36
|
# [config] 設定のキー
|
37
|
def multitext(label, config)
|
38
|
container = Gtk::HBox.new(false, 0)
|
39
|
input = Gtk::TextView.new
|
40
|
input.wrap_mode = Gtk::TextTag::WRAP_CHAR
|
41
|
input.border_width = 2
|
42
|
input.accepts_tab = false
|
43
|
input.editable = true
|
44
|
input.width_request = HYDE
|
45
|
input.buffer.text = Listener[config].get || ''
|
46
|
container.pack_start(Gtk::Label.new(label), false, true, 0) if label
|
47
|
container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
|
48
|
input.buffer.ssc('changed'){ |widget|
|
49
|
Listener[config].set widget.text }
|
50
|
closeup container
|
51
|
container
|
52
|
end
|
53
|
|
54
|
# 特定範囲の数値入力
|
55
|
# ==== Args
|
56
|
# [label] ラベル
|
57
|
# [config] 設定のキー
|
58
|
# [min] 最低値。これより小さい数字は入力できないようになる
|
59
|
# [max] 最高値。これより大きい数字は入力できないようになる
|
60
|
def adjustment(name, config, min, max)
|
61
|
container = Gtk::HBox.new(false, 0)
|
62
|
container.pack_start(Gtk::Label.new(name), false, true, 0)
|
63
|
adj = Gtk::Adjustment.new((Listener[config].get or min).to_f, min.to_f, max.to_f, 1.0, 5.0, 0.0)
|
64
|
spinner = Gtk::SpinButton.new(adj, 0, 0)
|
65
|
adj.signal_connect('value-changed'){ |widget, e|
|
66
|
Listener[config].set widget.value.to_i
|
67
|
false
|
68
|
}
|
69
|
closeup container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(spinner), true, true, 0)
|
70
|
container
|
71
|
end
|
72
|
|
73
|
# 真偽値入力
|
74
|
# ==== Args
|
75
|
# [label] ラベル
|
76
|
# [config] 設定のキー
|
77
|
def boolean(label, config)
|
78
|
input = Gtk::CheckButton.new(label)
|
79
|
input.active = Listener[config].get
|
80
|
input.signal_connect('toggled'){ |widget|
|
81
|
Listener[config].set widget.active? }
|
82
|
closeup input
|
83
|
input end
|
84
|
|
85
|
# ファイルを選択する
|
86
|
# ==== Args
|
87
|
# [label] ラベル
|
88
|
# [config] 設定のキー
|
89
|
# [current] 初期のディレクトリ
|
90
|
def fileselect(label, config, current=Dir.pwd)
|
91
|
fsselect(label, config, current, Gtk::FileChooser::ACTION_OPEN)
|
92
|
end
|
93
|
|
94
|
# ディレクトリを選択する
|
95
|
# ==== Args
|
96
|
# [label] ラベル
|
97
|
# [config] 設定のキー
|
98
|
# [current] 初期のディレクトリ
|
99
|
def dirselect(label, config, current=Dir.pwd)
|
100
|
fsselect(label, config, current, Gtk::FileChooser::ACTION_SELECT_FOLDER)
|
101
|
end
|
102
|
|
103
|
# 一行テキストボックス
|
104
|
# ==== Args
|
105
|
# [label] ラベル
|
106
|
# [config] 設定のキー
|
107
|
def input(label, config)
|
108
|
container = Gtk::HBox.new(false, 0)
|
109
|
input = Gtk::Entry.new
|
110
|
input.text = Listener[config].get || ""
|
111
|
container.pack_start(Gtk::Label.new(label), false, true, 0) if label
|
112
|
container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
|
113
|
input.signal_connect('changed'){ |widget|
|
114
|
Listener[config].set widget.text }
|
115
|
closeup container
|
116
|
container
|
117
|
end
|
118
|
|
119
|
# 一行テキストボックス(非表示)
|
120
|
# ==== Args
|
121
|
# [label] ラベル
|
122
|
# [config] 設定のキー
|
123
|
def inputpass(label, config)
|
124
|
container = Gtk::HBox.new(false, 0)
|
125
|
input = Gtk::Entry.new
|
126
|
input.visibility = false
|
127
|
input.text = Listener[config].get
|
128
|
container.pack_start(Gtk::Label.new(label), false, true, 0) if label
|
129
|
container.pack_start(Gtk::Alignment.new(1.0, 0.5, 0, 0).add(input), true, true, 0)
|
130
|
input.signal_connect('changed'){ |widget|
|
131
|
Listener[config].set widget.text }
|
132
|
closeup container
|
133
|
container
|
134
|
end
|
135
|
|
136
|
# 複数テキストボックス
|
137
|
# 任意個の項目を入力させて、配列で受け取る。
|
138
|
# ==== Args
|
139
|
# [label] ラベル
|
140
|
# [config] 設定のキー
|
141
|
def multi(label, config)
|
142
|
settings(label) do
|
143
|
container, box = Gtk::HBox.new(false, 0), Gtk::VBox.new(false, 0)
|
144
|
input_ary = []
|
145
|
btn_add = Gtk::Button.new(Gtk::Stock::ADD)
|
146
|
array_converter = lambda {
|
147
|
c = Listener[config].get || []
|
148
|
(c.is_a?(Array) ? c : [c]).compact }
|
149
|
add_button = lambda { |content|
|
150
|
input = Gtk::Entry.new
|
151
|
input.text = content.to_s
|
152
|
input.ssc(:changed) { |w|
|
153
|
Listener[config].set w.parent.children.map(&:text).compact }
|
154
|
input.ssc('focus_out_event'){ |w|
|
155
|
w.parent.remove(w) if w.text.empty?
|
156
|
false }
|
157
|
box.closeup input
|
158
|
input }
|
159
|
input_ary = array_converter.call.each(&add_button)
|
160
|
btn_add.ssc(:clicked) { |w|
|
161
|
w.get_ancestor(Gtk::Window).set_focus(add_button.call("").show)
|
162
|
false }
|
163
|
container.pack_start(box, true, true, 0)
|
164
|
container.pack_start(Gtk::Alignment.new(1.0, 1.0, 0, 0).add(btn_add), false, true, 0)
|
165
|
closeup container
|
166
|
container
|
167
|
end
|
168
|
end
|
169
|
|
170
|
# 設定のグループ。関連の強い設定をカテゴライズできる。
|
171
|
# ==== Args
|
172
|
# [title] ラベル
|
173
|
# [&block] ブロック
|
174
|
def settings(title)
|
175
|
group = Gtk::Frame.new.set_border_width(8)
|
176
|
if(title.is_a?(Gtk::Widget))
|
177
|
group.set_label_widget(title)
|
178
|
else
|
179
|
group.set_label(title) end
|
180
|
box = Plugin::Settings.new(@plugin).set_border_width(4)
|
181
|
box.instance_eval(&Proc.new)
|
182
|
closeup group.add(box)
|
183
|
group
|
184
|
end
|
185
|
|
186
|
# 〜についてダイアログを出すためのボタン。押すとダイアログが出てくる
|
187
|
# ==== Args
|
188
|
# [label] ラベル
|
189
|
# [options]
|
190
|
# 設定値。以下のキーを含むハッシュ。
|
191
|
# _:name_ :: ソフトウェア名
|
192
|
# _:version_ :: バージョン
|
193
|
# _:copyright_ :: コピーライト
|
194
|
# _:comments_ :: コメント
|
195
|
# _:license_ :: ライセンス
|
196
|
# _:website_ :: Webページ
|
197
|
# _:logo_ :: ロゴ画像のフルパス
|
198
|
# _:authors_ :: 作者の名前。通常Twitter screen name(Array)
|
199
|
# _:artists_ :: デザイナとかの名前。通常Twitter screen name(Array)
|
200
|
# _:documenters_ :: ドキュメントかいた人とかの名前。通常Twitter screen name(Array)
|
201
|
def about(label, options={})
|
202
|
about = Gtk::Button.new(label)
|
203
|
about.signal_connect("clicked"){
|
204
|
dialog = Gtk::AboutDialog.new.show
|
205
|
options.each { |key, value|
|
206
|
dialog.__send__("#{key}=", about_converter[key][value]) }
|
207
|
dialog.signal_connect('response') { dialog.destroy } }
|
208
|
closeup about
|
209
|
about end
|
210
|
|
211
|
# フォントを決定させる。押すとフォント、サイズを設定するダイアログが出てくる。
|
212
|
# ==== Args
|
213
|
# [label] ラベル
|
214
|
# [config] 設定のキー
|
215
|
def font(label, config)
|
216
|
closeup container = Gtk::HBox.new(false, 0).add(Gtk::Label.new(label).left).closeup(fontselect(label, config))
|
217
|
container end
|
218
|
|
219
|
# 色を決定させる。押すと色を設定するダイアログが出てくる。
|
220
|
# ==== Args
|
221
|
# [label] ラベル
|
222
|
# [config] 設定のキー
|
223
|
def color(label, config)
|
224
|
closeup container = Gtk::HBox.new(false, 0).add(Gtk::Label.new(label).left).closeup(colorselect(label, config))
|
225
|
container end
|
226
|
|
227
|
# フォントと色を決定させる。
|
228
|
# ==== Args
|
229
|
# [label] ラベル
|
230
|
# [font] フォントの設定のキー
|
231
|
# [color] 色の設定のキー
|
232
|
def fontcolor(label, font, color)
|
233
|
closeup container = font(label, font).closeup(colorselect(label, color))
|
234
|
container end
|
235
|
|
236
|
# 要素を1つ選択させる
|
237
|
# ==== Args
|
238
|
# [label] ラベル
|
239
|
# [config] 設定のキー
|
240
|
# [default]
|
241
|
# 連想配列で、 _値_ => _ラベル_ の形式で、デフォルト値を与える。
|
242
|
# _block_ と同時に与えれられたら、 _default_ の値が先に入って、 _block_ は後に入る。
|
243
|
# [&block] 内容
|
244
|
def select(label, config, default = {})
|
245
|
builder = Plugin::Settings::Select.new(@plugin, default)
|
246
|
builder.instance_eval(&Proc.new) if block_given?
|
247
|
closeup container = builder.build(label, config)
|
248
|
container end
|
249
|
|
250
|
# 要素を複数個選択させる
|
251
|
# ==== Args
|
252
|
# [label] ラベル
|
253
|
# [config] 設定のキー
|
254
|
# [default]
|
255
|
# 連想配列で、 _値_ => _ラベル_ の形式で、デフォルト値を与える。
|
256
|
# _block_ と同時に与えれられたら、 _default_ の値が先に入って、 _block_ は後に入る。
|
257
|
# [&block] 内容
|
258
|
def multiselect(label, config, default = {})
|
259
|
builder = Plugin::Settings::MultiSelect.new(@plugin, default)
|
260
|
builder.instance_eval(&Proc.new) if block_given?
|
261
|
closeup container = builder.build(label, config)
|
262
|
container end
|
263
|
|
264
|
private
|
265
|
def about_converter
|
266
|
Hash.new(ret_nth).merge!( :logo => lambda{ |value| Gtk::WebIcon.new(value).pixbuf rescue nil } ) end
|
267
|
memoize :about_converter
|
268
|
|
269
|
def colorselect(label, config)
|
270
|
color = Listener[config].get
|
271
|
button = Gtk::ColorButton.new((color and Gdk::Color.new(*color)))
|
272
|
button.title = label
|
273
|
button.signal_connect('color-set'){ |w|
|
274
|
Listener[config].set w.color.to_a }
|
275
|
button end
|
276
|
|
277
|
def fontselect(label, config)
|
278
|
button = Gtk::FontButton.new(Listener[config].get)
|
279
|
button.title = label
|
280
|
button.signal_connect('font-set'){ |w|
|
281
|
Listener[config].set w.font_name }
|
282
|
button end
|
283
|
|
284
|
def fsselect(label, config, current=Dir.pwd, action)
|
285
|
container = input(label, config)
|
286
|
input = container.children.last.children.first
|
287
|
button = Gtk::Button.new('参照')
|
288
|
container.pack_start(button, false)
|
289
|
button.signal_connect('clicked'){ |widget|
|
290
|
dialog = Gtk::FileChooserDialog.new("Open File",
|
291
|
widget.get_ancestor(Gtk::Window),
|
292
|
action,
|
293
|
nil,
|
294
|
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
295
|
[Gtk::Stock::OPEN, Gtk::Dialog::RESPONSE_ACCEPT])
|
296
|
dialog.current_folder = File.expand_path(current)
|
297
|
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
298
|
Listener[config].set dialog.filename
|
299
|
input.text = dialog.filename
|
300
|
end
|
301
|
dialog.destroy
|
302
|
}
|
303
|
container
|
304
|
end
|
305
|
|
306
|
def method_missing(*args, &block)
|
307
|
@plugin.__send__(*args, &block)
|
308
|
end
|
309
|
|
310
|
end
|
311
|
|
312
|
require File.expand_path File.join(File.dirname(__FILE__), 'select')
|
313
|
require File.expand_path File.join(File.dirname(__FILE__), 'multiselect')
|
314
|
require File.expand_path File.join(File.dirname(__FILE__), 'listener')
|