プロジェクト

全般

プロフィール

機能 #1002 » intent_selector.rb

Satoshi Okuno, 2017-03-23 23:32

 
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
(1-1/2)