iconify 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +3 -3
- data/bin/console +3 -3
- data/iconify.gemspec +13 -13
- data/lib/iconify.rb +198 -87
- data/lib/iconify/program.rb +70 -47
- data/lib/iconify/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac171efe65169c0ac77d43eb220ade5922c2fabe
|
4
|
+
data.tar.gz: b1b5d585fe38a96c5fef0000293e720c3ea098f8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01c795f2a4c8ddce60a0b78d03cb0b6b765ad13d506760d465fd5ed6042b936ca473a35adb9b0319c45692a1d285573525d0cda233dd29786b61f9fbce57bf61
|
7
|
+
data.tar.gz: cd8c6ca45d9ef94eea9916aa3e6bce3bd23f932ff5a5daebacd3fc422d25c60f3ad28091ca98f571521933cabe16eaf0ca61143b28526fc253d1a758d82f08a8
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'iconify'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "iconify"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
data/iconify.gemspec
CHANGED
@@ -4,25 +4,25 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'iconify/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'iconify'
|
8
8
|
spec.version = Iconify::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Yoteichi']
|
10
|
+
spec.email = ['plonk@piano.email.ne.jp']
|
11
11
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
14
|
-
spec.homepage =
|
12
|
+
spec.summary = 'Iconify is a utility program to turn a command line into a status icon.'
|
13
|
+
spec.description = 'Iconify is a utility program to turn a command line into a status icon.'
|
14
|
+
spec.homepage = 'https://github.com/plonk/iconifiy'
|
15
15
|
spec.licenses = ['GPL-2']
|
16
16
|
|
17
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
-
spec.bindir =
|
18
|
+
spec.bindir = 'exe'
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
21
21
|
|
22
|
-
spec.add_dependency
|
23
|
-
spec.add_dependency
|
22
|
+
spec.add_dependency 'gtk3'
|
23
|
+
spec.add_dependency 'vte3'
|
24
24
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
25
|
+
spec.add_development_dependency 'bundler', '~> 1.11'
|
26
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
28
28
|
end
|
data/lib/iconify.rb
CHANGED
@@ -1,130 +1,241 @@
|
|
1
1
|
require 'gtk3'
|
2
2
|
require 'vte3'
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require 'iconify/version'
|
5
|
+
require 'iconify/program'
|
6
6
|
|
7
7
|
module Iconify
|
8
|
+
# メインウィンドウ
|
9
|
+
class TerminalWindow < Gtk::Window
|
10
|
+
include Gtk
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
type_register
|
13
|
+
signal_new('changed', GLib::Signal::ACTION, nil, nil)
|
11
14
|
|
12
|
-
|
13
|
-
signal_new('changed', GLib::Signal::ACTION, nil, nil)
|
15
|
+
attr_reader :state
|
14
16
|
|
15
|
-
|
17
|
+
# [String]
|
18
|
+
def initialize(argv)
|
19
|
+
super()
|
16
20
|
|
17
|
-
|
18
|
-
|
21
|
+
@argv = argv
|
22
|
+
@state = :stopped
|
19
23
|
|
20
|
-
|
24
|
+
update_title
|
21
25
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
+
@rerun_button = create_rerun_button
|
27
|
+
@kill_button = create_kill_button
|
28
|
+
@quit_button = create_quit_button
|
29
|
+
@terminal = create_vte_terminal
|
30
|
+
@copy_button = create_copy_button
|
31
|
+
@paste_button = create_paste_button
|
26
32
|
|
27
|
-
|
33
|
+
layout
|
28
34
|
|
29
|
-
|
35
|
+
set_geometry_hints
|
36
|
+
end
|
30
37
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
rerun_button.signal_connect('clicked') do
|
35
|
-
self.exec
|
38
|
+
def set_geometry_hints
|
39
|
+
@terminal.realize
|
40
|
+
@terminal.set_geometry_hints_for_window(self)
|
36
41
|
end
|
37
|
-
|
38
|
-
|
42
|
+
|
43
|
+
def layout
|
44
|
+
vbox = Box.new(:vertical)
|
45
|
+
toolbar = Toolbar.new
|
46
|
+
|
47
|
+
toolbar.add(@rerun_button)
|
48
|
+
toolbar.add(@kill_button)
|
49
|
+
toolbar.add(SeparatorToolItem.new)
|
50
|
+
toolbar.add(@copy_button)
|
51
|
+
toolbar.add(@paste_button)
|
52
|
+
toolbar.add(SeparatorToolItem.new)
|
53
|
+
toolbar.add(@quit_button)
|
54
|
+
vbox.pack_start(toolbar, expand: false)
|
55
|
+
|
56
|
+
padding_box = Box.new(:vertical)
|
57
|
+
padding_box.pack_start(@terminal, expand: true, fill: true)
|
58
|
+
padding_box.border_width = 18
|
59
|
+
override_background_color(StateFlags::NORMAL, COLORS[15])
|
60
|
+
vbox.pack_start(padding_box, expand: true, fill: true)
|
61
|
+
|
62
|
+
add vbox
|
39
63
|
end
|
40
64
|
|
41
|
-
|
42
|
-
|
43
|
-
|
65
|
+
def create_copy_button
|
66
|
+
ToolButton.new(stock_id: Stock::COPY).tap do |b|
|
67
|
+
b.tooltip_text = 'Copy'
|
68
|
+
b.signal_connect('clicked') do
|
69
|
+
@terminal.copy_clipboard
|
70
|
+
end
|
71
|
+
end
|
44
72
|
end
|
45
|
-
|
46
|
-
|
73
|
+
|
74
|
+
def create_paste_button
|
75
|
+
ToolButton.new(stock_id: Stock::PASTE).tap do |b|
|
76
|
+
b.tooltip_text = 'Paste'
|
77
|
+
b.signal_connect('clicked') do
|
78
|
+
@terminal.paste_clipboard
|
79
|
+
end
|
80
|
+
end
|
47
81
|
end
|
48
82
|
|
49
|
-
|
50
|
-
|
51
|
-
|
83
|
+
def create_kill_button
|
84
|
+
ToolButton.new(label: 'Kill').tap do |b|
|
85
|
+
b.icon_name = Stock::STOP
|
86
|
+
b.tooltip_text = 'Stop the child process by sending the QUIT signal.'
|
87
|
+
b.signal_connect('clicked') do
|
88
|
+
Process.kill('KILL', @pid) if @pid
|
89
|
+
end
|
90
|
+
end
|
52
91
|
end
|
53
|
-
hbox.pack_start(rerun_button)
|
54
|
-
hbox.pack_start(kill_button)
|
55
|
-
hbox.pack_start(quit_button)
|
56
|
-
vbox.pack_start(hbox, expand: false)
|
57
|
-
vbox.pack_start(@terminal, expand: true, fill: true)
|
58
92
|
|
59
|
-
|
93
|
+
def create_rerun_button
|
94
|
+
ToolButton.new(label: 'Rerun').tap do |b|
|
95
|
+
b.icon_name = Stock::REFRESH
|
96
|
+
b.tooltip_text = 'Rerun the program.'
|
97
|
+
b.signal_connect('clicked') do
|
98
|
+
exec
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
60
102
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
103
|
+
def create_quit_button
|
104
|
+
ToolButton.new(label: 'Quit').tap do |b|
|
105
|
+
b.icon_name = Stock::QUIT
|
106
|
+
b.tooltip_text = 'Stop the program and quit.'
|
107
|
+
b.signal_connect('clicked') do
|
108
|
+
Gtk.main_quit
|
109
|
+
end
|
110
|
+
end
|
66
111
|
end
|
67
|
-
end
|
68
112
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
signal_emit('changed')
|
73
|
-
end
|
113
|
+
def update_title
|
114
|
+
self.title = "iconify - #{@argv[0]}"
|
115
|
+
end
|
74
116
|
|
75
|
-
|
117
|
+
def changed
|
118
|
+
@rerun_button.sensitive = (@state == :stopped)
|
119
|
+
@kill_button.sensitive = (@state == :running)
|
76
120
|
|
77
|
-
|
78
|
-
include Gtk
|
121
|
+
@copy_button.sensitive = @terminal.has_selection?
|
79
122
|
|
80
|
-
|
81
|
-
|
123
|
+
signal_emit('changed')
|
124
|
+
end
|
82
125
|
|
83
|
-
|
84
|
-
|
126
|
+
# the shimbun color scheme
|
127
|
+
COLORS = [[0x30, 0x30, 0x30],
|
128
|
+
[0xbe, 0x11, 0x37],
|
129
|
+
[0x29, 0x73, 0x2c],
|
130
|
+
[0xc9, 0x5c, 0x26],
|
131
|
+
[0x2a, 0x5a, 0xa2],
|
132
|
+
[0xcd, 0x3a, 0x93],
|
133
|
+
[0x07, 0x86, 0x92],
|
134
|
+
[0xd0, 0xd0, 0xd0],
|
135
|
+
[0x50, 0x50, 0x50],
|
136
|
+
[0xe6, 0x2b, 0x5d],
|
137
|
+
[0x40, 0x9e, 0x01],
|
138
|
+
[0xec, 0x75, 0x42],
|
139
|
+
[0x17, 0x7f, 0xe0],
|
140
|
+
[0xe9, 0x53, 0xba],
|
141
|
+
[0x00, 0xa9, 0xb2],
|
142
|
+
[0xf2, 0xf2, 0xf2]]
|
143
|
+
.map { |rgb| Gdk::RGBA.new(*rgb.map { |n| n.fdiv(255) }, 1.0) }
|
144
|
+
|
145
|
+
def create_vte_terminal
|
146
|
+
Vte::Terminal.new.tap do |t|
|
147
|
+
t.font = Pango::FontDescription.new('monospace 14')
|
148
|
+
t.set_size_request(t.char_width * 40, t.char_height * 12)
|
149
|
+
t.set_size(80, 24)
|
150
|
+
t.cursor_blink_mode = Vte::CursorBlinkMode::OFF
|
151
|
+
t.set_colors(COLORS[0], COLORS[15], COLORS)
|
152
|
+
|
153
|
+
t.signal_connect('child-exited') do
|
154
|
+
on_child_exited
|
155
|
+
end
|
156
|
+
t.signal_connect('selection-changed') do
|
157
|
+
changed
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
85
161
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
@
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
162
|
+
def on_child_exited
|
163
|
+
@state = :stopped
|
164
|
+
@pid = nil
|
165
|
+
@rerun_button.sensitive = true
|
166
|
+
changed
|
167
|
+
end
|
168
|
+
|
169
|
+
def exec
|
170
|
+
@pid = @terminal.spawn(argv: @argv)
|
171
|
+
@state = :running
|
172
|
+
changed
|
173
|
+
end
|
98
174
|
end
|
99
175
|
|
100
|
-
|
101
|
-
|
176
|
+
# プログラム名を表示するステータスアイコン
|
177
|
+
class CommandStatusIcon < Gtk::StatusIcon
|
178
|
+
include Gtk
|
179
|
+
include Cairo
|
180
|
+
|
181
|
+
def initialize(name)
|
182
|
+
super()
|
102
183
|
|
103
|
-
|
184
|
+
@name = name
|
185
|
+
end
|
104
186
|
|
105
|
-
|
187
|
+
COLOR_SCHEME = {
|
188
|
+
running: [[0.67, 1.0, 0.0], [0.1, 0.1, 0.1]],
|
189
|
+
stopped: [[0.75, 0.0, 0.0], [0.9, 0.9, 0.9]]
|
190
|
+
}.freeze
|
106
191
|
|
107
|
-
|
108
|
-
|
109
|
-
cr.paint
|
192
|
+
def update(state)
|
193
|
+
raise "unknown sate #{state}" unless COLOR_SCHEME.key?(state)
|
110
194
|
|
111
|
-
|
112
|
-
|
113
|
-
|
195
|
+
@background_color, @foreground_color = COLOR_SCHEME[state]
|
196
|
+
redraw
|
197
|
+
end
|
114
198
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
199
|
+
def using(destroyable)
|
200
|
+
yield destroyable
|
201
|
+
destroyable.destroy
|
202
|
+
end
|
119
203
|
|
120
|
-
cr
|
204
|
+
def paint_background(cr)
|
205
|
+
cr.save do
|
206
|
+
cr.set_source_rgb(0.8, 0.8, 0.8)
|
207
|
+
cr.set_operator(OPERATOR_SOURCE)
|
208
|
+
cr.paint
|
209
|
+
end
|
210
|
+
end
|
121
211
|
|
122
|
-
|
123
|
-
|
212
|
+
def fill_rounded_rectangle(cr)
|
213
|
+
cr.save do
|
214
|
+
cr.set_source_rgb(*@background_color)
|
215
|
+
cr.rounded_rectangle(1, 1, 62, 62, 15)
|
216
|
+
cr.fill
|
217
|
+
end
|
218
|
+
end
|
124
219
|
|
125
|
-
|
126
|
-
|
220
|
+
def draw_name(cr)
|
221
|
+
cr.save do
|
222
|
+
cr.set_font_size(24)
|
223
|
+
cr.move_to(3, 64 / 2 + cr.font_extents.ascent / 2)
|
224
|
+
cr.set_source_rgba(*@foreground_color, 1)
|
225
|
+
cr.show_text(@name)
|
226
|
+
end
|
227
|
+
end
|
127
228
|
|
128
|
-
|
229
|
+
def redraw
|
230
|
+
using ImageSurface.new(FORMAT_ARGB32, 64, 64) do |surface|
|
231
|
+
using Context.new(surface) do |cr|
|
232
|
+
paint_background(cr)
|
233
|
+
fill_rounded_rectangle(cr)
|
234
|
+
draw_name(cr)
|
235
|
+
end
|
129
236
|
|
237
|
+
self.pixbuf = surface.to_pixbuf(0, 0, 64, 64)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
130
241
|
end
|
data/lib/iconify/program.rb
CHANGED
@@ -1,59 +1,82 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
1
3
|
module Iconify
|
4
|
+
class Program
|
5
|
+
include Gtk
|
2
6
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
def initialize(argv)
|
8
|
+
@start_minimized = false
|
9
|
+
|
10
|
+
parse_args!(argv)
|
11
|
+
|
12
|
+
@argv = argv
|
13
|
+
@status_icon = CommandStatusIcon.new(argv[0])
|
14
|
+
@terminal_window = TerminalWindow.new(argv)
|
15
|
+
@terminal_window.signal_connect('delete-event') do
|
16
|
+
if @status_icon.embedded?
|
17
|
+
@terminal_window.hide
|
18
|
+
else
|
19
|
+
run_dialog('The status icon is not embedded in a notification area. The window cannot be hidden.')
|
20
|
+
end
|
21
|
+
true # do not close
|
17
22
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@terminal_window.
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
@terminal_window.signal_connect('changed') do
|
24
|
+
@status_icon.update(@terminal_window.state)
|
25
|
+
@terminal_window.icon = @status_icon.pixbuf
|
26
|
+
end
|
27
|
+
@terminal_window.show_all
|
28
|
+
GLib::Timeout.add(500) do
|
29
|
+
if @start_minimized
|
30
|
+
if @status_icon.embedded?
|
31
|
+
@terminal_window.hide
|
32
|
+
else
|
33
|
+
run_dialog('Iconify has detected its status icon is not embedded in a notification area. The window cannot be hidden.')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
false # one time
|
37
|
+
end
|
38
|
+
|
39
|
+
@status_icon.signal_connect('activate') do
|
40
|
+
if @terminal_window.visible? && @status_icon.embedded?
|
41
|
+
@terminal_window.hide
|
42
|
+
else
|
43
|
+
@terminal_window.show
|
44
|
+
end
|
30
45
|
end
|
31
|
-
false # one time
|
32
46
|
end
|
33
47
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
48
|
+
def parse_args!(argv)
|
49
|
+
parser = OptionParser.new do |opts|
|
50
|
+
opts.banner = 'Usage: iconify [OPTIONS] COMMAND [ARGUMENTS...]'
|
51
|
+
|
52
|
+
opts.on('-m', '--minimized', 'Start minimized') do |m|
|
53
|
+
@start_minimized = m
|
54
|
+
end
|
55
|
+
end
|
56
|
+
parser.parse!(argv)
|
57
|
+
|
58
|
+
if argv.empty?
|
59
|
+
STDERR.puts parser.banner
|
60
|
+
exit 1
|
39
61
|
end
|
62
|
+
rescue OptionParser::InvalidOption => e
|
63
|
+
STDERR.puts e
|
64
|
+
exit 1
|
40
65
|
end
|
41
|
-
end
|
42
66
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
67
|
+
def run_dialog(message)
|
68
|
+
dialog = MessageDialog.new(parent: @terminal_window,
|
69
|
+
flags: DialogFlags::DESTROY_WITH_PARENT,
|
70
|
+
type: MessageType::QUESTION,
|
71
|
+
buttons: ButtonsType::CLOSE,
|
72
|
+
message: message)
|
73
|
+
dialog.run
|
74
|
+
dialog.destroy
|
75
|
+
end
|
52
76
|
|
53
|
-
|
54
|
-
|
55
|
-
|
77
|
+
def run
|
78
|
+
@terminal_window.exec
|
79
|
+
Gtk.main
|
80
|
+
end
|
56
81
|
end
|
57
82
|
end
|
58
|
-
|
59
|
-
end
|
data/lib/iconify/version.rb
CHANGED