1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 12 июн. 2017 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/tk/tk.h>
23 
24 #define ID_GEN_MASK         0x7fffff
25 
26 namespace lsp
27 {
28     namespace tk
29     {
LSPSlot()30         LSPSlot::LSPSlot()
31         {
32             pRoot       = NULL;
33             nID         = 0;
34         }
35 
~LSPSlot()36         LSPSlot::~LSPSlot()
37         {
38             unbind_all();
39         }
40 
find_item(ui_handler_id_t id)41         inline LSPSlot::handler_item_t *LSPSlot::find_item(ui_handler_id_t id)
42         {
43             handler_item_t *ptr = pRoot;
44 
45             while (ptr != NULL)
46             {
47                 if (ptr->nID == id)
48                     break;
49                 ptr     = ptr->pNext;
50             }
51             return ptr;
52         }
53 
bind(ui_event_handler_t handler,void * arg,bool enabled)54         ui_handler_id_t LSPSlot::bind(ui_event_handler_t handler, void *arg, bool enabled)
55         {
56             return bind(handler, false, arg, enabled);
57         }
58 
intercept(ui_event_handler_t handler,void * arg,bool enabled)59         ui_handler_id_t LSPSlot::intercept(ui_event_handler_t handler, void *arg, bool enabled)
60         {
61             return bind(handler, true, arg, enabled);
62         }
63 
bind(ui_event_handler_t handler,bool intercept,void * arg,bool enabled)64         ui_handler_id_t LSPSlot::bind(ui_event_handler_t handler, bool intercept, void *arg, bool enabled)
65         {
66             // Check data
67             if (handler == NULL)
68                 return - STATUS_BAD_ARGUMENTS;
69 
70             // Now try to allocate new data
71             handler_item_t *item        = new handler_item_t;
72             if (item == NULL)
73                 return - STATUS_NO_MEM;
74 
75             // Generate handler identifier
76             do
77             {
78                 item->nID   = nID;
79                 nID         = (nID + 1) & ID_GEN_MASK;
80             } while (find_item(item->nID) != NULL);
81 
82             // Initialize item and bind it
83             size_t mask         = (intercept) ? BIND_DFL : BIND_DFL | BIND_INTERCEPT;
84             item->nFlags        = (enabled) ? mask | BIND_ENABLED : mask;
85             item->pHandler      = handler;
86             item->pPtr          = arg;
87             item->pNext         = pRoot;
88             pRoot               = item;
89 
90             return item->nID;
91         }
92 
unbind(ui_handler_id_t id)93         status_t LSPSlot::unbind(ui_handler_id_t id)
94         {
95             // Check data
96             if (id < 0)
97                 return STATUS_BAD_ARGUMENTS;
98 
99             handler_item_t *ptr     = pRoot;
100             handler_item_t *prev    = NULL;
101 
102             while (ptr != NULL)
103             {
104                 if (ptr->nID == id)
105                 {
106                     // Unbind handler and remove it
107                     if (prev == NULL)
108                         pRoot           = ptr->pNext;
109                     else
110                         prev->pNext     = ptr->pNext;
111                     delete ptr;
112                     return STATUS_OK;
113                 }
114                 prev    = ptr;
115                 ptr     = ptr->pNext;
116             }
117             return STATUS_NOT_FOUND;
118         }
119 
unbind(ui_event_handler_t handler,void * arg)120         ui_handler_id_t LSPSlot::unbind(ui_event_handler_t handler, void *arg)
121         {
122             // Check data
123             if (handler == NULL)
124                 return - STATUS_BAD_ARGUMENTS;
125 
126             handler_item_t *ptr     = pRoot;
127             handler_item_t *prev    = NULL;
128 
129             while (ptr != NULL)
130             {
131                 if ((ptr->pHandler == handler) && (ptr->pPtr == arg))
132                 {
133                     // Unbind handler and remove it
134                     ui_handler_id_t id  = ptr->nID;
135                     if (prev == NULL)
136                         pRoot           = ptr->pNext;
137                     else
138                         prev->pNext     = ptr->pNext;
139                     delete ptr;
140                     return id;
141                 }
142                 prev    = ptr;
143                 ptr     = ptr->pNext;
144             }
145             return - STATUS_NOT_FOUND;
146         }
147 
unbind_all()148         size_t LSPSlot::unbind_all()
149         {
150             handler_item_t *ptr     = pRoot;
151             handler_item_t *next    = NULL;
152             size_t removed          = 0;
153 
154             while (ptr != NULL)
155             {
156                 next        = ptr->pNext;
157                 delete ptr;
158                 ptr         = next;
159                 removed     ++;
160             }
161 
162             return removed;
163         }
164 
disable(ui_handler_id_t id)165         status_t LSPSlot::disable(ui_handler_id_t id)
166         {
167             // Check data
168             if (id < 0)
169                 return STATUS_BAD_ARGUMENTS;
170 
171             handler_item_t *ptr     = find_item(id);
172             if (ptr == NULL)
173                 return STATUS_NOT_FOUND;
174 
175             ptr->nFlags            &= ~BIND_ENABLED;
176             return STATUS_OK;
177         }
178 
disable_all()179         size_t LSPSlot::disable_all()
180         {
181             return disable_all(true, true);
182         }
183 
disable_all_interceptors()184         size_t LSPSlot::disable_all_interceptors()
185         {
186             return disable_all(false, true);
187         }
188 
disable_all_bindings()189         size_t LSPSlot::disable_all_bindings()
190         {
191             return disable_all(true, false);
192         }
193 
disable_all(bool handler,bool interceptor)194         size_t LSPSlot::disable_all(bool handler, bool interceptor)
195         {
196             if ((!handler) && (!interceptor))
197                 return 0;
198 
199             handler_item_t *ptr     = pRoot;
200             size_t disabled         = 0;
201             size_t mask             = (handler && interceptor) ? BIND_ENABLED : BIND_ENABLED | BIND_INTERCEPT;
202             size_t check            = ((!handler) && interceptor) ? BIND_INTERCEPT | BIND_ENABLED : BIND_ENABLED;
203 
204             while (ptr != NULL)
205             {
206                 if ((ptr->nFlags & mask) == check)
207                 {
208                     ptr->nFlags    &= ~BIND_ENABLED;
209                     disabled        ++;
210                 }
211                 ptr     = ptr->pNext;
212             }
213 
214             return disabled;
215         }
216 
enable(ui_handler_id_t id)217         status_t LSPSlot::enable(ui_handler_id_t id)
218         {
219             // Check data
220             if (id < 0)
221                 return STATUS_BAD_ARGUMENTS;
222 
223             handler_item_t *ptr     = find_item(id);
224             if (ptr == NULL)
225                 return STATUS_NOT_FOUND;
226 
227             ptr->nFlags            |= BIND_ENABLED;
228             return STATUS_OK;
229         }
230 
enable_all(bool handler,bool interceptor)231         size_t LSPSlot::enable_all(bool handler, bool interceptor)
232         {
233             handler_item_t *ptr     = pRoot;
234             size_t enabled          = 0;
235             size_t mask             = (handler && interceptor) ? BIND_ENABLED : BIND_ENABLED | BIND_INTERCEPT;
236             size_t check            = ((!handler) && interceptor) ? BIND_INTERCEPT : 0;
237 
238             while (ptr != NULL)
239             {
240                 if ((ptr->nFlags & mask) == check)
241                 {
242                     ptr->nFlags    |= BIND_ENABLED;
243                     enabled         ++;
244                 }
245                 ptr     = ptr->pNext;
246             }
247 
248             return enabled;
249         }
250 
enable_all()251         size_t LSPSlot::enable_all()
252         {
253             return enable_all(true, true);
254         }
255 
enable_all_bindings()256         size_t LSPSlot::enable_all_bindings()
257         {
258             return enable_all(true, false);
259         }
260 
enable_all_interceptors()261         size_t LSPSlot::enable_all_interceptors()
262         {
263             return enable_all(false, true);
264         }
265 
execute(LSPWidget * sender,void * data)266         status_t LSPSlot::execute(LSPWidget *sender, void *data)
267         {
268             // First iteration, iterate all interceptors
269             for (handler_item_t *ptr = pRoot; ptr != NULL; ptr = ptr->pNext)
270             {
271                 // Execute handler in the chain
272                 if ((ptr->nFlags & (BIND_ENABLED | BIND_INTERCEPT)) == (BIND_ENABLED | BIND_INTERCEPT))
273                 {
274                     status_t result      = ptr->pHandler(sender, ptr->pPtr, data);
275                     if (result != STATUS_OK)
276                         return STATUS_OK;
277                 }
278             }
279 
280             // Second iteration, iterate all handlers
281             for (handler_item_t *ptr = pRoot; ptr != NULL; ptr = ptr->pNext)
282             {
283                 // Execute handler in the chain
284                 if ((ptr->nFlags & (BIND_ENABLED | BIND_INTERCEPT)) == BIND_ENABLED)
285                 {
286                     status_t result      = ptr->pHandler(sender, ptr->pPtr, data);
287                     if (result != STATUS_OK)
288                         return result;
289                 }
290             }
291 
292             return STATUS_OK;
293         }
294 
295     }
296 } /* namespace lsp */
297