1/*
2 * Copyright (C) 2011 Michal Hruby <michal.mhr@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
17 *
18 * Authored by Michal Hruby <michal.mhr@gmail.com>
19 *
20 */
21
22namespace Synapse
23{
24  public class SelectionPlugin : Object, Activatable, ItemProvider
25  {
26    public bool enabled { get; set; default = true; }
27
28    private Gtk.Clipboard clipboard;
29    private SelectedTextItem item;
30
31    public void activate ()
32    {
33      item = new SelectedTextItem ();
34      clipboard = Gtk.Clipboard.get (Gdk.SELECTION_PRIMARY);
35      clipboard.owner_change.connect (this.cb_owner_change);
36    }
37
38    public void deactivate ()
39    {
40      clipboard.owner_change.disconnect (this.cb_owner_change);
41    }
42
43    bool cb_changed = true;
44
45    private void cb_owner_change ()
46    {
47      cb_changed = true;
48    }
49
50    // register your plugin in the UI
51    static void register_plugin ()
52    {
53      PluginRegistry.get_default ().register_plugin (
54        typeof (SelectionPlugin),
55        _("Selection"), // plugin title
56        _("Provides actions for currently selected text."), // description
57        "edit-select-all", // icon name
58        register_plugin // reference to this function
59      );
60    }
61
62    static construct
63    {
64      // register the plugin when the class is constructed
65      register_plugin ();
66    }
67
68    private class SelectedTextItem : TextMatch
69    {
70      public SelectedTextItem ()
71      {
72        Object (title: _("Selected text"),
73                description : "",
74                icon_name: "edit-select-all",
75                has_thumbnail: false,
76                text_origin: TextOrigin.CLIPBOARD);
77      }
78
79      public override string get_text ()
80      {
81        return content;
82      }
83
84      private string? content = null;
85
86      public void update_content (owned string content)
87      {
88        this.content = content;
89        string chugged = content.chug ();
90        string shortened;
91        if (chugged.char_count () > 100)
92          shortened = chugged.substring (0, chugged.index_of_nth_char (100));
93        else
94          shortened = chugged;
95        description = shortened.replace ("\n", " ");
96      }
97    }
98
99    public bool handles_query (Query query)
100    {
101      // we will only search in the "Actions" category
102      return (QueryFlags.ACTIONS in query.query_type);
103    }
104
105    public async ResultSet? search (Query query) throws SearchError
106    {
107      var matchers = Query.get_matchers_for_query (query.query_string,
108                                                   MatcherFlags.NO_FUZZY | MatcherFlags.NO_PARTIAL,
109                                                   RegexCompileFlags.CASELESS);
110      int relevancy = 0;
111      foreach (var matcher in matchers)
112      {
113        if (matcher.key.match (item.title))
114        {
115          relevancy = matcher.value;
116          break;
117        }
118      }
119
120      if (relevancy == 0) return null;
121      string? cb_text = null;
122
123      if (cb_changed)
124      {
125        clipboard.request_text ((cb, text) => {
126          cb_text = text;
127          search.callback ();
128        });
129      }
130      else
131      {
132        cb_text = "";
133        Idle.add (search.callback);
134      }
135      yield;
136
137      query.check_cancellable ();
138
139      if (cb_text != null)
140      {
141        if (cb_changed)
142        {
143          item.update_content ((owned) cb_text);
144          cb_changed = false;
145        }
146        var results = new ResultSet ();
147        results.add (item, relevancy);
148
149        query.check_cancellable ();
150        return results;
151      }
152
153      // make sure this method is called before returning any results
154      query.check_cancellable ();
155      return null;
156    }
157  }
158}
159