summaryrefslogtreecommitdiffstats
path: root/lib/menubuilder.lua
blob: b4f69ed2ec8a837743bda9134afd30fd67cf7469 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
--[[ parse through the *.menu tables and return a "menu" table
     Written for Alpine Configuration Framework (ACF) -- see www.alpinelinux.org
     Copyright (C) 2007  Nathan Angelacos
     Licensed under the terms of GPL2
  ]]--
local mymodule = {}

posix = require("posix")
format = require("acf.format")
fs = require("acf.fs")

-- returns a table of the "*.menu" tables
-- startdir should be the app dir.
local get_candidates = function (startdir)
	return fs.find_files_as_array(".*%.menu", startdir, true)
end

-- Split string into priority and name, convert '_' to space
local parse_menu_entry = function (entry)
	local name, priority
	if (string.match(entry, "^%d")) then
		priority, name = string.match(entry, "(%d+)(.*)")
	else
		name = entry
	end
	name = string.gsub(name, "_", " ")
	return name, priority
end

-- Parse menu file entry, returning cat, group, tab, action and priorities
local parse_menu_line = function (line)
	local result = nil
	--skip comments and blank lines
	if nil == (string.match(line, "^#") or string.match(line,"^$")) then
		local item = {}
		for i in string.gmatch(line, "%S+") do
			item[#item + 1] = i
		end
		if #item >= 1 then
			result = {}
			result.cat, result.cat_prio = parse_menu_entry(item[1])
			if (item[2]) then result.group, result.group_prio = parse_menu_entry(item[2]) end
			if (item[3]) then result.tab = parse_menu_entry(item[3]) end
			if (item[4]) then result.action = parse_menu_entry(item[4]) end
		end
	end
	return result
end

-- Function to compare priorities, missing priority moves to the front, same priority sorted alphabetically
local prio_compare = function(x,y)
	if x.priority == y.priority then
		if x.name < y.name then return true end
		return false
	end
	if nil == x.priority then return true end
	if nil == y.priority then return false end
	if tonumber(x.priority) < tonumber(y.priority) then return true end
	return false
end

-- returns a table of all the menu items found, sorted by priority
mymodule.get_menuitems = function (self)
	local cats = {}
	local reversecats = {}
	local foundcontrollers = {}
	for p in string.gmatch(self.conf.appdir, "[^,]+") do
		p = (string.gsub(p, "/$", ""))	--remove trailing /
		for k,filename in pairs(get_candidates(p)) do
			local controller = string.gsub(posix.basename(filename), ".menu$", "")
			local prefix = (string.gsub(posix.dirname(filename), p, "")).."/"
			if not foundcontrollers[prefix.."/"..controller] then
				foundcontrollers[prefix.."/"..controller] = true

				-- open the menu file, and parse the contents
				local handle = io.open(filename)
				for x in handle:lines() do
					local result = parse_menu_line(x)
					if result then
						for i = 1,1 do	-- loop so break works
							-- Add the category
							if nil == reversecats[result.cat] then
								table.insert ( cats,
									{ name=result.cat,
									groups = {},
									reversegroups = {} } )
								reversecats[result.cat] = #cats
							end
							local cat = cats[reversecats[result.cat]]
							cat.priority = cat.priority or result.cat_prio
							-- Add the group
							if nil == result.group then break end
							if nil == cat.groups[cat.reversegroups[result.group]] then
								table.insert ( cat.groups,
									{ name = result.group,
									controllers = {},
									reversetabs = {},
									tabs = {} } )
								cat.reversegroups[result.group] = #cat.groups
							end
							cat.groups[cat.reversegroups[result.group]].controllers[prefix..controller] = true
							local group = cat.groups[cat.reversegroups[result.group]]
							group.priority = group.priority or result.group_prio
							-- Add the tab
							if nil == result.tab or nil == result.action then break end
							local tab = { name = result.tab,
								controller = controller,
								prefix = prefix,
								action = result.action }
							table.insert(group.tabs, tab)
							if group.reversetabs[tab.name] then
								-- Flag for two tabs of same name in different controllers
								for i,t in ipairs(group.reversetabs[tab.name]) do
									if group.tabs[t].controller ~= tab.controller or group.tabs[t].prefix ~= tab.prefix then
										group.flag = tab.name
										break
									end
								end
								table.insert(group.reversetabs[tab.name], #group.tabs)
							else
								group.reversetabs[tab.name] = {#group.tabs}
							end
						end
					end
				end
				handle:close()
			end
		end
	end

	-- Now that we have the entire menu, sort by priority
	-- Categories first
	table.sort(cats, prio_compare)

	-- Then groups
	for x, cat in ipairs(cats) do
		-- Let's check for bad groups (multiple tabs with same name)
		for y,group in ipairs(cat.groups) do
			if group.flag then
				-- determine the difference between prefix/controller combos (start and stop chars)
				local start=0
				local done = false
				local first = ""
				for con in pairs(group.controllers) do
					first = con
					break
				end
				while not done and start<first:len() do
					start = start+1
					for con in pairs(group.controllers) do
						if con:sub(start,start) ~= first:sub(start,start) then
							done = true
							break
						end
					end
				end
				local stop=0
				done = false
				while not done and stop+first:len()>0 do
					stop = stop-1
					for con in pairs(group.controllers) do
						if con:sub(stop,stop) ~= first:sub(stop,stop) then
							done = true
							break
						end
					end
				end

				-- create new groups for each prefix/controller
				for con in pairs(group.controllers) do
					table.insert ( cat.groups,
						{ name = group.name..string.sub(con,start,stop),
						controllers = {},
						priority = group.priority,
						reversetabs = {},
						tabs = {} } )
					cat.groups[#cat.groups].controllers[con] = true
					cat.reversegroups[group.name..con] = #cat.groups
				end
				-- move the tabs into appropriate groups
				for z,tab in ipairs(group.tabs) do
					table.insert(cat.groups[cat.reversegroups[group.name..tab.prefix..tab.controller]].tabs, tab)
				end
				-- remove the group
				group.tabs = {}
			end
			group.reversetabs = nil -- don't need reverse table anymore
		end
		cat.reversegroups = nil	-- don't need reverse table anymore
		table.sort(cat.groups, prio_compare)
	end

	return cats
end

return mymodule