dynmenu 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/dynmenu +68 -0
- data/lib/command.rb +174 -0
- data/lib/dynamic.rb +3 -0
- data/lib/editor.rb +251 -0
- data/lib/history.rb +43 -0
- data/lib/item.rb +9 -0
- data/lib/menu.rb +93 -0
- data/lib/root_menu.rb +40 -0
- data/lib/run_menu.rb +50 -0
- data/lib/style.rb +41 -0
- metadata +55 -0
data/bin/dynmenu
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
$subtle = true
|
4
|
+
$lines = 20
|
5
|
+
begin
|
6
|
+
require 'subtle/subtlext'
|
7
|
+
rescue LoadError
|
8
|
+
$subtle = false
|
9
|
+
end
|
10
|
+
require 'root_menu'
|
11
|
+
require 'command'
|
12
|
+
require 'style'
|
13
|
+
require 'editor'
|
14
|
+
require 'history'
|
15
|
+
require 'run_menu'
|
16
|
+
|
17
|
+
license = <<END_LICENSE
|
18
|
+
dynmenu - A dmenu wrapper written in Ruby
|
19
|
+
---
|
20
|
+
Copyright (C) 2013 Joakim Reinert
|
21
|
+
|
22
|
+
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
23
|
+
|
24
|
+
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
25
|
+
|
26
|
+
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/
|
27
|
+
|
28
|
+
END_LICENSE
|
29
|
+
|
30
|
+
usage = <<END_USAGE
|
31
|
+
Usage: dynmenu [options] file
|
32
|
+
Options: -h --help : Show this help
|
33
|
+
-r : Open the menu as read only and static
|
34
|
+
-l <number> : Show max <number> lines in the menu (default=20)
|
35
|
+
File: : The menu file to be loaded and saved to.
|
36
|
+
It will be created if not existing.
|
37
|
+
END_USAGE
|
38
|
+
|
39
|
+
puts license
|
40
|
+
|
41
|
+
if ARGV.length > 4 || ARGV.length < 1 || ARGV.include?("-h") || ARGV.include?("--help")
|
42
|
+
puts usage
|
43
|
+
exit
|
44
|
+
elsif ARGV.include? "-l"
|
45
|
+
lines = ARGV[ARGV.index("-l") + 1]
|
46
|
+
if lines.to_i.to_s != lines || lines.to_i < 1
|
47
|
+
puts usage
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
$lines = lines
|
51
|
+
else
|
52
|
+
lines_matcher = Regexp.new(/^-l(\d+)$/)
|
53
|
+
ARGV.each do |arg|
|
54
|
+
if arg.match lines_matcher
|
55
|
+
$lines = Regexp.last_match(1)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
file = ARGV[ARGV.length - 1]
|
61
|
+
if File.exist? file
|
62
|
+
menu = YAML::load File.read(file)
|
63
|
+
else
|
64
|
+
menu = Root_Menu.new
|
65
|
+
end
|
66
|
+
$read_only = ARGV.include? "-r"
|
67
|
+
menu.execute
|
68
|
+
File.open(file, "w+") { |f| YAML::dump menu, f }
|
data/lib/command.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'item'
|
2
|
+
|
3
|
+
class Command
|
4
|
+
if $subtle
|
5
|
+
@@RE_COMMAND = Regexp.new(/^[+\^\*]*.+(\s[+\^\*])*(\s[@#][A-Za-z0-9_-]+)*$/)
|
6
|
+
@@RE_MODES = Regexp.new(/^[+\^\*]+$/)
|
7
|
+
@@RE_SEARCH = Regexp.new(/^[gs]\s+(.*)/)
|
8
|
+
@@RE_METHOD = Regexp.new(/^:\s*(.*)/)
|
9
|
+
@@RE_BROWSER = Regexp.new(/(chrom[e|ium]|iron|navigator|firefox|opera)/i)
|
10
|
+
end
|
11
|
+
@@RE_URI = Regexp.new(/^((w\s+((https?|ftp):\/\/)?)|(https?|ftp):\/\/)(?:[A-Z0-9-]+.)+[A-Z]{2,6}([\/?].+)?$/i)
|
12
|
+
@@RE_PROTO = Regexp.new(/^(http|https):\/\/.*/)
|
13
|
+
|
14
|
+
include Item
|
15
|
+
|
16
|
+
def initialize name, command = nil
|
17
|
+
@name = name
|
18
|
+
if $subtle
|
19
|
+
@tags = []
|
20
|
+
@views = []
|
21
|
+
@app = ""
|
22
|
+
@modes = ""
|
23
|
+
end
|
24
|
+
|
25
|
+
set_command command
|
26
|
+
end
|
27
|
+
|
28
|
+
def encode_with coder
|
29
|
+
coder['name'] = @name
|
30
|
+
coder['command'] = @command
|
31
|
+
if $subtle
|
32
|
+
coder['tags'] = @tags
|
33
|
+
coder['views'] = @views
|
34
|
+
coder['app'] = @app
|
35
|
+
coder['modes'] = @modes
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def init_with coder
|
40
|
+
@name = coder['name']
|
41
|
+
@command = coder['command']
|
42
|
+
if $subtle
|
43
|
+
@tags = coder['tags'] || []
|
44
|
+
@views = coder['views'] || []
|
45
|
+
@app = coder['app'] || ""
|
46
|
+
@modes = coder['modes'] || ""
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def command
|
51
|
+
@command
|
52
|
+
end
|
53
|
+
|
54
|
+
def set_command command
|
55
|
+
unless command.nil? || command.empty?
|
56
|
+
if @@RE_URI.match command
|
57
|
+
split = Regexp.last_match(0).split(/\s+/)
|
58
|
+
command = split[split.length - 1]
|
59
|
+
command.prepend("http://") unless @@RE_PROTO.match command
|
60
|
+
command = URI.parse command
|
61
|
+
elsif @@RE_SEARCH.match command
|
62
|
+
command = web_search Regexp.last_match(1)
|
63
|
+
elsif $subtle
|
64
|
+
if @@RE_METHOD.match(command)
|
65
|
+
command = Regexp.last_match(0).to_sym
|
66
|
+
elsif @@RE_COMMAND.match command
|
67
|
+
@tags = []
|
68
|
+
@views = []
|
69
|
+
@app = ""
|
70
|
+
@modes = ""
|
71
|
+
|
72
|
+
command.split.each do |arg|
|
73
|
+
case arg[0]
|
74
|
+
when '#' then @tags << arg[1..-1]
|
75
|
+
when '@' then @views << arg[1..-1]
|
76
|
+
when '+', '^', '*'
|
77
|
+
@modes += @@RE_MODES.match(arg).to_s
|
78
|
+
else
|
79
|
+
if @app.nil? || @app.empty?
|
80
|
+
@app = arg
|
81
|
+
else
|
82
|
+
@app += ' ' + arg
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
if @views.any? and not @app.empty? and @tags.empty?
|
87
|
+
@tags << "tag_#{rand(1337)}"
|
88
|
+
end
|
89
|
+
else
|
90
|
+
@app = command
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
@command = command
|
95
|
+
end
|
96
|
+
|
97
|
+
def web_search search_string, engine = :google
|
98
|
+
escaped_string = URI.escape search_string
|
99
|
+
case engine
|
100
|
+
when :duckduckgo then escaped_string.prepend "https://duckduckgo.com/?q="
|
101
|
+
else escaped_string.prepend "https://www.google.com/#q="
|
102
|
+
end
|
103
|
+
URI.parse escaped_string
|
104
|
+
end
|
105
|
+
|
106
|
+
def find_browser
|
107
|
+
begin
|
108
|
+
if @browser.nil?
|
109
|
+
Subtlext::Client.all.each do |c|
|
110
|
+
if c.klass.match(@@RE_BROWSER)
|
111
|
+
@browser = c
|
112
|
+
@view = c.views.first
|
113
|
+
return
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
rescue
|
118
|
+
@browser = nil
|
119
|
+
@view = nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def execute
|
124
|
+
case @command
|
125
|
+
when String
|
126
|
+
if $subtle
|
127
|
+
subtle_execute
|
128
|
+
else
|
129
|
+
system "#{@command} &>/dev/null"
|
130
|
+
end
|
131
|
+
when URI
|
132
|
+
puts @command.to_s
|
133
|
+
system "xdg-open '%s' &>/dev/null &" % [ @command.to_s ]
|
134
|
+
if $subtle
|
135
|
+
find_browser
|
136
|
+
@browser.focus unless @browser.nil?
|
137
|
+
end
|
138
|
+
end
|
139
|
+
true
|
140
|
+
end
|
141
|
+
|
142
|
+
def subtle_execute
|
143
|
+
tags = @tags.map do |t|
|
144
|
+
tag = Subtlext::Tag.first(t) || Subtlext::Tag.new(t)
|
145
|
+
tag.save
|
146
|
+
tag
|
147
|
+
end
|
148
|
+
@views.each do |v|
|
149
|
+
view = Subtlext::View.first(v) || Subtlext::View.new(v)
|
150
|
+
view.save
|
151
|
+
view.tag(tags) unless view.nil? or tags.empty?
|
152
|
+
end
|
153
|
+
unless (client = Subtlext::Client.spawn(@app)).nil?
|
154
|
+
client.tags = tags unless tags.empty?
|
155
|
+
unless @modes.empty?
|
156
|
+
flags = []
|
157
|
+
|
158
|
+
@modes.each_char do |c|
|
159
|
+
case c
|
160
|
+
when '+' then flags << :full
|
161
|
+
when '^' then flags << :float
|
162
|
+
when '*' then flags << :stick
|
163
|
+
when '=' then flags << :zaphod
|
164
|
+
end
|
165
|
+
end
|
166
|
+
client.flags = flags
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_s
|
172
|
+
"#{@name} => #{command}"
|
173
|
+
end
|
174
|
+
end
|
data/lib/dynamic.rb
ADDED
data/lib/editor.rb
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'menu'
|
2
|
+
require 'dynamic'
|
3
|
+
|
4
|
+
class Editor < Menu
|
5
|
+
|
6
|
+
include Dynamic
|
7
|
+
|
8
|
+
@@name = "Edit menu"
|
9
|
+
|
10
|
+
def self.name
|
11
|
+
@@name
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
"> #{@@name}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize parent
|
19
|
+
@parent = parent
|
20
|
+
set_style parent.style
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
"Edit menu"
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute
|
28
|
+
entries = []
|
29
|
+
selection = nil
|
30
|
+
while selection != "" && (entries.index selection) != entries.length - 1
|
31
|
+
entries = ["Name (#{@parent.name})"]
|
32
|
+
entries << "Style"
|
33
|
+
entries << "Entries"
|
34
|
+
entries << "Run menu"
|
35
|
+
entries << "Done"
|
36
|
+
selection = show_menu entries, @@name
|
37
|
+
case entries.index selection
|
38
|
+
when 0 then @parent.set_name show_menu([@parent.name], "Edit Name")
|
39
|
+
when 1 then @parent.set_style show_style_menu
|
40
|
+
when 2 then show_entries_menu
|
41
|
+
when 3 then show_run_menu_menu
|
42
|
+
end
|
43
|
+
end
|
44
|
+
@parent.execute
|
45
|
+
end
|
46
|
+
|
47
|
+
def show_run_menu_menu
|
48
|
+
entries = []
|
49
|
+
selection = nil
|
50
|
+
while selection != "" && (entries.index selection) != entries.length - 1
|
51
|
+
if @parent.run_menu?
|
52
|
+
entries = ["Disable run menu"]
|
53
|
+
else
|
54
|
+
entries = ["Enable run menu"]
|
55
|
+
end
|
56
|
+
entries << "History"
|
57
|
+
entries << "Done"
|
58
|
+
selection = show_menu entries, "Run menu"
|
59
|
+
case entries.index selection
|
60
|
+
when 0 then @parent.set_run_menu !@parent.run_menu?
|
61
|
+
when 1 then show_history_menu
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def show_history_menu
|
67
|
+
entries = []
|
68
|
+
selection = nil
|
69
|
+
clear = false
|
70
|
+
while selection != "" && (entries.index selection) != entries.length - 1
|
71
|
+
entries = ["History length (#{@parent.history.length})"]
|
72
|
+
entries << "Items shown in run menu (#{@parent.history.show_num_items})"
|
73
|
+
if clear
|
74
|
+
entries << "Undo"
|
75
|
+
else
|
76
|
+
entries << "Clear history"
|
77
|
+
end
|
78
|
+
entries << "Done"
|
79
|
+
|
80
|
+
selection = show_menu entries, "History"
|
81
|
+
case entries.index.selection
|
82
|
+
when 0
|
83
|
+
selection = (show_menu [], "Enter a number")
|
84
|
+
number = selection.to_i
|
85
|
+
@parent.history.set_length number unless number < 1 || number.to_s != selection
|
86
|
+
when 1
|
87
|
+
selection = (show_menu [], "Enter a number")
|
88
|
+
number = (show_menu [], "Enter a number").to_i
|
89
|
+
@parent.history.set_show_num_items number unless number.to_s != selection
|
90
|
+
when 2 then clear = !clear
|
91
|
+
when (entries.length - 1) then @parent.history.clear if clear
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def show_style_menu
|
97
|
+
style = @parent.style
|
98
|
+
entries = []
|
99
|
+
selection = nil
|
100
|
+
while selection != "" && (entries.index selection) != entries.length - 1
|
101
|
+
entries = ["Background: (#{style.color :bg})"]
|
102
|
+
entries << "Foreground: (#{style.color :fg})"
|
103
|
+
entries << "Selected Background: (#{style.color :bg_hi})"
|
104
|
+
entries << "Selected Foreground: (#{style.color :fg_hi})"
|
105
|
+
if style.font.nil?
|
106
|
+
entries << "Font: (default)"
|
107
|
+
else
|
108
|
+
entries << "Font: (#{style.font})"
|
109
|
+
end
|
110
|
+
entries << "Done"
|
111
|
+
selection = show_menu entries, "Edit style"
|
112
|
+
colors = [:bg, :fg, :bg_hi, :fg_hi]
|
113
|
+
unless (entries.index selection).nil?
|
114
|
+
if (entries.index selection) < 4
|
115
|
+
style.set_color colors[entries.index selection], show_menu([style.color(colors[entries.index selection])], "Enter a color")
|
116
|
+
elsif (entries.index selection) < 5
|
117
|
+
if style.font.nil?
|
118
|
+
selection = show_menu(["default"], "Set font")
|
119
|
+
else
|
120
|
+
selection = show_menu([style.font], "Set font")
|
121
|
+
end
|
122
|
+
unless selection.nil? || selection == "default"
|
123
|
+
style.set_font selection
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
style
|
129
|
+
end
|
130
|
+
|
131
|
+
def show_menu entries, name, show_edit_menue = false
|
132
|
+
super
|
133
|
+
end
|
134
|
+
|
135
|
+
def show_entries_menu
|
136
|
+
entries = ["Edit/Remove entries"]
|
137
|
+
entries << "Add entries"
|
138
|
+
entries << "Done"
|
139
|
+
selection = nil
|
140
|
+
while selection != "" && (entries.index selection) != 2
|
141
|
+
selection = show_menu entries, "Edit entries"
|
142
|
+
case entries.index selection
|
143
|
+
when 0 then show_edit_menu
|
144
|
+
when 1 then show_add_menu
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def show_edit_menu
|
150
|
+
selection = nil
|
151
|
+
while selection != "" && @parent.items[selection].nil?
|
152
|
+
selection = show_menu @parent.items.keys.sort, "Select item to edit"
|
153
|
+
item = @parent.items[selection]
|
154
|
+
unless item.nil?
|
155
|
+
if item.is_a? Command
|
156
|
+
show_edit_cmd_menu item
|
157
|
+
else
|
158
|
+
show_edit_menu_menu item
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def show_add_menu
|
165
|
+
entries = []
|
166
|
+
entries = ["Command"]
|
167
|
+
entries << "Menu"
|
168
|
+
entries << "Done"
|
169
|
+
selection = nil
|
170
|
+
while selection != "" && (entries.index selection) != entries.length - 1
|
171
|
+
selection = show_menu entries, "Add items"
|
172
|
+
case entries.index selection
|
173
|
+
when 0 then show_edit_cmd_menu
|
174
|
+
when 1 then show_edit_menu_menu
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def show_edit_cmd_menu command = Command.new("New command", "")
|
180
|
+
new = false
|
181
|
+
if (@parent.remove_item command).nil?
|
182
|
+
new = true
|
183
|
+
end
|
184
|
+
entries = []
|
185
|
+
selection = nil
|
186
|
+
delete = false
|
187
|
+
while selection != "" && (entries.index selection) != entries.length - 1
|
188
|
+
entries = ["Name (#{command.name})"]
|
189
|
+
entries << "Command (#{command.command})"
|
190
|
+
if new
|
191
|
+
string = "Add command"
|
192
|
+
else
|
193
|
+
string = "Edit command"
|
194
|
+
if delete
|
195
|
+
entries << "Undo"
|
196
|
+
else
|
197
|
+
entries << "Delete"
|
198
|
+
end
|
199
|
+
end
|
200
|
+
entries << "Done"
|
201
|
+
selection = show_menu entries, "Add Command"
|
202
|
+
case entries.index selection
|
203
|
+
when 0
|
204
|
+
new_name = show_menu([command.name], "Enter a name")
|
205
|
+
command.set_name new_name unless new_name.empty?
|
206
|
+
when 1
|
207
|
+
history = History.new 1, 1
|
208
|
+
history.update command.command unless command.command.nil? or command.command.empty?
|
209
|
+
new_command = Run_Menu.new(self, history, "Enter a command").show_menu
|
210
|
+
command.set_command new_command unless new_command.empty?
|
211
|
+
when 2 then delete = !delete unless new
|
212
|
+
end
|
213
|
+
end
|
214
|
+
delete = true if selection == "" && new
|
215
|
+
@parent.set_item command unless delete
|
216
|
+
end
|
217
|
+
|
218
|
+
def show_edit_menu_menu menu = nil
|
219
|
+
new = menu.nil?
|
220
|
+
if new
|
221
|
+
menu = Menu.new "New menu"
|
222
|
+
menu.set_style @parent.style
|
223
|
+
end
|
224
|
+
@parent.remove_item menu
|
225
|
+
entries = []
|
226
|
+
selection = nil
|
227
|
+
delete = false
|
228
|
+
while selection != "" && (entries.index selection) != entries.length - 1
|
229
|
+
entries = ["Name (#{menu.name})"]
|
230
|
+
if new
|
231
|
+
string = "Add menu"
|
232
|
+
else
|
233
|
+
string = "Edit menu"
|
234
|
+
if delete
|
235
|
+
entries << "Undo"
|
236
|
+
else
|
237
|
+
entries << "Remove"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
entries << "Done"
|
241
|
+
selection = show_menu entries, string
|
242
|
+
if (entries.index selection) == 0
|
243
|
+
new_name = show_menu [], "Enter a name"
|
244
|
+
menu.set_name new_name unless new_name.empty?
|
245
|
+
end
|
246
|
+
delete = !delete if (entries.index selection) == 1 && !new
|
247
|
+
end
|
248
|
+
delete = true if selection == "" && new
|
249
|
+
@parent.set_item menu unless delete
|
250
|
+
end
|
251
|
+
end
|
data/lib/history.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class History
|
2
|
+
def initialize length, show_num_items = 5
|
3
|
+
@items = {}
|
4
|
+
set_length length
|
5
|
+
set_show_num_items show_num_items
|
6
|
+
end
|
7
|
+
|
8
|
+
def length
|
9
|
+
@length
|
10
|
+
end
|
11
|
+
|
12
|
+
def show_num_items
|
13
|
+
@show_num_items
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_length length
|
17
|
+
@length = length
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_show_num_items num
|
21
|
+
@show_num_items = num
|
22
|
+
end
|
23
|
+
|
24
|
+
def update item
|
25
|
+
@items.each {|itm,age| @items[itm] += 1}
|
26
|
+
@items[item] = 0
|
27
|
+
if @items.length > @length
|
28
|
+
@items.delete((@items.max_by {|itm, age| age}).first)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def items
|
33
|
+
ret = {
|
34
|
+
:first => ((@items.sort_by {|item,age| age}).map {|pair| pair.first}).first(@show_num_items)
|
35
|
+
}
|
36
|
+
ret.store :rest, (@items.reject {|item, age| ret[:first].include? item}).keys
|
37
|
+
ret
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear
|
41
|
+
@items = {}
|
42
|
+
end
|
43
|
+
end
|
data/lib/item.rb
ADDED
data/lib/menu.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'item'
|
2
|
+
|
3
|
+
class Menu
|
4
|
+
|
5
|
+
include Item
|
6
|
+
|
7
|
+
def initialize name
|
8
|
+
@name = name
|
9
|
+
@items = Hash.new
|
10
|
+
@style = Style.new
|
11
|
+
set_item Editor.new self
|
12
|
+
end
|
13
|
+
|
14
|
+
def encode_with coder
|
15
|
+
coder['name'] = @name
|
16
|
+
coder['items'] = items
|
17
|
+
coder['style'] = @style
|
18
|
+
end
|
19
|
+
|
20
|
+
def init_with coder
|
21
|
+
@name = coder['name']
|
22
|
+
@items = coder['items']
|
23
|
+
@style = coder['style']
|
24
|
+
set_item Editor.new self
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_item item
|
28
|
+
@items[item.name] = item unless @items[item.name].is_a? Dynamic
|
29
|
+
end
|
30
|
+
|
31
|
+
def items
|
32
|
+
@items.reject { |name,item| item.is_a?(Dynamic) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def remove_item item
|
36
|
+
@items.delete item.name unless item.is_a? Dynamic
|
37
|
+
end
|
38
|
+
|
39
|
+
def name
|
40
|
+
"> #{super}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def style
|
44
|
+
@style
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_style style
|
48
|
+
@style = style
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
string = "#{name}\n"
|
53
|
+
string += (@items.keys.sort.map {|name| name.prepend(" ")}).join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
def execute
|
57
|
+
show = true
|
58
|
+
while show
|
59
|
+
selection = show_menu @items.keys.sort, @name
|
60
|
+
item = @items[selection]
|
61
|
+
if item.nil?
|
62
|
+
show = false
|
63
|
+
else
|
64
|
+
show = !item.execute unless item.nil?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
!item.nil?
|
68
|
+
end
|
69
|
+
def filter_entries entries
|
70
|
+
if @items.nil?
|
71
|
+
entries
|
72
|
+
else
|
73
|
+
entries.reject {|entry| @items[entry].is_a?(Editor)}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
def show_menu entries, name, show_edit_menu = true
|
77
|
+
entries = filter_entries entries
|
78
|
+
unless $read_only || self.is_a?(Dynamic)
|
79
|
+
entries << "> #{Editor.name}"
|
80
|
+
end
|
81
|
+
command = "echo \"#{entries.join "\n"}\" | dmenu -i -b -l #{$lines} #{get_font_string} -nf \"#{@style.color :fg}\" -nb \"#{@style.color :bg}\" -sf \"#{@style.color :fg_hi}\" -sb \"#{@style.color :bg_hi}\" -p \"#{name}\""
|
82
|
+
(`#{command}`).strip
|
83
|
+
end
|
84
|
+
|
85
|
+
def get_font_string
|
86
|
+
font = ""
|
87
|
+
unless @style.font.nil?
|
88
|
+
font = "-fn \"#{@style.font}\""
|
89
|
+
end
|
90
|
+
font
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/root_menu.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'menu'
|
2
|
+
class Root_Menu < Menu
|
3
|
+
|
4
|
+
def initialize history_length = 1000, history_items = 5
|
5
|
+
super "Dynmenu"
|
6
|
+
@run_menu = true
|
7
|
+
@history = History.new history_length, history_items
|
8
|
+
set_item Run_Menu.new self, @history
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_menu?
|
12
|
+
@run_menu
|
13
|
+
end
|
14
|
+
|
15
|
+
def filter_entries entries
|
16
|
+
partitions = super.partition {|entry| @items[entry].is_a?(Run_Menu)}
|
17
|
+
if run_menu?
|
18
|
+
partitions[0] + partitions[1]
|
19
|
+
else
|
20
|
+
partitions[1]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_run_menu value
|
25
|
+
@run_menu = value
|
26
|
+
end
|
27
|
+
|
28
|
+
def encode_with coder
|
29
|
+
super
|
30
|
+
coder['run_menu'] = @run_menu
|
31
|
+
coder['history'] = @history
|
32
|
+
end
|
33
|
+
|
34
|
+
def init_with coder
|
35
|
+
super
|
36
|
+
@run_menu = coder['run_menu']
|
37
|
+
@history = coder['history']
|
38
|
+
set_item Run_Menu.new self, @history if @run_menu
|
39
|
+
end
|
40
|
+
end
|
data/lib/run_menu.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'menu'
|
2
|
+
require 'dynamic'
|
3
|
+
class Run_Menu < Menu
|
4
|
+
|
5
|
+
include Dynamic
|
6
|
+
|
7
|
+
@@RE_HIDDEN = Regexp.new /\/{,1}\.[^\/]*$/
|
8
|
+
def initialize parent, history = History.new(1, 5), name = "Run"
|
9
|
+
@name = name
|
10
|
+
@parent = parent
|
11
|
+
@history = history
|
12
|
+
set_style parent.style
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
">#{super}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def execute
|
20
|
+
command = show_menu
|
21
|
+
Command.new("", command).execute
|
22
|
+
@history.update command unless command.empty?
|
23
|
+
!command.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def show_menu
|
27
|
+
get_files
|
28
|
+
history_items = @history.items
|
29
|
+
items = history_items[:first] + @files + history_items[:rest]
|
30
|
+
super items, @name
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_files dirs = (`echo $PATH`).split(':')
|
34
|
+
@files = []
|
35
|
+
dirs.each do |dir|
|
36
|
+
get_files_rec dir
|
37
|
+
end
|
38
|
+
end
|
39
|
+
def get_files_rec file
|
40
|
+
return if file.match @@RE_HIDDEN
|
41
|
+
if File.directory? file
|
42
|
+
Dir.foreach file do |cur_file|
|
43
|
+
get_files_rec cur_file
|
44
|
+
end
|
45
|
+
else
|
46
|
+
@files << file
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/style.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
class Style
|
2
|
+
@@re_color = Regexp.new(/^#[0-9a-fA-F]{6}$/)
|
3
|
+
def initialize
|
4
|
+
@colors = {
|
5
|
+
:bg => "#202020",
|
6
|
+
:fg => "#757575",
|
7
|
+
:bg_hi => "#303030",
|
8
|
+
:fg_hi => "#FECF35"
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_color color, value
|
13
|
+
unless @colors.keys.include? color
|
14
|
+
raise ArgumentError, "Invalid key!"
|
15
|
+
end
|
16
|
+
unless @@re_color.match value
|
17
|
+
raise ArgumentError, "Invalid color string!"
|
18
|
+
end
|
19
|
+
@colors[color] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def color color
|
23
|
+
unless @colors.keys.include? color
|
24
|
+
raise ArgumentError, "Invalid key!"
|
25
|
+
end
|
26
|
+
@colors[color]
|
27
|
+
end
|
28
|
+
|
29
|
+
def set_font font
|
30
|
+
@font = font
|
31
|
+
end
|
32
|
+
def to_s
|
33
|
+
string = ""
|
34
|
+
string = @font unless @font.nil?
|
35
|
+
string += " #{@colors}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def font
|
39
|
+
@font
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dynmenu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Joakim Reinert
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-03 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: A dmenu wrapper for subtle wm
|
15
|
+
email: mail@jreinert.com
|
16
|
+
executables:
|
17
|
+
- dynmenu
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/item.rb
|
22
|
+
- lib/editor.rb
|
23
|
+
- lib/style.rb
|
24
|
+
- lib/menu.rb
|
25
|
+
- lib/root_menu.rb
|
26
|
+
- lib/run_menu.rb
|
27
|
+
- lib/history.rb
|
28
|
+
- lib/dynamic.rb
|
29
|
+
- lib/command.rb
|
30
|
+
- bin/dynmenu
|
31
|
+
homepage: https://github.com/supasnashbuhl/dynmenu
|
32
|
+
licenses: []
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options: []
|
35
|
+
require_paths:
|
36
|
+
- lib
|
37
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '0'
|
43
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 1.8.23
|
52
|
+
signing_key:
|
53
|
+
specification_version: 3
|
54
|
+
summary: Dynmenu
|
55
|
+
test_files: []
|