dynmenu 0.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.
- 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: []
|