Fix crash that can be caused by the shutdown command. (#5292)
[oweals/minetest.git] / builtin / fstk / tabview.lua
1 --Minetest
2 --Copyright (C) 2014 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 --------------------------------------------------------------------------------
20 -- A tabview implementation                                                   --
21 -- Usage:                                                                     --
22 -- tabview.create: returns initialized tabview raw element                    --
23 -- element.add(tab): add a tab declaration                                    --
24 -- element.handle_buttons()                                                   --
25 -- element.handle_events()                                                    --
26 -- element.getFormspec() returns formspec of tabview                          --
27 --------------------------------------------------------------------------------
28
29 --------------------------------------------------------------------------------
30 local function add_tab(self,tab)
31         assert(tab.size == nil or (type(tab.size) == table and
32                         tab.size.x ~= nil and tab.size.y ~= nil))
33         assert(tab.cbf_formspec ~= nil and type(tab.cbf_formspec) == "function")
34         assert(tab.cbf_button_handler == nil or
35                         type(tab.cbf_button_handler) == "function")
36         assert(tab.cbf_events == nil or type(tab.cbf_events) == "function")
37
38         local newtab = {
39                 name = tab.name,
40                 caption = tab.caption,
41                 button_handler = tab.cbf_button_handler,
42                 event_handler = tab.cbf_events,
43                 get_formspec = tab.cbf_formspec,
44                 tabsize = tab.tabsize,
45                 on_change = tab.on_change,
46                 tabdata = {},
47         }
48
49         self.tablist[#self.tablist + 1] = newtab
50
51         if self.last_tab_index == #self.tablist then
52                 self.current_tab = tab.name
53                 if tab.on_activate ~= nil then
54                         tab.on_activate(nil,tab.name)
55                 end
56         end
57 end
58
59 --------------------------------------------------------------------------------
60 local function get_formspec(self)
61         local formspec = ""
62
63         if not self.hidden and (self.parent == nil or not self.parent.hidden) then
64
65                 if self.parent == nil then
66                         local tsize = self.tablist[self.last_tab_index].tabsize or
67                                         {width=self.width, height=self.height}
68                         formspec = formspec ..
69                                         string.format("size[%f,%f,%s]",tsize.width,tsize.height,
70                                                 dump(self.fixed_size))
71                 end
72                 formspec = formspec .. self:tab_header()
73                 formspec = formspec ..
74                                 self.tablist[self.last_tab_index].get_formspec(
75                                         self,
76                                         self.tablist[self.last_tab_index].name,
77                                         self.tablist[self.last_tab_index].tabdata,
78                                         self.tablist[self.last_tab_index].tabsize
79                                         )
80         end
81         return formspec
82 end
83
84 --------------------------------------------------------------------------------
85 local function handle_buttons(self,fields)
86
87         if self.hidden then
88                 return false
89         end
90
91         if self:handle_tab_buttons(fields) then
92                 return true
93         end
94
95         if self.glb_btn_handler ~= nil and
96                 self.glb_btn_handler(self,fields) then
97                 return true
98         end
99
100         if self.tablist[self.last_tab_index].button_handler ~= nil then
101                 return
102                         self.tablist[self.last_tab_index].button_handler(
103                                         self,
104                                         fields,
105                                         self.tablist[self.last_tab_index].name,
106                                         self.tablist[self.last_tab_index].tabdata
107                                         )
108         end
109
110         return false
111 end
112
113 --------------------------------------------------------------------------------
114 local function handle_events(self,event)
115
116         if self.hidden then
117                 return false
118         end
119
120         if self.glb_evt_handler ~= nil and
121                 self.glb_evt_handler(self,event) then
122                 return true
123         end
124
125         if self.tablist[self.last_tab_index].evt_handler ~= nil then
126                 return
127                         self.tablist[self.last_tab_index].evt_handler(
128                                         self,
129                                         event,
130                                         self.tablist[self.last_tab_index].name,
131                                         self.tablist[self.last_tab_index].tabdata
132                                         )
133         end
134
135         return false
136 end
137
138
139 --------------------------------------------------------------------------------
140 local function tab_header(self)
141
142         local toadd = ""
143
144         for i=1,#self.tablist,1 do
145
146                 if toadd ~= "" then
147                         toadd = toadd .. ","
148                 end
149
150                 toadd = toadd .. self.tablist[i].caption
151         end
152         return string.format("tabheader[%f,%f;%s;%s;%i;true;false]",
153                         self.header_x, self.header_y, self.name, toadd, self.last_tab_index);
154 end
155
156 --------------------------------------------------------------------------------
157 local function switch_to_tab(self, index)
158         --first call on_change for tab to leave
159         if self.tablist[self.last_tab_index].on_change ~= nil then
160                 self.tablist[self.last_tab_index].on_change("LEAVE",
161                                 self.current_tab, self.tablist[index].name)
162         end
163
164         --update tabview data
165         self.last_tab_index = index
166         local old_tab = self.current_tab
167         self.current_tab = self.tablist[index].name
168
169         if (self.autosave_tab) then
170                 core.setting_set(self.name .. "_LAST",self.current_tab)
171         end
172
173         -- call for tab to enter
174         if self.tablist[index].on_change ~= nil then
175                 self.tablist[index].on_change("ENTER",
176                                 old_tab,self.current_tab)
177         end
178 end
179
180 --------------------------------------------------------------------------------
181 local function handle_tab_buttons(self,fields)
182         --save tab selection to config file
183         if fields[self.name] then
184                 local index = tonumber(fields[self.name])
185                 switch_to_tab(self, index)
186                 return true
187         end
188
189         return false
190 end
191
192 --------------------------------------------------------------------------------
193 local function set_tab_by_name(self, name)
194         for i=1,#self.tablist,1 do
195                 if self.tablist[i].name == name then
196                         switch_to_tab(self, i)
197                         return true
198                 end
199         end
200
201         return false
202 end
203
204 --------------------------------------------------------------------------------
205 local function hide_tabview(self)
206         self.hidden=true
207
208         --call on_change as we're not gonna show self tab any longer
209         if self.tablist[self.last_tab_index].on_change ~= nil then
210                 self.tablist[self.last_tab_index].on_change("LEAVE",
211                                 self.current_tab, nil)
212         end
213 end
214
215 --------------------------------------------------------------------------------
216 local function show_tabview(self)
217         self.hidden=false
218
219         -- call for tab to enter
220         if self.tablist[self.last_tab_index].on_change ~= nil then
221                 self.tablist[self.last_tab_index].on_change("ENTER",
222                                 nil,self.current_tab)
223         end
224 end
225
226 local tabview_metatable = {
227         add                       = add_tab,
228         handle_buttons            = handle_buttons,
229         handle_events             = handle_events,
230         get_formspec              = get_formspec,
231         show                      = show_tabview,
232         hide                      = hide_tabview,
233         delete                    = function(self) ui.delete(self) end,
234         set_parent                = function(self,parent) self.parent = parent end,
235         set_autosave_tab          =
236                         function(self,value) self.autosave_tab = value end,
237         set_tab                   = set_tab_by_name,
238         set_global_button_handler =
239                         function(self,handler) self.glb_btn_handler = handler end,
240         set_global_event_handler =
241                         function(self,handler) self.glb_evt_handler = handler end,
242         set_fixed_size =
243                         function(self,state) self.fixed_size = state end,
244         tab_header = tab_header,
245         handle_tab_buttons = handle_tab_buttons
246 }
247
248 tabview_metatable.__index = tabview_metatable
249
250 --------------------------------------------------------------------------------
251 function tabview_create(name, size, tabheaderpos)
252         local self = {}
253
254         self.name     = name
255         self.type     = "toplevel"
256         self.width    = size.x
257         self.height   = size.y
258         self.header_x = tabheaderpos.x
259         self.header_y = tabheaderpos.y
260
261         setmetatable(self, tabview_metatable)
262
263         self.fixed_size     = true
264         self.hidden         = true
265         self.current_tab    = nil
266         self.last_tab_index = 1
267         self.tablist        = {}
268
269         self.autosave_tab   = false
270
271         ui.add(self)
272         return self
273 end