aboutsummaryrefslogtreecommitdiffstats
path: root/core/plugin/intent_selector/intent_selector.rb
blob: ffb5a8d69134c2dce335f68abc8219248511027a (plain)
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
# -*- coding: utf-8 -*-

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)
    when URI
      intent_open(intents, uri: model)
    when String
      intent_open(intents, uri: URI.parse(model))
    end
  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] += [{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)
    intent_slugs = UserConfig[:intent_selector_rules].select{|record|
      model_slug == record[:model].to_s && uri.to_s.start_with?(record[:str])
    }.map{|record|
      record[:intent].to_sym
    }
    intents.partition{|intent| intent_slugs.include?(intent.slug) }
  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