summaryrefslogtreecommitdiffstats
path: root/lib/menubuilder.lua
blob: 4105db6c9040eb58411270ba9a72c5c1511ce9d0 (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
--[[ 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
  ]]--
module(..., package.seeall)

require("format")

-- returns a table of the "*.menu" tables 
-- uses the system "find" command
-- startdir should be the app dir.
local get_candidates = function (startdir)
	local t = {}
	local fh = io.popen('find ' .. format.escapespecialcharacters(startdir) .. ' -name "*.menu"')
	for x in fh:lines() do
		t[#t + 1] = x
	end
	return t
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
get_menuitems = function (startdir)
	local cats = {}
	local reversecats = {}
	startdir = (string.gsub(startdir, "/$", ""))	--remove trailing /
	for k,filename in pairs(get_candidates(startdir)) do
		local controller = mvc.basename(filename, ".menu")
		local prefix = (string.gsub(mvc.dirname(filename), startdir, ""))

		-- 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,
						controller = controller,
						prefix = prefix,
						tabs = {} } )
					cat.reversegroups[result.group] = #cat.groups
				end
				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, action = result.action }
				table.insert(group.tabs, tab)
				end
			end
		end
		handle:close()
	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
		cat.reversegroups = nil	-- don't need reverse table anymore
		table.sort(cat.groups, prio_compare)
	end

	return cats
end