]> git.lizzy.rs Git - dragonfireclient.git/blob - builtin/common/filterlist.lua
Improve shadow filters (#12195)
[dragonfireclient.git] / builtin / common / filterlist.lua
1 --Minetest
2 --Copyright (C) 2013 sapier
3 --
4 --This program is free software; you can redistribute it and/or modify
5 --it under the terms of the GNU Lesser General Public License as published by
6 --the Free Software Foundation; either version 2.1 of the License, or
7 --(at your option) any later version.
8 --
9 --This program is distributed in the hope that it will be useful,
10 --but WITHOUT ANY WARRANTY; without even the implied warranty of
11 --MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 --GNU Lesser General Public License for more details.
13 --
14 --You should have received a copy of the GNU Lesser General Public License along
15 --with this program; if not, write to the Free Software Foundation, Inc.,
16 --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 --------------------------------------------------------------------------------
19 -- TODO improve doc                                                           --
20 -- TODO code cleanup                                                          --
21 -- Generic implementation of a filter/sortable list                           --
22 -- Usage:                                                                     --
23 -- Filterlist needs to be initialized on creation. To achieve this you need to --
24 -- pass following functions:                                                  --
25 -- raw_fct() (mandatory):                                                     --
26 --     function returning a table containing the elements to be filtered      --
27 -- compare_fct(element1,element2) (mandatory):                                --
28 --     function returning true/false if element1 is same element as element2  --
29 -- uid_match_fct(element1,uid) (optional)                                     --
30 --     function telling if uid is attached to element1                        --
31 -- filter_fct(element,filtercriteria) (optional)                              --
32 --     function returning true/false if filtercriteria met to element         --
33 -- fetch_param (optional)                                                     --
34 --     parameter passed to raw_fct to aquire correct raw data                 --
35 --                                                                            --
36 --------------------------------------------------------------------------------
37 filterlist = {}
38
39 --------------------------------------------------------------------------------
40 function filterlist.refresh(self)
41         self.m_raw_list = self.m_raw_list_fct(self.m_fetch_param)
42         filterlist.process(self)
43 end
44
45 --------------------------------------------------------------------------------
46 function filterlist.create(raw_fct,compare_fct,uid_match_fct,filter_fct,fetch_param)
47
48         assert((raw_fct ~= nil) and (type(raw_fct) == "function"))
49         assert((compare_fct ~= nil) and (type(compare_fct) == "function"))
50
51         local self = {}
52
53         self.m_raw_list_fct  = raw_fct
54         self.m_compare_fct   = compare_fct
55         self.m_filter_fct    = filter_fct
56         self.m_uid_match_fct = uid_match_fct
57
58         self.m_filtercriteria = nil
59         self.m_fetch_param = fetch_param
60
61         self.m_sortmode = "none"
62         self.m_sort_list = {}
63
64         self.m_processed_list = nil
65         self.m_raw_list = self.m_raw_list_fct(self.m_fetch_param)
66
67         self.add_sort_mechanism = filterlist.add_sort_mechanism
68         self.set_filtercriteria = filterlist.set_filtercriteria
69         self.get_filtercriteria = filterlist.get_filtercriteria
70         self.set_sortmode       = filterlist.set_sortmode
71         self.get_list           = filterlist.get_list
72         self.get_raw_list       = filterlist.get_raw_list
73         self.get_raw_element    = filterlist.get_raw_element
74         self.get_raw_index      = filterlist.get_raw_index
75         self.get_current_index  = filterlist.get_current_index
76         self.size               = filterlist.size
77         self.uid_exists_raw     = filterlist.uid_exists_raw
78         self.raw_index_by_uid   = filterlist.raw_index_by_uid
79         self.refresh            = filterlist.refresh
80
81         filterlist.process(self)
82
83         return self
84 end
85
86 --------------------------------------------------------------------------------
87 function filterlist.add_sort_mechanism(self,name,fct)
88         self.m_sort_list[name] = fct
89 end
90
91 --------------------------------------------------------------------------------
92 function filterlist.set_filtercriteria(self,criteria)
93         if criteria == self.m_filtercriteria and
94                 type(criteria) ~= "table" then
95                 return
96         end
97         self.m_filtercriteria = criteria
98         filterlist.process(self)
99 end
100
101 --------------------------------------------------------------------------------
102 function filterlist.get_filtercriteria(self)
103         return self.m_filtercriteria
104 end
105
106 --------------------------------------------------------------------------------
107 --supported sort mode "alphabetic|none"
108 function filterlist.set_sortmode(self,mode)
109         if (mode == self.m_sortmode) then
110                 return
111         end
112         self.m_sortmode = mode
113         filterlist.process(self)
114 end
115
116 --------------------------------------------------------------------------------
117 function filterlist.get_list(self)
118         return self.m_processed_list
119 end
120
121 --------------------------------------------------------------------------------
122 function filterlist.get_raw_list(self)
123         return self.m_raw_list
124 end
125
126 --------------------------------------------------------------------------------
127 function filterlist.get_raw_element(self,idx)
128         if type(idx) ~= "number" then
129                 idx = tonumber(idx)
130         end
131
132         if idx ~= nil and idx > 0 and idx <= #self.m_raw_list then
133                 return self.m_raw_list[idx]
134         end
135
136         return nil
137 end
138
139 --------------------------------------------------------------------------------
140 function filterlist.get_raw_index(self,listindex)
141         assert(self.m_processed_list ~= nil)
142
143         if listindex ~= nil and listindex > 0 and
144                 listindex <= #self.m_processed_list then
145                 local entry = self.m_processed_list[listindex]
146
147                 for i,v in ipairs(self.m_raw_list) do
148
149                         if self.m_compare_fct(v,entry) then
150                                 return i
151                         end
152                 end
153         end
154
155         return 0
156 end
157
158 --------------------------------------------------------------------------------
159 function filterlist.get_current_index(self,listindex)
160         assert(self.m_processed_list ~= nil)
161
162         if listindex ~= nil and listindex > 0 and
163                 listindex <= #self.m_raw_list then
164                 local entry = self.m_raw_list[listindex]
165
166                 for i,v in ipairs(self.m_processed_list) do
167
168                         if self.m_compare_fct(v,entry) then
169                                 return i
170                         end
171                 end
172         end
173
174         return 0
175 end
176
177 --------------------------------------------------------------------------------
178 function filterlist.process(self)
179         assert(self.m_raw_list ~= nil)
180
181         if self.m_sortmode == "none" and
182                 self.m_filtercriteria == nil then
183                 self.m_processed_list = self.m_raw_list
184                 return
185         end
186
187         self.m_processed_list = {}
188
189         for k,v in pairs(self.m_raw_list) do
190                 if self.m_filtercriteria == nil or
191                         self.m_filter_fct(v,self.m_filtercriteria) then
192                         self.m_processed_list[#self.m_processed_list + 1] = v
193                 end
194         end
195
196         if self.m_sortmode == "none" then
197                 return
198         end
199
200         if self.m_sort_list[self.m_sortmode] ~= nil and
201                 type(self.m_sort_list[self.m_sortmode]) == "function" then
202
203                 self.m_sort_list[self.m_sortmode](self)
204         end
205 end
206
207 --------------------------------------------------------------------------------
208 function filterlist.size(self)
209         if self.m_processed_list == nil then
210                 return 0
211         end
212
213         return #self.m_processed_list
214 end
215
216 --------------------------------------------------------------------------------
217 function filterlist.uid_exists_raw(self,uid)
218         for i,v in ipairs(self.m_raw_list) do
219                 if self.m_uid_match_fct(v,uid) then
220                         return true
221                 end
222         end
223         return false
224 end
225
226 --------------------------------------------------------------------------------
227 function filterlist.raw_index_by_uid(self, uid)
228         local elementcount = 0
229         local elementidx = 0
230         for i,v in ipairs(self.m_raw_list) do
231                 if self.m_uid_match_fct(v,uid) then
232                         elementcount = elementcount +1
233                         elementidx = i
234                 end
235         end
236
237
238         -- If there are more elements than one with same name uid can't decide which
239         -- one is meant. self shouldn't be possible but just for sure.
240         if elementcount > 1 then
241                 elementidx=0
242         end
243
244         return elementidx
245 end
246
247 --------------------------------------------------------------------------------
248 -- COMMON helper functions                                                    --
249 --------------------------------------------------------------------------------
250
251 --------------------------------------------------------------------------------
252 function compare_worlds(world1,world2)
253         if world1.path ~= world2.path then
254                 return false
255         end
256
257         if world1.name ~= world2.name then
258                 return false
259         end
260
261         if world1.gameid ~= world2.gameid then
262                 return false
263         end
264
265         return true
266 end
267
268 --------------------------------------------------------------------------------
269 function sort_worlds_alphabetic(self)
270
271         table.sort(self.m_processed_list, function(a, b)
272                 --fixes issue #857 (crash due to sorting nil in worldlist)
273                 if a == nil or b == nil then
274                         if a == nil and b ~= nil then return false end
275                         if b == nil and a ~= nil then return true end
276                         return false
277                 end
278                 if a.name:lower() == b.name:lower() then
279                         return a.name < b.name
280                 end
281                 return a.name:lower() < b.name:lower()
282         end)
283 end
284
285 --------------------------------------------------------------------------------
286 function sort_mod_list(self)
287
288         table.sort(self.m_processed_list, function(a, b)
289                 -- Show game mods at bottom
290                 if a.type ~= b.type or a.loc ~= b.loc then
291                         if b.type == "game" then
292                                 return a.loc ~= "game"
293                         end
294                         return b.loc == "game"
295                 end
296                 -- If in same or no modpack, sort by name
297                 if a.modpack == b.modpack then
298                         if a.name:lower() == b.name:lower() then
299                                 return a.name < b.name
300                         end
301                         return a.name:lower() < b.name:lower()
302                 -- Else compare name to modpack name
303                 else
304                         -- Always show modpack pseudo-mod on top of modpack mod list
305                         if a.name == b.modpack then
306                                 return true
307                         elseif b.name == a.modpack then
308                                 return false
309                         end
310
311                         local name_a = a.modpack or a.name
312                         local name_b = b.modpack or b.name
313                         if name_a:lower() == name_b:lower() then
314                                 return  name_a < name_b
315                         end
316                         return name_a:lower() < name_b:lower()
317                 end
318         end)
319 end