filterlist.lua 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  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. -- TODO improve doc --
  19. -- TODO code cleanup --
  20. -- Generic implementation of a filter/sortable list --
  21. -- Usage: --
  22. -- Filterlist needs to be initialized on creation. To achieve this you need to --
  23. -- pass following functions: --
  24. -- raw_fct() (mandatory): --
  25. -- function returning a table containing the elements to be filtered --
  26. -- compare_fct(element1,element2) (mandatory): --
  27. -- function returning true/false if element1 is same element as element2 --
  28. -- uid_match_fct(element1,uid) (optional) --
  29. -- function telling if uid is attached to element1 --
  30. -- filter_fct(element,filtercriteria) (optional) --
  31. -- function returning true/false if filtercriteria met to element --
  32. -- fetch_param (optional) --
  33. -- parameter passed to raw_fct to aquire correct raw data --
  34. -- --
  35. --------------------------------------------------------------------------------
  36. filterlist = {}
  37. --------------------------------------------------------------------------------
  38. function filterlist.refresh(self)
  39. self.m_raw_list = self.m_raw_list_fct(self.m_fetch_param)
  40. filterlist.process(self)
  41. end
  42. --------------------------------------------------------------------------------
  43. function filterlist.create(raw_fct,compare_fct,uid_match_fct,filter_fct,fetch_param)
  44. assert((raw_fct ~= nil) and (type(raw_fct) == "function"))
  45. assert((compare_fct ~= nil) and (type(compare_fct) == "function"))
  46. local self = {}
  47. self.m_raw_list_fct = raw_fct
  48. self.m_compare_fct = compare_fct
  49. self.m_filter_fct = filter_fct
  50. self.m_uid_match_fct = uid_match_fct
  51. self.m_filtercriteria = nil
  52. self.m_fetch_param = fetch_param
  53. self.m_sortmode = "none"
  54. self.m_sort_list = {}
  55. self.m_processed_list = nil
  56. self.m_raw_list = self.m_raw_list_fct(self.m_fetch_param)
  57. self.add_sort_mechanism = filterlist.add_sort_mechanism
  58. self.set_filtercriteria = filterlist.set_filtercriteria
  59. self.get_filtercriteria = filterlist.get_filtercriteria
  60. self.set_sortmode = filterlist.set_sortmode
  61. self.get_list = filterlist.get_list
  62. self.get_raw_list = filterlist.get_raw_list
  63. self.get_raw_element = filterlist.get_raw_element
  64. self.get_raw_index = filterlist.get_raw_index
  65. self.get_current_index = filterlist.get_current_index
  66. self.size = filterlist.size
  67. self.uid_exists_raw = filterlist.uid_exists_raw
  68. self.raw_index_by_uid = filterlist.raw_index_by_uid
  69. self.refresh = filterlist.refresh
  70. filterlist.process(self)
  71. return self
  72. end
  73. --------------------------------------------------------------------------------
  74. function filterlist.add_sort_mechanism(self,name,fct)
  75. self.m_sort_list[name] = fct
  76. end
  77. --------------------------------------------------------------------------------
  78. function filterlist.set_filtercriteria(self,criteria)
  79. if criteria == self.m_filtercriteria and
  80. type(criteria) ~= "table" then
  81. return
  82. end
  83. self.m_filtercriteria = criteria
  84. filterlist.process(self)
  85. end
  86. --------------------------------------------------------------------------------
  87. function filterlist.get_filtercriteria(self)
  88. return self.m_filtercriteria
  89. end
  90. --------------------------------------------------------------------------------
  91. --supported sort mode "alphabetic|none"
  92. function filterlist.set_sortmode(self,mode)
  93. if (mode == self.m_sortmode) then
  94. return
  95. end
  96. self.m_sortmode = mode
  97. filterlist.process(self)
  98. end
  99. --------------------------------------------------------------------------------
  100. function filterlist.get_list(self)
  101. return self.m_processed_list
  102. end
  103. --------------------------------------------------------------------------------
  104. function filterlist.get_raw_list(self)
  105. return self.m_raw_list
  106. end
  107. --------------------------------------------------------------------------------
  108. function filterlist.get_raw_element(self,idx)
  109. if type(idx) ~= "number" then
  110. idx = tonumber(idx)
  111. end
  112. if idx ~= nil and idx > 0 and idx <= #self.m_raw_list then
  113. return self.m_raw_list[idx]
  114. end
  115. return nil
  116. end
  117. --------------------------------------------------------------------------------
  118. function filterlist.get_raw_index(self,listindex)
  119. assert(self.m_processed_list ~= nil)
  120. if listindex ~= nil and listindex > 0 and
  121. listindex <= #self.m_processed_list then
  122. local entry = self.m_processed_list[listindex]
  123. for i,v in ipairs(self.m_raw_list) do
  124. if self.m_compare_fct(v,entry) then
  125. return i
  126. end
  127. end
  128. end
  129. return 0
  130. end
  131. --------------------------------------------------------------------------------
  132. function filterlist.get_current_index(self,listindex)
  133. assert(self.m_processed_list ~= nil)
  134. if listindex ~= nil and listindex > 0 and
  135. listindex <= #self.m_raw_list then
  136. local entry = self.m_raw_list[listindex]
  137. for i,v in ipairs(self.m_processed_list) do
  138. if self.m_compare_fct(v,entry) then
  139. return i
  140. end
  141. end
  142. end
  143. return 0
  144. end
  145. --------------------------------------------------------------------------------
  146. function filterlist.process(self)
  147. assert(self.m_raw_list ~= nil)
  148. if self.m_sortmode == "none" and
  149. self.m_filtercriteria == nil then
  150. self.m_processed_list = self.m_raw_list
  151. return
  152. end
  153. self.m_processed_list = {}
  154. for k,v in pairs(self.m_raw_list) do
  155. if self.m_filtercriteria == nil or
  156. self.m_filter_fct(v,self.m_filtercriteria) then
  157. self.m_processed_list[#self.m_processed_list + 1] = v
  158. end
  159. end
  160. if self.m_sortmode == "none" then
  161. return
  162. end
  163. if self.m_sort_list[self.m_sortmode] ~= nil and
  164. type(self.m_sort_list[self.m_sortmode]) == "function" then
  165. self.m_sort_list[self.m_sortmode](self)
  166. end
  167. end
  168. --------------------------------------------------------------------------------
  169. function filterlist.size(self)
  170. if self.m_processed_list == nil then
  171. return 0
  172. end
  173. return #self.m_processed_list
  174. end
  175. --------------------------------------------------------------------------------
  176. function filterlist.uid_exists_raw(self,uid)
  177. for i,v in ipairs(self.m_raw_list) do
  178. if self.m_uid_match_fct(v,uid) then
  179. return true
  180. end
  181. end
  182. return false
  183. end
  184. --------------------------------------------------------------------------------
  185. function filterlist.raw_index_by_uid(self, uid)
  186. local elementcount = 0
  187. local elementidx = 0
  188. for i,v in ipairs(self.m_raw_list) do
  189. if self.m_uid_match_fct(v,uid) then
  190. elementcount = elementcount +1
  191. elementidx = i
  192. end
  193. end
  194. -- If there are more elements than one with same name uid can't decide which
  195. -- one is meant. self shouldn't be possible but just for sure.
  196. if elementcount > 1 then
  197. elementidx=0
  198. end
  199. return elementidx
  200. end
  201. --------------------------------------------------------------------------------
  202. -- COMMON helper functions --
  203. --------------------------------------------------------------------------------
  204. --------------------------------------------------------------------------------
  205. function compare_worlds(world1,world2)
  206. if world1.path ~= world2.path then
  207. return false
  208. end
  209. if world1.name ~= world2.name then
  210. return false
  211. end
  212. if world1.gameid ~= world2.gameid then
  213. return false
  214. end
  215. return true
  216. end
  217. --------------------------------------------------------------------------------
  218. function sort_worlds_alphabetic(self)
  219. table.sort(self.m_processed_list, function(a, b)
  220. --fixes issue #857 (crash due to sorting nil in worldlist)
  221. if a == nil or b == nil then
  222. if a == nil and b ~= nil then return false end
  223. if b == nil and a ~= nil then return true end
  224. return false
  225. end
  226. if a.name:lower() == b.name:lower() then
  227. return a.name < b.name
  228. end
  229. return a.name:lower() < b.name:lower()
  230. end)
  231. end
  232. --------------------------------------------------------------------------------
  233. function sort_mod_list(self)
  234. table.sort(self.m_processed_list, function(a, b)
  235. -- Show game mods at bottom
  236. if a.typ ~= b.typ then
  237. if b.typ == "game" then
  238. return a.typ ~= "game_mod"
  239. end
  240. return b.typ == "game_mod"
  241. end
  242. -- If in same or no modpack, sort by name
  243. if a.modpack == b.modpack then
  244. if a.name:lower() == b.name:lower() then
  245. return a.name < b.name
  246. end
  247. return a.name:lower() < b.name:lower()
  248. -- Else compare name to modpack name
  249. else
  250. -- Always show modpack pseudo-mod on top of modpack mod list
  251. if a.name == b.modpack then
  252. return true
  253. elseif b.name == a.modpack then
  254. return false
  255. end
  256. local name_a = a.modpack or a.name
  257. local name_b = b.modpack or b.name
  258. if name_a:lower() == name_b:lower() then
  259. return name_a < name_b
  260. end
  261. return name_a:lower() < name_b:lower()
  262. end
  263. end)
  264. end