1
|
# -*- coding: utf-8 -*-
|
2
|
require_relative 'listview'
|
3
|
|
4
|
Plugin.create(:intent_selector) do
|
5
|
UserConfig[:intent_selector_rules] ||= []
|
6
|
|
7
|
on_intent_select do |intents, model|
|
8
|
case model
|
9
|
when Retriever::Model
|
10
|
intent_open(intents, model: model)
|
11
|
else
|
12
|
intent_open(intents, uri: Retriever::URI!(model))
|
13
|
end
|
14
|
end
|
15
|
|
16
|
settings(_('関連付け')) do
|
17
|
listview = Plugin::IntentSelector::IntentSelectorListView.new
|
18
|
pack_start(Gtk::VBox.new(false, 4).
|
19
|
closeup(listview.filter_entry).
|
20
|
add(Gtk::HBox.new(false, 4).
|
21
|
add(listview).
|
22
|
closeup(listview.buttons(Gtk::VBox))))
|
23
|
end
|
24
|
|
25
|
# _model:_ または _uri:_ を開くintentを _intents_ の中から選び出し、その方法で開く。
|
26
|
# このメソッドは、まず設定されたルールでintentを選出し、一つにintentが定まれば直ちにそれで開く。
|
27
|
# 候補が一つに絞れなかった場合は、intent選択ダイアログを表示して、ユーザに決定を仰ぐ。
|
28
|
# ==== Args
|
29
|
# [intents] Intent modelの配列
|
30
|
# [model:] 開くModel。 _uri:_ しかわからない場合は、省略してもよい
|
31
|
# [uri:] 開くURI。 _model:_ を渡している場合は、省略してもよい
|
32
|
def intent_open(intents, model: nil, uri: model.uri)
|
33
|
recommended, suggested = divide_intents(intents, uri, specified_model_slug(model))
|
34
|
if recommended.size >= 1
|
35
|
Plugin::Intent::IntentToken.open(
|
36
|
uri: uri,
|
37
|
model: model,
|
38
|
intent: recommended.first,
|
39
|
parent: nil)
|
40
|
else
|
41
|
intent_choose_dialog(recommended + suggested, model: model, uri: uri)
|
42
|
end
|
43
|
end
|
44
|
|
45
|
def intent_choose_dialog(intents, model: nil, uri: model.uri)
|
46
|
dialog = Gtk::Dialog.new('開く - %{application_name}' % {application_name: Environment::NAME})
|
47
|
dialog.window_position = Gtk::Window::POS_CENTER
|
48
|
dialog.add_button(Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK)
|
49
|
dialog.add_button(Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL)
|
50
|
dialog.vbox.closeup(Gtk::Label.new("%{uri}\nを開こうとしています。どの方法で開きますか?" % {uri: uri}, false))
|
51
|
intent_token_builder = {
|
52
|
uri: uri,
|
53
|
model: model,
|
54
|
intent: nil,
|
55
|
parent: nil }
|
56
|
intents.inject(nil) do |group, intent|
|
57
|
if group
|
58
|
radio = Gtk::RadioButton.new(group, intent.label)
|
59
|
else
|
60
|
intent_token_builder[:intent] = intent
|
61
|
radio = Gtk::RadioButton.new(intent.label) end
|
62
|
radio.ssc(:toggled) do |w|
|
63
|
intent_token_builder[:intent] = intent
|
64
|
false
|
65
|
end
|
66
|
radio.ssc(:activate) do |w|
|
67
|
intent_token_builder[:intent] = intent
|
68
|
dialog.signal_emit(:response, Gtk::Dialog::RESPONSE_OK)
|
69
|
false
|
70
|
end
|
71
|
dialog.vbox.closeup(radio)
|
72
|
group || radio
|
73
|
end
|
74
|
saving_rule_checkbox(dialog, intent_token_builder, specified_model_slug(model))
|
75
|
dialog.ssc(:response) do |w, response_id|
|
76
|
if response_id == Gtk::Dialog::RESPONSE_OK and intent_token_builder[:intent]
|
77
|
Plugin::Intent::IntentToken.open(**intent_token_builder)
|
78
|
end
|
79
|
w.destroy
|
80
|
false
|
81
|
end
|
82
|
dialog.show_all
|
83
|
end
|
84
|
|
85
|
def saving_rule_checkbox(dialog, intent_token_builder, model_slug)
|
86
|
save_check = Gtk::CheckButton.new(_('次回から、次の内容から始まるURLはこの方法で開く'))
|
87
|
rule = Gtk::Entry.new.set_text(intent_token_builder[:uri].to_s)
|
88
|
rule.sensitive = false
|
89
|
save_check.ssc(:toggled) do |widget|
|
90
|
rule.sensitive = widget.active?
|
91
|
false
|
92
|
end
|
93
|
dialog.ssc(:response) do |w, response_id|
|
94
|
if response_id == Gtk::Dialog::RESPONSE_OK and intent_token_builder[:intent] and save_check.active?
|
95
|
add_intent_rule(intent: intent_token_builder[:intent],
|
96
|
str: rule.text,
|
97
|
rule: 'start',
|
98
|
model_slug: model_slug)
|
99
|
end
|
100
|
false
|
101
|
end
|
102
|
dialog.vbox.
|
103
|
closeup(save_check).
|
104
|
closeup(rule)
|
105
|
end
|
106
|
|
107
|
def add_intent_rule(intent:, str:, rule:, model_slug:)
|
108
|
unless UserConfig[:intent_selector_rules].any?{|r| r[:intent].to_sym == intent.slug && r[:str] == str && r[:rule] == rule }
|
109
|
UserConfig[:intent_selector_rules] += [{uuid: SecureRandom.uuid, intent: intent.slug, model: model_slug, str: str, rule: rule}]
|
110
|
end
|
111
|
end
|
112
|
|
113
|
# intent の配列を受け取り、ユーザが過去に入力したルールに基づき、
|
114
|
# recommendedとsuggestedに分ける
|
115
|
# ==== Args
|
116
|
# [intents] スキャン対象のintent
|
117
|
# [uri] リソースのURI
|
118
|
# [model_slug] 絞り込みに使うModelのslug。
|
119
|
# ==== Return
|
120
|
# 条件に対して推奨されるintentの配列と、intentsに指定されたそれ以外の値の配列
|
121
|
def divide_intents(intents, uri, model_slug)
|
122
|
# モデル + URL(URLマッチ文字列が長い順)
|
123
|
intent_slugs_with_model = UserConfig[:intent_selector_rules].select{|record|
|
124
|
model_slug == record[:model].to_s && uri.to_s.start_with?(record[:str])
|
125
|
}.sort {|a, b| a[:str].length <=> b[:str.length] }.reverse
|
126
|
|
127
|
# URLのみ(URLマッチ文字列が長い順)
|
128
|
intens_slugs_without_model = UserConfig[:intent_selector_rules].select{|record|
|
129
|
record[:model] == nil && uri.to_s.start_with?(record[:str])
|
130
|
}.sort {|a, b| a[:str].length <=> b[:str.length] }.reverse
|
131
|
|
132
|
intent_slugs = (intent_slugs_with_model + intens_slugs_without_model).map { |record| record[:intent] }
|
133
|
|
134
|
intents.partition{|intent| intent_slugs.include?(intent.slug.to_sym) }
|
135
|
end
|
136
|
|
137
|
# _model_ のmodel slugを文字列で得る。
|
138
|
# ==== Args
|
139
|
# [model] Retriever::Modelのインスタンス又はnil
|
140
|
# ==== Return
|
141
|
# [String] Modelのslug。 _model_ がnilだった場合は空文字列
|
142
|
def specified_model_slug(model)
|
143
|
model ? model.class.slug.to_s : ''
|
144
|
end
|
145
|
|
146
|
end
|