プロジェクト

全般

プロフィール

機能 #1002 » intent_selector.rb

Satoshi Okuno, 2017-03-23 23:32

 
# -*- coding: utf-8 -*-
require_relative 'listview'

Plugin.create(:intent_selector) do
UserConfig[:intent_selector_rules] ||= []

on_intent_select do |intents, model|
case model
when Retriever::Model
intent_open(intents, model: model)
else
intent_open(intents, uri: Retriever::URI!(model))
end
end

settings(_('関連付け')) do
listview = Plugin::IntentSelector::IntentSelectorListView.new
pack_start(Gtk::VBox.new(false, 4).
closeup(listview.filter_entry).
add(Gtk::HBox.new(false, 4).
add(listview).
closeup(listview.buttons(Gtk::VBox))))
end

# _model:_ または _uri:_ を開くintentを _intents_ の中から選び出し、その方法で開く。
# このメソッドは、まず設定されたルールでintentを選出し、一つにintentが定まれば直ちにそれで開く。
# 候補が一つに絞れなかった場合は、intent選択ダイアログを表示して、ユーザに決定を仰ぐ。
# ==== Args
# [intents] Intent modelの配列
# [model:] 開くModel。 _uri:_ しかわからない場合は、省略してもよい
# [uri:] 開くURI。 _model:_ を渡している場合は、省略してもよい
def intent_open(intents, model: nil, uri: model.uri)
recommended, suggested = divide_intents(intents, uri, specified_model_slug(model))
if recommended.size >= 1
Plugin::Intent::IntentToken.open(
uri: uri,
model: model,
intent: recommended.first,
parent: nil)
else
intent_choose_dialog(recommended + suggested, model: model, uri: uri)
end
end

def intent_choose_dialog(intents, model: nil, uri: model.uri)
dialog = Gtk::Dialog.new('開く - %{application_name}' % {application_name: Environment::NAME})
dialog.window_position = Gtk::Window::POS_CENTER
dialog.add_button(Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK)
dialog.add_button(Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL)
dialog.vbox.closeup(Gtk::Label.new("%{uri}\nを開こうとしています。どの方法で開きますか?" % {uri: uri}, false))
intent_token_builder = {
uri: uri,
model: model,
intent: nil,
parent: nil }
intents.inject(nil) do |group, intent|
if group
radio = Gtk::RadioButton.new(group, intent.label)
else
intent_token_builder[:intent] = intent
radio = Gtk::RadioButton.new(intent.label) end
radio.ssc(:toggled) do |w|
intent_token_builder[:intent] = intent
false
end
radio.ssc(:activate) do |w|
intent_token_builder[:intent] = intent
dialog.signal_emit(:response, Gtk::Dialog::RESPONSE_OK)
false
end
dialog.vbox.closeup(radio)
group || radio
end
saving_rule_checkbox(dialog, intent_token_builder, specified_model_slug(model))
dialog.ssc(:response) do |w, response_id|
if response_id == Gtk::Dialog::RESPONSE_OK and intent_token_builder[:intent]
Plugin::Intent::IntentToken.open(**intent_token_builder)
end
w.destroy
false
end
dialog.show_all
end

def saving_rule_checkbox(dialog, intent_token_builder, model_slug)
save_check = Gtk::CheckButton.new(_('次回から、次の内容から始まるURLはこの方法で開く'))
rule = Gtk::Entry.new.set_text(intent_token_builder[:uri].to_s)
rule.sensitive = false
save_check.ssc(:toggled) do |widget|
rule.sensitive = widget.active?
false
end
dialog.ssc(:response) do |w, response_id|
if response_id == Gtk::Dialog::RESPONSE_OK and intent_token_builder[:intent] and save_check.active?
add_intent_rule(intent: intent_token_builder[:intent],
str: rule.text,
rule: 'start',
model_slug: model_slug)
end
false
end
dialog.vbox.
closeup(save_check).
closeup(rule)
end

def add_intent_rule(intent:, str:, rule:, model_slug:)
unless UserConfig[:intent_selector_rules].any?{|r| r[:intent].to_sym == intent.slug && r[:str] == str && r[:rule] == rule }
UserConfig[:intent_selector_rules] += [{uuid: SecureRandom.uuid, intent: intent.slug, model: model_slug, str: str, rule: rule}]
end
end

# intent の配列を受け取り、ユーザが過去に入力したルールに基づき、
# recommendedとsuggestedに分ける
# ==== Args
# [intents] スキャン対象のintent
# [uri] リソースのURI
# [model_slug] 絞り込みに使うModelのslug。
# ==== Return
# 条件に対して推奨されるintentの配列と、intentsに指定されたそれ以外の値の配列
def divide_intents(intents, uri, model_slug)
# モデル + URL(URLマッチ文字列が長い順)
intent_slugs_with_model = UserConfig[:intent_selector_rules].select{|record|
model_slug == record[:model].to_s && uri.to_s.start_with?(record[:str])
}.sort {|a, b| a[:str].length <=> b[:str.length] }.reverse

# URLのみ(URLマッチ文字列が長い順)
intens_slugs_without_model = UserConfig[:intent_selector_rules].select{|record|
record[:model] == nil && uri.to_s.start_with?(record[:str])
}.sort {|a, b| a[:str].length <=> b[:str.length] }.reverse

intent_slugs = (intent_slugs_with_model + intens_slugs_without_model).map { |record| record[:intent] }

intents.partition{|intent| intent_slugs.include?(intent.slug.to_sym) }
end

# _model_ のmodel slugを文字列で得る。
# ==== Args
# [model] Retriever::Modelのインスタンス又はnil
# ==== Return
# [String] Modelのslug。 _model_ がnilだった場合は空文字列
def specified_model_slug(model)
model ? model.class.slug.to_s : ''
end

end
(1-1/2)