clipcellar 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +502 -0
- data/NEWS.md +5 -0
- data/README.md +70 -0
- data/Rakefile +25 -0
- data/bin/clipcellar +21 -0
- data/clipcellar.gemspec +30 -0
- data/lib/clipcellar.rb +23 -0
- data/lib/clipcellar/clipboard.rb +82 -0
- data/lib/clipcellar/command.rb +158 -0
- data/lib/clipcellar/groonga_database.rb +100 -0
- data/lib/clipcellar/groonga_searcher.rb +56 -0
- data/lib/clipcellar/tree_view.rb +108 -0
- data/lib/clipcellar/version.rb +19 -0
- data/lib/clipcellar/window.rb +121 -0
- data/test/run-test.rb +27 -0
- data/test/test-command.rb +53 -0
- metadata +178 -0
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# Clipcellar
|
2
|
+
|
3
|
+
Clipcellar is a full-text searchable data store for clipboard.
|
4
|
+
|
5
|
+
Powered by [GTK+][] (via [Ruby/GTK2][]) and [Groonga][] (via [Rroonga][]).
|
6
|
+
|
7
|
+
[GTK+]:http://www.gtk.org/
|
8
|
+
[Ruby/GTK2]:http://ruby-gnome2.sourceforge.jp/
|
9
|
+
[Groonga]:http://groonga.org/
|
10
|
+
[Rroonga]:http://ranguba.org/
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
gem 'clipcellar'
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install clipcellar
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
### Show help
|
29
|
+
|
30
|
+
$ clipcellar
|
31
|
+
|
32
|
+
### Add a text (or clipboard) to data store
|
33
|
+
|
34
|
+
$ clipcellar set [TEXT]
|
35
|
+
|
36
|
+
### Add a text from files (or stdin)
|
37
|
+
|
38
|
+
$ clipcellar input [FILE]...
|
39
|
+
|
40
|
+
### Show added texts in data store
|
41
|
+
|
42
|
+
$ clipcellar show [--gui]
|
43
|
+
|
44
|
+
### Full-text search (and set a text to clipboard)
|
45
|
+
|
46
|
+
$ clipcellar search WORD... [--gui]
|
47
|
+
|
48
|
+
### Watch clipboard (Ctrl+C to stop)
|
49
|
+
|
50
|
+
$ clipcellar watch
|
51
|
+
|
52
|
+
### Delete data store and all added texts
|
53
|
+
|
54
|
+
$ clipcellar destroy
|
55
|
+
|
56
|
+
## License
|
57
|
+
|
58
|
+
Copyright (c) 2014 Masafumi Yokoyama `<myokoym@gmail.com>`
|
59
|
+
|
60
|
+
LGPLv2.1 or later.
|
61
|
+
|
62
|
+
See LICENSE.txt or http://www.gnu.org/licenses/lgpl-2.1 for details.
|
63
|
+
|
64
|
+
## Contributing
|
65
|
+
|
66
|
+
1. Fork it
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "bundler/gem_tasks"
|
18
|
+
|
19
|
+
desc "Run test"
|
20
|
+
task :test do
|
21
|
+
Bundler::GemHelper.install_tasks
|
22
|
+
ruby("test/run-test.rb")
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => :test
|
data/bin/clipcellar
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License as published by the Free Software Foundation; either
|
8
|
+
# version 2.1 of the License, or (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This library is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
# Lesser General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU Lesser General Public
|
16
|
+
# License along with this library; if not, write to the Free Software
|
17
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
|
19
|
+
require "clipcellar/command"
|
20
|
+
|
21
|
+
Clipcellar::Command.start
|
data/clipcellar.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'clipcellar/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "clipcellar"
|
8
|
+
spec.version = Clipcellar::VERSION
|
9
|
+
spec.authors = ["Masafumi Yokoyama"]
|
10
|
+
spec.email = ["myokoym@gmail.com"]
|
11
|
+
spec.summary = %q{Full-Text Searchable Data Store for Clipboard}
|
12
|
+
spec.description = %q{Clipcellar is a full-text searchable data store for clipboard by GTK+ (via Ruby/GTK2) and Groonga (via Rroonga).}
|
13
|
+
spec.homepage = "https://github.com/myokoym/clipcellar"
|
14
|
+
spec.license = "LGPLv2.1 or later"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) {|f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency("gtk2")
|
22
|
+
spec.add_runtime_dependency("rroonga")
|
23
|
+
spec.add_runtime_dependency("thor")
|
24
|
+
|
25
|
+
spec.add_development_dependency("test-unit")
|
26
|
+
spec.add_development_dependency("test-unit-notify")
|
27
|
+
spec.add_development_dependency("test-unit-rr")
|
28
|
+
spec.add_development_dependency("bundler", "~> 1.6")
|
29
|
+
spec.add_development_dependency("rake")
|
30
|
+
end
|
data/lib/clipcellar.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "clipcellar/version"
|
18
|
+
require "clipcellar/groonga_database"
|
19
|
+
require "clipcellar/groonga_searcher"
|
20
|
+
require "clipcellar/clipboard"
|
21
|
+
|
22
|
+
module Clipcellar
|
23
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
begin
|
18
|
+
require "gtk2"
|
19
|
+
rescue Gtk::InitError
|
20
|
+
# a window system is unnecessary.
|
21
|
+
end
|
22
|
+
|
23
|
+
module Clipcellar
|
24
|
+
class Clipboard
|
25
|
+
class << self
|
26
|
+
def watch
|
27
|
+
current_text = get
|
28
|
+
GLib::Timeout.add(500) do
|
29
|
+
text = get
|
30
|
+
if current_text != text
|
31
|
+
yield(text)
|
32
|
+
current_text = text
|
33
|
+
end
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
Gtk.main
|
39
|
+
rescue Interrupt => e
|
40
|
+
$stderr.puts(e.message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def get
|
45
|
+
if /darwin/ =~ RUBY_PLATFORM
|
46
|
+
`pbpaste`
|
47
|
+
else
|
48
|
+
clipboard.wait_for_text
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def set(text)
|
53
|
+
copy_to_clipboard(text)
|
54
|
+
|
55
|
+
# workaround for CLI
|
56
|
+
GLib::Timeout.add(1) do
|
57
|
+
Gtk.main_quit
|
58
|
+
end
|
59
|
+
Gtk.main
|
60
|
+
end
|
61
|
+
|
62
|
+
def copy_to_clipboard(text)
|
63
|
+
if /darwin/ =~ RUBY_PLATFORM
|
64
|
+
require "tempfile"
|
65
|
+
Tempfile.open(["clipcellar", "w"]) do |file|
|
66
|
+
text.each_line do |line|
|
67
|
+
file.puts(line)
|
68
|
+
end
|
69
|
+
file.flush
|
70
|
+
system("pbcopy < #{file.path}")
|
71
|
+
end
|
72
|
+
else
|
73
|
+
clipboard.text = text
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def clipboard
|
78
|
+
Gtk::Clipboard.get(Gdk::Selection::CLIPBOARD)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "thor"
|
18
|
+
require "fileutils"
|
19
|
+
require "clipcellar/version"
|
20
|
+
require "clipcellar/groonga_database"
|
21
|
+
require "clipcellar/groonga_searcher"
|
22
|
+
require "clipcellar/clipboard"
|
23
|
+
require "clipcellar/window"
|
24
|
+
|
25
|
+
module Clipcellar
|
26
|
+
class Command < Thor
|
27
|
+
map "-v" => :version
|
28
|
+
map "-a" => :set
|
29
|
+
map "-A" => :input
|
30
|
+
map "-s" => :show
|
31
|
+
map "-S" => :search
|
32
|
+
map "-W" => :watch
|
33
|
+
|
34
|
+
attr_reader :database_dir
|
35
|
+
|
36
|
+
def initialize(*args)
|
37
|
+
super
|
38
|
+
@base_dir = File.join(File.expand_path("~"), ".clipcellar")
|
39
|
+
@database_dir = File.join(@base_dir, "db")
|
40
|
+
end
|
41
|
+
|
42
|
+
desc "version", "Show version number."
|
43
|
+
def version
|
44
|
+
puts Clipcellar::VERSION
|
45
|
+
end
|
46
|
+
|
47
|
+
desc "set [TEXT]", "Add a text (or clipboard) to data store"
|
48
|
+
def set(text=nil)
|
49
|
+
text = Clipboard.get unless text
|
50
|
+
return unless text
|
51
|
+
Clipboard.set(text)
|
52
|
+
add(text)
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "input [FILE]...", "Add a text from files (or stdin)"
|
56
|
+
def input(*files)
|
57
|
+
ARGV.shift
|
58
|
+
text = ""
|
59
|
+
while line = ARGF.gets
|
60
|
+
text << line
|
61
|
+
end
|
62
|
+
Clipboard.set(text)
|
63
|
+
add(text)
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "watch", "Watch clipboard to add clipboard to data store"
|
67
|
+
def watch
|
68
|
+
$stderr.puts("Watching... (Ctrl+C to Stop)")
|
69
|
+
Clipboard.watch do |text|
|
70
|
+
add(text)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
desc "show [--gui]", "Show added texts in data store"
|
75
|
+
option :gui, :type => :boolean, :desc => "GUI mode"
|
76
|
+
def show
|
77
|
+
records = []
|
78
|
+
GroongaDatabase.new.open(@database_dir) do |database|
|
79
|
+
database.clipboards.each do |clipboard|
|
80
|
+
record = {}
|
81
|
+
record[:key] = clipboard._key
|
82
|
+
record[:text] = clipboard.text
|
83
|
+
record[:time] = clipboard.created_at
|
84
|
+
records << record
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
records.sort_by! do |record|
|
89
|
+
record[:time]
|
90
|
+
end
|
91
|
+
|
92
|
+
if options[:gui]
|
93
|
+
records.reverse!
|
94
|
+
window = Window.new(records)
|
95
|
+
window.run
|
96
|
+
else
|
97
|
+
records.each do |record|
|
98
|
+
text = record[:text]
|
99
|
+
time = record[:time].strftime("%Y-%m-%d %H:%M:%S")
|
100
|
+
formatted_text = text.gsub(/\n/, " ") if text
|
101
|
+
puts "#{time} #{formatted_text}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "search WORD... [--gui]", "Search texts from data store"
|
107
|
+
option :gui, :type => :boolean, :desc => "GUI mode"
|
108
|
+
def search(required_word, *optional_words)
|
109
|
+
words = [required_word]
|
110
|
+
words += optional_words
|
111
|
+
|
112
|
+
records = []
|
113
|
+
GroongaDatabase.new.open(@database_dir) do |database|
|
114
|
+
reverse = options[:gui] ? true : false
|
115
|
+
sorted_clipboards = GroongaSearcher.search(database,
|
116
|
+
words,
|
117
|
+
:reverse => reverse)
|
118
|
+
|
119
|
+
sorted_clipboards.each do |clipboard|
|
120
|
+
record = {}
|
121
|
+
record[:key] = clipboard._key
|
122
|
+
record[:text] = clipboard.text
|
123
|
+
record[:time] = clipboard.created_at
|
124
|
+
records << record
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
if options[:gui]
|
129
|
+
window = Window.new(records)
|
130
|
+
window.run
|
131
|
+
else
|
132
|
+
text = nil
|
133
|
+
records.each do |record|
|
134
|
+
text = record[:text]
|
135
|
+
time = record[:time].strftime("%Y-%m-%d %H:%M:%S")
|
136
|
+
formatted_text = text.gsub(/\n/, " ") if text
|
137
|
+
puts "#{time} #{formatted_text}"
|
138
|
+
end
|
139
|
+
Clipboard.set(text) if text
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
desc "destroy", "Delete data store and all added texts"
|
144
|
+
def destroy
|
145
|
+
FileUtils.rm(Dir.glob(File.join(@database_dir, "clipcellar.db*")))
|
146
|
+
FileUtils.rmdir(@database_dir)
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def add(text)
|
151
|
+
time = Time.now
|
152
|
+
id = time.strftime("%Y%m%d%H%M%S%L")
|
153
|
+
GroongaDatabase.new.open(@database_dir) do |database|
|
154
|
+
database.add(id, text, time)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# Copyright (C) 2014 Masafumi Yokoyama <myokoym@gmail.com>
|
2
|
+
#
|
3
|
+
# This library is free software; you can redistribute it and/or
|
4
|
+
# modify it under the terms of the GNU Lesser General Public
|
5
|
+
# License as published by the Free Software Foundation; either
|
6
|
+
# version 2.1 of the License, or (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This library is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
11
|
+
# Lesser General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Lesser General Public
|
14
|
+
# License along with this library; if not, write to the Free Software
|
15
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
16
|
+
|
17
|
+
require "groonga"
|
18
|
+
|
19
|
+
module Clipcellar
|
20
|
+
class GroongaDatabase
|
21
|
+
def initialize
|
22
|
+
@database = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def open(base_path, encoding=:utf8)
|
26
|
+
reset_context(encoding)
|
27
|
+
path = File.join(base_path, "clipcellar.db")
|
28
|
+
if File.exist?(path)
|
29
|
+
@database = Groonga::Database.open(path)
|
30
|
+
populate_schema
|
31
|
+
else
|
32
|
+
FileUtils.mkdir_p(base_path)
|
33
|
+
populate(path)
|
34
|
+
end
|
35
|
+
if block_given?
|
36
|
+
begin
|
37
|
+
yield(self)
|
38
|
+
ensure
|
39
|
+
close unless closed?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add(id, text, time)
|
45
|
+
clipboards.add(id,
|
46
|
+
{
|
47
|
+
:text => text,
|
48
|
+
:created_at => time,
|
49
|
+
})
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete(id)
|
53
|
+
clipboards.delete(id)
|
54
|
+
end
|
55
|
+
|
56
|
+
def close
|
57
|
+
@database.close
|
58
|
+
@database = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def closed?
|
62
|
+
@database.nil? or @database.closed?
|
63
|
+
end
|
64
|
+
|
65
|
+
def clipboards
|
66
|
+
@clipboards ||= Groonga["Clipboards"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def dump
|
70
|
+
Groonga::DatabaseDumper.dump
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def reset_context(encoding)
|
75
|
+
Groonga::Context.default_options = {:encoding => encoding}
|
76
|
+
Groonga::Context.default = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def populate(path)
|
80
|
+
@database = Groonga::Database.create(:path => path)
|
81
|
+
populate_schema
|
82
|
+
end
|
83
|
+
|
84
|
+
def populate_schema
|
85
|
+
Groonga::Schema.define do |schema|
|
86
|
+
schema.create_table("Clipboards", :type => :hash) do |table|
|
87
|
+
table.text("text")
|
88
|
+
table.time("created_at")
|
89
|
+
end
|
90
|
+
|
91
|
+
schema.create_table("Terms",
|
92
|
+
:type => :patricia_trie,
|
93
|
+
:key_normalize => true,
|
94
|
+
:default_tokenizer => "TokenBigram") do |table|
|
95
|
+
table.index("Clipboards.text")
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|